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.

Java Captain
Java Captain
Java Captain
Building a Spring Boot Web Application with MyBatis and Redis Caching

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.xml

Create 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.sql

Write 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentredisSpring BootMyBatis
Java Captain
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.