lunedì 3 novembre 2014

Ibernazione Le Collezioni blocco ottimistico

Hibernate fornisce un  meccanismo di blocco ottimistico  per prevenire  aggiornamenti persi  anche per-lunghe conversazioni. In combinazione con una memoria entità, si estende su più richieste degli utenti (contesto di persistenza esteso o entità indipendenti) Hibernate può garantire  a livello di applicazione ripetibile-legge .

Il  meccanismo di controllo sporca  entità rileva i cambiamenti di stato e incrementa la versione entità. Mentre le modifiche alle proprietà di base sono sempre presi in considerazione, le collezioni Hibernate sono più sottili in questo senso.

Di proprietà vs collezioni Inverse
Nei database relazionali, due record sono associati con un riferimento chiave esterna. In questo rapporto, il record si fa riferimento è il genitore, mentre la riga di riferimento (il lato chiave esterna) è il bambino. Una chiave esterna non nullo può fare riferimento solo a un record principale esistente.

Nello spazio a oggetti questa associazione può essere rappresentato in entrambe le direzioni. Possiamo avere un riferimento molti-a-uno da un bambino genitore e il genitore può anche avere una collezione uno-a-molti bambini.

Poiché entrambe le parti potrebbero potenzialmente controllare lo stato chiave esterna banca dati, dobbiamo fare in modo che solo una parte è il proprietario di questa associazione. Solo i  titolari di cambiamenti di stato laterali vengono propagate al database. Il lato non possessore è stato tradizionalmente definito come l'  inverso  lato.

Successivo vi descriverò i modi più comuni di modellazione questa associazione.

Il-madre che detiene-side-bambino associazione mappatura unidirezionale
Solo il lato genitore ha un  OneToMany  bambini non inversa raccolta. L'entità bambino non fa riferimento alla controllante a tutti.

Visualizza sorgentestampare ?
1.
Entity (name = "post" )
2.
pubblico class post {
3.
...
4.
OneToMany (cascade = CascadeType.ALL, orphanRemoval = true )
. 5
private List <Commento> commenti = nuovo ArrayList <Commento> ();
6.
...
7.
}
Il-madre che detiene-side-bambino associazione componente mappatura mappatura unidirezionale
Il lato bambino non sempre deve essere una entità e potremmo modellare come un tipo di componente  , invece. Un  integrabile  oggetto (tipo di componente) può contenere entrambi i tipi di base e le mappature di associazione, ma non può mai contenere unId. L'oggetto è integrabile persistente / rimosso insieme alla sua entità possedere.

Il genitore ha un  ElementCollection  associazione bambini. L'entità bambino può fare riferimento solo il genitore attraverso la non-interrogabile Sospensione specifico  Parent annotazione.

Visualizza sorgentestampare ?
01.
Entity (name = "post" )
02.
pubblica class post {
03.
...
04.
ElementCollection
05.
JoinTable (nome = "post_comments" , joinColumns = JoinColumn (name = "post_id" ))
06.
OrderColumn (name = "comment_index" )
. 07
private List <Commento> commenti = nuovo ArrayList <Commento> ();
08.
...
09.

10.
pubblico vuoto addComment (commento commenti) {
. 11
comment.setPost ( questo );
. 12
comments.add (commento);
13.
}
14.
}
15.

16.
Embeddable
17.
pubblica class {Commento
18.
...
19.
Parent
20.
privato annuncio Pubblica;
21.
...
22.
}
Il-madre che detiene-side-bambino associazione mappatura bidirezionale
Il genitore è il lato proprietaria quindi ha un  OneToMany  non inversa (senza una direttiva mappedBy) figli di raccolta. L'entità bambino fa riferimento alla controllante attraverso un  ManyToOne  associazione che è né inseribile né aggiornabile:

Visualizza sorgentestampare ?
01.
Entity (name = "post" )
02.
pubblica class post {
03.
...
04.
OneToMany (cascade = CascadeType.ALL, orphanRemoval = true )
. 05
private List <Commento> commenti = nuovo ArrayList <Commento> ();
06.
...
07.

08.
pubblico vuoto addComment (commento commenti) {
. 09
comment.setPost ( questo );
. 10
comments.add (commento);
11.
}
12.
}
13.

