zoukankan      html  css  js  c++  java
  • spring data redis

    1,保存一个key-value形式的结构到redis

    2,把一个对象保存成hash形式的结构到redis

    代码如下:

            // 保存key-value值
            pushFrequencyTemplate.opsForValue().set("test_key", "test_value111");
            // 读取刚才保存的key-value值
            System.out.println(pushFrequencyTemplate.opsForValue().get("test_key"));


            // 把对象保存成hash
            Map<String, String> map = frequencyMapper.toHash(frequency);
            pushFrequencyTemplate.opsForHash().putAll("push_frequency", map);
            // 把刚才保存的hash读出来,并显示
            Map<String, String> redisMap = pushFrequencyTemplate.opsForHash().entries("push_frequency");
            RedisPushFrequencyEntity redisFrequency = frequencyMapper.fromHash(redisMap);
            System.out.println(redisMap);

    问题1:

    声明一个redisTemplate,测试是否可以把对象保存成hash,并从hash还原成对象。

    只设置ConnectionFactory,其它什么也不设置。代码如下:

        @Bean(name = "pushFrequencyTemplate")
        public <String, V> RedisTemplate<String, V> getPushFrequencyTemplate() {
            RedisTemplate<String, V> redisTemplate = new RedisTemplate<String, V>();
            redisTemplate.setConnectionFactory(factory);
            return redisTemplate;
        }

    结果:

    get test_key// 返回为空(什么也不显示)

    hgetall push_frequency // 返回为空(什么也不显示)

    很奇怪为什么为空,因为查了一些资料,如果不进行设置的话,默认使用JdkSerializationRedisSerializer进行数据序列化。

    (把任何数据保存到redis中时,都需要进行序列化)

    用视图的形式查了一下,发现实际保存的内容如下:

    key-value:

      key:xACxEDx00x05tx00x08test_key

      value:xACxEDx00x05tx00x0Dtest_value111

    hash:

      key:xACxEDx00x05tx00x0Epush_frequency

      hashkey:xACxEDx00x05tx00x04date

      hashvalue:xACxEDx00x05tx00x0A2016-08-18

      hashkey:xACxEDx00x05tx00x09frequency

      hashvalue:xACxEDx00x05tx00x011

    所有的key和value还有hashkey和hashvalue的原始字符前,都加了一串字符。查了一下,这是JdkSerializationRedisSerializer进行序列化时,加上去的。

    原以为只会在value或hashvalue上加,没想到在key和hashkey上也加了,这样的话,用原来的key就取不到我们保存的数据了。

    所以,我们要针对我们的需求,设置RedisSerializer。

    现在可用的RedisSerializer主要有几种:

      (1)StringRedisSerializer

      (2)Jackson2JsonRedisSerializer

      (3)JdkSerializationRedisSerializer

      (4)GenericToStringSerializer

      (5)OxmSerializer

    StringRedisSerializer:对String数据进行序列化。序列化后,保存到Redis中的数据,不会有像上面的“xACxEDx00x05tx00x09”多余字符。就是"frequency".

    Jackson2JsonRedisSerializer:用Jackson2,将对象序列化成Json。这个Serializer功能很强大,但在现实中,是否需要这样使用,要多考虑。一旦这样使用后,要修改对象的一个属性值时,就需要把整个对象都读取出来,再保存回去。

    JdkSerializationRedisSerializer:使用Java序列化。结果就像最上面的样子。

    GenericToStringSerializer:使用Spring转换服务进行序列化。在网上没有找到什么例子,使用方法和StringRedisSerializer相比,StringRedisSerializer只能直接对String类型的数据进行操作,如果要被序列化的数据不是String类型的,需要转换成String类型,例如:String.valueOf()。但使用GenericToStringSerializer的话,不需要进行转换,直接由String帮我们进行转换。但这样的话,也就定死了序列化前和序列化后的数据类型,例如:template.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));

    我们只能用对Long型进行序列化和反序列化。(但基础类型也不多,定义8个可能也没什么)

    OxmSerializer:使用SpringO/X映射的编排器和解排器实现序列化,用于XML序列化。

    我们这里针对StringRedisSerializer,Jackson2JsonRedisSerializer和JdkSerializationRedisSerializer进行测试。

    下面是,把3种Serializer保存到Redis中的结果:

    1,所有的KeySerializer和HashKeySerializer都使用StringRedisSerializer,用其它Serializer的没有什么意义,就像最上面的例子一样。
    2,上面序列化后的值,是保存到redis中的值,从Redis中读取回Java中后,值的内容都是一样的。

    从上面的结果不难看出,

    1,用StringRedisSerializer进行序列化的值,在Java和Redis中保存的内容是一样的

    2,用Jackson2JsonRedisSerializer进行序列化的值,在Redis中保存的内容,比Java中多了一对双引号。

    3,用JdkSerializationRedisSerializer进行序列化的值,对于Key-Value的Value来说,是在Redis中是不可读的。对于Hash的Value来说,比Java的内容多了一些字符。

    (如果Key的Serializer也用和Value相同的Serializer的话,在Redis中保存的内容和上面Value的差异是一样的,所以我们保存时,只用StringRedisSerializer进行序列化)

    问题2:

    当想把一个对象保存成一个Hash的时候,用Spring提供的HashMapper相关类,进行转换。看了一些例子,使用方法如下:

    private final HashMapper<User, String, String> mapper =
         new DecoratingStringHashMapper<User>(new BeanUtilsHashMapper<User>(User.class));

    // 把对象保存成hash
    Map<String, String> map = mapper.toHash(user);
    pushFrequencyTemplate.opsForHash().putAll("user", map);
    // 把刚才保存的hash读出来,并显示
    Map<String, String> redisMap = pushFrequencyTemplate.opsForHash().entries("user");

    DecoratingStringHashMapper和BeanUtilsHashMapper都实现了HashMapper接口,例子中是嵌套使用的,能不能不嵌套使用,只使用BeanUtilsHashMapper呢。

    试了一下,出错了。

      private final HashMapper<DwUser, String, String> mapper = new BeanUtilsHashMapper<User>(User.class);

    看了一下代码,没具体测试和细看,好像Spring的PutAll方法,接收的是一种LinkedHashMap的Map,其它的会报错。

    问题3:

    把对象转换成Map的实现类,原来有2个:BeanUtilsHashMapper和JacksonHashMapper。但在1.7版本时,JacksonHashMapper不被推荐使用了,所以使用了BeanUtilsHashMapper。但BeanUtilsHashMapper有一个问题,它会把对象中所有的getter方法都把取出来,把get后面的字符串当成属性放到map里。所以每个对象都有的getClass方法也被当成一个属性,放到map里了,不得不手工把这个属性删除一下。

    为了避免这样的重复手工劳动,写了一个类来实现这个工作:

    共通类:

    import java.util.Map;

    import org.springframework.data.redis.hash.BeanUtilsHashMapper;
    import org.springframework.data.redis.hash.DecoratingStringHashMapper;
    import org.springframework.data.redis.hash.HashMapper;

    public class HashMapper<T, K, V> implements HashMapper<T, K, V> {

        private HashMapper<T, K, V> mapper;

        public HashMapper(HashMapper<T, K, V> mapper) {
            // this.mapper = mapper;
            this.mapper = mapper;
        }

        @Override
        public Map<K, V> toHash(T obj) {
            Map<K, V> map = mapper.toHash(obj);
            // 去掉Object类中的class属性生成的key/value
            map.remove("class");
            return map;
        }

        @Override
        public T fromHash(Map<K, V> map) {
            return mapper.fromHash(map);
        }
        

        public static <T, K, V> HashMapper<T, K, V> getInstance(Class<T> tClazz, Class<K> kClazz,
                Class<V> vClazz) {
            return new HashMapper<T, K, V>((HashMapper<T, K, V>) new DecoratingStringHashMapper<T>(
                    new BeanUtilsHashMapper<T>(tClazz)));
        }

    }

    使用方法:

        // 声明

        private final HashMapper<RedisPushFrequencyEntity, String, String> frequencyMapper =
                MOKOHashMapper.getInstance(RedisPushFrequencyEntity.class, String.class, String.class);

        // 使用

        frequencyMapper.toHash(xxx);

    问题4:

    如果想使用RedisTemplate来帮助你,把从Redis中取得的值直接转换成对象等数据类型的话,

    必须得像下面一样声明,有多少个转换的话,就要声明多少个RedisTemplate。

    声明RedisTemplate:

        @Bean(name = "userRedisTemplate")
        public RedisTemplate<String, User> getRedisTemplate() {
            RedisTemplate<String, User> redisTemplate = new RedisTemplate<String, User>();
            redisTemplate.setConnectionFactory(factory);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<User>(User.class));
            redisTemplate.setDefaultSerializer(new StringRedisSerializer());
            return redisTemplate;
        }

    使用地方:

        @Autowired
        private RedisTemplate<String, DwUser> userRedisTemplate;

    试了一下,可以写一个共通方法来把上面的做法简化一下。

    共通方法:

        public <String, V> RedisTemplate<String, V> getJacksonStringTemplate(Class<V> clazz) {
            RedisTemplate<String, V> redisTemplate = new RedisTemplate<String, V>();
            redisTemplate.setConnectionFactory(factory);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<V>(clazz));
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(new StringRedisSerializer());
            // 不是注入方法的话,必须调用它。Spring注入的话,会在注入时调用
            redisTemplate.afterPropertiesSet();

            return redisTemplate;
        }

    使用地方:

    private RedisTemplate<String, RedisPushFrequencyEntity> keyJacksonValueTemplate;

        @PostConstruct
        public void PushRedisServicePostConstruct() {
            keyJacksonValueTemplate =
                    redisTemplateFactory.getJacksonStringTemplate(RedisPushFrequencyEntity.class);
        }

     *  1,RedisTemplate声明时,不能使用@Autowire自动注入
     *  2,调用下面的方法时行初始化时,必须在@PostConstruct方法中去做。 

    问题5:

    Spring Data里还有一些Redis类,在包下面,

    例如:RedisAtomicInteger, RedisAtomicLong, RedisList, RedisSet, RedisZSet, RedisMap

    粗略看了一下,这些类的实现,都是使用上面的RedisTemplate的各种方法来实现的,方便使用。

    下面的文章和retwisj项目都介绍了一些上面的类的使用方法,可以看看。

    http://www.cnblogs.com/qijiang/p/5626461.html

    问题6:

    如果我想用Jedis原生接口怎么,也有办法:

    (ValueOperation,ListOperation,SetOperation等操作也都是用它实现的,可以看看源码)

    redisTemplate.execute(new RedisCallback<Object>() {  
            @Override  
            public Object doInRedis(RedisConnection connection)  
                    throws DataAccessException {  
                connection.set(  
                        redisTemplate.getStringSerializer().serialize(  
                                "user.uid." + user.getUid()),  
                        redisTemplate.getStringSerializer().serialize(  
                                user.getAddress()));  
                return null;  
            }  
        });  

    最后,送上一个关于用Spring Data Redis操作Redis各种类型的文章:

    https://omanandj.wordpress.com/2013/07/26/redis-using-spring-data-part-2-3/

  • 相关阅读:
    go爬虫
    node简单爬虫request简单运用
    Centos7.4安装Mysql5.6
    HTML本地资源读取!
    node-request模块
    react中使用AntDesign库 --- babel-plugin-import 配置
    Django 模型(数据库)
    TypeScript--安装依赖,vscode配置ts自动转换成js文件
    python爬虫
    nodejs爬虫简单实现
  • 原文地址:https://www.cnblogs.com/cmfwm/p/8005332.html
Copyright © 2011-2022 走看看