在使用Spring JPA插入数据时 一般使用CrudRepository自带的save方法。但是最近需要定时将云集群里的Scala程序计算的一些指标数据通过消息队列获取后存入本地DB,每次百万级的插入,使用该方法需要3H57min。查看JPA的源码想找个批量插入的方法提高速度。
在Spring Data JPA的源码中看到 Iterable save(Iterable entities);
查看其源码发现,其实现还是通过遍历逐个插入
save源码:
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.JpaRepository#save(java.lang.Iterable)
*/
@Transactional
public <S extends T> List<S> save(Iterable<S> entities) {
List<S> result = new ArrayList<S>();
if (entities == null) {
return result;
}
for (S entity : entities) {
result.add(save(entity));
}
return result;
}
通过在网上查找,发现可以通过Spring注解 @PersistenceContext() 实现事物管理 EntityManager 来完成批量插入,具体方法如下
package com.foundersc.ifc.adviser.decision.dao;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
/**
* Created by weimiantong on 17/6/28.
*/
public abstract class AbstractDao<T> {
@PersistenceContext()
protected EntityManager em;
@Transactional("")
public void batchInsert(List<T> list) {
for (int i = 0; i < list.size(); i++) {
em.persist(list.get(i));
if (i % 100 == 0) {
em.flush();
em.clear();
}
}
}
@Transactional()
public void batchUpdate(List<T> list) {
for (int i = 0; i < list.size(); i++) {
em.merge(list.get(i));
if (i % 100 == 0) {
em.flush();
em.clear();
}
}
}
}
该方法在service工程里单独跑单元测试是没问题的,百万级的插入缩短到2H23min。
但是当把单元测试通过的Service工程maven依赖到webapp工程进行使用时,由于webapp工程使用了多个Service需要配置多个数据源。启动报了如下错
Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jcSignalDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 6: entityManagerFactory,entityManagerFactory125,entityManagerFactoryFinancial,wxentityManagerFactory,entityManagerFactory_kh,entityManagerFactoryCommunity
jcSignalDao 需要一个事物管理,但是在spring-jpa.xml 中却配置了6个entityManagerFactory 的beam。导致不知道用哪一个。
解决方法:
通过 @Qualifier(“entityManagerFactoryCommunity”) 注解指定目标数据源的管理器即可, 正确写法如下:
package com.foundersc.ifc.adviser.decision.dao;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import java.util.List;
/**
* Created by weimiantong on 17/6/28.
*/
@Data
public abstract class AbstractDao<T> {
//@PersistenceContext(name = "entityManagerFactoryCommunity")
@Autowired
@Qualifier("entityManagerFactoryCommunity")
public EntityManagerFactory emfc;
@Transactional("transactionManagerCommunity")
public void batchInsert(List<T> list) {
EntityManager em = emfc.createEntityManager();
for (int i = 0; i < list.size(); i++) {
em.persist(list.get(i));
if (i % 100 == 0) {
em.flush();
em.clear();
}
}
em.close();//不关闭可能导致连接池异常
}
@Transactional("transactionManagerCommunity")
public void batchUpdate(List<T> list) {
EntityManager em = emfc.createEntityManager();
for (int i = 0; i < list.size(); i++) {
em.merge(list.get(i));
if (i % 100 == 0) {
em.flush();
em.clear();
}
}
em.close();
}
}