2012-04-12 9 views
7

Używam bazy danych Oracle i mam sekwencję i wyzwalacz do generowania i przechowywania ID przed wstawieniem.JPA @Id i insertable = false, updatable = false throws wyjątek

CREATE SEQUENCE CASE_SEQ START WITH 1001 INCREMENT BY 1 NOMAXVALUE; 

CREATE OR REPLACE TRIGGER CASE_TR_SEQ 
BEFORE INSERT ON CASE FOR EACH ROW 
BEGIN 
    SELECT CASE_SEQ.NEXTVAL INTO :NEW.CASE_ID FROM DUAL; 
END; 
/

Wtedy mam proste podmiot z własności:

@Id 
@Column(name = "CASE_ID", insertable = false, updatable = false) 
private Long caseId; 

... gdy próbuję zbudować projekt Dostaję:

Exception [EclipseLink-46] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): 
org.eclipse.persistence.exceptions.DescriptorException 
Exception Description: There should be one non-read-only mapping defined for the 
primary key field [CASE.CASE_ID]. 

Kiedy usunąć albo insercji lub mogą być aktualizowane słowa kluczowego , to działa. Wiem, że istnieje wiele rozwiązań generujących identyfikator za pomocą JPA, także JPA może użyć (wywołać) sekwencji oracle do ustawienia (wygenerowanego) identyfikatora. Ale próbuję zrozumieć, dlaczego moje rozwiązania są złe. Dlaczego nie mogę używać obu słów kluczowych razem z adnotacją @Id? Moją myślą jest: chcę zabronić wstawiania lub aktualizowania caseId przez JPA.

1) Jaka jest właściwa dusza? Należy używać tylko @Id:

@Id 
@Column(name = "CASE_ID") 
private Long caseId; 

lub lepiej (bezpieczniej) określają wkładane = false również:

@Id 
@Column(name = "CASE_ID", insertable = false) 
private Long caseId; 

2) Rozumiem, że aktualizowalny = false dla @Id nie mają znaczenia (aktualizacji Klucz podstawowy nie ma znaczenia, ale jest to możliwe przez surowego SQL), ale co to znaczy (czy masz jakiś przykład gdy jest to korzystne):

@Id 
@Column(name = "CASE_ID", updatable = false) 
private Long caseId; 

EDIT 2012-04-13

Zrobiłem kilka testów:

Podmiot

@Id 
@Column(name = "CASE_ID") 
private Long caseId; 

JPA zalogować

INSERT INTO CASE (CASE_ID, CASE_DATE, INFO) VALUES (?, ?, ?) 
bind => [3 parameters bound]|#] 

Więc to nie jest bezpieczne, ponieważ JPA próbuje sklepu CASE_ID (który jest następnie zastąpiony przez ID z sekwencji Oracle przez cyngiel).

Podmiot

@Id 
@Column(name = "CASE_ID", insertable = false) 
private Long caseId; 

Tworzenie metoda

public void createCase(final Case caseData) { 
    caseData.setCaseId(-1001L); 
    em.persist(caseData); 
} 

JPA zalogować

INSERT INTO CASE (CASE_DATE, INFO) VALUES (?, ?) 
bind => [2 parameters bound]|#] 

To dobrze, bo CASE_ID nie jest częścią polecenia insert.

i aktualizacja CASE_ID nie jest możliwe, ponieważ adnotacja ID:

public void update() { 
    Case update = em.find(Case.class, 1023L); 
    update.setCaseId(1028L); 
} 

Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): 
org.eclipse.persistence.exceptions.ValidationException 
Exception Description: The attribute [caseId] of class 
[com.wordpress.kamiluv.jsfprototype.model.entity.Case] is mapped to a primary 
key column in the database. Updates are not allowed. 

Więc teraz ostatnia wersja wygląda jak najbardziej bezpieczne, prawda?

+1

Zobacz http://stackoverflow.com/questions/3669883/hibernate-where-do-insertable-false-updatable-false-belong-in-composite-pr i http://stackoverflow.com/questions/3805584/please-explain-about-insertable-false-updatable-false i zobacz, czy to ci pomaga. –

+0

@tech_learner Niestety nie wiele – Ziletka

+1

Nie jestem pewien, czy to pomaga, ale czy szukasz niezmiennej klasy? Jeśli tak, spójrz na to: http://www.mkyong.com/hibernate/hibernate-mutable-example-class-and-collection/ –

Odpowiedz

5

Mówisz teraz przy użyciu adnotacji WZP, że masz kolumnę @Id, której nie można w żaden sposób wstawić ani zaktualizować. Musisz mieć możliwość ustawienia identyfikatora przed wstawieniem lub aktualizacją, ale JPA nie wie, jak to zrobić.Będziesz musiał użyć adnotacji @GeneratedValue na @Id, aby poinformować JPA jaką strategię zastosować (jedną z: TABELA, SEKWENCJA, IDENTYFIKACJA, AUTO), jeśli nie chcesz, aby identyfikator został ustawiony w twoim kodzie.

+0

Dobrze, ale nie chcę używać żadnej z opcji "@GeneratedValue" , Chciałbym użyć wyzwalacza Oracle, aby ustawić ID w kolumnach bazy danych. Usunięcie "@Id" nie jest dobrym rozwiązaniem, ponieważ istnieje pewna funkcjonalność połączona z "@Id" w JPA np .: EntityManager find (Case.class, caseId). – Ziletka

+0

Jeśli chcesz użyć wyzwalacza Oracle, spróbuj @GeneratorValue (strategy = GenerationType.AUTO). Nie jestem pewien co do Oracle, ale odniosłem sukces z AUTO na stole Sybase, który obsługiwał samą generację id. – bobz32

+0

działa, ale myślę, że to nie jest dobre rozwiązanie, strategia ATUTO jest zdefiniowana dla innego znaczenia ... DOC: Określenie strategii AUTO pozwala EclipseLink wybrać strategię do użycia. Zazwyczaj EclipseLink wybiera TABLE jako strategię, ponieważ jest to najbardziej przenośna strategia. Jednakże, gdy określono opcję AUTO, generowanie schematu musi być użyte co najmniej raz, aby tabela domyślna została utworzona w bazie danych. – Ziletka