14.
Entity (name = "commenti" )
15.
pubblica di classe Commento
16.
...
17.
ManyToOne
18.
JoinColumn (name = "post_id" , inseribile = falso , aggiornabile = falso )
19.
privato annuncio Pubblica;
20.
...
21.
}
Il figlio possedere-side-genitore associazione mappatura bidirezionale
L'entità bambino fa riferimento alla controllante attraverso un  ManyToOne  associazione, e il genitore ha un  mappedBy  OneToMany  bambini collezione. La parte principale è il lato inverso in modo che solo iManyToOne cambiamenti di stato vengono propagate al database.

Anche se c'è solo una parte proprietaria, è sempre una buona norma  tenere entrambe le parti in sincronia  utilizzando l'add / removeChild (metodi).

Visualizza sorgentestampare ?
01.
Entity (name = "post" )
02.
pubblica class post {
03.
...
04.
OneToMany (cascade = CascadeType.ALL, orphanRemoval = true , mappedBy = "post" )
. 05
private List <Commento> commenti = nuovo ArrayList <Commento> ();
06.
...
07.
pubblico vuoto addComment (commento commenti) {
. 08
comment.setPost ( questo );
. 09
comments.add (commento);
10.
}
11.
}
12.

13.
Entity (name = "commenti" )
14.
pubblica class {Commento
15.
...
16.
ManyToOne
17.
privato annuncio Pubblica;
18.
...
19.
}
Il figlio possedere-side-genitore associazione mappatura unidirezionale
L'entità bambino fa riferimento il genitore attraverso un  ManyToOne  associazione. Il genitore non ha una  OneToMany  bambini insieme in modo che il soggetto bambino diventa il lato di appartenenza. Questa mappatura associazione ricorda i dati relazionali linkage chiave esterna.

Visualizza sorgentestampare ?
1.
Entity (name = "commenti" )
2.
pubblico class {Commento
3.
...
4.
ManyToOne
5.
privato annuncio Pubblica;
6.
...
7.
}
Raccolta delle versioni
La sezione 3.4.2 della  specifica JPA 2.1  definisce il blocco ottimistico come:

L'attributo versione viene aggiornata dal runtime provider di persistenza quando l'oggetto viene scritto nel database. Tutti i campi non di relazione e cravatte propri e tutti i rapporti di proprietà del soggetto sono compresi nel controllo di versione [35].

[35] Ciò comprende i rapporti di proprietà mantenuti in uniscono tabelle

NB Solo i bambini collezione possedere-side in grado di aggiornare la versione del genitore.

Tempo di analisi
Testiamo come il tipo di associazione genitore-bambino influisce sul controllo delle versioni genitore. Poiché siamo interessati al controllo collezione bambini sporchi, il figlio possedere-side-genitore unidirezionale  associazione sta per saltare, come in questo caso il genitore non contiene una collezione per bambini.

Banco di prova
Il seguente banco di prova sta per essere utilizzato per tutti i casi d'uso di tipo di raccolta:

