JPA-常见的优化点

前言

JPA作为主流的持久层框架,被广大程序员所使用,基本的使用方法请参考JPA的常见用法。虽然JPA在编码上提供了很多的便利,但是其接口提供的插入数据的方法效率非常低,下面就简单介绍下效率低的原因和解决办法。

save和saveAll方法

单从方法签名来看,save方法用来插入单个数据,而saveAll方法插入多个数据。但是仔细看saveAll的方法实现,会发现saveAll里面是循环调用save方法来插入数据,所以很自然地以为saveAllsave方法插入数据的时间差不多。但是实际测试的结果却表明两者的差距在一到两倍左右,以下是同一机器上的测试数据(1k),单位毫秒:

savesaveAll
18216273579

那么,既是循环调用,两者应该差距不大才对,怎么会差距这么大呢?原因是事务。仔细看源码会发现,save方法上有@Transactional注解,而saveAll上也有@Transactional注解。对于嵌套事务,要根据事务的传播特性(spring默认是REQUIRED,即支持当前事务,如果不存在,则创建一个新事务。)决定事务的行为。这也是为啥saveAll会比save快很多的原因:循环里的save方法复用外层saveAll方法的事务,节省了很多事务方面的开销。

persist和merge

尽管saveAll方法在插入性能上提升了很多,但是和直接用jdbc的批量插入相比还有差距,那么原因是什么呢?仔细观察程序运行过程中控制台输出的sql语句,会发现save方法调用时,会先查询后插入。对于确定是新数据的对象,也会查询,这也正是save方法的实现。但是插入之前的查询确实影响了很多的性能,如果可以,对于确定是新的记录,能不能不要查询步骤呢?答案是可以的,那就是直接使用EntityManagerpersist方法。对于确定是已经存在于系统的记录,直接使用merge。这样少了一个查询操作,就可以显著提升插入的效率。

for (int i = 0; i < poList.size(); i++) {
	PO po = poList.get(i);
	entityManager.persist(po);
	if ((i+1) % 1024 == 0 || i == poList.size()) {
		//及时保存到数据
		entityManager.flush();
		//及时清理缓存,避免堆溢出
		entityManager.clear();
	}
}

JDBC

追求极致速度时,可以使用设置连接的自动提交为false,即setAutoCommit(false),手动管理事务。批量赋值之后,再统一提交或者回滚。

try{
	connection.setAutoCommit(false);
	//以下是批量赋值
	...
	connection.commit();
}catch(Exception e){
	connection.rollback();
}finally{
	connection.close();
}

参考JDBC工具组件DBUtils项目。

JdbcTemplate

连接url配置rewriteBatchedStatements=true参数。

结论

增删改尽量用JdbcTemplateJDBC查询才使用JPA

批量增删改效果不佳时,检查链接url是否添加了rewriteBatchedStatements=true参数。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值