spring+redis缓存配置,以及报连接池错误的可能

一.spring集成redis

1.引入jar包

      jedis-2.9.0.jar
      spring-data-redis-1.7.11.RELEASE.jar

2.redis配置文件

        
    
    
	
	
	
	
	
		
		
		
		
		
		
		
		
	
	
	
		
		
		
		
	
	
	
		
		
		 
			
		
		
			
		 
		
		
	
	
	
		
			
				
				
			
		
	

缓存管理器中我set了两个模块,分别叫selectUserInfo和selectRole,设置过期时间为120秒,这两个模块接下来会用到,com.caodaxing.redis.cache.MyCache为实现Cache接口的自定义实现类,

下面为缓存自定义实现类代码:

@Component
public class MyCache implements Cache {
	
	@Autowired
	private RedisTemplate redisTemplate;
	
	private String name;
	
	private long timeout;

	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public Object getNativeCache() {
		return this.redisTemplate;
	}

	@Override
	public ValueWrapper get(Object key) {
		if(key == null || StringUtils.isEmpty(key.toString())) {
			return null;
		}else {
			final String finalKey = formatStr(key);
			redisTemplate.multi();
			Object object = redisTemplate.execute(new RedisCallback() {

				@Override
				public Object doInRedis(RedisConnection connection) throws DataAccessException {
					byte[] newKey = finalKey.getBytes();
					byte[] value = connection.get(newKey);
					if(value == null) {
						return null;
					}
					return SerializableUtil.unserializeList(value);
				}
			});
			redisTemplate.exec();
			return object != null?new SimpleValueWrapper(object):null;
		}
	}


	@Override
	public void put(Object key, Object value) {
		if (key==null || value==null) {
            return;
        } else {
            final String finalKey = formatStr(key);
            if (!StringUtils.isEmpty(finalKey)) {
                final Object finalValue = value;
                redisTemplate.multi();
                redisTemplate.execute(new RedisCallback() {
                    @Override
                    public Boolean doInRedis(RedisConnection connection) {
                        connection.set(finalKey.getBytes(), SerializableUtil.serialize(finalValue));
                        // 设置超时间
                        connection.expire(finalKey.getBytes(), timeout);
                        return true;
                    }
                });
                redisTemplate.exec();
            }
        }
	}


	@Override
	public void evict(Object key) {
		if(key == null) {
			return;
		}else {
			final String finalKey = formatStr(key);
			redisTemplate.multi();
			redisTemplate.execute(new RedisCallback() {

				@Override
				public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
					connection.del(finalKey.getBytes());
					return true;
				}
			});
			redisTemplate.exec();
		}
	}

	@Override
	public void clear() {
		redisTemplate.multi();
		redisTemplate.execute(new RedisCallback() {

			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				connection.flushDb();
				return true;
			}
		});
		redisTemplate.exec();
	}
	
	private String formatStr(Object obj) {
		if(obj instanceof String) {
			return (String)obj;
		}else {
			return obj.toString();
		}
	}

	public RedisTemplate getRedisTemplate() {
		return redisTemplate;
	}

	public void setRedisTemplate(RedisTemplate redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	public long getTimeout() {
		return timeout;
	}

	public void setTimeout(long timeout) {
		this.timeout = timeout;
	}

	public void setName(String name) {
		this.name = name;
	}

} 
  

记住存储对象时,一定要序列化,不然会报错,name为配置文件中的配置,getNativeCache()方法为设置你的缓存对象,我这里用的是redis,当然你也集成mongo和memcache等,timeout为设置数据的过期时间.

3.缓存方法

下面是我对service层中两个方法进行缓存

@Service
public class LoginUserServiceImpl implements LoginUserService {
	
	@Autowired
	private LoginUserMapper loginUserMapper;

	/**
	 * 调用该方法时,会先触发Cache接口的get方法,如果缓存中查到数据,则直接返回数据,不再走该方法
	 * 如果没有查到数据,执行该方法,结束后调用Cache接口的put方法,将返回的数据存入缓存中
	 * 注意:key的值是字符串的话,使用单引号括起来,例:key="'userName'",如果不加单引号默认做Spel表达式识别
	 * 会报SpelEvaluationException错误,spel表达式:#方法参数
	 */
	@Cacheable(value="selectUserInfo",key="'userinfo_'+#pageNumber")
	@Override
	public List getUserInfoList(int pageNumber) {
		System.out.println("select userInfo start...");
		return getUser(pageNumber);
	}
	
	/**
	 * 删除缓存数据,value为要删除的哪个模块数据,key为要删除的数据键值,调用Cache类的evict方法
	 * 当allEntries为true时,删除value模块里的所有数据,调用Cache类的clear方法,key可不填
	 * allEntries默认为false,此时key值必填,否则会以默认key查询,有可能报错
	 */
	@CacheEvict(value="selectUserInfo",key="'userinfo_1'",allEntries=false)
	@Override
	public void clearData() {
		System.out.println("clear all data...");
	}
	
	private List getUser(int pageNumber) {
		System.out.println("query database");
		LoginUserExample example = new LoginUserExample();
		example.setOrderByClause(" id asc limit "+((pageNumber-1)*1)+","+(pageNumber*1));
		List selectByExample = loginUserMapper.selectByExample(example);
		return selectByExample;
	}

}

可以看到,我在方法的上面加入spring自带的@Cacheable和@CacheEvict注解,并且用到了上面配置文件中配置的name,我这里是将从数据库查询的用户信息放入名字为"selectUserInfo"模块的redis缓存中,key为"userinfo_"加当前页pageNumber参数

对于spring缓存注解不了解的可百度一下,这里就不多做介绍了,在这说一下该类中两个方法与上面自定义缓存类的联系,当调用getUserInfoList()方法的时候,会先调用Cache类中的get()方法,根据key值查询是否有数据,如果有,则直接返回数据,不执行getUserInfoList()方法中的代码,如果没有查到数据,则执行该方法中的代码,在方法执行完成后,会调用Cache类中的put()方法,将返回的数据放入到redis缓存中,这就是@Cacheable注解执行的全过程,

另外再讲下@CachePut注解,它在方法执行完后直接调用Cache类中的put()方法,不会调用get()方法,用于更新数据等使用

至于@CacheEvict注解,为清除数据使用,它有两种情况,当你配置allEntries=true时,他执行的是Cache类中的clear()方法,为false时,执行evict()方法,具体使用哪种情况,可根据需要自行选择.

到这里spring集成redis做缓存就讲到这里了,下面我们讲一下集成redis出现的连接池错误:

二.redis连接池问题:

一般使用redis连接池后,系统会帮你分配资源以及管理连接,但是当你打开了redis的事务时,有事务的连接池是不会自动关闭redis对象连接的,需自己手动关闭并释放连接,而且想要事务生效必须使用RedisTemplate中的multi()和exec()包住,如上面代码所示:

至于手动关闭连接,回收到redis连接池中可在exec()方法之后使用

RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());

具体是否有用没有测试,可以参考:https://blog.csdn.net/LiuHanFanShuang/article/details/52136438

还有一种情况会报跟连接池相关的错误,但并不是连接数满了,而是强制关闭Redis快照导致不能持久化。

这种情况我们只需要在redis服务器上执行以下代码就可:

127.0.0.1:6379> CONFIG SET stop-writes-on-bgsave-error no

 

你可能感兴趣的:(java)