【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
这两天在想在项目中使用spring-data-redis没想到和已存在的spring-data-jpa冲突了,启动报错,原因是spring-data-jpa和spring-data-redis都使用了共同的CrudRepository接口导致Spring无法判断哪些Repository由jpa管理哪些由redis管理,解决办法在spring-data-jpa的文档中https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.multiple-modules
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
/*
* 使用多个spring data时需要使用严格模式来指定包路径,通配符的方式无法区分
*/
@Configuration
@EnableJpaRepositories(basePackages = "test.repositories.jpa")
@EnableRedisRepositories(basePackages = "test.repositories.redis")
public class ConfigurationClass { }
import java.io.Serializable;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.index.GeoIndexed;
public class Address implements Serializable
{
private static final long serialVersionUID = -232005037310316196L;
@GeoIndexed
Point location;
public Address()
{
}
public Address(Point location)
{
super();
this.location = location;
}
public Point getLocation()
{
return location;
}
public void setLocation(Point location)
{
this.location = location;
}
}
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
@RedisHash("persons")
public class Person
{
private Address address;
@Id
private String name;
private String value;
public Person(String name, String value)
{
super();
this.name = name;
this.value = value;
}
public Address getAddress()
{
return address;
}
public void setAddress(Address address)
{
this.address = address;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
}
import java.util.List;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PersonRepository extends CrudRepository<Person, String>
{
List<Person> findByAddressLocationNear(Point point, Distance distance);
List<Person> findByAddressLocationWithin(Circle circle);
}
使用save方法时使用的是用Redis命令GEOADD persons:address:location 13.361389 38.115556 some-id;
使用findByAddressLocationNear(new Point(15D, 37D), new Distance(200, Metrics.KILOMETERS));是使用Redis命令GEORADIUS persons:address:location 15.0 37.0 200.0 km;
删除时执行的connection.sRem(binKeyspace, binId);方法
spring-data-redis对Redis GEO的支持是可以使用对象的,处理方式是把经纬度使用GEO来存储,对象内容使用HASH来存放的,流程是先通过经纬度来找到member再通过member来到HASH中来查询对象,最后反序列化成对象
SimpleKeyValueRepository.class
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null!");
if (entityInformation.isNew(entity)) {
operations.insert(entity);
} else {
operations.update(entityInformation.getId(entity), entity);
}
return entity;
}
RedisKeyValueAdapter.class
public void update(final PartialUpdate<?> update) {
final RedisPersistentEntity<?> entity = this.converter.getMappingContext().getPersistentEntity(update.getTarget());
final String keyspace = entity.getKeySpace();
final Object id = update.getId();
final byte[] redisKey = createKey(keyspace, converter.getConversionService().convert(id, String.class));
final RedisData rdo = new RedisData();
this.converter.write(update, rdo);
redisOps.execute(new RedisCallback<Void>() {
@Override
public Void doInRedis(RedisConnection connection) throws DataAccessException {
RedisUpdateObject redisUpdateObject = new RedisUpdateObject(redisKey, keyspace, id);
for (PropertyUpdate pUpdate : update.getPropertyUpdates()) {
String propertyPath = pUpdate.getPropertyPath();
if (UpdateCommand.DEL.equals(pUpdate.getCmd()) || pUpdate.getValue() instanceof Collection
|| pUpdate.getValue() instanceof Map
|| (pUpdate.getValue() != null && pUpdate.getValue().getClass().isArray()) || (pUpdate.getValue() != null
&& !converter.getConversionService().canConvert(pUpdate.getValue().getClass(), byte[].class))) {
redisUpdateObject = fetchDeletePathsFromHashAndUpdateIndex(redisUpdateObject, propertyPath, connection);
}
}
if (!redisUpdateObject.fieldsToRemove.isEmpty()) {
connection.hDel(redisKey,
redisUpdateObject.fieldsToRemove.toArray(new byte[redisUpdateObject.fieldsToRemove.size()][]));
}
for (RedisUpdateObject.Index index : redisUpdateObject.indexesToUpdate) {
if (ObjectUtils.nullSafeEquals(DataType.ZSET, index.type)) {
connection.zRem(index.key, toBytes(redisUpdateObject.targetId));
} else {
connection.sRem(index.key, toBytes(redisUpdateObject.targetId));
}
}
if (!rdo.getBucket().isEmpty()) {
if (rdo.getBucket().size() > 1
|| (rdo.getBucket().size() == 1 && !rdo.getBucket().asMap().containsKey("_class"))) {
connection.hMSet(redisKey, rdo.getBucket().rawMap());
}
}
if (update.isRefreshTtl()) {
if (rdo.getTimeToLive() != null && rdo.getTimeToLive().longValue() > 0) {
connection.expire(redisKey, rdo.getTimeToLive().longValue());
// add phantom key so values can be restored
byte[] phantomKey = ByteUtils.concat(redisKey, toBytes(":phantom"));
//保存对象到Hash中
connection.hMSet(phantomKey, rdo.getBucket().rawMap());
connection.expire(phantomKey, rdo.getTimeToLive().longValue() + 300);
} else {
connection.persist(redisKey);
connection.persist(ByteUtils.concat(redisKey, toBytes(":phantom")));
}
}
new IndexWriter(connection, converter).updateIndexes(toBytes(id), rdo.getIndexedData());
return null;
}
});
}
RedisQueryEngine.class
public <T> Collection<T> execute(final RedisOperationChain criteria, final Comparator<?> sort, final int offset,
final int rows, final Serializable keyspace, Class<T> type) {
if (criteria == null
|| (CollectionUtils.isEmpty(criteria.getOrSismember()) && CollectionUtils.isEmpty(criteria.getSismember()))
&& criteria.getNear() == null) {
return (Collection<T>) getAdapter().getAllOf(keyspace, offset, rows);
}
RedisCallback<Map<byte[], Map<byte[], byte[]>>> callback = new RedisCallback<Map<byte[], Map<byte[], byte[]>>>() {
@Override
public Map<byte[], Map<byte[], byte[]>> doInRedis(RedisConnection connection) throws DataAccessException {
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())));
}
if (criteria.getNear() != null) {
//查询所有位置信息
GeoResults<GeoLocation<byte[]>> x = connection.geoRadius(geoKey(keyspace + ":", criteria.getNear()),
new Circle(criteria.getNear().getPoint(), criteria.getNear().getDistance()));
for (GeoResult<GeoLocation<byte[]>> y : x) {
allKeys.add(y.getContent().getName());
}
}
byte[] keyspaceBin = getAdapter().getConverter().getConversionService().convert(keyspace + ":", byte[].class);
final Map<byte[], Map<byte[], byte[]>> rawData = new LinkedHashMap<byte[], Map<byte[], byte[]>>();
if (allKeys.isEmpty() || allKeys.size() < offset) {
return Collections.emptyMap();
}
int offsetToUse = Math.max(0, offset);
if (rows > 0) {
allKeys = allKeys.subList(Math.max(0, offsetToUse), Math.min(offsetToUse + rows, allKeys.size()));
}
for (byte[] id : allKeys) {
//查询实体类
byte[] singleKey = ByteUtils.concat(keyspaceBin, id);
rawData.put(id, connection.hGetAll(singleKey));
}
return rawData;
}
};
Map<byte[], Map<byte[], byte[]>> raw = this.getAdapter().execute(callback);
//转换
List<T> result = new ArrayList<T>(raw.size());
for (Map.Entry<byte[], Map<byte[], byte[]>> entry : raw.entrySet()) {
RedisData data = new RedisData(entry.getValue());
data.setId(getAdapter().getConverter().getConversionService().convert(entry.getKey(), String.class));
data.setKeyspace(keyspace.toString());
//转换成相应的对象
T converted = this.getAdapter().getConverter().read(type, data);
if (converted != null) {
result.add(converted);
}
}
return result;
}
来源:oschina
链接:https://my.oschina.net/u/1169836/blog/1540793