├── .gitignore ├── LICENSE ├── README.md ├── Redis技术总结.png ├── distributedId └── OrderNumberGenerate.java ├── distributedLock ├── README.md ├── RedisLockTool.java └── Test.java └── redisTools ├── GeoRadiusDto.java └── RedisCacheUtil.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | *.iml 14 | .idea/spring-data-redis-tools.iml 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-data-redis-tools 2 | - RedisTemplate封装工具类 `redisTools` 3 | - 可视化分布式ID生成器 `distributedId` (eg:JD202501010001) 4 | - 可靠分布式锁工具类 `distributedLock`(lua脚本实现原子性解决断电问题、valueId避免错误释放问题) 5 | - [Redis技术总结思维导图](#user-content-redis技术总结) 6 | *** 7 | ## Maven 8 | ```xml 9 | 10 | org.springframework.boot 11 | spring-boot-starter-data-redis 12 | 13 | ``` 14 | ## RedisConfiguration 15 | ```java 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.cache.CacheManager; 18 | import org.springframework.cache.annotation.EnableCaching; 19 | import org.springframework.context.annotation.Bean; 20 | import org.springframework.context.annotation.Configuration; 21 | import org.springframework.data.redis.cache.RedisCacheManager; 22 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 23 | import org.springframework.data.redis.core.RedisTemplate; 24 | import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; 25 | import org.springframework.data.redis.serializer.StringRedisSerializer; 26 | import redis.clients.jedis.JedisPoolConfig; 27 | 28 | /** 29 | * @author wellJay 30 | */ 31 | @Configuration 32 | @EnableCaching 33 | public class RedisConfiguration { 34 | //过期时间一天 35 | private static final int DEFAULT_EXPIRE_TIME = 3600 * 24; 36 | 37 | //从配置文件读取redis参数 38 | @Autowired 39 | private CloudConfigProperties cloudConfigProperties; 40 | 41 | /** 42 | * jedisPoolConfig config 43 | */ 44 | @Bean 45 | public JedisPoolConfig jedisPoolConfig() { 46 | JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); 47 | jedisPoolConfig.setMaxIdle(cloudConfigProperties.getRedis().getMaxIdle()); 48 | jedisPoolConfig.setMinIdle(cloudConfigProperties.getRedis().getMinIdle()); 49 | jedisPoolConfig.setTestOnBorrow(cloudConfigProperties.getRedis().getTestOnBorrow()); 50 | jedisPoolConfig.setTestOnReturn(cloudConfigProperties.getRedis().getTestOnReturn()); 51 | return jedisPoolConfig; 52 | } 53 | 54 | /** 55 | * JedisConnectionFactory 56 | */ 57 | @Bean 58 | public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) { 59 | JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); 60 | jedisConnectionFactory.setHostName(cloudConfigProperties.getRedis().getHost()); 61 | jedisConnectionFactory.setPort(cloudConfigProperties.getRedis().getPort()); 62 | jedisConnectionFactory.setPassword(cloudConfigProperties.getRedis().getPassword()); 63 | jedisConnectionFactory.setTimeout(cloudConfigProperties.getRedis().getTimeout()); 64 | jedisConnectionFactory.setUsePool(true); 65 | jedisConnectionFactory.setPoolConfig(jedisPoolConfig); 66 | return jedisConnectionFactory; 67 | } 68 | 69 | /** 70 | * RedisTemplate 71 | * 从执行时间上来看,JdkSerializationRedisSerializer是最高效的(毕竟是JDK原生的),但是是序列化的结果字符串是最长的。 72 | * JSON由于其数据格式的紧凑性,序列化的长度是最小的,时间比前者要多一些。 73 | * 所以个人的选择是倾向使用JacksonJsonRedisSerializer作为POJO的序列器。 74 | */ 75 | @Bean 76 | public RedisTemplate redisTemplate(JedisConnectionFactory jedisConnectionFactory) { 77 | RedisTemplate redisTemplate = new RedisTemplate(); 78 | redisTemplate.setConnectionFactory(jedisConnectionFactory); 79 | redisTemplate.setDefaultSerializer(new StringRedisSerializer()); 80 | //设置普通value序列化方式 81 | redisTemplate.setValueSerializer(new StringRedisSerializer()); 82 | redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); 83 | return redisTemplate; 84 | } 85 | 86 | @Bean 87 | public CacheManager cacheManager(RedisTemplate redisTemplate) { 88 | RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate); 89 | redisCacheManager.setDefaultExpiration(DEFAULT_EXPIRE_TIME); 90 | return redisCacheManager; 91 | } 92 | } 93 | ``` 94 | 95 | ## How To Use 96 | 1、注入util方式,适用于复杂的业务处理 97 | ```java 98 | @Autowired 99 | private RedisCacheUtil redisCacheUtil; 100 | ``` 101 | 2、Spring注解方式适用于简单的数据缓存 102 | ```java 103 | @Cacheable(value = Constants.RedisKey.XXX_KEY) 104 | ``` 105 | ## Redis技术总结 106 | ![](Redis技术总结.png) 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /Redis技术总结.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WellJay/spring-data-redis-tools/001a6dcadb58cf914cfc9a082c6f1e5a999ce536/Redis技术总结.png -------------------------------------------------------------------------------- /distributedId/OrderNumberGenerate.java: -------------------------------------------------------------------------------- 1 | import org.springframework.beans.factory.annotation.Autowired; 2 | import org.springframework.data.redis.core.StringRedisTemplate; 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | /** 9 | * Created by WenJie on 2015/11/3. 10 | * 可视化分布式唯一订单号生成器 20151103001 11 | */ 12 | @Component 13 | public class OrderNumberGenerate { 14 | 15 | public static final String FROMAT_STR = "yyyyMMddHHmm"; 16 | private static StringRedisTemplate staticTemplate; 17 | 18 | @Autowired 19 | public static void setStaticTemplate(StringRedisTemplate staticTemplate) { 20 | OrderNumberGenerate.staticTemplate = staticTemplate; 21 | } 22 | 23 | private OrderNumberGenerate() { 24 | } 25 | 26 | /** 27 | * 生成订单编号 28 | * date存redis,只要date变化则直接increment=1,否则就是并发冲突直接increment++保证分布式唯一id 29 | * 30 | * public static final String FLOWER_ORDER_NUMBER_INCR = "order_number_incr"; 31 | * public static final String FLOWER_ORDER_NUMBER_DATE = "order_number_date"; 32 | * 33 | * @return 34 | */ 35 | public static synchronized String getOrderNo() { 36 | String str = new SimpleDateFormat(FROMAT_STR).format(new Date()); 37 | String date = staticTemplate.opsForValue().get(CacheKey.FLOWER_ORDER_NUMBER_DATE); 38 | if (date == null || !date.equals(str)) { 39 | //保存时间 40 | staticTemplate.opsForValue().set(CacheKey.FLOWER_ORDER_NUMBER_DATE, str); 41 | //缓存清除 42 | staticTemplate.delete(CacheKey.FLOWER_ORDER_NUMBER_INCR); 43 | } 44 | //缓存++ 45 | Long incrementValue = staticTemplate.opsForValue().increment(CacheKey.FLOWER_ORDER_NUMBER_INCR, 1); 46 | long orderNo = Long.parseLong(date) * 10000; 47 | orderNo += incrementValue; 48 | return orderNo + ""; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /distributedLock/README.md: -------------------------------------------------------------------------------- 1 | - `RedisLockTool.tryGetDistributedLock();` GET LOCK key = caller className&method value = Current Thread Id 2 | - `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId);` GET LOCK 3 | - `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond);` 4 | - `RedisLockTool.tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond, Long loopTimes, Long sleepInterval);` 5 | 6 | redis集群情况下推荐[RedLock](https://github.com/redisson/redisson) 7 | -------------------------------------------------------------------------------- /distributedLock/RedisLockTool.java: -------------------------------------------------------------------------------- 1 | import org.springframework.beans.factory.annotation.Autowired; 2 | import org.springframework.data.redis.core.RedisTemplate; 3 | import org.springframework.data.redis.core.script.DefaultRedisScript; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.util.Collections; 7 | import java.util.concurrent.TimeUnit; 8 | /** 9 | * @author wenjie 10 | * @date 2018/5/7 0007 16:44 11 | */ 12 | @Component 13 | public final class RedisLockTool { 14 | 15 | private static final Long SUCCESS = 1L; 16 | public static final String LOCK_SCRIPT_STR = "if redis.call('set',KEYS[1],ARGV[1],'EX',ARGV[2],'NX') then return 1 else return 0 end"; 17 | public static final String UNLOCK_SCRIPT_STR = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; 18 | 19 | //default value 20 | public static final Integer DEFAULT_EXPIRE_SECOND = 60; 21 | public static final Long DEFAULT_LOOP_TIMES = 10L; 22 | public static final Long DEFAULT_SLEEP_INTERVAL = 500L; 23 | public static final String PACKAGE_NAME_SPLIT_STR = "\\."; 24 | public static final String CLASS_AND_METHOD_CONCAT_STR = "->"; 25 | 26 | private static RedisTemplate redisTemplate; 27 | 28 | @Autowired 29 | public void init(RedisTemplate redisTemplate) { 30 | this.redisTemplate = redisTemplate; 31 | } 32 | 33 | /** 34 | * 得到分布式锁 35 | * 默认key:调用者类名 36 | * 37 | * @return 38 | * @throws InterruptedException 39 | */ 40 | public static boolean tryGetDistributedLock() { 41 | String callerKey = getCurrentThreadCaller(); 42 | String requestId = String.valueOf(Thread.currentThread().getId()); 43 | return tryGetDistributedLock(callerKey, requestId); 44 | } 45 | 46 | /** 47 | * @param lockKey 锁名称 48 | * @param requestId 随机请求id 49 | * @return 50 | * @throws InterruptedException 51 | */ 52 | public static boolean tryGetDistributedLock(String lockKey, String requestId) { 53 | return tryGetDistributedLock(lockKey, requestId, DEFAULT_EXPIRE_SECOND); 54 | } 55 | 56 | /** 57 | * @param lockKey key 58 | * @param requestId 随机请求id 59 | * @param expireSecond 超时秒 60 | * @return 61 | * @throws InterruptedException 62 | */ 63 | public static boolean tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond) { 64 | return tryGetDistributedLock(lockKey, requestId, expireSecond, DEFAULT_LOOP_TIMES, DEFAULT_SLEEP_INTERVAL); 65 | } 66 | 67 | 68 | /** 69 | * 加锁 70 | * 71 | * @param lockKey key 72 | * @param requestId 随机请求id 73 | * @param expireSecond 超时秒 74 | * @param loopTimes 循环次数 75 | * @param sleepInterval 等待间隔(毫秒) 76 | * @return 77 | */ 78 | public static boolean tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond, Long loopTimes, Long sleepInterval) { 79 | DefaultRedisScript redisScript = new DefaultRedisScript<>(LOCK_SCRIPT_STR, Long.class); 80 | while (loopTimes-- >= 0) { 81 | Object result = redisTemplate.execute(redisScript, Lists.newArrayList(lockKey), requestId, String.valueOf(expireSecond)); 82 | if (SUCCESS.equals(result)) { 83 | return true; 84 | } 85 | try { 86 | TimeUnit.MILLISECONDS.sleep(sleepInterval); 87 | } catch (InterruptedException e) { 88 | e.printStackTrace(); 89 | } 90 | continue; 91 | } 92 | return false; 93 | } 94 | 95 | 96 | /** 97 | * 释放锁 98 | * 99 | * @return 100 | */ 101 | public static boolean releaseDistributedLock() { 102 | String callerKey = getCurrentThreadCaller(); 103 | String requestId = String.valueOf(Thread.currentThread().getId()); 104 | return releaseDistributedLock(callerKey, requestId); 105 | } 106 | 107 | /** 108 | * 释放锁 109 | * 110 | * @param lockKey key 111 | * @param requestId 加锁的请求id 112 | * @return 113 | */ 114 | public static boolean releaseDistributedLock(String lockKey, String requestId) { 115 | DefaultRedisScript redisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT_STR, Long.class); 116 | Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId); 117 | if (SUCCESS.equals(result)) { 118 | return true; 119 | } 120 | return false; 121 | } 122 | 123 | private static String getSimpleClassName(String className) { 124 | String[] splits = className.split(PACKAGE_NAME_SPLIT_STR); 125 | return splits[splits.length - 1]; 126 | } 127 | 128 | /** 129 | * Get caller 130 | * 131 | * @return 132 | */ 133 | private static String getCurrentThreadCaller() { 134 | StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3]; 135 | return getSimpleClassName(stackTraceElement.getClassName()) + CLASS_AND_METHOD_CONCAT_STR + stackTraceElement.getMethodName(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /distributedLock/Test.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | import org.junit.runner.RunWith; 3 | import org.springframework.boot.test.context.SpringBootTest; 4 | import org.springframework.test.context.junit4.SpringRunner; 5 | 6 | import java.util.Random; 7 | import java.util.UUID; 8 | import java.util.concurrent.CountDownLatch; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | @RunWith(SpringRunner.class) 12 | @SpringBootTest(classes = Application.class) 13 | public class ApplicationTests { 14 | 15 | @Test 16 | public void distributedLock() throws InterruptedException { 17 | CountDownLatch countDownLatch = new CountDownLatch(10); 18 | 19 | for (int i = 0; i < 10; i++) { 20 | Thread thread = new Thread(new Tester(), "Thread-" + i); 21 | countDownLatch.countDown(); 22 | thread.start(); 23 | } 24 | 25 | countDownLatch.await(); 26 | 27 | System.out.println("down"); 28 | 29 | Thread.sleep(Integer.MAX_VALUE); 30 | } 31 | 32 | class Tester implements Runnable { 33 | 34 | public static final int RANDOM_NUMBER_RANGE = 3; 35 | 36 | @Override 37 | public void run() { 38 | //获取锁, key:caller className&method value:Current Thread Id 39 | //也可以传参 key value 可选[expireSecond loopTimes sleepInterval] 40 | boolean result = RedisLockTool.tryGetDistributedLock(); 41 | if (result) { 42 | System.out.println(Thread.currentThread().getName() + ": get lock"); 43 | } else { 44 | System.err.println(Thread.currentThread().getName() + ": get lock timeout"); 45 | } 46 | 47 | //if get the lock 48 | if (result) { 49 | try { 50 | TimeUnit.SECONDS.sleep(new Random().nextInt(RANDOM_NUMBER_RANGE)); 51 | } catch (InterruptedException e) { 52 | e.printStackTrace(); 53 | } 54 | RedisLockTool.releaseDistributedLock(); 55 | System.out.println(Thread.currentThread().getName() + ": release lock"); 56 | } 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /redisTools/GeoRadiusDto.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 地理位置相关dto 3 | */ 4 | public class GeoRadiusDto { 5 | 6 | private String member; 7 | 8 | private Double x; 9 | 10 | private Double y; 11 | 12 | public String getMember() { 13 | return member; 14 | } 15 | 16 | public void setMember(String member) { 17 | this.member = member; 18 | } 19 | 20 | public Double getX() { 21 | return x; 22 | } 23 | 24 | public void setX(Double x) { 25 | this.x = x; 26 | } 27 | 28 | public Double getY() { 29 | return y; 30 | } 31 | 32 | public void setY(Double y) { 33 | this.y = y; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /redisTools/RedisCacheUtil.java: -------------------------------------------------------------------------------- 1 | import org.springframework.beans.factory.annotation.Autowired; 2 | import org.springframework.data.redis.core.*; 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.*; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * @author wenjie 10 | * @create 2017-06-15 19:09 11 | **/ 12 | @Component 13 | public class RedisCacheUtil { 14 | 15 | @Autowired 16 | public RedisTemplate redisTemplate; 17 | 18 | /** 19 | * 缓存基本的对象,Integer、String、实体类等 20 | * 21 | * @param key 缓存的键值 22 | * @param value 缓存的值 23 | * @return 缓存的对象 24 | */ 25 | public ValueOperations setCacheObject(String key, T value) { 26 | ValueOperations operation = redisTemplate.opsForValue(); 27 | operation.set(key, value); 28 | return operation; 29 | } 30 | 31 | public ValueOperations setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit) { 32 | ValueOperations operation = redisTemplate.opsForValue(); 33 | operation.set(key, value, timeout, timeUnit); 34 | return operation; 35 | } 36 | 37 | /** 38 | * 获得缓存的基本对象。 39 | * 40 | * @param key 缓存键值 41 | * @return 缓存键值对应的数据 42 | */ 43 | public T getCacheObject(String key) { 44 | ValueOperations operation = redisTemplate.opsForValue(); 45 | return operation.get(key); 46 | } 47 | 48 | /** 49 | * 删除单个对象 50 | * 51 | * @param key 52 | */ 53 | public void deleteObject(String key) { 54 | redisTemplate.delete(key); 55 | } 56 | 57 | /** 58 | * 删除集合对象 59 | * 60 | * @param collection 61 | */ 62 | public void deleteObject(Collection collection) { 63 | redisTemplate.delete(collection); 64 | } 65 | 66 | /** 67 | * 缓存List数据 68 | * 69 | * @param key 缓存的键值 70 | * @param dataList 待缓存的List数据 71 | * @return 缓存的对象 72 | */ 73 | public ListOperations setCacheList(String key, List dataList) { 74 | ListOperations listOperation = redisTemplate.opsForList(); 75 | if (null != dataList) { 76 | int size = dataList.size(); 77 | for (int i = 0; i < size; i++) { 78 | listOperation.leftPush(key, dataList.get(i)); 79 | } 80 | } 81 | return listOperation; 82 | } 83 | 84 | /** 85 | * 获得缓存的list对象 86 | * 87 | * @param key 缓存的键值 88 | * @return 缓存键值对应的数据 89 | */ 90 | public List getCacheList(String key) { 91 | List dataList = new ArrayList(); 92 | ListOperations listOperation = redisTemplate.opsForList(); 93 | Long size = listOperation.size(key); 94 | 95 | for (int i = 0; i < size; i++) { 96 | dataList.add(listOperation.index(key, i)); 97 | } 98 | return dataList; 99 | } 100 | 101 | /** 102 | * 缓存Set 103 | * 104 | * @param key 缓存键值 105 | * @param dataSet 缓存的数据 106 | * @return 缓存数据的对象 107 | */ 108 | public BoundSetOperations setCacheSet(String key, Set dataSet) { 109 | BoundSetOperations setOperation = redisTemplate.boundSetOps(key); 110 | Iterator it = dataSet.iterator(); 111 | while (it.hasNext()) { 112 | setOperation.add(it.next()); 113 | } 114 | return setOperation; 115 | } 116 | 117 | /** 118 | * 获得缓存的set 119 | * 120 | * @param key 121 | * @return 122 | */ 123 | public Set getCacheSet(String key) { 124 | Set dataSet = new HashSet(); 125 | BoundSetOperations operation = redisTemplate.boundSetOps(key); 126 | Long size = operation.size(); 127 | for (int i = 0; i < size; i++) { 128 | dataSet.add(operation.pop()); 129 | } 130 | return dataSet; 131 | } 132 | 133 | /** 134 | * 缓存Map 135 | * 136 | * @param key 137 | * @param dataMap 138 | * @return 139 | */ 140 | public HashOperations setCacheMap(String key, Map dataMap) { 141 | 142 | HashOperations hashOperations = redisTemplate.opsForHash(); 143 | if (null != dataMap) { 144 | for (Map.Entry entry : dataMap.entrySet()) { 145 | hashOperations.put(key, entry.getKey(), entry.getValue()); 146 | } 147 | } 148 | return hashOperations; 149 | } 150 | 151 | /** 152 | * 获得缓存的Map 153 | * 154 | * @param key 155 | * @return 156 | */ 157 | public Map getCacheMap(String key) { 158 | Map map = redisTemplate.opsForHash().entries(key); 159 | return map; 160 | } 161 | 162 | 163 | /** 164 | * 缓存Map 165 | * 166 | * @param key 167 | * @param dataMap 168 | * @return 169 | */ 170 | public HashOperations setCacheIntegerMap(String key, Map dataMap) { 171 | HashOperations hashOperations = redisTemplate.opsForHash(); 172 | if (null != dataMap) { 173 | for (Map.Entry entry : dataMap.entrySet()) { 174 | hashOperations.put(key, entry.getKey(), entry.getValue()); 175 | } 176 | } 177 | return hashOperations; 178 | } 179 | 180 | /** 181 | * 获得缓存的Map 182 | * 183 | * @param key 184 | * @return 185 | */ 186 | public Map getCacheIntegerMap(String key) { 187 | Map map = redisTemplate.opsForHash().entries(key); 188 | return map; 189 | } 190 | 191 | 192 | //=====================GEO地理位置相关===================== 193 | 194 | /** 195 | * 增加定位点 196 | * 197 | * @param x 198 | * @param y 199 | * @param member 200 | * @param time 201 | * @return 202 | */ 203 | public boolean addGeo(double x, double y, String member, long time) { 204 | String key = GEO_KEY; 205 | try { 206 | GeoOperations geoOps = redisTemplate.opsForGeo(); 207 | geoOps.add(key, new Point(x, y), member); 208 | if (time > 0) { 209 | redisTemplate.expire(key, time, TimeUnit.SECONDS); 210 | } 211 | } catch (Throwable t) { 212 | t.printStackTrace(); 213 | log.error("缓存[" + key + "]" + "失败, point[" + x + "," + 214 | y + "], member[" + member + "]" + ", error[" + t + "]"); 215 | } 216 | return true; 217 | } 218 | 219 | 220 | /** 221 | * 删除定位点 222 | * 223 | * @param members 224 | * @return 225 | */ 226 | public boolean removeGeo(String... members) { 227 | try { 228 | GeoOperations geoOps = redisTemplate.opsForGeo(); 229 | geoOps.remove(GEO_KEY, members); 230 | } catch (Throwable t) { 231 | log.error("移除[" + GEO_KEY + "]" + "失败" + ", error[" + t + "]"); 232 | } 233 | return true; 234 | } 235 | 236 | 237 | /** 238 | * 计算定位距离 239 | * 240 | * @param member1 241 | * @param member2 242 | * @return 243 | */ 244 | public Distance distanceGeo(String member1, String member2) { 245 | try { 246 | GeoOperations geoOps = redisTemplate.opsForGeo(); 247 | return geoOps.geoDist(GEO_KEY, member1, member2); 248 | } catch (Throwable t) { 249 | log.error("计算距离[" + GEO_KEY + "]" + "失败, member[" + member1 + "," + member2 + "], error[" + t + "]"); 250 | } 251 | return null; 252 | } 253 | 254 | /** 255 | * 获取坐标 256 | * 257 | * @param members 258 | * @return 259 | */ 260 | public List getGeo(String... members) { 261 | try { 262 | GeoOperations geoOps = redisTemplate.opsForGeo(); 263 | return geoOps.position(GEO_KEY, members); 264 | } catch (Throwable t) { 265 | log.error("获取坐标[" + GEO_KEY + "]" + "失败]" + ", error[" + t + "]"); 266 | } 267 | return null; 268 | } 269 | 270 | /** 271 | * 基于某个坐标的附近的东西 272 | * 273 | * @param x 274 | * @param y 275 | * @param distance 276 | * @param direction 277 | */ 278 | public List raduisGeo(double x, double y, double distance, Sort.Direction direction) { 279 | List radiusDtos = new ArrayList<>(); 280 | try { 281 | GeoOperations geoOps = redisTemplate.opsForGeo(); 282 | 283 | //设置geo查询参数 284 | RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs(); 285 | geoRadiusArgs = geoRadiusArgs.includeCoordinates().includeDistance();//查询返回结果包括距离和坐标 286 | if (Sort.Direction.ASC.equals(direction)) {//按查询出的坐标距离中心坐标的距离进行排序 287 | geoRadiusArgs.sortAscending(); 288 | } else if (Sort.Direction.DESC.equals(direction)) { 289 | geoRadiusArgs.sortDescending(); 290 | } 291 | GeoResults> geoResults = geoOps.radius(GEO_KEY, new Circle(new Point(x, y), new Distance(distance, RedisGeoCommands.DistanceUnit.METERS)), geoRadiusArgs); 292 | 293 | List>> geoResultList = geoResults.getContent(); 294 | for (GeoResult> geoResult : geoResultList) { 295 | String name = geoResult.getContent().getName(); 296 | Point point = geoResult.getContent().getPoint(); 297 | GeoRadiusDto radiusDto = new GeoRadiusDto(); 298 | radiusDto.setMember(name); 299 | radiusDto.setX(point.getX()); 300 | radiusDto.setY(point.getY()); 301 | radiusDtos.add(radiusDto); 302 | } 303 | } catch (Throwable t) { 304 | 305 | } 306 | return radiusDtos; 307 | } 308 | 309 | 310 | /** 311 | * 基于某个key的附近的东西 312 | * 313 | * @param member 314 | * @param distance 315 | * @param direction 316 | */ 317 | public List raduisGeo(String member, double distance, Sort.Direction direction) { 318 | List radiusDtos = new ArrayList<>(); 319 | try { 320 | GeoOperations geoOps = redisTemplate.opsForGeo(); 321 | 322 | //设置geo查询参数 323 | RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs(); 324 | geoRadiusArgs = geoRadiusArgs.includeCoordinates().includeDistance();//查询返回结果包括距离和坐标 325 | if (Sort.Direction.ASC.equals(direction)) {//按查询出的坐标距离中心坐标的距离进行排序 326 | geoRadiusArgs.sortAscending(); 327 | } else if (Sort.Direction.DESC.equals(direction)) { 328 | geoRadiusArgs.sortDescending(); 329 | } 330 | GeoResults> geoResults = geoOps.radius(GEO_KEY, member, new Distance(distance, RedisGeoCommands.DistanceUnit.METERS), geoRadiusArgs); 331 | 332 | List>> geoResultList = geoResults.getContent(); 333 | for (GeoResult> geoResult : geoResultList) { 334 | String name = geoResult.getContent().getName(); 335 | //结果集排除自己 336 | if (!name.equals(member)) { 337 | Point point = geoResult.getContent().getPoint(); 338 | GeoRadiusDto radiusDto = new GeoRadiusDto(); 339 | radiusDto.setMember(name); 340 | radiusDto.setX(point.getX()); 341 | radiusDto.setY(point.getY()); 342 | radiusDtos.add(radiusDto); 343 | } 344 | } 345 | } catch (Throwable t) { 346 | 347 | } 348 | return radiusDtos; 349 | } 350 | 351 | 352 | } 353 | --------------------------------------------------------------------------------