Hibernate Multi-table 批量操作总是尝试创建临时 table
Hibernate Multi-table Bulk Operations always try to create the temporary table
我有一些使用连接继承的实体,我正在对它们进行批量操作。如 Multi-table Bulk Operations 中所述,Hibernate 使用临时 table 来执行批量操作。
据我了解,临时 tables 中的数据是临时的(在事务或会话结束时删除),但 table 本身是永久的。我看到的是 Hibernate 每次执行这样的查询时都会尝试创建临时 table 。在我的例子中,每小时上升超过 35.000 次。 create table 语句显然每次都会失败,因为同名的 table 已经存在。这真的是不必要的,可能会损害性能,而且 DBA 也不高兴...
有没有办法让 Hibernate 记住它已经创建了临时 table?
如果没有,是否有任何解决方法?我唯一的想法是使用单table继承来避免完全使用临时table。
Hibernate 版本为 4.2.8,数据库为 Oracle 11g。
我认为这是 TemporaryTableBulkIdStrategy, because when using the Oracle8iDialect 中的一个错误,表示不应删除临时 tables:
@Override
public boolean dropTemporaryTableAfterUse() {
return false;
}
但只有在删除 table:
时才进行此检查
protected void releaseTempTable(Queryable persister, SessionImplementor session) {
if ( session.getFactory().getDialect().dropTemporaryTableAfterUse() ) {
TemporaryTableDropWork work = new TemporaryTableDropWork( persister, session );
if ( shouldIsolateTemporaryTableDDL( session ) ) {
session.getTransactionCoordinator()
.getTransaction()
.createIsolationDelegate()
.delegateWork( work, shouldTransactIsolatedTemporaryTableDDL( session ) );
}
else {
final Connection connection = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getConnection();
work.execute( connection );
session.getTransactionCoordinator()
.getJdbcCoordinator()
.afterStatementExecution();
}
}
else {
// at the very least cleanup the data :)
PreparedStatement ps = null;
try {
final String sql = "delete from " + persister.getTemporaryIdTableName();
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate( ps );
}
catch( Throwable t ) {
log.unableToCleanupTemporaryIdTable(t);
}
finally {
if ( ps != null ) {
try {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
catch( Throwable ignore ) {
// ignore
}
}
}
}
}
但现在在创建 table 时:
protected void createTempTable(Queryable persister, SessionImplementor session) {
// Don't really know all the codes required to adequately decipher returned jdbc exceptions here.
// simply allow the failure to be eaten and the subsequent insert-selects/deletes should fail
TemporaryTableCreationWork work = new TemporaryTableCreationWork( persister );
if ( shouldIsolateTemporaryTableDDL( session ) ) {
session.getTransactionCoordinator()
.getTransaction()
.createIsolationDelegate()
.delegateWork( work, shouldTransactIsolatedTemporaryTableDDL( session ) );
}
else {
final Connection connection = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getConnection();
work.execute( connection );
session.getTransactionCoordinator()
.getJdbcCoordinator()
.afterStatementExecution();
}
}
作为解决方法,您可以扩展 Oracle 方言并将 dropTemporaryTableAfterUse
方法覆盖为 return false
.
我为此填写了 HHH-9744 问题。
在 Vlad 的指引下,我想出了以下解决方法来缓存已创建的临时表的名称:
public class FixedTemporaryTableBulkIdStrategy extends TemporaryTableBulkIdStrategy {
private final Set<String> tables = new CopyOnWriteArraySet<>();
@Override
protected void createTempTable(Queryable persister, SessionImplementor session) {
final String temporaryIdTableName = persister.getTemporaryIdTableName();
if (!tables.contains(temporaryIdTableName)) {
super.createTempTable(persister, session);
tables.add(temporaryIdTableName);
}
}
}
这可以通过将 属性 hibernate.hql.bulk_id_strategy
设置为此 class 的完全限定名称来使用。
请注意,这不是通用解决方案,只有在 database/dialect 使用全局临时表(相对于 session/transaction 特定)时才有效。
我有一些使用连接继承的实体,我正在对它们进行批量操作。如 Multi-table Bulk Operations 中所述,Hibernate 使用临时 table 来执行批量操作。
据我了解,临时 tables 中的数据是临时的(在事务或会话结束时删除),但 table 本身是永久的。我看到的是 Hibernate 每次执行这样的查询时都会尝试创建临时 table 。在我的例子中,每小时上升超过 35.000 次。 create table 语句显然每次都会失败,因为同名的 table 已经存在。这真的是不必要的,可能会损害性能,而且 DBA 也不高兴...
有没有办法让 Hibernate 记住它已经创建了临时 table?
如果没有,是否有任何解决方法?我唯一的想法是使用单table继承来避免完全使用临时table。
Hibernate 版本为 4.2.8,数据库为 Oracle 11g。
我认为这是 TemporaryTableBulkIdStrategy, because when using the Oracle8iDialect 中的一个错误,表示不应删除临时 tables:
@Override
public boolean dropTemporaryTableAfterUse() {
return false;
}
但只有在删除 table:
时才进行此检查protected void releaseTempTable(Queryable persister, SessionImplementor session) {
if ( session.getFactory().getDialect().dropTemporaryTableAfterUse() ) {
TemporaryTableDropWork work = new TemporaryTableDropWork( persister, session );
if ( shouldIsolateTemporaryTableDDL( session ) ) {
session.getTransactionCoordinator()
.getTransaction()
.createIsolationDelegate()
.delegateWork( work, shouldTransactIsolatedTemporaryTableDDL( session ) );
}
else {
final Connection connection = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getConnection();
work.execute( connection );
session.getTransactionCoordinator()
.getJdbcCoordinator()
.afterStatementExecution();
}
}
else {
// at the very least cleanup the data :)
PreparedStatement ps = null;
try {
final String sql = "delete from " + persister.getTemporaryIdTableName();
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate( ps );
}
catch( Throwable t ) {
log.unableToCleanupTemporaryIdTable(t);
}
finally {
if ( ps != null ) {
try {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
catch( Throwable ignore ) {
// ignore
}
}
}
}
}
但现在在创建 table 时:
protected void createTempTable(Queryable persister, SessionImplementor session) {
// Don't really know all the codes required to adequately decipher returned jdbc exceptions here.
// simply allow the failure to be eaten and the subsequent insert-selects/deletes should fail
TemporaryTableCreationWork work = new TemporaryTableCreationWork( persister );
if ( shouldIsolateTemporaryTableDDL( session ) ) {
session.getTransactionCoordinator()
.getTransaction()
.createIsolationDelegate()
.delegateWork( work, shouldTransactIsolatedTemporaryTableDDL( session ) );
}
else {
final Connection connection = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getConnection();
work.execute( connection );
session.getTransactionCoordinator()
.getJdbcCoordinator()
.afterStatementExecution();
}
}
作为解决方法,您可以扩展 Oracle 方言并将 dropTemporaryTableAfterUse
方法覆盖为 return false
.
我为此填写了 HHH-9744 问题。
在 Vlad 的指引下,我想出了以下解决方法来缓存已创建的临时表的名称:
public class FixedTemporaryTableBulkIdStrategy extends TemporaryTableBulkIdStrategy {
private final Set<String> tables = new CopyOnWriteArraySet<>();
@Override
protected void createTempTable(Queryable persister, SessionImplementor session) {
final String temporaryIdTableName = persister.getTemporaryIdTableName();
if (!tables.contains(temporaryIdTableName)) {
super.createTempTable(persister, session);
tables.add(temporaryIdTableName);
}
}
}
这可以通过将 属性 hibernate.hql.bulk_id_strategy
设置为此 class 的完全限定名称来使用。
请注意,这不是通用解决方案,只有在 database/dialect 使用全局临时表(相对于 session/transaction 特定)时才有效。