1. 程式人生 > >Spring快取機制和Redis的結合

Spring快取機制和Redis的結合

  • 這裡使用reids作為快取。
  • 使用Mybatis來操作資料庫。
  • 並使用Spring的@Cacheable,@CachePut,@CacheEvict註解來操作redis快取。

1 準備環境

包結構
在這裡插入圖片描述

1 Spring配置 RootConfig.java

@Configuration
@ComponentScan(basePackages="cn.wu")
@EnableTransactionManagement
public class RootConfig {
	
	// 配置資料來源
	@Bean
	public DataSource initDataSource() {
		DataSource dataSource = null;
		Properties properties =  new Properties();
		properties.setProperty("driverClassName", "com.mysql.jdbc.Driver");
		properties.setProperty("url", "jdbc:mysql://localhost:3306/ssm?useSSL=false");
		properties.setProperty("username", "root");
		properties.setProperty("password", "資料庫連線密碼");
		try {
			dataSource = BasicDataSourceFactory.createDataSource(properties);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return dataSource;
	}
	
	// 配置mybatis的SqlSession的工廠
	@Bean(name="sqlSessionFactory")
	public SqlSessionFactoryBean initSqlSessionFactoryBean() {
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		sqlSessionFactoryBean.setDataSource(initDataSource()); // 設定dataSource
		Resource resource = new ClassPathResource("mybatis-config.xml"); 
		sqlSessionFactoryBean.setConfigLocation(resource); // 設定mybatis配置檔案
		return sqlSessionFactoryBean;
	}
	
	// 配置mybatis對映器的掃描器
	@Bean
	public MapperScannerConfigurer initMapperScannerConfigurer() {
		MapperScannerConfigurer mapperScannerConfigurer  = new MapperScannerConfigurer();
		mapperScannerConfigurer.setBasePackage("cn.wu.mapper");
		mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
		mapperScannerConfigurer.setAnnotationClass(Repository.class);
		return mapperScannerConfigurer;
	}
	
	// 配置事務管理器
	@Bean
	public PlatformTransactionManager initTransactionManager() {
		DataSourceTransactionManager transactionManage = new DataSourceTransactionManager();
		transactionManage.setDataSource(initDataSource());
		return transactionManage;
	}

}

mybatis-config.xml程式碼:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration  
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
 <configuration>
 	
 </configuration>

2 使用Redis作為快取的相關配置RedisConfig.java

@Configuration
@EnableCaching // 讓Spring IOC容器啟動快取機制
public class RedisConfig {

	@Bean
	public RedisConnectionFactory initRedisConnectionFactory() {
		JedisPoolConfig poolConfig = new JedisPoolConfig();
		poolConfig.setMaxIdle(50);
		poolConfig.setMaxTotal(100);
		poolConfig.setMaxWaitMillis(20000);

		JedisConnectionFactory connectionFactory = new JedisConnectionFactory(poolConfig);
		connectionFactory.setHostName("localhost");
		connectionFactory.setPort(6379);
		return connectionFactory;
	}

	@Bean
	public RedisTemplate initRedisTemplate() {
		RedisTemplate redisTemplate = new RedisTemplate();
		redisTemplate.setConnectionFactory(initRedisConnectionFactory());

		StringRedisSerializer stringSerializer = new StringRedisSerializer();
		JdkSerializationRedisSerializer jdkSSerializer = new JdkSerializationRedisSerializer();

		redisTemplate.setKeySerializer(stringSerializer);
		redisTemplate.setValueSerializer(jdkSSerializer);
		redisTemplate.setDefaultSerializer(stringSerializer);
		redisTemplate.setHashKeySerializer(stringSerializer);
		redisTemplate.setHashValueSerializer(jdkSSerializer);

		return redisTemplate;
	}

	/*
	 * 配置Spring快取管理器
	 * 這裡的快取管理器是RedisCacheManager。
	 * RedisCacheManager實現了Spring的CacheManager介面
	 */
	@Bean(name = "redisCacheManager")
	public CacheManager initRedisCacheManager(@Autowired RedisTemplate redisTemplate) {
		RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
		// 設定鍵的超時時間為10分鐘
		redisCacheManager.setDefaultExpiration(600L);
		// 設定快取名稱
		List<String> cacheNames = new ArrayList<>();
		cacheNames.add("redisCacheManager");
		redisCacheManager.setCacheNames(cacheNames);
		return redisCacheManager;
	}

}

2 Mybatis對映器程式碼與配置

1 Role.java

public class Role implements Serializable{
	
