Spring(五)Spring緩存機制與Redis的結合
一、Redis和數據庫的結合
使用Redis可以優化性能,但是存在Redis的數據和數據庫同步的問題。
例如,T1時刻以將 key1 保存數據到 Redis,T2時刻刷新進入數據庫,但是T3時刻發生了其他業務需要改變數據庫同一條記錄的數據,但是采用了 key2 保存到Redis中,然後又寫入了更新數據到數據庫中,這就導致 Redis 中key1 的數據是臟數據,和數據庫中的數據不一致。
1.Redis和數據庫讀操作
數據緩存往往會在 Redis 上設置超時時間,當設置 Redis 的數據超時後,Redis 就沒法讀出數據了,這個時候就會觸發程序讀取數據庫,然後將讀取數據庫數據寫入 Redis,並給數據重設超時時間,這樣程序在讀取的過程中就能按一定的時間間隔刷新數據了。
public DataObject readMethod(args) { DataObject data = getRedis(key); if(data != null){ data = getFromDataBase(); writeRedis(key, data); setRedisExpire(key, 5); } return data; }
2. Redis 和數據庫寫操作
寫操作要考慮數據一致的問題,尤其是那些重要的業務數據,所以首先應該考慮從數據庫中讀取最新的數據,然後對數據進行操作,最後把數據寫入 Redis 緩存中。
寫入業務數據時,應該先從數據庫中讀取最新數據,然後進行業務操作,更新業務數據到數據庫後,再將數據刷新到 Redis 緩存中,這樣就能避免將臟數據寫入數據庫中。
public DataObject writeMethod(args) { DataObject data = getFromDataBase(args); ExecLogic(data); updateDataBase(data); updateRedisData(data); }
二、使用Spring緩存機制整合Redis
1.定義一個POJO類和Mybatis
package com.ssm.chapter21.pojo; import java.io.Serializable; public class Role implements Serializable { private static final long serialVersionUID = -1194462093889377366L; private Long id; private String roleName; private String note; /**** setter and getter ****/ }POJO
<?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> <mappers> <mapper resource="com/ssm/chapter21/mapper/RoleMapper.xml"/> </mappers> </configuration>Mybatis-config.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="com.ssm.chapter21.dao.RoleDao"> <select id="getRole" resultType="com.ssm.chapter21.pojo.Role"> select id, role_name as roleName, note from t_role where id = #{id} </select> <delete id="deleteRole"> delete from t_role where id=#{id} </delete> <insert id="insertRole" parameterType="com.ssm.chapter21.pojo.Role" useGeneratedKeys="true" keyProperty="id"> insert into t_role (role_name, note) values(#{roleName}, #{note}) </insert> <update id="updateRole" parameterType="com.ssm.chapter21.pojo.Role"> update t_role set role_name = #{roleName}, note = #{note} where id = #{id} </update> <select id="findRoles" resultType="com.ssm.chapter21.pojo.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>RoleMapper.xml
package com.ssm.chapter21.dao; import java.util.List; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import com.ssm.chapter21.pojo.Role; /**** imports ****/ @Repository public interface RoleDao { public Role getRole(Long id); public int deleteRole(Long id); public int insertRole(Role role); public int updateRole(Role role); public List<Role> findRoles(@Param("roleName") String roleName, @Param("note") String note); }RoleDao.java
package com.ssm.chapter21.service; import java.util.List; import com.ssm.chapter21.pojo.Role; public interface RoleService { public Role getRole(Long id); public int deleteRole(Long id); public Role insertRole(Role role); public int updateRole(Role role); public List<Role> findRoles(String roleName, String note); public int insertRoles(List<Role> roleList); }RoleService
2.通過Java配置Spring
RootConfig.java的定義,其中包含了4個部分
package com.ssm.chapter21.config;/**** imports ****/ @Configuration // 定義Spring掃描的包 @ComponentScan("com.*") // 使用事務驅動管理器 @EnableTransactionManagement // 實現接口TransactionManagementConfigurer,這樣可以配置註解驅動事務 public class RootConfig implements TransactionManagementConfigurer {
private DataSource dataSource = null;
...
}
(1)配置數據庫
/** * 配置數據庫 * * @return 數據連接池 */ @Bean(name = "dataSource") public DataSource initDataSource() { if (dataSource != null) { return dataSource; } Properties props = new Properties(); props.setProperty("driverClassName", "com.mysql.jdbc.Driver"); props.setProperty("url", "jdbc:mysql://localhost:3306/chapter6?useSSL=false"); props.setProperty("username", "root"); props.setProperty("password", "bjtungirc"); try { dataSource = BasicDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } return dataSource; }
(2)配置SqlSessionFactoryBean
/** * * 配置SqlSessionFactoryBean * * @return SqlSessionFactoryBean */ @Bean(name = "sqlSessionFactory") public SqlSessionFactoryBean initSqlSessionFactory() { SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); sqlSessionFactory.setDataSource(initDataSource()); // 加載Mybatis配置文件 Resource resource = new ClassPathResource("mybatis/mybatis-config.xml"); sqlSessionFactory.setConfigLocation(resource); return sqlSessionFactory; }
(3)配置Mybatis Mapper
/** * * 通過自動掃描,發現Mybatis Mapper映射器 * * @return Mapper映射器 */ @Bean public MapperScannerConfigurer initMapperScannerConfigurer() { MapperScannerConfigurer msc = new MapperScannerConfigurer(); // 定義掃描包 msc.setBasePackage("com.*"); msc.setSqlSessionFactoryBeanName("sqlSessionFactory"); // 區分註解掃描 msc.setAnnotationClass(Repository.class); return msc; }
(4)配置註解驅動,使得@Transactional可以觸發事務
/** * 實現接口方法,註冊註解事務,當@Transactional使用的時候產生數據庫事務 */ @Override @Bean(name = "annotationDrivenTransactionManager") public PlatformTransactionManager annotationDrivenTransactionManager() { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(initDataSource()); return transactionManager; }
3.通過Java配置RedisTemplate和Redis緩存管理器
RedisConfig.java,其中包含兩個部分,RedisTemplate和Redis緩存管理器
其中@EnableCaching 表示Spring IoC 容器啟動了緩存機制。
package com.ssm.chapter21.config;/**** imports ****/ @Configuration @EnableCaching public class RedisConfig {...}
(1)RedisTemplate配置
@Bean(name = "redisTemplate") public RedisTemplate initRedisTemplate() { JedisPoolConfig poolConfig = new JedisPoolConfig(); // 最大空閑數 poolConfig.setMaxIdle(50); // 最大連接數 poolConfig.setMaxTotal(100); // 最大等待毫秒數 poolConfig.setMaxWaitMillis(20000); // 創建 Jedis 連接工廠 JedisConnectionFactory connectionFactory = new JedisConnectionFactory(poolConfig); connectionFactory.setHostName("localhost"); connectionFactory.setPort(6379); // 調用後初始化方法,沒有它將拋出異常 connectionFactory.afterPropertiesSet(); // 自定義兩個Redis序列化器 RedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(); RedisSerializer stringRedisSerializer = new StringRedisSerializer(); // 定義RedisTemplate對象,並設置連接工程 RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(connectionFactory); // 設置序列化器 redisTemplate.setDefaultSerializer(stringRedisSerializer); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(jdkSerializationRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setHashValueSerializer(jdkSerializationRedisSerializer); return redisTemplate; }
(2)配置Redis緩存管理器
定義默認超時時間為10分鐘,這樣就可以在一定的時間間隔後重新從數據庫中讀取數據了。另外redisCacheManager名稱在之後的業務方法中也會用到。
@Bean(name = "redisCacheManager") public CacheManager initRedisCacheManager(@Autowired RedisTemplate redisTempate) { RedisCacheManager cacheManager = new RedisCacheManager(redisTempate); // 設置默認超時時間,為10分鐘 cacheManager.setDefaultExpiration(600); // 設置緩存管理器名稱 List<String> cacheNames = new ArrayList<String>(); cacheNames.add("redisCacheManager"); cacheManager.setCacheNames(cacheNames); return cacheManager; }
4.緩存註解說明
- @Cacheable:表明在進入方法之前,Spring會先去緩存服務器中查找對應key的緩存值,如果找打緩存值,那麽Spring將不會再調用方法,而是將緩存值讀出,返回給調用者;如果沒有找到緩存值,那麽Spring就會執行自定義的方法,將最後的結果通過key保存到緩存服務器中
- @CachaPut:Spring 會將該方法返回的值緩存到緩存服務器中,Spring不會事先去緩存服務器中查找,而是直接執行方法,然後緩存。就該方法始終會被Spring所調用
- @CacheEvict:移除緩存對應的key的值
- @Caching:分組註解,能夠同時應用於其他緩存的註解
上面的註解都能標註到類或者方法上,如果放到類上,則對所有的方法都有效;如果放在方法上,則只是對方法有效。在大部分情況下,會放置到方法上。
一般而言,對於查詢,可以使用@Cacheable;對於插入和修改,可以使用@CachePut;對於刪除操作,可以使用@CacheEvict
@Cacheable和@CachaPut的配置屬性為:
- value(String[]):使用緩存管理器的名稱
- condition(String):Spring表達式,如果返回值為false,則不會將緩存應用到方法上
- key(String):Spring表達式,通過它來計算對應緩存的key
- unless(String):Spring表達式,如果表達式的返回值為true,則不會將方法的結果放到緩存上
RoleService接口的實現類中的方法的定義為:
package com.ssm.chapter21.service.impl;/**** imports ****/ @Service public class RoleServiceImpl implements RoleService { // 角色DAO,方便執行SQL @Autowired private RoleDao roleDao = null;
...
}
(1)使用@Cacheable註解的getRole方法
在Spring的調用中,會先查詢Redis中看是否存在key為redis_role_id的鍵值對,如果有,就返回結果。如果沒有,就訪問getRole方法,從數據庫中查詢到數據,返回給調用者,然後將鍵值對redis_role_id---roleDao.getRole(id)保存到Redis中。
/** * 使用@Cacheable定義緩存策略 當緩存中有值,則返回緩存數據,否則訪問方法得到數據 通過value引用緩存管理器,通過key定義鍵 * @param id 角色編號 * @return 角色對象 */ @Override @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) @Cacheable(value = "redisCacheManager", key = "‘redis_role_‘+#id") public Role getRole(Long id) { return roleDao.getRole(id); }
(2)使用@CachePut註解的insertRole方法和updateRole方法
由於需要先執行insertRole把對應的信息更新到數據庫,然後才能刷新Redis。因此,Spring會先執行roleDao.insertRole(role);,然後根據return得到的role,將redis_role_role.id---role保存到Redis中。而updateRole方法也是同理,先執行updateRole方法更新對象,然後將redis_role_role.id---role保存到Redis中。保存到Redis中的過程都遵循redisCacheManager緩存管理器定義的過程。
/** * 使用@CachePut則表示無論如何都會執行方法,最後將方法的返回值再保存到緩存中 * 使用在插入數據的地方,則表示保存到數據庫後,會同期插入到Redis緩存中 * * @param role 角色對象 * @return 角色對象(會回填主鍵) */ @Override @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) @CachePut(value = "redisCacheManager", key = "‘redis_role_‘+#result.id") public Role insertRole(Role role) { roleDao.insertRole(role); return role; } /** * 使用@CachePut,表示更新數據庫數據的同時,也會同步更新緩存 * * @param role 角色對象 * @return 影響條數 */ @Override @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) @CachePut(value = "redisCacheManager", key = "‘redis_role_‘+#role.id") public int updateRole(Role role) { return roleDao.updateRole(role); }
(3)使用@CacheEvict註解的deleteRole方法在方法,可以執行完成後會移除對應的緩存,
/** * 使用@CacheEvict刪除緩存對應的key * * @param id 角色編號 * @return 返回刪除記錄數 */ @Override @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) @CacheEvict(value = "redisCacheManager", key = "‘redis_role_‘+#id") public int deleteRole(Long id) { return roleDao.deleteRole(id); }
(4)測試@CachePut註解、@Cacheable和@CacheEvict註解:
package com.ssm.chapter21.main; public class Chapter21Main { public static void main(String[] args) { //使用註解Spring IoC容器 ApplicationContext ctx = new AnnotationConfigApplicationContext(RootConfig.class, RedisConfig.class); //獲取角色服務類 RoleService roleService = ctx.getBean(RoleService.class); Role role = new Role(); role.setRoleName("role_name_1"); role.setNote("role_note_1"); //插入角色 roleService.insertRole(role); //獲取角色 Role getRole = roleService.getRole(role.getId()); getRole.setNote("role_note_1_update"); //更新角色 roleService.updateRole(getRole); //刪除角色 roleService.deleteRole(getRole.getId()); } }
輸出日誌:
在第二部分getRole部分可以看到,只出現了兩次Opening RedisConnection和Closing Redis Connection而沒有出現任何SQL執行,因為在Redis中已經先查找到了對應的數據。
Creating new transaction with name [com.ssm.chapter21.service.impl.RoleServiceImpl.insertRole]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; ‘‘
Acquired Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] for JDBC transaction
Changing isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to 2
Switching JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to manual commitCreating a new SqlSession Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@71e2843b] JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] will be managed by Spring ==> Preparing: insert into t_role (role_name, note) values(?, ?) ==> Parameters: role_name_1(String), role_note_1(String) <== Updates: 1Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@71e2843b] Opening RedisConnection Closing Redis Connection Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@71e2843b] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@71e2843b] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@71e2843b] Initiating transaction commit Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to 4 Releasing JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] after transaction Returning JDBC Connection to DataSource
Adding transactional method ‘RoleServiceImpl.getRole‘ with attribute: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; ‘‘Adding cacheable method ‘getRole‘ with attribute: [Builder[public com.ssm.chapter21.pojo.Role com.ssm.chapter21.service.impl.RoleServiceImpl.getRole(java.lang.Long)] caches=[redisCacheManager] | key=‘‘redis_role_‘+#id‘ | keyGenerator=‘‘ | cacheManager=‘‘ | cacheResolver=‘‘ | condition=‘‘ | unless=‘‘ | sync=‘false‘] Creating new transaction with name [com.ssm.chapter21.service.impl.RoleServiceImpl.getRole]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; ‘‘Acquired Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] for JDBC transaction Changing isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to 2 Switching JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to manual commit Opening RedisConnection Closing Redis Connection Opening RedisConnection Closing Redis Connection Initiating transaction commit Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to 4 Releasing JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] after transaction Returning JDBC Connection to DataSource
Adding transactional method ‘RoleServiceImpl.updateRole‘ with attribute: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; ‘‘Adding cacheable method ‘updateRole‘ with attribute: [Builder[public int com.ssm.chapter21.service.impl.RoleServiceImpl.updateRole(com.ssm.chapter21.pojo.Role)] caches=[redisCacheManager] | key=‘‘redis_role_‘+#role.id‘ | keyGenerator=‘‘ | cacheManager=‘‘ | cacheResolver=‘‘ | condition=‘‘ | unless=‘‘] Creating new transaction with name [com.ssm.chapter21.service.impl.RoleServiceImpl.updateRole]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; ‘‘Acquired Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] for JDBC transaction DChanging isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to 2 Switching JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to manual commitCreating a new SqlSession Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6636448b] JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] will be managed by Spring ==> Preparing: update t_role set role_name = ?, note = ? where id = ? ==> Parameters: role_name_1(String), role_note_1_update(String), 7(Long) <== Updates: 1Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6636448b] Opening RedisConnection Closing Redis Connection Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6636448b] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6636448b] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6636448b] Initiating transaction commit Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to 4 Releasing JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] after transaction Returning JDBC Connection to DataSource
Adding transactional method ‘RoleServiceImpl.deleteRole‘ with attribute: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; ‘‘Adding cacheable method ‘deleteRole‘ with attribute: [Builder[public int com.ssm.chapter21.service.impl.RoleServiceImpl.deleteRole(java.lang.Long)] caches=[redisCacheManager] | key=‘‘redis_role_‘+#id‘ | keyGenerator=‘‘ | cacheManager=‘‘ | cacheResolver=‘‘ | condition=‘‘,false,false] Creating new transaction with name [com.ssm.chapter21.service.impl.RoleServiceImpl.deleteRole]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; ‘‘Acquired Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] for JDBC transaction Changing isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to 2 Switching JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to manual commitCreating a new SqlSession Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c681761] JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] will be managed by Spring ==> Preparing: delete from t_role where id=?==> Parameters: 7(Long) <== Updates: 1Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c681761] Opening RedisConnection Closing Redis Connection Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c681761] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c681761] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c681761] Initiating transaction commit Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] to 4 Releasing JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, UserName=root@, MySQL-AB JDBC Driver] after transaction Returning JDBC Connection to DataSource
(4)findRoles方法
使用緩存的前提是----高命中率。由於這裏根據角色名稱和備註查找角色信息,該方法的返回值會根據查詢條件而多樣化,導致其不確定和命中率低下,這種情況下使用緩存並不能有效提高性能,所以findRoles方法就不必使用緩存註解來進行標註了。
@Override @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public List<Role> findRoles(String roleName, String note) { return roleDao.findRoles(roleName, note); }
(5)insertRoles方法
insertRoles方法中調用了insertRole方法,而insertRole方法本身帶有註解@CachePut,這時如果要執行insertRoles方法,會發現緩存失效了。
這裏失效的原因是和之前討論過的數據庫事務失效的情況一樣,由於緩存註解也是使用了Spring AOP 來實現,而Spring AOP使用了動態代理,即只有代理對象的相互調用,AOP才具有攔截功能。而這裏的自調用是沒有代理對象存在的,因此註解功能失效。
@Override @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public int insertRoles(List<Role> roleList) { for (Role role : roleList) { //同一類的方法調用自己方法,產生自調用[插入:失效]問題 this.insertRole(role); } return roleList.size(); }
Spring(五)Spring緩存機制與Redis的結合