DATAREDIS-425 - CRUD operation and index resolution refinements.
- Add a composite IndexResolver implementation that iterates over a given collection of delegate IndexResolver instances and collects IndexedData from those. - Break up cycle involving ReferenceResolver and let the resolver just returns the raw hash. - Remove IndexType and use dedicated classes for index definitions. - Fix pagination error and follow up to changes introduced via DATAKV-123. Original Pull Request: #156
This commit is contained in:
@@ -35,14 +35,13 @@ import org.springframework.data.redis.connection.RedisClusterNode.RedisClusterNo
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
|
||||
import org.springframework.data.redis.connection.RedisNode.NodeType;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.NumberUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Common type converters
|
||||
*
|
||||
*
|
||||
* @author Jennifer Hickey
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
@@ -85,7 +84,7 @@ abstract public class Converters {
|
||||
Set<Flag> flags = parseFlags(args);
|
||||
|
||||
String portPart = hostAndPort[1];
|
||||
if(portPart.contains("@")){
|
||||
if (portPart.contains("@")) {
|
||||
portPart = portPart.substring(0, portPart.indexOf('@'));
|
||||
}
|
||||
|
||||
@@ -195,7 +194,7 @@ abstract public class Converters {
|
||||
|
||||
/**
|
||||
* Converts the result of a single line of {@code CLUSTER NODES} into a {@link RedisClusterNode}.
|
||||
*
|
||||
*
|
||||
* @param clusterNodesLine
|
||||
* @return
|
||||
* @since 1.7
|
||||
@@ -206,7 +205,7 @@ abstract public class Converters {
|
||||
|
||||
/**
|
||||
* Converts lines from the result of {@code CLUSTER NODES} into {@link RedisClusterNode}s.
|
||||
*
|
||||
*
|
||||
* @param clusterNodes
|
||||
* @return
|
||||
* @since 1.7
|
||||
@@ -228,6 +227,7 @@ abstract public class Converters {
|
||||
|
||||
/**
|
||||
* Converts the result of {@code CLUSTER NODES} into {@link RedisClusterNode}s.
|
||||
*
|
||||
* @param clusterNodes
|
||||
* @return
|
||||
* @since 1.7
|
||||
@@ -259,24 +259,8 @@ abstract public class Converters {
|
||||
* @return
|
||||
*/
|
||||
public static Long toTimeMillis(String seconds, String microseconds) {
|
||||
return NumberUtils.parseNumber(seconds, Long.class) * 1000L + NumberUtils.parseNumber(microseconds, Long.class)
|
||||
/ 1000L;
|
||||
return NumberUtils.parseNumber(seconds, Long.class) * 1000L
|
||||
+ NumberUtils.parseNumber(microseconds, Long.class) / 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge multiple {@code byte} arrays into one array
|
||||
* @param firstArray must not be {@literal null}
|
||||
* @param additionalArrays must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public static byte[][] mergeArrays(byte[] firstArray, byte[]... additionalArrays){
|
||||
Assert.notNull(firstArray, "first array must not be null");
|
||||
Assert.notNull(additionalArrays, "additional arrays must not be null");
|
||||
|
||||
byte[][] result = new byte[additionalArrays.length + 1][];
|
||||
result[0] = firstArray;
|
||||
System.arraycopy(additionalArrays, 0, result, 1, additionalArrays.length);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ import org.springframework.data.redis.core.ScanIteration;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
import org.springframework.data.redis.core.types.RedisClientInfo;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -79,7 +80,7 @@ import redis.clients.jedis.ZParams;
|
||||
* {@link RedisClusterConnection} implementation on top of {@link JedisCluster}.<br/>
|
||||
* Uses the native {@link JedisCluster} api where possible and falls back to direct node communication using
|
||||
* {@link Jedis} where needed.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 1.7
|
||||
@@ -126,7 +127,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
/**
|
||||
* Create new {@link JedisClusterConnection} utilizing native connections via {@link JedisCluster} running commands
|
||||
* across the cluster via given {@link ClusterCommandExecutor}.
|
||||
*
|
||||
*
|
||||
* @param cluster must not be {@literal null}.
|
||||
* @param executor must not be {@literal null}.
|
||||
*/
|
||||
@@ -894,7 +895,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public Long bitOp(BitOperation op, byte[] destination, byte[]... keys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destination, keys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destination, keys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
try {
|
||||
@@ -1332,7 +1333,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public Long sInterStore(byte[] destKey, byte[]... keys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, keys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
try {
|
||||
@@ -1392,7 +1393,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public Long sUnionStore(byte[] destKey, byte[]... keys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, keys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
try {
|
||||
@@ -1455,7 +1456,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public Long sDiffStore(byte[] destKey, byte[]... keys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, keys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
try {
|
||||
@@ -2083,7 +2084,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public Long zUnionStore(byte[] destKey, byte[]... sets) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, sets);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, sets);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
|
||||
@@ -2104,7 +2105,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public Long zUnionStore(byte[] destKey, Aggregate aggregate, int[] weights, byte[]... sets) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, sets);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, sets);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
|
||||
@@ -2123,7 +2124,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public Long zInterStore(byte[] destKey, byte[]... sets) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, sets);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, sets);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
|
||||
@@ -2140,7 +2141,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public Long zInterStore(byte[] destKey, Aggregate aggregate, int[] weights, byte[]... sets) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, sets);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, sets);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
|
||||
@@ -3298,7 +3299,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
@Override
|
||||
public void pfMerge(byte[] destinationKey, byte[]... sourceKeys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destinationKey, sourceKeys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destinationKey, sourceKeys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
try {
|
||||
@@ -3592,6 +3593,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisClusterCommands#clusterGetMasterSlaveMap()
|
||||
*/
|
||||
@Override
|
||||
public Map<RedisClusterNode, Collection<RedisClusterNode>> clusterGetMasterSlaveMap() {
|
||||
|
||||
List<NodeResult<Collection<RedisClusterNode>>> nodeResults = clusterCommandExecutor
|
||||
@@ -3753,7 +3755,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
|
||||
/**
|
||||
* {@link Jedis} specific {@link ClusterCommandCallback}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
* @since 1.7
|
||||
@@ -3771,7 +3773,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
|
||||
/**
|
||||
* Jedis specific implementation of {@link ClusterNodeResourceProvider}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
@@ -3781,7 +3783,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
|
||||
/**
|
||||
* Creates new {@link JedisClusterNodeResourceProvider}.
|
||||
*
|
||||
*
|
||||
* @param cluster must not be {@literal null}.
|
||||
*/
|
||||
public JedisClusterNodeResourceProvider(JedisCluster cluster) {
|
||||
@@ -3829,20 +3831,20 @@ public class JedisClusterConnection implements RedisClusterConnection {
|
||||
|
||||
/**
|
||||
* Jedis specific implementation of {@link ClusterTopologyProvider}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
static class JedisClusterTopologyProvider implements ClusterTopologyProvider {
|
||||
|
||||
private Object lock = new Object();
|
||||
private final Object lock = new Object();
|
||||
private final JedisCluster cluster;
|
||||
private long time = 0;
|
||||
private ClusterTopology cached;
|
||||
|
||||
/**
|
||||
* Create new {@link JedisClusterTopologyProvider}.s
|
||||
*
|
||||
*
|
||||
* @param cluster
|
||||
*/
|
||||
public JedisClusterTopologyProvider(JedisCluster cluster) {
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.springframework.data.redis.connection.util.ByteArraySet;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.data.redis.core.types.RedisClientInfo;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -102,7 +103,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
/**
|
||||
* Creates new {@link LettuceClusterConnection} using {@link RedisClusterClient} running commands across the cluster
|
||||
* via given {@link ClusterCommandExecutor}.
|
||||
*
|
||||
*
|
||||
* @param clusterClient must not be {@literal null}.
|
||||
* @param executor must not be {@literal null}.
|
||||
*/
|
||||
@@ -131,6 +132,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceConnection#keys(byte[])
|
||||
*/
|
||||
@Override
|
||||
public Set<byte[]> keys(final byte[] pattern) {
|
||||
|
||||
Assert.notNull(pattern, "Pattern must not be null!");
|
||||
@@ -156,6 +158,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceConnection#flushAll()
|
||||
*/
|
||||
@Override
|
||||
public void flushAll() {
|
||||
|
||||
clusterCommandExecutor.executeCommandOnAllNodes(new LettuceClusterCommandCallback<String>() {
|
||||
@@ -1109,7 +1112,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
@Override
|
||||
public Long sInterStore(byte[] destKey, byte[]... keys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, keys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
return super.sInterStore(destKey, keys);
|
||||
@@ -1161,7 +1164,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
@Override
|
||||
public Long sUnionStore(byte[] destKey, byte[]... keys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, keys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
return super.sUnionStore(destKey, keys);
|
||||
@@ -1216,7 +1219,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
@Override
|
||||
public Long sDiffStore(byte[] destKey, byte[]... keys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destKey, keys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
return super.sDiffStore(destKey, keys);
|
||||
@@ -1276,7 +1279,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
@Override
|
||||
public void pfMerge(byte[] destinationKey, byte[]... sourceKeys) {
|
||||
|
||||
byte[][] allKeys = Converters.mergeArrays(destinationKey, sourceKeys);
|
||||
byte[][] allKeys = ByteUtils.mergeArrays(destinationKey, sourceKeys);
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
|
||||
try {
|
||||
@@ -1318,7 +1321,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceConnection#getConfig(java.lang.String)
|
||||
*/
|
||||
@@ -1538,7 +1541,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
|
||||
/**
|
||||
* Lettuce specific implementation of {@link ClusterCommandCallback}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
* @since 1.7
|
||||
@@ -1548,7 +1551,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
|
||||
/**
|
||||
* Lettuce specific implementation of {@link MultiKeyClusterCommandCallback}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
* @since 1.7
|
||||
@@ -1560,7 +1563,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
|
||||
/**
|
||||
* Lettuce specific implementation of {@link ClusterNodeResourceProvider}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
@@ -1605,7 +1608,7 @@ public class LettuceClusterConnection extends LettuceConnection
|
||||
|
||||
/**
|
||||
* Lettuce specific implementation of {@link ClusterTopologyProvider}.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.redis.core;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.convert.IndexedData;
|
||||
import org.springframework.data.redis.core.convert.RedisConverter;
|
||||
@@ -122,7 +123,7 @@ class IndexWriter {
|
||||
protected void removeKeyFromExistingIndexes(byte[] key, IndexedData indexedData) {
|
||||
|
||||
Assert.notNull(indexedData, "IndexedData must not be null!");
|
||||
Set<byte[]> existingKeys = connection.keys(toBytes(indexedData.getKeySpace() + ":" + indexedData.getIndexName()
|
||||
Set<byte[]> existingKeys = connection.keys(toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName()
|
||||
+ ":*"));
|
||||
|
||||
if (!CollectionUtils.isEmpty(existingKeys)) {
|
||||
@@ -158,12 +159,12 @@ class IndexWriter {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] indexKey = toBytes(indexedData.getKeySpace() + ":" + indexedData.getIndexName() + ":");
|
||||
byte[] indexKey = toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName() + ":");
|
||||
indexKey = ByteUtils.concat(indexKey, toBytes(value));
|
||||
connection.sAdd(indexKey, key);
|
||||
|
||||
// keep track of indexes used for the object
|
||||
connection.sAdd(ByteUtils.concatAll(toBytes(indexedData.getKeySpace() + ":"), key, toBytes(":idx")), indexKey);
|
||||
connection.sAdd(ByteUtils.concatAll(toBytes(indexedData.getKeyspace() + ":"), key, toBytes(":idx")), indexKey);
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Cannot write index data for unknown index type %s",
|
||||
indexedData.getClass()));
|
||||
@@ -180,6 +181,14 @@ class IndexWriter {
|
||||
return (byte[]) source;
|
||||
}
|
||||
|
||||
return converter.getConversionService().convert(source, byte[].class);
|
||||
if (converter.getConversionService().canConvert(source.getClass(), byte[].class)) {
|
||||
return converter.getConversionService().convert(source, byte[].class);
|
||||
}
|
||||
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String
|
||||
.format(
|
||||
"Cannot convert %s to binary representation for index key generation. Are you missing a Converter? Did you register a non PathBasedRedisIndexDefinition that might apply to a complex type?",
|
||||
source.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,12 +40,12 @@ import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.convert.CustomConversions;
|
||||
import org.springframework.data.redis.core.convert.IndexResolverImpl;
|
||||
import org.springframework.data.redis.core.convert.KeyspaceConfiguration;
|
||||
import org.springframework.data.redis.core.convert.MappingRedisConverter;
|
||||
import org.springframework.data.redis.core.convert.PathIndexResolver;
|
||||
import org.springframework.data.redis.core.convert.RedisConverter;
|
||||
import org.springframework.data.redis.core.convert.RedisData;
|
||||
import org.springframework.data.redis.core.convert.ReferenceResolver;
|
||||
import org.springframework.data.redis.core.convert.ReferenceResolverImpl;
|
||||
import org.springframework.data.redis.core.mapping.RedisMappingContext;
|
||||
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
@@ -86,8 +86,8 @@ import org.springframework.util.Assert;
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements ApplicationContextAware,
|
||||
ApplicationListener<RedisKeyspaceEvent> {
|
||||
public class RedisKeyValueAdapter extends AbstractKeyValueAdapter
|
||||
implements ApplicationContextAware, ApplicationListener<RedisKeyspaceEvent> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RedisKeyValueAdapter.class);
|
||||
|
||||
@@ -131,8 +131,8 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
Assert.notNull(redisOps, "RedisOperations must not be null!");
|
||||
Assert.notNull(mappingContext, "RedisMappingContext must not be null!");
|
||||
|
||||
MappingRedisConverter mappingConverter = new MappingRedisConverter(mappingContext, new IndexResolverImpl(
|
||||
mappingContext), new ReferenceResolverImpl(this));
|
||||
MappingRedisConverter mappingConverter = new MappingRedisConverter(mappingContext,
|
||||
new PathIndexResolver(mappingContext), new ReferenceResolverImpl(redisOps));
|
||||
mappingConverter.setCustomConversions(customConversions == null ? new CustomConversions() : customConversions);
|
||||
mappingConverter.afterPropertiesSet();
|
||||
|
||||
@@ -173,7 +173,7 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
|
||||
if (rdo.getId() == null) {
|
||||
|
||||
rdo.setId(id);
|
||||
rdo.setId(converter.getConversionService().convert(id, String.class));
|
||||
|
||||
if (!(item instanceof RedisData)) {
|
||||
KeyValuePersistentProperty idProperty = converter.getMappingContext().getPersistentEntity(item.getClass())
|
||||
@@ -249,7 +249,10 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
*/
|
||||
public <T> T get(Serializable id, Serializable keyspace, Class<T> type) {
|
||||
|
||||
final byte[] binId = createKey(keyspace, id);
|
||||
String stringId = asString(id);
|
||||
String stringKeyspace = asString(keyspace);
|
||||
|
||||
final byte[] binId = createKey(stringKeyspace, stringId);
|
||||
|
||||
Map<byte[], byte[]> raw = redisOps.execute(new RedisCallback<Map<byte[], byte[]>>() {
|
||||
|
||||
@@ -260,8 +263,8 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
});
|
||||
|
||||
RedisData data = new RedisData(raw);
|
||||
data.setId(id);
|
||||
data.setKeyspace(keyspace.toString());
|
||||
data.setId(stringId);
|
||||
data.setKeyspace(stringKeyspace);
|
||||
|
||||
return converter.read(type, data);
|
||||
}
|
||||
@@ -287,15 +290,17 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
|
||||
if (o != null) {
|
||||
|
||||
final byte[] keyToDelete = createKey(asString(keyspace), asString(id));
|
||||
|
||||
redisOps.execute(new RedisCallback<Void>() {
|
||||
|
||||
@Override
|
||||
public Void doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
|
||||
connection.del(createKey(keyspace, id));
|
||||
connection.del(keyToDelete);
|
||||
connection.sRem(binKeyspace, binId);
|
||||
|
||||
new IndexWriter(connection, converter).removeKeyFromIndexes(keyspace.toString(), binId);
|
||||
new IndexWriter(connection, converter).removeKeyFromIndexes(asString(keyspace), binId);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@@ -322,7 +327,8 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
Set<byte[]> members = connection.sMembers(binKeyspace);
|
||||
|
||||
for (byte[] id : members) {
|
||||
rawData.add(connection.hGetAll(createKey(binKeyspace, id)));
|
||||
rawData.add(connection
|
||||
.hGetAll(createKey(asString(keyspace), getConverter().getConversionService().convert(id, String.class))));
|
||||
}
|
||||
|
||||
return rawData;
|
||||
@@ -349,7 +355,7 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
public Void doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
|
||||
connection.del(toBytes(keyspace));
|
||||
new IndexWriter(connection, converter).removeAllIndexes(keyspace.toString());
|
||||
new IndexWriter(connection, converter).removeAllIndexes(asString(keyspace));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@@ -404,7 +410,12 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public byte[] createKey(Serializable keyspace, Serializable id) {
|
||||
private String asString(Serializable value) {
|
||||
return value instanceof String ? (String) value
|
||||
: getConverter().getConversionService().convert(value, String.class);
|
||||
}
|
||||
|
||||
public byte[] createKey(String keyspace, String id) {
|
||||
return toBytes(keyspace + ":" + id);
|
||||
}
|
||||
|
||||
@@ -533,8 +544,8 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
|
||||
byte[] key = message.getBody();
|
||||
|
||||
final byte[] phantomKey = ByteUtils.concat(key, converter.getConversionService()
|
||||
.convert(":phantom", byte[].class));
|
||||
final byte[] phantomKey = ByteUtils.concat(key,
|
||||
converter.getConversionService().convert(":phantom", byte[].class));
|
||||
|
||||
Map<byte[], byte[]> hash = ops.execute(new RedisCallback<Map<byte[], byte[]>>() {
|
||||
|
||||
@@ -569,40 +580,4 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter implements App
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ReferenceResolver} using {@link RedisKeyValueAdapter} to read and convert referenced entities.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
static class ReferenceResolverImpl implements ReferenceResolver {
|
||||
|
||||
private RedisKeyValueAdapter adapter;
|
||||
|
||||
ReferenceResolverImpl() {}
|
||||
|
||||
/**
|
||||
* @param adapter must not be {@literal null}.
|
||||
*/
|
||||
public ReferenceResolverImpl(RedisKeyValueAdapter adapter) {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param adapter
|
||||
*/
|
||||
public void setAdapter(RedisKeyValueAdapter adapter) {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.convert.ReferenceResolver#resolveReference(java.io.Serializable, java.io.Serializable, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> T resolveReference(Serializable id, Serializable keyspace, Class<T> type) {
|
||||
return (T) adapter.get(id, keyspace, type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ package org.springframework.data.redis.core;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.keyvalue.core.CriteriaAccessor;
|
||||
@@ -32,6 +32,7 @@ import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.convert.RedisData;
|
||||
import org.springframework.data.redis.repository.query.RedisOperationChain;
|
||||
import org.springframework.data.redis.repository.query.RedisOperationChain.PathAndValue;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
|
||||
/**
|
||||
@@ -65,8 +66,8 @@ class RedisQueryEngine extends QueryEngine<RedisKeyValueAdapter, RedisOperationC
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.QueryEngine#execute(java.lang.Object, java.lang.Object, int, int, java.io.Serializable, java.lang.Class)
|
||||
*/
|
||||
public <T> Collection<T> execute(final RedisOperationChain criteria, final Comparator<?> sort, int offset, int rows,
|
||||
final Serializable keyspace, Class<T> type) {
|
||||
public <T> Collection<T> execute(final RedisOperationChain criteria, final Comparator<?> sort, final int offset,
|
||||
final int rows, final Serializable keyspace, Class<T> type) {
|
||||
|
||||
RedisCallback<Map<byte[], Map<byte[], byte[]>>> callback = new RedisCallback<Map<byte[], Map<byte[], byte[]>>>() {
|
||||
|
||||
@@ -81,12 +82,25 @@ class RedisQueryEngine extends QueryEngine<RedisKeyValueAdapter, RedisOperationC
|
||||
i++;
|
||||
}
|
||||
|
||||
Set<byte[]> allKeys = connection.sInter(keys);
|
||||
List<byte[]> allKeys = new ArrayList<byte[]>();
|
||||
if (!criteria.getSismember().isEmpty()) {
|
||||
allKeys.addAll(connection.sInter(keys(keyspace + ":", criteria.getSismember())));
|
||||
}
|
||||
if (!criteria.getOrSismember().isEmpty()) {
|
||||
allKeys.addAll(connection.sUnion(keys(keyspace + ":", criteria.getOrSismember())));
|
||||
}
|
||||
|
||||
byte[] keyspaceBin = getAdapter().getConverter().getConversionService().convert(keyspace + ":", byte[].class);
|
||||
|
||||
final Map<byte[], Map<byte[], byte[]>> rawData = new LinkedHashMap<byte[], Map<byte[], byte[]>>();
|
||||
|
||||
if (allKeys.size() == 0 || allKeys.size() < offset) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
if (offset >= 0 && rows > 0) {
|
||||
allKeys = allKeys.subList(Math.max(0, offset), Math.min(offset + rows, allKeys.size()));
|
||||
}
|
||||
for (byte[] id : allKeys) {
|
||||
|
||||
byte[] singleKey = ByteUtils.concat(keyspaceBin, id);
|
||||
@@ -150,6 +164,23 @@ class RedisQueryEngine extends QueryEngine<RedisKeyValueAdapter, RedisOperationC
|
||||
});
|
||||
}
|
||||
|
||||
private byte[][] keys(String prefix, Collection<PathAndValue> source) {
|
||||
|
||||
byte[][] keys = new byte[source.size()][];
|
||||
int i = 0;
|
||||
for (PathAndValue pathAndValue : source) {
|
||||
|
||||
byte[] convertedValue = getAdapter().getConverter().getConversionService()
|
||||
.convert(pathAndValue.getFirstValue(), byte[].class);
|
||||
byte[] fullPath = getAdapter().getConverter().getConversionService()
|
||||
.convert(prefix + pathAndValue.getPath() + ":", byte[].class);
|
||||
|
||||
keys[i] = ByteUtils.concat(fullPath, convertedValue);
|
||||
i++;
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Composite {@link IndexResolver} implementation that iterates over a given collection of delegate
|
||||
* {@link IndexResolver} instances. <br />
|
||||
* <br />
|
||||
* <strong>NOTE</strong> {@link IndexedData} created by an {@link IndexResolver} can be overwritten by subsequent
|
||||
* {@link IndexResolver}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class CompositeIndexResolver implements IndexResolver {
|
||||
|
||||
private final List<IndexResolver> resolvers;
|
||||
|
||||
/**
|
||||
* Create new {@link CompositeIndexResolver}.
|
||||
*
|
||||
* @param resolvers must not be {@literal null}.
|
||||
*/
|
||||
public CompositeIndexResolver(Collection<IndexResolver> resolvers) {
|
||||
|
||||
Assert.notNull(resolvers, "Resolvers must not be null!");
|
||||
if (CollectionUtils.contains(resolvers.iterator(), null)) {
|
||||
throw new IllegalArgumentException("Resolvers must no contain null values");
|
||||
}
|
||||
this.resolvers = new ArrayList<IndexResolver>(resolvers);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.convert.IndexResolver#resolveIndexesFor(org.springframework.data.util.TypeInformation, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Set<IndexedData> resolveIndexesFor(TypeInformation<?> typeInformation, Object value) {
|
||||
|
||||
if (resolvers.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<IndexedData> data = new LinkedHashSet<IndexedData>();
|
||||
for (IndexResolver resolver : resolvers) {
|
||||
data.addAll(resolver.resolveIndexesFor(typeInformation, value));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,6 @@ public interface IndexedData {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getKeySpace();
|
||||
String getKeyspace();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core.convert;
|
||||
|
||||
import org.springframework.data.redis.core.index.IndexConfiguration;
|
||||
import org.springframework.data.redis.core.index.ConfigurableIndexDefinitionProvider;
|
||||
|
||||
/**
|
||||
* {@link MappingConfiguration} is used for programmatic configuration of secondary indexes, key prefixes, expirations
|
||||
@@ -26,7 +26,7 @@ import org.springframework.data.redis.core.index.IndexConfiguration;
|
||||
*/
|
||||
public class MappingConfiguration {
|
||||
|
||||
private final IndexConfiguration indexConfiguration;
|
||||
private final ConfigurableIndexDefinitionProvider indexConfiguration;
|
||||
private final KeyspaceConfiguration keyspaceConfiguration;
|
||||
|
||||
/**
|
||||
@@ -35,7 +35,8 @@ public class MappingConfiguration {
|
||||
* @param indexConfiguration must not be {@literal null}.
|
||||
* @param keyspaceConfiguration must not be {@literal null}.
|
||||
*/
|
||||
public MappingConfiguration(IndexConfiguration indexConfiguration, KeyspaceConfiguration keyspaceConfiguration) {
|
||||
public MappingConfiguration(ConfigurableIndexDefinitionProvider indexConfiguration,
|
||||
KeyspaceConfiguration keyspaceConfiguration) {
|
||||
|
||||
this.indexConfiguration = indexConfiguration;
|
||||
this.keyspaceConfiguration = keyspaceConfiguration;
|
||||
@@ -44,7 +45,7 @@ public class MappingConfiguration {
|
||||
/**
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public IndexConfiguration getIndexConfiguration() {
|
||||
public ConfigurableIndexDefinitionProvider getIndexConfiguration() {
|
||||
return indexConfiguration;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core.convert;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -138,7 +137,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
|
||||
this.referenceResolver = referenceResolver;
|
||||
|
||||
this.indexResolver = indexResolver != null ? indexResolver : new IndexResolverImpl(this.mappingContext);
|
||||
this.indexResolver = indexResolver != null ? indexResolver : new PathIndexResolver(this.mappingContext);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -210,35 +209,29 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
if (persistentProperty.isMap()) {
|
||||
|
||||
if (conversionService.canConvert(byte[].class, persistentProperty.getMapValueType())) {
|
||||
accessor.setProperty(
|
||||
persistentProperty,
|
||||
readMapOfSimpleTypes(currentPath, persistentProperty.getType(), persistentProperty.getComponentType(),
|
||||
persistentProperty.getMapValueType(), source));
|
||||
accessor.setProperty(persistentProperty, readMapOfSimpleTypes(currentPath, persistentProperty.getType(),
|
||||
persistentProperty.getComponentType(), persistentProperty.getMapValueType(), source));
|
||||
} else {
|
||||
accessor.setProperty(
|
||||
persistentProperty,
|
||||
readMapOfComplexTypes(currentPath, persistentProperty.getType(), persistentProperty.getComponentType(),
|
||||
persistentProperty.getMapValueType(), source));
|
||||
accessor.setProperty(persistentProperty, readMapOfComplexTypes(currentPath, persistentProperty.getType(),
|
||||
persistentProperty.getComponentType(), persistentProperty.getMapValueType(), source));
|
||||
}
|
||||
}
|
||||
|
||||
else if (persistentProperty.isCollectionLike()) {
|
||||
|
||||
if (conversionService.canConvert(byte[].class, persistentProperty.getComponentType())) {
|
||||
accessor.setProperty(
|
||||
persistentProperty,
|
||||
readCollectionOfSimpleTypes(currentPath, persistentProperty.getType(), persistentProperty
|
||||
.getTypeInformation().getComponentType().getActualType().getType(), source));
|
||||
accessor.setProperty(persistentProperty,
|
||||
readCollectionOfSimpleTypes(currentPath, persistentProperty.getType(),
|
||||
persistentProperty.getTypeInformation().getComponentType().getActualType().getType(), source));
|
||||
} else {
|
||||
accessor.setProperty(
|
||||
persistentProperty,
|
||||
readCollectionOfComplexTypes(currentPath, persistentProperty.getType(), persistentProperty
|
||||
.getTypeInformation().getComponentType().getActualType().getType(), source.getBucket()));
|
||||
accessor.setProperty(persistentProperty,
|
||||
readCollectionOfComplexTypes(currentPath, persistentProperty.getType(),
|
||||
persistentProperty.getTypeInformation().getComponentType().getActualType().getType(),
|
||||
source.getBucket()));
|
||||
}
|
||||
|
||||
} else if (persistentProperty.isEntity()
|
||||
&& !conversionService.canConvert(byte[].class, persistentProperty.getTypeInformation().getActualType()
|
||||
.getType())) {
|
||||
} else if (persistentProperty.isEntity() && !conversionService.canConvert(byte[].class,
|
||||
persistentProperty.getTypeInformation().getActualType().getType())) {
|
||||
|
||||
Class<?> targetType = persistentProperty.getTypeInformation().getActualType().getType();
|
||||
|
||||
@@ -284,8 +277,8 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
@Override
|
||||
public void doWithAssociation(Association<KeyValuePersistentProperty> association) {
|
||||
|
||||
String currentPath = !path.isEmpty() ? path + "." + association.getInverse().getName() : association
|
||||
.getInverse().getName();
|
||||
String currentPath = !path.isEmpty() ? path + "." + association.getInverse().getName()
|
||||
: association.getInverse().getName();
|
||||
|
||||
if (association.getInverse().isCollectionLike()) {
|
||||
|
||||
@@ -299,11 +292,10 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
String referenceKey = fromBytes(entry.getValue(), String.class);
|
||||
String[] args = referenceKey.split(":");
|
||||
|
||||
Object loadedObject = referenceResolver.resolveReference(args[1], args[0], association.getInverse()
|
||||
.getActualType());
|
||||
Map<byte[], byte[]> rawHash = referenceResolver.resolveReference(args[1], args[0]);
|
||||
|
||||
if (loadedObject != null) {
|
||||
target.add(loadedObject);
|
||||
if (!CollectionUtils.isEmpty(rawHash)) {
|
||||
target.add(read(association.getInverse().getActualType(), new RedisData(rawHash)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,11 +311,12 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
String key = fromBytes(binKey, String.class);
|
||||
|
||||
String[] args = key.split(":");
|
||||
Object loadedObject = referenceResolver.resolveReference(args[1], args[0], association.getInverse()
|
||||
.getActualType());
|
||||
|
||||
if (loadedObject != null) {
|
||||
accessor.setProperty(association.getInverse(), loadedObject);
|
||||
Map<byte[], byte[]> rawHash = referenceResolver.resolveReference(args[1], args[0]);
|
||||
|
||||
if (!CollectionUtils.isEmpty(rawHash)) {
|
||||
accessor.setProperty(association.getInverse(),
|
||||
read(association.getInverse().getActualType(), new RedisData(rawHash)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,7 +338,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
sink.setKeyspace(entity.getKeySpace());
|
||||
|
||||
writeInternal(entity.getKeySpace(), "", source, entity.getTypeInformation(), sink);
|
||||
sink.setId((Serializable) entity.getIdentifierAccessor(source).getIdentifier());
|
||||
sink.setId(getConversionService().convert(entity.getIdentifierAccessor(source).getIdentifier(), String.class));
|
||||
|
||||
Long ttl = entity.getTimeToLiveAccessor().getTimeToLive(source);
|
||||
if (ttl != null && ttl > 0) {
|
||||
@@ -748,8 +741,8 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
private static class ConverterAwareParameterValueProvider implements
|
||||
PropertyValueProvider<KeyValuePersistentProperty> {
|
||||
private static class ConverterAwareParameterValueProvider
|
||||
implements PropertyValueProvider<KeyValuePersistentProperty> {
|
||||
|
||||
private final RedisData source;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,9 +25,13 @@ import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.redis.core.index.ConfigurableIndexDefinitionProvider;
|
||||
import org.springframework.data.redis.core.index.IndexConfiguration;
|
||||
import org.springframework.data.redis.core.index.IndexConfiguration.RedisIndexSetting;
|
||||
import org.springframework.data.redis.core.index.IndexDefinition;
|
||||
import org.springframework.data.redis.core.index.IndexDefinition.Condition;
|
||||
import org.springframework.data.redis.core.index.IndexDefinition.IndexingContext;
|
||||
import org.springframework.data.redis.core.index.Indexed;
|
||||
import org.springframework.data.redis.core.index.SimpleIndexDefinition;
|
||||
import org.springframework.data.redis.core.mapping.RedisMappingContext;
|
||||
import org.springframework.data.redis.core.mapping.RedisPersistentEntity;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
@@ -41,24 +45,24 @@ import org.springframework.util.Assert;
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class IndexResolverImpl implements IndexResolver {
|
||||
public class PathIndexResolver implements IndexResolver {
|
||||
|
||||
private IndexConfiguration indexConfiguration;
|
||||
private ConfigurableIndexDefinitionProvider indexConfiguration;
|
||||
private RedisMappingContext mappingContext;
|
||||
|
||||
/**
|
||||
* Creates new {@link IndexResolverImpl} with empty {@link IndexConfiguration}.
|
||||
* Creates new {@link PathIndexResolver} with empty {@link IndexConfiguration}.
|
||||
*/
|
||||
public IndexResolverImpl() {
|
||||
public PathIndexResolver() {
|
||||
this(new RedisMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IndexResolverImpl} with given {@link IndexConfiguration}.
|
||||
* Creates new {@link PathIndexResolver} with given {@link IndexConfiguration}.
|
||||
*
|
||||
* @param mapppingContext must not be {@literal null}.
|
||||
*/
|
||||
public IndexResolverImpl(RedisMappingContext mappingContext) {
|
||||
public PathIndexResolver(RedisMappingContext mappingContext) {
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
this.mappingContext = mappingContext;
|
||||
@@ -71,21 +75,16 @@ public class IndexResolverImpl implements IndexResolver {
|
||||
*/
|
||||
public Set<IndexedData> resolveIndexesFor(TypeInformation<?> typeInformation, Object value) {
|
||||
return doResolveIndexesFor(mappingContext.getPersistentEntity(typeInformation).getKeySpace(), "", typeInformation,
|
||||
value);
|
||||
null, value);
|
||||
}
|
||||
|
||||
private Set<IndexedData> doResolveIndexesFor(final String keyspace, final String path,
|
||||
TypeInformation<?> typeInformation, Object value) {
|
||||
TypeInformation<?> typeInformation, PersistentProperty<?> fallback, Object value) {
|
||||
|
||||
RedisPersistentEntity<?> entity = mappingContext.getPersistentEntity(typeInformation);
|
||||
|
||||
if (entity == null) {
|
||||
|
||||
IndexedData index = resolveIndex(keyspace, path, null, value);
|
||||
if (index != null) {
|
||||
return Collections.singleton(index);
|
||||
}
|
||||
return Collections.emptySet();
|
||||
return resolveIndex(keyspace, path, fallback, value);
|
||||
}
|
||||
|
||||
final PersistentPropertyAccessor accessor = entity.getPropertyAccessor(value);
|
||||
@@ -102,14 +101,9 @@ public class IndexResolverImpl implements IndexResolver {
|
||||
|
||||
if (propertyValue != null) {
|
||||
|
||||
TypeInformation<?> typeHint = persistentProperty.isMap() ? persistentProperty.getTypeInformation()
|
||||
.getMapValueType() : persistentProperty.getTypeInformation().getActualType();
|
||||
|
||||
IndexedData index = resolveIndex(keyspace, currentPath, persistentProperty, propertyValue);
|
||||
|
||||
if (index != null) {
|
||||
indexes.add(index);
|
||||
}
|
||||
TypeInformation<?> typeHint = persistentProperty.isMap()
|
||||
? persistentProperty.getTypeInformation().getMapValueType()
|
||||
: persistentProperty.getTypeInformation().getActualType();
|
||||
|
||||
if (persistentProperty.isMap()) {
|
||||
|
||||
@@ -117,7 +111,7 @@ public class IndexResolverImpl implements IndexResolver {
|
||||
|
||||
TypeInformation<?> typeToUse = updateTypeHintForActualValue(typeHint, entry.getValue());
|
||||
indexes.addAll(doResolveIndexesFor(keyspace, currentPath + "." + entry.getKey(),
|
||||
typeToUse.getActualType(), entry.getValue()));
|
||||
typeToUse.getActualType(), persistentProperty, entry.getValue()));
|
||||
}
|
||||
|
||||
} else if (persistentProperty.isCollectionLike()) {
|
||||
@@ -125,7 +119,8 @@ public class IndexResolverImpl implements IndexResolver {
|
||||
for (Object listValue : (Iterable<?>) propertyValue) {
|
||||
|
||||
TypeInformation<?> typeToUse = updateTypeHintForActualValue(typeHint, listValue);
|
||||
indexes.addAll(doResolveIndexesFor(keyspace, currentPath, typeToUse.getActualType(), listValue));
|
||||
indexes.addAll(
|
||||
doResolveIndexesFor(keyspace, currentPath, typeToUse.getActualType(), persistentProperty, listValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +128,10 @@ public class IndexResolverImpl implements IndexResolver {
|
||||
|| persistentProperty.getTypeInformation().getActualType().equals(ClassTypeInformation.OBJECT)) {
|
||||
|
||||
typeHint = updateTypeHintForActualValue(typeHint, propertyValue);
|
||||
indexes.addAll(doResolveIndexesFor(keyspace, currentPath, typeHint.getActualType(), propertyValue));
|
||||
indexes.addAll(doResolveIndexesFor(keyspace, currentPath, typeHint.getActualType(), persistentProperty,
|
||||
propertyValue));
|
||||
} else {
|
||||
indexes.addAll(resolveIndex(keyspace, currentPath, persistentProperty, propertyValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,37 +155,55 @@ public class IndexResolverImpl implements IndexResolver {
|
||||
return indexes;
|
||||
}
|
||||
|
||||
protected IndexedData resolveIndex(String keyspace, String propertyPath, PersistentProperty<?> property, Object value) {
|
||||
protected Set<IndexedData> resolveIndex(String keyspace, String propertyPath, PersistentProperty<?> property,
|
||||
Object value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
String path = normalizeIndexPath(propertyPath, property);
|
||||
|
||||
Set<IndexedData> data = new LinkedHashSet<IndexedData>();
|
||||
|
||||
if (indexConfiguration.hasIndexFor(keyspace, path)) {
|
||||
// FIXME it seems there is a mis-match between IndexConfiguration
|
||||
// resolving many RedisIndexSetting objects to resolving a single
|
||||
// IndexData in this method.
|
||||
RedisIndexSetting indexSetting = indexConfiguration.getIndexDefinitionsFor(keyspace, path).iterator().next();
|
||||
return new SimpleIndexedPropertyValue(keyspace, indexSetting.getIndexName(), value);
|
||||
|
||||
IndexingContext context = new IndexingContext(keyspace, path,
|
||||
property != null ? property.getTypeInformation() : ClassTypeInformation.OBJECT);
|
||||
|
||||
for (IndexDefinition indexDefinition : indexConfiguration.getIndexDefinitionsFor(keyspace, path)) {
|
||||
|
||||
if (!verifyConditions(indexDefinition.getConditions(), value, context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data.add(new SimpleIndexedPropertyValue(keyspace, indexDefinition.getIndexName(),
|
||||
indexDefinition.valueTransformer().convert(value)));
|
||||
}
|
||||
}
|
||||
|
||||
else if (property != null && property.isAnnotationPresent(Indexed.class)) {
|
||||
|
||||
Indexed indexed = property.findAnnotation(Indexed.class);
|
||||
SimpleIndexDefinition indexDefinition = new SimpleIndexDefinition(keyspace, path);
|
||||
indexConfiguration.addIndexDefinition(indexDefinition);
|
||||
|
||||
indexConfiguration.addIndexDefinition(new RedisIndexSetting(keyspace, path, indexed.type()));
|
||||
data.add(new SimpleIndexedPropertyValue(keyspace, path, indexDefinition.valueTransformer().convert(value)));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
switch (indexed.type()) {
|
||||
case SIMPLE:
|
||||
return new SimpleIndexedPropertyValue(keyspace, path, value);
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Unsupported index type '%s' for path '%s'.",
|
||||
indexed.type(), path));
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private boolean verifyConditions(Iterable<Condition<?>> conditions, Object value, IndexingContext context) {
|
||||
|
||||
for (Condition condition : conditions) {
|
||||
|
||||
// TODO: generics lookup
|
||||
if (!condition.matches(value, context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String normalizeIndexPath(String path, PersistentProperty<?> property) {
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core.convert;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -34,7 +33,7 @@ import org.springframework.util.Assert;
|
||||
public class RedisData {
|
||||
|
||||
private String keyspace;
|
||||
private Serializable id;
|
||||
private String id;
|
||||
|
||||
private Bucket bucket;
|
||||
private Set<IndexedData> indexedData;
|
||||
@@ -74,14 +73,14 @@ public class RedisData {
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
public void setId(Serializable id) {
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Serializable getId() {
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.redis.core.convert;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.annotation.Reference;
|
||||
|
||||
@@ -30,8 +31,7 @@ public interface ReferenceResolver {
|
||||
/**
|
||||
* @param id must not be {@literal null}.
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return {@literal null} if referenced object does not exist.
|
||||
*/
|
||||
<T> T resolveReference(Serializable id, Serializable keyspace, Class<T> type);
|
||||
Map<byte[], byte[]> resolveReference(Serializable id, String keyspace);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.convert;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.RedisKeyValueAdapter;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.convert.BinaryConverters.StringToBytesConverter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ReferenceResolver} using {@link RedisKeyValueAdapter} to read raw data.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class ReferenceResolverImpl implements ReferenceResolver {
|
||||
|
||||
private final RedisOperations<?, ?> redisOps;
|
||||
private final StringToBytesConverter converter;
|
||||
|
||||
/**
|
||||
* @param redisOperations must not be {@literal null}.
|
||||
*/
|
||||
public ReferenceResolverImpl(RedisOperations<?, ?> redisOperations) {
|
||||
|
||||
Assert.notNull(redisOperations);
|
||||
|
||||
this.redisOps = redisOperations;
|
||||
this.converter = new StringToBytesConverter();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.convert.ReferenceResolver#resolveReference(java.io.Serializable, java.io.Serializable, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Map<byte[], byte[]> resolveReference(Serializable id, String keyspace) {
|
||||
|
||||
final byte[] key = converter.convert(keyspace + ":" + id);
|
||||
|
||||
return redisOps.execute(new RedisCallback<Map<byte[], byte[]>>() {
|
||||
|
||||
@Override
|
||||
public Map<byte[], byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
return connection.hGetAll(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class SimpleIndexedPropertyValue implements IndexedData {
|
||||
* @see org.springframework.data.redis.core.convert.IndexedData#getKeySpace()
|
||||
*/
|
||||
@Override
|
||||
public String getKeySpace() {
|
||||
public String getKeyspace() {
|
||||
return this.keyspace;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,16 @@
|
||||
package org.springframework.data.redis.core.convert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity;
|
||||
import org.springframework.data.redis.core.index.IndexConfiguration;
|
||||
import org.springframework.data.redis.core.index.IndexConfiguration.RedisIndexSetting;
|
||||
import org.springframework.data.redis.core.index.ConfigurableIndexDefinitionProvider;
|
||||
import org.springframework.data.redis.core.index.IndexDefinition;
|
||||
import org.springframework.data.redis.core.index.SpelIndexDefinition;
|
||||
import org.springframework.data.redis.core.mapping.RedisMappingContext;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
@@ -35,16 +38,19 @@ import org.springframework.util.Assert;
|
||||
* An {@link IndexResolver} that resolves {@link IndexedData} using a {@link SpelExpressionParser}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class SpelIndexResolver implements IndexResolver {
|
||||
|
||||
private final IndexConfiguration settings;
|
||||
private final ConfigurableIndexDefinitionProvider settings;
|
||||
private final SpelExpressionParser parser;
|
||||
private final RedisMappingContext mappingContext;
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
private Map<SpelIndexDefinition, Expression> expressionCache;
|
||||
|
||||
/**
|
||||
* Creates a new instance using a default {@link SpelExpressionParser}.
|
||||
*
|
||||
@@ -66,6 +72,7 @@ public class SpelIndexResolver implements IndexResolver {
|
||||
Assert.notNull(parser, "SpelExpressionParser must not be null!");
|
||||
this.mappingContext = mappingContext;
|
||||
this.settings = mappingContext.getMappingConfiguration().getIndexConfiguration();
|
||||
this.expressionCache = new HashMap<SpelIndexDefinition, Expression>();
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@@ -88,26 +95,41 @@ public class SpelIndexResolver implements IndexResolver {
|
||||
|
||||
Set<IndexedData> indexes = new HashSet<IndexedData>();
|
||||
|
||||
for (RedisIndexSetting setting : settings.getIndexDefinitionsFor(keyspace)) {
|
||||
for (IndexDefinition setting : settings.getIndexDefinitionsFor(keyspace)) {
|
||||
|
||||
Expression expression = parser.parseExpression(setting.getPath());
|
||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
context.setRootObject(value);
|
||||
context.setVariable("this", value);
|
||||
if (setting instanceof SpelIndexDefinition) {
|
||||
|
||||
if (beanResolver != null) {
|
||||
context.setBeanResolver(beanResolver);
|
||||
}
|
||||
Expression expression = getAndCacheIfAbsent((SpelIndexDefinition) setting);
|
||||
|
||||
Object index = expression.getValue(context);
|
||||
if (index != null) {
|
||||
indexes.add(new SimpleIndexedPropertyValue(keyspace, setting.getIndexName(), index));
|
||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
context.setRootObject(value);
|
||||
context.setVariable("this", value);
|
||||
|
||||
if (beanResolver != null) {
|
||||
context.setBeanResolver(beanResolver);
|
||||
}
|
||||
|
||||
Object index = expression.getValue(context);
|
||||
if (index != null) {
|
||||
indexes.add(new SimpleIndexedPropertyValue(keyspace, setting.getIndexName(), index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
private Expression getAndCacheIfAbsent(SpelIndexDefinition indexDefinition) {
|
||||
|
||||
if (expressionCache.containsKey(indexDefinition)) {
|
||||
return expressionCache.get(indexDefinition);
|
||||
}
|
||||
|
||||
Expression expression = parser.parseExpression(indexDefinition.getExpression());
|
||||
expressionCache.put(indexDefinition, expression);
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting the BeanResolver
|
||||
*
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
/**
|
||||
* {@link IndexDefinitionProvider} that allows registering new {@link IndexDefinition} via
|
||||
* {@link IndexDefinitionRegistry}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public interface ConfigurableIndexDefinitionProvider extends IndexDefinitionProvider, IndexDefinitionRegistry {
|
||||
|
||||
}
|
||||
@@ -16,13 +16,13 @@
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -33,70 +33,55 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Rob Winch
|
||||
* @since 1.7
|
||||
*/
|
||||
public class IndexConfiguration {
|
||||
public class IndexConfiguration implements ConfigurableIndexDefinitionProvider {
|
||||
|
||||
private final Set<RedisIndexSetting> definitions;
|
||||
private final Set<IndexDefinition> definitions;
|
||||
|
||||
/**
|
||||
* Creates new empty {@link IndexConfiguration}.
|
||||
*/
|
||||
public IndexConfiguration() {
|
||||
|
||||
this.definitions = new CopyOnWriteArraySet<RedisIndexSetting>();
|
||||
for (RedisIndexSetting initial : initialConfiguration()) {
|
||||
this.definitions = new CopyOnWriteArraySet<IndexDefinition>();
|
||||
for (IndexDefinition initial : initialConfiguration()) {
|
||||
addIndexDefinition(initial);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an index is defined for a given keyspace and property path.
|
||||
*
|
||||
* @param keyspace
|
||||
* @param path
|
||||
* @return true if index is defined.
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinitionProvider#hasIndexFor(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public boolean hasIndexFor(Serializable keyspace) {
|
||||
return !getIndexDefinitionsFor(keyspace).isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinitionProvider#hasIndexFor(java.io.Serializable, java.lang.String)
|
||||
*/
|
||||
public boolean hasIndexFor(Serializable keyspace, String path) {
|
||||
|
||||
for (IndexType type : IndexType.values()) {
|
||||
RedisIndexSetting def = getIndexDefinition(keyspace, path, type);
|
||||
if (def != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return !getIndexDefinitionsFor(keyspace, path).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of {@link RedisIndexSetting} for a given keyspace and property path.
|
||||
*
|
||||
* @param keyspace
|
||||
* @param path
|
||||
* @return never {@literal null}.
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinitionProvider#getIndexDefinitionsFor(java.io.Serializable, java.lang.String)
|
||||
*/
|
||||
public List<RedisIndexSetting> getIndexDefinitionsFor(Serializable keyspace, String path) {
|
||||
|
||||
List<RedisIndexSetting> indexDefinitions = new ArrayList<RedisIndexSetting>();
|
||||
for (IndexType type : IndexType.values()) {
|
||||
RedisIndexSetting def = getIndexDefinition(keyspace, path, type);
|
||||
if (def != null) {
|
||||
indexDefinitions.add(def);
|
||||
}
|
||||
}
|
||||
|
||||
return indexDefinitions;
|
||||
public Set<IndexDefinition> getIndexDefinitionsFor(Serializable keyspace, String path) {
|
||||
return getIndexDefinitions(keyspace, path, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the {@link RedisIndexSetting} for a given keyspace.
|
||||
*
|
||||
* @param keyspace the keyspace to get
|
||||
* @return never {@literal null}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinitionProvider#getIndexDefinitionsFor(java.io.Serializable)
|
||||
*/
|
||||
public List<RedisIndexSetting> getIndexDefinitionsFor(Serializable keyspace) {
|
||||
public Set<IndexDefinition> getIndexDefinitionsFor(Serializable keyspace) {
|
||||
|
||||
List<RedisIndexSetting> indexDefinitions = new ArrayList<RedisIndexSetting>();
|
||||
Set<IndexDefinition> indexDefinitions = new LinkedHashSet<IndexDefinition>();
|
||||
|
||||
for (RedisIndexSetting indexDef : definitions) {
|
||||
for (IndexDefinition indexDef : definitions) {
|
||||
if (indexDef.getKeyspace().equals(keyspace)) {
|
||||
indexDefinitions.add(indexDef);
|
||||
}
|
||||
@@ -105,26 +90,33 @@ public class IndexConfiguration {
|
||||
return indexDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given {@link RedisIndexSetting}.
|
||||
*
|
||||
* @param indexDefinition must not be {@literal null}.
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinitionRegistry#addIndexDefinition(org.springframework.data.redis.core.index.IndexDefinition)
|
||||
*/
|
||||
public void addIndexDefinition(RedisIndexSetting indexDefinition) {
|
||||
public void addIndexDefinition(IndexDefinition indexDefinition) {
|
||||
|
||||
Assert.notNull(indexDefinition, "RedisIndexDefinition must not be null in order to be added.");
|
||||
this.definitions.add(indexDefinition);
|
||||
}
|
||||
|
||||
private RedisIndexSetting getIndexDefinition(Serializable keyspace, String path, IndexType type) {
|
||||
private Set<IndexDefinition> getIndexDefinitions(Serializable keyspace, String path, Class<?> type) {
|
||||
|
||||
for (RedisIndexSetting indexDef : definitions) {
|
||||
if (indexDef.getKeyspace().equals(keyspace) && indexDef.getPath().equals(path) && indexDef.getType().equals(type)) {
|
||||
return indexDef;
|
||||
Set<IndexDefinition> def = new LinkedHashSet<IndexDefinition>();
|
||||
for (IndexDefinition indexDef : definitions) {
|
||||
if (ClassUtils.isAssignable(type, indexDef.getClass()) && indexDef.getKeyspace().equals(keyspace)) {
|
||||
|
||||
if (indexDef instanceof PathBasedRedisIndexDefinition) {
|
||||
if (ObjectUtils.nullSafeEquals(((PathBasedRedisIndexDefinition) indexDef).getPath(), path)) {
|
||||
def.add(indexDef);
|
||||
}
|
||||
} else {
|
||||
def.add(indexDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,93 +124,8 @@ public class IndexConfiguration {
|
||||
*
|
||||
* @return must not return {@literal null}.
|
||||
*/
|
||||
protected Iterable<RedisIndexSetting> initialConfiguration() {
|
||||
protected Iterable<? extends IndexDefinition> initialConfiguration() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Rob Winch
|
||||
* @since 1.7
|
||||
*/
|
||||
public static class RedisIndexSetting {
|
||||
|
||||
private final Serializable keyspace;
|
||||
private final String path;
|
||||
private final String indexName;
|
||||
private final IndexType type;
|
||||
|
||||
public RedisIndexSetting(Serializable keyspace, String path) {
|
||||
this(keyspace, path, null);
|
||||
}
|
||||
|
||||
public RedisIndexSetting(Serializable keyspace, String path, IndexType type) {
|
||||
this(keyspace, path, path, type);
|
||||
}
|
||||
|
||||
public RedisIndexSetting(Serializable keyspace, String path, String indexName, IndexType type) {
|
||||
this.keyspace = keyspace;
|
||||
this.path = path;
|
||||
this.indexName = indexName;
|
||||
this.type = type == null ? IndexType.SIMPLE : type;
|
||||
}
|
||||
|
||||
public String getIndexName() {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
public Serializable getKeyspace() {
|
||||
return keyspace;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public IndexType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = ObjectUtils.nullSafeHashCode(keyspace);
|
||||
result += ObjectUtils.nullSafeHashCode(path);
|
||||
result += ObjectUtils.nullSafeHashCode(indexName);
|
||||
result += ObjectUtils.nullSafeHashCode(type);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof RedisIndexSetting)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RedisIndexSetting that = (RedisIndexSetting) obj;
|
||||
|
||||
if (!ObjectUtils.nullSafeEquals(this.keyspace, that.keyspace)) {
|
||||
return false;
|
||||
}
|
||||
if (!ObjectUtils.nullSafeEquals(this.path, that.path)) {
|
||||
return false;
|
||||
}
|
||||
if (!ObjectUtils.nullSafeEquals(this.indexName, that.indexName)) {
|
||||
return false;
|
||||
}
|
||||
if (!ObjectUtils.nullSafeEquals(this.type, that.type)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* {@link IndexDefinition} allow to set up a blueprint for creating secondary index structures in Redis. Setting up
|
||||
* conditions allows to define {@link Condition} that have to be passed in order to add a value to the index. This
|
||||
* allows to fine grained tune the I index structure. {@link IndexValueTransformer} gets applied to the raw value for
|
||||
* creating the actual index entry.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public interface IndexDefinition {
|
||||
|
||||
/**
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
String getKeyspace();
|
||||
|
||||
/**
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Collection<Condition<?>> getConditions();
|
||||
|
||||
/**
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
IndexValueTransformer valueTransformer();
|
||||
|
||||
/**
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
String getIndexName();
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
* @param <T>
|
||||
*/
|
||||
public static interface Condition<T> {
|
||||
boolean matches(T value, IndexingContext context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Context in which a particular value is about to get indexed.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class IndexingContext {
|
||||
|
||||
private final String keyspace;
|
||||
private final String path;
|
||||
private final TypeInformation<?> typeInformation;
|
||||
|
||||
public IndexingContext(String keyspace, String path, TypeInformation<?> typeInformation) {
|
||||
|
||||
this.keyspace = keyspace;
|
||||
this.path = path;
|
||||
this.typeInformation = typeInformation;
|
||||
}
|
||||
|
||||
public String getKeyspace() {
|
||||
return keyspace;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public TypeInformation<?> getTypeInformation() {
|
||||
return typeInformation;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link IndexDefinitionProvider} give access to {@link IndexDefinition}s for creating secondary index structures.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public interface IndexDefinitionProvider {
|
||||
|
||||
/**
|
||||
* Gets all of the {@link RedisIndexSetting} for a given keyspace.
|
||||
*
|
||||
* @param keyspace the keyspace to get
|
||||
* @return never {@literal null}
|
||||
*/
|
||||
boolean hasIndexFor(Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Checks if an index is defined for a given keyspace and property path.
|
||||
*
|
||||
* @param keyspace
|
||||
* @param path
|
||||
* @return true if index is defined.
|
||||
*/
|
||||
boolean hasIndexFor(Serializable keyspace, String path);
|
||||
|
||||
/**
|
||||
* Get the list of {@link IndexDefinition} for a given keyspace.
|
||||
*
|
||||
* @param keyspace
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Set<IndexDefinition> getIndexDefinitionsFor(Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Get the list of {@link IndexDefinition} for a given keyspace and property path.
|
||||
*
|
||||
* @param keyspace
|
||||
* @param path
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Set<IndexDefinition> getIndexDefinitionsFor(Serializable keyspace, String path);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
/**
|
||||
* Registry that allows adding {@link IndexDefinition}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public interface IndexDefinitionRegistry {
|
||||
|
||||
/**
|
||||
* Add given {@link RedisIndexSetting}.
|
||||
*
|
||||
* @param indexDefinition must not be {@literal null}.
|
||||
*/
|
||||
void addIndexDefinition(IndexDefinition indexDefinition);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* {@link Converter} implementation that is used to transform values for usage in a particular secondary index.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public interface IndexValueTransformer extends Converter<Object, Object> {
|
||||
|
||||
}
|
||||
@@ -22,7 +22,9 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Mark property to be included in a secondary index.
|
||||
* Mark properties value to be included in a secondary index. <br />
|
||||
* Uses Redos {@literal SET} for storage. <br />
|
||||
* The value will be part of the key built for the index.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
@@ -32,11 +34,4 @@ import java.lang.annotation.Target;
|
||||
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Indexed {
|
||||
|
||||
/**
|
||||
* Type of index to use.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
IndexType type() default IndexType.SIMPLE;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,15 +16,16 @@
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
/**
|
||||
* {@link IndexType} indicates structure of index.
|
||||
* {@link IndexDefinition} that is based on a property paths.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public enum IndexType {
|
||||
public interface PathBasedRedisIndexDefinition extends IndexDefinition {
|
||||
|
||||
/**
|
||||
* Simple indicates usage of Redis Set for persisting data.
|
||||
* @return can be {@literal null}.
|
||||
*/
|
||||
SIMPLE
|
||||
String getPath();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base {@link IndexDefinition} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public abstract class RedisIndexDefinition implements IndexDefinition {
|
||||
|
||||
private final String keyspace;
|
||||
private final String indexName;
|
||||
private final String path;
|
||||
private List<Condition<?>> conditions;
|
||||
private IndexValueTransformer valueTransformer;
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisIndexDefinition}.
|
||||
*
|
||||
* @param keyspace
|
||||
* @param path
|
||||
* @param indexName
|
||||
*/
|
||||
protected RedisIndexDefinition(String keyspace, String path, String indexName) {
|
||||
|
||||
this.keyspace = keyspace;
|
||||
this.indexName = indexName;
|
||||
this.path = path;
|
||||
this.conditions = new ArrayList<IndexDefinition.Condition<?>>();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinition#getKeyspace()
|
||||
*/
|
||||
@Override
|
||||
public String getKeyspace() {
|
||||
return keyspace;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinition#getConditions()
|
||||
*/
|
||||
@Override
|
||||
public Collection<Condition<?>> getConditions() {
|
||||
return Collections.unmodifiableCollection(conditions);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinition#valueTransformer()
|
||||
*/
|
||||
@Override
|
||||
public IndexValueTransformer valueTransformer() {
|
||||
return valueTransformer != null ? valueTransformer : NoOpValueTransformer.INSTANCE;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinition#getIndexName()
|
||||
*/
|
||||
@Override
|
||||
public String getIndexName() {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
protected void addCondition(Condition<?> condition) {
|
||||
|
||||
Assert.notNull(condition, "Condition must not be null!");
|
||||
this.conditions.add(condition);
|
||||
}
|
||||
|
||||
public void setValueTransformer(IndexValueTransformer valueTransformer) {
|
||||
this.valueTransformer = valueTransformer;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = ObjectUtils.nullSafeHashCode(indexName);
|
||||
return result + ObjectUtils.nullSafeHashCode(keyspace);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof RedisIndexDefinition)) {
|
||||
return false;
|
||||
}
|
||||
RedisIndexDefinition that = (RedisIndexDefinition) obj;
|
||||
|
||||
if (!ObjectUtils.nullSafeEquals(this.keyspace, that.keyspace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ObjectUtils.nullSafeEquals(this.indexName, that.indexName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public static enum NoOpValueTransformer implements IndexValueTransformer {
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object source) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public static enum LowercaseIndexValueTransformer implements IndexValueTransformer {
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object source) {
|
||||
|
||||
if (!(source instanceof String)) {
|
||||
return source;
|
||||
}
|
||||
|
||||
return ((String) source).toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public static class CompositeValueTransformer implements IndexValueTransformer {
|
||||
|
||||
private final List<IndexValueTransformer> transformers = new ArrayList<IndexValueTransformer>();
|
||||
|
||||
public CompositeValueTransformer(Collection<IndexValueTransformer> transformers) {
|
||||
this.transformers.addAll(transformers);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object source) {
|
||||
|
||||
Object tmp = source;
|
||||
for (IndexValueTransformer transformer : transformers) {
|
||||
tmp = transformer.convert(tmp);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
* @param <T>
|
||||
*/
|
||||
public static class OrCondition<T> implements Condition<T> {
|
||||
|
||||
private final List<Condition<T>> conditions = new ArrayList<Condition<T>>();
|
||||
|
||||
public OrCondition(Collection<Condition<T>> conditions) {
|
||||
this.conditions.addAll(conditions);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinition.Condition#matches(java.lang.Object, org.springframework.data.redis.core.index.IndexDefinition.IndexingContext)
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(T value, IndexingContext context) {
|
||||
|
||||
for (Condition<T> condition : conditions) {
|
||||
if (condition.matches(value, context)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public static class PathCondition implements Condition<Object> {
|
||||
|
||||
private final String path;
|
||||
|
||||
public PathCondition(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.IndexDefinition.Condition#matches(java.lang.Object, org.springframework.data.redis.core.index.IndexDefinition.IndexingContext)
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(Object value, IndexingContext context) {
|
||||
|
||||
if (!StringUtils.hasText(path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ObjectUtils.nullSafeEquals(context.getPath(), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
/**
|
||||
* {@link PathBasedRedisIndexDefinition} for including property values in a secondary index. <br />
|
||||
* Uses Redis {@literal SET} for storage. <br />
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class SimpleIndexDefinition extends RedisIndexDefinition implements PathBasedRedisIndexDefinition {
|
||||
|
||||
/**
|
||||
* Creates new {@link SimpleIndexDefinition}.
|
||||
*
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @param path
|
||||
*/
|
||||
public SimpleIndexDefinition(String keyspace, String path) {
|
||||
this(keyspace, path, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link SimpleIndexDefinition}.
|
||||
*
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @param path
|
||||
* @param name must not be {@literal null}.
|
||||
*/
|
||||
public SimpleIndexDefinition(String keyspace, String path, String name) {
|
||||
super(keyspace, path, name);
|
||||
addCondition(new PathCondition(path));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core.index;
|
||||
|
||||
import org.springframework.data.redis.core.convert.SpelIndexResolver;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link SpelIndexDefinition} defines index that is evaluated based on a {@link SpelExpression} requires the
|
||||
* {@link SpelIndexResolver} to be evaluated.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class SpelIndexDefinition extends RedisIndexDefinition {
|
||||
|
||||
private final String expression;
|
||||
|
||||
/**
|
||||
* Creates new {@link SpelIndexDefinition}.
|
||||
*
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @param expression must not be {@literal null}.
|
||||
* @param indexName must not be {@literal null}.
|
||||
*/
|
||||
public SpelIndexDefinition(String keyspace, String expression, String indexName) {
|
||||
super(keyspace, null, indexName);
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw expression.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.RedisIndexDefinition#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result += ObjectUtils.nullSafeHashCode(expression);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.index.RedisIndexDefinition#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof SpelIndexDefinition)) {
|
||||
return false;
|
||||
}
|
||||
SpelIndexDefinition that = (SpelIndexDefinition) obj;
|
||||
return ObjectUtils.nullSafeEquals(this.expression, that.expression);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core.mapping;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.keyvalue.core.mapping.BasicKeyValuePersistentEntity;
|
||||
import org.springframework.data.keyvalue.core.mapping.KeySpaceResolver;
|
||||
import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.redis.core.TimeToLiveAccessor;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -27,7 +30,8 @@ import org.springframework.util.Assert;
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
*/
|
||||
public class BasicRedisPersistentEntity<T> extends BasicKeyValuePersistentEntity<T> implements RedisPersistentEntity<T> {
|
||||
public class BasicRedisPersistentEntity<T> extends BasicKeyValuePersistentEntity<T>
|
||||
implements RedisPersistentEntity<T> {
|
||||
|
||||
private TimeToLiveAccessor timeToLiveAccessor;
|
||||
|
||||
@@ -55,4 +59,47 @@ public class BasicRedisPersistentEntity<T> extends BasicKeyValuePersistentEntity
|
||||
return this.timeToLiveAccessor;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.BasicPersistentEntity#returnPropertyIfBetterIdPropertyCandidateOrNull(org.springframework.data.mapping.PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
protected KeyValuePersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(
|
||||
KeyValuePersistentProperty property) {
|
||||
|
||||
Assert.notNull(property);
|
||||
|
||||
if (!property.isIdProperty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
KeyValuePersistentProperty currentIdProperty = getIdProperty();
|
||||
boolean currentIdPropertyIsSet = currentIdProperty != null;
|
||||
|
||||
if (!currentIdPropertyIsSet) {
|
||||
return property;
|
||||
}
|
||||
|
||||
boolean currentIdPropertyIsExplicit = currentIdProperty.isAnnotationPresent(Id.class);
|
||||
boolean newIdPropertyIsExplicit = property.isAnnotationPresent(Id.class);
|
||||
|
||||
if (currentIdPropertyIsExplicit && newIdPropertyIsExplicit) {
|
||||
throw new MappingException(String.format(
|
||||
"Attempt to add explicit id property %s but already have an property %s registered "
|
||||
+ "as explicit id. Check your mapping configuration!",
|
||||
property.getField(), currentIdProperty.getField()));
|
||||
}
|
||||
|
||||
if (!currentIdPropertyIsExplicit && !newIdPropertyIsExplicit) {
|
||||
throw new MappingException(
|
||||
String.format("Attempt to add id property %s but already have an property %s registered "
|
||||
+ "as id. Check your mapping configuration!", property.getField(), currentIdProperty.getField()));
|
||||
}
|
||||
|
||||
if (newIdPropertyIsExplicit) {
|
||||
return property;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,8 @@ package org.springframework.data.redis.core.mapping;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
@@ -31,6 +33,12 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
*/
|
||||
public class RedisPersistentProperty extends KeyValuePersistentProperty {
|
||||
|
||||
private static final Set<String> SUPPORTED_ID_PROPERTY_NAMES = new HashSet<String>();
|
||||
|
||||
static {
|
||||
SUPPORTED_ID_PROPERTY_NAMES.add("id");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisPersistentProperty}.
|
||||
*
|
||||
@@ -44,4 +52,17 @@ public class RedisPersistentProperty extends KeyValuePersistentProperty {
|
||||
super(field, propertyDescriptor, owner, simpleTypeHolder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isIdProperty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isIdProperty() {
|
||||
|
||||
if (super.isIdProperty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return SUPPORTED_ID_PROPERTY_NAMES.contains(getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.redis.repository.configuration;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -26,6 +30,7 @@ import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.data.keyvalue.repository.config.KeyValueRepositoryConfigurationExtension;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
import org.springframework.data.redis.core.RedisKeyValueAdapter;
|
||||
import org.springframework.data.redis.core.RedisKeyValueTemplate;
|
||||
import org.springframework.data.redis.core.convert.CustomConversions;
|
||||
@@ -79,6 +84,8 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon
|
||||
@Override
|
||||
public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource configurationSource) {
|
||||
|
||||
String redisTemplateRef = configurationSource.getAttribute("redisTemplateRef");
|
||||
|
||||
RootBeanDefinition mappingContextDefinition = createRedisMappingContext(configurationSource);
|
||||
mappingContextDefinition.setSource(configurationSource.getSource());
|
||||
|
||||
@@ -86,10 +93,11 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon
|
||||
|
||||
// register coustom conversions
|
||||
RootBeanDefinition customConversions = new RootBeanDefinition(CustomConversions.class);
|
||||
registerIfNotAlreadyRegistered(customConversions, registry, REDIS_CUSTOM_CONVERSIONS_BEAN_NAME, configurationSource);
|
||||
registerIfNotAlreadyRegistered(customConversions, registry, REDIS_CUSTOM_CONVERSIONS_BEAN_NAME,
|
||||
configurationSource);
|
||||
|
||||
// Register referenceResolver
|
||||
RootBeanDefinition redisReferenceResolver = createRedisReferenceResolverDefinition();
|
||||
RootBeanDefinition redisReferenceResolver = createRedisReferenceResolverDefinition(redisTemplateRef);
|
||||
redisReferenceResolver.setSource(configurationSource.getSource());
|
||||
registerIfNotAlreadyRegistered(redisReferenceResolver, registry, REDIS_REFERENCE_RESOLVER_BEAN_NAME,
|
||||
configurationSource);
|
||||
@@ -104,16 +112,14 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon
|
||||
RootBeanDefinition redisKeyValueAdapterDefinition = new RootBeanDefinition(RedisKeyValueAdapter.class);
|
||||
|
||||
ConstructorArgumentValues constructorArgumentValuesForRedisKeyValueAdapter = new ConstructorArgumentValues();
|
||||
|
||||
String redisTemplateRef = configurationSource.getAttribute("redisTemplateRef");
|
||||
if (StringUtils.hasText(redisTemplateRef)) {
|
||||
|
||||
constructorArgumentValuesForRedisKeyValueAdapter.addIndexedArgumentValue(0, new RuntimeBeanReference(
|
||||
redisTemplateRef));
|
||||
constructorArgumentValuesForRedisKeyValueAdapter.addIndexedArgumentValue(0,
|
||||
new RuntimeBeanReference(redisTemplateRef));
|
||||
}
|
||||
|
||||
constructorArgumentValuesForRedisKeyValueAdapter.addIndexedArgumentValue(1, new RuntimeBeanReference(
|
||||
REDIS_CONVERTER_BEAN_NAME));
|
||||
constructorArgumentValuesForRedisKeyValueAdapter.addIndexedArgumentValue(1,
|
||||
new RuntimeBeanReference(REDIS_CONVERTER_BEAN_NAME));
|
||||
|
||||
redisKeyValueAdapterDefinition.setConstructorArgumentValues(constructorArgumentValuesForRedisKeyValueAdapter);
|
||||
registerIfNotAlreadyRegistered(redisKeyValueAdapterDefinition, registry, REDIS_ADAPTER_BEAN_NAME,
|
||||
@@ -122,14 +128,15 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon
|
||||
super.registerBeansForRoot(registry, configurationSource);
|
||||
}
|
||||
|
||||
private RootBeanDefinition createRedisReferenceResolverDefinition() {
|
||||
private RootBeanDefinition createRedisReferenceResolverDefinition(String redisTemplateRef) {
|
||||
|
||||
RootBeanDefinition beanDef = new RootBeanDefinition();
|
||||
beanDef.setBeanClassName("org.springframework.data.redis.core.RedisKeyValueAdapter.ReferenceResolverImpl");
|
||||
beanDef.setBeanClassName("org.springframework.data.redis.core.convert.ReferenceResolverImpl");
|
||||
|
||||
MutablePropertyValues props = new MutablePropertyValues();
|
||||
props.add("adapter", new RuntimeBeanReference(REDIS_ADAPTER_BEAN_NAME));
|
||||
beanDef.setPropertyValues(props);
|
||||
ConstructorArgumentValues constructorArgs = new ConstructorArgumentValues();
|
||||
constructorArgs.addIndexedArgumentValue(0, new RuntimeBeanReference(redisTemplateRef));
|
||||
|
||||
beanDef.setConstructorArgumentValues(constructorArgs);
|
||||
|
||||
return beanDef;
|
||||
}
|
||||
@@ -178,10 +185,10 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon
|
||||
RootBeanDefinition keyValueTemplateDefinition = new RootBeanDefinition(RedisKeyValueTemplate.class);
|
||||
|
||||
ConstructorArgumentValues constructorArgumentValuesForKeyValueTemplate = new ConstructorArgumentValues();
|
||||
constructorArgumentValuesForKeyValueTemplate.addIndexedArgumentValue(0, new RuntimeBeanReference(
|
||||
REDIS_ADAPTER_BEAN_NAME));
|
||||
constructorArgumentValuesForKeyValueTemplate.addIndexedArgumentValue(1, new RuntimeBeanReference(
|
||||
MAPPING_CONTEXT_BEAN_NAME));
|
||||
constructorArgumentValuesForKeyValueTemplate.addIndexedArgumentValue(0,
|
||||
new RuntimeBeanReference(REDIS_ADAPTER_BEAN_NAME));
|
||||
constructorArgumentValuesForKeyValueTemplate.addIndexedArgumentValue(1,
|
||||
new RuntimeBeanReference(MAPPING_CONTEXT_BEAN_NAME));
|
||||
|
||||
keyValueTemplateDefinition.setConstructorArgumentValues(constructorArgumentValuesForKeyValueTemplate);
|
||||
|
||||
@@ -205,4 +212,13 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon
|
||||
return beanDef;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingAnnotations()
|
||||
*/
|
||||
@Override
|
||||
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
return Collections.<Class<? extends Annotation>> singleton(RedisHash.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.repository.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.redis.core.mapping.RedisPersistentEntity;
|
||||
import org.springframework.data.repository.core.support.PersistentEntityInformation;
|
||||
|
||||
/**
|
||||
* {@link RedisEntityInformation} implementation using a {@link MongoPersistentEntity} instance to lookup the necessary
|
||||
* information. Can be configured with a custom collection to be returned which will trump the one returned by the
|
||||
* {@link MongoPersistentEntity} if given.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
* @param <ID>
|
||||
*/
|
||||
public class MappingRedisEntityInformation<T, ID extends Serializable>
|
||||
extends PersistentEntityInformation<T, Serializable> implements RedisEntityInformation<T, Serializable> {
|
||||
|
||||
private final RedisPersistentEntity<T> entityMetadata;
|
||||
|
||||
/**
|
||||
* @param entity
|
||||
*/
|
||||
public MappingRedisEntityInformation(RedisPersistentEntity<T> entity) {
|
||||
super(entity);
|
||||
|
||||
this.entityMetadata = entity;
|
||||
|
||||
if (!entityMetadata.hasIdProperty()) {
|
||||
|
||||
throw new MappingException(
|
||||
String.format("Entity %s requires to have an explicit id field. Did you forget to provide one using @Id?",
|
||||
entity.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.repository.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @param <T>
|
||||
* @param <ID>
|
||||
*/
|
||||
public interface RedisEntityInformation<T, ID extends Serializable> extends EntityInformation<T, ID> {
|
||||
|
||||
}
|
||||
@@ -16,9 +16,12 @@
|
||||
package org.springframework.data.redis.repository.query;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Simple set of operations requried to run queries against Redis.
|
||||
*
|
||||
@@ -27,27 +30,102 @@ import java.util.Set;
|
||||
*/
|
||||
public class RedisOperationChain {
|
||||
|
||||
Set<Object> sismember = new LinkedHashSet<Object>();
|
||||
Set<Object> orSismember = new LinkedHashSet<Object>();
|
||||
private Set<PathAndValue> sismember = new LinkedHashSet<PathAndValue>();
|
||||
private Set<PathAndValue> orSismember = new LinkedHashSet<PathAndValue>();
|
||||
|
||||
public void sismember(Object next) {
|
||||
sismember.add(next);
|
||||
public void sismember(String path, Object value) {
|
||||
sismember(new PathAndValue(path, value));
|
||||
}
|
||||
|
||||
public Set<Object> getSismember() {
|
||||
public void sismember(PathAndValue pathAndValue) {
|
||||
sismember.add(pathAndValue);
|
||||
}
|
||||
|
||||
public Set<PathAndValue> getSismember() {
|
||||
return sismember;
|
||||
}
|
||||
|
||||
public void orSismember(Object next) {
|
||||
orSismember.add(next);
|
||||
public void orSismember(String path, Object value) {
|
||||
orSismember(new PathAndValue(path, value));
|
||||
}
|
||||
|
||||
public void orSismember(Collection<Object> next) {
|
||||
public void orSismember(PathAndValue pathAndValue) {
|
||||
orSismember.add(pathAndValue);
|
||||
}
|
||||
|
||||
public void orSismember(Collection<PathAndValue> next) {
|
||||
orSismember.addAll(next);
|
||||
}
|
||||
|
||||
public Set<Object> getOrSismember() {
|
||||
public Set<PathAndValue> getOrSismember() {
|
||||
return orSismember;
|
||||
}
|
||||
|
||||
public static class PathAndValue {
|
||||
|
||||
private final String path;
|
||||
private final Collection<Object> values;
|
||||
|
||||
public PathAndValue(String path, Object singleValue) {
|
||||
|
||||
this.path = path;
|
||||
this.values = Collections.singleton(singleValue);
|
||||
}
|
||||
|
||||
public PathAndValue(String path, Collection<Object> values) {
|
||||
|
||||
this.path = path;
|
||||
this.values = values != null ? values : Collections.emptySet();
|
||||
}
|
||||
|
||||
public boolean isSingleValue() {
|
||||
return values.size() == 1;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public Collection<Object> values() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public Object getFirstValue() {
|
||||
return values.isEmpty() ? null : values.iterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return path + ":" + (isSingleValue() ? getFirstValue() : values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = ObjectUtils.nullSafeHashCode(path);
|
||||
result += ObjectUtils.nullSafeHashCode(values);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof PathAndValue)) {
|
||||
return false;
|
||||
}
|
||||
PathAndValue that = (PathAndValue) obj;
|
||||
if (!ObjectUtils.nullSafeEquals(this.path, that.path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ObjectUtils.nullSafeEquals(this.values, that.values);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class RedisQueryCreator extends AbstractQueryCreator<KeyValueQuery<RedisO
|
||||
|
||||
switch (part.getType()) {
|
||||
case SIMPLE_PROPERTY:
|
||||
sink.sismember(part.getProperty().toDotPath() + ":" + iterator.next());
|
||||
sink.sismember(part.getProperty().toDotPath(), iterator.next());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(part.getType() + "is not supported for redis query derivation");
|
||||
@@ -74,7 +74,7 @@ public class RedisQueryCreator extends AbstractQueryCreator<KeyValueQuery<RedisO
|
||||
*/
|
||||
@Override
|
||||
protected RedisOperationChain or(RedisOperationChain base, RedisOperationChain criteria) {
|
||||
base.orSismember(criteria.sismember);
|
||||
base.orSismember(criteria.getSismember());
|
||||
return base;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,12 @@ public class RedisQueryCreator extends AbstractQueryCreator<KeyValueQuery<RedisO
|
||||
|
||||
KeyValueQuery<RedisOperationChain> query = new KeyValueQuery<RedisOperationChain>(criteria);
|
||||
|
||||
if (query.getCritieria().getSismember().size() == 1 && query.getCritieria().getOrSismember().size() == 1) {
|
||||
|
||||
query.getCritieria().getOrSismember().add(query.getCritieria().getSismember().iterator().next());
|
||||
query.getCritieria().getSismember().clear();
|
||||
}
|
||||
|
||||
if (sort != null) {
|
||||
query.setSort(sort);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.redis.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
@@ -22,6 +23,9 @@ import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;
|
||||
import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.QueryInitialization;
|
||||
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.redis.core.mapping.RedisPersistentEntity;
|
||||
import org.springframework.data.redis.repository.core.MappingRedisEntityInformation;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
@@ -69,6 +73,21 @@ public class RedisRepositoryFactory extends KeyValueRepositoryFactory {
|
||||
return new RedisQueryLookupStrategy(key, evaluationContextProvider, getKeyValueOperations(), getQueryCreator());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory#getEntityInformation(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
|
||||
|
||||
RedisPersistentEntity<T> entity = (RedisPersistentEntity<T>) getMappingContext().getPersistentEntity(domainClass);
|
||||
EntityInformation<T, ID> entityInformation = (EntityInformation<T, ID>) new MappingRedisEntityInformation<T, ID>(
|
||||
entity);
|
||||
|
||||
return entityInformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,9 +18,10 @@ package org.springframework.data.redis.repository.support;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
|
||||
/**
|
||||
* Adapter for Springs {@link FactoryBean} interface to allow easy setup of {@link RedisRepositoryFactory} via Spring
|
||||
@@ -37,10 +38,11 @@ public class RedisRepositoryFactoryBean<T extends Repository<S, ID>, S, ID exten
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory()
|
||||
* @see org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean#createRepositoryFactory(org.springframework.data.keyvalue.core.KeyValueOperations, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryFactorySupport createRepositoryFactory() {
|
||||
return new RedisRepositoryFactory(getOperations(), getQueryCreator());
|
||||
protected RedisRepositoryFactory createRepositoryFactory(KeyValueOperations operations,
|
||||
Class<? extends AbstractQueryCreator<?, ?>> queryCreator) {
|
||||
return new RedisRepositoryFactory(operations, queryCreator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,10 +19,13 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Some handy methods for dealing with byte arrays.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 1.7
|
||||
*/
|
||||
public final class ByteUtils {
|
||||
@@ -76,4 +79,23 @@ public final class ByteUtils {
|
||||
}
|
||||
return bytes.toArray(new byte[bytes.size()][]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge multiple {@code byte} arrays into one array
|
||||
*
|
||||
* @param firstArray must not be {@literal null}
|
||||
* @param additionalArrays must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public static byte[][] mergeArrays(byte[] firstArray, byte[]... additionalArrays) {
|
||||
|
||||
Assert.notNull(firstArray, "first array must not be null");
|
||||
Assert.notNull(additionalArrays, "additional arrays must not be null");
|
||||
|
||||
byte[][] result = new byte[additionalArrays.length + 1][];
|
||||
result[0] = firstArray;
|
||||
System.arraycopy(additionalArrays, 0, result, 1, additionalArrays.length);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user