Visualizza sorgentestampare ?
01.
protette vuoto simulateConcurrentTransactions ( finale boolean shouldIncrementParentVersion) {
02.
def ExecutorService ExecutorService = Executors.newSingleThreadExecutor ();
03.

04.
doInTransaction ( nuovo TransactionCallable <Vuoto> () {
05.
Override
06.
pubblica Void execute (Session session) {
07.
provare {
. 08
P post = postClass.newInstance ();
09.
post.setId (1L);
10.
post.setName ( "Sospensione di formazione" );
. 11
session.persist (post);
. 12
ritorno nullo ;
13.
} fermo (Exception e) {
. 14
gettare nuova IllegalArgumentException (e);
15.
}
16.
}
17.
});
18.

19.
doInTransaction ( nuovo TransactionCallable <Vuoto> () {
20.
Override
21.
pubblica Void execute ( finale Session session) {
. 22
def post = P (P) session.get (postClass, 1L);
23.
provare {
24.
executorService.submit ( nuovo Callable <Vuoto> () {
25.
Override
26.
pubblica chiamata Void () lancia Exception {
27.
ritorno doInTransaction ( nuovo TransactionCallable <Vuoto> () {
28.
Override
29.
pubblica Void execute (_SESSION Session) {
30.
provare {
31.
P otherThreadPost = (P) _session.get (postClass, 1L);
32.
int loadTimeVersion = otherThreadPost.getVersion ();
33.
assertNotSame (post, otherThreadPost);
. 34
assertEquals (0L, otherThreadPost.getVersion ());
. 35
C comment = commentClass.newInstance ();
36.
comment.setReview ( "Buon posto!" );
37.
otherThreadPost.addComment (commento);
. 38
_session.flush ();
39.
se (shouldIncrementParentVersion) {
. 40
assertEquals (otherThreadPost.getVersion (), loadTimeVersion + 1 );
41.
} altrimenti {
. 42
assertEquals (otherThreadPost.getVersion (), loadTimeVersion);
43.
}
. 44
ritorno nullo ;
45.
} fermo (Exception e) {
. 46
gettare nuova IllegalArgumentException (e);
47.
}
48.
}
49.
});
50.
}
. 51
.}) get ();
52.
} fermo (Exception e) {
. 53
gettare nuova IllegalArgumentException (e);
54.
}
. 55
post.setName ( "Sospensione Master Class" );
. 56
session.flush ();
. 57
ritorno nullo ;
58.
}
59.
});
60.
}
Il test di associazione-madre che detiene-side-bambino unidirezionale

Visualizza sorgentestampare ?
01.
tabelle #create
02.
Domanda: {[creare commento della tabella (idbigint generato da impostazione predefinita come identità (iniziare con 1 ), recensione varchar ( 255 ), chiave primaria (id))] []}
03.
Domanda: {[create table posta (non idbigint nullo , nome varchar ( 255 ), la versione numero intero non nullo , chiave primaria (id))] []}
04.
Domanda: {[create table post_comment (post_id bigint non nullo , bigint comments_id non nullo , comment_index intero non nullo , chiave primaria (post_id, comment_index))] []}
05.
Domanda: {[alter table post_comment aggiungere vincolo di chiave esterna FK_se9l149iyyao6va95afioxsrl (comments_id) riferimenti commento] []}
06.
Domanda: {[alter table post_comment aggiungere vincolo di chiave esterna FK_6o1igdm04v78cwqre59or1yj1 (post_id) riferimenti post] []}
07.

08.
posto #insert transazione primaria
09.
Domanda: {[(???,,) inserire in valori postale (nome, versione, id)] [formazione Hibernate, 0 , 1 ]}
10.

11.
posto #SELECT transazione secondaria
12.
Domanda: {[? selectentityopti0_.idas id1_1_0_, entityopti0_.name come name2_1_0_, entityopti0_.version come version3_1_0_ dal post entityopti0_ dove entityopti0_.id =] [ 1 ]}
13.

14.
commento #insert transazione secondaria
15.
aggiornamento di bloccaggio #optimistic versione post in un'operazione secondaria
16.
Domanda: {[inserire nel commento (id, recensione) valori ( di default ,?)] [buon posto!]}
. 17
Domanda: {[update dopo setName = ?, version =? dove id =? e la versione =?] [Hibernate formazione, 1 , 1 , 0 ]}
18.
Domanda: {[inserire in valori post_comment (post_id, comment_index, comments_id) (,,???)] [ 1 , 0 , 1 ]}
19.

