Writing in the front
This preliminary set down a series of blog posts including SpringBoot introduction, introduction, configuration, log, web development, data access, combined with related docker, caching, message queue, retrieve, tasks, security, distributed and so on a series of blog posts, a lot of work, is a long process, every step I have detailed as far as possible, deserve to go up screenshot shows, I hope it’s really useful for those of you who are watching. I just want to share technical blog posts, but also want to say that if you think it is useful, please click on the following, give a like, also a relief to me, after all, I have to lose a lot of hair, hey hey hey
Serial article transport bar
Detailed SpringBoot tutorial introduction (1) detailed SpringBoot tutorial introduction (2) detailed SpringBoot tutorial configuration file (1) detailed SpringBoot tutorial configuration file (2) detailed SpringBoot tutorial log framework Detailed SpringBoot tutorial Web development (1) detailed SpringBoot tutorial Web development (2) detailed SpringBoot tutorial Web development (3) detailed SpringBoot tutorial data access detailed SpringBoot tutorial boot configuration principle Detailed SpringBoot tutorial for cache development
Why cache
Cache is an essential optimization point in development. Many points about cache have been optimized. For example, in the scene of loading a lot of data, cache mechanism will be used to improve interface response speed and indirectly improve user experience. Of course, there is also a need to pay attention to the use of cache, for example, if it is not well handled, not good such as LRU strategy, not timely update the data of the database will lead to data lag, and then produce user misread, or confusion. But for all of this, SpringBoot has provided us with handy development tools.
JSR107
Before explaining Caching, I would like to talk about the JSR107 annotation standard. What is it? Java Caching defines five core interfaces for Caching. They are CachingProvider, CacheManager, Cache, Entry and Expiry.
- CachingProvider defines how to create, configure, obtain, manage, and control multiple CacheManager. An application can access multiple CachingProviders at run time.
- CacheManager defines the creation, configuration, acquisition, management, and control of multiple uniquely named caches. These caches reside in the CacheManager context. A CacheManager is owned by only one CachingProvider.
- A Cache is a map-like data structure that temporarily stores key-indexed values. A Cache is owned by only one CacheManager.
- Entry is a key-value pair stored in the Cache.
- Expiry Every entry stored in the Cache has a defined Expiry date. Once this time is exceeded, the entry is in an expired state. Once expired, entries are inaccessible, updated, and deleted. The expiration date of a cache can be set using ExpiryPolicy.
Their relationship goes something like this
Spring cache abstraction
Spring since 3.1 defines the org. Springframework. Cache. The cache and org., springframework. Cache. CacheManager unified interface to different caching technology, It also supports using JCache (JSR-107) annotations to simplify our development.
- The Cache interface is defined by the component specification of the Cache and contains various operations of the Cache.
- Spring provides various implementations of xxxCache under the Cache interface. Such as RedisCache, EhCacheCache, ConcurrentMapCache, etc.
- Each time a method is called that requires caching, Spring checks to see if the specified target method with the specified parameter has already been called. If so, Spring fetches the result of the method call directly from the cache. If not, Spring fetches the result directly from the cache for the next call.
- There are two things we need to focus on when using Spring cache abstraction: first, identifying methods that need to be cached and their caching strategy, and second, reading data from the cache stored in the previous cache
SpringBoot enables annotations
1.1: Setting up the SpringBoot environment
Setting up a SpringBoot in IDEA is simple. Next, I will briefly describe the steps:
Let’s start by using the Idea wizard to create a project with a Web module, which is a very basic operation as mentioned in the previous blog post, and then we can start writing code. First we need to add cache-available annotations to the main program, as follows
@SpringBootApplication @EnableAutoConfiguration @EnableCaching public class SpringbootcacheApplication { public static void main(String[] args) { SpringApplication.run(SpringbootcacheApplication.class, args); }}Copy the code
The @enablecaching is mainly used to start the driver for caching annotations. Otherwise, the caching used later is invalid. After this is started, it can be used.
Common cache annotations
@CacheConfig
The main purpose of this annotation is to configure the cache globally. For example, to configure the cache name (cacheNames), you only need to configure it once on the class.
@Cacheable
This annotation is the most important, main implementation of the function of the next read operation. If we can’t find it, we go to the database execution method. This is the most important method for caching annotations, and basically all of our caching implementations rely on it.
@CacheEvict
This annotation is used in conjunction with @cacheable. Its main purpose is to clear the cache, which is deleted when a method is updated or deleted. If you do not delete the cache, you will not be able to read the latest cache, and the data will be out of date. It can specify the cache key and conditon, and has an important property called allEntries. The default value is false, or it can be set to true. Its main function is to clear all caches, not the specified key.
@CachePut
This annotation it always interpreted the data cache, rather than every time I do check it exists, compared with its usage scenario is less, after all we want is not always give out to all of the data, we still hope to be able to find the cached data, direct return, so that can improve efficiency of our software.
@cache
Cacheable; cachePut; CacheEvict; cacheable; CacheEvict; cacheable; CacheEvict
The above notes are summarized in the following table:
I will present a list of attributes related to the annotations described above so that they can be easily understood
Using the instance
Let’s create a new table, meaning for the article, the following example will operate in this table, the framework used for SSM+SpringBoot, the following is to create a database table.
CREATE TABLE Artile (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL ,
`author` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL ,
`content` mediumtext CHARACTER SET gbk COLLATE gbk_chinese_ci NULL ,
`file_name` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL ,
`state` smallint(2) NULL DEFAULT 1 COMMENT 'state'. PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=gbk COLLATE=gbk_chinese_ci AUTO_INCREMENT=11 ROW_FORMAT=COMPACTCopy the code
Then we create the Mapper layer, which is basically the business operations of adding, deleting, modifying and querying articles, and map them to the specific SQL of XML (the principle of mapping is described in the previous post on data access, of course, we can also directly write the @select annotation in Mapper layer to write SQL statements). Then call it with service
Public interface ArticleMapper {/** * insert an article * @param title * @param author * @param content * @param fileName * @return
*/
public Integer addArticle(@Param("title") String title,@Param("author")String author,
@Param("content")String content,@Param("fileName")String fileName); /** * get articles by id * @param id * @return
*/
public Article getArticleById(@Param("id") Integer id); /** * updateContentById(@param)"content")String content,@Param("id")Integer id); /** * delete articles by id * @param id * @return
*/
public Integer removeArticleById(@Param("id")Integer id); /** * gets the id * @ of the last insertionreturn
*/
public Integer getLastInertId();
}
Copy the code
Then there is the Service layer. The main thing to note is that the caching annotations we described above are based on the Service layer (not on the Contoller and DAO layers). First we configure a CacheConfig on the class and then cacheNames. The following methods all use this cache name as their default value, and they all use this cache name without additional configuration. When performing a select query, we configure @cacheable and specify a key so that all but the first result is cached, and all subsequent results are returned directly from that cache. When updating data (delete or update operations), use @cacheevict to clear the cache in case the cache is not updated when calling @cacheabel
@Service
@CacheConfig(cacheNames = "articleCache") public class ArticleService { private AtomicInteger count =new AtomicInteger(0); @Autowired private ArticleMapper articleMapper; /** * add an article to the cache each time * @return
*/
@CachePut
public Integer addArticle(Article article){
Integer result = articleMapper.addArticle(article.getTitle(), article.getAuthor(), article.getContent(), article.getFileName());
if (result>0) {
Integer lastInertId = articleMapper.getLastInertId();
System.out.println("-- add operation --id:" + lastInertId);
}
returnresult; } /** ** if state is 0, no caching is performed * @param id article id * @return
*/
@Cacheable(key = "#id",unless = "#result.state==0") public Article getArticle(Integer ID) {try {thread.sleep (5000); } catch (InterruptedException e) { e.printStackTrace(); } final Article artcile = articleMapper.getArticleById(id); System.out.println("-- perform database query operation"+count.incrementAndGet()+"Time"+"id:"+id);
returnartcile; } /** * clear the cache with id as key ** @param id * @ with id update contentreturn
*/
@CacheEvict(key = "#id")
public Integer updateContentById(String contetnt, Integer id) {
Integer result = articleMapper.updateContentById(contetnt, id);
System.out.println("-- perform update operation id:--"+id);
returnresult; } /** ** removes articles by ID * @param id clears cache with id as key * @return
*/
@CacheEvict(key = "#id")
public Integer removeArticleById(Integer id){
final Integer result = articleMapper.removeArticleById(id);
System.out.println("Execute delete operation with id:"+id);
returnresult; }}Copy the code
Then we write the Controller layer, which is mainly to accept requests from clients. We configure @RestController to indicate that it is a REST style application. When receiving an ADD request, it will add a data, get request will query a data, RESH will update a data, and REM will delete a data
@RestController
@ComponentScan(basePackages = {"com.wyq.controller"."com.wyq.service"})
@MapperScan(basePackages = {"com.wyq.dao"})
public class ArticleController {
@Autowired
private ArticleService articleService;
@Autowired
ArticleMapper articleMapper;
@PostMapping("/add")
public ResultVo addArticle(@RequestBody Article article) {
System.out.println(article.toString());
Integer result = articleService.addArticle(article);
if (result >= 0) {
return ResultVo.success(result);
}
return ResultVo.fail();
}
@GetMapping("/get")
public ResultVo getArticle(@RequestParam("id") Integer id) {
Long start = System.currentTimeMillis();
Article article = articleService.getArticle(id);
Long end = System.currentTimeMillis();
System.out.println("Time:"+(end-start));
if(null ! = article)return ResultVo.success(article);
returnResultVo.fail(); } /** * update a post ** @param conteTNT * @param id * @return
*/
@GetMapping("/resh")
public ResultVo update(@RequestParam("content") String contetnt, @RequestParam("id") Integer id) {
final Integer result = articleService.updateContentById(contetnt, id);
if (result > 0) {
return ResultVo.success(result);
} else {
returnResultVo.fail(); }} /** * Delete an article ** @param id * @return
*/
@GetMapping("/rem")
public ResultVo remove(@RequestParam("id") Integer id) {
final Integer result = articleService.removeArticleById(id);
if (result > 0) {
return ResultVo.success(result);
} else {
returnResultVo.fail(); }}}Copy the code
Test case
Here we use Postman to simulate interface requests. First we add an article: request add interface:
Success is displayed in the background:
I see the background database has inserted data, its ID is 11
Execute the query operation, in the query operation, getArticle, I use the thread sleep way, simulated 5 seconds of time to deal with time-consuming business, the first request will certainly query the database, theoretically the second request, will go cache, let’s test: first execute the query operation
If the interface responds successfully, check the background output: It indicates that a query operation is performed, which takes 5078 seconds
Okay, so the point is, let’s ask the interface again and see what it returns, okay? In theory, it will not go to the database to execute the operation, and the time will be greatly reduced: compared with the above, this time no print to execute the database query operation, and the time is only 5ms, success! The cache came into play, reduced from 5078 seconds to 5 seconds! Greatly improved response speed, ha ha!
Update operation, when we make a change operation, we want the cache data to be empty: look at the interface return value success, then look at the database
Background console printing:
Id :--11Copy the code
To strike while the iron is hot, let’s ask the interface three more times to see what is returned. Each time, it returns this result, but my intuition is that the first time is the slowest, and the second and third times are fast
And what did you print in the background? Execute the database query with id 11. This is because the cache was cleared, so it goes to the database again (to get the latest data), and then all subsequent queries go to the cache! Obviously, the experiment was successful!
Delete operation. In the same way, in a delete operation, the cache will also be cleared, and the query will go through the database again.
Redis cache integration
SpringBoot is very easy to integrate with Redis (ok, SpringBoot is very easy to integrate everything), we just need to import spring-boot-starter-data-redis, and then configure the redis connection address in application.yml. We then use the RestTemplate to manipulate Redis, and here’s how redis handles the cache
- redisTemplate.opsForValue(); // Operation string
- redisTemplate.opsForHash(); / / the hash operation
- redisTemplate.opsForList(); / / the list to operate
- redisTemplate.opsForSet(); / / set operation
- redisTemplate.opsForZSet(); // set in order
- Configure caching and CacheManagerCustomizers
- Test using cache, switch cache, CompositeCacheManager
The next article
This blog has covered some of the ways to use caching in springBoot. How to use caching in development? It’s worth learning how to use it properly. This blog is just exploring Spring’s annotation cache, which is relatively simple. Hopefully, the next post will cover SpringBoot’s use of message queues.