h3. Summary
When using the IDENTITY strategy and a custom SQLInsert to silently ignore duplicates:
{code:java}// MySQL @SQLInsert(sql = "INSERT IGNORE INTO custom_post(body , published_at, title) VALUES (?, ?, ?)")
// PostgreSQL @SQLInsert(sql = "INSERT INTO custom_post(body, published_at, title) VALUES (?, ?, ?) ON CONFLICT DO NOTHING"){code}
Hibernate throws PersistenceException instead of ignoring the duplicate key violation (source code: [https://github.com/behrangsa/hibernate-issues/tree/issue/conflict-resolution-identity-strategy|https://github.com/behrangsa/hibernate-issues/tree/issue/conflict-resolution-identity-strategy|smart-link]).
h3. Expected outcome
Duplicate entities not throwing a constraint violation or other types of exception.
h3. Actual outcome
Duplicate throw exception:
{code:java}javax.persistence.PersistenceException: org.hibernate.HibernateException: The database returned no natively generated identity value{code}
h3. Example
{code:java}package org.behrang.misc.hibiss;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test;
import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import java.time.LocalDate;
class Tests {
private static EntityManagerFactory mysqlFactory;
private static EntityManagerFactory postgresFactory;
@BeforeAll static void setup() { mysqlFactory = Persistence.createEntityManagerFactory("mysql");
postgresFactory = Persistence.createEntityManagerFactory("postgres"); }
@AfterAll static void teardown() { if (mysqlFactory.isOpen()) { mysqlFactory.close(); }
if (postgresFactory.isOpen()) { postgresFactory.close(); } }
@Test void testPostgres() { final EntityManager entityManager = postgresFactory.createEntityManager(); entityManager.getTransaction().begin();
final PostgresPost post1 = new PostgresPost(); post1.setTitle("Simple title (unique)"); post1.setBody("Simple post 1"); post1.setPublishedAt(LocalDate.of(2019, 1, 1));
final PostgresPost post2 = new PostgresPost(); post2.setTitle("Simple title (unique)"); post2.setBody("Simple post 2"); post2.setPublishedAt(LocalDate.of(2019, 1, 2));
entityManager.persist(post1); entityManager.persist(post2);
entityManager.getTransaction().commit(); entityManager.close(); }
@Test void testMySQL() { final EntityManager entityManager = mysqlFactory.createEntityManager(); entityManager.getTransaction().begin();
final MySQLPost post1 = new MySQLPost(); post1.setTitle("Simple title (unique)"); post1.setBody("Simple post"); post1.setPublishedAt(LocalDate.of(2019, 1, 1));
final MySQLPost post2 = new MySQLPost(); post2.setTitle("Simple title (unique)"); post2.setBody("Simple post"); post2.setPublishedAt(LocalDate.of(2019, 1, 1));
entityManager.persist(post1); entityManager.persist(post2);
entityManager.getTransaction().commit(); entityManager.close(); }
}
package org.behrang.misc.hibiss;
import org.hibernate.annotations.SQLInsert;
import javax.persistence.*; import java.time.LocalDate;
import static javax.persistence.GenerationType.IDENTITY;
@Entity @Table(name = "custom_post") @SQLInsert(sql = "INSERT INTO custom_post(body, published_at, title) VALUES (?, ?, ?) ON CONFLICT DO NOTHING") public class PostgresPost {
@Id @GeneratedValue(strategy = IDENTITY) private Long id;
@Column(unique = true) private String title;
private String body;
@Column(name = "published_at") private LocalDate publishedAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; }
public LocalDate getPublishedAt() { return publishedAt; }
public void setPublishedAt(LocalDate publishedAt) { this.publishedAt = publishedAt; } }
package org.behrang.misc.hibiss;
import org.hibernate.annotations.SQLInsert;
import javax.persistence.*; import java.time.LocalDate;
import static javax.persistence.GenerationType.IDENTITY;
@Entity @Table(name = "custom_post") @SQLInsert(sql = "INSERT IGNORE INTO custom_post(body, published_at, title) VALUES (?, ?, ?)") public class MySQLPost {
@Id @GeneratedValue(strategy = IDENTITY) private Long id;
@Column(unique = true) private String title;
private String body;
@Column(name = "published_at") private LocalDate publishedAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; }
public LocalDate getPublishedAt() { return publishedAt; }
public void setPublishedAt(LocalDate publishedAt) { this.publishedAt = publishedAt; } }{code}
Both tests fail:
{code:java}javax.persistence.PersistenceException: org.hibernate.HibernateException: The database returned no natively generated identity value
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:709) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:689) at org.behrang.misc.hibiss.Tests.testMySQL(Tests.java:75) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:515) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:105) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71) at java.util.ArrayList.forEach(ArrayList.java:1257) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71) at java.util.ArrayList.forEach(ArrayList.java:1257) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220) at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188) at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) Caused by: org.hibernate.HibernateException: The database returned no natively generated identity value at org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity(IdentifierGeneratorHelper.java:75) at org.hibernate.dialect.identity.GetGeneratedKeysDelegate.executeAndExtract(GetGeneratedKeysDelegate.java:61) at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:43) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3106) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3699) at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:84) at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645) at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282) at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263) at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317) at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:335) at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:292) at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:198) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:128) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:192) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:108) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:703) ... 41 more
javax.persistence.PersistenceException: org.hibernate.HibernateException: The database returned no natively generated identity value
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:709) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:689) at org.behrang.misc.hibiss.Tests.testPostgres(Tests.java:53) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:515) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:105) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71) at java.util.ArrayList.forEach(ArrayList.java:1257) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71) at java.util.ArrayList.forEach(ArrayList.java:1257) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220) at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188) at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) Caused by: org.hibernate.HibernateException: The database returned no natively generated identity value at org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity(IdentifierGeneratorHelper.java:75) at org.hibernate.dialect.identity.GetGeneratedKeysDelegate.executeAndExtract(GetGeneratedKeysDelegate.java:61) at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:43) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3106) at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3699) at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:84) at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645) at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282) at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263) at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317) at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:335) at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:292) at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:198) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:128) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:192) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:108) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:703) ... 41 more{code}
|
|