JPA EntityManager: навіщо використовувати persist () над злиттям ()?


951

EntityManager.merge() може вставляти нові об’єкти та оновлювати існуючі.

Навіщо хотіти використовувати persist()(які можуть створювати лише нові об’єкти)?


13
techblog.bozho.net/?p=266 пов’язано
Божо

2
Якщо вам подобаються діаграми. Посилайтеся на це: spitballer.blogspot.in/2010/04/…
RBz

Відповіді:


1615

Будь-який спосіб додасть сутність до PersistentContext, різниця полягає в тому, що ви робите з сутністю згодом.

Persist приймає екземпляр сутності, додає його до контексту і робить цей екземпляр керованим (тобто майбутні оновлення сутності будуть відстежуватися).

Злиття повертає керований екземпляр, до якого була об'єднана держава. Це повертає щось, що існує в PersistenceContext, або створює новий екземпляр вашої сутності. У будь-якому випадку він скопіює стан із наданої сутності та поверне керовану копію. Екземпляр, який ви передасте, не буде керований (будь-які внесені вами зміни не будуть частиною транзакції - якщо ви знову не зателефонуєте злити). Ви можете скористатися поверненим екземпляром (керованим).

Можливо, допоможе приклад коду.

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.


3
@dma_k: Схоже, ви використовуєте сплячий режим. Я менш знайомий зі сплячим режимом, ніж JPA - але в JPA, якщо ви зателефонуєте EntityManager.persist () і переходите до відокремленої сутності, ви: Можливо, я тут неправильно зрозумів питання?
Майк

49
Ця відповідь може бути вдосконалена, якщо вона також охоплює випадки, коли об'єднання / збереження суб'єкта господарювання вже існує в стійкому контексті (або принаймні зрозуміло, що воно описує поведінку лише тоді, коли існування / об'єднання об'єкта вже не існує)
Генрі

2
Чи є один метод більш ефективним? Можливо merge, повна копія об'єкта, перш ніж керувати ним, уражає продуктивність?
Кевін Мередіт

2
А що з ідс? Якщо я маю, чи @GeneratedIdможу я це отримати у сценарії 2?
rascio

7
Майк: "Об'єднання створює новий екземпляр ...": це не завжди так. Якщо EntityManager знайде вже керовану сутність у її контексті, він повертає цей екземпляр (після оновлення полів). Будь ласка, відредагуйте свою відповідь, тоді я проголосую за неї.
Хері

181

Персист і злиття мають дві різні цілі (вони взагалі не є альтернативою).

(відредаговано для розширення інформації про відмінності)

зберігаються:

  • Вставте в базу даних новий реєстр
  • Приєднайте об’єкт до менеджера сутностей.

злиття:

  • Знайдіть доданий об’єкт з тим же ідентифікатором та оновіть його.
  • Якщо існує оновлення та повернення вже приєднаного об'єкта.
  • Якщо його немає, вставіть новий реєстр до бази даних.

зберегти () ефективність:

  • Це може бути ефективніше для вставки нового реєстру в базу даних, ніж об'єднання ().
  • Він не дублює оригінальний об'єкт.

перманентна () семантика:

  • Це гарантує, що ви вставляєте та не оновлюєтесь помилково.

Приклад:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

Таким чином існує лише 1 доданий об'єкт для будь-якого реєстру в адміністраторі сутностей.

merge () для об'єкта з ідентифікатором - це щось на зразок:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

Хоча якщо підключення до MySQL merge () може бути настільки ж ефективним, як і persist (), використовуючи виклик INSERT з опцією ON DUPLICATE KEY UPDATE, JPA є програмуванням дуже високого рівня, і ви не можете припустити, що це буде повсюдно.


Можете чи ви назвати випадок , коли це не діє , щоб замінити em.persist(x)з x = em.merge(x)?
Аарон Дігулла

20
persist () може кинути EntityExistsException. Якщо ви хочете бути впевнені, що ваш код робить вставку, а не оновлення даних, потрібно використовувати зберегти.
Хосеп Панадеро

