Spring Boot 入門之緩存和 NoSQL 篇(四)

原文地址:Spring Boot 入門之緩存和 NoSQL 篇(四)
博客地址:http://www.extlight.comhtml

1、前言

當系統的訪問量增大時,相應的數據庫的性能就逐漸降低。可是,大多數請求都是在重複的獲取相同的數據,若是使用緩存,將結果數據放入其中能夠很大程度上減輕數據庫的負擔,提高系統的響應速度。java

本篇將介紹 Spring Boot 中緩存和 NoSQL 的使用。上篇文章《Spring Boot 入門之持久層篇(三)》git

2、整合緩存

Spring Boot 針對不一樣的緩存技術實現了不一樣的封裝,本篇主要介紹 EhCache 和 Redis 緩存。github

Spring Boot 提供瞭如下幾個註解實現聲明式緩存:redis

註解 說明
@EnableCaching 開啓緩存功能,放在配置類或啓動類上
@CacheConfig 緩存配置,設置緩存名稱
@Cacheable 執行方法前先查詢緩存是否有數據。有則直接返回緩存數據;不然查詢數據再將數據放入緩存
@CachePut 執行新增或更新方法後,將數據放入緩存中
@CacheEvict 清除緩存
@Caching 將多個緩存操做從新組合到一個方法中

2.1 EhCache 緩存

2.1.1 添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

2.1.2 添加配置

1)在 src/main/resources 目錄下建立 ehcache.xml 文件,內容以下:spring

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

    <!-- 磁盤緩存位置 -->
    <diskStore path="java.io.tmpdir/ehcache"/>

    <!-- 默認緩存 -->
    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <!-- 自定義緩存 -->
    <cache name="department"
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="50"
           timeToLiveSeconds="50"
           overflowToDisk="false"
           memoryStoreEvictionPolicy="LRU"/>
</ehcache>

說明:sql

name:Cache 的惟一標識
maxElementsInMemory:內存中容許存儲的最大的元素個數
maxElementsOnDisk:硬盤最大緩存個數,0表明無限個
clearOnFlush:內存數量最大時是否清除
eternal:緩存對象是否永久有效,若是是,超時設置將被忽略
overflowToDisk:內存不足(超過 maxElementsInMemory)時,是否啓用磁盤緩存
timeToIdleSeconds:設置對象在失效前的容許閒置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閒置時間無窮大
timeToLiveSeconds:緩存數據的生存時間(TTL),也就是一個元素從構建到消亡的最大時間間隔值,這隻能在元素不是永久駐留時有效,若是該值是0就意味着元素能夠停頓無窮長的時間
diskPersistent:是否將緩存數據持久化到磁盤上,若是爲 true,JVM 重啓數據依然存在。默認值是false
diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每一個Cache都應該有本身的一個緩衝區
diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒
memoryStoreEvictionPolicy:當達到 maxElementsInMemory 限制時,Ehcache 將根據指定策略清除內存。默認爲 LRU(最近最少使用),其餘策略有 FIFO(先進先出),LFU(較少使用)

2)application.properties :mongodb

# 緩存類型(ehcache、redis)
spring.cache.type=ehcache

# ehcache 配置文件
spring.cache.ehcache.config=classpath:ehcache.xml

# 打印日誌,查看 sql
logging.level.com.light.springboot=DEBUG

2.1.3 編碼

在持久層篇的基礎上,結合 Mybatis 測試:數據庫

Service 層:緩存

@CacheConfig(cacheNames = "department")
@Service
public class DepartmentService {

    @Autowired
    private DepartmentMapper departmentMapper;

    @CachePut(key = "#department.id")
    public Department save(Department department) {
        System.out.println("保存 id=" + department.getId() + " 的數據");
        this.departmentMapper.insert(department);
        return department;
    }

    @CachePut(key = "#department.id")
    public Department update(Department department) {
        System.out.println("修改 id=" + department.getId() + " 的數據");
        this.departmentMapper.update(department);
        return department;
    }

    @Cacheable(key = "#id")
    public Department getDepartmentById(Integer id) {
        System.out.println("獲取 id=" + id + " 的數據");
        Department department = this.departmentMapper.getById(id);
        return department;
    }

