添加maven依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
只需在spring boot项目中添加mybatis-spring-boot-starter依赖即可,我们示例使用的是mysql所以添加了mysql的数据库驱动依赖。可根据自身的需求替换。
编写MybatisConfig配置类
import com.yyoo.boot.config.MybatisBaseConfig;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import javax.sql.DataSource;
@Configuration
@Slf4j
@Data
// mapper* 表示mapper包及其子包
@MapperScan(value = {
"com.yyoo.boot.mybatis.mapper*"
})
@ConfigurationProperties("my.mybaties")
public class MybatisConfig extends MybatisBaseConfig{
/**
* Mybatis别名文件夹
*/
private String aliasesPackage;
/**
* 映射的 mapper文件
*/
private String[] mapperLocations;
@Bean
@ConfigurationProperties("my.hikari")
public HikariConfig getHikariConfig(){
return new HikariConfig();
}
@Bean
public HikariDataSource hikariDataSource(HikariConfig hikariConfig){
return new HikariDataSource(hikariConfig);
}
@Bean(name = "sessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("hikariDataSource") DataSource ds) throws Exception {
log.info("数据源:{},aliasesPackage:{}",ds,this.aliasesPackage);
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(ds);
if(this.aliasesPackage != null && !"".equals(this.aliasesPackage)) {
factoryBean.setTypeAliasesPackage(this.aliasesPackage);
}
//指定mapper xml目录
Assert.notEmpty(this.mapperLocations,"扫描的Mapper xml不能为空");
factoryBean.setMapperLocations(resolveMapperLocations(this.mapperLocations));
return factoryBean.getObject();
}
@Bean("sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory factory) {
// 使用上面配置的Factory
// 要设置Template为BATCH方式,请使用new SqlSessionTemplate(factory,ExecutorType.BATCH);
SqlSessionTemplate template = new SqlSessionTemplate(factory);
return template;
}
}
@MapperScan扫描Mapper接口所在的包,可以列多个
本文使用HikariDataSource所为数据源,可根据自身情况替换
resolveMapperLocations方法在MybatisBaseConfig中定义,下面贴出代码,实现可扫描classpath*:mapper/Mapper.xml,classpath:mapper/*/*Mapper.xml这样的配置,所以此处没有直接使用Resource[]数组。
事务配置,本示例中没有自定义配置事务管理器PlatformTransactionManager,spring会自动根据我们的数据源配置相应的事务管理器,一般情况下是使用DataSourceTransactionManager或JtaTransactionManager。
MybatisBaseConfig
package com.yyoo.boot.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Slf4j
public class MybatisBaseConfig {
protected Resource[] resolveMapperLocations(String[] mapperLocations) {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList<Resource>();
if (mapperLocations != null) {
for (String mapperLocation : mapperLocations) {
log.debug("数据源扫描的映射文件地址{}",mapperLocation);
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException e) {
// ignore
e.printStackTrace();
}
}
}
return resources.toArray(new Resource[resources.size()]);
}
}
Mapper接口和Mapper.xml文件
package com.yyoo.boot.mybatis.mapper;
import com.yyoo.boot.mybatis.beans.MyEmp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface MyEmpMapper {
int insert(MyEmp myEmp);
int insertForeach(@Param("myEmpList") List<MyEmp> myEmpList);
}
注意包名@MapperScan中配置的包名需要包含到
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace我们对应到了我们的EmpMapper接口 -->
<mapper namespace="com.yyoo.boot.mybatis.mapper.MyEmpMapper">
<insert id="insert" parameterType="MyEmp">
insert into t_my_emp(name,age,sex)
values(#{name},#{age},#{sex})
</insert>
<insert id="insertForeach">
insert into t_my_emp(name,age,sex)
values
<foreach item="myEmp" collection="myEmpList" separator=",">
(#{myEmp.name},#{myEmp.age},#{myEmp.sex})
</foreach>
</insert>
</mapper>
注意xml文件所在的路径,必须是mapperLocations配置所在的文件夹(此处在resource目录下的mapper文件夹)
定义Service以及相应的测试代码
package com.yyoo.boot.mybatis.service;
public interface MyEmpService {
void insert(int num);
void insertForeach(int num);
}
package com.yyoo.boot.mybatis.service;
import com.yyoo.boot.mybatis.AutoNameUtil;
import com.yyoo.boot.mybatis.beans.MyEmp;
import com.yyoo.boot.mybatis.mapper.MyEmpMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Service
public class MyEmpServiceImpl implements MyEmpService{
@Resource
MyEmpMapper myEmpMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void insert(int num) {
for (int i = 0;i < num;i++) {
Random random = new Random();
MyEmp myEmp = new MyEmp();
myEmp.setName(AutoNameUtil.autoSurAndName());
myEmp.setAge(random.nextInt(50) + 15);// 15岁及以上
myEmp.setSex(random.nextInt(2));
myEmpMapper.insert(myEmp);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void insertForeach(int num) {
List<MyEmp> list = new ArrayList<>();
for (int i = 0;i < num;i++) {
Random random = new Random();
MyEmp myEmp = new MyEmp();
myEmp.setName(AutoNameUtil.autoSurAndName());
myEmp.setAge(random.nextInt(50) + 15);// 15岁及以上
myEmp.setSex(random.nextInt(2));
list.add(myEmp);
}
myEmpMapper.insertForeach(list);
}
}
insert方法其实就是直接插入,根据SqlSessionTemplate创建是是否使用BATCH模式来区分直接插入和BATCH插入。
package com.yyoo.boot.mybatis;
import com.yyoo.boot.Application;
import com.yyoo.boot.mybatis.service.MyEmpService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test1 {
@Resource
private MyEmpService myEmpService;
@Test
public void t1(){
long start = System.currentTimeMillis();
myEmpService.insertForeach(1000000);
long end = System.currentTimeMillis();
System.out.println("直接insert执行时间:"+(end - start));
}
@Test
public void t2(){
long start = System.currentTimeMillis();
myEmpService.insertForeach(1000000);
long end = System.currentTimeMillis();
System.out.println("foreach执行时间:"+(end - start));
}
}
在SpringBoot下再次进行一次执行时间对比
执行方式 | 100条 | 1000条 | 1w条 | 10w条 | 100w条 |
循环insert | 282ms | 1557ms | 6714ms | 48256ms | 450864ms |
foreach插入 | 212ms | 279ms | 840ms | 4915ms | 报错 |
BATCH插入 | 173ms | 510ms | 2989ms | 17579ms | 174985ms |
可以看到foreach方式插入优势明显,只是数据量过大时会报错而已。建议还是使用foreach方式,然后通过代码控制foreach一次执行的数据体量。