Mybatis
Posted top啦它
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis相关的知识,希望对你有一定的参考价值。
JDBC执行流程
https://blog.csdn.net/YJT180/article/details/99828947
Mybatis执行流程
详细来看就是
首先学习会话和执行器两部分
Mybatis核心执行组件——会话(SqlSession)
首先先看Executor最简单的实现——SimpleExecutor
先引入Mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
package com.example.xuexitongtwo;
import org.apache.ibatis.executor.SimpleExecutor;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
class XuexitongtwoApplicationTests
private JdbcTransaction jdbcTransaction;
private Configuration configuration;
private Connection connection;
@Test
public void test1() throws SQLException, IOException
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = factoryBuilder.build(is);
configuration = build.getConfiguration();
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/xuexitong?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC","root","root");
jdbcTransaction = new JdbcTransaction(connection);
SimpleExecutor executor = new SimpleExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.xuexitongtwo.mapper.TestMapper.selectIdByUsername");
// 之所以需要再传一次参数名,是因为没有使用executor.query
List<Object> es = executor.doQuery(ms, "a1", RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER ,ms.getBoundSql("a1"));
executor.doQuery(ms, "a1", RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER ,ms.getBoundSql("a1"));
System.out.println("id:"+es.get(0));
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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="$spring.datasource.driver-class-name"></property>
<property name="url" value="$spring.datasource.url"></property>
<property name="username" value="$spring.datasource.username"></property>
<property name="password" value="$spring.datasource.password"></property>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/testmapper.xml"/>
</mappers>
</configuration>
运行测试类后输出:
可以看到一共预编译了两次。因为我使用的是SimpleExecutor
如果我没有执行两次,而是执行两百次,那么每一次都要进行预处理,是非常耗时并且浪费性能的。
那么可以使用下面的可重用执行器
可重用执行器——ReuseExecutor
simple每次调用都会创建一个preparestatement,然后去预编译。而resue使用了一个map来存statement,每次调用,直接从map里找,找到了就复用,不用再去编译sql。
ReuseExecutor代码如下:
// 可重用执行器
@Test
public void test2() throws SQLException, IOException
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = factoryBuilder.build(is);
configuration = build.getConfiguration();
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/xuexitong?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC","root","root");
jdbcTransaction = new JdbcTransaction(connection);
ReuseExecutor executor = new ReuseExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.xuexitongtwo.mapper.TestMapper.selectIdByUsername");
// 之所以需要再传一次参数名,是因为没有使用executor.query
List<Object> es = executor.doQuery(ms, "a1", RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER ,ms.getBoundSql("a1"));
executor.doQuery(ms, "a1", RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER ,ms.getBoundSql("a1"));
System.out.println("id:"+es.get(0));
输出如下:
可见,只预编译了一次。
如果要执行大量的修改操作,如果要执行一万条修改操作,一条一条的执行的话,就要执行一万次,有没有其他方法呢?怎么做?这里就引出了批处理执行器——BatchExecutor(比如说:先处理一百条,处理完后在执行一百条)
批处理执行器——BatchExecutor
@Test
public void test3() throws SQLException, IOException
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = factoryBuilder.build(is);
configuration = build.getConfiguration();
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/xuexitong?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC","root","root");
jdbcTransaction = new JdbcTransaction(connection);
BatchExecutor executor = new BatchExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.xuexitongtwo.mapper.TestMapper.selectIdByUsername");
// 之所以需要再传一次参数名,是因为没有使用executor.query
List<Object> es = executor.doQuery(ms, "a1", RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER ,ms.getBoundSql("a1"));
executor.doQuery(ms, "a1", RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER ,ms.getBoundSql("a1"));
System.out.println("id:"+es.get(0));
可以看到,预编译了两次,明明使用了批处理执行器,那为什么出现了两次呢?这是因为BatchExecutor仅在进行改操作时才会进行批处理。
下面进行修改操作
@CacheNamespace(blocking = true)
public interface TestMapper
public Integer selectIdByUsername(String username);
@Update("update user set email=#arg1 where username=#arg0")
int setEmail(String username,String email);
@Test
public void test3() throws SQLException, IOException
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = factoryBuilder.build(is);
configuration = build.getConfiguration();
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/xuexitong?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC","root","root");
jdbcTransaction = new JdbcTransaction(connection);
BatchExecutor executor = new BatchExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.xuexitongtwo.mapper.TestMapper.setEmail");
Map param = new HashMap();
param.put("arg0","a1");
param.put("arg1","emailss");
executor.doUpdate(ms,param);
executor.doUpdate(ms,param);
之后查看数据库,发现并没有修改数据。因为批处理操作需要手动刷新。
代码如下:
@Test
public void test3() throws SQLException, IOException
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = factoryBuilder.build(is);
configuration = build.getConfiguration();
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/xuexitong?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC","root","root");
jdbcTransaction = new JdbcTransaction(connection);
BatchExecutor executor = new BatchExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.xuexitongtwo.mapper.TestMapper.setEmail");
Map param = new HashMap();
param.put("arg0","a1");
param.put("arg1","emailss");
executor.doUpdate(ms,param);
executor.doUpdate(ms,param);
executor.doFlushStatements(false);//执行刷新
数据库数据被修改
执行了一次预处理,两次数据填充
不是说Executor中会用到到缓存吗?我发现里面并没有使用到缓存。
下面看
因为要在SimpleExecutor和ReuseExecutor和BatchExecutor中都使用缓存,所以给他们继承了同一个父类BaseExecutor
在BaseExecutor中实现了一级缓存,以及获取连接的方法。因为上面我都是直接调用的这三个实现类,所以没有使用到缓存。
在BaseExecutor中实现了两个方法query和update
在query中调用了抽象方法doQuery
在update中调用了抽象方法doUpdate
如下:
doQuery和doUpdate需要在SimpleExecutor和ReuseExecutor和BatchExecutor中实现。
执行query方法
@Test
public void test4() throws SQLException, IOException
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = factoryBuilder.build(is);
configuration = build.getConfiguration();
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/xuexitong?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC","root","root");
jdbcTransaction = new JdbcTransaction(connection);
Executor executor = new SimpleExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.xuexitongtwo.mapper.TestMapper.selectIdByUsername");
executor.query(ms,"a1",RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
executor.query(ms,"a1",RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
运行结果
可以发现在使用的是SimpleExecutor执行器的情况下,因为使用到了一级缓存,所以只预编译了一次。并且也只进行了一次调用。
我看下ReuseExecutor,发现执行结果也是这样。因为第二次调用会走缓存的逻辑。
@Test
public void test4() throws SQLException, IOException
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = factoryBuilder.build(is);
configuration = build.getConfiguration();
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/xuexitong?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC","root","root");
jdbcTransaction = new JdbcTransaction(connection);
Executor executor = new ReuseExecutor(configuration,jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.example.xuexitongtwo.mapper.TestMapper.selectIdByUsername");
executor.query(ms,"a1",RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
executor.query(ms,"a1",RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
下面进行调试。
1、
executor.query(ms,"a1",RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);
2、
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
3、
@Override
public <E> List<E以上是关于Mybatis的主要内容,如果未能解决你的问题,请参考以下文章