1
merge()також може кинутиEntityExistsException
Шон

1
@ Ніхто Це може, тому що це є RuntimeException, але це не згадується в Javadoc.
Мартін

154

Якщо ви використовуєте призначений генератор, використання об'єднання замість збереження може призвести до надмірного оператора SQL , що впливає на продуктивність.

Крім того, виклик злиття для керованих об'єктів також є помилкою, оскільки керованими сутностями автоматично керує Hibernate, а їх стан синхронізується із записом бази даних брудним механізмом перевірки при змиванні контенту збереження .

Щоб зрозуміти, як все це працює, спершу слід знати, що Hibernate змінює мислення розробника з операторів SQL на переходи стану сутності .

Після того, як суб'єктом активно керує Hibernate, всі зміни будуть автоматично передані до бази даних.

Монітор сплячого режиму в даний час приєднаних осіб. Але для того, щоб суб'єкт господарювання отримав управління, він повинен знаходитись у правильній державі.

Щоб краще зрозуміти переходи стану JPA, ви можете візуалізувати таку схему:

Переходи стану сутності JPA

Або якщо ви використовуєте специфічний API Hibernate:

Переходи в стан сплячої сутності

Як показано на наведених діаграмах, сутність може перебувати в одному з наступних чотирьох станів:

  • Нове (тимчасове)

    Новостворений об’єкт, який ніколи не асоціювався зі сплячим режимом Session(ака Persistence Context) і не відображається в жодному рядку таблиці баз даних, вважається в новому (перехідному) стані.

    Щоб зберегтись, нам потрібно або явно викликати EntityManager#persistметод, або скористатися механізмом транзитивної стійкості.

  • Постійний (керований)

    Послідовна сутність була пов’язана з рядком таблиці баз даних, і нею керує поточний контекст постійності. Будь-які зміни, внесені до такої сутності, будуть виявлені та розповсюджені до бази даних (під час часу сесії). У режимі Hibernate нам більше не потрібно виконувати заяви INSERT / UPDATE / DELETE. Hibernate використовує стиль роботи транзакційного запису, і зміни синхронізуються в останній відповідальний момент, під час поточного Sessionпотоку.

  • Окремі

    Після закриття поточного контексту постійності всі раніше керовані об'єкти стають відокремленими. Послідовні зміни більше не відстежуються, і автоматична синхронізація бази даних не відбуватиметься.

    Щоб приєднати відокремлену сутність до активної сплячої сесії, ви можете вибрати один із наступних варіантів:

    • Повторне з'єднання

      Hibernate (але не JPA 2.1) підтримує повторне з'єднання за допомогою методу оновлення Session #. Гібернатна сесія може асоціювати лише один об'єкт Entity для заданого рядка бази даних. Це пояснюється тим, що контекст стійкості діє як кеш пам'яті (кеш першого рівня), і лише одне значення (сутність) пов'язане з заданим ключем (тип сутності та ідентифікатор бази даних). Суб'єкт можна повторно приєднати, лише якщо немає жодного іншого об'єкта JVM (відповідного тому ж рядку бази даних), який уже пов'язаний з поточним сеансом сплячого режиму.

    • Злиття

    Злиття збирається скопіювати відокремлений стан об'єкта (джерело) в керований екземпляр об'єкта (призначення). Якщо об'єднання, що зливається, не має еквівалента в поточній сесії, його буде вилучено з бази даних. Екземпляр від'єднаного об'єкта продовжить залишатися від'єднаним навіть після операції злиття.

  • Вилучено

    Хоча JPA вимагає, щоб лише керованим об'єктам було дозволено видаляти, Hibernate також може видалити відокремлені об'єкти (але лише за допомогою виклику методу видалення сесії #). Видалене об'єкт призначено лише для видалення, а фактичний оператор DELETE бази даних буде виконуватися під час часу сесії.


ви можете подивитися stackoverflow.com/questions/46214322/… ?
gstackoverflow

@gstackoverflow Відповідь, яку ви отримали, є правильною. Щоб отримати детальнішу інформацію, перегляньте цю статтю чи мою книгу, Високопродуктивна наполегливість Java .
Влад Михальча

Таким чином, немає можливості змінити порядок роботи на orphanremoval = true?
gstackoverflow

Ваша стаття про замовлення на експлуатацію у звичайному випадку. Моє запитання, специфічне для orphanRemoval
gstackoverflow,

1
Справа в тому, що неможливо пояснити сплячий стан такою діаграмою. Чому не можете змити сеанс після від'єднання? Що відбувається, коли ви намагаєтеся зберегти суть, що вже зберігається? Чому така поведінка рум'яна відрізняється, коли йдеться про збереження та збереження? Є 1000 таких питань, до яких ніхто не має чіткої логіки.
GingerBeer

37

Я помітив, що коли я використовував em.merge, я отримував SELECTзаяву для кожного INSERT, навіть коли не було поля, яке JPA створює для мене - поле первинного ключа - це UUID, який я встановив сам. Я перейшов em.persist(myEntityObject)і отримав лише INSERTзаяви тоді.


3
Має сенс, оскільки ви присвоюєте ідентифікатори, і контейнер JPA не має уявлення, звідки ви це отримали. Існує (невеликий) шанс, що об'єкт вже існує в базі даних, наприклад, у сценарії, коли кілька додатків записують в одну базу даних.
Аарон Дігулла

Я стикався з подібним питанням merge(). У мене була база даних PostgreSQL зі складним виглядом : перегляд агрегованих даних з кількох таблиць (таблиці мали однакову структуру, але різні назви). Так JPA намагався зробити merge(), але насправді JPA спершу зробив SELECT(база даних завдяки налаштуванням перегляду могла повернути кілька записів з однаковим первинним ключем з різних таблиць!), Потім JPA (Hibernate - це впровадження) не вдалося: є кілька записів з одним ключем ( org.hibernate.HibernateException: More than one row with the given identifier was found). У моєму випадку persist()мені допомогли.
flaz14

29

Специфікація JPA говорить про наступне persist().

Якщо X є відокремленим об'єктом, він EntityExistsExceptionможе бути кинутий, коли викликається тривала операція, EntityExistsExceptionабо інша PersistenceExceptionможе бути кинута під час потоку або введення часу.

Тому використання persist()було б придатним, коли об'єкт не повинен бути відокремленим об'єктом. Можливо, ви хочете, щоб код кинув, PersistenceExceptionщоб він швидко вийшов з ладу.

Хоча специфікація незрозуміла , persist()може встановити @GeneratedValue @Idоб'єкт. merge()однак повинен мати об'єкт із @Idвже створеним.


5
+1 для " merge()однак повинен мати об'єкт із @Id вже створеним . " Щоразу, коли EntityManager не знайде значення для поля ідентифікатора об'єкта, воно зберігається (вставляється) в БД.
Омар

Я не зрозумів це спочатку, як мені не було зрозуміло про штати. Сподіваюся, це допомагає комусь, як це було для мене. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz

1
@GeneratedValue не має різних наслідків для злиття () та збереження ()
SandeepGodara

17

Ще кілька подробиць про злиття, які допоможуть вам використовувати об'єднання над збереженням:

Повернення керованого екземпляра, відмінного від початкового об'єкта, є важливою частиною процесу злиття. Якщо екземпляр суб'єкта господарювання з тим самим ідентифікатором вже існує в контексті збереження, провайдер перезаписать його стан у стан об'єднання, яке об'єднується, але керована версія, що існувала, вже повинна бути повернена клієнту, щоб він міг бути б / в. Якщо постачальник не оновив екземпляр Співробітника в контексті постійності, будь-які посилання на цей екземпляр стануть невідповідними новому стану, в якому об'єднано.

Коли Merge () викликається новим об'єктом, він поводиться аналогічно операції persist (). Це додає сутність до контексту стійкості, але замість того, щоб додати оригінальний екземпляр сутності, він створює нову копію та керує цим екземпляром. Копія, що створюється операцією merge (), зберігається так, ніби на неї було викликано метод persist ().

За наявності зв’язків операція merge () спробує оновити керовану сутність, щоб вказати на керовані версії сутностей, на які посилається відокремлена сутність. Якщо суб'єкт господарювання має відношення до об'єкта, який не має стійкої ідентичності, результат операції злиття не визначений. Деякі провайдери можуть дозволити керованій копії вказувати на непостійний об'єкт, тоді як інші можуть негайно кинути виняток. Операція злиття () може бути необов'язково каскадована в цих випадках, щоб запобігти виникненню виключення. Пізніше в цьому розділі ми розглянемо каскадування операції злиття (). Якщо об'єднання об'єднано вказує на видалену сутність, буде викинуто виняток IllegalArgumentException.

Відносини ледачих навантажень є особливим випадком в операції злиття. Якщо відносини з ледачим завантаженням не були запущені на об'єкт до його від'єднання, це відношення буде ігноровано при об'єднанні сутності. Якщо зв'язок був запущений під час керування, а потім встановлений на нуль, поки сутність від'єднана, керована версія сутності також очистить відносини під час злиття ".

Вся вищенаведена інформація була взята з "Pro JPA 2 Освоєння Java ™ Persistent API" Майком Кітом та Мерріком Шнікаріолом. Глава 6. Відділення та злиття секцій. Ця книга насправді є другою книгою, присвяченою авторами проекту JPA. Ця нова книга має багато нової інформації, ніж колишня. Я дійсно рекомендував прочитати цю книгу для тих, хто буде серйозно пов’язаний із JPA. Мені шкода, що анонімно опублікував свою першу відповідь.


17

Є ще кілька відмінностей між mergeі persist(я перелічу ще раз ті, які вже розміщені тут):

D1. mergeне робить передану суть керованою, а повертає інший керований екземпляр. persistз іншого боку зробить передану особу керованою:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2. Якщо ви видалите сутність, а потім вирішите зберегти об'єкт назад, ви можете зробити це лише з persist (), тому що mergeбуде кинути IllegalArgumentException.

D3. Якщо ви вирішили піклуватися про свої ідентифікатори вручну (наприклад, використовуючи UUID), тоді merge операція запустить наступні SELECTзапити, щоб шукати існуючі об'єкти з цим ідентифікатором, хоча persistці запити можуть не потребувати.

D4. Бувають випадки, коли ви просто не довіряєте коду, який викликає ваш код, і для того, щоб переконатися, що дані не оновлюються, а, скоріше, вставлені, ви повинні використовувати persist.


8

Я отримував винятки у форматі 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!!

7

Це пояснення я знайшов у документі Hibernate просвітлюючи, оскільки вони містять випадок використання:

Використання та семантика злиття () видається заплутаною для нових користувачів. По-перше, до тих пір, поки ви не намагаєтеся використовувати стан об'єктів, завантажених в один менеджер об'єктів, в інший новий менеджер сутності, вам взагалі не потрібно використовувати merge () . Деякі цілі програми ніколи не використовуватимуть цей метод.

Зазвичай merge () використовується в наступному сценарії:

  • Додаток завантажує об'єкт у перший менеджер сутності
  • об'єкт передається до шару презентації
  • деякі об’єкти вносяться до модифікацій
  • об'єкт передається назад до рівня бізнес-логіки
  • додаток зберігає ці зміни, викликаючи merge () у другому менеджері сутності

Ось точний семантичний злиття ():

  • якщо є керований екземпляр з тим самим ідентифікатором, який в даний час пов'язаний з контекстом постійності, скопіюйте стан даного об'єкта на керований екземпляр
  • якщо в даний час немає керованого екземпляра, пов'язаного з контекстом стійкості, спробуйте завантажити його з бази даних або створити новий керований екземпляр
  • керований екземпляр повертається
  • даний екземпляр не стає асоційованим із контекстом стійкості, він залишається відокремленим і зазвичай відкидається

Від: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html


6

Переглядаючи відповіді, деякі деталі відсутні стосовно "Cascade" та генерації ідентифікаторів. Дивіться питання

Також варто згадати, що ви можете мати окремі Cascadeпримітки для об'єднання та збереження: Cascade.MERGEі Cascade.PERSISTякі будуть оброблятися відповідно до використовуваного методу.

Специфікація - твій друг;)


