Spring data jpa saveAll 默认是逐条执行的,按网上的一般说法配置 order_inserts: true
batch_size: 批量大小 和数据库的jdbc参数 reWriteBatchedInserts/rewriteBatchedStatements
就可以实现批量操作 ,
spring:
jpa:
properties:
hibernate:
order_inserts: true
jdbc:
batch_size: 100
这时候你可能发现没有真正启用批量操作 而且你的id生成策略也不是IDENTITY
这时候就要注意你是否自定义了id生成测量但是又不是通过实现IdentifierGenerator接口做的
supportsJdbcBatchInserts方法返回了false
public boolean supportsJdbcBatchInserts() {
return true;
}
决定是否走批量插入的核心代码
org.hibernate.persister.entity.AbstractEntityPersister#insert
/**
* Perform an SQL INSERT.
* <p/>
* This for is used for all non-root tables as well as the root table
* in cases where the identifier value is known before the insert occurs.
*/
public void insert(
final Serializable id,
final Object[] fields,
final boolean[] notNull,
final int j,
final String sql,
final Object object,
final SharedSessionContractImplementor session) throws HibernateException {
if ( isInverseTable( j ) ) {
return;
}
//note: it is conceptually possible that a UserType could map null to
// a non-null value, so the following is arguable:
if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
return;
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Inserting entity: {0}", MessageHelper.infoString( this, id, getFactory() ) );
if ( j == 0 && isVersioned() ) {
LOG.tracev( "Version: {0}", Versioning.getVersion( fields, this ) );
}
}
// TODO : shouldn't inserts be Expectations.NONE?
final Expectation expectation = Expectations.appropriateExpectation( insertResultCheckStyles[j] );
final int jdbcBatchSizeToUse = session.getConfiguredJdbcBatchSize();
final boolean useBatch = expectation.canBeBatched() &&
jdbcBatchSizeToUse > 1 &&
getIdentifierGenerator().supportsJdbcBatchInserts();
if ( useBatch && insertBatchKey == null ) {
insertBatchKey = new BasicBatchKey(
getEntityName() + "#INSERT",
expectation
);
}
final boolean callable = isInsertCallable( j );
try {
// Render the SQL query
final PreparedStatement insert;
if ( useBatch ) {
insert = session
.getJdbcCoordinator()
.getBatch( insertBatchKey )
.getBatchStatement( sql, callable );
}
else {
insert = session
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql, callable );
}
try {
int index = 1;
index += expectation.prepare( insert );
// Write the values of fields onto the prepared statement - we MUST use the state at the time the
// insert was issued (cos of foreign key constraints). Not necessarily the object's current state
dehydrate( id, fields, null, notNull, propertyColumnInsertable, j, insert, session, index, false );
if ( useBatch ) {
session.getJdbcCoordinator().getBatch( insertBatchKey ).addToBatch();
}
else {
expectation.verifyOutcome(
session.getJdbcCoordinator()
.getResultSetReturn()
.executeUpdate( insert ), insert, -1, sql
);
}
}
catch (SQLException | RuntimeException e) {
if ( useBatch ) {
session.getJdbcCoordinator().abortBatch();
}
throw e;
}
finally {
if ( !useBatch ) {
session.getJdbcCoordinator().getResourceRegistry().release( insert );
session.getJdbcCoordinator().afterStatementExecution();
}
}
}
catch (SQLException e) {
throw getFactory().getSQLExceptionHelper().convert(
e,
"could not insert: " + MessageHelper.infoString( this ),
sql
);
}
}