如何解决Spring TransactionTemplate不使用批量插入/更新
我有一个使用Spring Data REST,Spring JPA,Hibernate,Mysql的Spring Boot 2.3应用程序。
我有一个服务,其中编写了一个使用Spring "paths": {
"@app/lib/*": ["app/lib/*"]
}
的@Async方法,因为我需要确定何时提交事务。
它可以工作,但是最近我注意到即使设置了import { yourExport } from '@app/lib/wherever';
,我也没有利用它。
这是我的Hibernate配置:
TransactionTemplate
我具有以下设置:
hibernate.jdbc.batch_size
这是我服务的相关部分:
@Configuration
@Profile({"dev","stage","prod"})
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.server",entityManagerFactoryRef = "entityManagerFactory",transactionManagerRef = "transactionManager"
)
public class HibernateConfig {
@Autowired
private Environment env;
@Autowired
private LocalValidatorFactoryBean validator;
@Primary
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(MultiTenantConnectionProvider multiTenantConnectionProviderImpl,CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) throws InstantiationException,IllegalAccessException,ClassNotFoundException,IllegalArgumentException,InvocationTargetException,NoSuchMethodException,SecurityException {
HashMap jpaProperties = getJpaProperties(multiTenantConnectionProviderImpl,currentTenantIdentifierResolverImpl);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("testPU");
em.setDataSource(dataSource());
em.setPackagesToScan("come.server");
em.setJpaPropertyMap(jpaProperties);
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaPropertyMap(jpaProperties);
em.afterPropertiesSet();
return em;
}
@Primary
@Bean(name = "dataSource")
public DataSource dataSource() throws InstantiationException,SecurityException {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriver(((Driver) Class.forName(env.getProperty("primary.datasource.driver-class-name")).getDeclaredConstructor().newInstance()));
dataSource.setUrl(env.getProperty("primary.datasource.url"));
dataSource.setUsername(env.getProperty("primary.datasource.username"));
dataSource.setPassword(env.getProperty("primary.datasource.password"));
return dataSource;
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(MultiTenantConnectionProvider multiTenantConnectionProviderImpl,SecurityException {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory(multiTenantConnectionProviderImpl,currentTenantIdentifierResolverImpl).getObject());
return transactionManager;
}
private HashMap getJpaProperties(MultiTenantConnectionProvider multiTenantConnectionProviderImpl,CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
HashMap properties = new HashMap<>();
properties.put(AvailableSettings.MULTI_TENANT,MultiTenancyStrategy.DATABASE);
properties.put(AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER,multiTenantConnectionProviderImpl);
properties.put(AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER,currentTenantIdentifierResolverImpl);
properties.put(AvailableSettings.HBM2DDL_AUTO,env.getProperty(AvailableSettings.HBM2DDL_AUTO));
properties.put(AvailableSettings.GENERATE_STATISTICS,env.getProperty(AvailableSettings.GENERATE_STATISTICS));
properties.put(AvailableSettings.DIALECT,env.getProperty(AvailableSettings.DIALECT));
properties.put(AvailableSettings.STORAGE_ENGINE,env.getProperty(AvailableSettings.STORAGE_ENGINE));
properties.put(AvailableSettings.SHOW_SQL,env.getProperty(AvailableSettings.SHOW_SQL));
properties.put(AvailableSettings.IMPLICIT_NAMING_STRATEGY,env.getProperty(AvailableSettings.IMPLICIT_NAMING_STRATEGY));
properties.put(AvailableSettings.STATEMENT_FETCH_SIZE,env.getProperty(AvailableSettings.STATEMENT_FETCH_SIZE));
properties.put(AvailableSettings.STATEMENT_BATCH_SIZE,env.getProperty(AvailableSettings.STATEMENT_BATCH_SIZE));
properties.put(AvailableSettings.MAX_FETCH_DEPTH,env.getProperty(AvailableSettings.MAX_FETCH_DEPTH));
properties.put(AvailableSettings.USE_SECOND_LEVEL_CACHE,env.getProperty(AvailableSettings.USE_SECOND_LEVEL_CACHE));
properties.put(AvailableSettings.CACHE_REGION_FACTORY,env.getProperty(AvailableSettings.CACHE_REGION_FACTORY));
properties.put(AvailableSettings.JDBC_TIME_ZONE,env.getProperty(AvailableSettings.JDBC_TIME_ZONE));
properties.put(AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS,env.getProperty(AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS));
properties.put(AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS_SKIP_COLUMN_DEFINITIONS,env.getProperty(AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS_SKIP_COLUMN_DEFINITIONS));
return properties;
}
}
简而言之,有一个循环将一些实体插入数据库。在我的hibernate.jdbc.fetch_size = 15
hibernate.jdbc.batch_size = 30
hibernate.max_fetch_depth = 3
上设置一个断点,我看到它包含具有正确jpa属性的正确transactionManager。
但是,当我检查Mysql日志时,我看到有很多插入内容:
@Service
@Transactional
@Log4j2
public class StsService {
@PersistenceContext(unitName = "testPU")
private EntityManager entityManager;
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private Environment environment;
@Async
public void collectDocumentToSend(String query,String tenantId) {
//some stuff
transactionTemplate.setTransactionManager(transactionManager);
transactionTemplate.execute(status -> {
try {
for (SendMessageBatchResponse res : response) {
//********************************************************************
// AUDIT LOG INSERT
//********************************************************************
AuditSts auditSts = new AuditSts();
auditSts.setParentType(Document.class.getSimpleName());
auditSts.setParentId(documentId);
auditSts.setText(MessageUtils.getMessage(LocaleContextHolder.getLocale(),"sts.queued"));
auditLogRepository.save(auditSts);
}
}
} catch (Throwable e) {
log.error("",e);
status.setRollbackOnly();
}
return null;
});
page++;
} while (resultPage.hasNext());
}
我检查了Hibernate日志,但是看不到任何奇怪的东西。您是否有一些提示可以指示我正确的方向?
解决方法
最后,我理解了这个问题:如果使用身份标识符生成器,Hibernate会在JDBC级别透明地禁用插入批处理。检查此回复:Hibernate disabled insert batching when using an identity identifier generator
我这样做是为了使用Jdbc模板:
jdbcTemplate.batchUpdate("insert into `AuditLog` (`createdBy`,`createdDate`,`lastModifiedBy`,`lastModifiedDate`,`sid`,`version`,`operationType`,`parentId`,`parentType`,`remoteAddress`,`text`,`type`)" +
" values (?,?,?)",new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps,int i)
throws SQLException {
Long documentId = (Long) documentsToUpdate.toArray()[i];
ps.setString(1,loggedAgent);
ps.setTimestamp(2,Timestamp.from(Instant.now()));
ps.setString(3,loggedAgent);
ps.setTimestamp(4,Timestamp.from(Instant.now()));
ps.setString(5,UUID.randomUUID().toString());
ps.setInt(6,1);
ps.setString(7,"STS");
ps.setLong(8,documentId);
ps.setString(9,Document.class.getSimpleName());
ps.setString(10,NetworkUtils.getRemoteIpFromCurrentContext());
ps.setString(11,auditLogMessage);
ps.setString(12,AuditSts.class.getSimpleName());
}
@Override
public int getBatchSize() {
return documentsToUpdate.size();
}
});
执行此插入数百项操作需要花费几毫秒的时间;与以前相比,有了很大的进步。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。