PageRenderTime 25ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/atlassian-hibernate4-extras/src/main/java/com/atlassian/hibernate/extras/hql/JoinTableBasedDeleteHandlerImpl.java

https://bitbucket.org/atlassian/atlassian-hibernate-extras
Java | 194 lines | 149 code | 29 blank | 16 comment | 11 complexity | 4384420275b6de96f313ccf48e4c1232 MD5 | raw file
Possible License(s): LGPL-2.1
  1. package com.atlassian.hibernate.extras.hql;
  2. import org.hibernate.engine.spi.QueryParameters;
  3. import org.hibernate.engine.spi.SessionFactoryImplementor;
  4. import org.hibernate.engine.spi.SessionImplementor;
  5. import org.hibernate.hql.internal.ast.HqlSqlWalker;
  6. import org.hibernate.hql.internal.ast.tree.DeleteStatement;
  7. import org.hibernate.hql.internal.ast.tree.FromElement;
  8. import org.hibernate.hql.spi.AbstractTableBasedBulkIdHandler;
  9. import org.hibernate.hql.spi.MultiTableBulkIdStrategy;
  10. import org.hibernate.param.ParameterSpecification;
  11. import org.hibernate.persister.collection.AbstractCollectionPersister;
  12. import org.hibernate.persister.entity.Queryable;
  13. import org.hibernate.sql.Alias;
  14. import org.hibernate.sql.JoinFragment;
  15. import org.hibernate.sql.JoinType;
  16. import org.hibernate.type.CollectionType;
  17. import org.hibernate.type.Type;
  18. import org.slf4j.Logger;
  19. import org.slf4j.LoggerFactory;
  20. import java.sql.PreparedStatement;
  21. import java.sql.SQLException;
  22. import java.util.ArrayList;
  23. import java.util.List;
  24. /**
  25. * Custom multi-table bulk delete strategy, derived from Hibernate's own {@code TableBasedDeleteHandlerImpl}.
  26. * Similar to its prototype, this class relies on a temporary table that is fed with IDs of the entities to remove,
  27. * based on the original HQL query.
  28. * <p>
  29. * The primary difference is that this class performs the deletes using an inner join to the temporary table, rather
  30. * than a subquery to load all the IDs to delete, as in the original implementation. The inner join approach performs
  31. * significantly better on certain database engines, primarily MySQL. Note: the performance measurements of the join vs.
  32. * sub-query approach on MySQL have been performed using the InnoDB storage engine.
  33. *
  34. * @since 5.0.0
  35. * @see JoinTemporaryTableBulkIdStrategy
  36. */
  37. public class JoinTableBasedDeleteHandlerImpl extends AbstractTableBasedBulkIdHandler
  38. implements MultiTableBulkIdStrategy.DeleteHandler {
  39. private static final Logger log = LoggerFactory.getLogger(JoinTableBasedDeleteHandlerImpl.class);
  40. private static final String DELETE_SUFFIX = "_0";
  41. private final Queryable targetedPersister;
  42. private final String idInsertSelect;
  43. private final List<ParameterSpecification> idSelectParameterSpecifications;
  44. private final List<String> deletes;
  45. public JoinTableBasedDeleteHandlerImpl(SessionFactoryImplementor factory, HqlSqlWalker walker) {
  46. this(factory, walker, null, null);
  47. }
  48. public JoinTableBasedDeleteHandlerImpl(SessionFactoryImplementor factory, HqlSqlWalker walker, String catalog,
  49. String schema) {
  50. super(factory, walker, catalog, schema);
  51. DeleteStatement deleteStatement = (DeleteStatement) walker.getAST();
  52. FromElement fromElement = deleteStatement.getFromClause().getFromElement();
  53. targetedPersister = fromElement.getQueryable();
  54. String bulkTargetAlias = fromElement.getTableAlias();
  55. ProcessedWhereClause processedWhereClause = processWhereClause(deleteStatement.getWhereClause());
  56. idSelectParameterSpecifications = processedWhereClause.getIdSelectParameterSpecifications();
  57. idInsertSelect = generateIdInsertSelect(targetedPersister, bulkTargetAlias, processedWhereClause);
  58. log.trace("Generated ID-INSERT-SELECT SQL (multi-table delete) : {0}", idInsertSelect);
  59. deletes = new ArrayList<>();
  60. for (Type type : targetedPersister.getPropertyTypes()) {
  61. if (type.isCollectionType()) {
  62. CollectionType collectionType = (CollectionType) type;
  63. AbstractCollectionPersister collectionPersister =
  64. (AbstractCollectionPersister) factory.getCollectionPersister(collectionType.getRole());
  65. if (collectionPersister.isManyToMany()) {
  66. deletes.add(generateDelete(collectionPersister.getTableName(),
  67. collectionPersister.getKeyColumnNames()));
  68. }
  69. }
  70. }
  71. String[] tableNames = targetedPersister.getConstraintOrderedTableNameClosure();
  72. String[][] columnNames = targetedPersister.getContraintOrderedTableKeyColumnClosure();
  73. for (int i = 0; i < tableNames.length; i++) {
  74. deletes.add(generateDelete(tableNames[i], columnNames[i]));
  75. }
  76. }
  77. @Override
  78. public Queryable getTargetedQueryable() {
  79. return targetedPersister;
  80. }
  81. @Override
  82. public String[] getSqlStatements() {
  83. return deletes.toArray(new String[deletes.size()]);
  84. }
  85. @Override
  86. public int execute(SessionImplementor session, QueryParameters queryParameters) {
  87. prepareForUse(targetedPersister, session);
  88. try {
  89. PreparedStatement ps = null;
  90. int resultCount = 0;
  91. try {
  92. try {
  93. ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement(idInsertSelect, false);
  94. int pos = 1;
  95. pos += handlePrependedParametersOnIdSelection(ps, session, pos);
  96. for (ParameterSpecification parameterSpecification : idSelectParameterSpecifications) {
  97. pos += parameterSpecification.bind(ps, queryParameters, session, pos);
  98. }
  99. resultCount = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate(ps);
  100. } finally {
  101. if (ps != null) {
  102. session.getTransactionCoordinator().getJdbcCoordinator().release(ps);
  103. }
  104. }
  105. } catch (SQLException e) {
  106. throw convert(e, "could not insert/select ids for bulk delete", idInsertSelect);
  107. }
  108. for (String delete : deletes) {
  109. try {
  110. try {
  111. ps = session.getTransactionCoordinator()
  112. .getJdbcCoordinator()
  113. .getStatementPreparer()
  114. .prepareStatement(delete, false);
  115. handleAddedParametersOnDelete(ps, session);
  116. session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate(ps);
  117. } finally {
  118. if (ps != null) {
  119. session.getTransactionCoordinator().getJdbcCoordinator().release(ps);
  120. }
  121. }
  122. } catch (SQLException e) {
  123. throw convert(e, "error performing bulk delete", delete);
  124. }
  125. }
  126. return resultCount;
  127. } finally {
  128. releaseFromUse(targetedPersister, session);
  129. }
  130. }
  131. protected int handlePrependedParametersOnIdSelection(PreparedStatement ps, SessionImplementor session, int pos) throws SQLException {
  132. return 0;
  133. }
  134. protected void handleAddedParametersOnDelete(PreparedStatement ps, SessionImplementor session) throws SQLException {
  135. }
  136. private static String[] prefixWithAlias(String[] original, String alias) {
  137. final String fullPrefix = alias + ".";
  138. String[] aliased = new String[original.length];
  139. for (int i = 0; i < original.length; i++) {
  140. aliased[i] = fullPrefix + original[i];
  141. }
  142. return aliased;
  143. }
  144. private String generateDelete(String tableName, String[] columnNames) {
  145. Alias alias = new Alias(DELETE_SUFFIX);
  146. String deleteTableAlias = alias.toAliasString(tableName);
  147. String tempIdTable = determineIdTableName(targetedPersister);
  148. String tempIdTableAlias = alias.toAliasString(tempIdTable);
  149. String[] aliasedColumnNames = prefixWithAlias(columnNames, deleteTableAlias);
  150. // join temp table on delete table IDs = temp table IDs (which are the same as the targeted persister IDs)
  151. // the assumption is that the order of IDs matches
  152. JoinFragment deleteJoin = factory().getDialect().createOuterJoinFragment();
  153. deleteJoin.addJoin(tempIdTable, tempIdTableAlias, aliasedColumnNames,
  154. targetedPersister.getIdentifierColumnNames(),
  155. JoinType.INNER_JOIN);
  156. // construct the aliased delete
  157. return new StringBuilder("delete ")
  158. .append(deleteTableAlias)
  159. .append(" from ")
  160. .append(tableName).append(' ')
  161. .append(deleteTableAlias).append(' ')
  162. .append(deleteJoin.toFromFragmentString())
  163. .toString();
  164. }
  165. }