Building a Spring Boot Web Application with MyBatis and Redis Caching
This tutorial demonstrates how to quickly build a modern Java web application using Spring Boot, integrate MyBatis as the ORM, configure Redis as a second‑level cache, and write unit tests with H2, providing step‑by‑step code and configuration details for backend developers.
Background
Spring Boot is a mainstream Java web framework that provides many ready‑to‑use starters. MyBatis is a lightweight ORM, and Redis is a popular distributed key‑value store often used for caching. This article shows how to combine them into a fast, modern web project.
Environment
OS: macOS 10.11
IDE: IntelliJ 2017.1
JDK: 1.8
Spring Boot: 1.5.3.RELEASE
Redis: 3.2.9
MySQL: 5.7
Project Initialization
Use IntelliJ's Spring Initializr to create a new project with the dependencies Web, MyBatis, Redis, MySQL, H2. The generated starter class looks like this:
@SpringBootApplication
public class SpringBootMybatisWithRedisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMybatisWithRedisApplication.class, args);
}
}Creating API Endpoints
Define a Product entity that implements Serializable and a ProductController with @RestController, @GetMapping and @PutMapping methods.
public class Product implements Serializable {
private static final long serialVersionUID = 1435515995276255188L;
private long id;
private String name;
private long price;
// getters & setters
}
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductMapper productMapper;
@GetMapping("/{id}")
public Product getProductInfo(@PathVariable("id") Long productId) {
return productMapper.select(productId);
}
@PutMapping("/{id}")
public Product updateProductInfo(@PathVariable("id") Long productId,
@RequestBody Product newProduct) {
// update logic
return null;
}
}Integrating MyBatis
Add the MyBatis starter in pom.xml. In application.yml configure the datasource and MyBatis settings:
spring:
datasource:
url: jdbc:mysql://{your_host}/{your_db}
username: {your_username}
password: {your_password}
driver-class-name: org.gjt.mm.mysql.Driver
mybatis:
type-aliases-package: com.wooyoo.learning.dao.domain
mapper-locations:
- mappers/ProductMapper.xmlCreate the mapper interface:
@Mapper
public interface ProductMapper {
Product select(@Param("id") long id);
void update(Product product);
}Enable the second‑level cache in ProductMapper.xml:
<mapper namespace="com.wooyoo.learning.dao.mapper.ProductMapper">
<!-- Enable Redis based second‑level cache -->
<cache type="com.wooyoo.learning.util.RedisCache"/>
<select id="select" resultType="Product">
SELECT * FROM products WHERE id = #{id} LIMIT 1
</select>
<update id="update" parameterType="Product" flushCache="true">
UPDATE products SET name = #{name}, price = #{price} WHERE id = #{id} LIMIT 1
</update>
</mapper>Integrating Redis as a Second‑Level Cache
Implement a custom cache class that uses Spring’s RedisTemplate:
public class RedisCache implements Cache {
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final String id;
private RedisTemplate redisTemplate;
private static final long EXPIRE_TIME_IN_MINUTES = 30;
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
}
@Override
public String getId() { return id; }
@Override
public void putObject(Object key, Object value) {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.opsForValue().set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
logger.debug("Put query result to redis");
}
@Override
public Object getObject(Object key) {
RedisTemplate redisTemplate = getRedisTemplate();
logger.debug("Get cached query result from redis");
return redisTemplate.opsForValue().get(key);
}
@Override
public Object removeObject(Object key) {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(key);
logger.debug("Remove cached query result from redis");
return null;
}
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.execute((RedisCallback) connection -> { connection.flushDb(); return null; });
logger.debug("Clear all the cached query result from redis");
}
@Override
public int getSize() { return 0; }
@Override
public ReadWriteLock getReadWriteLock() { return readWriteLock; }
private RedisTemplate getRedisTemplate() {
if (redisTemplate == null) {
redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
}Configure Redis connection in application.yml (host, port, database index, etc.). The cache is referenced in the mapper XML as shown above, and flushCache="true" on update statements ensures automatic invalidation.
Testing with H2
For unit tests, switch to an in‑memory H2 database by adding a separate profile section in application.yml:
---
spring:
profiles: test
datasource:
url: jdbc:h2:mem:test
username: root
password: 123456
driver-class-name: org.h2.Driver
schema: classpath:schema.sql
data: classpath:data.sqlWrite a test class using SpringBootTest and TestRestTemplate:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(profiles = "test")
public class SpringBootMybatisWithRedisApplicationTests {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void test() {
long productId = 1L;
Product product = restTemplate.getForObject("http://localhost:" + port + "/product/" + productId, Product.class);
assertThat(product.getPrice()).isEqualTo(200);
Product newProduct = new Product();
long newPrice = new Random().nextLong();
newProduct.setName("new name");
newProduct.setPrice(newPrice);
restTemplate.put("http://localhost:" + port + "/product/" + productId, newProduct);
Product testProduct = restTemplate.getForObject("http://localhost:" + port + "/product/" + productId, Product.class);
assertThat(testProduct.getPrice()).isEqualTo(newPrice);
}
}The test verifies that the GET request caches the product in Redis, the PUT request invalidates the cache, and a subsequent GET retrieves the updated data.
Conclusion
The article walks through building a Spring Boot web project with MyBatis, adding Redis as a second‑level cache, and testing the whole flow with H2. It highlights the convenience of Spring Boot’s auto‑configuration, the importance of proper cache invalidation, and the value of unit testing for reliable code.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
