一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - springboot+mybatis+redis 二級緩存問題實(shí)例詳解

springboot+mybatis+redis 二級緩存問題實(shí)例詳解

2021-03-02 13:53扎心了老鐵 Java教程

Mybatis默認(rèn)沒有開啟二級緩存,需要在全局配置(mybatis-config.xml)中開啟二級緩存。本文講述的是使用Redis作為緩存,與springboot、mybatis進(jìn)行集成的方法。需要的朋友參考下吧

前言

什么是mybatis二級緩存

二級緩存是多個(gè)sqlsession共享的,其作用域是mapper的同一個(gè)namespace。

即,在不同的sqlsession中,相同的namespace下,相同的sql語句,并且sql模板中參數(shù)也相同的,會命中緩存。

第一次執(zhí)行完畢會將數(shù)據(jù)庫中查詢的數(shù)據(jù)寫到緩存,第二次會從緩存中獲取數(shù)據(jù)將不再從數(shù)據(jù)庫查詢,從而提高查詢效率。

Mybatis默認(rèn)沒有開啟二級緩存,需要在全局配置(mybatis-config.xml)中開啟二級緩存。

本文講述的是使用Redis作為緩存,與springboot、mybatis進(jìn)行集成的方法。

1、pom依賴

使用springboot redis集成包,方便redis的訪問。redis客戶端選用Jedis。

另外讀寫kv緩存會進(jìn)行序列化,所以引入了一個(gè)序列化包。      

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-redis</artifactId>
 </dependency>
 <dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>2.8.0</version>
 </dependency>
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.19</version>
 </dependency>

依賴搞定之后,下一步先調(diào)通Redis客戶端。

2、Redis訪問使用的Bean

增加Configuration,配置jedisConnectionFactory bean,留待后面使用。

一般來講,也會生成了redisTemplate bean,但是在接下來的場景沒有使用到。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Configuration
public class RedisConfig {
 @Value("${spring.redis.host}")
 private String host;
 // 篇幅受限,省略了
 @Bean
 public JedisPoolConfig getRedisConfig(){
  JedisPoolConfig config = new JedisPoolConfig();
  config.setMaxIdle(maxIdle);
  config.setMaxTotal(maxTotal);
  config.setMaxWaitMillis(maxWaitMillis);
  config.setMinIdle(minIdle);
  return config;
 }
 @Bean(name = "jedisConnectionFactory")
 public JedisConnectionFactory getConnectionFactory(){
  JedisConnectionFactory factory = new JedisConnectionFactory();
  JedisPoolConfig config = getRedisConfig();
  factory.setPoolConfig(config);
  factory.setHostName(host);
  factory.setPort(port);
  factory.setDatabase(database);
  factory.setPassword(password);
  factory.setTimeout(timeout);
  return factory;
 }
 @Bean(name = "redisTemplate")
 public RedisTemplate<?, ?> getRedisTemplate(){
  RedisTemplate<?,?> template = new StringRedisTemplate(getConnectionFactory());
  return template;
 }
}

這里使用@Value讀入了redis相關(guān)配置,有更簡單的配置讀取方式(@ConfigurationProperties(prefix=...)),可以嘗試使用。

Redis相關(guān)配置如下

?
1
2
3
4
5
6
7
8
9
10
#redis
spring.redis.host=10.93.84.53
spring.redis.port=6379
spring.redis.password=bigdata123
spring.redis.database=15
spring.redis.timeout=0
spring.redis.pool.maxTotal=8
spring.redis.pool.maxWaitMillis=1000
spring.redis.pool.maxIdle=8
spring.redis.pool.minIdle=0

Redis客戶端的配置含義,這里不再講解了。pool相關(guān)的一般都和性能有關(guān),需要根據(jù)并發(fā)量權(quán)衡句柄、內(nèi)存等資源進(jìn)行設(shè)置。

Redis客戶端設(shè)置好了,我們開始配置Redis作為Mybatis的緩存。

3、Mybatis Cache

