我正在尝试使用Hibernate 4.3和SQL Server 2014,仅对尚未存储的实体执行批量插入到表中。我创建了一个简单的表,其中定义了一个主键来忽略重复的键
create table items
(
itemid uniqueidentifier not null,
itemname nvarchar(30) not null,
)
alter table items add constraint items_pk primary key ( itemid ) with ( ignore_dup_key = on );
尝试通过Stateless会话插入方法执行批处理插入,如果一个或多个实体已经存储到数据库表中,则批处理插入可能会失败:Hibernate抛出StaleStateException:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:63)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3124)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3581)
at org.hibernate.internal.StatelessSessionImpl.insert(StatelessSessionImpl.java:144)
at org.hibernate.internal.StatelessSessionImpl.insert(StatelessSessionImpl.java:123)
at it.test.testingestion.HibernateStatelessSessionPersisterImpl.persistData(HibernateStatelessSessionPersisterImpl.java:18)
at it.test.testingestion.App.main(App.java:76)
当批处理语句完成时,Hibernate会检查返回的行数,由于忽略重复键,返回的行数与预期的不同。
使用JDBC,使用准备好的语句执行批插入,将跳过已存储到目标表中的实体,但会正确保存新实体。
如何将Hibernate配置为忽略现有数据执行批插入,或不执行受影响行的检查?
谢谢
更新#1
作为解决方法,为了强制影响的行数,即使发生重复插入,我创建了以下Hibernate Interceptor:
public class CustomInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = -8022068618978801796L;
private String getTemporaryTableName() {
String currentThreadName = Thread.currentThread().getName();
return "##" + currentThreadName.replaceAll("[^A-Za-z0-9\\_]", "");
}
private void createTemporaryTable(Connection connection) {
String tempTableName = this.getTemporaryTableName();
String commandText = String.format("if (object_id('tempdb.dbo.%s') is null) begin create table [%s] ( dummyfield int ); insert into %s ( dummyfield ) values ( 0 ) end ", tempTableName, tempTableName, tempTableName);
try (PreparedStatement statement = connection.prepareStatement(commandText)) {
statement.execute();
connection.commit();
} catch (SQLException e) {
throw new RuntimeException(String.format("An error has been occurred trying to create the temporary table %s", tempTableName), e);
}
}
public CustomInterceptor(Connection connection) {
this.createTemporaryTable(connection);
}
@Override
public String onPrepareStatement(String sql) {
int ps = sql.toLowerCase().indexOf("insert into ");
if (ps == 0) {
String tableName = this.getTemporaryTableName();
return sql + "; if (@@rowcount = 0) update [" + tableName + "] set dummyfield = 1";
}
return super.onPrepareStatement(sql);
}
}
当创建新实例时,拦截器会创建一个新的临时表,插入一条新记录。截取insert语句时,如果insert语句未影响任何行,则会执行保存到实例化临时表中的记录的更新:如果插入了重复实体且未引发无状态SessionImpl异常,则这会使Hibernate对返回的行事件保持Hibernate。
显然,这一技巧的缺点是,对未插入表中的每一行执行额外更新的成本。
有没有人知道一种更好的方法,不影响插入性能,将实体插入到使用Hibernate忽略重复条目的表中?
谢谢
为了获得更好的性能,我更喜欢使用JDBCBatchUpdate
方法1:
当您过滤新记录时,记录的数量不会成为约束。因此,您可以在实体层中指定关联映射,并可以执行Hibernate批处理插入或JDBC批处理更新。
方法2:使用本机SQl查询
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//get Connction from Session
session.doWork(new Work() {
@Override
public void execute(Connection conn) throws SQLException {
PreparedStatement pstmt = null;
try{
String sqlInsert = "insert into tbl(name) values (?) ";
pstmt = conn.prepareStatement(sqlInsert );
int i=0;
for(String name : list){
pstmt .setString(1, name);
pstmt .addBatch();
//20 : JDBC batch size
if ( i % 20 == 0 ) {
pstmt .executeBatch();
}
i++;
}
pstmt .executeBatch();
}
finally{
pstmt .close();
}
}
});
tx.commit();
session.close();
我的用户表有以下存储库界面。 我想禁用在用户表中插入新实体,并仅在存在现有条目的情况下使用保存功能来更新现有条目。我如何才能做到这一点?是否有可以传递到存储库界面的选项?或者,当没有匹配的实体时,我应该重写save方法而不执行任何操作吗?
如果要将很多对象持久化,你必须通过经常的调用 flush() 以及稍后调用 clear() 来控制第一级缓存的大小。 Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); for ( int i=0; i<100000; i++ ) { Custome
我的php代码: 当我使用echo而不是mysql_query时,它返回: 它在我的phpmyadmin上运行得很好,但是当我将echo更改为mysql_query时,它只影响5行(两个表) P1_1-K1 P1_1-K4 P1_1-K3 P1_1-K2 P12-K1 我的完整数据库 --版本4.3.11 --主机:127.0.0.1--生成时间:2015年7月23日下午12:16--服务器版本:
OrientDB是一个NoSQL数据库,可以存储文档和面向图形的数据。 NoSQL数据库不包含任何表,那么要如何将数据作为记录插入?在这里,您可以以类,属性,顶点和边的形式查看表数据,类表就像表,属性就像表中的文件。 可以在OrientDB中使用模式定义所有这些实体。 属性数据可以被插入到一个类中。 插入命令在数据库模式中创建一条新记录。 记录可以无模式或遵循一些指定的规则。 以下语句是“插入记录
问题内容: 我一直使用类似于以下内容的方法来实现它: …但是一旦加载,就会发生主键冲突。这是唯一插入到该表中的唯一语句。那么这是否意味着上述陈述不是原子的? 问题在于,这几乎不可能随意重建。 也许我可以将其更改为以下内容: 虽然,也许我使用了错误的锁,或者使用了过多的锁之类的东西。 看到了其他问题,那里的答案都提示“ IF(SELECT COUNT(*)…INSERT”等),但我始终处于(也许不正
我使用Liquibase insert将两行添加到数据库中,并通过外键链接它们,如下所示: 除了表2中外键的值外,两条记录都正确插入。此值保持为空。因此,“computeValue”属性似乎没有正确执行,但我不明白为什么。我希望在Liquibase中有一个解决方案,而不是在普通SQL中,因为Liquibase提供了一种可能性,可以方便地插入clob字段。 表1:ID- 55228390499230