为什么数据获得存储在Redis的怪异按键使用Jedis时与Spring的数据?(Why is dat

2019-06-18 19:24发布

我使用与Jedis春数据Redis的。 我想存储与关键的哈希vc:${list_id} 。 我能够成功插入到Redis的。 然而,当我检查使用Redis的-CLI的钥匙,我没有看到关键vc:501381 。 相反,我看到\xac\xed\x00\x05t\x00\tvc:501381

这究竟是为什么?我如何改变呢?

Answer 1:

好吧,用Google搜索了一段时间,发现在帮助http://java.dzone.com/articles/spring-data-redis 。

它的发生是因为Java序列化的。

需求redisTemplate的关键串行器进行配置,以StringRedisSerializer即是这样的:

<bean 
    id="jedisConnectionFactory" 
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" 
    p:host-name="${redis.server}" 
    p:port="${redis.port}" 
    p:use-pool="true"/>

<bean 
    id="stringRedisSerializer" 
    class="org.springframework.data.redis.serializer.StringRedisSerializer"/>

<bean 
    id="redisTemplate" 
    class="org.springframework.data.redis.core.RedisTemplate"
    p:connection-factory-ref="jedisConnectionFactory" 
    p:keySerializer-ref="stringRedisSerializer"
    p:hashKeySerializer-ref="stringRedisSerializer" 
/>

现在,在Redis的关键是vc:501381

或者像@niconic说,我们还可以设置默认的序列本身的字符串串行如下:

<bean 
    id="redisTemplate" 
    class="org.springframework.data.redis.core.RedisTemplate"
    p:connection-factory-ref="jedisConnectionFactory" 
    p:defaultSerializer-ref="stringRedisSerializer"
/>

这意味着我们所有的键和值都是字符串。 但是请注意,这可能不是最好的,因为你可能希望你的价值观是不是只是字符串。

如果你的价值是一个域对象,那么你可以使用杰克逊串行和配置所提到串行这里即是这样的:

<bean id="userJsonRedisSerializer" class="org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer">
    <constructor-arg type="java.lang.Class" value="com.mycompany.redis.domain.User"/>
</bean>

和配置模板:

<bean 
    id="redisTemplate" 
    class="org.springframework.data.redis.core.RedisTemplate"
    p:connection-factory-ref="jedisConnectionFactory" 
    p:keySerializer-ref="stringRedisSerializer"
    p:hashKeySerializer-ref="stringRedisSerializer" 
    p:valueSerialier-ref="userJsonRedisSerializer"
/>


Answer 2:

使用StringRedisTemplate更换RedisTemplate

默认情况下, RedisTemplate使用Java序列化, StringRedisTemplate使用StringRedisSerializer

<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>


Answer 3:

我知道这个问题已经有一段时间,但我做了一些关于这个论题的研究最近一次,所以我想和大家分享如何“半散列”关键是要通过这里通春源代码的一部分生成。

首先,充分利用春季AOP来解决类似注解@Cacheable, @CacheEvict or @CachePut等咨询类是CacheInterceptor从Spring上下文中的依赖,这是子类CacheAspectSupport (也从Spring上下文)。 为了便于这样的解释,我会用@Cacheable作为一个例子去直通这里的源代码的一部分。

当注释为方法@Cacheable被调用时,AOP将它路由到此方法protected Collection<? extends Cache> getCaches(CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver) protected Collection<? extends Cache> getCaches(CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver)CacheAspectSupport类,其中它会尝试解决此@Cacheable注释。 反过来,它导致了这种方法的调用public Cache getCache(String name)在实施CacheManager中。 对于这个解释,执行CacheManage将RedisCacheManager (从Spring数据redis的相关性)。

如果高速缓存未命中,将继续以创建缓存。 下面是从关键方法RedisCacheManager

protected Cache getMissingCache(String name) {
    return this.dynamic ? createCache(name) : null;
}

@SuppressWarnings("unchecked")
protected RedisCache createCache(String cacheName) {
    long expiration = computeExpiration(cacheName);
    return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration,
            cacheNullValues);
}