這一步是最為關(guān)鍵的一步。實(shí)現(xiàn)方式是實(shí)現(xiàn)Mybatis的一個(gè)接口org.apache.ibatis.cache.Cache。

這個(gè)接口設(shè)計(jì)了寫緩存,讀緩存,銷毀緩存的方式,和訪問控制讀寫鎖。

我們實(shí)現(xiàn)實(shí)現(xiàn)Cache接口的類是MybatisRedisCache。

MybatisRedisCache.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public class MybatisRedisCache implements Cache {
 private static JedisConnectionFactory jedisConnectionFactory;
 private final String id;
 private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 public MybatisRedisCache(final String id) {
  if (id == null) {
   throw new IllegalArgumentException("Cache instances require an ID");
  }
  this.id = id;
 }
 @Override
 public void clear() {
  RedisConnection connection = null;
  try {
   connection = jedisConnectionFactory.getConnection();
   connection.flushDb();
   connection.flushAll();
  } catch (JedisConnectionException e) {
   e.printStackTrace();
  } finally {
   if (connection != null) {
    connection.close();
   }
  }
 }
 @Override
 public String getId() {
  return this.id;
 }
 @Override
 public Object getObject(Object key) {
  Object result = null;
  RedisConnection connection = null;
  try {
   connection = jedisConnectionFactory.getConnection();
   RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
   result = serializer.deserialize(connection.get(serializer.serialize(key)));
  } catch (JedisConnectionException e) {
   e.printStackTrace();
  } finally {
   if (connection != null) {
    connection.close();
   }
  }
  return result;
 }
 @Override
 public ReadWriteLock getReadWriteLock() {
  return this.readWriteLock;
 }
 @Override
 public int getSize() {
  int result = 0;
  RedisConnection connection = null;
  try {
   connection = jedisConnectionFactory.getConnection();
   result = Integer.valueOf(connection.dbSize().toString());
  } catch (JedisConnectionException e) {
   e.printStackTrace();
  } finally {
   if (connection != null) {
    connection.close();
   }
  }
  return result;
 }
 @Override
 public void putObject(Object key, Object value) {
  RedisConnection connection = null;
  try {
   connection = jedisConnectionFactory.getConnection();
   RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
   connection.set(serializer.serialize(key), serializer.serialize(value));
  } catch (JedisConnectionException e) {
   e.printStackTrace();
  } finally {
   if (connection != null) {
    connection.close();
   }
  }
 }
 @Override
 public Object removeObject(Object key) {
  RedisConnection connection = null;
  Object result = null;
  try {
   connection = jedisConnectionFactory.getConnection();
   RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
   result = connection.expire(serializer.serialize(key), 0);
  } catch (JedisConnectionException e) {
   e.printStackTrace();
  } finally {
   if (connection != null) {
    connection.close();
   }
  }
  return result;
 }
 public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
  MybatisRedisCache.jedisConnectionFactory = jedisConnectionFactory;
 }
}

注意:

可以看到,這個(gè)類并不是由Spring虛擬機(jī)管理的類,但是,其中有一個(gè)靜態(tài)屬性jedisConnectionFactory需要注入一個(gè)Spring bean,也就是在RedisConfig中生成的bean。

在一個(gè)普通類中使用Spring虛擬機(jī)管理的Bean,一般使用Springboot自省的SpringContextAware。

這里使用了另一種方式,靜態(tài)注入的方式。這個(gè)方式是通過RedisCacheTransfer來實(shí)現(xiàn)的。

4、靜態(tài)注入

RedisCacheTransfer.java

?
1
2
3
4
5
6
7
@Component
public class RedisCacheTransfer {
 @Autowired
 public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
  MybatisRedisCache.setJedisConnectionFactory(jedisConnectionFactory);
 }
}

可以看到RedisCacheTransfer是一個(gè)springboot bean,在容器創(chuàng)建之初進(jìn)行初始化的時(shí)候,會注入jedisConnectionFactory bean給setJedisConnectionFactory方法的傳參。

而setJedisConnectionFactory通過調(diào)用靜態(tài)方法設(shè)置了類MybatisRedisCache的靜態(tài)屬性jedisConnectionFactory。

