51ape是誰做的網(wǎng)站推廣app最快的方法
1,spring-data-redis官網(wǎng)
1)特點
- 提供了對不同Redis客戶端的整合(Lettuce和Jedis)
- 提供了RedisTemplate統(tǒng)一API來操作Redis
- 支持Redis的發(fā)布訂閱模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的響應式編程
- 支持基于JDK、JSON、字符串、Spring獨享的數(shù)據(jù)序列化及反序列化
- 支持基于Redis的JDKCollection實現(xiàn)
2,RedisTemplate
SpringDataRedis中提供了RedisTemplate工具類,其中封裝了各種對Redis的操作。
1)常用API
api | 說明 |
---|---|
redisTemplate.opsForValue(); | 操作字符串 |
redisTemplate.opsForHash(); | 操作hash |
redisTemplate.opsForList(); | 操作list |
redisTemplate.opsForSet(); | 操作set |
redisTemplate.opsForZSet(); | 操作有序set |
redisTemplate.expire(key, 60 * 10000 * 30, TimeUnit.MILLISECONDS); | 設置過期時間 |
1>API:String
redisTemplate.opsForValue().set("name","tom")
說明 | api | 備注 |
---|---|---|
添加單值 | .set(key,value) | |
獲取單值 | .get(key) | |
添加單值并返回這個值是否已經(jīng)存在 | .setIfAbsent(key, value) | |
添加單值并返回舊值 | .getAndSet(key, value) | |
批量添加 | Map<String,String> maps; .multiSet(maps) | |
批量獲取 | List<String> keys; .multiGet(keys) | |
數(shù)值型+1 | .increment(key,1) | |
設置過期時間 | set(key, value, timeout, TimeUnit.SECONDS) | 過期返回null |
字符串追加 | .append(key,"Hello"); |
2>API:List數(shù)據(jù)
template.opsForList().range("list",0,-1)
說明 | api | 備注 |
---|---|---|
單個插入 | Long leftPush(key, value); | 返回操作后的列表的長度 |
批量插入 | Long leftPushAll(K key, V… values); | 返回操作后的列表的長度; values可以是 String[] 、List<Object> |
查看 | .range(key,0,-1) | 從左往右:0,1,2; 從右往左:-1,-2,-3; 可做分頁 |
彈出最左邊的元素 | .leftPop("list") | 彈出之后該值在列表中將不復存在 |
修改 | set(key, index, value) | |
key存在則插入 | Long rightPushIfPresent(K key, V value); | 返回操作后的列表的長度 |
求subList | .trim(key,1,-1) | |
移除元素 | Long remove(key, long count, Object value); | count> 0:刪除從左到右共count個等于value的元素。 count <0:刪除等于從右到左共count個等于value的元素。 count = 0:刪除等于value的所有元素。 |
求長度 | .size(key) |
3>API:Hash操作
一個key1對應一個Map,map中每個value中@class
后面對應的值為類信息。
template.opsForHash().put("redisHash","name","tom");
說明 | api | 備注 |
---|---|---|
單插入 | .put(redisKey,hashKey, value) | |
批量插入 | Map<String,Object> map .putAll(key, map) | |
查單數(shù)據(jù) | .get(redisKey,hashKey) | |
查所有數(shù)據(jù) | .entries(redisHash) | |
查key是否存在 | Boolean hasKey(redisKey, Object hashKey); | |
批量獲取Hash值 | List multiGet(redisKey, List<Object> kes); | |
獲取key集合 | Set keys(redisKey) | |
批量刪除 | Long delete(redisKey, Object… hashKeys) | |
數(shù)值型value + 5 | increment(redisKey, hashKey, 5) | 返回操作后的value值 |
hashkey不存在時設置value | Boolean putIfAbsent(redisKey,hashKey, value) | 存在返回true,不存在返回true |
遍歷:
Cursor<Map.Entry<Object, Object>> curosr = template.opsForHash().scan("redisHash", ScanOptions.ScanOptions.NONE);while(curosr.hasNext()){Map.Entry<Object, Object> entry = curosr.next();System.out.println(entry.getKey()+":"+entry.getValue());}
4>API:Set數(shù)據(jù)
template.opsForSet().add(k,v)
說明 | api | 備注 |
---|---|---|
添加 | Long add(key, V… values); | values可以是:String[] |
查看所有 | .members(key) | |
查詢長度 | .size(key) | |
查詢元素是否存在 | Boolean isMember(key, Object o); | |
批量刪除 | Long remove(key, Object… values); | values可以是:String[] |
隨機移除 | V pop(K key); | |
將元素value 從 sourcekey所在集合移動到 destKey所在集合 | Boolean move(sourcekey, V value, destKey) | 移動后sourcekey集合再沒有value元素,destKey集合去重。 |
求兩個集合的交集 | Set intersect(K key, K otherKey); | |
求多個無序集合的交集 | Set intersect(K key, Collection otherKeys); | |
求多個無序集合的并集 | Set union(K key, Collection otherKeys); |
遍歷:
Cursor<Object> curosr = template.opsForSet().scan("setTest", ScanOptions.NONE);while(curosr.hasNext()){System.out.println(curosr.next());}
5>API:ZSet集合
有序的Set集合,排序依據(jù)是Score。
template.opsForZSet().add("zset1","zset-1",1.0)
說明 | api | 備注 |
---|---|---|
添加單個元素 | Boolean add(k, v, double score) | 返回元素是否已存在 |
批量添加元素 | Long add(k, Set<TypedTuple> tuples) | 舉例:見下文1. |
批量刪除 | Long remove(K key, Object… values); | |
排序按分數(shù)值asc,返回成員o的排名 | Long rank(key, Object o); | 排名從0開始 |
排序按分數(shù)值desc,返回成員o的排名 | Long reverseRank(key, Object o); | 排名從0開始 |
按區(qū)間查詢,按分數(shù)值asc | Set range(key, 0, -1); | |
增加元素的score值,并返回增加后的值 | Double incrementScore(K key, V value, double delta); |
- 批量添加元素
ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<Object>("zset-5",9.6);ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<Object>("zset-6",9.9);Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<ZSetOperations.TypedTuple<Object>>();tuples.add(objectTypedTuple1);tuples.add(objectTypedTuple2);System.out.println(template.opsForZSet().add("zset1",tuples));System.out.println(template.opsForZSet().range("zset1",0,-1));
- 遍歷
Cursor<ZSetOperations.TypedTuple<Object>> cursor = template.opsForZSet().scan("zzset1", ScanOptions.NONE);while (cursor.hasNext()){ZSetOperations.TypedTuple<Object> item = cursor.next();System.out.println(item.getValue() + ":" + item.getScore());}
2)使用
1>依賴
<!-- Redis依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
<!-- 連接池依賴--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>
2>配置文件
spring:redis:host: 127.0.0.1 # Redis服務器地址port: 6379 # Redis服務器連接端口 timeout:0 # 連接超時時間(毫秒)
# database: 0 # Redis數(shù)據(jù)庫索引(默認為0)
# password: # Redis服務器連接密碼(默認為空)lettuce: # 使用的是lettuce連接池pool:max-active: 8 # 連接池最大連接數(shù)(使用負值表示沒有限制)max-idle: 8 # 連接池中的最大空閑連接min-idle: 0 # 連接池中的最小空閑連接max-wait: 100 # 連接池最大阻塞等待時間(使用負值表示沒有限制)
1>序列化配置
RedisTemplate默認采用JDK的序列化工具,序列化為字節(jié)形式,在redis中可讀性很差。
修改默認的序列化方式為jackson:
@Configuration
public class RedisConfig {@Bean //RedisConnectionFactory不需要我們創(chuàng)建Spring會幫助我們創(chuàng)建public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 1.創(chuàng)建RedisTemplate對象RedisTemplate<String,Object> template = new RedisTemplate<>();
// 2.設置連接工廠template.setConnectionFactory(connectionFactory);
// 3.創(chuàng)建JSON序列化工具GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 4.設置Key的序列化template.setKeySerializer(RedisSerializer.string());template.setHashKeySerializer(RedisSerializer.string());
// 5.設置Value的序列化 jsonRedisSerializer使我們第三步new出來的template.setValueSerializer(jsonRedisSerializer);template.setHashValueSerializer(jsonRedisSerializer);
// 6.返回return template;}
}
但是json序列號可能導致一些其他的問題:JSON序列化器會將類的class類型寫入到JSON結(jié)果中并存入Redis,會帶來額外的內(nèi)存開銷。
為了節(jié)省內(nèi)存空間,我們并不會使用JSON序列化器來處理value,而是統(tǒng)一使用String序列化器,要求只能存儲String類型的key哈value,當要存儲Java對象時,手動完成對象的序列化和反序列化。
4>java實現(xiàn)
public class RedisUtil {@Autowiredprivate RedisTemplate redisTemplate;/*** 批量刪除對應的value* * @param keys*/public void remove(final String... keys) {for (String key : keys) {remove(key);}}/*** 批量刪除key* * @param pattern*/public void removePattern(final String pattern) {Set<Serializable> keys = redisTemplate.keys(pattern);if (keys.size() > 0)redisTemplate.delete(keys);}public void remove(final String key) {if (exists(key)) {redisTemplate.delete(key);}}public boolean exists(final String key) {return redisTemplate.hasKey(key);}public String get(final String key) {Object result = null;ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();result = operations.get(key);if (result == null) {return null;}return result.toString();}public boolean set(final String key, Object value) {boolean result = false;try {ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.set(key, value);result = true;} catch (Exception e) {e.printStackTrace();}return result;}public boolean set(final String key, Object value, Long expireTime) {boolean result = false;try {ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.set(key, value);redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);result = true;} catch (Exception e) {e.printStackTrace();}return result;}public boolean hmset(String key, Map<String, String> value) {boolean result = false;try {redisTemplate.opsForHash().putAll(key, value);result = true;} catch (Exception e) {e.printStackTrace();}return result;}public Map<String, String> hmget(String key) {Map<String, String> result = null;try {result = redisTemplate.opsForHash().entries(key);} catch (Exception e) {e.printStackTrace();}return result;}
}
3)StringRedisTemplate
key和value的序列化方式默認就是String方式,省去了我們自定義RedisTemplate的過程。
@Autowiredprivate StringRedisTemplate stringRedisTemplate;
// JSON工具private static final ObjectMapper mapper = new ObjectMapper();@Testvoid testStringTemplate() throws JsonProcessingException {
// 準備對象User user = new User("abc", 18);
// 手動序列化String json = mapper.writeValueAsString(user);
// 寫入一條數(shù)據(jù)到RedisstringRedisTemplate.opsForValue().set("user:200",json);// 讀取數(shù)據(jù)String s = stringRedisTemplate.opsForValue().get("user:200");
// 反序列化User user1 = mapper.readValue(s,User.class);System.out.println(user1);}
3,Redis數(shù)據(jù)序列化
4,Repository操作
類似jpa操作,只要Redis服務器版本在2.8.0以上,不用事務,就可以使用Repository做各種操作。
注意:Repository和jpa的Repository是同一個,意味著jpa支持的api在redis操作中通用。
1)注解
注解 | 說明 | 屬性 | 對比jpa |
---|---|---|---|
@RedisHash | 用來定義實體。 | value :定義了不同類型對象存儲時使用的前綴,也叫做鍵空間,默認是全限定類名;timeToLive 定義緩存的秒數(shù); | 類似@Entity |
@Id | 定義對象的標識符 | 類似@Id | |
@Indexed | 定義二級索引,加在屬性上可以將該屬性定義為查詢用的索引 | ||
@Reference | 緩存對象引用,一般引用的對象也會被展開存儲在當前對象中,添加了該注解后會直接存儲該對象在Redis中的引用 |
2)使用
- 不需要添加依賴。
spring-boot自動添加了@EnableRedisRepositories注解。 - 實體
@RedisHash(value="menu",timeToLive=60)
public class RedisMenuItem implements Serializable{@Idprivste Long id;@Indexedprivate String name;private Size size;private Money price;
}
- 定義Repository接口
public interface RedisMenuRepository extends CrudRepository<RedisMenuItem, Long>{List<RedisMenuItem> findByName(String name);
}
- 查詢
List<MenuItem> itemList = menuRepository.findAll();menuRepository.save(menuItem);
5,Spring Cache
Spring 3.1 引入了對 Cache 的支持,使用使用 JCache(JSR-107)注解簡化開發(fā)。
注意:可支持冪等操作的接口才可以使用緩存注解,因為緩存和參數(shù)無關(guān),即:不管什么參數(shù),返回值一樣。
1)org.springframework.cache.Cache接口
- 包含了緩存的各種操作集合;
- 提供了各種xxxCache 的實現(xiàn),比如:RedisCache
2)org.springframework.cache.CacheManager接口
- 定義了創(chuàng)建、配置、獲取、管理和控制多個唯一命名的 Cache。這些 Cache 存在于 CacheManager 的上下文中。
- 提供了各種xxxCacheManager 的實現(xiàn),比如RedisCacheManager。
3)相關(guān)注解
1>@EnableCaching
- 開啟基于注解的緩存;
- 作用在緩存配置類上或者SpringBoot 的主啟動類上;
2>@Cacheable
緩存注解。
使用注意:
- 基于AOP去實現(xiàn)的,所以必須通過IOC對象去調(diào)用。
- 要緩存的 Java 對象必須實現(xiàn) Serializable 接口。
@Cacheable(cacheNames = "usersBySpEL",//key通過變量拼接key="#root.methodName + '[' + #id + ']'",//id大于1才緩存??扇笔?/span>condition = "#id > 1",//當id大于10時,條件為true,方法返回值不會被緩存。可缺省unless = "#id > 10")public User getUserBySpEL(Integer id) {}@Cacheable(value = {"menuById"}, key = "'id-' + #menu.id")public Menu findById(Menu menu) {return menu;}
常用屬性 | 說明 | 備注 | 代碼示例 |
---|---|---|---|
cacheNames/value | 緩存名稱,用來劃分不同的緩存區(qū),避免相同key值互相影響。 | 可以是單值、數(shù)組; 在redis中相當于key的一級目錄,支持 : 拼接多層目錄 | cacheNames = "users" cacheNames = {"users","account"} |
key | 緩存數(shù)據(jù)時使用的 key,默認是方法參數(shù)。 可以使用 spEL 表達式來編寫 | ||
keyGenerator | key 的生成器,統(tǒng)一管理key。 | key 和 keyGenerator 二選一使用,同時使用會導致異常。 | keyGenerator = "myKeyGenerator" |
cacheManager | 指定緩存管理器,從哪個緩存管理器里面獲取緩存 | ||
condition | 可以用來指定符合條件的情況下才緩存 | ||
unless | 否定緩存。 | 當 unless 指定的條件為 true ,方法的返回值就不會被緩存 | 通過 #result 獲取方法結(jié)果進行判斷。 |
sync | 是否使用異步模式。 | 默認是方法執(zhí)行完,以同步的方式將方法返回的結(jié)果存在緩存中 |
spEL常用元數(shù)據(jù):
說明 | 示例 | 備注 |
---|---|---|
#root.methodName | 當前被調(diào)用的方法名 | |
#root.method.name | 當前被調(diào)用的方法 | |
#root.target | 當前被調(diào)用的目標對象 | |
#root.targetClass | 當前被調(diào)用的目標對象類 | |
#root.args[0] | 當前被調(diào)用的方法的參數(shù)列表 | |
#root.cacheds[0].name | 當前方法調(diào)用使用的緩存區(qū)列表 | |
#參數(shù)名 或 #p0 或 #a0 | 方法的參數(shù)名; 0代表參數(shù)的索引 | |
#result | 方法執(zhí)行后的返回值 | 如果沒有執(zhí)行則沒有內(nèi)容 |
3>@CachePut
主要針對配置,能夠根據(jù)方法的請求參數(shù)對其結(jié)果進行緩存。
- 區(qū)別于 @Cacheable,它每次都會觸發(fā)真實方法的調(diào)用,可以保證緩存的一致性。
- 屬性與 @Cacheable 類同。
4>@CacheEvict
根據(jù)一定的條件對緩存進行清空。
- 標記在類上時表示其中所有方法的執(zhí)行都會觸發(fā)緩存的清除操作;
常用屬性 | 說明 | 備注 | 代碼示例 |
---|---|---|---|
value | |||
key | |||
condition | |||
allEntries | 為true時,清除value屬性值中的所有緩存;默認為false,可以指定清除value屬性值下具體某個key的緩存 | ||
beforeInvocation | 1. 默認是false,即在方法執(zhí)行成功后觸發(fā)刪除緩存的操作; 2.如果方法拋出異常未能成功返回,不會觸發(fā)刪除緩存的操作 3.當改為true時,方法執(zhí)行之前會清除指定的緩存,這樣不論方法執(zhí)行成功還是失敗都會清除緩存 |
4)緩存實現(xiàn)
上文中基于注解的緩存接口,有一層CacheMananger抽象,其中用ConcurrentHashMap維護了多個Cache,通過cacheNames指定了哪個Cache。