20.
eccezione di bloccaggio #optimistic transazione primaria
. 21
Domanda: {[update dopo setName = ?, version =? dove id =? e la versione =?] [Hibernate Master Class, 1 , 1 , 0 ]}
22.
org.hibernate.StaleObjectStateException: Riga è stato aggiornato o cancellato da un'altra transazione (o mappatura unsaved-value non è corretta): [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnUnidirectionalCollectionTest$Post# 1 ]
Il-madre che detiene-side-bambino test di associazione componente unidirezionale
Visualizza sorgentestampare ?
01.
tabelle #create
02.
Domanda: {[create table posta (non idbigint nullo , nome varchar ( 255 ), la versione numero intero non nullo , chiave primaria (id))] []}
03.
Domanda: {[creare post_comments tavolo (post_id bigint non nulli , recensione varchar ( 255 ), numero intero comment_index non nullo chiave, primaria (post_id, comment_index))] []}
04.
Domanda: {[alterare post_comments table Aggiungi vincolo di chiave esterna FK_gh9apqeduab8cs0ohcq1dgukp (post_id) riferimenti post] []}
05.

06.
posto #insert transazione primaria
07.
Domanda: {[(???,,) inserire in valori postale (nome, versione, id)] [formazione Hibernate, 0 , 1 ]}
08.

09.
posto #SELECT transazione secondaria
10.
Domanda: {[? selectentityopti0_.idas id1_0_0_, entityopti0_.name come name2_0_0_, entityopti0_.version come version3_0_0_ dal post entityopti0_ dove entityopti0_.id =] [ 1 ]}
11.
Domanda: {[? selectcomments0_.post_id come post_id1_0_0_, comments0_.review come review2_1_0_, comments0_.comment_index come comment_3_0_ da post_comments comments0_ dove comments0_.post_id =] [ 1 ]}
12.

13.
commento #insert transazione secondaria
14.
aggiornamento di bloccaggio #optimistic versione post in un'operazione secondaria
. 15
Domanda: {[update dopo setName = ?, version =? dove id =? e la versione =?] [Hibernate formazione, 1 , 1 , 0 ]}
16.
Domanda: {[inserire in post_comments (post_id, comment_index, revisione) valori (,,???)] [ 1 , 0 , buon post!]}
17.

18.
eccezione di bloccaggio #optimistic transazione primaria
. 19
Domanda: {[update dopo setName = ?, version =? dove id =? e la versione =?] [Hibernate Master Class, 1 , 1 , 0 ]}
20.
org.hibernate.StaleObjectStateException: Riga è stato aggiornato o cancellato da un'altra transazione (o mappatura unsaved-value non è corretta): [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnComponentCollectionTest$Post# 1 ]
Il test di associazione-madre che detiene-side-bambino bidirezionale
Visualizza sorgentestampare ?
01.
tabelle #create
02.
Domanda: {[creare commento della tabella (idbigint generato da impostazione predefinita come identità (iniziare con 1 ), recensione varchar ( 255 ), bigint post_id chiave primaria, (id))] []}
03.
Domanda: {[create table posta (non idbigint nullo , nome varchar ( 255 ), la versione numero intero non nullo , chiave primaria (id))] []}
04.
Domanda: {[create table post_comment (post_id bigint non nullo , bigint comments_id non nullo )] []}
05.
Domanda: {[alter table post_comment aggiungere vincolo UK_se9l149iyyao6va95afioxsrl unico (comments_id)] ​​[]}
06.
Domanda: {[alter table vincolo commento aggiuntivo FK_f1sl0xkd2lucs7bve3ktt3tu5 chiave esterna (post_id) riferimenti post] []}
07.
Domanda: {[alter table post_comment aggiungere vincolo di chiave esterna FK_se9l149iyyao6va95afioxsrl (comments_id) riferimenti commento] []}
08.
Domanda: {[alter table post_comment aggiungere vincolo di chiave esterna FK_6o1igdm04v78cwqre59or1yj1 (post_id) riferimenti post] []}
09.

10.
posto #insert transazione primaria
11.
Domanda: {[(???,,) inserire in valori postale (nome, versione, id)] [formazione Hibernate, 0 , 1 ]}
12.

