Skip to content
This repository has been archived by the owner on Jun 13, 2024. It is now read-only.

Commit

Permalink
Made deletion of rows configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
grimsa committed Sep 28, 2016
1 parent fd45aac commit 84d9021
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 25 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Can be useful in environments where DDL statements cannot be executed from appli
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.CLEAN_ROWS, "true");
```

## Versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
Expand Down Expand Up @@ -43,14 +44,21 @@ public class SingleGlobalTemporaryTableBulkIdStrategy implements MultiTableBulkI
*/
public static final String DISCRIMINATOR_COLUMN = "hibernate.hql.bulk_id_strategy.single_global_temporary.discriminator_column";

/**
* Whether ID rows should be deleted after update/delete is processed. Defaults to {@code false}
*/
public static final String CLEAN_ROWS = "hibernate.hql.bulk_id_strategy.single_global_temporary.clean_rows";

private String fullyQualifiedTableName;
private String discriminatorColumn;
private boolean cleanRows;

@Override
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.cleanRows = configService.getSetting(CLEAN_ROWS, StandardConverters.BOOLEAN, false);
}

@Override
Expand All @@ -77,7 +85,9 @@ protected void addAnyExtraIdSelectValues(SelectValues selectClause) {

@Override
protected void releaseFromUse(Queryable persister, SessionImplementor session) {
cleanUpRows(session, targetedPersister);
if (cleanRows) {
cleanUpRows(session, targetedPersister);
}
}
};
}
Expand All @@ -102,7 +112,9 @@ protected void addAnyExtraIdSelectValues(SelectValues selectClause) {

@Override
protected void releaseFromUse(Queryable persister, SessionImplementor session) {
cleanUpRows(session, persister);
if (cleanRows) {
cleanUpRows(session, persister);
}
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
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 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;

@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");
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.createSQLQuery("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
doInTransaction(() -> {
session.save(new Human());
session.flush();

// when
doWithLogging(() -> session.createQuery("delete from Mammal").executeUpdate());
});

// then: row delete statement was executed
assertTrue(sqlAppender.log.contains("delete from HT_TEMP_IDS where ENTITY_NAME=?"));
}

@Test
public void testUpdateCleansUpRows() {
// given
doInTransaction(() -> {
session.save(new Human());
session.flush();

// when
doWithLogging(() -> session.createQuery("update Human h set h.mammalField = 'someCoolValue'").executeUpdate());
});

// 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<String> log = new ArrayList<>();

@Override
protected void append(LoggingEvent event) {
log.add(event.getRenderedMessage());
}

@Override
public boolean requiresLayout() {
return false;
}

@Override
public void close() {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

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 java.util.stream.Collectors;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
Expand All @@ -27,16 +27,14 @@ public class SingleGlobalTemporaryTableBulkIdStrategyTest extends BaseCoreFuncti

private Logger sqlLogger = Logger.getLogger("org.hibernate.SQL");

private MessageCapturingAppender sqlCapturingAppender;
private MessageCapturingAppender sqlAppender;
private boolean ddlExecuted;

@Override
protected Configuration constructConfiguration() {
Configuration configuration = super.constructConfiguration();
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");
return configuration;
}

@Override
Expand All @@ -46,20 +44,22 @@ protected Class<?>[] getAnnotatedClasses() {

@Override
protected void prepareTest() throws Exception {
sqlCapturingAppender = new MessageCapturingAppender();
sqlAppender = new MessageCapturingAppender();
openSession();
if (!ddlExecuted) {
doInTransaction(() -> session.createSQLQuery("create global temporary table HT_TEMP_IDS (ID CHAR(36), ENTITY_NAME VARCHAR(100))").executeUpdate());
ddlExecuted = true;
}
sqlLogger.addAppender(sqlCapturingAppender);
sqlLogger.addAppender(sqlAppender);
}

@Override
protected void cleanupTest() throws Exception {
sqlLogger.removeAppender(sqlCapturingAppender);
sqlLogger.removeAppender(sqlAppender);
}



@Test
public void testDelete() {
// given
Expand All @@ -79,16 +79,14 @@ public void testDelete() {
assertNull(session.byId(Human.class).load(human.id));

// then: expected SQL was generated
List<String> executedSql = (sqlCapturingAppender.events.stream().map(e -> e.getRenderedMessage()).collect(Collectors.toList()));
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",
executedSql.get(0));
assertEquals("delete from Dog where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')", executedSql.get(1));
assertEquals("delete from Human where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')", executedSql.get(2));
assertEquals("delete from Mammal where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')", executedSql.get(3));
assertEquals("delete from Animal where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Mammal')", executedSql.get(4));
assertEquals("delete from HT_TEMP_IDS where ENTITY_NAME=?", executedSql.get(5));
assertEquals(6, executedSql.size());
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());
}

@Test
Expand All @@ -110,13 +108,11 @@ public void testUpdate() {
assertEquals("someCoolValue", session.byId(Human.class).load(human.id).mammalField);

// then: expected SQL was generated
List<String> sql = (sqlCapturingAppender.events.stream().map(e -> e.getRenderedMessage()).collect(Collectors.toList()));
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",
sql.get(0));
assertEquals("update Mammal set mammalField='someCoolValue' where (id) IN (select id from HT_TEMP_IDS where ENTITY_NAME='model.TestEntities$Human')", sql.get(1));
assertEquals("delete from HT_TEMP_IDS where ENTITY_NAME=?", sql.get(2));
assertEquals(3, sql.size());
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) {
Expand All @@ -132,11 +128,11 @@ private void doWithLogging(Runnable runnable) {
}

private static class MessageCapturingAppender extends AppenderSkeleton {
private final List<LoggingEvent> events = new ArrayList<>();
private final List<String> log = new ArrayList<>();

@Override
protected void append(LoggingEvent event) {
events.add(event);
log.add(event.getRenderedMessage());
}

@Override
Expand Down

0 comments on commit 84d9021

Please sign in to comment.