Mam tabelę zawierającą dane klientów w bazie danych Oracle. Oto uproszczona definicja:Wspólny klucz podstawowy JPA z wartością pustą
CUSTOMER (CUSTOMER_ID NUMBER NOT NULL,
SOURCE_SYSTEM VARCHAR2(30),
FULL_NAME VARCHAR2(360),
PHONE_NUMBER VARCHAR2(240)
)
Klucz podstawowy dla tej tabeli to (CUSTOMER_ID, SOURCE_SYSTEM)
.
Tabela ma wiele wierszy, dla których SOURCE_SYSTEM
ma wartość null. Na poziomie bazy danych nie ma problemu, ale gdy próbuję uzyskać dostęp do dowolnego z tych wierszy za pośrednictwem jednostki JPA, powoduje to szereg problemów:
1: użycie em.find()
do pobrania wiersza z wartością zerową SOURCE_SYSTEM
zawsze powoduje zwracana jest wartość null.
2: Użycie em.merge()
do wstawienia wiersza o wartości zerowej SOURCE_SYSTEM
powiedzie się, jeśli rekord nie istnieje w tabeli, ale kończy się niepowodzeniem w kolejnych aktualizacjach, ponieważ scalenie ZAWSZE powoduje uruchomienie wkładki.
3: Korzystanie em.createQuery()
jawnie zapytać o wierszu z pustym powoduje następujący wyjątek:
Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.3.1.v20111018-r10243):
org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
CUSTOMER.CUSTOMER_ID => 1
CUSTOMER.FULL_NAME => GUY PERSON
CUSTOMER.PHONE_NUMBER => 555-555-1234
CUSTOMER.SOURCE_SYSTEM => null)] during the execution of the query was detected to be null.
Primary keys must not contain null.
Query: ReadAllQuery(referenceClass=Customer sql="SELECT CUSTOMER_ID, FULL_NAME, PHONE_NUMBER, SOURCE_SYSTEM FROM CUSTOMER WHERE ((CUSTOMER_ID = ?) AND (SOURCE_SYSTEM IS NULL))")
Niestety, „Klucze główne nie może zawierać null” wydaje się dość ostateczna. Nie mogłem znaleźć zbyt wielu informacji na temat obejścia tego błędu, co sprawia, że wydaje się, że nie ma rozwiązania.
KWESTIA: Chciałbym wiedzieć, czy ktoś ma jakieś kodu Java rozwiązanie które nie wymagają wprowadzania zmian do bazy danych. Moje bieżące obejście polega na użyciu ROW_ID
jako tabeli @Id
, ale oznacza to, że nie mogę już używać em.merge()
ani em.find()
.
Oto moje klasy Java:
Customer.java:
@Entity
@Table(name = "CUSTOMER")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
private Customer_Id key;
@Column(name = "CUSTOMER_ID", nullable = false, insertable = false, updatable = false)
private Long customerId;
@Column(name = "SOURCE_SYSTEM", length = 30, insertable = false, updatable = false)
private String sourceSystem;
@Column(name = "FULL_NAME", length = 360)
private String fullName;
@Column(name = "PHONE_NUMBER", length = 240)
private String phoneNumber;
//Setters, Getters, etc
...
}
Customer_Id.java
@Embeddable
public class Customer_Id implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name = "CUSTOMER_ID", nullable = false)
private Long customerId;
@Column(name = "SOURCE_SYSTEM", length = 30)
private String sourceSystem;
//Setters, Getters, etc
...
}
Dlaczego używasz wartości null dla identyfikatora klienta? Wygląda to na coś, co powinno korzystać z sekwencjonowania, a jeśli nie jest zerowe, powinno pozwolić na to, by pole source_system miało wartość null. Możesz spróbować określić walidację, która ma być używana, jak opisano tutaj http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_primarykey.htm, ale nie jestem pewien, dlaczego to, co zrobiłeś, nie zadziałałoby. Odwzorowujesz pole customer_id dwa razy, więc upewnij się, że obie opcje są poprawne. – Chris
Włącz także logowanie, aby sprawdzić, czy występują problemy z odwzorowaniami lub wygenerowanym SQL. Zapytania dotyczące pól pustych muszą się zasadniczo różnić, co może nie występować od sprawdzenia pk. http: //wiki.eclipse.org/EclipseLink/Examples/JPA/Logging opisuje logowanie Eclipselink – Chris
'CUSTOMER_ID' nigdy nie będzie mieć wartości NULL i jest oznaczone jako" nullable = false "w klasie, a także" NOT NULL "w bazie danych, chociaż nie jest oparte na sekwencja. Jest bardzo prawdopodobne, że sql dla 'em.find()' jest generowany z 'SOURCE_SYSTEM = ''' zamiast 'SOURCE_SYSTEM IS NULL', dlatego rekord nie jest zwracany poprawnie. –