19.SPRINGBOOT JPA REDIS 整合在一起做一套帶分頁的 增刪改查 CRUD


pom.xml

增加對 Redis 支持的包
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<? xml  version = "1.0"  encoding = "UTF-8" ?>
< project  xmlns = "http://maven.apache.org/POM/4.0.0"  xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >
     < modelVersion >4.0.0</ modelVersion >
 
   < groupId >com.how2java</ groupId >
   < artifactId >springboot</ artifactId >
   < version >0.0.1-SNAPSHOT</ version >
   < name >springboot</ name >
   < description >springboot</ description >
   < packaging >war</ packaging >
   
     < parent >
         < groupId >org.springframework.boot</ groupId >
         < artifactId >spring-boot-starter-parent</ artifactId >
         < version >1.5.9.RELEASE</ version >
     </ parent >
     < dependencies >
         < dependency >
             < groupId >org.springframework.boot</ groupId >
             < artifactId >spring-boot-starter-web</ artifactId >
         </ dependency >
         < dependency >
             < groupId >org.springframework.boot</ groupId >
             < artifactId >spring-boot-starter-tomcat</ artifactId >
             
         </ dependency >
         < dependency >
               < groupId >junit</ groupId >
               < artifactId >junit</ artifactId >
               < version >3.8.1</ version >
               < scope >test</ scope >
         </ dependency >
         <!-- servlet依賴. -->
         < dependency >
               < groupId >javax.servlet</ groupId >
               < artifactId >javax.servlet-api</ artifactId >
               
         </ dependency >
               < dependency >
                      < groupId >javax.servlet</ groupId >
                      < artifactId >jstl</ artifactId >
               </ dependency >
         <!-- tomcat的支持.-->
         < dependency >
                < groupId >org.apache.tomcat.embed</ groupId >
                < artifactId >tomcat-embed-jasper</ artifactId >
                
         </ dependency >    
         < dependency >
             < groupId >org.springframework.boot</ groupId >
             < artifactId >spring-boot-devtools</ artifactId >
             < optional >true</ optional <!-- 這個需要為 true 熱部署才有效 -->
         </ dependency >
         
         <!-- mysql-->
         < dependency >
             < groupId >mysql</ groupId >
             < artifactId >mysql-connector-java</ artifactId >
             < version >5.1.21</ version >
         </ dependency >
 
         <!-- jpa-->
         < dependency >
             < groupId >org.springframework.boot</ groupId >
             < artifactId >spring-boot-starter-data-jpa</ artifactId >
         </ dependency >
         <!-- redis -->       
         < dependency >
             < groupId >org.springframework.boot</ groupId >
             < artifactId >spring-boot-starter-data-redis</ artifactId >
         </ dependency >       
         < dependency >
             < groupId >org.springframework.boot</ groupId >
             < artifactId >spring-boot-starter-test</ artifactId >
             < scope >test</ scope >
         </ dependency >       
         < dependency >
           < groupId >junit</ groupId >
           < artifactId >junit</ artifactId >
           < version > 4.12</ version >
         </ dependency >       
     </ dependencies >
 
     < properties >
         < java.version >1.8</ java.version >
     </ properties >
 
     < build >
         < plugins >
             < plugin >
                 < groupId >org.springframework.boot</ groupId >
                 < artifactId >spring-boot-maven-plugin</ artifactId >
             </ plugin >
         </ plugins >
     </ build >
 
</ project >
 步驟 8 : 

application.properties

增加redis相關配置
同時讓hibernate的sql語句顯示出來,這樣才知道到底是通過 Redis 取到的數據,還是依然是從數據庫取到的數據
 
spring.jpa.show-sql=true
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
 
###########################redis#########################
#Redis數據庫索引(默認為0)
spring.redis.database=0
#Redis服務器地址
spring.redis.host=127.0.0.1
#Redis服務器連接端口
spring.redis.port=6379
#Redis服務器連接密碼(默認為空)
spring.redis.password=
#連接池最大連接數(使用負值表示沒有限制)
spring.redis.pool.max-active=10
#連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
#連接池中的最大空閑連接
spring.redis.pool.max-idle=8
#連接池中的最小空閑連接
spring.redis.pool.min-idle=0
#連接超時時間(毫秒)
spring.redis.timeout=0
 
spring.jpa.show-sql=true
 步驟 9 : 

Application

增加注解,以開啟緩存
 
@EnableCaching
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package  com.how2java.springboot;
  
import  org.springframework.boot.SpringApplication;
import  org.springframework.boot.autoconfigure.SpringBootApplication;
import  org.springframework.cache.annotation.EnableCaching;
  
@SpringBootApplication
@EnableCaching
public  class  Application {
  
     public  static  void  main(String[] args) {
         SpringApplication.run(Application. class , args);
     }
  
}
 步驟 10 : 

