Mybatis三種執行器

 


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&amp;useSSL=false&amp;serverTimezone=UTC&amp;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);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章