    @CacheEvict(key = "#id")
    public void delete(Integer id) {
        System.out.println("刪除 id=" + id + " 的數據");
        this.departmentMapper.deleteById(id);
    }
}

控制層:

@Controller
@RequestMapping("department")
@ResponseBody
public class DepartmentController {

    @Autowired
    private DepartmentService departmentService;
    
    @RequestMapping("save")
    public Map<String,Object> save(Department department) {
        this.departmentService.save(department);
        
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code", "200");
        map.put("msg", "保存成功");
        return map;
    }
    
    @RequestMapping("get/{id}")
    public Map<String,Object> get(@PathVariable("id") Integer id) {
        Department department = this.departmentService.getDepartmentById(id);
        
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code", "200");
        map.put("msg", "獲取成功");
        map.put("data", department);
        return map;
    }
    
    @RequestMapping("update")
    public Map<String,Object> update(Department department) {
        this.departmentService.update(department);
        
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code", "200");
        map.put("msg", "修改爲功");
        return map;
    }
    
    @RequestMapping("delete/{id}")
    public Map<String,Object> delete(@PathVariable("id") Integer id) {
        this.departmentService.delete(id);
        
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code", "200");
        map.put("msg", "刪除成功");
        return map;
    }
}

啓動類:

添加 @EnableCaching 註解,開啓緩存功能。

@EnableCaching
@SpringBootApplication
public class SpringbootNosqlApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootNosqlApplication.class, args);
    }
}

2.1.4 測試說明

1) 發起保存請求:

保存 id=2 的數據
2017-12-06 14:50:48.800 DEBUG 680 --- [nio-8081-exec-7] c.l.s.dao.DepartmentMapper.insert        : ==>  Preparing: insert into department(id,name,descr) values(?,?,?) 
2017-12-06 14:50:48.801 DEBUG 680 --- [nio-8081-exec-7] c.l.s.dao.DepartmentMapper.insert        : ==> Parameters: 2(Integer), Ehcache 部門(String), Ehcache(String)
2017-12-06 14:50:48.868 DEBUG 680 --- [nio-8081-exec-7] c.l.s.dao.DepartmentMapper.insert        : <==    Updates: 1

2) 保存成功後,馬上發起查詢請求,沒有日誌打印,但返回對象數據,說明數據是從緩存中獲取。

3) 發起修改請求:

修改 id=2 的數據
2017-12-06 14:51:16.588 DEBUG 680 --- [nio-8081-exec-8] c.l.s.dao.DepartmentMapper.update        : ==>  Preparing: update department set name = ? , descr = ? where id = ? 
2017-12-06 14:51:16.589 DEBUG 680 --- [nio-8081-exec-8] c.l.s.dao.DepartmentMapper.update        : ==> Parameters: Ehcache 部門2(String), Ehcache2(String), 2(Integer)
2017-12-06 14:51:16.657 DEBUG 680 --- [nio-8081-exec-8] c.l.s.dao.DepartmentMapper.update        : <==    Updates: 1

4) 修改爲功後,馬上發起查詢請求,沒有日誌打印,但返回修改後的對象數據,說明緩存中的數據已經同步。

5) 發起刪除請求:

刪除 id=2 的數據
2017-12-06 14:52:07.572 DEBUG 680 --- [nio-8081-exec-1] c.l.s.dao.DepartmentMapper.deleteById    : ==>  Preparing: delete from department where id = ? 
2017-12-06 14:52:07.572 DEBUG 680 --- [nio-8081-exec-1] c.l.s.dao.DepartmentMapper.deleteById    : ==> Parameters: 2(Integer)
2017-12-06 14:52:07.613 DEBUG 680 --- [nio-8081-exec-1] c.l.s.dao.DepartmentMapper.deleteById    : <==    Updates: 1

6) 刪除成功後,馬上發起查詢請求,控制檯打印 sql 語句,說明緩存數據被刪除,須要查詢數據庫。

獲取 id=2 的數據
2017-12-06 14:52:40.324 DEBUG 680 --- [nio-8081-exec-3] c.l.s.dao.DepartmentMapper.getById       : ==>  Preparing: select id,name,descr from department where id = ? 
2017-12-06 14:52:40.325 DEBUG 680 --- [nio-8081-exec-3] c.l.s.dao.DepartmentMapper.getById       : ==> Parameters: 2(Integer)
2017-12-06 14:52:40.328 DEBUG 680 --- [nio-8081-exec-3] c.l.s.dao.DepartmentMapper.getById       : <==      Total: 0

2.2 Redis 緩存

2.2.1 添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.2.2 添加配置

application.properties :

# redis 配置
spring.redis.host=192.168.2.11
spring.redis.port=6379
spring.redis.password=redis123
# 緩存過時時間,單位毫秒
spring.cache.redis.time-to-live=60000

# 緩存類型(ehcache、redis)
spring.cache.type=redis

# 打印日誌,查看 sql
logging.level.com.light.springboot=DEBUG

注意:spring.cache.type=redis,緩存類型設置成 redis。

完成上邊 2 個步驟後,其餘步驟與測試 Ehcache 時的步驟一致。

測試結果也一致,此處省略。

3、整合 Redis

上一個小節其實已經介紹了 Spring Boot 整合 Redis 的內容。

在添加 redis 依賴包啓動項目後,Spring Boot 會自動配置 RedisCacheManger 和 RedisTemplate 的 Bean。若是開發者不想使用 Spring Boot 寫好的 Redis 緩存,而是想使用其 API 本身實現緩存功能、消息隊列或分佈式鎖之類的需求時,能夠繼續往下瀏覽。

Spring Data Redis 爲咱們提供 RedisTemplate 和 StringRedisTemplate 兩個模板進行數據操做,它們主要 的訪問方法以下:

方法 說明
opsForValue() 操做簡單屬性的數據
opsForList() 操做含有 list 的數據
opsForSet() 操做含有 set 的數據
opsForZSet() 操做含有 zset 的數據
opsForHash() 操做含有 hash 的數據

3.1 添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

3.2 配置鏈接

spring.redis.host=192.168.2.11
spring.redis.port=6379
spring.redis.password=redis123

3.3 編碼

@Component
public class RedisDao {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void set(String key, String value) {
        this.stringRedisTemplate.opsForValue().set(key, value);
    }

    public String get(String key) {
        return this.stringRedisTemplate.opsForValue().get(key);
    }

    public void delete(String key) {
        this.stringRedisTemplate.delete(key);
    }
}

3.4 測試

@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisDaoTest {

    @Autowired
    private RedisDao redisDao;
    
    @Test
    public void testSet() {
        String key = "name";
        String value = "zhangsan";
        
        this.redisDao.set(key, value);
    }
    
    @Test
    public void testGet() {
        String key = "name";
        String value = this.redisDao.get(key);
        System.out.println(value);
    }
    
    @Test
    public void testDelete() {
        String key = "name";
        this.redisDao.delete(key);
    }
}

測試結果省略...

4、整合 MongoDB

Spring Data MongoDB 提供了 MongoTemplate 模板 和 Repository 讓開發者進行數據訪問。

4.1 添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

4.2 配置鏈接

spring.data.mongodb.host=192.168.2.31
spring.data.mongodb.port=27017
spring.data.mongodb.database=test

4.3 編碼

4.3.1 使用 MongoTemplate

@Component
public class MongodbDao {

    @Autowired
    private MongoTemplate mongoTemplate;

    public void insert(Department department) {
        this.mongoTemplate.insert(department);
    }

    public void deleteById(int id) {
        Criteria criteria = Criteria.where("id").is(id);
        Query query = new Query(criteria);
        this.mongoTemplate.remove(query, Department.class);
    }

    public void update(Department department) {
        Criteria criteria = Criteria.where("id").is(department.getId());
        Query query = new Query(criteria);
        Update update = new Update();
        update.set("descr", department.getDescr());
        this.mongoTemplate.updateMulti(query, update, Department.class);
    }

    public Department getById(int id) {
        Criteria criteria = Criteria.where("id").is(id);
        Query query = new Query(criteria);
        return this.mongoTemplate.findOne(query, Department.class);
    }
    
    public List<Department> getAll() {
        List<Department> userList = this.mongoTemplate.findAll(Department.class);
        return userList;
    }
    
}

4.3.2 使用 Repository

public interface DepartmentRepository extends MongoRepository<Department, Integer> {

    
}

測試方式與 Redis 測試大同小異,測試結果省略...

5、源碼下載

6、參考資料

相關文章
相關標籤/搜索