RedisConfig.java

Redis 緩存配置類。
這個配置,一個作用: 讓保存到 Redis 里的 key 和 value 都轉換為可讀的 json 格式。 否則會是二進制格式,通過 RedisClient 工具也無法識別。
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package  com.how2java.springboot.config;
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.core.RedisTemplate;
import  org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import  org.springframework.data.redis.serializer.RedisSerializer;
import  org.springframework.data.redis.serializer.StringRedisSerializer;
 
import  com.fasterxml.jackson.annotation.JsonAutoDetect;
import  com.fasterxml.jackson.annotation.PropertyAccessor;
import  com.fasterxml.jackson.databind.ObjectMapper;
 
@Configuration
@EnableCaching
//Redis 緩存配置類
public  class  RedisConfig  extends  CachingConfigurerSupport {
 
     @Bean
     public  CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
         RedisSerializer stringSerializer =  new  StringRedisSerializer();
         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =  new  Jackson2JsonRedisSerializer(Object. class );
         ObjectMapper om =  new  ObjectMapper();
         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY);
         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         jackson2JsonRedisSerializer.setObjectMapper(om);
         redisTemplate.setKeySerializer(stringSerializer);
         redisTemplate.setHashKeySerializer(stringSerializer);  
         
         redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);         
         redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
         CacheManager cacheManager =  new  RedisCacheManager(redisTemplate);
         return  cacheManager;
  
     }
}
 步驟 11 : 

Page4Navigator

創建一個工具類 Page4Navigator 用以替換 原本分頁查詢要返回的 org.springframework.data.domain.Page 類。 原因是 Page 類對json 還原不支持,在放如 Redis 之后,再拿出來,就會報錯失敗。
使用 Page4Navigator 對其包裹,就解決了這個問題了。
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package  com.how2java.springboot.util;
 
import  java.util.List;
 
import  org.springframework.data.domain.Page;
 
public  class  Page4Navigator<T> {
     Page<T> page4jpa;
     int  navigatePages;
     
     int  totalPages;
 
     int  number;
     
     long  totalElements;
     
     int  size;
 
     int  numberOfElements;
 
     List<T> content;
 
     boolean  isHasContent;
 
     boolean  first;
 
     boolean  last;
     
     boolean  isHasNext;
 
     boolean  isHasPrevious;
     
     int [] navigatepageNums;
     
     public  Page4Navigator() {
         //這個空的分頁是為了 Redis 從 json格式轉換為 Page4Navigator 對象而專門提供的
     }
     
     public  Page4Navigator(Page<T> page4jpa, int  navigatePages) {
         this .page4jpa = page4jpa;
         this .navigatePages = navigatePages;
         
         totalPages = page4jpa.getTotalPages();
         
         number  = page4jpa.getNumber() ;
         
         totalElements = page4jpa.getTotalElements();
         
         size = page4jpa.getSize();
         
         numberOfElements = page4jpa.getNumberOfElements();
         
         content = page4jpa.getContent();
         
         isHasContent = page4jpa.hasContent();
                 
         first = page4jpa.isFirst();
         
         last = page4jpa.isLast();
         
         isHasNext = page4jpa.hasNext();
         
         isHasPrevious  = page4jpa.hasPrevious();       
         
         calcNavigatepageNums();
         
     }
 