這樣就把spring容器管理的jedisConnectionFactory注入到了靜態(tài)域。

到這里,代碼基本已經(jīng)搞定,下面是一些配置。主要有(1)全局開關(guān);(2)namespace作用域開關(guān);(3)Model實(shí)例序列化。

5、Mybatis二級緩存的全局開關(guān)

前面提到過,默認(rèn)二級緩存沒有打開,需要設(shè)置為true。這是全局二級緩存的開關(guān)。

Mybatis的全局配置。

?
1
2
3
4
5
6
7
8
9
<?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>
 <!-- 全局參數(shù) -->
 <settings>
  <!-- 使全局的映射器啟用或禁用緩存。 -->
  <setting name="cacheEnabled" value="true"/>
 </settings>
</configuration>

全局配置的加載在dataSource中可以是這樣的。

bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis-mapper/*.xml"));

指定了mapper.xml的存放路徑,在mybatis-mapper路徑下,所有后綴是.xml的都會讀入。

bean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));

指定了mybatis-config.xml的存放路徑,直接放在Resource目錄下即可。

?
1
2
3
4
5
6
7
8
9
@Bean(name = "moonlightSqlSessionFactory")
 @Primary
 public SqlSessionFactory moonlightSqlSessionFactory(@Qualifier("moonlightData") DataSource dataSource) throws Exception {
  SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  bean.setDataSource(dataSource);
  bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis-mapper/*.xml"));
  bean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
  return bean.getObject();
 }

6、配置mapper作用域namespace

前面提到過,二級緩存的作用域是mapper的namespace,所以這個(gè)配置需要到mapper中去寫。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<mapper namespace="com.kangaroo.studio.moonlight.dao.mapper.MoonlightMapper">
 <cache type="com.kangaroo.studio.moonlight.dao.cache.MybatisRedisCache"/>
 <resultMap id="geoFenceList" type="com.kangaroo.studio.moonlight.dao.model.GeoFence">
 <constructor>
  <idArg column="id" javaType="java.lang.Integer" jdbcType="INTEGER" />
  <arg column="name" javaType="java.lang.String" jdbcType="VARCHAR" />
  <arg column="type" javaType="java.lang.Integer" jdbcType="INTEGER" />
  <arg column="group" javaType="java.lang.String" jdbcType="VARCHAR" />
  <arg column="geo" javaType="java.lang.String" jdbcType="VARCHAR" />
  <arg column="createTime" javaType="java.lang.String" jdbcType="VARCHAR" />
  <arg column="updateTime" javaType="java.lang.String" jdbcType="VARCHAR" />
 </constructor>
 </resultMap>
 
<select id="queryGeoFence" parameterType="com.kangaroo.studio.moonlight.dao.model.GeoFenceQueryParam" resultMap="geoFenceList">
 select <include refid="base_column"/> from geoFence where 1=1
 <if test="type != null">
  and type = #{type}
 </if>
 <if test="name != null">
  and name like concat('%', #{name},'%')
 </if>
 <if test="group != null">
  and `group` like concat('%', #{group},'%')
 </if>
 <if test="startTime != null">
  and createTime &gt;= #{startTime}
 </if>
 <if test="endTime != null">
  and createTime &lt;= #{endTime}
 </if>
 </select>
</mapper>

注意:

namespace下的cache標(biāo)簽就是加載緩存的配置,緩存使用的正式我們剛才實(shí)現(xiàn)的MybatisRedisCache。

?
1
<cache type="com.kangaroo.studio.moonlight.dao.cache.MybatisRedisCache"/>

這里只實(shí)現(xiàn)了一個(gè)查詢queryGeoFence,你可以在select標(biāo)簽中,開啟或者關(guān)閉這個(gè)sql的緩存。使用屬性值useCache=true/false。

7、Mapper和Model

讀寫緩存Model需要序列化:只需要類聲明的時(shí)候?qū)崿F(xiàn)Seriaziable接口就好了。

?
1
2
3
4
5
6
public class GeoFence implements Serializable {
 // setter和getter省略
}
public class GeoFenceParam implements Serializable {
 // setter和getter省略
}

mapper就還是以前的寫法,使用mapper.xml的方式這里只需要定義出抽象函數(shù)即可。

?
1
2
3
4
@Mapper
public interface MoonlightMapper {
 List<GeoFence> queryGeoFence(GeoFenceQueryParam geoFenceQueryParam);
}

到這里,所有的代碼和配置都完成了,下面測試一下。

8、測試一下

Controller中實(shí)現(xiàn)一個(gè)這樣的接口POST。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RequestMapping(value = "/fence/query", method = RequestMethod.POST)
 @ResponseBody
 public ResponseEntity<Response> queryFence(@RequestBody GeoFenceQueryParam geoFenceQueryParam) {
  try {
   Integer pageNum = geoFenceQueryParam.getPageNum()!=null?geoFenceQueryParam.getPageNum():1;
   Integer pageSize = geoFenceQueryParam.getPageSize()!=null?geoFenceQueryParam.getPageSize():10;
   PageHelper.startPage(pageNum, pageSize);
   List<GeoFence> list = moonlightMapper.queryGeoFence(geoFenceQueryParam);
   return new ResponseEntity<>(
     new Response(ResultCode.SUCCESS, "查詢geoFence成功", list),
     HttpStatus.OK);
  } catch (Exception e) {
   logger.error("查詢geoFence失敗", e);
   return new ResponseEntity<>(
     new Response(ResultCode.EXCEPTION, "查詢geoFence失敗", null),
     HttpStatus.INTERNAL_SERVER_ERROR);
  }

 使用curl發(fā)送請求,注意

1)-H - Content-type:application/json方式

2)-d - 后面是json格式的參數(shù)包體

?
1
2
3
4
5
6
7
8
curl -H "Content-Type:application/json" -XPOST http://。。。/moonlight/fence/query -d '{
 "name" : "test",
 "group": "test",
 "type": 1,
 "startTime":"2017-12-06 00:00:00",
 "endTime":"2017-12-06 16:00:00",
 "pageNum": 1,
 "pageSize": 8

請求了三次,日志打印如下,

可以看到,只有第一次執(zhí)行了sql模板查詢,后面都是命中了緩存。

在我們的測試環(huán)境中由于數(shù)據(jù)量比較小,緩存對查詢速度的優(yōu)化并不明顯。這里就不過多說明了。

springboot+mybatis+redis 二級緩存問題實(shí)例詳解

原文鏈接:http://www.cnblogs.com/kangoroo/p/8021457.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 色综合欧美色综合七久久 | 免费在线看a | 日本孕妇与黑人xxxxxx | 亚洲精品无码不卡 | 国产精品高清一区二区三区不卡 | 亚州中文字幕 | 日韩成人精品在线 | 欧亚专线欧洲m码可遇不可求 | juliaann厨房大战| 好男人好资源在线观看免费 | 欧美高清免费一级在线 | 男同桌扒开女同桌胸罩喝奶 | 久久re亚洲在线视频 | 99久久精品免费看国产高清 | 国产精品二区高清在线 | 亚洲色图欧美偷拍 | 91po国产在线高清福利 | 国产高清不卡视频在线播放 | 四虎影视永久在线精品免费 | 国产91精选在线观看麻豆 | 日本一区二区三区在线 视频 | 日本粉色视频 | 精品国内自产拍在线视频 | 成人3p视频免费 | 波多野结中文字幕在线69视频 | 羞羞答答免费人成黄页在线观看国产 | 日本免费播放 | 亚洲国产在线综合018 | 91精品国产9l久久久久 | 亚洲丰满模特裸做爰 | 国模大胆一区二区三区 | 久久人妻熟女中文字幕AV蜜芽 | 国产伦码精品一区二区 | 精品久久免费视频 | 99久久精品国产免看国产一区 | 亚洲冬月枫中文字幕在线看 | www.伊人| 男人操女人视频 | 男人捅女人的鸡鸡 | 精品国语国产在线对白 | 大杳蕉在线影院在线播放 |