1 Mybatis中的三种sql执行器
https://mybatis.org/mybatis-3/zh/configuration.html#settings
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
@Test
public void test_() {
//SIMPLE REUSE BATCH
Arrays.stream(ExecutorType.values()).forEach((item) -> {
System.out.print(item + " ");
});
}
2 测试三种sql执行器
public class UserMapperTest {
private static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void initSqlSessionFactory() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
}
//清空表中数据, 同时重置自增序列从0开始
public void clearTable() {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.getMapper(UserMapper.class).clear();
}
/**
* @param list 插入的User集合
* @param type ExecutorType
* @param autoCommit 是否自动提交
*/
public void insertUser(List<User> list, ExecutorType type, boolean autoCommit) {
//每次测试前清空表
clearTable();
SqlSession sqlSession = sqlSessionFactory.openSession(type, autoCommit);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
long start = System.currentTimeMillis();
try {
list.stream().forEach((user -> mapper.insert(user)));
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
sqlSession.close();
}
long end = System.currentTimeMillis();
System.out.println("【"+type+"耗时】: " + (end - start) + "(ms)");
}
@Test
public void testInsertUser() {
// 初始化50000个对象
List<User> list = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
list.add(new User(i, i + "", "男", new Date(), "重庆万州", "123456"));
}
//测试
//BATCH: 批量执行器, 对相同sql进行一次预编译, 然后设置参数, 最后统一执行操作
insertUser(list, ExecutorType.BATCH, false);//【BATCH耗时】: 59105(ms)
//SIMPLE: 默认的执行器, 对每条sql进行预编译->设置参数->执行等操作
insertUser(list, ExecutorType.SIMPLE, false);//【SIMPLE耗时】: 118136(ms)
//REUSE: REUSE 执行器会重用预处理语句(prepared statements)
insertUser(list, ExecutorType.REUSE, false);//【REUSE耗时】: 114406(ms)
}
public class JDBCTest {
/**
* 获取Connection
*/
public static Connection getConn() throws SQLException {
String url = "jdbc:mysql://127.0.0.1:6033/mybatis_bash_db?serverTimezone=UTC";
String user = "root";
String password = "123456";
return DriverManager.getConnection(url, user, password);
}
/**
* 清空表
*/
public void clearTable() throws SQLException {
Statement statement = getConn().createStatement();
String sql = "truncate table mybatis_bash_db.user";
statement.executeUpdate(sql);
}
public void test_1(int count) throws SQLException {
Connection conn = getConn();
Statement statement = conn.createStatement();
for (int i = 1; i <= count; i++) {
String sql = "insert into mybatis_bash_db.user(id, username) " +
"values (" + i + ",'蔡徐坤')";
int res = statement.executeUpdate(sql);
}
}
public void test_2(int count) throws SQLException {
Connection conn = getConn();
String sql = "insert into mybatis_bash_db.user(id, username) " +
"values (?,?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
for (int i = 1; i <= count; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, "乔碧萝");
int res = pstmt.executeUpdate();
}
}
public void test_3(int count) throws SQLException {
String sql = "insert into mybatis_bash_db.user(id, username) " +
"values (1,'蔡徐坤')";
Connection conn = getConn();
PreparedStatement pstmt = conn.prepareStatement("insert into mybatis_bash_db.user(id, username) " +
"values (?,?)");
for (int i = 1; i <= count; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, "卢本伟");
pstmt.addBatch();
}
int[] resArr = pstmt.executeBatch();
}
@Test
public void test() throws SQLException {
int count = 5000;
clearTable();
long start = System.currentTimeMillis();
test_1(count);
//test_2(count);
//test_3(count);
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - start) + "(ms)");
}
2.1 SIMPLE方式
从执行日志可以看出, 每次插入操作,都会执行编译,设置参数,执行sql操作。
DEBUG [main] ==> Preparing: insert into mybatis_bash_db.user(id, username, sex, birthday, address, password) values (?, ?, ?, ?, ?, ?)
DEBUG [main] ==> Parameters: 0(Integer), 0(String), 男(String), 2020-04-01 11:18:16.44(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] <== Updates: 1
DEBUG [main] ==> Preparing: insert into mybatis_bash_db.user(id, username, sex, birthday, address, password) values (?, ?, ?, ?, ?, ?)
DEBUG [main] ==> Parameters: 1(Integer), 1(String), 男(String), 2020-04-01 11:18:16.44(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] <== Updates: 1
DEBUG [main] ==> Preparing: insert into mybatis_bash_db.user(id, username, sex, birthday, address, password) values (?, ?, ?, ?, ?, ?)
DEBUG [main] ==> Parameters: 2(Integer), 2(String), 男(String), 2020-04-01 11:18:16.44(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] <== Updates: 1
DEBUG [main] ==> Preparing: insert into mybatis_bash_db.user(id, username, sex, birthday, address, password) values (?, ?, ?, ?, ?, ?)
DEBUG [main] ==> Parameters: 3(Integer), 3(String), 男(String), 2020-04-01 11:18:16.44(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] <== Updates: 1
...
2.2 REUSE方式
从执行日志可以看出,只有第一次插入操作,执行了sql编译步骤,对其它插入操作执行了设置参数,执行sql的操作。
DEBUG [main] ==> Preparing: insert into mybatis_bash_db.user(id, username, sex, birthday, address, password) values (?, ?, ?, ?, ?, ?)
DEBUG [main] ==> Parameters: 0(Integer), 0(String), 男(String), 2020-04-01 11:23:58.522(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] <== Updates: 1
DEBUG [main] ==> Parameters: 1(Integer), 1(String), 男(String), 2020-04-01 11:23:58.522(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] <== Updates: 1
DEBUG [main] ==> Parameters: 2(Integer), 2(String), 男(String), 2020-04-01 11:23:58.522(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] <== Updates: 1
DEBUG [main] ==> Parameters: 3(Integer), 3(String), 男(String), 2020-04-01 11:23:58.522(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] <== Updates: 1
...
2.3 BATCH
从执行日志可以看出,只对第一次插入操作执行了sql编译操作,对其它插入操作仅执行了设置参数操作,最后统一执行。
DEBUG [main] ==> Preparing: insert into mybatis_bash_db.user(id, username, sex, birthday, address, password) values (?, ?, ?, ?, ?, ?)
DEBUG [main] ==> Parameters: 0(Integer), 0(String), 男(String), 2020-04-01 11:26:24.683(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] ==> Parameters: 1(Integer), 1(String), 男(String), 2020-04-01 11:26:24.683(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] ==> Parameters: 2(Integer), 2(String), 男(String), 2020-04-01 11:26:24.683(Timestamp), 重庆万州(String), 123456(String)
DEBUG [main] ==> Parameters: 3(Integer), 3(String), 男(String), 2020-04-01 11:26:24.683(Timestamp), 重庆万州(String), 123456(String)
...
docker run -d --rm --name mybatis_db -p 6033:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0.18
jdbc:mysql://127.0.0.1:6033?serverTimezone=UTC
CREATE DATABASE mybatis_bash_db;
USE mybatis_bash_db;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`(
`id` int(11) NOT NULL,
`username` varchar(20) DEFAULT NULL,
`sex` varchar(6) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`address` varchar(20) DEFAULT NULL,
`password` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
);
src/main/resources/mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:6033/mybatis_bash_db?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.executor.mapper"/>
</mappers>
</configuration>
src/main/resources/log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%5p [%t] %m%n"/>
</layout>
</appender>
<logger name="com.executor.mapper">
<level value="DEBUG"/>
</logger>
<root>
<level value="ERROR"/>
<appender-ref ref="STDOUT"/>
</root>
</log4j:configuration>
src/main/resources/com/executor/mapper/UserMapper.xml
<?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">
<mapper namespace="com.executor.mapper.UserMapper">
<resultMap type="com.executor.domain.User" id="UserMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="sex" column="sex" jdbcType="VARCHAR"/>
<result property="birthday" column="birthday" jdbcType="TIMESTAMP"/>
<result property="address" column="address" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
</resultMap>
<!-- 清空表中数据, 同时重置自增序列从0开始 -->
<delete id="clear">
truncate table mybatis_bash_db.user
</delete>
<!--查询单个-->
<select id="queryById" resultMap="UserMap">
select id,
username,
sex,
birthday,
address,
password
from mybatis_bash_db.user
where id = #{id}
</select>
<!--新增所有列-->
<insert id="insert" keyProperty="id" useGeneratedKeys="true">
insert into mybatis_bash_db.user(id, username, sex, birthday, address, password)
values (#{id}, #{username}, #{sex}, #{birthday}, #{address}, #{password})
</insert>
</mapper>
com.executor.domain.User
@Data
public class User implements Serializable {
private static final long serialVersionUID = 414848905562793591L;
private Integer id;
private String username;
private String sex;
private Date birthday;
private String address;
private String password;
public User() {
}
public User(Integer id, String username, String sex, Date birthday, String address, String password) {
this.id = id;
this.username = username;
this.sex = sex;
this.birthday = birthday;
this.address = address;
this.password = password;
}
}
com.executor.mapper.UserMapper
public interface UserMapper {
/**
* 清空表
*/
void clear();
/**
* 通过ID查询单条数据
* @param id 主键
* @return 实例对象
*/
User queryById(Integer id);
/**
* 新增数据
* @param user 实例对象
* @return 影响行数
*/
int insert(User user);
}
pom.xml
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
com.executor.mapper.UserMapperTest
public class UserMapperTest {
private static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void initSqlSessionFactory() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
}
//清空表中数据, 同时重置自增序列从0开始
public void clearTable() {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.getMapper(UserMapper.class).clear();
}
@Test
public void testEnvIsOk() {
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.insert(new User(1, "蔡徐坤", "男", new Date(), "重庆万州", "123456"));
System.out.println(res);
System.out.println(mapper.queryById(1));
mapper.clear();
}
/**
* @param list 插入的User集合
* @param type ExecutorType
* @param autoCommit 是否自动提交
*/
public void testSave(List<User> list, ExecutorType type, boolean autoCommit) {
clearTable();
SqlSession sqlSession = sqlSessionFactory.openSession(type, autoCommit);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
long start = System.currentTimeMillis();
try {
list.stream().forEach((user -> mapper.insert(user)));
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.rollback();
}
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - start) + "(ms)");
}
@Test
public void test_() {
// 初始化10000个对象
List<User> list = new ArrayList<>();
for (int i = 0; i < 2; i++) {
list.add(new User(i, i + "", "男", new Date(), "重庆万州", "123456"));
}
//测试
System.out.println("BATCH");
testSave(list, ExecutorType.BATCH, false);
System.out.println("SIMPLE");
testSave(list, ExecutorType.SIMPLE, false);
System.out.println("REUSE");
testSave(list, ExecutorType.REUSE, false);
}
}