     private  void  calcNavigatepageNums() {
         int  navigatepageNums[];
         int  totalPages = getTotalPages();
         int  num = getNumber();
         //當總頁數小於或等於導航頁碼數時
         if  (totalPages <= navigatePages) {
             navigatepageNums =  new  int [totalPages];
             for  ( int  i =  0 ; i < totalPages; i++) {
                 navigatepageNums[i] = i +  1 ;
             }
         else  //當總頁數大於導航頁碼數時
             navigatepageNums =  new  int [navigatePages];
             int  startNum = num - navigatePages /  2 ;
             int  endNum = num + navigatePages /  2 ;
 
             if  (startNum <  1 ) {
                 startNum =  1 ;
                 //(最前navigatePages頁
                 for  ( int  i =  0 ; i < navigatePages; i++) {
                     navigatepageNums[i] = startNum++;
                 }
             else  if  (endNum > totalPages) {
                 endNum = totalPages;
                 //最后navigatePages頁
                 for  ( int  i = navigatePages -  1 ; i >=  0 ; i--) {
                     navigatepageNums[i] = endNum--;
                 }
             else  {
                 //所有中間頁
                 for  ( int  i =  0 ; i < navigatePages; i++) {
                     navigatepageNums[i] = startNum++;
                 }
             }
         }  
         this .navigatepageNums = navigatepageNums;
     }
 
     public  int  getNavigatePages() {
         return  navigatePages;
     }
 
     public  void  setNavigatePages( int  navigatePages) {
         this .navigatePages = navigatePages;
     }
 
     public  int  getTotalPages() {
         return  totalPages;
     }
 
     public  void  setTotalPages( int  totalPages) {
         this .totalPages = totalPages;
     }
 
     public  int  getNumber() {
         return  number;
     }
 
     public  void  setNumber( int  number) {
         this .number = number;
     }
 
     public  long  getTotalElements() {
         return  totalElements;
     }
 
     public  void  setTotalElements( long  totalElements) {
         this .totalElements = totalElements;
     }
 
     public  int  getSize() {
         return  size;
     }
 
     public  void  setSize( int  size) {
         this .size = size;
     }
 
     public  int  getNumberOfElements() {
         return  numberOfElements;
     }
 
     public  void  setNumberOfElements( int  numberOfElements) {
         this .numberOfElements = numberOfElements;
     }
 
     public  List<T> getContent() {
         return  content;
     }
 
     public  void  setContent(List<T> content) {
         this .content = content;
     }
 
     public  boolean  isHasContent() {
         return  isHasContent;
     }
 
     public  void  setHasContent( boolean  isHasContent) {
         this .isHasContent = isHasContent;
     }
 
     public  boolean  isFirst() {
         return  first;
     }
 
     public  void  setFirst( boolean  first) {
         this .first = first;
     }
 
     public  boolean  isLast() {
         return  last;
     }
 
     public  void  setLast( boolean  last) {
         this .last = last;
     }
 
     public  boolean  isHasNext() {
         return  isHasNext;
     }
 
     public  void  setHasNext( boolean  isHasNext) {
         this .isHasNext = isHasNext;
     }
 
     public  boolean  isHasPrevious() {
         return  isHasPrevious;
     }
 
     public  void  setHasPrevious( boolean  isHasPrevious) {
         this .isHasPrevious = isHasPrevious;
     }
 
     public  int [] getNavigatepageNums() {
         return  navigatepageNums;
     }
 
     public  void  setNavigatepageNums( int [] navigatepageNums) {
         this .navigatepageNums = navigatepageNums;
     }
 
}
 步驟 12 : 

CategoryService

增加 Service接口。 注意: list 返回的是 Page4Navigator 而不是 Page 類型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package  com.how2java.springboot.service;
 
import  org.springframework.data.domain.Pageable;
 
import  com.how2java.springboot.pojo.Category;
import  com.how2java.springboot.util.Page4Navigator;
 
public  interface  CategoryService {
 
     public  Page4Navigator<Category> list(Pageable pageable);
     
     public  void  save(Category category);
     
     public  void  delete( int  id);
     
     public  Category get( int  id);
}
 步驟 13 : 

CategoryServiceImpl

實現類CategoryServiceImp 做了一下工作:
1. 實現 CategoryService 接口,提供 crud

2. 在相應方法實現的時候,都是通過調用 dao 實現的

3. CacheConfig,表示 分類數據在 redis 中都放在 category 這個分組里。
 
@CacheConfig(cacheNames="category")
 


4. list方法講解:
先說注解
 
@Cacheable(key="'category '+#p0.offset + '-' + #p0.pageSize ")
 

假如是第一頁,即offset=0,pageSize=0,那么會創建一個 key: "category 0-5"
首先根據這個key 到 redis中查詢數據。 第一次是不會有數據的,那么就會從數據庫中取到這5條數據,然后以這個 key: "category 0-5" 保存到 redis 數據庫中。
下一次再次訪問的時候,根據這個key,就可以從 redis 里取到數據了。

5. get 方法講解
先說注解:
 
@Cacheable(key="'category '+ #p0")
 

假如是獲取id=71的數據,那么
就會以 key= "category 71" 到reids中去獲取,如果沒有就會從數據庫中拿到,然后再以 key= "category 71" 這個值存放到 redis 當中。
下一次再次訪問的時候,根據這個key,就可以從 redis 里取到數據了。

6. add 方法講解
先說注解:
 
@CacheEvict(allEntries=true)
// @CachePut(key="'category '+ #p0")
 

可以看到,本來有個 CachePut,但是被注釋掉了。 按理說,本來是應該用這個的。 這樣會到在,在增加數據之后,就會在Redis 中以 key= "category 71" 緩存一條數據。 但是為什么被注釋掉不用呢? 
因為加入這樣做了,那么 list 對應的數據,在緩存在對應的數據,並沒有發生變化呀? 因為 list 對應的數據是這樣的 key: "category 0-5"。 如果用這種方式,就會導致數據不同步,即,雖然增加了,並且也增加到緩存中了,但是因為 key 不一樣,通過查詢拿到的數據,是不會包含新的這一條的。 
所以,才會使用CacheEvict 這個注解,這個注解就表示清除掉緩存。 allEntries= true 是表示清除掉 category 分組 下所有的keys. 注意看截圖,里面有一個 category~keys ,里面就表明了都有哪些 keys,都會被清除掉。 
假如這個時候,還有一個分組 cacheNames="product", 那么它下面對應的緩存,都是不會被影響到的。 這樣就保證了,只清楚當前分組下的緩存,而不是清除 redis 所有的數據了。

7. delete 方法講解
先說注解:
 
@CacheEvict(allEntries=true)
// @CacheEvict(key="'category '+ #p0")
 

這個道理和 add 是一樣的,僅僅刪除 key= "category 71" ,沒有什么意義, key: "category 0-5" 里面的數據沒有影響呀。 所以還是通過 CacheEvict刪除掉所有的緩存就好了。
CategoryServiceImpl
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package  com.how2java.springboot.service.impl;
 
import  org.springframework.beans.factory.annotation.Autowired;
import  org.springframework.cache.annotation.CacheConfig;
import  org.springframework.cache.annotation.CacheEvict;
import  org.springframework.cache.annotation.CachePut;
import  org.springframework.cache.annotation.Cacheable;
import  org.springframework.data.domain.Page;
import  org.springframework.data.domain.Pageable;
import  org.springframework.stereotype.Service;
 
import  com.how2java.springboot.dao.CategoryDAO;
import  com.how2java.springboot.pojo.Category;
import  com.how2java.springboot.service.CategoryService;
import  com.how2java.springboot.util.Page4Navigator;
 
@Service
@CacheConfig (cacheNames= "category" )
public  class  CategoryServiceImpl  implements  CategoryService{
 
     @Autowired  CategoryDAO categoryDAO;
     
     @Override
     @Cacheable (key= "'category '+#p0.offset + '-' + #p0.pageSize " )
     public  Page4Navigator<Category> list(Pageable pageable) {
         Page<Category> pageFromJPA=  categoryDAO.findAll(pageable);
         Page4Navigator<Category> page =  new  Page4Navigator<>(pageFromJPA, 5 );
         return  page;
     }
 
     @Override
     @Cacheable (key= "'category '+ #p0" )
     public  Category get( int  id) {
         Category c =categoryDAO.findOne(id);
         return  c;
     }  
     
     @Override
     @CacheEvict (allEntries= true )
//  @CachePut(key="'category '+ #p0")
     public  void  save(Category category) {
         // TODO Auto-generated method stub
         categoryDAO.save(category);
     }
     
     @Override
     @CacheEvict (allEntries= true )
//  @CacheEvict(key="'category '+ #p0")
     public  void  delete( int  id) {
         // TODO Auto-generated method stub
         categoryDAO.delete(id);
     }
 
}
 步驟 14 : 

CategoryController

 
由原來直接從 dao 獲取,變為從 Service 獲取了
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package  com.how2java.springboot.web;
import  org.springframework.beans.factory.annotation.Autowired;
import  org.springframework.data.domain.PageRequest;
import  org.springframework.data.domain.Pageable;
import  org.springframework.data.domain.Sort;
import  org.springframework.stereotype.Controller;
import  org.springframework.ui.Model;
import  org.springframework.web.bind.annotation.RequestMapping;
import  org.springframework.web.bind.annotation.RequestParam;
 
import  com.how2java.springboot.pojo.Category;
import  com.how2java.springboot.service.CategoryService;
import  com.how2java.springboot.util.Page4Navigator;
  
@Controller
public  class  CategoryController {
     @Autowired  CategoryService categoryService;
     
     @RequestMapping ( "/listCategory" )
     
     public  String listCategory(Model m, @RequestParam (value =  "start" , defaultValue =  "0" int start, @RequestParam (value =  "size" , defaultValue =  "5" int  size)  throws  Exception {
         start = start< 0 ? 0 :start;
         Sort sort =  new  Sort(Sort.Direction.DESC,  "id" );
         Pageable pageable =  new  PageRequest(start, size, sort);
         Page4Navigator<Category> page =categoryService.list(pageable);
         m.addAttribute( "page" , page);
         return  "listCategory" ;
     }
 
     @RequestMapping ( "/addCategory" )
     public  String addCategory(Category c)  throws  Exception {
         categoryService.save(c);
         return  "redirect:listCategory" ;
     }
     @RequestMapping ( "/deleteCategory" )
     public  String deleteCategory(Category c)  throws  Exception {
         categoryService.delete(c.getId());
         return  "redirect:listCategory" ;
     }
     @RequestMapping ( "/updateCategory" )
     public  String updateCategory(Category c)  throws  Exception {
         categoryService.save(c);
         return  "redirect:listCategory" ;
     }
     @RequestMapping ( "/editCategory" )
     public  String ediitCategory( int  id,Model m)  throws  Exception {
         Category c= categoryService.get(id);
         m.addAttribute( "c" , c);
         return  "editCategory" ;
     }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM