Spring Boot整合JPA+MySQL+Redis
在開始之前,我們在pom.xml添入關鍵依賴:
JPA依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
MySQL依賴:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
Redis依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
下面開始整合過程:
①首先,我們在application.yml添入以下配置:
spring:
##配置數據源
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: ##數據庫連接地址
username: ##數據庫用戶名
password: ##數據庫密碼
##開啓JPA
jpa:
hibernate:
##自動建表
ddl-auto: update
##打印sql語句
show-sql: true
##配置redis
redis:
##默認redis客戶端連接爲0 可修改
database: 0
host: ##連接地址 如:00.000.000.000
##連接端口號 默認是6379 可修改
port: 6379
password: ##redis客戶端密碼
pool:
##連接池最大空閒連接
max-idle: 8
##連接池最小空閒連接
min-idle: 0
##連接池最大連接數
max-active: 8
##連接池最大等待時間
max-wait: 1
##連接超時時間
timeout: 5000
②配置Redis,注意加上@EnableCaching註解開啓緩存:
package com.java.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis配置
* @author zxy
*
*/
@Configuration
@EnableCaching//啓用緩存
public class RedisConfig extends CachingConfigurerSupport{
/**
* 注入 RedisConnectionFactory
*/
@Autowired
RedisConnectionFactory redisConnectionFactory;
/**
* 實例化 RedisTemplate 對象
*
* @return
*/
@Bean
public RedisTemplate<String, Object> functionDomainRedisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
initDomainRedisTemplate(redisTemplate, redisConnectionFactory);
return redisTemplate;
}
/**
* 設置數據存入 redis 的序列化方式
*
* @param redisTemplate
* @param factory
*/
private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
//定義key生成策略
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setConnectionFactory(factory);
}
/**
* 緩存管理器
* @param redisTemplate
* @return
*/
@Bean
public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
CacheManager cacheManager = new RedisCacheManager(redisTemplate);
return cacheManager;
}
/**
* 實例化 HashOperations 對象,可以使用 Hash 類型操作
*
* @param redisTemplate
* @return
*/
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
/**
* 實例化 ValueOperations 對象,可以使用 String 操作
*
* @param redisTemplate
* @return
*/
@Bean
public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForValue();
}
/**
* 實例化 ListOperations 對象,可以使用 List 操作
*
* @param redisTemplate
* @return
*/
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
/**
* 實例化 SetOperations 對象,可以使用 Set 操作
*
* @param redisTemplate
* @return
*/
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
/**
* 實例化 ZSetOperations 對象,可以使用 ZSet 操作
*
* @param redisTemplate
* @return
*/
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}
③創建User實體類,一定要實現Serializable序列化:
package com.java.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="t_user")
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String redisKey;//redis中的key
@Id
@GeneratedValue
private Integer id;
private String name;
private String sex;
private String address;
public String getRedisKey() {
return redisKey;
}
public void setRedisKey(String keyStr) {
this.redisKey = "user_"+keyStr;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [redisKey=" + redisKey + ", id=" + id + ", name=" + name + ", sex=" + sex + ", address=" + address
+ "]";
}
}
④定義User用戶JPA接口:
package com.java.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.java.entity.User;
/**
* 用戶 Repository層接口
* @author zxy
*
*/
public interface UserRepository extends JpaRepository<User, Integer>{
}
⑤定義抽象類RedisService實現redis對泛型對象的操作,多態調用getRedisKey()
方法獲取子類的自定義redis key生成相應的Hash表結構:
package com.java.redis.service;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public abstract class RedisService<T> {
/**
* 實例化 RedisTemplate對象
*/
@Autowired
protected RedisTemplate<String, Object> redisTemplate;
/**
* 定義Hash結構 操作存儲實體對象
*/
@Resource
protected HashOperations<String, String, T> hashOperations;
/**
* 定義Hash表的redis key名稱
*
* @return
*/
protected abstract String getRedisKey();
/**
* 在相應Hash表中添加鍵值對 key:Object(doamin)
*
* @param key key
* @param doamin 對象
* @param expire 過期時間(單位:秒),傳入 -1 時表示不設置過期時間
*/
public void put(String key, T doamin, long expire) {
hashOperations.put(getRedisKey(), key, doamin);
if (expire != -1) {
redisTemplate.expire(getRedisKey(), expire, TimeUnit.SECONDS);
}
}
/**
* 在相應Hash表中刪除key名稱的元素
*
* @param key 傳入key的名稱
*/
public void remove(String key) {
hashOperations.delete(getRedisKey(), key);
}
/**
* 在相應Hash表中查詢key名稱的元素
*
* @param key 查詢的key
* @return
*/
public T get(String key) {
return hashOperations.get(getRedisKey(), key);
}
/**
* 獲取在相應Hash表下的所有實體對象
*
* @return
*/
public List<T> getAll() {
return hashOperations.values(getRedisKey());
}
/**
* 查詢在相應Hash表下的所有key名稱
*
* @return
*/
public Set<String> getKeys() {
return hashOperations.keys(getRedisKey());
}
/**
* 判斷在相應Hash表下key是否存在
*
* @param key 傳入key的名稱
* @return
*/
public boolean isKeyExists(String key) {
return hashOperations.hasKey(getRedisKey(), key);
}
/**
* 查詢相應Hash表的緩存數量
*
* @return
*/
public long count() {
return hashOperations.size(getRedisKey());
}
/**
* 清空相應Hash表的所有緩存
*/
public void empty() {
Set<String> set = hashOperations.keys(getRedisKey());
set.stream().forEach(key -> hashOperations.delete(getRedisKey(), key));
}
}
⑥繼承RedisService類自定義實現UserRedisServiceImpl,泛型注入User實體,自定義redis key的名稱爲USER_KEY,User實體的所有緩存均存入名爲USER_KEY的Hash表中:
package com.java.redis.service.impl;
import org.springframework.stereotype.Service;
import com.java.entity.User;
import com.java.redis.service.RedisService;
/**
* 用戶redis service繼承類
* @author zxy
*
*/
@Service("userRedisService")
public class UserRedisServiceImpl extends RedisService<User>{
//自定義redis key作爲Hash表的key名稱
private static final String REDIS_KEY = "USER_KEY";
@Override
protected String getRedisKey() {
// TODO Auto-generated method stub
return REDIS_KEY;
}
}
⑦定義User用戶Service接口UserService:
package com.java.service;
import java.util.List;
import com.java.entity.User;
/**
* 用戶Service層接口
* @author zxy
*
*/
public interface UserService {
/**
* 根據Id查詢用戶信息
* @param id
* @return
*/
public User findById(Integer id);
/**
* 查詢所有用戶信息
* @return
*/
public List<User> findAll();
/**
* 添加或修改用戶信息
* @param user
*/
public void save(User user);
/**
* 根據Id刪除用戶信息
* @param id
*/
public void delete(Integer id);
}
⑧定義User用戶Service接口實現類UserServiceImpl,@CacheEvict註解中的allEntries=true
屬性很重要,確保數據修改後緩存能夠得到刷新:
package com.java.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.java.entity.User;
import com.java.redis.service.impl.UserRedisServiceImpl;
import com.java.repository.UserRepository;
import com.java.service.UserService;
/**
* 用戶Service層接口實現類
* @author zxy
*
*/
@Service("userService")
public class UserServiceImpl implements UserService{
@Resource
private UserRepository userRepository;
@Resource
private UserRedisServiceImpl userRedisService;
@Cacheable(value="userCache") //緩存,這裏沒有指定key.
@Override
public User findById(Integer id) {
// TODO Auto-generated method stub
return userRepository.findOne(id);
}
@Cacheable(value="userCache")
@Override
public List<User> findAll() {
// TODO Auto-generated method stub
return userRepository.findAll();
}
//allEntries 清空緩存所有屬性 確保更新後緩存刷新
@CacheEvict(value="userCache", allEntries=true)
@Override
public void save(User user) {
// TODO Auto-generated method stub
userRepository.save(user);
if(user.getRedisKey()==null||"".equals(user.getRedisKey().trim())){
user.setRedisKey(user.getId().toString());
userRepository.save(user);
}
userRedisService.put(user.getRedisKey(), user, -1);
}
//allEntries 清空緩存所有屬性 確保更新後緩存刷新
@CacheEvict(value="userCache", allEntries=true)
@Override
public void delete(Integer id) {
// TODO Auto-generated method stub
User user=userRepository.findOne(id);
userRedisService.remove(user.getRedisKey());
userRepository.delete(id);
}
}
自此,所有準備工作全部完成,下面定義一個Controller層測試類TestController:
package com.java.controller;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.java.entity.User;
import com.java.service.UserService;
/**
* 測試Controller層
* @author zxy
*
*/
@RestController
@RequestMapping("/test")
public class TestController {
private static Integer userNum=0;
@Resource
private UserService userService;
/**
* 根據Id查詢用戶信息
* @param id
* @return
*/
@RequestMapping("/findById/{id}")
public User findById(@PathVariable("id")Integer id){
return userService.findById(id);
}
/**
* 查詢所有用戶信息
* @return
*/
@RequestMapping("/findAll")
public List<User> findAll(){
return userService.findAll();
}
/**
* 添加用戶信息
* @return
*/
@RequestMapping("/add")
public String add(){
User user=new User();
++userNum;
user.setName("張三"+userNum);
user.setSex("男"+userNum);
user.setAddress("廣州"+userNum);
userService.save(user);
return "success";
}
/**
* 修改用戶信息
* @return
*/
@RequestMapping("/update/{id}")
public String update(@PathVariable("id")Integer id){
User user=userService.findById(id);
user.setName(user.getName()+"asd");
user.setSex(user.getSex()+"asd");
user.setAddress(user.getAddress()+"asd");
userService.save(user);
return "success";
}
/**
* 根據Id刪除用戶信息
* @return
*/
@RequestMapping("/delete/{id}")
public String delete(@PathVariable("id")Integer id){
userService.delete(id);
return "success";
}
}
* 測試過程:
項目啓動,redis客戶端數據庫均爲空,數據表建表t_user結構:
地址欄兩次訪問http://localhost/test/add後:
MySQL數據庫:
redis客戶端:
地址欄兩次訪問http://localhost/test/findById/1後,可以看到只執行了一次sql語句:
頁面數據顯示:
地址欄訪問http://localhost/test/update/1後,分別使用http://localhost/test/findById/1和http://localhost/test/findAll查詢數據,可以看到數據均發生了修改(數據庫和redis客戶端的數據均對應發生變化,在此筆者就不貼圖了):
地址欄訪問http://localhost/test/delete/1後,使用http://localhost/test/findAll查詢數據,可以看到數據的變化(數據庫和redis客戶端的數據均對應發生變化,在此筆者就不貼圖了):
總結:以上爲Spring Boot整合JPA+MySQL+Redis的所有過程,實現了作爲中間層的redis緩存以及MySQL數據庫和redis客戶端的數據同步,完成了對實體對象的CRUD操作。筆者不才,如有遺漏、錯誤讀者可提出建議。
*注:後續進一步的實現可參考筆者的下一篇文章,Spring Boot整合JPA+MySQL+Redis(二)