Using Redis Caching in MyBatis Operations with Spring Boot

In the previous article, we explored Spring Boot integration with Redis. This article focuses on implementing Redis caching in MyBatis operations. We'll examine four key annotations: @CachePut, @Cacheable, @CacheEvict, and @CacheConfig.

Fundamentals

@Cacheable

The @Cacheable annotation configures method-level caching, storing results based on method parameters.

Parameter Description Example
value Cache name defined in configuration, at least one required @Cacheable(value="mycache") @Cacheable(value={"cache1","cache2"})
key Cache key, uses SpEL expressions. If omitted, defaults to all method parameters @Cacheable(value="testcache",key="#username")
condition SpEL expression returning true/false. Only caches when true @Cacheable(value="testcache",condition="#username.langth()>2")

@CachePut

@CachePut also provides method-level caching, but unlike @Cacheable, it always executes the method and caches the return value. A common pitfall occurs when applying this annotation at the Mapper layer with void return types. Since @CachePut stores the method's return value, not null, attempting to deserialize stored values as domain objects will fail.

@CachePut(cacheNames="account",key = "#p0.accountId")
@Update("UPDATE account SET name=#{name},balance=#{balance} WHERE id=#{id}")
Account updateAccount(Account account);

</div>| Parameter | Description | Example |
|---|---|---|
| value | Cache name defined in configuration, at least one required | @CachePut(value="mycache") |
| key | Cache key using SpEL. Defaults to all method parameters if unspecified | @CachePut(value="testcache",key="#username") |
| condition | SpEL expression for conditional caching | @CachePut(value="testcache",condition="#username.length()&gt;2") |

### @CacheEvict

@CacheEvict clears cached entries based on specified conditions.

| Parameter | Description | Example |
|---|---|---|
| value | Cache name, at least one required | @CacheEvict(value="mycache") |
| key | Cache key to evict | @CacheEvict(value="testcache",key="#username") |
| condition | SpEL expression for conditional eviction | @CacheEvict(value="testcache",condition="#username.length()&gt;2") |
| allEntries | If true, clears entire cache after method execution. Default: false | @CacheEvict(value="testcache",allEntries=trrue) |
| beforeInvocation | If true, evicts before method execution. Default: false (exception prevents eviction) | @CacheEvict(value="testcache",beforeInvocation=true) |

### @CacheConfig

When multiple methods share the same cache name with @Cacheable annotations, @CacheConfig provides class-level cache configuration to avoid repetition. Method-level cache names override class-level settings.

Implemantation Example
----------------------

The following example builds upon a previous Spring Boot demo project. Since MyBatis Mapper methods typically return void for updates, a service layer is introduced to handle caching properly.

### Service Layer

<div>```
package com.example.services;

import java.util.List;
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.stereotype.Service;
import com.example.model.User;
import com.example.write.mapper.WriteUserMapper;

@Service
@CacheConfig(cacheNames="user")
public class UserServices {
    
    @Autowired 
    private WriteUserMapper writeUserMapper;

    public List<User> getAll() {
        return writeUserMapper.getAll();
    }
    
    @Cacheable(key = "#p0")
    public User getOne(String id) {
        return writeUserMapper.getOne(id);
    }
    
    public void insert(User user) {
        writeUserMapper.insert(user);
    }
    
    @CachePut(value="user", key = "#p0.id")
    public User update(User user) {
        writeUserMapper.update(user);
        return user;
    }
    
    @CacheEvict(value="user", key ="#p0", allEntries=true)
    public void delete(String id) {
        writeUserMapper.delete(id);
    }
}

import java.io.Serializable; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import com.example.model.User; import com.example.model.UserSexEnum; import com.example.read.mapper.ReadUserMapper; import com.example.services.UserServices; import com.example.write.mapper.WriteUserMapper;

@Controller @RequestMapping("/user") public class UserController {

@Autowired
private WriteUserMapper userMapperWrite;

@Autowired
private ReadUserMapper userMapperRead;

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Autowired
private RedisTemplate<String, Serializable> redisCacheTemplate;

@Autowired
private UserServices userServices;

@RequestMapping(value = "/alluser.do", method = RequestMethod.GET)
public String getAllUsers(Model model) {
   List<User> users = userServices.getAll();
   model.addAttribute("users", users);
   return "userlist";
}

@RequestMapping(value = "/insert.do", method = RequestMethod.GET)
public String addUser(Model model) {
   User user = new User();
   user.setName("john");
   user.setAge(30);    
   userServices.insert(user);
   return "forward:/user/alluser.do";  
}

@RequestMapping(value = "/getuserbyid.do/{id}", method = RequestMethod.GET)
public ModelAndView getUserById(@PathVariable("id") String id) {
   System.out.println(id);
   User user = userServices.getOne(id);
   System.out.println(user.toString());
   ModelAndView modelAndView = new ModelAndView("userlist"); 
   modelAndView.addObject("user", user);
   return modelAndView;  
}

@RequestMapping(value = "/deleteuserbyid.do/{id}", method = RequestMethod.GET)
public String deleteUserById(@PathVariable("id") String id) {
    userServices.delete(id);
    return "forward:/user/alluser.do";  
}

@RequestMapping(value = "/updateuserbyid.do/{id}", method = RequestMethod.GET)
public String updateUserById(@PathVariable("id") String id) {
    User user = userServices.getOne(id);
    System.out.println(user.toString());
    user.setAge(31);
    System.out.println(user.toString());
    userServices.update(user);
    System.out.println(user.toString());
    return "forward:/user/alluser.do";  
}

}


</div>### Testing the Implementation

First, access http://localhost:8080/user/getuserbyid.do/17 to cache a user via the getOne() method. Use redis-cli to verify that user::17 exists in Redis.

Next, update the user through http://localhost:8080/user/updateuserbyid.do/17. The age changes to 31 and the database updates accordingly. Multiple refreshes of this URL will execute successfully because @CachePut stores the returned User object. This differs from applying @CachePut directly on a Mapper with void return, which would store null and cause deserialization errors.

Finally, delete the user via http://localhost:8080/user/deleteuserbyid.do/17, which removes the entry from both Redis and the database.

### Component Scanning Configuration

Ensure the service package is included in component scanning by adding @ComponentScan with the appropriate base packages in your main application class:

<div>```
@ComponentScan(basePackages={"com.example.config","com.example.demo","com.example.services"})

Tags: spring-boot Redis MyBatis Caching cacheable

Posted on Wed, 20 May 2026 07:19:39 +0000 by santhosh_89