6

JPA - це безперечно велике спрощення в області корпоративних додатків, побудованих на платформі Java. Як розробник, який повинен був впоратися з тонкощами старих бобів сутності в J2EE, я бачу включення JPA до специфікацій Java EE як великий стрибок уперед. Однак, заглиблюючись у деталі JPA, я знаходжу речі не такі прості. У цій статті я розглядаю порівняння методів злиття та збереження EntityManager, поведінка яких перекривається може спричинити плутанину не тільки у новачків. Крім того, я пропоную узагальнення, яке розглядає обидва методи як окремі випадки комбінування більш загального методу.

Суттєві утворення

На відміну від методу злиття, метод персистування є досить простим та інтуїтивним. Найпоширеніший сценарій використання персистуючого методу можна підсумувати наступним чином:

"Новостворений екземпляр класу сутності передається методу" Персист ". Після повернення цього методу управління об'єктом керується та планується для вставки в базу даних. Це може статися під час або до того, як транзакція буде здійснена, або коли буде викликаний метод флеш. Якщо суб'єкт господарювання посилається на іншу сутність через зв'язок, позначений каскадною стратегією PERSIST, ця процедура також застосовується до неї. "

введіть тут опис зображення

У специфікаціях детальніше йдеться про деталі, проте запам'ятовування їх не є вирішальним, оскільки ці деталі охоплюють лише більш-менш екзотичні ситуації.

