赞
踩
在原有SpringBoot项目中,集成Redis,并实现Dao层,Service层,Controller层。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
使用Spring Boot的Redis依赖。
spring:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
database: 0
password: ${REDIS_PASSWORD}
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * @author zyl */ @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用jackson的序列化方式 template.setHashKeySerializer(jackson2JsonRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
这里配置使用redis的链接池,以及key,hash key和数据存储使用的序列化方式。
import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * @author zyl */ @NoArgsConstructor @AllArgsConstructor @Data @Builder public class City implements Serializable { private Long id; private String name; private String state; private String country; }
注意:这里要实现Serializable接口,只有这样,我们这里使用的Redis依赖库才能够正常使用。
/**
* @author zyl
*/
public interface CityDao {
void save(City city);
City findById(Long id);
void delete(Long id);
}
上面是Dao的定义层,下面来看看Dao的实现层:
import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; /** * @author zyl */ @Repository public class CityDaoImpl implements CityDao { private static final String HASH_REFERENCE = "City"; @Resource private RedisTemplate<String, City> redisTemplate; @Override public void save(City city) { Long id = city.getId(); String key = HASH_REFERENCE + "_" + id; BoundHashOperations<String, Long, City> boundHashOperations = redisTemplate.boundHashOps(key); boundHashOperations.putIfAbsent(id, city); boundHashOperations.expire(60, TimeUnit.SECONDS); } @Override public City findById(Long id) { String key = HASH_REFERENCE + "_" +id; BoundHashOperations<String, Long, City> boundHashOperations = redisTemplate.boundHashOps(key); return boundHashOperations.get(id); } @Override public void delete(Long id) { String key = HASH_REFERENCE + "_" +id; BoundHashOperations<String, Long, City> boundHashOperations = redisTemplate.boundHashOps(key); boundHashOperations.delete(id); } }
注意这里重点是通过BoundHashOperations类来实现Redis相关对象的操作。BoundHashOperations类是操作Redis的重点类。如果使用通过HashOperations类让redis里面的数据过期处理,有点麻烦,故这里使用BoundHashOperations类处理。
import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.http.HttpStatus; /** * 通用响应类 * @author zyl */ @NoArgsConstructor @AllArgsConstructor @Builder @Data public class ApiRes<T> { private int status = HttpStatus.OK.value(); private String message; private T data; public static final String SUCCESS_MSG = "操作成功"; public static final String ERROR_MSG = "操作成功"; /** * 返回成功数据 * @author zyl * @return 成功消息 */ public static <T> ApiRes<T> success(T data) { return ApiRes.success(SUCCESS_MSG, data); } /** * 返回成功消息 * @author zyl * @param msg 返回内容 * @return 成功消息 */ public static <T> ApiRes<T> success(String msg) { return ApiRes.success(msg, null); } /** * 返回成功消息 * @author zyl * @param msg 返回内容 * @param data 数据对象 * @return 成功消息 */ public static <T> ApiRes<T> success(String msg, T data) { return new ApiRes<>(HttpStatus.OK.value(), msg, data); } /** * 返回错误消息 * @author zyl */ public static <T> ApiRes<T> error() { return ApiRes.error(ERROR_MSG); } /** * 返回错误消息 * @author zyl * @param msg 返回内容 * @return 警告消息 */ public static <T> ApiRes<T> error(String msg) { return ApiRes.error(msg, null); } /** * 返回错误消息 * @author zyl * @param msg 返回内容 * @param data 数据对象 * @return 警告消息 */ public static <T> ApiRes<T> error(String msg, T data) { return new ApiRes<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, data); } /** * 返回错误消息 * @author zyl * @param msg 返回内容 * @param code 响应编码 * @return 警告消息 */ public static <T> ApiRes<T> error(int code, String msg) { return new ApiRes<>(code, msg, null); } }
这里定义实现了统一响应VO,下面继续实现定义City的响应VO:
import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * @author zyl */ @NoArgsConstructor @AllArgsConstructor @Data @Builder public class CityRes { private Long id; private String name; private String state; private String country; }
City响应VO定义实现。
/**
* 测试city模板
* @author zyl
*/
public interface CityService {
ApiRes<CityRes> findByState(String state);
ApiRes<CityRes> findById(Long id);
void delete(Long id);
}
import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author zyl */ @Service public class CityServiceImpl implements CityService { @Resource private CityMapper cityMapper; @Resource private CityDao cityDao; @Override public ApiRes<CityRes> findByState(String state) { City city = cityMapper.findByState(state); if (city == null) { throw new HandleException("没有找到城市数据"); } CityRes cityRes = CityRes.builder().build(); BeanUtils.copyProperties(city, cityRes); return ApiRes.success(cityRes); } @Override public ApiRes<CityRes> findById(Long id) { // 先查缓存 City city = cityDao.findById(id); if (city == null) { // 查数据库 city = cityMapper.findById(id); if (city != null) { // 更新缓存 cityDao.save(city); } } if (city == null) { throw new HandleException("没有找到城市数据"); } CityRes cityRes = CityRes.builder().build(); BeanUtils.copyProperties(city, cityRes); return ApiRes.success(cityRes); } @Override public void delete(Long id) { cityMapper.delete(id); cityDao.delete(id); } }
这里的HandleException类,是自定义异常处理类,这里就不再给出详细实现了。CityMapper类是mybatis的相关定义实现,这不是这里需要关注的,故也不再给出详细实现了。
import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.Valid; /** * 测试模板 * @author zyl */ @RefreshScope @RestController @RequestMapping("/hello") public class HelloController { @Resource private CityService cityService; @GetMapping("/city/findById") public ResponseEntity<ApiRes<CityRes>> cityFindById(@RequestParam Long id) { return ResponseEntity.ok(cityService.findById(id)); } @GetMapping("/city/delete") public ResponseEntity<ApiRes<String>> cityDelete(@RequestParam Long id) { cityService.delete(id); ApiRes<String> apiRes = ApiRes.success("删除成功"); return ResponseEntity.ok(apiRes); } }
drop table if exists city;
create table city (id int primary key auto_increment, name varchar(200), state varchar(200), country varchar(200));
insert into city (name, state, country) values ('San Francisco', 'CA', 'US');
insert into city (name, state, country) values ('San Francisco2', 'CA2', 'US2');
这里只看看Redis里面存的数据效果图:
看看postman中的响应:
这里主要使用BoundHashOperations类来处理redis中的对象。为什么不使用HashOperations类?因为不能直接使用HashOperations类对对象进行过期时间设置,而BoundHashOperations类可以这弄。这里值得注意的是,我们只能对Redis里面的整个散列进行过期,不能对散列里面的具体key过期。这一点Redis规范要注意一下。具体如下图:
我们能对1过期,但是不能对2进行过期处理。HashOperations类就是把多个对象存在一个1里面,我们这里为了实现单个对象过期处理,就只能使用BoundHashOperations类,在一个1里面存一个对象,这样实现对一个对象的redis过期。
总的来说,BoundHashOperations类可以自己过期Redis数据,HashOperations类没有直接过期数据方法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。