Spring Boot整合JPA+MySQL+Redis(一)

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結構:
MySQL表結構

地址欄兩次訪問http://localhost/test/add後:

MySQL數據庫:
MySQL數據

redis客戶端:
redis客戶端數據

地址欄兩次訪問http://localhost/test/findById/1後,可以看到只執行了一次sql語句:
控制檯sql打印
頁面數據顯示:
findById數據顯示

地址欄訪問http://localhost/test/update/1後,分別使用http://localhost/test/findById/1http://localhost/test/findAll查詢數據,可以看到數據均發生了修改(數據庫和redis客戶端的數據均對應發生變化,在此筆者就不貼圖了):
findById數據顯示
findAll數據顯示

地址欄訪問http://localhost/test/delete/1後,使用http://localhost/test/findAll查詢數據,可以看到數據的變化(數據庫和redis客戶端的數據均對應發生變化,在此筆者就不貼圖了):
findAll數據顯示

總結:以上爲Spring Boot整合JPA+MySQL+Redis的所有過程,實現了作爲中間層的redis緩存以及MySQL數據庫和redis客戶端的數據同步,完成了對實體對象的CRUD操作。筆者不才,如有遺漏、錯誤讀者可提出建議。

*注:後續進一步的實現可參考筆者的下一篇文章,Spring Boot整合JPA+MySQL+Redis(二)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章