前言
Redis是开源的,内存数据结构存储的健值数据库。即Key: Value形式来存储每一个数据,以Redis String、Hash、List类型为例:
- String 相当于 Map<String, String>
- Hash 相当于 Map<String, Map<String, String>>
- List 相当于 Map<String, List<String>>
每一个记录都相当于一个Map键值对,并根据不同的值类型,使用不同类型的Map。
如何用Redis存储一个对象实体
那么如何使用这种结构来存储一个对象记录呢,例如User {id: "user1", name: "sundial dreams", age: 21, sex: "man"},其实存储方式有很多,我们挑一种简单的存储方式,即直接用Hash类型来存储,以user:id当成一个键,如下:
user:user1=> {name: "sundial dreams", age: 21, sex: "man"}
但是我们存储了一堆的User,然后我们需要获取所有的User咋办呢,最暴力的方式是遍历所用的键,然匹配出user:为前缀的键,但是效率上肯定不高,因此我们可以使用一个List来存储当前的用户Id,并以user:list为键,如下
user:list => ["user1"]
那如果我们需要根据用户名来查询一条用户的信息呢,最暴力的方式还是枚举出所有的用户,然后再匹配姓名,考虑到效率问题,我们使用反向索引的方式来实现,即以user:username为键值为userid的List类型,比如
user:sundial dreams => ["user1"]
如果还想根据年龄来查找用户,也可以为年龄构建一个反向索引,即
user:21 => ["user1"]
有了这个思路,其他的都是同样的道理。
基于Java反射的Dao类
可以看到,User实体的存储其实也是有固定模式的(其实其他实体也可以用类似的存储模式),因此可以写一个UserDao类来存储每一个User实体,基本方法包括增删改查,但是,如果我们的实体类一换,即不是User,而是其他类,如Article,那么我们不得不写一个ArticleDao类,并重新实现跟UserDao类似的方法,这不符合代码复用原则,即同一份代码,在UserDao的实现类里要写一遍,而在ArticleDao的实现类里也得写一遍,所以我使用Java的反射和泛型来实现了一个公共的Dao类,并且以Dao<User> userDao = new Dao()的方式来产生操作User实体类的Dao对象,同理Dao<Article> articleDao = new Dao()来产生操作Article实体类的Dao对象,这样同一个方法只需实现一次即可,提高代码复用率。
IDao接口的定义
基于上面的思路我们定义如下接口:
/**
* Dao 接口
*
* @param <E>
*/
interface IDao<E> {
//查询所有 返回一个Map类型的值,其中键为Id,值为泛型类对象(实体类对象,如User类的对象)
Map<String, E> queryAll() throws Exception;
//基于域查询 返回值与queryAll一致, 根据对应属性的值来查询,以User类为例,queryByField("name", "sundial dreams") => 查询叫做sundial dreams的用户ID
Map<String, E> queryByField(String field, String value) throws Exception;
//基于Id查询
Tuple2<String, E> queryById(String id) throws Exception;
//插入操作
String insert(E ele) throws Exception;
//更新操作
String update(String id, E ele) throws Exception;
//删除操作
void delete(String id) throws Exception;
}
定义实体,以User实体为例
/**
* User实体类
* 因为我实现的Dao是通用的Dao,而且在构建反向索引时不可能为类每一个属性都构建,
* 比如可以通过name查询对应用户,但没有根据密码来查询用户的说法。
* 因此,我只会为设置为public的属性构建反向索引,并且定义的实体类名,最后会对应上Redis数据库上存储的名字
*/
class User {
public String name; // 用户名 会构建反向索引
public String birthday; // 生日 会构建反向索引
public String area; // 所在地区 会构建反向索引
private String sex; // 性别 不会构建反向索引
private String password; // 密码 不会构建反向索引
public User() {
}
public User(String name, String birthday, String password, String area, String sex) {
this.name = nam