	private static final long serialVersionUID = -4295925571801283375L;
	private Long id;
	private String roleName;
	private String note;
    // getter setter
}

2 RoleMapper.java

@Repository
public interface RoleMapper {
	Role getRole(Long id);
	
	int deleteRole(Long id);
	
	int insertRole(Role role);
	
	int updateRole(Role role);
	
	List<Role> findRoles(@Param("roleName")String roleName, @Param("note")String note);
}

3 RoleMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper  
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.wu.mapper.RoleMapper">
	<select id="getRole" parameterType="long"
		resultType="cn.wu.domain.Role">
		select id, role_name as roleName, note from t_role where id
		= #{id}
	</select>

	<delete id="deleteRole" parameterType="long">
		delete from t_role where
		id = #{id}
	</delete>

	<insert id="insertRole" parameterType="cn.wu.domain.Role"
		useGeneratedKeys="true" keyProperty="id">
		insert into t_role(role_name,
		note) values(#{roleName}, #{note})
	</insert>



	<update id="updateRole" parameterType="cn.wu.domain.Role">
		update t_role set
		role_name = #{roleName}, note = #{note} where id = #{id}
	</update>

	<select id="findRoles" resultType="cn.wu.domain.Role">
		select id, role_name as roleName, note from t_role
		<where>
			<if test="roleName != null">
				role_name like concat('%', #{roleName}, '%')
			</if>

			<if test="note != null">
				note like concat('%', #{note}, '%')
			</if>
		</where>
	</select>
</mapper>

3 使用對映器的Service程式碼

RoleService.java

public interface RoleService {
	Role getRole(Long id);

	int deleteRole(Long id);

	Role insertRole(Role role);

	Role updateRole(Role role);

	List<Role> findRoles(String roleName, String note);
}

RoleServiceImpl.java

@Service
public class RoleServiceImpl implements RoleService {
	@Autowired
	private RoleMapper roleMapper;
	
	/**
	 * value = "redisCacheManager"表示使用的快取名稱(這裡使用的是RedisConfig中配置的快取管理器名稱)
	 * key = "'redis_role_'+#id" 表示快取到redis時的key
	 * 
	 * @Cacheable 表示在執行getRole()程式碼前,先到redis查詢對應key的快取,
	 * 若存在快取則直接返回快取;
	 * 若快取不存在,則執行資料庫查詢程式碼,然後將結果快取到redis,其key為"'redis_role_'+#id"。
	 * 所以@Cacheable 適合放在查詢命中率高的方法上
	 */
	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
	@Cacheable(value = "redisCacheManager", key = "'redis_role_'+#id")
	public Role getRole(Long id) {
		return roleMapper.getRole(id);
	}
	
	/**
	 * @CachePut , 不管有沒有快取,insertRole()程式碼都會去執行,然後將返回的結果快取到redis中。
	 * 所以@CachePut 適合放在插入和更新資料庫的方法上。
	 */
	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
	@CachePut(value="redisCacheManager",key="'redis_role_'+#role.id")
	public Role insertRole(Role role) {
		roleMapper.insertRole(role);
		return role;
	}

	/**
	 * @CacheEvict 表示刪除redis中的快取,這裡刪除的時key="'redis_role_'+#id"的快取
	 */
	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
	@CacheEvict(value="redisCacheManager", key="'redis_role_'+#id")
	public int deleteRole(Long id) {
		return roleMapper.deleteRole(id);
	}

	@Override
	@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
	@CachePut(value="redisCacheManager", key="'redis_role_'+#role.id")
	public Role updateRole(Role role) {
		roleMapper.updateRole(role);
		return role;
	}

	@Override
	public List<Role> findRoles(String roleName, String note) {
		return roleMapper.findRoles(roleName, note);
	}

}

4 測試

public class Main {
	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(RootConfig.class, RedisConfig.class);
		RoleService roleService = context.getBean(RoleService.class);
		Role role = new Role();
		role.setRoleName("李華");
		role.setNote("好人");
		roleService.insertRole(role); // reids中快取了key=redis_role_67,值為role物件
		System.out.println(role); // Role [id=67, roleName=李華, note=好人]
		Role role2 = roleService.getRole(role.getId()); // 沒有執行資料庫查詢語句,而是直接返回了redis中的快取
		role2.setNote("備註好人");
		roleService.updateRole(role2);// 更新了資料庫中的記錄和reids中的快取
	}
}