Sto usando Spring Data Redis con Jedis. Sto cercando di memorizzare un hash con la chiave vc:${list_id}
. Sono stato in grado di inserire correttamente i redis. Tuttavia, quando controllo i tasti tramite redis-cli, non vedo la chiave vc:501381
. Invece vedo \xac\xed\x00\x05t\x00\tvc:501381
. Perché sta succedendo questo e come posso cambiarlo?Chiave redis strana con dati di primavera Jedis
risposta
Ok, ho cercato su Google un po 'di tempo e ho trovato aiuto allo http://java.dzone.com/articles/spring-data-redis.
È successo a causa della serializzazione Java.
Il serializzatore chiave per redisTemplate deve essere configurato per StringRedisSerializer
vale a dire in questo modo:
<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"
/>
Ora la chiave in Redis è vc:501381
.
O come @niconic dice, possiamo anche impostare il serializzatore predefinita stessa al serializzatore stringa come segue:
<bean
id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"
p:defaultSerializer-ref="stringRedisSerializer"
/>
che significa tutte le nostre chiavi e valori sono stringhe. Si noti tuttavia che questo potrebbe non essere preferibile, dal momento che si potrebbe desiderare che i propri valori non siano solo stringhe.
Se il valore è un oggetto di dominio, quindi è possibile utilizzare Jackson serializzatore e configurare un serializzatore come accennato here vale a dire in questo modo:
<bean id="userJsonRedisSerializer" class="org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer">
<constructor-arg type="java.lang.Class" value="com.mycompany.redis.domain.User"/>
</bean>
e configurare il modello come:
<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"
/>
È devi serializzare gli oggetti che stai inviando ai redis. Di seguito è riportato l'esempio completo in esecuzione. Esso utilizza l'interfaccia DomainObject
come Serializable
Di seguito sono riportati i passaggi
1) Fai la tua pom.xml Maven con i seguenti barattoli
<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) rendere il vostro XML di configurazione come segue
<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) Rendi i tuoi corsi come segue
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;
}
}
Per ulteriori riferimento Jedi sprinf potete vedere http://www.javacodegeeks.com/2012/06/using-redis-with-spring.html
codice di esempio è tratto da http://javakart.blogspot.in/2012/12/spring-data-redis-hello-world-example.html
Usa StringRedisTemplate
per sostituire RedisTemplate
.
Per impostazione predefinita, RedisTemplate
utilizza la serializzazione Java, StringRedisTemplate
utilizza StringRedisSerializer
.
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
Questa è la risposta esatta. RedisTemplate utilizza la serializzazione Java quindi assicurati che la tua classe sia scritta con "implements java.io.Serializable", che è abbastanza. –
So che questa domanda è stato un po ', ma ho fatto qualche ricerca su questo argomento ancora una volta di recente, quindi vorrei condividere come questa "semi-hashing" chiave è generata da passando attraverso una parte della molla codice sorgente qui.
Prima di tutto, Primavera sfrutta AOP per risolvere le annotazioni come @Cacheable, @CacheEvict or @CachePut
ecc La classe consiglio è CacheInterceptor
dalla primavera-contesto di dipendenza, che è una sottoclasse di CacheAspectSupport
(anche a partire dalla primavera-contesto). Per la semplicità di questa spiegazione, vorrei usare @Cacheable
come esempio per passare attraverso parte del codice sorgente qui.
Quando il metodo annotato come @Cacheable
viene invocato, AOP avrebbe percorso a questo metodo protected Collection<? extends Cache> getCaches(CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver)
da CacheAspectSupport
di classe, in cui si sarebbe cercare di risolvere questo @Cacheable
annotazione. A sua volta, porta all'invocazione di questo metodo public Cache getCache(String name)
nell'implementazione di CacheManager. Per questa spiegazione, l'implementazione di CacheManage sarà RedisCacheManager
(dalla dipendenza Spring-data-redis).
Se la cache non è stata colpita, andrà avanti per creare la cache. Sotto è i principali metodi di 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);
}
sostanza, si istanzia un oggetto RedisCache
. Per fare ciò, richiede 4 parametri, ovvero, cacheName, prefisso (questo è il parametro chiave per quanto riguarda la risposta a questa domanda), redisOperation (ovvero, redisTemplate configurato), expiration (predefinito su 0) e cacheNullValues (predefinito per false). Il costruttore di seguito mostra maggiori dettagli su 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())));
}
}
}
Così che l'uso di prefix
in questo RedisCache? -> Come mostrato nel costruttore riguardo, è usato in questa informativa this.cacheMetadata = new RedisCacheMetadata(name, prefix);
, e il costruttore di RedisCacheMetadata
sotto mostra maggiori dettagli:
/**
* @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");
}
A questo punto, sappiamo che alcuni parametri prefisso è stato impostato su RedisCacheMetadata
, ma come esattamente questo prefisso utilizzato per formare la chiave in Redis (ad esempio, \ xac \ xed \ x00 \ x05t \ x00 \ tvc: 501381 come hai menzionato)?
Fondamentalmente, il CacheInterceptor
sarà successivamente avanzare di richiamare un metodo private RedisCacheKey getRedisCacheKey(Object key)
dalla suddetta RedisCache
oggetto, che restituisce un'istanza di RedisCacheKey
utilizzando il prefisso da RedisCacheMetadata
e keySerializer da RedisOperation
.
private RedisCacheKey getRedisCacheKey(Object key) {
return new RedisCacheKey(key).usePrefix(this.cacheMetadata.getKeyPrefix())
.withKeySerializer(redisOperations.getKeySerializer());
}
Con il raggiungimento di questo punto, il consiglio "pre" di CacheInterceptor
è stato completato, e sarebbe andare avanti per eseguire il metodo effettivo annotata da @Cacheable
. E dopo aver completato l'esecuzione del metodo effettivo, farà il consiglio "post" di CacheInterceptor
, che in sostanza metterà il risultato su RedisCache. Sotto è il metodo di mettere il risultato a Redis cache:
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));
}
All'interno dell'oggetto RedisCachePutCallback
, il suo metodo di callback doInRedis()
effettivamente invocare un metodo per formare la chiave effettiva in Redis, e il nome del metodo è getKeyBytes()
da RedisCacheKey
esempio.Qui di seguito mostra i dettagli di questo metodo:
/**
* 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;
}
Come si può vedere nel metodo getKeyBytes
, utilizza sia la chiave prima (vc: 501.381 nel tuo caso) e la chiave di prefisso (\ xac \ sso \ x00 \ x05t \ x00 \ t nel tuo caso).
Se si modifica il riferimento a tutti i serializzatori, potrebbe essere utile modificare direttamente il serializzatore predefinito. – reallynice
nota che se si utilizza 'convertAndSend' con' redisTemplate' il 'p: keySerializer' e' hashKeySerializer' non sono sufficienti. cambiare il 'serializzatore predefinito' ha fatto il lavoro – oak
Grazie! Perché ho usato il boot primaverile e l'ho risolto facendo riferimento a questo documento: http://stackoverflow.com/questions/34201135/spring-redis-read-configuration-from-application-properties-file – zhuguowei