13.
posto #SELECT transazione secondaria
14.
Domanda: {[? selectentityopti0_.idas id1_1_0_, entityopti0_.name come name2_1_0_, entityopti0_.version come version3_1_0_ dal post entityopti0_ dove entityopti0_.id =] [ 1 ]}
15.
Domanda: {[selectcomments0_.post_id come post_id1_1_0_, comments0_.comments_id come comments2_2_0_, entityopti1_.idas id1_0_1_, entityopti1_.post_id come post_id3_0_1_, entityopti1_.review come review2_0_1_, entityopti2_.idas id1_1_2_, entityopti2_.name come name2_1_2_, entityopti2_.version come version3_1_2_ da post_comment comments0_ entityopti1_ joincomment interno su comments0_.comments_id = entityopti1_.idleft esterno joinpost entityopti2_ su entityopti1_.post_id = entityopti2_.idwhere comments0_.post_id =?] [ 1 ]}
16.

17.
commento #insert transazione secondaria
18.
aggiornamento di bloccaggio #optimistic versione post in un'operazione secondaria
19.
Domanda: {[inserire nel commento (id, recensione) valori ( di default ,?)] [buon posto!]}
. 20
Domanda: {[update dopo setName = ?, version =? dove id =? e la versione =?] [Hibernate formazione, 1 , 1 , 0 ]}
21.
Domanda: {[inserire in valori post_comment (post_id, comments_id) (,??)] [ 1 , 1 ]}
22.

23.
eccezione di bloccaggio #optimistic transazione primaria
. 24
Domanda: {[update dopo setName = ?, version =? dove id =? e la versione =?] [Hibernate Master Class, 1 , 1 , 0 ]}
25.
org.hibernate.StaleObjectStateException: Riga è stato aggiornato o cancellato da un'altra transazione (o mappatura unsaved-value non è corretta): [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnBidirectionalParentOwningCollectionTest$Post# 1 ]
Il test di associazione-bambino possedere-side-genitore bidirezionale
Visualizza sorgentestampare ?
01.
tabelle #create
02.
Domanda: {[creare commento della tabella (idbigint generato da impostazione predefinita come identità (iniziare con 1 ), recensione varchar ( 255 ), bigint post_id chiave primaria, (id))] []}
03.
Domanda: {[create table posta (non idbigint nullo , nome varchar ( 255 ), la versione numero intero non nullo , chiave primaria (id))] []}
04.
Domanda: {[alter table vincolo commento aggiuntivo FK_f1sl0xkd2lucs7bve3ktt3tu5 chiave esterna (post_id) riferimenti post] []}
05.

06.
posto #insert transazione primaria
07.
Domanda: {[(???,,) inserire in valori postale (nome, versione, id)] [formazione Hibernate, 0 , 1 ]}
08.

09.
posto #SELECT transazione secondaria
10.
Domanda: {[? selectentityopti0_.idas id1_1_0_, entityopti0_.name come name2_1_0_, entityopti0_.version come version3_1_0_ dal post entityopti0_ dove entityopti0_.id =] [ 1 ]}
11.

12.
commento #insert transazione secondaria
13.
Versione #post non viene incrementato in un'operazione secondaria
14.
Domanda: {[inserire in valori commento (id, post_id, recensione) ( impostazione predefinita ,,??)] [ 1 , buon post!]}
15.
Domanda: {[? selectcount (id) da un commento in cui post_id =] [ 1 ]}
16.

17.
#update lavora a operazione primaria
. 18
Domanda: {[update dopo setName = ?, version =? dove id =? e la versione =?] [Hibernate Master Class, 1 , 1 , 0 ]}
Non tener conto di raccolta delle versioni di default
Se il valore di default che possiede sul lato raccolta delle versioni non è adatto per il vostro caso d'uso, è sempre possibile non tener conto con Hibernate [a href = "http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/ # d0e2903 "style =" font-family: inherit; font-size: 14px; font-style: inherit; font-weight: inherit; text-decoration: none; color: rgb (1, 160, 219); -webkit- tap-highlight-color: rgb (240, 29, 79); sfondo: trasparente; "] @ OptimisticLock annotazione.

Cerchiamo di non tener conto del meccanismo di versione di aggiornamento genitore predefinito per  bidirezionale associazione-madre che detiene-lato-bambino :

