1.通用Mapper
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
/**
* 基础mapper,实现增删改查,分页等基本功能
*
* @param 泛型参数
* @author lw
* @since 2019年2月14日 13:48:17
*/
public interface BaseMapper extends Mapper, MySqlMapper {
}
2.DO对象的Mapper继承通用Mapper
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author lw
* @since 2019年2月14日 13:50:22
*/
@Mapper
public interface AdminMapper extends BaseMapper {
@Select("select * from t_wj_seats_admin")
List list();
void batchInsert(List admins);
}
3.通用BaseService,这里只定义了批量插入
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
/**
* 通用业务逻辑层
*
* @author lw
* @version 1.0
* @since 2019/2/21 上午 11:26
*/
public class BaseService {
@Autowired
private BaseMapper baseMapper;
@Autowired
private SqlSessionFactory sqlSessionFactory;
protected final Class doClass;
private Integer batchCommitCount = 10000;
public BaseService() {
doClass = getGenericParamClass();
}
public BaseService(Integer batchCommitCount) {
this.batchCommitCount = batchCommitCount;
doClass = getGenericParamClass();
}
@Transactional(rollbackFor = Exception.class)
public boolean batchInsert(List DOs) {
if (CollectionUtils.isEmpty(DOs)) {
return false;
}
SqlSession sqlSession = null;
// 新获取一个模式为BATCH,关闭自动提交的session
try {
sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
// 通过新的sqlSession获取Mapper
BaseMapper mapper = (BaseMapper) sqlSession.getMapper(getMapperClass());
for (int i = 0, len = DOs.size(); i < len; i++) {
mapper.insertSelective(DOs.get(i));
if (i % batchCommitCount == 0 && i >= batchCommitCount) {
// 提交,无法回滚
sqlSession.commit();
sqlSession.clearCache();
}
}
sqlSession.commit();
sqlSession.clearCache();
} catch (Exception e) {
// 回滚
sqlSession.rollback();
e.printStackTrace();
return false;
} finally {
sqlSession.close();
}
return true;
}
/**
* 获取对应的Mapper Class
*
* @return class
*/
private Class getMapperClass() {
Class> clazz = baseMapper.getClass().getSuperclass();
try {
Field h = clazz.getDeclaredField("h");
h.setAccessible(true);
Object hObject = h.get(baseMapper);
Field mapperInterface = hObject.getClass().getDeclaredField("mapperInterface");
mapperInterface.setAccessible(true);
Object mapperObject = mapperInterface.get(hObject);
Field name = mapperObject.getClass().getDeclaredField("name");
name.setAccessible(true);
Object nameValue = name.get(mapperObject);
return Class.forName((String) nameValue);
} catch (Exception e) {
e.printStackTrace();
}
throw new RuntimeException("基础BaseService中无法获取mapper真实Class对象");
}
/**
* 获取泛型参数Class对象
*
* @return Class对象
*/
private Class getGenericParamClass() {
ParameterizedType genericSuperclass = (ParameterizedType) this.getClass().getGenericSuperclass();
Type[] actualTypeArguments = genericSuperclass.getActualTypeArguments();
return (Class) actualTypeArguments[0];
}
}
4.连接url加入rewriteBatchedStatements=true
jdbc:mysql://localhost:3306/db?useSSL=false&rewriteBatchedStatements=true
4.具体业务逻辑类继承通用Service,直接调用父类的批量方法就好
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 管理员业务逻辑类
*
* @author lw
* @since 2019年2月14日 15:43:08
*/
@Service
public class AdminService extends BaseService {
@Autowired
private AdminMapper mapper;
@Transactional(rollbackFor = Exception.class)
public void test() {
List admins = genList();
long startTime = System.currentTimeMillis();
batchInsert(admins);
long endTime = System.currentTimeMillis();
System.out.println(
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()) +
" baseService 批量插入" + admins.size() + "条 耗时(ms):" + (endTime - startTime));
}
private List genList() {
int len = 100;
List admins = new ArrayList<>(len);
for (int i = 0; i < len; i++) {
Admin admin = new Admin();
admin.setUsername("测试" + i);
admins.add(admin);
}
return admins;
}
}
以上批量操作就完成啦
-------------------------------------------------------------------------------------------------------------------------------------------------
将BaseService的批量插入与使用xml的foreach进行批量插入进行性能对比
1.xml文件
insert t_admin (username)
values
(#{admin.username})
2.测试类
@Transactional(rollbackFor = Exception.class)
public void test() {
List admins = genList();
long startTime = System.currentTimeMillis();
batchInsert(admins);
long endTime = System.currentTimeMillis();
System.out.println(
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()) +
" baseService 批量插入" + admins.size() + "条 耗时(ms):" + (endTime - startTime));
}
@Transactional(rollbackFor = Exception.class)
public void test1() {
List admins = genList();
long startTime = System.currentTimeMillis();
mapper.batchInsert(admins);
long endTime = System.currentTimeMillis();
System.out.println(
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()) +
" xml foreach 批量插入" + admins.size() + "条 耗时(ms):" + (endTime - startTime));
}
private List genList() {
int len = 100;
List admins = new ArrayList<>(len);
for (int i = 0; i < len; i++) {
Admin admin = new Admin();
admin.setUsername("测试" + i);
admins.add(admin);
}
return admins;
}
3.结果分析
5千条数据 | 一万五千条数据 | 十万条数据 | 一千万条数据 | |
xml的foreach的批量插入 耗时(ms) | 153 | 388 | 2901 | OutOfMemoryError |
BaseService的批量插入 耗时(ms) | 391 | 1399 | 29035 | 时间太长,未计量,但确实可以插入 |
可以看到,使用xml的foreach的批量插入方式相对而言效率要高很多,当数据量很大时,会耗空内存,
但是这可以进行编程控制,比如进行多次的批量插入.