Mybatis默认开启一级缓存,二级缓存需要手动开启。Mybatis的二级缓存是多个SqlSession共享的,作用于是mapper配置文件中同一个namespace,不同的SqlSession两次执行相同namespace下的sql语句且参数如果也一样,则通过缓存查询的cacheKey也是一样的,则最终执行的sql语句是相同的。每次查询都会先看看缓存中是否有对应查询结果,如果有就从缓存拿,如果没有就执行sql语句从数据库中读取,从而提高查询效率。本文提使用redis的hash结构存储来实现mybatis的二级缓存方案。
Executor是跟SqlSession绑定在一起的,每一个SqlSession都拥有一个新的Executor对象,由Configuration创建。Executor接口代码如下
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement var1, Object var2) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean var1) throws SQLException;
void rollback(boolean var1) throws SQLException;
CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
boolean isCached(MappedStatement var1, CacheKey var2);
void clearLocalCache();
void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);
Transaction getTransaction();
void close(boolean var1);
boolean isClosed();
void setExecutorWrapper(Executor var1);
}
Executor接口的实现类有2个,一个是BaseExecutor,另外一个是CachingExecutor。Executor是MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护;CachingExecutor是一个Executor的装饰器,给一个Executor增加了缓存的功能。此时可以看做是对Executor类的一个增强,故使用装饰器模式是不错的选择。
Executor是跟SqlSession绑定在一起的,每一个SqlSession都拥有一个新的Executor对象,由Configuration创建。代码如下
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
1.BaseExecutor
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
int var6;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var6 = handler.update(stmt);
} finally {
this.closeStatement(stmt);
}
return var6;
}
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
Statement stmt = this.prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
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(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
Statement stmt = this.prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}
以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
Statement stmt;
//通过sql作为key查找是否存在
if (this.hasStatementFor(sql)) {
stmt = this.getStatement(sql);
this.applyTransactionTimeout(stmt);
} else {
Connection connection = this.getConnection(statementLog);
stmt = handler.prepare(connection, this.transaction.getTimeout());
this.putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
//通过sql作为key,判断是否存在
private boolean hasStatementFor(String sql) {
try {
return this.statementMap.keySet().contains(sql) && !((Statement)this.statementMap.get(sql)).getConnection().isClosed();
} catch (SQLException var3) {
return false;
}
}
//存放于statementMap中
private void putStatement(String sql, Statement stmt) {
this.statementMap.put(sql, stmt);
}
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
Statement stmt;
if (sql.equals(this.currentSql) && ms.equals(this.currentStatement)) {
int last = this.statementList.size() - 1;
stmt = (Statement)this.statementList.get(last);
this.applyTransactionTimeout(stmt);
handler.parameterize(stmt);
BatchResult batchResult = (BatchResult)this.batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = this.getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
this.currentSql = sql;
this.currentStatement = ms;
this.statementList.add(stmt);
this.batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
handler.batch(stmt);
return -2147482646;
}
2.CachingExecutor
先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。代码如下:
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
this.flushCacheIfRequired(ms);
return this.delegate.update(ms, parameterObject);
}
//通过cacheKey获取缓存数据
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
this.flushCacheIfRequired(ms);
return this.delegate.queryCursor(ms, parameter, rowBounds);
}
//缓存判断,如果存在则返回缓存中的数据
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) {
this.flushCacheIfRequired(ms);
//判断是否为isUserCache为true 且resusultHandle不为空
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql);
//通过key,获取缓存中的数据
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
//缓存不存在,直接去数据库查询
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
this.tcm.putObject(cache, key, list);
}
//存在,直接返回缓存数据
return list;
}
}
//直接去数据库查询
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
本实例采用的是springboot2.1.1
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
配置端口8016,mysql与redis以及mybatis的配置
server:
port: 8016
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/lss0555?serverTimezone=UTC&characterEncoding=utf-8
username: root
password: 888888
dbcp2:
validation-query: select 1 from dual
test-on-borrow: true
min-evictable-idle-time-millis: 600000
time-between-eviction-runs-millis: 300000
redis:
host: 127.0.0.1
port: 6379
password: 888888
timeout: 10000
jedis:
pool:
max-idle: 8
min-idle: 10
max-active: 100
max-wait: -1
mybatis:
configuration:
mapUnderscoreToCamelCase: false
cache-enabled: true
type-aliases-package: com.example.mybatistest.model
mapper-locations: classpath*:mapping/*.xml
mybatis开启缓存使用: cache-enabled: true
实现了Spring的ApplicationContextAware来获取ApplicationContext,从中获取容器的bean
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext; // NOSONAR
}
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
checkApplicationContext();
return applicationContext;
}
/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
checkApplicationContext();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
checkApplicationContext();
return (T) applicationContext.getBeansOfType(clazz);
}
/**
* 清除applicationContext静态变量.
*/
public static void cleanApplicationContext() {
applicationContext = null;
}
private static void checkApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
}
}
}
public class RedisCache implements Cache {
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
// 读写锁
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
private RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");
private String id;
public RedisCache(){
}
public RedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.info("Redis Cache id " + id);
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
if (value != null) {
// 向Redis中添加数据,有效时间是1天
logger.info("缓存:新增缓存 key:"+key);
redisTemplate.opsForValue().set(key.toString(), value, 1, TimeUnit.DAYS);
}
}
@Override
public Object getObject(Object key) {
try {
if (key != null) {
logger.info("缓存:获取 key:"+key);
Object obj = redisTemplate.opsForValue().get(key.toString());
return obj;
}
} catch (Exception e) {
logger.error("redis 异常:"+e);
}
return null;
}
@Override
public Object removeObject(Object key) {
try {
if (key != null) {
logger.info("缓存:移除:"+key);
redisTemplate.delete(key.toString());
}
} catch (Exception e) {
}
return null;
}
@Override
public void clear() {
logger.info("缓存:清空");
try {
Set<String> keys = redisTemplate.keys("*:" + this.id + "*");
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
} catch (Exception e) {
}
}
@Override
public int getSize() {
Long size = (Long) redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.dbSize();
}
});
logger.info("缓存:数量:"+size.intValue());
return size.intValue();
}
@Override
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
}
}
(1)dao层接口
@Mapper
public interface UserDaoMapper {
List<User> userList();
}
(2)Mapping实现层
<mapper namespace="com.example.mybatistest.dao.UserDaoMapper">
<cache type="com.example.mybatistest.cache.RedisCache">
<property name="eviction" value="LRU" />
<property name="flushInterval" value="6000000" />
<property name="size" value="1024" />
<property name="readOnly" value="false" />
</cache>
<select id="userList" resultType="com.example.mybatistest.model.User">
select * from `user`
</select>
</mapper>
cache标签内属性:
eviction:定义缓存移除机制(算法),默认为LRU(最近最少使用),它会清除最少使用的数据,还有一种FIFO(先进先出),它会清除最先进来的数据。
flushInterval:定义缓存刷新周期,单位为毫秒。
size:标识缓存cache中容纳的最大元素,默认为1024。
readOnly:默认为false,可配置为true缓存只读。
注意:
select 默认useCache为true:使用缓存,flushCache为false:不清空缓存
insert、update、delete 默认flushCache为true:清空缓存
(3)Service层
UserServiceInter 接口层
public interface UserServiceInter {
List<User> userList();
}
UserServiceImpl实现层
@Service
public class UserServiceImpl implements UserServiceInter {
@Resource
UserDaoMapper userDaoMapper;
@Override
public List<User> userList() {
return userDaoMapper.userList();
}
}
(4)controller层
@GetMapping("/userList")
public TResult getUserList(){
TResult<User> result = new TResult<>();
result.setList(userServiceInter.userList());
return result;
}
(5)测试结果
第一次访问 http://localhost:8016/userList1 控制台dubug信息如下:
2019-01-28 16:26:49.344 [http-nio-8016-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - GET "/userList2", parameters={}
2019-01-28 16:26:49.345 [http-nio-8016-exec-5] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public com.example.mybatistest.model.TResult com.example.mybatistest.controller.UserController.userList2()
2019-01-28 16:26:49.345 [http-nio-8016-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
2019-01-28 16:26:49.345 [http-nio-8016-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1bd8321] was not registered for synchronization because synchronization is not active
2019-01-28 16:26:49.345 [http-nio-8016-exec-5] INFO com.example.mybatistest.cache.RedisCache - 缓存:获取 key:-1672553611:3475886595:com.example.mybatistest.dao.UserDaoMapper.userList2:0:2147483647:select * from `user`:SqlSessionFactoryBean
2019-01-28 16:26:49.345 [http-nio-8016-exec-5] DEBUG o.s.data.redis.core.RedisConnectionUtils - Opening RedisConnection
2019-01-28 16:26:49.347 [http-nio-8016-exec-5] DEBUG io.lettuce.core.RedisChannelHandler - dispatching command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:26:49.347 [http-nio-8016-exec-5] DEBUG io.lettuce.core.protocol.DefaultEndpoint - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, epid=0x1] write() writeAndFlush command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:26:49.347 [http-nio-8016-exec-5] DEBUG io.lettuce.core.protocol.DefaultEndpoint - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, epid=0x1] write() done
2019-01-28 16:26:49.347 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] write(ctx, AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise)
2019-01-28 16:26:49.348 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandEncoder - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379] writing command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:26:49.349 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] Received: 5 bytes, 1 commands in the stack
2019-01-28 16:26:49.349 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] Stack contains: 1 commands
2019-01-28 16:26:49.349 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.RedisStateMachine - Decode AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:26:49.349 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.RedisStateMachine - Decoded AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], empty stack: true
2019-01-28 16:26:49.349 [http-nio-8016-exec-5] DEBUG o.s.data.redis.core.RedisConnectionUtils - Closing Redis Connection
2019-01-28 16:26:49.349 [http-nio-8016-exec-5] DEBUG com.example.mybatistest.dao.UserDaoMapper - Cache Hit Ratio [com.example.mybatistest.dao.UserDaoMapper]: 0.3333333333333333
2019-01-28 16:26:49.349 [http-nio-8016-exec-5] DEBUG o.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
2019-01-28 16:26:49.351 [http-nio-8016-exec-5] DEBUG o.m.spring.transaction.SpringManagedTransaction - JDBC Connection [HikariProxyConnection@21358673 wrapping com.mysql.cj.jdbc.ConnectionImpl@14e9682] will not be managed by Spring
2019-01-28 16:26:49.351 [http-nio-8016-exec-5] DEBUG c.example.mybatistest.dao.UserDaoMapper.userList2 - ==> Preparing: select * from `user`
2019-01-28 16:26:49.351 [http-nio-8016-exec-5] DEBUG c.example.mybatistest.dao.UserDaoMapper.userList2 - ==> Parameters:
2019-01-28 16:26:49.354 [http-nio-8016-exec-5] DEBUG c.example.mybatistest.dao.UserDaoMapper.userList2 - <== Total: 5
2019-01-28 16:26:49.354 [http-nio-8016-exec-5] INFO com.example.mybatistest.cache.RedisCache - 缓存:新增缓存 key:-1672553611:3475886595:com.example.mybatistest.dao.UserDaoMapper.userList2:0:2147483647:select * from `user`:SqlSessionFactoryBean
2019-01-28 16:26:49.354 [http-nio-8016-exec-5] DEBUG o.s.data.redis.core.RedisConnectionUtils - Opening RedisConnection
2019-01-28 16:26:49.354 [http-nio-8016-exec-5] DEBUG io.lettuce.core.RedisChannelHandler - dispatching command AsyncCommand [type=SETEX, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:26:49.355 [http-nio-8016-exec-5] DEBUG io.lettuce.core.protocol.DefaultEndpoint - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, epid=0x1] write() writeAndFlush command AsyncCommand [type=SETEX, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:26:49.355 [http-nio-8016-exec-5] DEBUG io.lettuce.core.protocol.DefaultEndpoint - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, epid=0x1] write() done
2019-01-28 16:26:49.355 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] write(ctx, AsyncCommand [type=SETEX, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise)
2019-01-28 16:26:49.356 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandEncoder - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379] writing command AsyncCommand [type=SETEX, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:26:49.357 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] Received: 5 bytes, 1 commands in the stack
2019-01-28 16:26:49.357 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] Stack contains: 1 commands
2019-01-28 16:26:49.357 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.RedisStateMachine - Decode AsyncCommand [type=SETEX, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:26:49.357 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.RedisStateMachine - Decoded AsyncCommand [type=SETEX, output=StatusOutput [output=OK, error='null'], commandType=io.lettuce.core.protocol.Command], empty stack: true
2019-01-28 16:26:49.357 [http-nio-8016-exec-5] DEBUG o.s.data.redis.core.RedisConnectionUtils - Closing Redis Connection
2019-01-28 16:26:49.357 [http-nio-8016-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1bd8321]
2019-01-28 16:26:49.357 [http-nio-8016-exec-5] DEBUG o.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
2019-01-28 16:26:49.357 [http-nio-8016-exec-5] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json]
2019-01-28 16:26:49.358 [http-nio-8016-exec-5] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing [TResult{errorCode=0, model=null, list=[User{id=1, username='22', password='dd', Salt='ee'}, User{id=2, username='dd', password='dd', Salt='dd'}, User{id=3, username='ccc', password='ccc', Salt='sd'}, User{id=4, username='d', password='d', Salt='d'}, User{id=5, username='1', password='1', Salt='11'}]}]
2019-01-28 16:26:49.359 [http-nio-8016-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK
发现里面是有走数据库的查询操作
再次访问,log如下:
2019-01-28 16:27:54.968 [http-nio-8016-exec-7] DEBUG org.springframework.web.servlet.DispatcherServlet - GET "/userList2", parameters={}
2019-01-28 16:27:54.968 [http-nio-8016-exec-7] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public com.example.mybatistest.model.TResult com.example.mybatistest.controller.UserController.userList2()
2019-01-28 16:27:54.968 [http-nio-8016-exec-7] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
2019-01-28 16:27:54.968 [http-nio-8016-exec-7] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@364b19] was not registered for synchronization because synchronization is not active
2019-01-28 16:27:54.969 [http-nio-8016-exec-7] INFO com.example.mybatistest.cache.RedisCache - 缓存:获取 key:-1672553611:3475886595:com.example.mybatistest.dao.UserDaoMapper.userList2:0:2147483647:select * from `user`:SqlSessionFactoryBean
2019-01-28 16:27:54.969 [http-nio-8016-exec-7] DEBUG o.s.data.redis.core.RedisConnectionUtils - Opening RedisConnection
2019-01-28 16:27:54.969 [http-nio-8016-exec-7] DEBUG io.lettuce.core.RedisChannelHandler - dispatching command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:27:54.969 [http-nio-8016-exec-7] DEBUG io.lettuce.core.protocol.DefaultEndpoint - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, epid=0x1] write() writeAndFlush command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:27:54.969 [http-nio-8016-exec-7] DEBUG io.lettuce.core.protocol.DefaultEndpoint - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, epid=0x1] write() done
2019-01-28 16:27:54.969 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] write(ctx, AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise)
2019-01-28 16:27:54.970 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandEncoder - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379] writing command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:27:54.971 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] Received: 298 bytes, 1 commands in the stack
2019-01-28 16:27:54.971 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.CommandHandler - [channel=0xc7f50bf0, /127.0.0.1:49477 -> /127.0.0.1:6379, chid=0x1] Stack contains: 1 commands
2019-01-28 16:27:54.971 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.RedisStateMachine - Decode AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
2019-01-28 16:27:54.971 [lettuce-nioEventLoop-4-1] DEBUG io.lettuce.core.protocol.RedisStateMachine - Decoded AsyncCommand [type=GET, output=ValueOutput [output=[B@15219b4, error='null'], commandType=io.lettuce.core.protocol.Command], empty stack: true
2019-01-28 16:27:54.972 [http-nio-8016-exec-7] DEBUG o.s.data.redis.core.RedisConnectionUtils - Closing Redis Connection
2019-01-28 16:27:54.972 [http-nio-8016-exec-7] DEBUG com.example.mybatistest.dao.UserDaoMapper - Cache Hit Ratio [com.example.mybatistest.dao.UserDaoMapper]: 0.5
2019-01-28 16:27:54.972 [http-nio-8016-exec-7] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@364b19]
2019-01-28 16:27:54.972 [http-nio-8016-exec-7] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json]
2019-01-28 16:27:54.972 [http-nio-8016-exec-7] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing [TResult{errorCode=0, model=null, list=[User{id=1, username='22', password='dd', Salt='ee'}, User{id=2, username='dd', password='dd', Salt='dd'}, User{id=3, username='ccc', password='ccc', Salt='sd'}, User{id=4, username='d', password='d', Salt='d'}, User{id=5, username='1', password='1', Salt='11'}]}]
2019-01-28 16:27:54.975 [http-nio-8016-exec-7] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK
发现这时候已经没有走数据库查询,直接从redis缓存中拿出数据