Visualizza sorgentestampare ?
01.
Entity (name = "post" )
02.
pubblica class post {
03.
...
04.
OneToMany (cascade = CascadeType.ALL, orphanRemoval = true )
05.
OptimisticLock (escluso = true )
. 06
private List <Commento> commenti = nuovo ArrayList <Commento> ();
07.
...
08.

09.
pubblico vuoto addComment (commento commenti) {
. 10
comment.setPost ( questo );
. 11
comments.add (commento);
12.
}
13.
}
14.

15.
Entity (name = "commenti" )
16.
pubblica class {Commento
17.
...
18.
ManyToOne
19.
JoinColumn (name = "post_id" , inseribile = falso , aggiornabile = falso )
20.
privato annuncio Pubblica;
21.
...
22.
}
Questa volta, i cambiamenti di raccolta i bambini non si innescherà un aggiornamento di versione genitore:

Visualizza sorgentestampare ?
01.
tabelle #create
02.
Domanda: {[creare commento della tabella (idbigint generato da impostazione predefinita come identità (iniziare con 1 ), recensione varchar ( 255 ), bigint post_id chiave primaria, (id))] []}
03.
Domanda: {[create table posta (non idbigint nullo , nome varchar ( 255 ), la versione numero intero non nullo , chiave primaria (id))] []}
04.
Domanda: {[create table post_comment (post_id bigint non nullo , bigint comments_id non nullo )] []}
05.
Domanda: {[]}
06.
Domanda: {[alter table vincolo commento aggiuntivo FK_f1sl0xkd2lucs7bve3ktt3tu5 chiave esterna (post_id) riferimenti post] []}
07.
Domanda: {[alter table post_comment aggiungere vincolo di chiave esterna FK_se9l149iyyao6va95afioxsrl (comments_id) riferimenti commento] []}
08.
Domanda: {[alter table post_comment aggiungere vincolo di chiave esterna FK_6o1igdm04v78cwqre59or1yj1 (post_id) riferimenti post] []}
09.

10.
posto #insert transazione primaria
11.
Domanda: {[(???,,) inserire in valori postale (nome, versione, id)] [formazione Hibernate, 0 , 1 ]}
12.

13.
posto #SELECT transazione secondaria
14.
Domanda: {[? selectentityopti0_.idas id1_1_0_, entityopti0_.name come name2_1_0_, entityopti0_.version come version3_1_0_ dal post entityopti0_ dove entityopti0_.id =] [ 1 ]}
15.
Domanda: {[selectcomments0_.post_id come post_id1_1_0_, comments0_.comments_id come comments2_2_0_, entityopti1_.idas id1_0_1_, entityopti1_.post_id come post_id3_0_1_, entityopti1_.review come review2_0_1_, entityopti2_.idas id1_1_2_, entityopti2_.name come name2_1_2_, entityopti2_.version come version3_1_2_ da post_comment comments0_ entityopti1_ joincomment interno su comments0_.comments_id = entityopti1_.idleft esterno joinpost entityopti2_ su entityopti1_.post_id = entityopti2_.idwhere comments0_.post_id =?] [ 1 ]}
16.

17.
commento #insert transazione secondaria
18.
Domanda: {[inserire nel commento (id, recensione) valori ( di default ,?)] [buon posto!]}
19.
Domanda: {[inserire in valori post_comment (post_id, comments_id) (,??)] [ 1 , 1 ]}
20.

21.
#update lavora a operazione primaria
. 22
Domanda: {[update dopo setName = ?, version =? dove id =? e la versione =?] [Hibernate Master Class, 1 , 1 , 0 ]}
Conclusione
E 'molto importante capire come le varie strutture di modellazione modelli di concorrenza impatto. Le collezioni possedere lato modifiche vengono presi in considerazione per incrementare il numero di versione principale, e si può sempre bypassare utilizzando l'  OptimisticLock   annotazione.

Codice disponibile su  GitHub .

Se avete goduto la lettura del mio articolo e state cercando l'ora di arrivare notifiche e-mail istantanee dei miei ultimi messaggi, non vi resta che seguire il mio blog .

Nessun commento:

Posta un commento

Nota. Solo i membri di questo blog possono postare un commento.