diff --git a/README.md b/README.md index 68b85be..a41523e 100644 --- a/README.md +++ b/README.md @@ -24,20 +24,22 @@ Can be useful in environments where DDL statements cannot be executed from appli } ``` -1. Create a shared global temporary table, e.g. +2. Create a shared global temporary table, e.g. ``` - create global temporary table HT_TEMP_IDS (ID CHAR(36), ENTITY_NAME VARCHAR(100)); - ``` -2. Set the following Hibernate properties: + create global temporary table HT_TEMP_IDS (ID CHAR(36), ENTITY_NAME VARCHAR(100)); + ``` +3. Set the following Hibernate properties: ``` configuration.setProperty(AvailableSettings.HQL_BULK_ID_STRATEGY, SingleGlobalTemporaryTableBulkIdStrategy.class.getName()); configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.TABLE, "HT_TEMP_IDS"); - configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.DISCRIMINATOR_COLUMN, "ENTITY_NAME"); + configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.ID_COLUMN, "ID"); // This is new in 1.2 + configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.DISCRIMINATOR_COLUMN, "ENTITY_NAME"); // This is default in 1.2 configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.CLEAN_ROWS, "true"); ``` ## Release history +* 1.2 to be released * 1.1 released 2016-09-29. Built for Hibernate 5.2 * 1.0 released 2016-09-29. Built for Hibernate 5.1 and JDK 1.8 diff --git a/pom.xml b/pom.xml index e03c685..d5637d7 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.grimsa.hibernate single-table-bulk-id-strategy - 1.1 + 1.2-SNAPSHOT ${project.groupId}:${project.artifactId} Single Table Bulk ID Strategy for Hibernate diff --git a/src/main/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategy.java b/src/main/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategy.java index 8bb4daa..7eefff8 100644 --- a/src/main/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategy.java +++ b/src/main/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategy.java @@ -40,7 +40,12 @@ public class SingleGlobalTemporaryTableBulkIdStrategy implements MultiTableBulkI public static final String TABLE = "hibernate.hql.bulk_id_strategy.single_global_temporary.table"; /** - * Column to be used as entity discriminator + * Column to be used as entity id. Defaults to {@code ID} + */ + public static final String ID_COLUMN = "hibernate.hql.bulk_id_strategy.single_global_temporary.id_column"; + + /** + * Column to be used as entity discriminator. Defaults to {@code ENTITY_NAME} */ public static final String DISCRIMINATOR_COLUMN = "hibernate.hql.bulk_id_strategy.single_global_temporary.discriminator_column"; @@ -50,6 +55,7 @@ public class SingleGlobalTemporaryTableBulkIdStrategy implements MultiTableBulkI public static final String CLEAN_ROWS = "hibernate.hql.bulk_id_strategy.single_global_temporary.clean_rows"; private String fullyQualifiedTableName; + private String idColumn; private String discriminatorColumn; private boolean cleanRows; @@ -57,7 +63,8 @@ public class SingleGlobalTemporaryTableBulkIdStrategy implements MultiTableBulkI public void prepare(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess, MetadataImplementor metadata, SessionFactoryOptions sessionFactoryOptions) { ConfigurationService configService = sessionFactoryOptions.getServiceRegistry().getService(ConfigurationService.class); this.fullyQualifiedTableName = Objects.requireNonNull(configService.getSetting(TABLE, String.class, null), "Property " + TABLE + " must be set."); - this.discriminatorColumn = Objects.requireNonNull(configService.getSetting(DISCRIMINATOR_COLUMN, String.class, null), "Property " + DISCRIMINATOR_COLUMN + " must be set."); + this.idColumn = configService.getSetting(ID_COLUMN, String.class, "ID"); + this.discriminatorColumn = configService.getSetting(DISCRIMINATOR_COLUMN, String.class, "ENTITY_NAME"); this.cleanRows = configService.getSetting(CLEAN_ROWS, StandardConverters.BOOLEAN, false); } @@ -69,18 +76,17 @@ public void release(JdbcServices jdbcServices, JdbcConnectionAccess connectionAc public UpdateHandler buildUpdateHandler(SessionFactoryImplementor factory, HqlSqlWalker walker) { final UpdateStatement updateStatement = (UpdateStatement) walker.getAST(); final Queryable targetedPersister = updateStatement.getFromClause().getFromElement().getQueryable(); - final String discriminator = generateDiscriminatorValue(targetedPersister); return new TableBasedUpdateHandlerImpl(factory, walker, this::getTableName) { @Override protected String generateIdSubselect(Queryable persister, IdTableInfo idTableInfo) { - return super.generateIdSubselect(persister, idTableInfo) + " where " + discriminatorColumn + "='" + discriminator + '\''; + return getTempTableIdSubselect(idTableInfo, targetedPersister); } @Override protected void addAnyExtraIdSelectValues(SelectValues selectClause) { - selectClause.addColumn(null, '\'' + discriminator + '\'', discriminatorColumn); + addExtraIdSelectValues(targetedPersister, selectClause); } @Override @@ -96,18 +102,17 @@ protected void releaseFromUse(Queryable persister, SharedSessionContractImplemen public DeleteHandler buildDeleteHandler(SessionFactoryImplementor factory, HqlSqlWalker walker) { final DeleteStatement deleteStatement = (DeleteStatement) walker.getAST(); final Queryable targetedPersister = deleteStatement.getFromClause().getFromElement().getQueryable(); - final String discriminator = generateDiscriminatorValue(targetedPersister); return new TableBasedDeleteHandlerImpl(factory, walker, this::getTableName) { @Override protected String generateIdSubselect(Queryable persister, IdTableInfo idTableInfo) { - return super.generateIdSubselect(persister, idTableInfo) + " where " + discriminatorColumn + "='" + discriminator + '\''; + return getTempTableIdSubselect(idTableInfo, targetedPersister); } @Override protected void addAnyExtraIdSelectValues(SelectValues selectClause) { - selectClause.addColumn(null, '\'' + generateDiscriminatorValue(targetedPersister) + '\'', discriminatorColumn); + addExtraIdSelectValues(targetedPersister, selectClause); } @Override @@ -140,6 +145,16 @@ protected String generateDiscriminatorValue(Queryable persister) { return persister.getEntityName(); } + protected String getTempTableIdSubselect(IdTableInfo idTableInfo, Queryable persister) { + return "select " + idColumn + + " from " + idTableInfo.getQualifiedIdTableName() + + " where " + discriminatorColumn + "='" + generateDiscriminatorValue(persister) + "'"; + } + + protected void addExtraIdSelectValues(final Queryable targetedPersister, SelectValues selectClause) { + selectClause.addColumn(null, '\'' + generateDiscriminatorValue(targetedPersister) + '\'', discriminatorColumn); + } + private String getTableName() { return fullyQualifiedTableName; } diff --git a/src/test/java/lt/grimsa/hibernate/id/AbstractSingleGlobalTemporaryTableBulkIdStrategyTest.java b/src/test/java/lt/grimsa/hibernate/id/AbstractSingleGlobalTemporaryTableBulkIdStrategyTest.java new file mode 100644 index 0000000..d98f43c --- /dev/null +++ b/src/test/java/lt/grimsa/hibernate/id/AbstractSingleGlobalTemporaryTableBulkIdStrategyTest.java @@ -0,0 +1,96 @@ +package lt.grimsa.hibernate.id; + +import model.TestEntities.*; +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.spi.LoggingEvent; +import org.hibernate.Transaction; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +import static org.junit.Assert.assertTrue; + +public abstract class AbstractSingleGlobalTemporaryTableBulkIdStrategyTest extends BaseCoreFunctionalTestCase { + private Logger sqlLogger = Logger.getLogger("org.hibernate.SQL"); + private MessageCapturingAppender sqlAppender; + private boolean ddlExecuted; + + @Override + protected void configure(Configuration configuration) { + configuration.setProperty(AvailableSettings.HQL_BULK_ID_STRATEGY, SingleGlobalTemporaryTableBulkIdStrategy.class.getName()); + configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.TABLE, "HT_TEMP_IDS"); + configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.DISCRIMINATOR_COLUMN, "ENTITY_NAME"); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{Animal.class, Mammal.class, Reptile.class, Human.class, Dog.class}; + } + + @Override + protected void prepareTest() throws Exception { + sqlAppender = new MessageCapturingAppender(); + openSession(); + if (!ddlExecuted) { + doInTransaction(() -> session.createNativeQuery("create global temporary table HT_TEMP_IDS (" + + getIdColumnName() + " CHAR(36), " + + getEntityColumnName() + " VARCHAR(100))") + .executeUpdate()); + ddlExecuted = true; + } + sqlLogger.addAppender(sqlAppender); + } + + protected String getIdColumnName() { + return "ID"; + } + + protected String getEntityColumnName() { + return "ENTITY_NAME"; + } + + @Override + protected void cleanupTest() throws Exception { + sqlLogger.removeAppender(sqlAppender); + } + + protected void doInTransaction(Runnable runnable) { + Transaction transaction = session.beginTransaction(); + runnable.run(); + transaction.commit(); + } + + protected void doWithLogging(Runnable runnable) { + sqlLogger.setLevel(Level.DEBUG); + runnable.run(); + sqlLogger.setLevel(Level.OFF); + } + + protected void verify(Predicate> sqlLogConsumingPredicate) { + assertTrue(sqlLogConsumingPredicate.test(sqlAppender.log)); + } + + private static class MessageCapturingAppender extends AppenderSkeleton { + private final List log = new ArrayList<>(); + + @Override + protected void append(LoggingEvent event) { + log.add(event.getRenderedMessage()); + } + + @Override + public boolean requiresLayout() { + return false; + } + + @Override + public void close() { + } + } +} \ No newline at end of file diff --git a/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyRowCleanupTest.java b/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyRowCleanupTest.java index 82aca11..c02373d 100644 --- a/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyRowCleanupTest.java +++ b/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyRowCleanupTest.java @@ -1,62 +1,17 @@ package lt.grimsa.hibernate.id; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.spi.LoggingEvent; -import org.hibernate.Transaction; -import org.hibernate.cfg.AvailableSettings; +import model.TestEntities.Human; import org.hibernate.cfg.Configuration; -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; -import model.TestEntities.Animal; -import model.TestEntities.Dog; -import model.TestEntities.Human; -import model.TestEntities.Mammal; -import model.TestEntities.Reptile; - -public class SingleGlobalTemporaryTableBulkIdStrategyRowCleanupTest extends BaseCoreFunctionalTestCase { - - private Logger sqlLogger = Logger.getLogger("org.hibernate.SQL"); - - private MessageCapturingAppender sqlAppender; - private boolean ddlExecuted; +public class SingleGlobalTemporaryTableBulkIdStrategyRowCleanupTest extends AbstractSingleGlobalTemporaryTableBulkIdStrategyTest { @Override protected void configure(Configuration configuration) { - configuration.setProperty(AvailableSettings.HQL_BULK_ID_STRATEGY, SingleGlobalTemporaryTableBulkIdStrategy.class.getName()); - configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.TABLE, "HT_TEMP_IDS"); - configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.DISCRIMINATOR_COLUMN, "ENTITY_NAME"); + super.configure(configuration); configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.CLEAN_ROWS, "true"); } - @Override - protected Class[] getAnnotatedClasses() { - return new Class[] { Animal.class, Mammal.class, Reptile.class, Human.class, Dog.class }; - } - - @Override - protected void prepareTest() throws Exception { - sqlAppender = new MessageCapturingAppender(); - openSession(); - if (!ddlExecuted) { - doInTransaction(() -> session.createNativeQuery("create global temporary table HT_TEMP_IDS (ID CHAR(36), ENTITY_NAME VARCHAR(100))").executeUpdate()); - ddlExecuted = true; - } - sqlLogger.addAppender(sqlAppender); - } - - @Override - protected void cleanupTest() throws Exception { - sqlLogger.removeAppender(sqlAppender); - } - @Test public void testDeleteCleansUpRows() { // given @@ -69,7 +24,7 @@ public void testDeleteCleansUpRows() { }); // then: row delete statement was executed - assertTrue(sqlAppender.log.contains("delete from HT_TEMP_IDS where ENTITY_NAME=?")); + verify(sqlLog -> sqlLog.contains(("delete from HT_TEMP_IDS where ENTITY_NAME=?"))); } @Test @@ -84,36 +39,6 @@ public void testUpdateCleansUpRows() { }); // then: row delete statement was executed - assertTrue(sqlAppender.log.contains("delete from HT_TEMP_IDS where ENTITY_NAME=?")); - } - - private void doInTransaction(Runnable runnable) { - Transaction transaction = session.beginTransaction(); - runnable.run(); - transaction.commit(); - } - - private void doWithLogging(Runnable runnable) { - sqlLogger.setLevel(Level.DEBUG); - runnable.run(); - sqlLogger.setLevel(Level.OFF); - } - - private static class MessageCapturingAppender extends AppenderSkeleton { - private final List log = new ArrayList<>(); - - @Override - protected void append(LoggingEvent event) { - log.add(event.getRenderedMessage()); - } - - @Override - public boolean requiresLayout() { - return false; - } - - @Override - public void close() { - } + verify(sqlLog -> sqlLog.contains(("delete from HT_TEMP_IDS where ENTITY_NAME=?"))); } } \ No newline at end of file diff --git a/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyTest.java b/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyTest.java index 9543b0e..9269d23 100644 --- a/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyTest.java +++ b/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyTest.java @@ -1,62 +1,13 @@ package lt.grimsa.hibernate.id; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.spi.LoggingEvent; -import org.hibernate.Transaction; -import org.hibernate.cfg.AvailableSettings; -import org.hibernate.cfg.Configuration; -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.junit.Test; - -import model.TestEntities.Animal; -import model.TestEntities.Dog; import model.TestEntities.Human; -import model.TestEntities.Mammal; import model.TestEntities.Reptile; +import org.junit.Test; -public class SingleGlobalTemporaryTableBulkIdStrategyTest extends BaseCoreFunctionalTestCase { - - private Logger sqlLogger = Logger.getLogger("org.hibernate.SQL"); - - private MessageCapturingAppender sqlAppender; - private boolean ddlExecuted; - - @Override - protected void configure(Configuration configuration) { - configuration.setProperty(AvailableSettings.HQL_BULK_ID_STRATEGY, SingleGlobalTemporaryTableBulkIdStrategy.class.getName()); - configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.TABLE, "HT_TEMP_IDS"); - configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.DISCRIMINATOR_COLUMN, "ENTITY_NAME"); - } - - @Override - protected Class[] getAnnotatedClasses() { - return new Class[] { Animal.class, Mammal.class, Reptile.class, Human.class, Dog.class }; - } - - @Override - protected void prepareTest() throws Exception { - sqlAppender = new MessageCapturingAppender(); - openSession(); - if (!ddlExecuted) { - doInTransaction(() -> session.createNativeQuery("create global temporary table HT_TEMP_IDS (ID CHAR(36), ENTITY_NAME VARCHAR(100))").executeUpdate()); - ddlExecuted = true; - } - sqlLogger.addAppender(sqlAppender); - } +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; - @Override - protected void cleanupTest() throws Exception { - sqlLogger.removeAppender(sqlAppender); - } +public class SingleGlobalTemporaryTableBulkIdStrategyTest extends AbstractSingleGlobalTemporaryTableBulkIdStrategyTest { @Test public void testDelete() { @@ -77,14 +28,12 @@ public void testDelete() { assertNull(session.find(Human.class, human.id)); // then: expected SQL was generated - assertEquals( - "insert into HT_TEMP_IDS select testentiti0_.id as id, 'model.TestEntities$Mammal' as ENTITY_NAME from Mammal testentiti0_ inner join Animal testentiti0_1_ on testentiti0_.id=testentiti0_1_.id", - sqlAppender.log.get(0)); - assertTrue(sqlAppender.log.contains("delete from Dog where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); - assertTrue(sqlAppender.log.contains("delete from Human where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); - assertTrue(sqlAppender.log.contains("delete from Mammal where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); - assertTrue(sqlAppender.log.contains("delete from Animal where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); - assertEquals(5, sqlAppender.log.size()); + verify(sqlLog -> sqlLog.get(0).equals("insert into HT_TEMP_IDS select testentiti0_.id as id, 'model.TestEntities$Mammal' as ENTITY_NAME from Mammal testentiti0_ inner join Animal testentiti0_1_ on testentiti0_.id=testentiti0_1_.id")); + verify(sqlLog -> sqlLog.contains("delete from Dog where (id) IN (select ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); + verify(sqlLog -> sqlLog.contains("delete from Human where (id) IN (select ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); + verify(sqlLog -> sqlLog.contains("delete from Mammal where (id) IN (select ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); + verify(sqlLog -> sqlLog.contains("delete from Animal where (id) IN (select ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); + verify(sqlLog -> sqlLog.size() == 5); } @Test @@ -106,40 +55,8 @@ public void testUpdate() { assertEquals("someCoolValue", session.find(Human.class, human.id).mammalField); // then: expected SQL was generated - assertEquals( - "insert into HT_TEMP_IDS select testentiti0_.id as id, 'model.TestEntities$Human' as ENTITY_NAME from Human testentiti0_ inner join Mammal testentiti0_1_ on testentiti0_.id=testentiti0_1_.id inner join Animal testentiti0_2_ on testentiti0_.id=testentiti0_2_.id", - sqlAppender.log.get(0)); - assertTrue(sqlAppender.log.contains("update Mammal set mammalField='someCoolValue' where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Human')")); - assertEquals(2, sqlAppender.log.size()); - } - - private void doInTransaction(Runnable runnable) { - Transaction transaction = session.beginTransaction(); - runnable.run(); - transaction.commit(); - } - - private void doWithLogging(Runnable runnable) { - sqlLogger.setLevel(Level.DEBUG); - runnable.run(); - sqlLogger.setLevel(Level.OFF); - } - - private static class MessageCapturingAppender extends AppenderSkeleton { - private final List log = new ArrayList<>(); - - @Override - protected void append(LoggingEvent event) { - log.add(event.getRenderedMessage()); - } - - @Override - public boolean requiresLayout() { - return false; - } - - @Override - public void close() { - } + verify(sqlLog -> sqlLog.get(0).equals("insert into HT_TEMP_IDS select testentiti0_.id as id, 'model.TestEntities$Human' as ENTITY_NAME from Human testentiti0_ inner join Mammal testentiti0_1_ on testentiti0_.id=testentiti0_1_.id inner join Animal testentiti0_2_ on testentiti0_.id=testentiti0_2_.id")); + verify(sqlLog -> sqlLog.contains("update Mammal set mammalField='someCoolValue' where (id) IN (select ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Human')")); + verify(sqlLog -> sqlLog.size() == 2); } } \ No newline at end of file diff --git a/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyTestIdColumn.java b/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyTestIdColumn.java new file mode 100644 index 0000000..3fdd15e --- /dev/null +++ b/src/test/java/lt/grimsa/hibernate/id/SingleGlobalTemporaryTableBulkIdStrategyTestIdColumn.java @@ -0,0 +1,75 @@ +package lt.grimsa.hibernate.id; + +import model.TestEntities.Human; +import model.TestEntities.Reptile; +import org.hibernate.cfg.Configuration; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class SingleGlobalTemporaryTableBulkIdStrategyTestIdColumn extends AbstractSingleGlobalTemporaryTableBulkIdStrategyTest { + private static final String CUSTOM_ID_COLUMN_NAME = "MY_ID"; + + @Override + protected void configure(Configuration configuration) { + super.configure(configuration); + configuration.setProperty(SingleGlobalTemporaryTableBulkIdStrategy.ID_COLUMN, CUSTOM_ID_COLUMN_NAME); + } + + @Override + protected String getIdColumnName() { + return CUSTOM_ID_COLUMN_NAME; + } + + @Test + public void testDelete() { + // given + Human human = new Human(); + doInTransaction(() -> { + session.save(human); + Reptile reptile = new Reptile(); + session.save(reptile); + session.flush(); + + // when + doWithLogging(() -> session.createQuery("delete from Mammal").executeUpdate()); + session.clear(); + }); + + // then: entity was deleted + assertNull(session.find(Human.class, human.id)); + + // then: expected SQL was generated + verify(sqlLog -> sqlLog.get(0).equals("insert into HT_TEMP_IDS select testentiti0_.id as id, 'model.TestEntities$Mammal' as ENTITY_NAME from Mammal testentiti0_ inner join Animal testentiti0_1_ on testentiti0_.id=testentiti0_1_.id")); + verify(sqlLog -> sqlLog.contains("delete from Dog where (id) IN (select MY_ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); + verify(sqlLog -> sqlLog.contains("delete from Human where (id) IN (select MY_ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); + verify(sqlLog -> sqlLog.contains("delete from Mammal where (id) IN (select MY_ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); + verify(sqlLog -> sqlLog.contains("delete from Animal where (id) IN (select MY_ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')")); + verify(sqlLog -> sqlLog.size() == 5); + } + + @Test + public void testUpdate() { + // given + Human human = new Human(); + Reptile reptile = new Reptile(); + doInTransaction(() -> { + session.save(human); + session.save(reptile); + session.flush(); + + // when + doWithLogging(() -> session.createQuery("update Human h set h.mammalField = 'someCoolValue'").executeUpdate()); + session.clear(); + }); + + // then: update was performed + assertEquals("someCoolValue", session.find(Human.class, human.id).mammalField); + + // then: expected SQL was generated + verify(sqlLog -> sqlLog.get(0).equals("insert into HT_TEMP_IDS select testentiti0_.id as id, 'model.TestEntities$Human' as ENTITY_NAME from Human testentiti0_ inner join Mammal testentiti0_1_ on testentiti0_.id=testentiti0_1_.id inner join Animal testentiti0_2_ on testentiti0_.id=testentiti0_2_.id")); + verify(sqlLog -> sqlLog.contains("update Mammal set mammalField='someCoolValue' where (id) IN (select MY_ID from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Human')")); + verify(sqlLog -> sqlLog.size() == 2); + } +} \ No newline at end of file