Об'єднання об'єднань

У порівнянні зі стійкими, опис поведінки злиття не так просто. Немає головного сценарію, як це у випадку збереження, і програміст повинен запам'ятати всі сценарії, щоб написати правильний код. Мені здається, що розробники проекту JPA хотіли створити якийсь метод, основним завданням якого буде поводження з відокремленими об'єктами (як протилежний персистентному методу, який стосується насамперед новостворених структур). Основним завданням методу злиття є перенесення стану з некерована сутність (передається як аргумент) своєму керованому колезі в контексті стійкості. Це завдання, однак, ділиться далі на кілька сценаріїв, які погіршують зрозумілість загальної поведінки методу.

Замість повторення абзаців зі специфікації JPA я підготував блок-схему, яка схематично зображує поведінку методу злиття:

введіть тут опис зображення

Отже, коли я повинен використовувати наполегливі та коли злиття?

зберігаються

  • Ви хочете, щоб метод завжди створював нову сутність і ніколи не оновлює об'єкт. В іншому випадку метод викидає виняток як наслідок порушення унікальності первинного ключа.
  • Пакетні процеси, які обробляють об'єкти в стані (див. Схему шлюзу).
  • Оптимізація продуктивності

злиття

  • Ви хочете, щоб метод або вставляв, або оновлював об'єкт у базі даних.
  • Ви хочете обробляти об'єкти без громадянства (об'єкти передачі даних у службах)
  • Ви хочете вставити нову сутність, яка може мати посилання на іншу сутність, яка може, але може бути ще не створена (відносини повинні бути позначені МЕРГО). Наприклад, вставлення нової фотографії з посиланням на новий або попередній альбом.

