集成spring-data-jpa和spring-data-redis

五迷三道 提交于 2019-12-18 22:34:01

【推荐】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;
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!