4. Spring Data Redis
http://projects.spring.io/spring-data/
1) 왜 스프링 데이터 Redis?
- Spring Framework는 최고의 풀 스택 Java / JEE 애플리케이션 프레임 워크.
가벼운 컨테이너와 의존성 삽입, AOP 및 편리한 서비스 추상화 제공. - Spring Data Redis는 Spring 애플리케이션의 Redis에 대한 쉬운 구성과 액세스를 제공.
저장소와의 상호 작용을 위한 하위 수준 및 높은 수준의 추상화를 모두 제공하여 사용자를 인프라 관련 문제로부터 자유롭게 함.
2) 요구사항
- JDK 레벨 6.0 이상
- Spring Framework 5.0.0.M5 이상
- 키 값 저장소의 경우 Redis 2.6.x 이상
- 스프링 데이터 Redis는 현재 최신 3.2 릴리스에 대해 테스트.
- Spring 알기(이해하기)
- NoSQL 및 Key Value 저장소
3) Redis 연결
dependencies { compile('org.springframework.boot:spring-boot-starter-data-redis') }
- RedisTemplate, RedisConnectionFactory 두개만 설정하면 됨.
가. RedisConnectionFactory
- Redis 서버와의 통신을 위한 low-level 추상화를 제공
- 설정에 따라서 새로운 RedisConnection 또는 이미 존재하는 RedisConnection을 리턴.
RedisConnection 은 Redis 서버와의 통신 추상화를 제공하며, exception 발생시 Spring DataAccessException으로 전환.
- RedisConnection 은 binary value를 인자로 받고 결과를 리턴하는 low-level method를 리턴.
- Jedis, Jredis(1.7 Deprecated), Lettuce, SRP, RJC 등의 클라이언트 라이브러리가 있음.
Jedis 를 보편적으로 많이 사용하는거 같음
나. RedisTemplate
- Redis 서버에 Redis Command를 수행하기 위한 high-level 추상화를 제공
- 오브젝트 serialization 과 connection management를 수행
- Redis 서버에 데이터 CRUD를 위한 Key Type Operations 와 Key Bound Operations 인터페이스를 제공
Key Type Operations
ValueOperations -
Redis string (or value) operations- ListOperations - Redis list operations
- SetOperations - Redis set operations
- ZSetOperations - Redis zset (or sorted set) operations
- HashOperations - Redis hash operations
- HyperLogLogOperations - Redis HyperLogLog operations like (pfadd, pfcount,…?)
- GeoOperations - Redis geospatial operations like GEOADD, GEORADIUS,…?)
Key Bound Operations
- BoundValueOperations - Redis string (or value) key bound operations
- BoundListOperations - Redis list key bound operations
- BoundSetOperations - Redis set key bound operations
- BoundZSetOperations - Redis zset (or sorted set) key bound operations
- BoundHashOperations - Redis hash key bound operations
- BoundGeoOperations - Redis key bound geospatial operations
- thread-safe 하며, 재 사용이 가능
- 대부분의 기능에 RedisSerializer 인터페이스를 사용.
StringRedisSerializer, JdkSerializationRedisSerializer, JacksonJsonRedisSerializer, Jackson2JsonRedisSerializer, GenericJackson2JsonRedisSerializer, OxmSerializer를 사용할 수 있음.
- Redis에 저장된 키와 값이 java.lang.String이 되도록하는 것이 일반적이므로 StringRedisTemplate 확장 기능을 제공.
StringRedisSerializer를 사용하며, 저장된 키와 값은 사람이 읽을 수 있음.
@Configuration public class RedisConfig { @Bean public JedisConnectionFactory jedisConnectionFactory() { JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); jedisConnectionFactory.setHostName("localhost"); jedisConnectionFactory.setPort(6379); jedisConnectionFactory.setTimeout(0); jedisConnectionFactory.setUsePool(true); return jedisConnectionFactory; } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.setConnectionFactory(jedisConnectionFactory()); return redisTemplate; } @Bean public StringRedisTemplate stringRedisTemplate() { StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(); stringRedisTemplate.setKeySerializer(new StringRedisSerializer()); stringRedisTemplate.setValueSerializer(new StringRedisSerializer()); stringRedisTemplate.setConnectionFactory(jedisConnectionFactory()); return stringRedisTemplate; } }
@Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class SpringbootRedisApplicationTests { --- 전체 소스는 github --- @Resource(name="redisTemplate") private ValueOperations<String, String> valueOperations; @Resource(name="redisTemplate") private ValueOperations<String, User> userOperations; /** * Redis string (or value) operations */ @Resource(name="redisTemplate") private ListOperations<String, String> listOperations; /** * Redis string (or value) operations */ @Resource(name="redisTemplate") private SetOperations<String, String> setOperations; /** * Redis string (or value) operations */ @Resource(name="redisTemplate") private ZSetOperations zSetOperations; /** * Redis string (or value) operations */ @Resource(name="redisTemplate") private HashOperations hashOperations; @Before public void 깨끗한_상태로() { // Set<String> keys = redisTemplate.keys("*"); // log.info("@@@@@@@@@ 현재 키가 {} 개 들어 있습니다.", keys.size()); // for (String key : keys) { // redisTemplate.delete(key); // } } @After public void 원래대로_되돌림() { System.out.println(); // try { // redisTemplate.execute(new RedisCallback() { // @Override // public Object doInRedis(RedisConnection connection) throws DataAccessException { // connection.flushAll(); // return null; // } // }); // } catch (Exception e) { // log.warn("모든 캐시를 삭제하는데 실패했습니다.", e); // } } @Test public void valueOperations() { log.info("----------- valueOperations start --------"); String key1 = "key1"; String key2 = "허균"; valueOperations.set(key1, "value1"); valueOperations.set(key2, "홍길동, 허생전"); log.info("{}의 값은 = {}, 사이즈는 = {} 입니다.", key1, valueOperations.size(key1)); log.info("{}의 값은 = {}, 사이즈는 = {} 입니다.", key2, valueOperations.size(key2)); log.info("----------- valueOperations end --------"); } @Test public void listOperations() { log.info("----------- listOperations start --------"); String key1 = "여자랭킹"; listOperations.rightPush(key1, "이나영"); listOperations.rightPush(key1, "소녀시대 윤아"); String key2 = "남자랭킹"; listOperations.leftPush(key2, "이승환"); listOperations.leftPush(key2, "송광호"); log.info(" {} 의 왼쪽 값은 {} ", key1, listOperations.leftPop(key1)); log.info(" {} 의 오른쪽 값은 {} ", key2, listOperations.rightPop(key2)); log.info("----------- listOperations end --------"); } @Test public void setOperations() { log.info("----------- setOperations start --------"); String key1 = "user:gt1000:intro"; setOperations.add(key1, "잘 생긴 훈남"); setOperations.add(key1, "영화감삼"); setOperations.add(key1, "감성적"); setOperations.add(key1, "제일 중요한건.... 소고기 함 쏴 알려 주께..."); Set<String> intro = setOperations.members(key1); intro.forEach((value) -> log.info(" 나는 = {} ", value)); // for(String value : intro) { // log.info(" 나는 = {} ", value); // } log.info("----------- setOperations end --------"); } @Test public void hashOperations() { log.info("----------- hashOperations start --------"); String key1 = "user:gt1000:detail"; hashOperations.put(key1, "age", 43); hashOperations.put(key1, "재산", "개털이다"); hashOperations.put(key1, "job", "언제 짤릴지 모르는 프로그래머"); hashOperations.put(key1, "술은 쇠주", 1); Map<String, Object> entry = hashOperations.entries(key1); log.info(" 그이 나이 {} 세", entry.get("age")); log.info(" 모아둔 재산은 {}", entry.get("재산")); log.info(" 직업은 {}", entry.get("job")); log.info(" 주량은 소주 {} 병이다.", entry.get("술은 쇠주")); log.info("----------- hashOperations end --------"); } @Test public void userTest() { String userId1 = "gt1000"; String userId2 = "gt1003"; String userId3 = "gt1005"; User user1 = new User(userId1, "엄청 멋진 놈"); user1.setAge(30); User user2 = new User(userId2, "당신의 천사가 되고 싶은 사람 == gt1000"); user2.setAge(40); User user3 = new User(userId3, "천사 보다 더 좋은 사람 == gt1000"); user3.setAge(43); userOperations.set(userId1, user1); userOperations.set(userId2, user2); userOperations.set(userId3, user3); log.info("key = {}, value = {}", userId1, userOperations.get(userId1)); log.info("key = {}, value = {}", userId2, userOperations.get(userId2)); log.info("key = {}, value = {}", userId3, userOperations.get(userId3)); } }