У чому різниця між керуванням E та Чи містить ПК керована версія E?
GingerBeer

5

Сценарій X:

Таблиця: Spitter (One), Table: Spittles (багато) (Spittles є власником відносин з FK: spitter_id)

Цей сценарій призводить до економії: Spitter і обидва Spittles як би належать одному Spitter.

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

Сценарій Y:

Це врятує Spitter, врятує 2 Spittles. Але вони не будуть посилатися на того ж Spitter!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

1
Шпигун - це предмет, узятий із книги "Весна в дії" третього видання Graig Walls. Бридці - це люди, які щось говорять, і їх Шпилі - це те, що вони насправді говорять. Тож у Шпітера є багато колючок, значить, у нього є список струн.
Джордж Папатеодору

1
Ви могли б використати приклад, який трохи читабельніше, не читаючи Spring in Action ...
wonderb0lt

1
Вам насправді не потрібно знати, що таке колючка чи спирт, оскільки вгорі написано, що Шпітер - це стіл, спітер - ще один стіл, якому належить .. це та інше ...
Джордж Папатеодору

3

Ще одне зауваження:

merge()буде піклуватися про автоматично згенерований ідентифікатор (перевірений на IDENTITYта SEQUENCE), коли запис із таким ідентифікатором вже існує у вашій таблиці. У такому випадку merge()спробують оновити запис. Якщо, однак, ідентифікатор відсутній або не відповідає жодним існуючим записам, merge()він повністю ігнорує його і попросить db виділити новий. Іноді це джерело безлічі помилок. Не використовуйте merge()для присвоєння ідентифікатора для нового запису.

