1. 程式人生 > >Spring(五)Spring緩存機制與Redis的結合

Spring(五)Spring緩存機制與Redis的結合

jedispool gen ali 插入數據 sco com generate ret 提高

  一、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的結合