Spring + MyBatis 是經典持久層組合,加上 Spring Cache 抽象與 Redis 可快速構建高效能讀取路徑。本文示範基於註解(@Cacheable、@CacheEvict)的整合完整配置。
<dependencies> <!-- Spring Context, TX, JDBC --> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.x</version></dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.3.x</version></dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.x</version></dependency> <!-- MyBatis-Spring --> <dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.x</version></dependency> <!-- Redis --> <dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.7.x</version></dependency> <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.3.x</version></dependency> <!-- JSON 序列化 --> <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.x</version></dependency> </dependencies>
@Data @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class UserVO implements Serializable {
private int id; private String username, password; private int age;
}
public interface UserDao {
void addUser(UserVO user);
void deleteUser(UserVO user);
void updateUser(UserVO user);
UserVO getUserById(int id);
UserVO getUser(int id);
}
<mapper namespace="com.sl.user.dao.UserDao">
<resultMap id="userResult" type="UserVO">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="age" property="age"/>
</resultMap>
<insert id="addUser" parameterType="UserVO">INSERT INTO t_user(username,password,age) VALUES(#{username},#{password},#{age})</insert>
<update id="deleteUser" parameterType="UserVO">DELETE FROM t_user WHERE id=#{id}</update>
<update id="updateUser" parameterType="UserVO">
UPDATE t_user SET <if test="username!=null">username=#{username},</if>... WHERE id=#{id}
</update>
<select id="getUser" parameterType="int" resultMap="userResult">SELECT * FROM t_user WHERE id=#{id}</select>
<select id="getUserById" parameterType="int" resultType="String">SELECT username FROM t_user WHERE id=#{id}</select>
</mapper>
@Service @Transactional
public class UserServiceImpl implements UserService {
@Autowired UserDao userDao;
@CacheEvict(value="User", allEntries=true)
public void addUser(UserVO user) { userDao.addUser(user); }
@CacheEvict(value={"getUser", "getUserById"}, allEntries=true)
public void deleteUser(UserVO user) { userDao.deleteUser(user); }
@CacheEvict(value={"getUser", "getUserById"}, allEntries=true)
public void updateUser(UserVO user) { userDao.updateUser(user); }
@Cacheable(value="User", key="#id")
public UserVO getUserById(int id) { return userDao.getUserById(id); }
@Cacheable(value="User", key="'getUser_' + #id")
public UserVO getUser(int id) { return userDao.getUser(id); }
}
關鍵點:
value 對應快取名稱(Redis 中的 Key 前綴)。key 支援 SpEL,#id 表示方法參數,'getUser_' + #id 組合字串。@CacheEvict 清除相關快取,allEntries=true 清空整個快取區域(簡單但粗暴),生產建議精確 Key 清除。@Configuration @EnableCaching
public class RedisConfig {
@Bean public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory f = new JedisConnectionFactory();
f.setHostName("127.0.0.1"); f.setPort(6379);
f.setPoolConfig(new JedisPoolConfig()); return f;
}
@Bean public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory cf) {
RedisTemplate<String, Object> t = new RedisTemplate<>();
t.setConnectionFactory(cf);
t.setKeySerializer(new StringRedisSerializer());
t.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // JSON 序列化
return t;
}
@Bean public CacheManager cacheManager(RedisTemplate<String, Object> rt) {
RedisCacheManager.Builder builder = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(rt.getConnectionFactory());
return builder.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24))).build();
}
}
@Configuration @MapperScan("com.sl.user.dao")
public class MyBatisConfig {
@Bean public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test"); ds.setUsername("root"); ds.setPassword("root");
return ds;
}
@Bean public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(ds);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return bean.getObject();
}
@Bean public DataSourceTransactionManager txManager(DataSource ds) {
return new DataSourceTransactionManager(ds);
}
}
@RestController @RequestMapping("/user")
public class UserCtrl {
@Autowired UserService userService;
@PostMapping("/add") public void add(@RequestBody UserVO u) { userService.addUser(u); }
@GetMapping("/{id}") public UserVO get(@PathVariable int id) { return userService.getUser(id); }
}
GET /user/1 → 控制台印出 SQL 查詢。GET /user/1 → 無 SQL,直接從 Redis 返回(可用 Redis CLI KEYS User:* 查看)。Spring Cache 抽象讓快取邏輯與業務解耦,配合 Redis 高效能分散式快取,能大幅降低資料庫壓力。生產環境建議:
allEntries=true 全量清除。GenericJackson2JsonRedisSerializer)提升可讀性與跨語言相容性。