這篇文章我決定一改以往的風格,以幽默風趣的故事博文來介紹如何整合 SpringBoot、Mybatis、Redis。mysql
好久好久之前,森林裏有一隻可愛的小青蛙,他邁着沉重的步伐走向了找工做的道路,結果發現許多的招聘要求都要會 Redis。git
小青蛙就想啥是 Redis 呢,爲何要用 Redis 呢?難道是由於 Mysql 的幣格不夠高嗎,小青蛙點開了收藏已久的網站:十萬個爲何github
發現原來隨着使用網站的用戶愈來愈多,表中的數據也愈來愈多,查詢速度愈來愈慢。redis
MySql 的性能遇到了瓶頸,因此許多網站都用 Redis 做緩存。spring
然而能做緩存的不只只有 Redis,還有 Memcache,那爲何要用 Redis 呢?sql
一、性能方面:它們都是將數據存放在內存中,因此性能基本類似。數據庫
二、數據類型方面:Redis 支持五種數據數據類型:字符串、散列、列表、集合、有序集合,而 Memcache 僅僅支持簡單的 key-value。apache
三、數據持久化方面:Redis 能夠經過 RDB快照、AOF日誌 等方式進行數據持久化,可是 Memcache 不能夠。api
四、數據備份方面:Redis 支持 master-slave 主從模式的數據備份。緩存
在瞭解到許多 Redis 的好處後,小青蛙已經火燒眉毛的想了解它了。
爲了更好的使用 Redis,瞭解 Redis 的五種數據類型適應場景是頗有必要的。
一、String 類型:一個 key 對應一個 value,而 value 不單單是 String,也能夠是數字、甚至是一個序列化對象。
二、Hash 類型:一個 key 對應 多個 field,一個 field 對應 yige value,實際上該類型最適合存儲序列化對象。
key 至關於數據庫表名字,field 至關於主鍵,value 也就是序列化對象。
三、List 類型:簡單的字符串列表,按照插入順序排序,該結構相似於數據結構中的雙向鏈表,能夠從頭部插也能夠從尾部插。
四、Set 類型:它是字符串集合,只不過它是無序的且不存在重複的字符串,可是它能夠實現 交集、並集、差集。
五、ZSet 類型:它也是字符串集合,它和 Set 的區別是該集合的元素存在 score 屬性,按照 score 屬性的高低排序。能夠應用在排行榜上。
值得注意的是,不要習慣性的認爲 Redis 字符串只能存字符串,實際上,它能夠存儲任何序列化後的對象,固然也能夠讀出來。
小青蛙知道了 Redis 的五種數據類型應用場景後,火燒眉毛的想要實踐它了。
爲了知道如何讓它做爲緩存,以及如何操做數據,小青蛙打開了珍藏已久的視頻網站來學習:青蛙愛學習
在該視頻網站上,小青蛙沉迷其中沒法自拔,額,呸呸。緩過神來,發現了一個很好的視頻。小青蛙幽默的說:快進個人收藏夾吃灰去吧。
小青蛙向來都不是一個收藏從未中止,學習從未開始的青蛙。它模仿着視頻建了一個 SpringBoot 項目。
此時,小青蛙想爲何要勾選 Lombok 呢,由於它能夠簡化類,而且提供了 log 等功能。
以後小青蛙爲了連上 Mysql 和 Redis 就開始配置 application.properties 文件:
# 數據源配置 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/demo?serverTimezone=UTC&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=lemon@mango # Redis 配置 spring.redis.database=0 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1 spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 spring.redis.lettuce.shutdown-timeout=100
配置好該文件後,須要 redisTemplate 模板 Bean,由於自動配置的 redisTemplate Bean 沒有提供序列化操做:(由於是入門版的,因此這樣最好理解)
package com.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(factory); GenericJackson2JsonRedisSerializer genericJsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(genericJsonRedisSerializer); template.setHashKeySerializer(genericJsonRedisSerializer); template.setHashValueSerializer(genericJsonRedisSerializer); template.afterPropertiesSet(); return template; } }
至此就整合完成了,小青蛙心想這就完事了?!!!,不信?那就來演示一下:(先建一個簡單的類測試一下)
package com.test.serviceImpl; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.beans.factory.annotation.Autowired; import lombok.extern.slf4j.Slf4j; @Service @Slf4j public class JustForTest { @Autowired private RedisTemplate redisTemplate; public void test(String username) { if(redisTemplate.hasKey(username)) { log.info((String)redisTemplate.opsForValue().get(username)); log.info("get value from redis"); }else { String password = "password"; log.info(password); log.info("get value from mysql"); log.info("set value to redis"); redisTemplate.opsForValue().set(username, password); } } }
package com.test; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.beans.factory.annotation.Autowired; import com.test.serviceImpl.JustForTest; @SpringBootTest class TestApplicationTests { @Autowired private JustForTest justFortest; @Test void contextLoads() { justFortest.test("username"); } }
哦嚯,報錯了,原來是 pom.xml 中少了 commons.pool 依賴,咱給它加上:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
再來一次:
再再來一次:
能夠看到確實存入 Redis 了,小青蛙便去 Redis 數據庫中看看有沒有:
事實證實確實整合完畢了,小青蛙仍然表示不解,說好的是SpringBoot、Mybatis、Redis的整合呢,怎麼只看到 Redis 的?
小青蛙剛這麼想,而後視頻裏就說了,心急吃不了熱豆腐,須要慢慢來。緊接着,小青蛙就看到了完整的項目結構:
爲了讓青蛙們只關注有關整合的部分,視頻裏僅僅只給出 serviceImpl 中的代碼:
package com.demo.serviceImpl; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import com.demo.pojo.SimpleUser; import com.demo.dao.SimpleUserDao; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import lombok.extern.slf4j.Slf4j; @Service @Slf4j @SuppressWarnings({"rawtypes","unchecked"}) public class SimpleUserServiceImpl implements UserDetailsService { @Autowired private RedisTemplate redisTemplate; @Autowired private SimpleUserDao userDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO Auto-generated method stub if(redisTemplate.opsForHash().hasKey("user",username)) { SimpleUser user = (SimpleUser)redisTemplate.opsForHash().get("user",username); return new User(user.getUsername(),user.getPassword(),user.getAuthorities()); }else { SimpleUser user = userDao.findUserByUsername(username); if(user != null) { redisTemplate.opsForHash().put("user", "username", user); return new User(user.getUsername(),user.getPassword(),user.getAuthorities()); }else { throw new UsernameNotFoundException("Username or Password is not correct"); } } } public int addSimpleUser(SimpleUser user) { user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword())); return userDao.addSimpleUser(user); } }
和上面測試的簡單小例子類似,僅僅是把 String 換成了對象。小青蛙對 Mysql 的表結構和Redis中存入的數據比較感心趣:
因爲對 String 類型帶有 " 符號,因此須要對其進行轉義。
小青蛙是一個有分享精神的蛙,每次它以爲有價值的東西,它都會分享給它的朋友們,項目地址爲:GitHub
小青娃逐漸的弄懂了怎麼去進行整合,可是它仍是不太明白爲何這樣就能整合,也就是 know how but don't konw why!
小青蛙知道:萬事開頭難,但它不知道的是,後面也很難...今後,小青蛙踏上了探尋源碼的道路!呱呱呱......