mybatis源码---Executor执行器
java原生jdbc操作
流程
- 读取配置信息(基本信息)
- 加载驱动
- 获取连接
- 获取PreparedStatement的实例 (或:预编译sql语句)
- 针对结果集的处理(增删改用 execute() 不需处理结果集,查询用 .executeQuery(); )
- 关闭连接
流程图
示例
//获取连接
Connection conn = JDBCUtils.getConnection();
String sql = "insert intocustomers(name,email,birth,photo)values(?,?,?,?)";
//调用 PreparedStatement进行预编译
PreparedStatement ps = conn.prepareStatement(sql);
// 填充占位符
ps.setString(1, "徐海强");
ps.setString(2, "xhq@126.com");
ps.setDate(3, new Date(new java.util.Date().getTime()));
// 操作Blob类型的变量
FileInputStream fis = new FileInputStream("xhq.png");
ps.setBlob(4, fis);
//执行
ps.execute();
fis.close();
JDBCUtils.closeResource(conn, ps);
java原生的执行器
1.Statement可以支持重用执行多个静态SQL,并可以设置addBatch、setFetchSize等操作。Statement的每次执行都是给数据库发送一个静态SQL。多次执行,即发送多个静态SQL。
2.PreparedStatement可以对SQL进行预编译,可以有效防止SQL注入(参数转义话在数据端执行,并非在Applicattion)。并且,每次执行都是给数据库发送一个SQL,加上若干组参数。
3.CallableStatement集成以上两个接口的基础上,扩展了返回结果的读写。
mybatis工作流程
Mybatis作为封装JDBC操作的半自动框架,也离不开JDBC的基本流程,以及java.sql给出的规范。
mybatis执行流程
Mybatis将Connection对象维护交由SqlSession这个环节来处理,将SQL预编译与执行交给Executor这个环节来处理,将结果集提取以及数的处理交给StatemntHandler来处理。(那么执行器扮演者jdbc重要的内容部分)
Executor执行器
结构体系
1.BaseExecutor:作为Executor的基本抽象实现,里边提取了连接维护、一级缓存的公有功能,供子类复用。
2.CachingExecutor:作为BaseExecutor的一个装饰器,用来负责二级缓存功能。而其他操作都是丢给BaseExecutor来操作。
3.Mybatis的执行器有主要有三种,SimpleExecutor、ReuseExecutor、BatchExecutor。而且默认情况下,Mybatis的三种执行器都底层都调用PreparedStatement。
SimpleExecutor执行器
简单执行每次都会创建一个新的预处理器(PrepareStatement)
源码:
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//初始化statement,调用prepareStatement就会新创建一个statement(默认使用PreparedStatement)
stmt = prepareStatement(handler, ms.getStatementLog());
//执行,调用对应的PreparedStatement的execute执行
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
//每次执行都会创建一个新Statement 处理
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
}
前置代码
//我们采用执行器的方式运行mybatis
//全局配置
private Configuration configuration;
private Transaction transaction;
private Connection connection;
@Before
public void init() throws Exception {
//加载mybatis文件
InputStream inputStream = Resources.getResourceAsStream("mybats.xml");
//加载jdbc配置
InputStream in = Resources.getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(in);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
configuration=sqlSessionFactory.getConfiguration();
connection= DriverManager.getConnection(properties.getProperty("jdbc.url"),properties.getProperty("jdbc.username"),properties.getProperty("jdbc.password"));
transaction=new JdbcTransaction(connection);
}
测试
@Test
public void simpleEXc() throws SQLException {
//SimpleExecutor简单执行器
/*
无论sql是否相同,每次都会进行预编译
*/
SimpleExecutor executor=new SimpleExecutor(configuration,transaction);
MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
//两条相同的sql
List<Object> objects = executor.doQuery(statement, 1,
RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
statement.getBoundSql(1));
System.out.println(objects.get(0));
List<Object> objects2 = executor.doQuery(statement, 1,
RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
statement.getBoundSql(1));
System.out.println(objects2.get(0));
}
执行
DEBUG 08-10 14:46:48,978 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 14:46:49,019 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 14:46:49,071 <== Total: 1 (BaseJdbcLogger.java:137)
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
DEBUG 08-10 14:46:49,084 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 14:46:49,084 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 14:46:49,086 <== Total: 1 (BaseJdbcLogger.java:137)
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
-------------------------------------------------------------------------------------
可以看出两次相同的sql语句,每次都会调用PreparedStatement进行预编译
ReuseExecutor执行器
可重用执行器,底层维护了一个Map<String sql,Statement stmt> 来捕捉到相同的SQL,则直接取对应缓存的Statement进行执行,所以对于相同SQL(包括query、update),不同参数,则只进行一次预编译。
源码
//看prepareStatement方法
public class ReuseExecutor extends BaseExecutor {
//内部维护了一个HashMap容器存放执行的sql语句
private final Map<String, Statement> statementMap = new HashMap<>();
public ReuseExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}
@Override
protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.queryCursor(stmt);
}
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {
for (Statement stmt : statementMap.values()) {
closeStatement(stmt);
}
statementMap.clear();
return Collections.emptyList();
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
//执行器执行前检查是否存在sql
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
//不存在,则会新建一个Statement存入map
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
//检查map容器中是否存在要执行的sql
private boolean hasStatementFor(String sql) {
try {
Statement statement = statementMap.get(sql);
return statement != null && !statement.getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
//通过sql得到对应的statement
private Statement getStatement(String s) {
return statementMap.get(s);
}
//存入一个statement
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
}
测试
@Test
public void ReuseEXc() throws SQLException {
//ReuseExecutor 可重用执行器
/*
SQL语句相同时,在已经域编译后,不需要在次编译,
*/
ReuseExecutor executor= new ReuseExecutor(configuration,transaction);
MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
List<Object> objects = executor.doQuery(statement, 1,
RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
statement.getBoundSql(1));
List<Object> objects2 = executor.doQuery(statement, 2,
RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
statement.getBoundSql(2));
}
执行
DEBUG 08-10 15:11:15,432 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 15:11:15,471 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 15:11:15,513 <== Total: 1 (BaseJdbcLogger.java:137)
//下面是第二次执行相同的语句,可以看出并没有再次进行预编译,而是直接进行参数处理
DEBUG 08-10 15:11:15,516 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 15:11:15,517 <== Total: 1 (BaseJdbcLogger.java:137)
BatchExecutor执行器(是否会重用sql?)
先来测试一下查询
@Test
public void BatchEXc() throws SQLException {
/*BatchExecutor 批处理执行器 简化插入和修改,对于查询,和simple执行器一样,先将所有语句编译好后,再交给数据库进行
批处理操作必须手动刷新`在这里插入代码片`
*/
BatchExecutor executor= new BatchExecutor(configuration,transaction);
MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
List<Object> objects = executor.doQuery(statement, 1,
RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
statement.getBoundSql(1));
List<Object> objects2 = executor.doQuery(statement, 2,
RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
statement.getBoundSql(2));
executor.doFlushStatements(false);
}
执行
DEBUG 08-10 15:16:46,375 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 15:16:46,414 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 15:16:46,451 <== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 08-10 15:16:46,453 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 15:16:46,453 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 15:16:46,455 <== Total: 1 (BaseJdbcLogger.java:137)
可以看出两次相同的sql编译了两次
测试更新
@Test
public void BatchEXc() throws SQLException {
/*BatchExecutor 批处理执行器 简化插入和修改,对于查询,和simple执行器一样,先将所有语句编译好后,再交给数据库进行
批处理操作必须手动刷新才会执行结果
*/
BatchExecutor executor= new BatchExecutor(configuration,transaction);
MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.updateByPrimaryKey");
int update = executor.doUpdate(statement, new User(6, "dfgtb", "fsdfsf", null, null));
int update2 = executor.doUpdate(statement, new User(7, "dfhjgtb", "fserfrg", null, null));
executor.doFlushStatements(false);
}
执行
DEBUG 08-10 15:25:13,548 ==> Preparing: update t_user set user_name = ?, pass_word = ?, email = ?, td_id = ? where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 15:25:13,582 ==> Parameters: dfgtb(String), fsdfsf(String), null, null, 6(Integer) (BaseJdbcLogger.java:137)
下一次更新没有再次编译,直接进行参数处理
DEBUG 08-10 15:25:13,583 ==> Parameters: dfhjgtb(String), fserfrg(String), null, null, 7(Integer) (BaseJdbcLogger.java:137)
那是不是认为BatchExecutor 也会重用sql呢?
再测试混合情况
@Test
public void BatchEXc() throws SQLException {
/*BatchExecutor 批处理执行器 简化插入和修改,对于查询,和simple执行器一样,先将所有语句编译好后,再交给数据库进行
批处理操作必须手动刷新才会执行结果
*/
BatchExecutor executor= new BatchExecutor(configuration,transaction);
MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.updateByPrimaryKey");
int update = executor.doUpdate(statement, new User(6, "dfgtb", "fsdfsf", null, null));
MappedStatement statement2=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
List<Object> objects = executor.doQuery(statement2, 1,
RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
statement2.getBoundSql(1));
int update2 = executor.doUpdate(statement, new User(7, "dfhjgtb", "fserfrg", null, null));
executor.doFlushStatements(false);
}
执行
DEBUG 08-10 15:31:25,807 ==> Preparing: update t_user set user_name = ?, pass_word = ?, email = ?, td_id = ? where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 15:31:25,845 ==> Parameters: dfgtb(String), fsdfsf(String), null, null, 6(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 15:31:25,849 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 15:31:25,850 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 15:31:25,889 <== Total: 1 (BaseJdbcLogger.java:137)
//下面这个更新按理来说上面已经编译过一次,既然上次觉得会重用SQL,那么下次就应该直接进行参数处理,可是为什么下次又进行了一次编译?
DEBUG 08-10 15:31:25,891 ==> Preparing: update t_user set user_name = ?, pass_word = ?, email = ?, td_id = ? where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 15:31:25,891 ==> Parameters: dfhjgtb(String), fserfrg(String), null, null, 7(Integer) (BaseJdbcLogger.java:137)
源码解决问题?
BaseExecutor执行器
源码
BaseExecutor结构
测试
@Test
public void BASET() throws SQLException {
/*
在BaseExecutor 的query和update中使用了缓存,这两个方法会调用doquery和doupdate方法(由子类实现)执行
所以BaseExecutor和其子类在使用query和update才会调用一级缓存机制
*/
BaseExecutor executor=new SimpleExecutor(configuration,transaction );
MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
List<Object> query = executor.query(statement, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
System.out.println(query.get(0));
List<Object> query2=executor.query(statement,1,RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER);
System.out.println(query2.get(0));
}
执行
DEBUG 08-10 16:04:07,338 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 16:04:07,382 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 16:04:07,429 <== Total: 1 (BaseJdbcLogger.java:137)
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
//上次执行的结果保存到了一级缓存中,下次查询相同的内容,会直接从缓存中取出
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
在其中出入一条更新语句?
@Test
public void BASET() throws SQLException {
/*
在BaseExecutor 的query和update中使用了缓存,这两个方法会调用doquery和doupdate方法(由子类实现)执行
所以BaseExecutor和其子类在使用query和update才会调用一级缓存机制
*/
BaseExecutor executor=new SimpleExecutor(configuration,transaction );
MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
List<Object> query = executor.query(statement, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
System.out.println(query.get(0));
MappedStatement statement2=configuration.getMappedStatement("com.me.dao.UserMapper.updateByPrimaryKey");
int update = executor.update(statement2, new User(6, "dfgtb", "fsdfsf", null, null));
List<Object> query2=executor.query(statement,1,RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER);
System.out.println(query2.get(0));
}
执行
DEBUG 08-10 16:32:13,939 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 16:32:13,986 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 16:32:14,029 <== Total: 1 (BaseJdbcLogger.java:137)
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
DEBUG 08-10 16:32:14,047 ==> Preparing: update t_user set user_name = ?, pass_word = ?, email = ?, td_id = ? where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 16:32:14,049 ==> Parameters: dfgtb(String), fsdfsf(String), null, null, 6(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 16:32:14,050 <== Updates: 1 (BaseJdbcLogger.java:137)
//同样再次查询同样的内容,没有从一级缓存中取,而是有进行了一次编译查询,这是为什么?
DEBUG 08-10 16:32:14,051 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 16:32:14,052 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 16:32:14,053 <== Total: 1 (BaseJdbcLogger.java:137)
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
上面属于一级缓存失效,可以参考我的下一篇—缓存机制 o!
CachingExecutor执行器
测试
需要再mapper.xml开启缓存
@Test
public void cacheT() throws SQLException {
/*
需要再mapper.xml开启缓存
*/
Executor executor=new SimpleExecutor(configuration,transaction );
Executor executor1=new CachingExecutor(executor);
MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
List<Object> query = executor1.query(statement, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
System.out.println(query.get(0));
List<Object> query2 =executor1.query(statement,1,RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER);
System.out.println(query2.get(0));
}
执行
DEBUG 08-10 16:41:34,470 Cache Hit Ratio [com.me.dao.UserMapper]: 0.0 (LoggingCache.java:60)
DEBUG 08-10 16:41:34,495 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 16:41:34,539 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 16:41:34,581 <== Total: 1 (BaseJdbcLogger.java:137)
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
//命中缓存,直接在缓存中取出
DEBUG 08-10 16:41:34,606 Cache Hit Ratio [com.me.dao.UserMapper]: 0.0 (LoggingCache.java:60)
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
源码
在CachingExecutor 中包装了Executor(装饰者模式–可以参考博客->装饰者模式) ,CachingExecutor 只履行二级缓存的职责,将其他的操作交给了baseExecutor及其子类来实现,可以从源码中看出jdbc的操作都是有delegate的方法来执行的。
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
//包装了executor
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
**delegate.setExecutorWrapper(this);
}
@Override
public Transaction getTransaction() {
return **delegate.getTransaction();
}
@Override
public void close(boolean forceRollback) {
try {
// issues #499, #524 and #573
if (forceRollback) {
tcm.rollback();
} else {
tcm.commit();
}
} finally {
** delegate.close(forceRollback);
}
}
@Override
public boolean isClosed() {
return ** delegate.isClosed();
}
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
flushCacheIfRequired(ms);
return **delegate.update(ms, parameterObject);
}
@Override
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
flushCacheIfRequired(ms);
return **delegate.queryCursor(ms, parameter, rowBounds);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
为什么先执行二级缓存,再执行一级缓存?
测试
开启二级缓存
@Test
public void twst() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybats.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectByPrimaryKey(2);
System.out.println(user);
}
执行
Connected to the target VM, address: '127.0.0.1:63104', transport: 'socket'
//命中l二级缓存
DEBUG 08-10 17:09:27,265 Cache Hit Ratio [com.me.dao.UserMapper]: 0.0 (LoggingCache.java:60)
DEBUG 08-10 17:09:27,288 ==> Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ? (BaseJdbcLogger.java:137)
DEBUG 08-10 17:09:27,354 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 08-10 17:09:27,406 <== Total: 1 (BaseJdbcLogger.java:137)
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
Disconnected from the target VM, address: '127.0.0.1:63104', transport: 'socket'
经过debug发现,先进入CachingExecutor ,在进入simpleExecutor(默认)的
第一步:
第二步: