Hibernate + SQLServer /批处理仅插入新记录

时间:2019-01-25 15:39:57

标签: java sql-server hibernate batch-insert

我正在尝试使用Hibernate 4.3和SQL-Server 2014对仅尚未存储的实体执行批量插入表中。 我创建了一个简单的表,其中的主键定义为忽略重复的键

@app.route('/Search', methods=['POST', 'GET'])
@login_required
def Search():
    if request.method == 'POST':
        query_result = google_places.nearby_search(
            lat_lng={'lat':31.7917, 'lng' : 7.0926},
            radius=500,
            types=[types.TYPE_SHOPPING_MALL] or [types.TYPE_STORE])`

        if query_result.has_attributions:
            print(query_result.html_attributions)

        for place in query_result.places:
            place.get_details()
            place_name = place.name
            print(place.name)
            place_rating = place.rating
            print(place.rating)
            place_location = place.get_location
            print(place.get_location)
            for photo in place.photos:
                photo.get(maxheight=500, maxwidth=500)
                photo.mimetype
                photo.url
                photo.filename
                photo.data
            return render_template('Search.html', place_name, place_rating, place_location)
    else:
        return render_template('Search.html')```


#Note: i am new to python in general

尝试通过StatelessSession插入方法执行批量插入,如果一个或多个实体已经存储到数据库表中,则批量插入可能会失败:Hibernate抛出StaleStateException:

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 );

批处理语句完成后,由于忽略重复键,Hibernate将对返回的行数进行检查,该计数与预期的不同。

对于JDBC,使用预处理语句执行批处理插入,将跳过已存储到目标表中的实体,但正确保存新实体。

如何将Hibernate配置为忽略现有数据执行批量插入,或者不对受影响的行进行检查?

非常感谢

更新#1

作为一种解决方法,即使发生重复的插入,也要强制受影响的行数,我创建了以下Hibernate Interceptor:

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)

拦截器在创建新实例时会创建一个插入新记录的新临时表。 当一条插入语句被拦截时,如果没有行受到插入语句的影响,则将更新保存到实例临时表中的记录:如果插入了重复的实体并且没有StatelessSessionImpl异常,这将使Hibernate有关返回的行事件抛出。

显然,此技巧的缺点是对未插入表中的每一行执行额外更新的成本。

有没有人知道一种更好的方法,该方法不影响插入性能,而是将实体插入到使用Hibernate忽略重复条目的表中?

谢谢

1 个答案:

答案 0 :(得分:0)

为了获得更好的性能,我更喜欢使用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();