• 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();
        }

      

  • 相关阅读:
    MyBatis使用总结
    RPC与RMI的区别
    Python Web-第五周-Web Services and XML(Using Python to Access Web Data)
    Python Web-第四周-Programs that Surf the Web(Using Python to Access Web Data)
    Python Web-第三周-Networks and Sockets(Using Python to Access Web Data)
    Python Web-第二周-正则表达式(Using Python to Access Web Data)
    DELL XPS 13 9350 装Win7系统(坑爹)
    手心输入法皮肤制作技巧
    请对照这二十四条逻辑谬误自行打脸(转自知乎谢熊猫专栏)
    Aspose实现Office转PDF (ASP.NET)
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9794911.html
Copyright © 2020-2023  润新知