从本质上讲,它会实例化一个RedisCache对象。 要做到这一点,它需要4个参数,即cacheName, 前缀 (这是关于回答这个问题的关键参数),redisOperation(又名,配置redisTemplate),过期(默认为0)和cacheNullValues(默认为false) 。 下面的构造函数显示有关RedisCache更多细节。

/**
 * Constructs a new {@link RedisCache} instance.
 *
 * @param name cache name
 * @param prefix must not be {@literal null} or empty.
 * @param redisOperations
 * @param expiration
 * @param allowNullValues
 * @since 1.8
 */
public RedisCache(String name, byte[] prefix, RedisOperations<? extends Object, ? extends Object> redisOperations,
        long expiration, boolean allowNullValues) {

    super(allowNullValues);

    Assert.hasText(name, "CacheName must not be null or empty!");

    RedisSerializer<?> serializer = redisOperations.getValueSerializer() != null ? redisOperations.getValueSerializer()
            : (RedisSerializer<?>) new JdkSerializationRedisSerializer();

    this.cacheMetadata = new RedisCacheMetadata(name, prefix);
    this.cacheMetadata.setDefaultExpiration(expiration);
    this.redisOperations = redisOperations;
    this.cacheValueAccessor = new CacheValueAccessor(serializer);

    if (allowNullValues) {

        if (redisOperations.getValueSerializer() instanceof StringRedisSerializer
                || redisOperations.getValueSerializer() instanceof GenericToStringSerializer
                || redisOperations.getValueSerializer() instanceof JacksonJsonRedisSerializer
                || redisOperations.getValueSerializer() instanceof Jackson2JsonRedisSerializer) {
            throw new IllegalArgumentException(String.format(
                    "Redis does not allow keys with null value ¯\\_(ツ)_/¯. "
                            + "The chosen %s does not support generic type handling and therefore cannot be used with allowNullValues enabled. "
                            + "Please use a different RedisSerializer or disable null value support.",
                    ClassUtils.getShortName(redisOperations.getValueSerializer().getClass())));
        }
    }
}

那么什么用的prefix在此RedisCache? - >如在构造约,则在这个语句中使用this.cacheMetadata = new RedisCacheMetadata(name, prefix); ,和构造RedisCacheMetadata下面显示的详细信息:

/**
     * @param cacheName must not be {@literal null} or empty.
     * @param keyPrefix can be {@literal null}.
     */
    public RedisCacheMetadata(String cacheName, byte[] keyPrefix) {

        Assert.hasText(cacheName, "CacheName must not be null or empty!");
        this.cacheName = cacheName;
        this.keyPrefix = keyPrefix;

        StringRedisSerializer stringSerializer = new StringRedisSerializer();

        // name of the set holding the keys
        this.setOfKnownKeys = usesKeyPrefix() ? new byte[] {} : stringSerializer.serialize(cacheName + "~keys");
        this.cacheLockName = stringSerializer.serialize(cacheName + "~lock");
    }

在这一点上,我们知道有些前缀参数已被设置为RedisCacheMetadata ,但究竟是怎么这个前缀用来形成Redis的关键(例如,\西飞\固定的\ X00 \ x05t \ X00 \ TVC:501381正如你所提到的) ?

基本上, CacheInterceptor随后将前进到调用一个方法private RedisCacheKey getRedisCacheKey(Object key)从上述RedisCache对象,它返回的一个实例RedisCacheKey通过利用从前缀 RedisCacheMetadata从和keySerializer RedisOperation

private RedisCacheKey getRedisCacheKey(Object key) {
    return new RedisCacheKey(key).usePrefix(this.cacheMetadata.getKeyPrefix())
            .withKeySerializer(redisOperations.getKeySerializer());
}

通过达到这一点,的“预”的建议CacheInterceptor完成,它会继续执行由注释的实际方法@Cacheable 。 并完成实际的方法执行后,它会做的“后”的建议CacheInterceptor ,基本上把结果RedisCache。 下面是把结果Redis的缓存的方法:

public void put(final Object key, final Object value) {

    put(new RedisCacheElement(getRedisCacheKey(key), toStoreValue(value))
            .expireAfter(cacheMetadata.getDefaultExpiration()));
}