persist()з іншого боку, ніколи не дозволить вам навіть передати ідентифікатор. Це негайно вийде з ладу. У моєму випадку це:

Викликано: org.hibernate.PersistentObjectException: відокремлений об'єкт передається для збереження

hibernate-jpa javadoc має підказку:

Кидає : javax.persistence.EntityExistsException - якщо сутність вже існує. (Якщо сутність вже існує, EntityExistsException може бути кинуто, коли викликається збережена операція, або EntityExistsException або інший PersistentException може бути кинутий під час флеш-трансляції або фіксації часу.)


2
Якщо ви не використовуєте автоматично створені ідентифікатори, вам доведеться вручну надати ідентифікатору новому об'єкту. persist()не скаржиться, що має ідентифікатор, він скаржиться лише тоді, коли щось із таким самим ідентифікатором вже є в базі даних.
ч.

1

Можливо, ви завітали сюди за порадами щодо того, коли використовувати персист і коли використовувати злиття . Я думаю, що це залежить від ситуації: наскільки ймовірно, що вам потрібно створити новий запис і наскільки важко отримати збережені дані.

Припустимо, ви можете використовувати природний ключ / ідентифікатор.

  • Дані потрібно зберігати, але раз у раз існує запис і вимагається оновлення. У цьому випадку ви можете спробувати зберегти, і якщо він кидає EntityExistsException, ви перегляньте його та об'єднайте дані:

    спробуйте {entitManager.persist (сутність)}

    catch (виняток EntityExistsException) {/ * вилучення та об'єднання * /}

  • Постійні дані потрібно оновлювати, але час від часу для цих даних ще немає записів. У такому випадку ви перегляньте його та зробіть збереження, якщо організація відсутня:

    entitet = entitetManager.find (ключ);

    if (entitet == null) {entitManager.persist (сутність); }

    else {/ * злиття * /}

Якщо у вас немає природного ключа / ідентифікатора, вам буде складніше зрозуміти, чи існує сутність чи ні, або як її шукати.

Злиття також може бути вирішено двома способами:

  1. Якщо зміни зазвичай невеликі, застосуйте їх до керованої сутності.
  2. Якщо зміни є загальними, скопіюйте ідентифікатор із збереженої сутності, а також незмінні дані. Потім зателефонуйте EntityManager :: merge (), щоб замінити старий вміст.

0

persist (сутність) слід використовувати з абсолютно новими об'єктами, щоб додати їх до БД (якщо сутність вже існує в БД, буде кинути EntityExistsException).

слід об'єднати об'єднання (сутність), щоб повернути сутність в контекст стійкості, якщо сутність була від'єднана та змінена.

Можливо, зберігається генерує оператор INSERT sql та об'єднує оператор UPDATE sql (але я не впевнений).


Це неправильно. Якщо ви викликаєте merge (e) на новий e, він повинен зберігатися.
Pedro Lamarão


Із специфікації JPA версії 2.1, розділ 3.2.7.1, друга куля: "Якщо X - це новий екземпляр об'єкта, створюється новий керований екземпляр об'єкта X ', а стан X копіюється в новий керований екземпляр об'єкта X". "
Педро Ламарао
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.