需要导包,jedis-2.8.1.jar和博主的序列化工具类SerializeUtils

package com.demo.redis;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import redis.clients.jedis.BinaryJedisCluster;
import redis.clients.jedis.BinaryJedisPubSub;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPubSub;
import redis.clients.util.Pool;
import redis.clients.util.SafeEncoder; import com.demo.utils.SerializeUtils; /**
* JedisClient api 添加了中文注释,便于工程师更方便使用,另外还原样保持了 Jedis api 的方法名称及使用方法,以便于仅仅通过查看
* Redis 文档 即可快速掌握使用方法 Redis 命令参考: http://redisdoc.com/
*/
public class JedisClient { private final String name;
private final JedisPool jedisPool;
private final BinaryJedisCluster cluster;
private final boolean isCluster; public BinaryJedisCluster getCluster() {
return cluster;
} public Pool<Jedis> getJedisPool() {
return jedisPool;
} private final ThreadLocal<Jedis> threadLocalJedis = new ThreadLocal<Jedis>(); public JedisClient(String name, JedisPool jedisPool, BinaryJedisCluster cluster) {
this.name = name;
this.jedisPool = jedisPool;
this.cluster = cluster;
this.isCluster = this.cluster != null;
} /**
* 存放 key value 对到 redis 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
* 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
*/
public String set(String key, Object value) {
return isCluster ? clusterSet(key, value) : jedisSet(key, value);
} private String clusterSet(String key, Object value) {
if (value == null) {
cluster.del(keyToBytes(key));
return null;
} else {
return cluster.set(keyToBytes(key), valueToBytes(value));
}
} private String jedisSet(String key, Object value) {
Jedis jedis = getJedis();
try {
if (value == null) {
jedis.del(keyToBytes(key));
return null;
} else {
return jedis.set(keyToBytes(key), valueToBytes(value));
}
} finally {
close(jedis);
}
} /**
* 存放 key value 对到 redis 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
* 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
*/
public void update(String key, Object value) {
if (isCluster) {
clusterUpdate(key, value);
} else {
jedisUpdate(key, value);
}
} private void clusterUpdate(String key, Object value) {
Jedis jedis = getJedis();
try {
byte[] bytes = keyToBytes(key);
if (value == null) {
cluster.del(bytes);
} else {
Long ttl = cluster.ttl(bytes);
if (ttl > 0) {
cluster.setex(bytes, ttl.intValue(), valueToBytes(value));
} else {
cluster.set(bytes, valueToBytes(value));
}
}
} finally {
close(jedis);
}
} private void jedisUpdate(String key, Object value) {
Jedis jedis = getJedis();
try {
byte[] bytes = keyToBytes(key);
if (value == null) {
jedis.del(bytes);
} else {
Long ttl = jedis.pttl(bytes);
if (ttl > 0) {
jedis.psetex(bytes, ttl, valueToBytes(value));
} else {
jedis.set(bytes, valueToBytes(value));
}
}
} finally {
close(jedis);
}
} /**
* 存放 key value 对到 redis,并将 key 的生存时间设为 seconds (以秒为单位)。 如果 key 已经存在, SETEX
* 命令将覆写旧值。
*/
public String setex(String key, int seconds, Object value) {
return isCluster ? clusterSetex(key, seconds, value) : jedisSetex(key, seconds, value);
} public String clusterSetex(String key, int seconds, Object value) {
if (value == null) {
cluster.del(keyToBytes(key));
return null;
}
return cluster.setex(keyToBytes(key), seconds, valueToBytes(value));
} public String jedisSetex(String key, int seconds, Object value) {
Jedis jedis = getJedis();
try {
if (value == null) {
jedis.del(keyToBytes(key));
return null;
}
return jedis.setex(keyToBytes(key), seconds, valueToBytes(value));
} finally {
close(jedis);
}
} /**
* 返回 key 所关联的 value 值 如果 key 不存在那么返回特殊值 nil 。
*/
@SuppressWarnings("unchecked")
public <T> T get(String key) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.get(keyToBytes(key)));
} finally {
close(jedis);
}
} /**
* 返回 key 所关联的 value 值 如果 key 不存在那么返回特殊值 nil 。
*/
@SuppressWarnings("unchecked")
public <T> List<T> getList(List<String> keys) {
if (keys == null) {
return Collections.emptyList();
}
Jedis jedis = getJedis();
try {
List<T> list = new ArrayList<T>();
for (String key : keys) {
list.add((T) valueFromBytes(jedis.get(keyToBytes(key))));
}
return list;
} finally {
close(jedis);
}
} /**
* 删除给定的一个 key 不存在的 key 会被忽略。
*/
public long del(String key) {
Jedis jedis = getJedis();
try {
return jedis.del(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 删除给定的多个 key 不存在的 key 会被忽略。
*
* @param <T>
*/
public <T> long del(List<T> keys) {
if (keys == null) {
return 0;
}
Jedis jedis = getJedis();
try {
return jedis.del(keysToBytesList(keys));
} finally {
close(jedis);
}
} /**
* 删除给定的多个 key 开头的
*
* @param <T>
*/
public void clear(String region) {
Jedis jedis = getJedis();
try {
Set<String> keys = jedis.keys(region + ":*");
if (keys == null || keys.size() == 0) {
return;
}
jedis.del(keysToBytesSet(keys));
} finally {
close(jedis);
}
} /**
* 删除给定的多个 key 不存在的 key 会被忽略。
*/
public long del(String... keys) {
if (keys == null) {
return 0;
}
Jedis jedis = getJedis();
try {
return jedis.del(keysToBytesArray(keys));
} finally {
close(jedis);
}
} /**
* 查找所有符合给定模式 pattern 的 key 。 KEYS * 匹配数据库中所有 key 。 KEYS h?llo 匹配 hello ,
* hallo 和 hxllo 等。 KEYS h*llo 匹配 hllo 和 heeeeello 等。 KEYS h[ae]llo 匹配 hello
* 和 hallo ,但不匹配 hillo 。 特殊符号用 \ 隔开
*/
public Set<String> keys(String pattern) {
Jedis jedis = getJedis();
try {
return jedis.keys(pattern);
} finally {
close(jedis);
}
} /**
* 同时设置一个或多个 key-value 对。 如果某个给定 key 已经存在,那么 MSET
* 会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX 命令:它只会在所有给定 key 都不存在的情况下进行设置操作。
* MSET 是一个原子性(atomic)操作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key
* 没有改变的情况,不可能发生。
*
* <pre>
* 例子:
* Cache cache = RedisKit.use(); // 使用 Redis 的 cache
* cache.mset("k1", "v1", "k2", "v2"); // 放入多个 key value 键值对
* List list = cache.mget("k1", "k2"); // 利用多个键值得到上面代码放入的值
* </pre>
*/
public String mset(String... keysValues) {
if (keysValues.length % 2 != 0)
throw new IllegalArgumentException("wrong number of arguments for met, keysValues length can not be odd");
Jedis jedis = getJedis();
try {
byte[][] kv = new byte[keysValues.length][];
for (int i = 0; i < keysValues.length; i++) {
if (i % 2 == 0)
kv[i] = keyToBytes(keysValues[i]);
else
kv[i] = valueToBytes(keysValues[i]);
}
return jedis.mset(kv);
} finally {
close(jedis);
}
} /**
* 返回所有(一个或多个)给定 key 的值。 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil
* 。因此,该命令永不失败。
*/
@SuppressWarnings("rawtypes")
public List mget(String... keys) {
Jedis jedis = getJedis();
try {
byte[][] keysBytesArray = keysToBytesArray(keys);
List<byte[]> data = jedis.mget(keysBytesArray);
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* 将 key 中储存的数字值减一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
* 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。
* 关于递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。
*/
public Long decr(String key) {
Jedis jedis = getJedis();
try {
return jedis.decr(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 将 key 所储存的值减去减量 decrement 。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY
* 操作。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。
* 关于更多递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。
*/
public Long decrBy(String key, long longValue) {
Jedis jedis = getJedis();
try {
return jedis.decrBy(keyToBytes(key), longValue);
} finally {
close(jedis);
}
} /**
* 将 key 中储存的数字值增一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
* 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。
*/
public Long incr(String key) {
Jedis jedis = getJedis();
try {
return jedis.incr(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 将 key 所储存的值加上增量 increment 。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY
* 命令。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。
* 关于递增(increment) / 递减(decrement)操作的更多信息,参见 INCR 命令。
*/
public Long incrBy(String key, long longValue) {
Jedis jedis = getJedis();
try {
return jedis.incrBy(keyToBytes(key), longValue);
} finally {
close(jedis);
}
} /**
* 检查给定 key 是否存在。
*/
public boolean exists(String key) {
Jedis jedis = getJedis();
try {
return jedis.exists(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 从当前数据库中随机返回(不删除)一个 key 。
*/
public String randomKey() {
Jedis jedis = getJedis();
try {
return jedis.randomKey();
} finally {
close(jedis);
}
} /**
* 将 key 改名为 newkey 。 当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。 当 newkey 已经存在时,
* RENAME 命令将覆盖旧值。
*/
public String rename(String oldkey, String newkey) {
Jedis jedis = getJedis();
try {
return jedis.rename(keyToBytes(oldkey), keyToBytes(newkey));
} finally {
close(jedis);
}
} /**
* 将当前数据库的 key 移动到给定的数据库 db 当中。 如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者
* key 不存在于当前数据库,那么 MOVE 没有任何效果。 因此,也可以利用这一特性,将 MOVE
* 当作锁(locking)原语(primitive)。
*/
public Long move(String key, int dbIndex) {
Jedis jedis = getJedis();
try {
return jedis.move(keyToBytes(key), dbIndex);
} finally {
close(jedis);
}
} /**
* 将 key 原子性地从当前实例传送到目标实例的指定数据库上,一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。
*/
public String migrate(String host, int port, String key, int destinationDb, int timeout) {
Jedis jedis = getJedis();
try {
return jedis.migrate(valueToBytes(host), port, keyToBytes(key), destinationDb, timeout);
} finally {
close(jedis);
}
} /**
* 切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值。 默认使用 0 号数据库。 注意:在 Jedis
* 对象被关闭时,数据库又会重新被设置为初始值,所以本方法 select(...) 正常工作需要使用如下方式之一: 1:使用
* RedisInterceptor,在本线程内共享同一个 Jedis 对象 2:使用 Redis.call(ICallback) 进行操作
* 3:自行获取 Jedis 对象进行操作
*/
public String select(int databaseIndex) {
Jedis jedis = getJedis();
try {
return jedis.select(databaseIndex);
} finally {
close(jedis);
}
} /**
* 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。 在 Redis 中,带有生存时间的 key
* 被称为『易失的』(volatile)。
*/
public Long expire(String key, int seconds) {
Jedis jedis = getJedis();
try {
return jedis.expire(keyToBytes(key), seconds);
} finally {
close(jedis);
}
} /**
* EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。不同在于 EXPIREAT 命令接受的时间参数是 UNIX
* 时间戳(unix timestamp)。
*/
public Long expireAt(String key, long unixTime) {
Jedis jedis = getJedis();
try {
return jedis.expireAt(keyToBytes(key), unixTime);
} finally {
close(jedis);
}
} /**
* 这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。
*/
public Long pexpire(String key, long milliseconds) {
Jedis jedis = getJedis();
try {
return jedis.pexpire(keyToBytes(key), milliseconds);
} finally {
close(jedis);
}
} /**
* 这个命令和 EXPIREAT 命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳,而不是像 EXPIREAT 那样,以秒为单位。
*/
public Long pexpireAt(String key, long millisecondsTimestamp) {
Jedis jedis = getJedis();
try {
return jedis.pexpireAt(keyToBytes(key), millisecondsTimestamp);
} finally {
close(jedis);
}
} /**
* 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 当 key 存在但不是字符串类型时,返回一个错误。
*/
@SuppressWarnings("unchecked")
public <T> T getSet(String key, Object value) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.getSet(keyToBytes(key), valueToBytes(value)));
} finally {
close(jedis);
}
} /**
* 移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。
*/
public Long persist(String key) {
Jedis jedis = getJedis();
try {
return jedis.persist(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 返回 key 所储存的值的类型。
*/
public String type(String key) {
Jedis jedis = getJedis();
try {
return jedis.type(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。
*/
public Long ttl(String key) {
Jedis jedis = getJedis();
try {
return jedis.ttl(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而不是像 TTL 命令那样,以秒为单位。
*/
public Long pttl(String key) {
Jedis jedis = getJedis();
try {
return jedis.pttl(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 对象被引用的数量
*/
public Long objectRefcount(String key) {
Jedis jedis = getJedis();
try {
return jedis.objectRefcount(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 对象没有被访问的空闲时间
*/
public Long objectIdletime(String key) {
Jedis jedis = getJedis();
try {
return jedis.objectIdletime(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 将哈希表 key 中的域 field 的值设为 value 。 如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。 如果域
* field 已经存在于哈希表中,旧值将被覆盖。
*/
public Long hset(String key, Object field, Object value) {
Jedis jedis = getJedis();
try {
return jedis.hset(keyToBytes(key), keyToBytes(field), valueToBytes(value));
} finally {
close(jedis);
}
} /**
* 将哈希表 key 中的域 field 的值设为 value 。并设置超时时间 如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。 如果域
* field 已经存在于哈希表中,旧值将被覆盖。
* @return
*/
public Long hsetEx(String key, Object field, Object value, int expire) {
Jedis jedis = getJedis();
try {
Long hset = jedis.hset(keyToBytes(key), keyToBytes(field), valueToBytes(value));
jedis.expire(keyToBytes(key + ":" + field), expire);
return hset;
} finally {
close(jedis);
}
} /**
* 同时将多个 field-value (域-值)对设置到哈希表 key 中。 此命令会覆盖哈希表中已存在的域。 如果 key
* 不存在,一个空哈希表被创建并执行 HMSET 操作。
*/
public String hmset(String key, Map<Object, Object> hash) {
Jedis jedis = getJedis();
try {
Map<byte[], byte[]> para = new HashMap<byte[], byte[]>();
for (Entry<Object, Object> e : hash.entrySet())
para.put(keyToBytes(e.getKey()), valueToBytes(e.getValue()));
return jedis.hmset(keyToBytes(key), para);
} finally {
close(jedis);
}
} /**
* 返回哈希表 key 中给定域 field 的值。
*/
@SuppressWarnings("unchecked")
public <T> T hget(String key, Object field) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.hget(keyToBytes(key), keyToBytes(field)));
} finally {
close(jedis);
}
} /**
* 返回哈希表 key 中,一个或多个给定域的值。 如果给定的域不存在于哈希表,那么返回一个 nil 值。 因为不存在的 key
* 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。
*/
@SuppressWarnings("rawtypes")
public List hmget(String key, Object... fields) {
Jedis jedis = getJedis();
try {
List<byte[]> data = jedis.hmget(keyToBytes(key), keysToBytesArray(fields));
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
*/
public Long hdel(String key, Object... fields) {
Jedis jedis = getJedis();
try {
return jedis.hdel(keyToBytes(key), keysToBytesArray(fields));
} finally {
close(jedis);
}
} /**
* 查看哈希表 key 中,给定域 field 是否存在。
*/
public boolean hexists(String key, Object field) {
Jedis jedis = getJedis();
try {
return jedis.hexists(keyToBytes(key), keyToBytes(field));
} finally {
close(jedis);
}
} /**
* 返回哈希表 key 中,所有的域和值。 在返回值里,紧跟每个域名(field
* name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
*/
@SuppressWarnings("rawtypes")
public Map hgetAll(String key) {
Jedis jedis = getJedis();
try {
Map<byte[], byte[]> data = jedis.hgetAll(keyToBytes(key));
Map<Object, Object> result = new HashMap<Object, Object>();
for (Entry<byte[], byte[]> e : data.entrySet())
result.put(keyFromBytes(e.getKey()), valueFromBytes(e.getValue()));
return result;
} finally {
close(jedis);
}
} /**
* 返回哈希表 key 中所有域的值。
*/
@SuppressWarnings("rawtypes")
public List hvals(String key) {
Jedis jedis = getJedis();
try {
List<byte[]> data = jedis.hvals(keyToBytes(key));
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* 返回哈希表 key 中的所有域。
*/
public Set<String> hkeys(String key) {
Jedis jedis = getJedis();
try {
Set<byte[]> keySet = jedis.hkeys(keyToBytes(key));
return keySetFromBytesSet(keySet); // 返回 key 的方法不能使用
// valueSetFromBytesSet(...)
} finally {
close(jedis);
}
} /**
* 返回哈希表 key 中域的数量。
*/
public Long hlen(String key) {
Jedis jedis = getJedis();
try {
return jedis.hlen(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 返回列表 key 中,下标为 index 的元素。 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0
* 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2
* 表示列表的倒数第二个元素,以此类推。 如果 key 不是列表类型,返回一个错误。
*/
@SuppressWarnings("unchecked") /**
* 返回列表 key 中,下标为 index 的元素。 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0
* 表示列表的第一个元素, 以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2
* 表示列表的倒数第二个元素,以此类推。 如果 key 不是列表类型,返回一个错误。
*/
public <T> T lindex(String key, long index) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.lindex(keyToBytes(key), index));
} finally {
close(jedis);
}
} /**
* 获取记数器的值
*/
public Long getCounter(String key) {
Jedis jedis = getJedis();
try {
return Long.parseLong((String) jedis.get(key.toString()));
} finally {
close(jedis);
}
} /**
* 返回列表 key 的长度。 如果 key 不存在,则 key 被解释为一个空列表,返回 0 . 如果 key 不是列表类型,返回一个错误。
*/
public Long llen(String key) {
Jedis jedis = getJedis();
try {
return jedis.llen(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 移除并返回列表 key 的头元素。
*/
@SuppressWarnings("unchecked")
public <T> T lpop(String key) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.lpop(keyToBytes(key)));
} finally {
close(jedis);
}
} /**
* 将一个或多个值 value 插入到列表 key 的表头 如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头:
* 比如说, 对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a , 这等同于原子性地执行 LPUSH
* mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。 如果 key 不存在,一个空列表会被创建并执行
* LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。
*/
public Long lpush(String key, Object... values) {
Jedis jedis = getJedis();
try {
return jedis.lpush(keyToBytes(key), valuesToBytesArray(values));
} finally {
close(jedis);
}
} /**
* 将列表 key 下标为 index 的元素的值设置为 value 。 当 index 参数超出范围,或对一个空列表( key 不存在)进行
* LSET 时,返回一个错误。 关于列表下标的更多信息,请参考 LINDEX 命令。
*/
public String lset(String key, long index, Object value) {
Jedis jedis = getJedis();
try {
return jedis.lset(keyToBytes(key), index, valueToBytes(value));
} finally {
close(jedis);
}
} /**
* 根据参数 count 的值,移除列表中与参数 value 相等的元素。 count 的值可以是以下几种: count > 0 :
* 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。 count < 0 : 从表尾开始向表头搜索,移除与 value
* 相等的元素,数量为 count 的绝对值。 count = 0 : 移除表中所有与 value 相等的值。
*/
public Long lrem(String key, long count, Object value) {
Jedis jedis = getJedis();
try {
return jedis.lrem(keyToBytes(key), count, valueToBytes(value));
} finally {
close(jedis);
}
} /**
* 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。 下标(index)参数 start 和 stop 都以 0
* 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素,
* -2 表示列表的倒数第二个元素,以此类推。
*
* <pre>
* 例子:
* 获取 list 中所有数据:cache.lrange(listKey, 0, -1);
* 获取 list 中下标 1 到 3 的数据: cache.lrange(listKey, 1, 3);
* </pre>
*/
@SuppressWarnings("rawtypes")
public List lrange(String key, long start, long end) {
Jedis jedis = getJedis();
try {
List<byte[]> data = jedis.lrange(keyToBytes(key), start, end);
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 举个例子,执行命令 LTRIM list
* 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。 下标(index)参数 start 和 stop 都以 0
* 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素,
* -2 表示列表的倒数第二个元素,以此类推。 当 key 不是列表类型时,返回一个错误。
*/
public String ltrim(String key, long start, long end) {
Jedis jedis = getJedis();
try {
return jedis.ltrim(keyToBytes(key), start, end);
} finally {
close(jedis);
}
} /**
* 移除并返回列表 key 的尾元素。
*/
@SuppressWarnings("unchecked")
public <T> T rpop(String key) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.rpop(keyToBytes(key)));
} finally {
close(jedis);
}
} /**
* 命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作: 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。 将
* source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。
*/
@SuppressWarnings("unchecked")
public <T> T rpoplpush(String srcKey, String dstKey) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.rpoplpush(keyToBytes(srcKey), keyToBytes(dstKey)));
} finally {
close(jedis);
}
} /**
* 将一个或多个值 value 插入到列表 key 的表尾(最右边)。 如果有多个 value 值,那么各个 value
* 值按从左到右的顺序依次插入到表尾:比如 对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c ,
* 等同于执行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。 如果 key
* 不存在,一个空列表会被创建并执行 RPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。
*/
public Long rpush(String key, Object... values) {
Jedis jedis = getJedis();
try {
return jedis.rpush(keyToBytes(key), valuesToBytesArray(values));
} finally {
close(jedis);
}
} /**
* BLPOP 是列表的阻塞式(blocking)弹出原语。 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被
* BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 当给定多个 key 参数时,按参数 key
* 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。
*/
@SuppressWarnings("rawtypes")
public List blpop(String... keys) {
Jedis jedis = getJedis();
try {
List<byte[]> data = jedis.blpop(keysToBytesArray(keys));
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* BLPOP 是列表的阻塞式(blocking)弹出原语。 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被
* BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 当给定多个 key 参数时,按参数 key
* 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。
*/
@SuppressWarnings("rawtypes")
public List blpop(int timeout, String... keys) {
Jedis jedis = getJedis();
try {
List<byte[]> data = jedis.blpop(timeout, keysToBytesArray(keys));
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* BRPOP 是列表的阻塞式(blocking)弹出原语。 它是 RPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被
* BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 当给定多个 key 参数时,按参数 key
* 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和
* BLPOP 不同之外,其他表现一致。
*/
@SuppressWarnings("rawtypes")
public List brpop(String... keys) {
Jedis jedis = getJedis();
try {
List<byte[]> data = jedis.brpop(keysToBytesArray(keys));
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* BRPOP 是列表的阻塞式(blocking)弹出原语。 它是 RPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被
* BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 当给定多个 key 参数时,按参数 key
* 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和
* BLPOP 不同之外,其他表现一致。
*/
@SuppressWarnings("rawtypes")
public List brpop(int timeout, String... keys) {
Jedis jedis = getJedis();
try {
List<byte[]> data = jedis.brpop(timeout, keysToBytesArray(keys));
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* 使用客户端向 Redis 服务器发送一个 PING ,如果服务器运作正常的话,会返回一个 PONG 。
* 通常用于测试与服务器的连接是否仍然生效,或者用于测量延迟值。
*/
public String ping() {
Jedis jedis = getJedis();
try {
return jedis.ping();
} finally {
close(jedis);
}
} /**
* 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。 假如 key 不存在,则创建一个只包含
* member 元素作成员的集合。 当 key 不是集合类型时,返回一个错误。
*/
public Long sadd(String key, Object... members) {
Jedis jedis = getJedis();
try {
return jedis.sadd(keyToBytes(key), valuesToBytesArray(members));
} finally {
close(jedis);
}
} /**
* 返回集合 key 的基数(集合中元素的数量)。
*/
public Long scard(String key) {
Jedis jedis = getJedis();
try {
return jedis.scard(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 移除并返回集合中的一个随机元素。 如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用 SRANDMEMBER 命令。
*/
@SuppressWarnings("unchecked")
public <T> T spop(String key) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.spop(keyToBytes(key)));
} finally {
close(jedis);
}
} /**
* 返回集合 key 中的所有成员。 不存在的 key 被视为空集合。
*/
@SuppressWarnings("rawtypes")
public Set smembers(String key) {
Jedis jedis = getJedis();
try {
Set<byte[]> data = jedis.smembers(keyToBytes(key));
Set<Object> result = new HashSet<Object>();
valueSetFromBytesSet(data, result);
return result;
} finally {
close(jedis);
}
} /**
* 判断 member 元素是否集合 key 的成员。
*/
public boolean sismember(String key, Object member) {
Jedis jedis = getJedis();
try {
return jedis.sismember(keyToBytes(key), valueToBytes(member));
} finally {
close(jedis);
}
} /**
* 返回多个集合的交集,多个集合由 keys 指定
*/
@SuppressWarnings("rawtypes")
public Set sinter(String... keys) {
Jedis jedis = getJedis();
try {
Set<byte[]> data = jedis.sinter(keysToBytesArray(keys));
Set<Object> result = new HashSet<Object>();
valueSetFromBytesSet(data, result);
return result;
} finally {
close(jedis);
}
} /**
* 返回集合中的一个随机元素。
*/
@SuppressWarnings("unchecked")
public <T> T srandmember(String key) {
Jedis jedis = getJedis();
try {
return (T) valueFromBytes(jedis.srandmember(keyToBytes(key)));
} finally {
close(jedis);
}
} /**
* 返回集合中的 count 个随机元素。 从 Redis 2.6 版本开始, SRANDMEMBER 命令接受可选的 count 参数: 如果
* count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。 如果 count
* 大于等于集合基数,那么返回整个集合。 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count
* 的绝对值。 该操作和 SPOP 相似,但 SPOP 将随机元素从集合中移除并返回,而 SRANDMEMBER
* 则仅仅返回随机元素,而不对集合进行任何改动。
*/
@SuppressWarnings("rawtypes")
public List srandmember(String key, int count) {
Jedis jedis = getJedis();
try {
List<byte[]> data = jedis.srandmember(keyToBytes(key), count);
return valueListFromBytesList(data);
} finally {
close(jedis);
}
} /**
* 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
*/
public Long srem(String key, Object... members) {
Jedis jedis = getJedis();
try {
return jedis.srem(keyToBytes(key), valuesToBytesArray(members));
} finally {
close(jedis);
}
} /**
* 返回多个集合的并集,多个集合由 keys 指定 不存在的 key 被视为空集。
*/
@SuppressWarnings("rawtypes")
public Set sunion(String... keys) {
Jedis jedis = getJedis();
try {
Set<byte[]> data = jedis.sunion(keysToBytesArray(keys));
Set<Object> result = new HashSet<Object>();
valueSetFromBytesSet(data, result);
return result;
} finally {
close(jedis);
}
} /**
* 返回一个集合的全部成员,该集合是所有给定集合之间的差集。 不存在的 key 被视为空集。
*/
@SuppressWarnings("rawtypes")
public Set sdiff(String... keys) {
Jedis jedis = getJedis();
try {
Set<byte[]> data = jedis.sdiff(keysToBytesArray(keys));
Set<Object> result = new HashSet<Object>();
valueSetFromBytesSet(data, result);
return result;
} finally {
close(jedis);
}
} /**
* 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。 如果某个 member 已经是有序集的成员,那么更新这个
* member 的 score 值, 并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。
*/
public Long zadd(String key, double score, Object member) {
Jedis jedis = getJedis();
try {
return jedis.zadd(keyToBytes(key), score, valueToBytes(member));
} finally {
close(jedis);
}
} public Long zadd(String key, Map<Object, Double> scoreMembers) {
Jedis jedis = getJedis();
try {
Map<byte[], Double> para = new HashMap<byte[], Double>();
for (Entry<Object, Double> e : scoreMembers.entrySet())
para.put(valueToBytes(e.getKey()), e.getValue()); // valueToBytes
// is
// important
return jedis.zadd(keyToBytes(key), para);
} finally {
close(jedis);
}
} /**
* 返回有序集 key 的基数。
*/
public Long zcard(String key) {
Jedis jedis = getJedis();
try {
return jedis.zcard(keyToBytes(key));
} finally {
close(jedis);
}
} /**
* 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。 关于参数
* min 和 max 的详细使用方法,请参考 ZRANGEBYSCORE 命令。
*/
public Long zcount(String key, double min, double max) {
Jedis jedis = getJedis();
try {
return jedis.zcount(keyToBytes(key), min, max);
} finally {
close(jedis);
}
} /**
* 为有序集 key 的成员 member 的 score 值加上增量 increment 。
*/
public Double zincrby(String key, double score, Object member) {
Jedis jedis = getJedis();
try {
return jedis.zincrby(keyToBytes(key), score, valueToBytes(member));
} finally {
close(jedis);
}
} /**
* 返回有序集 key 中,指定区间内的成员。 其中成员的位置按 score 值递增(从小到大)来排序。 具有相同 score
* 值的成员按字典序(lexicographical order )来排列。 如果你需要成员按 score 值递减(从大到小)来排列,请使用
* ZREVRANGE 命令。
*/
@SuppressWarnings("rawtypes")
public Set zrange(String key, long start, long end) {
Jedis jedis = getJedis();
try {
Set<byte[]> data = jedis.zrange(keyToBytes(key), start, end);
Set<Object> result = new LinkedHashSet<Object>(); // 有序集合必须
// LinkedHashSet
valueSetFromBytesSet(data, result);
return result;
} finally {
close(jedis);
}
} /**
* 返回有序集 key 中,指定区间内的成员。 其中成员的位置按 score 值递减(从大到小)来排列。 具有相同 score
* 值的成员按字典序的逆序(reverse lexicographical order)排列。 除了成员按 score 值递减的次序排列这一点外,
* ZREVRANGE 命令的其他方面和 ZRANGE 命令一样。
*/
@SuppressWarnings("rawtypes")
public Set zrevrange(String key, long start, long end) {
Jedis jedis = getJedis();
try {
Set<byte[]> data = jedis.zrevrange(keyToBytes(key), start, end);
Set<Object> result = new LinkedHashSet<Object>(); // 有序集合必须
// LinkedHashSet
valueSetFromBytesSet(data, result);
return result;
} finally {
close(jedis);
}
} /**
* 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。 有序集成员按 score
* 值递增(从小到大)次序排列。
*/
@SuppressWarnings("rawtypes")
public Set zrangeByScore(String key, double min, double max) {
Jedis jedis = getJedis();
try {
Set<byte[]> data = jedis.zrangeByScore(keyToBytes(key), min, max);
Set<Object> result = new LinkedHashSet<Object>(); // 有序集合必须
// LinkedHashSet
valueSetFromBytesSet(data, result);
return result;
} finally {
close(jedis);
}
} /**
* 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。 排名以 0 为底,也就是说,
* score 值最小的成员排名为 0 。 使用 ZREVRANK 命令可以获得成员按 score 值递减(从大到小)排列的排名。
*/
public Long zrank(String key, Object member) {
Jedis jedis = getJedis();
try {
return jedis.zrank(keyToBytes(key), valueToBytes(member));
} finally {
close(jedis);
}
} /**
* 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。 排名以 0 为底,也就是说, score
* 值最大的成员排名为 0 。 使用 ZRANK 命令可以获得成员按 score 值递增(从小到大)排列的排名。
*/
public Long zrevrank(String key, Object member) {
Jedis jedis = getJedis();
try {
return jedis.zrevrank(keyToBytes(key), valueToBytes(member));
} finally {
close(jedis);
}
} /**
* 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。 当 key 存在但不是有序集类型时,返回一个错误。
*/
public Long zrem(String key, Object... members) {
Jedis jedis = getJedis();
try {
return jedis.zrem(keyToBytes(key), valuesToBytesArray(members));
} finally {
close(jedis);
}
} /**
* 返回有序集 key 中,成员 member 的 score 值。 如果 member 元素不是有序集 key 的成员,或 key 不存在,返回
* nil 。
*/
public Double zscore(String key, Object member) {
Jedis jedis = getJedis();
try {
return jedis.zscore(keyToBytes(key), valueToBytes(member));
} finally {
close(jedis);
}
} public void subscribe(JedisPubSub jedisPubSub, String... channels) {
Jedis jedis = getJedis();
try {
jedis.subscribe(jedisPubSub, channels);
} finally {
close(jedis);
}
} public void publish(String channel, byte[] message) {
Jedis jedis = getJedis();
try {
jedis.publish(keyToBytes(channel), message);
} finally {
close(jedis);
}
} // --------- public byte[] keyToBytes(String key) {
return SafeEncoder.encode(key);
} private byte[] keyToBytes(Object key) {
return SafeEncoder.encode(key.toString());
} private String keyFromBytes(byte[] bytes) {
return SafeEncoder.encode(bytes);
} private byte[][] keysToBytesArray(String... keys) {
byte[][] result = new byte[keys.length][];
for (int i = 0; i < result.length; i++)
result[i] = keyToBytes(keys[i]);
return result;
} private <T> byte[][] keysToBytesList(List<T> keys) {
if (keys == null) {
return null;
}
byte[][] result = new byte[keys.size()][];
for (int i = 0; i < result.length; i++)
result[i] = keyToBytes(keys.get(i));
return result;
} private <T> byte[][] keysToBytesSet(Set<T> keys) {
if (keys == null) {
return null;
}
byte[][] result = new byte[keys.size()][];
int i = 0;
for (T key : keys) {
result[i++] = keyToBytes(key);
}
return result;
} private byte[][] keysToBytesArray(Object... keys) {
byte[][] result = new byte[keys.length][];
for (int i = 0; i < result.length; i++)
result[i] = keyToBytes(keys[i]);
return result;
} private Set<String> keySetFromBytesSet(Set<byte[]> data) {
Set<String> result = new HashSet<String>();
for (byte[] keyBytes : data)
result.add(keyFromBytes(keyBytes));
return result;
} private byte[] valueToBytes(Object object) {
return SerializeUtils.fstserialize(object);
} protected Object valueFromBytes(byte[] bytes) {
if (bytes != null)
return SerializeUtils.fstdeserialize(bytes);
return null;
} private byte[][] valuesToBytesArray(Object... objectArray) {
byte[][] data = new byte[objectArray.length][];
for (int i = 0; i < data.length; i++)
data[i] = valueToBytes(objectArray[i]);
return data;
} private void valueSetFromBytesSet(Set<byte[]> data, Set<Object> result) {
for (byte[] d : data)
result.add(valueFromBytes(d));
} @SuppressWarnings("rawtypes")
private List valueListFromBytesList(List<byte[]> data) {
List<Object> result = new ArrayList<Object>();
for (byte[] d : data)
result.add(valueFromBytes(d));
return result;
} public String getName() {
return name;
} public Jedis getJedis() {
Jedis jedis = threadLocalJedis.get();
if (jedis == null) {
if (jedisPool != null) {
jedis = jedisPool.getResource();
}
}
return jedis;
} public void close(Jedis jedis) {
if (threadLocalJedis.get() == null && jedis != null)
jedis.close();
} public Jedis getThreadLocalJedis() {
return threadLocalJedis.get();
} public void setThreadLocalJedis(Jedis jedis) {
threadLocalJedis.set(jedis);
} public void removeThreadLocalJedis() {
threadLocalJedis.remove();
} public void puffsubscribe(BinaryJedisPubSub jedisPubSub, String... channels) {
final byte[][] cs = new byte[channels.length][];
for (int i = 0; i < cs.length; i++) {
cs[i] = SafeEncoder.encode(channels[i]);
}
if (cluster == null) {
Jedis jedis = getJedis();
try {
jedis.subscribe(jedisPubSub, cs);
} finally {
close(jedis);
}
} else {
cluster.subscribe(jedisPubSub, cs);
}
} public void puffpublish(String channel, byte[] message) {
if (cluster == null) {
Jedis jedis = getJedis();
try {
jedis.publish(keyToBytes(channel), message);
} finally {
close(jedis);
}
} else {
cluster.publish(keyToBytes(channel), message);
}
}
}

封装JedisClient.提供API实现对redis的操作的更多相关文章

  1. 利用 Java 操作 Jenkins API 实现对 Jenkins 的控制详解

    本文转载自利用 Java 操作 Jenkins API 实现对 Jenkins 的控制详解 导语 由于最近工作需要利用 Jenkins 远程 API 操作 Jenkins 来完成一些列操作,就抽空研究 ...

  2. 在应用程序中实现对NandFlash的操作

    以TC58NVG2S3ETA00 为例: 下面是它的一些物理参数: 图一 图二 图三 图四 图五 图6-0 图6-1 说明一下,在图6-1中中间的那个布局表可以看做是实际的NandFlash一页数据的 ...

  3. centos8平台php7.4.2安装phpredis实现对redis的访问

    一,下载phpredis 1,官方下载地址: https://github.com/phpredis/phpredis/releases 2,wget下载 [root@yjweb source]# w ...

  4. Spring通过AOP实现对Redis的缓存同步

    废话不多说 说下思路:使用aop注解,在Service实现类添加需要用到redis的方法上,当每次请求过来则对其进行拦截,如果是查询则从redis进行get key,如果是update则删除key,防 ...

  5. 【原】Spring AOP实现对Redis的缓存同步

    前言:刚开始采用spring cache作为缓存数据,到后面发现扩展性不灵活,于是基于sprig cache原理自定义一套规则用于缓存数据. 请求过程: 根据请求参数生成Key,后面我们会对生成Key ...

  6. 在Asp.Net MVC中使用NPOI插件实现对Excel的操作(导入,导出,合并单元格,设置样式,输入公式)

    前言 NPOI 是 POI 项目的.NET版本,它不使用 Office COM 组件,不需要安装 Microsoft Office,目前支持 Office 2003 和 2007 版本. 1.整个Ex ...

  7. c++简单实现对mysql数据库操作

    1.连接数据库 #include <mysql.h> #include <iostream> #include<string> #include<vector ...

  8. Jquery实现对select的操作

    select实现对文本框的显示和隐藏 /** * 通过select的值实现对文本框的显示和隐藏 * #id为一个select控件 * .obj为一个文本框 */ function initSelect ...

  9. Python实现对excel的操作

    1.操作excel使用第三方库openpyxl安装:pip install openpyxy引入:import openpyxl2.常用简单操作1)打开excel文件获取工作簿wb = openpyx ...

随机推荐

  1. docker之数据卷管理

    转自:https://www.cnblogs.com/jsonhc/p/7777811.html docker之数据卷的备份和还原 1.现在利用镜像创建一个nginx的服务容器,并挂载一个数据卷 [r ...

  2. 打开Delphi 10.2提示脚本错误的解决方法

    HKEY_CURRENT_USER\SOFTWARE\Embarcadero\BDS\18.0\Known IDE Packages $(BDS)\Bin\CommunityToolbar240.bp ...

  3. 【原创】思科和锐捷组建多VLAN交换网络(隧道模式Trunk)

    组建简单交换网络设计与实施 [利用思科仿真与锐捷实践] 本文目录 第一部分 预备知识 第二部分 设计与仿真 需求分析 整体设计 PT仿真 第三部分 施工部署 console配置 连通测试 第一部分 预 ...

  4. 用adb取出在手机中安装的apk

    第一步:列出所有安装的apk adb shell pm list packages 然后找到自己要取出来的apk的包名. 第二布:找到apk的位置(后面跟上包名) adb shell pm path ...

  5. 第五次Scrum冲刺

    第五次Scrum冲刺 1.成员今日完成情况 队员 今日完成任务 刘佳 前端初步构建 李佳 后端设计初级阶段 周世元 数据设计 杨小妮 博客编写 许燕婷 管理团队当日及次日任务 陈水莲 测试矩阵用例设计 ...

  6. MVC part3

    SpringMVC原理图 SpringMVC接口解释 DispatcherServlet接口: Spring提供的前端控制器,所有的请求都有经过它来统一分发.在DispatcherServlet将请求 ...

  7. 计算平面面积和斜面面积-ArcGIS案例学习笔记

    计算平面面积和斜面面积-ArcGIS案例学习笔记 联系方式:谢老师,135_4855_4328,xiexiaokui#139.com 数据:实验数据\Chp8\Ex5\demTif.tif 平面面积= ...

  8. swift和OC中frame的小差异

    //1.0 OC中 CGRect .CGPoint.CGSize 的结构如下: struct CGRect { CGPoint origin; CGSize size; }; struct CGPoi ...

  9. 学Android开发的人可以去的几个网站

    学Android开发的人可以去的几个网站 1.<IT蓝豹>Android开源项目分享平台国内非常好的一个Android开发者分享站,分享android所有特效,每天都有最新的Android ...

  10. mysql执行流程

      https://www.jianshu.com/p/71a98f1347b9 image   image SQL示例: SELECT DISTINCT < select_list > ...