/**
 * Add the element by adding {@link RedisCacheElement#get()} at {@link RedisCacheElement#getKeyBytes()}. If the cache
 * previously contained a mapping for this {@link RedisCacheElement#getKeyBytes()}, the old value is replaced by
 * {@link RedisCacheElement#get()}.
 *
 * @param element must not be {@literal null}.
 * @since 1.5
 */
public void put(RedisCacheElement element) {

    Assert.notNull(element, "Element must not be null!");

    redisOperations
            .execute(new RedisCachePutCallback(new BinaryRedisCacheElement(element, cacheValueAccessor), cacheMetadata));
}

RedisCachePutCallback对象,其回调方法doInRedis()实际调用的方法来形成redis的实际键,方法名是getKeyBytes()RedisCacheKey实例。 下面显示了该方法的详细信息:

/**
 * Get the {@link Byte} representation of the given key element using prefix if available.
 */
public byte[] getKeyBytes() {

    byte[] rawKey = serializeKeyElement();
    if (!hasPrefix()) {
        return rawKey;
    }

    byte[] prefixedKey = Arrays.copyOf(prefix, prefix.length + rawKey.length);
    System.arraycopy(rawKey, 0, prefixedKey, prefix.length, rawKey.length);

    return prefixedKey;
}

正如我们在看到getKeyBytes (:你的情况501381 VC)和前缀键(\西飞\固定的\ X00 \ x05t \ X00 \吨你的情况)的方法,它同时利用了原始密钥。



Answer 4:

你必须序列您发送它的Redis TEH对象。 下面是它的完整的运行实例。 它使用接口DomainObjectSerializable

下面是步骤

1)让你的Maven的pom.xml有以下罐子

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency>

    <dependency>
           <groupId>org.springframework.data</groupId>
           <artifactId>spring-data-redis</artifactId>
           <version>1.3.0.RELEASE</version>
        </dependency>

            <dependency>
               <groupId>redis.clients</groupId>
               <artifactId>jedis</artifactId>
               <version>2.4.1</version>
            </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.0</version>
    </dependency>

2)使你的XML配置如下

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
        xmlns:c="http://www.springframework.org/schema/c"
        xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd

        http://www.springframework.org/schema/cache 
        http://www.springframework.org/schema/cache/spring-cache.xsd">



    <bean id="jeidsConnectionFactory"
      class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
      p:host-name="localhost" p:port="6379" p:password="" />

     <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
      p:connection-factory-ref="jeidsConnectionFactory" />

     <bean id="imageRepository" class="com.self.common.api.poc.ImageRepository">
      <property name="redisTemplate" ref="redisTemplate"/>
     </bean>

</beans>

3)使类如下

package com.self.common.api.poc;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class RedisMainApp {

 public static void main(String[] args) throws IOException {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("mvc-dispatcher-servlet.xml");
  ImageRepository imageRepository = (ImageRepository) applicationContext.getBean("imageRepository");

  BufferedImage img = ImageIO.read(new File("files/img/TestImage.png"));
  BufferedImage newImg;
  String imagestr;
  imagestr = encodeToString(img, "png");
  Image image1 = new Image("1", imagestr);

  img = ImageIO.read(new File("files/img/TestImage2.png"));
  imagestr = encodeToString(img, "png");
  Image image2 = new Image("2", imagestr);

  imageRepository.put(image1);
  System.out.println(" Step 1 output : " + imageRepository.getObjects());
  imageRepository.put(image2);
  System.out.println(" Step 2 output : " + imageRepository.getObjects());
  imageRepository.delete(image1);
  System.out.println(" Step 3 output : " + imageRepository.getObjects());

 }

 /**
  * Decode string to image
  * @param imageString The string to decode
  * @return decoded image
  */
 public static BufferedImage decodeToImage(String imageString) {

     BufferedImage image = null;
     byte[] imageByte;
     try {
         BASE64Decoder decoder = new BASE64Decoder();
         imageByte = decoder.decodeBuffer(imageString);
         ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
         image = ImageIO.read(bis);
         bis.close();
     } catch (Exception e) {
         e.printStackTrace();
     }
     return image;
 }

 /**
  * Encode image to string
  * @param image The image to encode
  * @param type jpeg, bmp, ...
  * @return encoded string
  */
 public static String encodeToString(BufferedImage image, String type) {
     String imageString = null;
     ByteArrayOutputStream bos = new ByteArrayOutputStream();

     try {
         ImageIO.write(image, type, bos);
         byte[] imageBytes = bos.toByteArray();

         BASE64Encoder encoder = new BASE64Encoder();
         imageString = encoder.encode(imageBytes);

         bos.close();
     } catch (IOException e) {
         e.printStackTrace();
     }
     return imageString;
 }
}

