spring boot對常用的數(shù)據(jù)庫支持外拍谐,對nosql 數(shù)據(jù)庫也進行了封裝自動化。
redis介紹
Redis是目前業(yè)界使用最廣泛的內(nèi)存數(shù)據(jù)存儲辆飘。相比memcached,Redis支持更豐富的數(shù)據(jù)結構赞厕,例如hashes, lists, sets等,同時支持數(shù)據(jù)持久化定硝。除此之外皿桑,Redis還提供一些類數(shù)據(jù)庫的特性,比如事務喷斋,HA唁毒,主從庫⌒亲Γ可以說Redis兼具了緩存系統(tǒng)和數(shù)據(jù)庫的一些特性浆西,因此有著豐富的應用場景。本文介紹Redis在Spring Boot中兩個典型的應用場景顽腾。
如何使用
1近零、引入 spring-boot-starter-redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
2、添加配置文件
# REDIS (RedisProperties)
# Redis數(shù)據(jù)庫索引(默認為0)
spring.redis.database=0
# Redis服務器地址
spring.redis.host=192.168.0.58
# Redis服務器連接端口
spring.redis.port=6379
# Redis服務器連接密碼(默認為空)
spring.redis.password=
# 連接池最大連接數(shù)(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閑連接
spring.redis.pool.max-idle=8
# 連接池中的最小空閑連接
spring.redis.pool.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=0
3抄肖、添加cache的配置類
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
@SuppressWarnings("rawtypes")
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//設置緩存過期時間
// rcm.setDefaultExpiration(10);//秒
return rcm;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
好了久信,接下來就可以直接使用了
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class TestRedis {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test() throws Exception {
stringRedisTemplate.opsForValue().set("aaa", "111");
Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa"));
}
@Test
public void testObj() throws Exception {
User user=new User("aa@126.com", "aa", "aa123456", "aa","123");
ValueOperations<String, User> operations=redisTemplate.opsForValue();
operations.set("com.neox", user);
operations.set("com.neo.f", user,1,TimeUnit.SECONDS);
Thread.sleep(1000);
//redisTemplate.delete("com.neo.f");
boolean exists=redisTemplate.hasKey("com.neo.f");
if(exists){
System.out.println("exists is true");
}else{
System.out.println("exists is false");
}
// Assert.assertEquals("aa", operations.get("com.neo.f").getUserName());
}
}
以上都是手動使用的方式,如何在查找數(shù)據(jù)庫的時候自動使用緩存呢漓摩,看下面裙士;
4、創(chuàng)建個JavaBean對象管毙。
@Entity
public class Post implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String content;
private Integer weight;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
}
5腿椎、創(chuàng)建個repository
@CacheConfig(cacheNames = "post")
public interface PostRepository extends PagingAndSortingRepository<Post, Integer> {
@Cacheable(key = "#p0")
Post findById(int id);
/**
* 新增或修改時
*/
@CachePut(key = "#p0.id")
@Override
Post save(Post post);
@Transactional
@Modifying
@CacheEvict(key = "#p0")
int deleteById(int id);
}
這個里面有個CacheConfig,配置了cacheNames夭咬。
- 我在findById方法時加了個@Cacheable(key= "#p0")啃炸,#p0代表第一個參數(shù),也就是id卓舵。這句話加上之后南用,當你在調用findById時,就會先從redis的post緩存對象里去查詢key等于傳過來的id的值。如果沒有裹虫,就去查表肿嘲。
- 在save方法上加了個CachePut,代表往緩存里添加值恒界,key為參數(shù)post的id屬性睦刃,這樣當我們save一個Post對象時,redis就會新增一個以id為key的Post對象十酣;如果是update操作,那同樣际长,redis會覆蓋id相同的Post對象的值耸采,也完成一次更新。
- 在delete方法上加了個@CacheEvict(key = "#p0")工育,當調用deleteById時虾宇,就會同時清空指定key的緩存。
- 更多標簽如绸,請看cache標簽用法
這樣嘱朽,在對post的增、刪怔接、改搪泳、查時都會自動緩存到redis里。下面來驗證一下扼脐。
加個service
@Service
public class PostService {
@Autowired
private PostRepository postRepository;
public Post findById(int id) {
return postRepository.findById(id);
}
public Post save(Post post) {
return postRepository.save(post);
}
public int delete(int id) {
return postRepository.deleteById(id);
}
}
來個controller
@RestController
public class PostController {
@Autowired
private PostService postService;
@RequestMapping("/query/{id}")
public Object query(@PathVariable int id) {
return postService.findById(id);
}
@RequestMapping("/save")
public Object save(@ModelAttribute Post post) {
return postService.save(post);
}
@RequestMapping("/delete/{id}")
public Object delete(@PathVariable int id) {
return postService.delete(id);
}
@RequestMapping("/queryPage")
public Object query(String name, int pageNum, int count) {
//根據(jù)weight倒序分頁查詢
// Pageable pageable = new PageRequest(pageNum, count, Sort.Direction.DESC, "weight");
// return userRepository.findByName(name, pageable);
return null;
}
}
然后啟動Application岸军,在啟動之前需要加上@EnableCaching注解,緩存才能正常工作瓦侮。
@SpringBootApplication
@EnableCaching
public class Demo0Application {
public static void main(String[] args) {
SpringApplication.run(Demo0Application.class, args);
}
}
啟動后訪問 http://localhost:8080/save?content=1&weight=1
如果想看是否能夠正常緩存艰赞,有三種方法:
- 在加緩存控制的地方打系統(tǒng)日志,比如
/**
* 新增或修改時
*/
@CachePut(key = "#p0.id")
@Override
Post save(Post post){
system.out.println("命中緩存時將不會打印此日志");
}
}
- 在applcation.properties中設置sql控制臺日志打開
show-sql: true
- 登陸redis服務器查看響應的key值是否有增肚吏、刪方妖、改的變化。
# 獲取所有keys
keys *
# 查看目標key是否有效
get <key>
# 查詢操作時罚攀,使用的是get方法党觅;新增和更新時,用的set方法坞生;刪除時仔役,用的del方法,可以分別查看響應操作時是己,對應的key是否發(fā)生了正確的變化
然后訪問查詢又兵,http://localhost:8080/query/1
會發(fā)現(xiàn)查詢到了id為1的這條記錄,并且控制臺沒有走select查詢語句,也就是根本沒訪問數(shù)據(jù)庫沛厨,直接從redis緩存拿的值宙地。
下面做一個更新操作,看看是否會同步到redis里逆皮。http://localhost:8080/save?content=1&weight=2&id=1
把weight改為2宅粥,訪問地址看看結果。
控制臺打印了兩條語句
hibernate: select post0_.id as id1_0_0_, post0_.content as content2_0_0_, post0_.weight as weight3_0_0_ from post post0_ where post0_.id=?Hibernate: update post set content=?, weight=? where id=?
說明數(shù)據(jù)已經(jīng)被更新了电谣。然后再查詢http://localhost:8080/query/1
發(fā)現(xiàn)查到的數(shù)據(jù)已經(jīng)改變秽梅,并且控制臺沒有走select語句,說明在update時剿牺,redis已經(jīng)更新了企垦。
下面做刪除操作,可以直接在數(shù)據(jù)庫里刪這條記錄晒来,或者通過瀏覽器訪問來刪除钞诡。http://localhost:8080/delete/1
控制臺走了刪除delete語句,redis也一并刪除了湃崩,這時執(zhí)行查詢將會看不到響應的數(shù)據(jù)荧降。
這樣我們就完成了一個最簡單的整合redis的demo。包括了對單個對象的增刪改查的緩存攒读。
那么下面來講幾個疑問:
-
為什么不用配置redis的地址朵诫,port,密碼什么的整陌?
上面的那些默認的對redis的操作拗窃,來源于Springboot里整合的RedisTemplate,template里會默認使用一個JedisConnectionFactory來做默認的連接屬性配置泌辫。
這里面已經(jīng)對jedis的連接地址和jedisPool做了初始化操作了随夸,都是默認值。系統(tǒng)就會使用這些默認值來操作redis震放。后面我們會對Connection進行自定義宾毒,設置value的序列化方式,還有修改連接地址殿遂,那時就會使用自定義的配置了诈铛。
能否用上面的方法來存儲集合?譬如所有的Post集合墨礁,當新增時集合也隨之改變幢竹?
不行的,假如給List<Post> findAll做了個緩存恩静,那下次查詢時確實不用查表了焕毫,但是當你新增蹲坷、修改、刪除任何一個對象時邑飒,這個緩存的集合都是不變的循签。
除非你在所有的能修改對象的地方,都加上CacheEvict疙咸,key為集合的key县匠,這樣任何修改,都是刪除整個集合對象的緩存撒轮,下次再查時才能緩存起來乞旦。而大部分時候,集合對象都是在不停變化的腔召,除了一些不變的如城市列表之類的杆查,其他的都不適合用這種緩存方式。會導致頻繁創(chuàng)建大對象臀蛛,而且大部分時候也不需要查整個集合,而是分頁崖蜜。怎么用redis來做集合查詢浊仆,分頁查詢,甚至于條件分頁查詢
參考文檔
http://blog.csdn.net/tianyaleixiaowu/article/details/70314277
https://yq.aliyun.com/articles/91595?t=t1
http://blog.csdn.net/tianyaleixiaowu/article/details/70595073