JPA EntityManager: Γιατί να χρησιμοποιήσετε την persist() αντί της merge();

Η EntityManager.merge() μπορεί να εισάγει νέα αντικείμενα και να ενημερώνει τα υπάρχοντα.

Γιατί θα ήθελε κανείς να χρησιμοποιήσει την persist() (η οποία μπορεί να δημιουργήσει μόνο νέα αντικείμενα);

Λύση

Και οι δύο τρόποι προσθέτουν μια οντότητα σε ένα PersistenceContext, η διαφορά έγκειται στο τι κάνετε με την οντότητα μετά.

Το Persist παίρνει μια περίπτωση οντότητας, την προσθέτει στο πλαίσιο και καθιστά αυτή την περίπτωση διαχειρίσιμη (δηλαδή οι μελλοντικές ενημερώσεις της οντότητας θα παρακολουθούνται).

Η συγχώνευση δημιουργεί μια νέα περίπτωση της οντότητάς σας, αντιγράφει την κατάσταση από την παρεχόμενη οντότητα και καθιστά το νέο αντίγραφο διαχειρίσιμο. Η περίπτωση που παραδίδετε δεν θα είναι διαχειρίσιμη (οποιεσδήποτε αλλαγές κάνετε δεν θα είναι μέρος της συναλλαγής - εκτός αν καλέσετε ξανά τη συγχώνευση).

Ίσως ένα παράδειγμα κώδικα βοηθήσει.

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

Τα σενάρια 1 και 3 είναι περίπου ισοδύναμα, αλλά υπάρχουν ορισμένες περιπτώσεις όπου θα θέλατε να χρησιμοποιήσετε το σενάριο 2.

Σχόλια (12)

Παρατήρησα ότι όταν χρησιμοποίησα το em.merge, πήρα μια δήλωση SELECT για κάθε INSERT, ακόμη και όταν δεν υπήρχε πεδίο που η JPA δημιουργούσε για μένα - το πεδίο του πρωτεύοντος κλειδιού ήταν ένα UUID που όρισα ο ίδιος. Άλλαξα σε em.persist(myEntityObject) και πήρα μόνο δηλώσεις INSERT τότε.

Σχόλια (2)

Έπαιρνα εξαιρέσεις lazyLoading στην οντότητά μου επειδή προσπαθούσα να αποκτήσω πρόσβαση σε μια συλλογή που είχε φορτωθεί νωχελικά και βρισκόταν σε περίοδο λειτουργίας.

Αυτό που έκανα ήταν σε μια ξεχωριστή αίτηση, να ανακτήσω την οντότητα από τη σύνοδο και στη συνέχεια να προσπαθήσω να προσπελάσω μια συλλογή στη σελίδα jsp μου, η οποία ήταν προβληματική.

Για να το ανακουφίσω αυτό, ενημέρωσα την ίδια οντότητα στον ελεγκτή μου και την πέρασα στην jsp μου, αν και φαντάζομαι ότι όταν την αποθήκευσα εκ νέου στη σύνοδο, θα είναι επίσης προσβάσιμη μέσω του SessionScope και δεν θα πετάξει ένα LazyLoadingException, μια τροποποίηση του παραδείγματος 2:

Το ακόλουθο έχει δουλέψει για μένα:

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!
Σχόλια (0)