package com.self.common.api.poc;

public class Image implements DomainObject {

 public static final String OBJECT_KEY = "IMAGE";

 public Image() {
 }

 public Image(String imageId, String imageAsStringBase64){
  this.imageId = imageId;
  this.imageAsStringBase64 = imageAsStringBase64;
 }
 private String imageId;
 private String imageAsStringBase64;

 public String getImageId() {
  return imageId;
 }

 public void setImageId(String imageId) {
  this.imageId = imageId;
 }

 public String getImageName() {
  return imageAsStringBase64;
 }

 public void setImageName(String imageAsStringBase64) {
  this.imageAsStringBase64 = imageAsStringBase64;
 }

 @Override
 public String toString() {
  return "User [id=" + imageAsStringBase64 + ", imageAsBase64String=" + imageAsStringBase64 + "]";
 }

 @Override
 public String getKey() {
  return getImageId();
 }

 @Override
 public String getObjectKey() {
  return OBJECT_KEY;
 }
}

package com.self.common.api.poc;

import java.io.Serializable;

public interface DomainObject extends Serializable {

 String getKey();

 String getObjectKey();
}

package com.self.common.api.poc;

import java.util.List;

import com.self.common.api.poc.DomainObject;

public interface Repository<V extends DomainObject> {

 void put(V obj);

 V get(V key);

 void delete(V key);

 List<V> getObjects();
}

package com.self.common.api.poc;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import com.self.common.api.poc.DomainObject;

public class ImageRepository implements Repository<Image>{

 @Autowired
 private RedisTemplate<String,Image> redisTemplate;

 public RedisTemplate<String,Image> getRedisTemplate() {
  return redisTemplate;
 }

 public void setRedisTemplate(RedisTemplate<String,Image> redisTemplate) {
  this.redisTemplate = redisTemplate;
 }

 @Override
 public void put(Image image) {
  redisTemplate.opsForHash()
    .put(image.getObjectKey(), image.getKey(), image);
 }

 @Override
 public void delete(Image key) {
  redisTemplate.opsForHash().delete(key.getObjectKey(), key.getKey());
 }

 @Override
 public Image get(Image key) {
  return (Image) redisTemplate.opsForHash().get(key.getObjectKey(),
    key.getKey());
 }

 @Override
 public List<Image> getObjects() {
  List<Image> users = new ArrayList<Image>();
  for (Object user : redisTemplate.opsForHash().values(Image.OBJECT_KEY) ){
   users.add((Image) user);
  }
  return users;
 }

}

有关sprinf jedis你可以看到更多的参考http://www.javacodegeeks.com/2012/06/using-redis-with-spring.html

示例代码摘自http://javakart.blogspot.in/2012/12/spring-data-redis-hello-world-example.html



Answer 5:

这是一个非常古老的问题,但我的答案可能是人谁得到了同样的问题,与工作时有用的使用Spring启动Redis的 。 我被困在同一个问题,而存储redis的散列类型的数据。 我已经写了所需的配置文件的变化RedisTemplate 。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.redis")
public class AppCofiguration {

    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConFactory = new JedisConnectionFactory();
        jedisConFactory.setHostName("127.0.0.1");
        jedisConFactory.setPort(6379);
        return jedisConFactory;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(jedisConnectionFactory());
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new StringRedisSerializer());

        // the following is not required      
        template.setHashValueSerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        return template;
    }

}

如果数据类型是串然后template.setHashValueSerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer()); 不是必需的。



文章来源: Why is data getting stored with weird keys in Redis when using Jedis with Spring Data?