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, [email protected], MySQL-AB JDBC Driver] for JDBC transactionChanging isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to 2Switching JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to manual commitCreating a new SqlSession
Registering transaction synchronization for SqlSession [[email protected]]
JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], 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 [[email protected]]
Opening RedisConnection
Closing Redis Connection
Transaction synchronization committing SqlSession [[email protected]]
Transaction synchronization deregistering SqlSession [[email protected]]
Transaction synchronization closing SqlSession [[email protected]]
Initiating transaction commit
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver]
Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to 4
Releasing JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], 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, [email protected], MySQL-AB JDBC Driver] for JDBC transaction
Changing isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to 2
Switching JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], 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, [email protected], MySQL-AB JDBC Driver]
Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to 4
Releasing JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], 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, [email protected], MySQL-AB JDBC Driver] for JDBC transaction
DChanging isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to 2
Switching JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to manual commitCreating a new SqlSession
Registering transaction synchronization for SqlSession [[email protected]]
JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], 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 [[email protected]]
Opening RedisConnection
Closing Redis Connection
Transaction synchronization committing SqlSession [[email protected]]
Transaction synchronization deregistering SqlSession [[email protected]]
Transaction synchronization closing SqlSession [[email protected]]
Initiating transaction commit
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver]
Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to 4
Releasing JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], 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, [email protected], MySQL-AB JDBC Driver] for JDBC transaction
Changing isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to 2
Switching JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to manual commitCreating a new SqlSession
Registering transaction synchronization for SqlSession [[email protected]]
JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] will be managed by Spring
==>  Preparing: delete from t_role where id=?==> Parameters: 7(Long)
<==    Updates: 1Releasing transactional SqlSession [[email protected]]
Opening RedisConnection
Closing Redis Connection
Transaction synchronization committing SqlSession [[email protected]]
Transaction synchronization deregistering SqlSession [[email protected]]
Transaction synchronization closing SqlSession [[email protected]]
Initiating transaction commit
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver]
Resetting isolation level of JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], MySQL-AB JDBC Driver] to 4
Releasing JDBC Connection [jdbc:mysql://localhost:3306/chapter6?useSSL=false, [email protected], 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();
    }

  

原文地址:https://www.cnblogs.com/BigJunOba/p/9794911.html

时间: 2024-11-09 04:16:26

Spring(五)Spring缓存机制与Redis的结合的相关文章

hbase 学习(十五)缓存机制以及可以利用SSD作为存储的BucketCache

下面介绍Hbase的缓存机制: a.HBase在读取时,会以Block为单位进行cache,用来提升读的性能 b.Block可以分类为DataBlock(默认大小64K,存储KV).BloomBlock(默认大小128K,存储BloomFilter数据).IndexBlock(默认大小128K,索引数据,用来加快Rowkey所在DataBlock的定位) c.对于一次随机读,Block的访问顺序为BloomBlock.IndexBlock.DataBlock,如果Region下面的StoreFi

缓存机制总结(JVM内置缓存机制,MyBatis和Hibernate缓存机制,Redis缓存)

一.JVM内置缓存(值存放在JVM缓存中) 我们可以先了解一下Cookie,Session,和Cache Cookie:当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cookie 会帮你在网站上所打的文字或是一些选择都纪录下来.当下次你再光临同一个网站,WEB 服务器会先看看有没有它上次留下的 Cookie 资料,有的话,就会依据 Cookie里的内容来判断使用者,送出特定的网页内容给你.具体来说Cookie机制采用的是在客户端保持状态的方案(保存客户浏览器请求服务器页面

我的redis缓存机制和redis数据结构整理(一)

摘要: 0.redis安装 1.redis的常用数据结构类型 1.1  String 1.2  List 1.3  Set 1.4  Sorted Set 1.5  Hash 2.redis是单进程单线程的 3.管道 4.我的测试代码 内容总结 0.redis安装 官网地址 http://redis.io/ 下载:wget http://download.redis.io/releases/redis-3.2.4.tar.gz 解压:tar xzf redis-3.2.4.tar.gz 编译:c

Redis整合Spring结合使用缓存实例

摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的方法切入到有需要进入缓存的类或方法前面. 一.Redis介绍 什么是Redis? redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set –有序集合)和hash(哈希类型).这些数据类型都支持push/pop.add/remove及取交集并集和差集及更丰富的操作

Redis整合Spring结合使用缓存实例(转)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的方法切入到有需要进入缓存的类或方法前面. 一.Redis介绍 什么是Redis? redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set --有序集合)和h

使用Spring提供的缓存抽象机制整合EHCache为项目提供二级缓存

Spring自身并没有实现缓存解决方案,但是对缓存管理功能提供了声明式的支持,能够与多种流行的缓存实现进行集成. Spring Cache是作用在方法上的(不能理解为只注解在方法上),其核心思想是:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值存放在缓存中,等到下次利用同样的参数调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回.所以在使用Spring Cache的时候我们要保证我们的缓存的方法对于相同的方法参数要有相同的返回结果. 1.适合和不适合保存到二级缓存的数

Spring控制Hibernate的缓存机制ehcache

首先在spring.xml中进入bean <prop key="hibernate.cache.use_second_level_cache">true</prop> <!--设置缓存机制为二级缓存 --> <prop key="hibernate.cache.use_query_cache">true</prop> <!--启动查询缓存 --> <prop key="hiber

Spring笔记——14.Spring3新增加的缓存机制

Spring3.1新增了一种缓存机制,这种缓存机制与Spring容器无缝结合在一起,可以对容器中的任意bean或bean的方法增加缓存.Spring的缓存级别更高,可以在控制器组件或业务逻辑组件级别进行缓存,这样应用完全无需重复调用底层的DAO. 启动Spring缓存 xml中有一个cache命名空间专门用来配置缓存.启动缓存需要在xml中添加如下一行: <cache:annotation-driven chache-manager="缓存管理器ID"/> 该元素指定Spr

spring的缓存机制

在Spring缓存机制中,包括了两个方面的缓存操作:1.缓存某个方法返回的结果:2.在某个方法执行前或后清空缓存. spring是怎么进行缓存的,白话点讲就是:一个map来进行缓存,当调用aop时访问缓存,判断是否有对应数据存在.具体如下: 1.EHCache Spring仅仅是提供了对缓存的支持,但它并没有任何的缓存功能的实现,spring使用的是第三方的缓存框架来实现缓存的功能.其中,spring对EHCache提供了很好的支持.下面我们以EHCache为例来介绍spring的缓存配置. 在