文章目录
greenDao的流程图:
下面代码展示了数据库的使用:
BaseDao baseDao = BaseDaoFactory.getOurInstance().getBaseDao(User.class);//1
baseDao.insert(new User(1, "hongxue", "123"));//2
注释1:使用BaseDaoFactory工厂得到BaseDao。
注释2:执行插入数据操作。
1 BaseDaoFactory
BaseDaoFactory用于创建数据库,BaseDaoFactory的getBaseDao方法用于获取BaseDao。
public class BaseDaoFactory {
private static final BaseDaoFactory ourInstance = new BaseDaoFactory();
private SQLiteDatabase sqLiteDatabase;
//定义建数据库的路劲
//建议写到sd卡中,好处,app 删除了,下次再安装的时候,数据还在
private String sqliteDatabasePath;
public static BaseDaoFactory getOurInstance(){
return ourInstance;
}
public BaseDaoFactory() {
//新建数据库
sqliteDatabasePath = "data/data/com.hongx.database/hongxue.db";
sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null);
}
public <T> BaseDao<T> getBaseDao(Class<T> entityClass){
BaseDao baseDao = null;
try {
baseDao = BaseDao.class.newInstance();
baseDao.init(sqLiteDatabase, entityClass);
} catch (Exception e) {
e.printStackTrace();
}
return baseDao;
}
}
2 IBaseDao
创建一个接口IBaseDao,包含了增删改查操作。
public interface IBaseDao<T> {
//插入
long insert(T entity);
//更新
long updata(T entity, T where); //new User(1, "alan0", "123")
//删除
int delete(T where);
//查询
List<T> query(T where);
List<T> query(T where, String orderBy, Integer startIndex, Integer limit);
}
接下来创建BaseDao继承IBaseDao,在实现BaseDao之前有必要先从javabean说起,比如User.java
public class User {
private Integer id;
private String name;
private String password;
}
我们可以将类名作为表名,参数名作为表的属性名。
如果想使用自定义的表名和属性名,可以通过注解+反射来操作。
3 两个注解
添加两个注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
String value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField {
String value();
}
4 javabean-User
在User中使用注解:
@DbTable("tb_user")
public class User {
@DbField("_id")
private Integer id;
private String name;
private String password;
public User() {
}
public User(Integer id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
接下来创建BaseDao,如果javabean中添加了注解,就反射注解获取表名或者属性名,如果没有添加注解就直接反射获取类名或者参数名
5 BaseDao
5.1 实现IBaseDao接口
public class BaseDao<T> implements IBaseDao<T> {
@Override
public long insert(T entity) {
return 0;
}
@Override
public long update(T entity, T where) {
return 0;
}
@Override
public int delete(T where) {
return 0;
}
@Override
public List<T> query(T where) {
return null;
}
@Override
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
return null;
}
}
5.2 创建表
我们再看看BaseDaoFactory的getBaseDao方法及其调用:
public <T> BaseDao<T> getBaseDao(Class<T> entityClass){
BaseDao baseDao = null;
try {
baseDao = BaseDao.class.newInstance();//1
baseDao.init(sqLiteDatabase, entityClass);
} catch (Exception e) {
e.printStackTrace();
}
return baseDao;
}
BaseDao baseDao = BaseDaoFactory.getOurInstance().getBaseDao(User.class);
注释1:通过newInstance获取BaseDao实例,而不是直接new一个对象的目的是,不想将BaseDao对外提供,而是要通过BaseDaoFactory。
从上面看出,BaseDao中需要:
1 持有数据库操作的引用sqLiteDatabase
2 持有操作数据库所对应的java类型entityClass
public class BaseDao<T> implements IBaseDao<T>{
//持有数据库操作的引用
private SQLiteDatabase sqLiteDatabase;
//表名
private String tableName;
//持有操作数据库所对应的java类型
private Class<T> entityClass;
//标记:用来表示是否做过初始化操作
private boolean isInit = false;
//定义一个缓存空间(key-字段名 value-成员变量)
private HashMap<String, Field> cacheMap;
//架构内部的逻辑,最后不要提供构造方法给调用层使用
protected void init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
this.sqLiteDatabase = sqLiteDatabase;//1
this.entityClass = entityClass;//2
//可以根据传入的entityClass类型来建表,只需要建一次
if(!isInit){//3
//自动建表
//取得表明
if(entityClass.getAnnotation(DbTable.class) == null){//4
//反射到类名
this.tableName = entityClass.getSimpleName();
} else {//5
//取得注解上的名字
this.tableName = entityClass.getAnnotation(DbTable.class).value();
}
//执行建表操作
//create table if not exists
// tb_user(_id integer, name varchar(20), password varchar(20))
//使用getCreateTableSql() 生成sql语句
String createTableSql = getCreateTableSql();//6
Log.i(TAG, createTableSql);
this.sqLiteDatabase.execSQL(createTableSql);//7
cacheMap = new HashMap<>();
initCacheMap();//8
isInit = true;
}
}
...
}
注释1:获取SQLiteDatabase
注释2:获取Class对象
注释3:为了防止一个表创建多次,添加一个标记。
注释4:没有获取到DbTable注解,直接反射类名作为表名
注释5:获取到DbTable注解,反射注解得到表名
注释6:生成创建表的sql语句
注释7:执行sql语句创建表
注释8:初始化一个缓存空间(key-字段名 value-成员变量)
生成创建表的sql语句
private String getCreateTableSql() {
//create table if not exists
// tb_user(_id integer, name varchar(20), password varchar(20),)
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("create table if not exists ");
stringBuffer.append(tableName + "(");
//反射得到所有的成员变量
Field[] declaredFields = entityClass.getDeclaredFields();//1
for (Field field : declaredFields) {//2
Class type = field.getType();// 拿到成员的类型
if(field.getAnnotation(DbField.class) != null){
//通过注解获取
if(type == String.class){
stringBuffer.append(field.getAnnotation(DbField.class).value() + " TEXT,");
} else if(type == Integer.class){
stringBuffer.append(field.getAnnotation(DbField.class).value() + " INTEGER,");
}else if(type == Long.class){
stringBuffer.append(field.getAnnotation(DbField.class).value() + " BIGINT,");
}else if(type == Double.class){
stringBuffer.append(field.getAnnotation(DbField.class).value() + " DOUBLE,");
}else if(type == byte[].class){
stringBuffer.append(field.getAnnotation(DbField.class).value() + " BLOB,");
}else {
//不支持的类型
continue;
}
} else {
//通过反射获取
if(type == String.class){
stringBuffer.append(field.getName() + " TEXT,");
} else if(type == Integer.class){
stringBuffer.append(field.getName() + " INTEGER,");
}else if(type == Long.class){
stringBuffer.append(field.getName() + " BIGINT,");
}else if(type == Double.class){
stringBuffer.append(field.getName() + " DOUBLE,");
}else if(type == byte[].class){
stringBuffer.append(field.getName() + " BLOB,");
}else {
//不支持的类型
continue;
}
}
}
if(stringBuffer.charAt(stringBuffer.length() - 1) == ','){
stringBuffer.deleteCharAt(stringBuffer.length() - 1);
}
stringBuffer.append(")");
return stringBuffer.toString();
}
注释1:反射得到所有的成员变量。
注释2:for循环拿到成员的类型,同样的,如果存在注解就通过注解获取,不存在就通过反射直接获取。
initCacheMap
initCacheMap方法作用是将所有成员变量名保存到缓存中,这样在操作增删改查的时候,不需要每次都去获取成员变量名。
private void initCacheMap() {
//1、取得所有字段名
String sql = "select * from " + tableName + " limit 1, 0"; //空表
Cursor cursor = sqLiteDatabase.rawQuery(sql, null);
String[] columnNames = cursor.getColumnNames();
//2、取所有的成员变量
Field[] declaredFields = entityClass.getDeclaredFields();
//把所有字段的访问权限打开
for (Field f: declaredFields) {
f.setAccessible(true);
}
//字段 跟 成员变量一一对应
for(String columNage : columnNames){
Field columnField = null;
for(Field field : declaredFields){
String fieldName = ""; //对象中的成员变量名字
if(field.getAnnotation(DbField.class) != null){
fieldName = field.getAnnotation(DbField.class).value();
} else {
fieldName = field.getName();
}
if(columNage.equals(fieldName)){ //匹配
columnField = field;
break;
}
}
if(columnField != null){
cacheMap.put(columNage, columnField);
}
}
}
5.3 实现插入
我们常规的插入操作如下:
@Override
public long insert(T entity) {
ContentValues contentValues = new ContentValues();
contentValues.put("_id", 1);
contentValues.put("name", "hongxue");
contentValues.put("password", "123456");
sqLiteDatabase.insert(tableName, null, contentValues);
}
我们要实现的是通过对象拿到属性和值,而不是手动去设置。
@Override
public long insert(T entity) {
//1、准本好ContentValues中需要的数据
Map<String, String> map = getValues(entity);
//2、把数据转移到ContentValues
ContentValues values = getContentValues(map);
//3、开始插入
sqLiteDatabase.insert(tableName, null, values);
return 0;
}
//key(字段)--- value(成员变量)
private Map<String, String> getValues(T entity) {
HashMap<String, String> map = new HashMap<>();
//返回的是所有的成员变量
Iterator<Field> iterator = cacheMap.values().iterator();//1
while (iterator.hasNext()){
Field field = iterator.next();
field.setAccessible(true);
try {
//获取对象的属性值
Object obj = field.get(entity);//2
if(obj == null){
continue;
}
String value = obj.toString();
String key = "";
if(field.getAnnotation(DbField.class) != null){
key = field.getAnnotation(DbField.class).value();
} else {
key = field.getName();
}
if(!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)){
map.put(key, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return map;
}
注释1:在initCacheMap中已经将成员变量名保存在了cacheMap中。
注释2:通过变量名反射,获取对象中变量名对应的属性值。
把数据转移到ContentValues,代码如下:
private ContentValues getContentValues(Map<String, String> map) {
ContentValues contentValues = new ContentValues();
Set keys = map.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()){
String key = iterator.next();
String value = map.get(key);
if(value != null){
contentValues.put(key, value);
}
}
return contentValues;
}
5.4 更新
BaseDao的update方法:
@Override
public long update(T entity, T where) {
/**
* whereClause 可选的where语句
* whereArgs whereClause语句中表达式的?占位参数列表
*/
// sqLiteDatabase.update(tableName, contentValue, "id = ?", new String[]{"1"});
int result = -1;
//1、准本好ContentValues中需要的数据
Map<String, String> values = getValues(entity);
ContentValues contentValues = getContentValues(values);
//条件map
Map<String, String> values1 = getValues(where);
Condition condition = new Condition(values1);
result = sqLiteDatabase.update(tableName, contentValues, condition.whereClause, condition.whereArgs);
return result;
}
Condition中完成了update的sql语句,如下:
private class Condition {
private String whereClause; //1=1 and password = ? --- >password = ?
private String[] whereArgs;
/**
* new Person(1, "alan", "123")
* @param whereCasue
*/
public Condition(Map<String, String> whereCasue ) {
ArrayList list = new ArrayList(); //whereArgs里面的内容存入的list
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("1=1");
//取得所有成员变量的名字
Set<String> keys = whereCasue.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()){
String key = iterator.next();
String value = whereCasue.get(key);
if(value != null){
stringBuffer.append(" and " + key + "=?");
list.add(value);
}
}
this.whereClause = stringBuffer.toString();
this.whereArgs = (String[]) list.toArray(new String[list.size()]);
}
}
5.5 删除
BaseDao的delete方法:
@Override
public int delete(T where) {
// sqLiteDatabase.delete(tableName,"id = ?", new String[]{"1"} );
int result = -1;
//1、准本好ContentValues中需要的数据
Map<String, String> values = getValues(where);
Condition condition = new Condition(values);
result = sqLiteDatabase.delete(tableName, condition.whereClause, condition.whereArgs);
return result;
}
5.5 查询
@Override
public List<T> query(T where) {
return query(where, null, null, null);
}
@Override
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
// sqLiteDatabase.query(tableName, null, "id = ?",
// new String[], null. null, orderBy, "1, 5");
//1、准本好ContentValues中需要的数据
Map<String, String> values = getValues(where);
String limitString = ""; //"2,6"
if(startIndex != null && limit != null){
limitString = startIndex + " , " + limit;
}
Condition condition = new Condition(values);
Cursor query = sqLiteDatabase.query(tableName, null, condition.whereClause,
condition.whereArgs, null, orderBy, limitString);
List<T> result = getResult(query, where); //游标 --- 》 javabean --- list<javabaen>
return result;
}
private List<T> getResult(Cursor query, T where) {
ArrayList list = new ArrayList();
Object item = null;
while (query.moveToNext()){
try {
item = where.getClass().newInstance(); //因为不知道 new ? , 所以通过反射方式
//cacheMap (字段---成员变量的名字)
Iterator<Map.Entry<String, Field>> iterator = cacheMap.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<String, Field> entry = iterator.next();
//取列名
String columnName = entry.getKey();
//以列名拿到列名在游标中的位置
int columnIndex = query.getColumnIndex(columnName);
Field value = entry.getValue(); //id
Class<?> type = value.getType(); //Integer
if(columnIndex != -1){ //columnName = "age"
if(type==String.class){
value.set(item,query.getString(columnIndex));//setid(1)
}else if(type==Double.class){
value.set(item,query.getDouble(columnIndex));
}else if(type==Integer.class){
value.set(item,query.getInt(columnIndex));
}else if(type==Long.class){
value.set(item,query.getLong(columnIndex));
}else if(type==byte[].class){
value.set(item,query.getBlob(columnIndex));
}else{
continue;
}
}
}
list.add(item);
} catch (Exception e) {
e.printStackTrace();
}
}
query.close();
return list;
}
对应SQLiteDatabase中的query方法如下:
public Cursor query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy) {
return query(false, table, columns, selection, selectionArgs, groupBy,
having, orderBy, null /* limit */);
}
6 BaseDao的扩展
创建BaseDaoNewImpl继承BaseDao:
public class BaseDaoNewImpl<T> extends BaseDao<T>{
public List<T> query(String sql){
return null;
};
}
在BaseDaoFactory中添加getBaseDao方法:
public <T extends BaseDao<M>, M>T getBaseDao(Class<T> daoClass, Class<M> entityClass){
BaseDao baseDao = null;
try {
baseDao = daoClass.newInstance();
baseDao.init(sqLiteDatabase, entityClass);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return (T)baseDao;
}
使用:
public void clickUpdate(View view) {
BaseDaoNewImpl baseDao = BaseDaoFactory.getOurInstance().getBaseDao(
BaseDaoNewImpl.class, Person.class);
Person person = new Person();
person.setName("jett");
Person where = new Person();
where.setId(1);
long update = baseDao.update(person, where);
Toast.makeText(this, "执行成功!", Toast.LENGTH_SHORT).show();
}
public void clickDelete(View view) {
BaseDaoNewImpl baseDao = BaseDaoFactory.getOurInstance().getBaseDao(
BaseDaoNewImpl.class, Person.class);
Person where = new Person();
where.setName("jett");
where.setId(1);
int delete = baseDao.delete(where);
Toast.makeText(this, "执行成功!", Toast.LENGTH_SHORT).show();
}
public void clickSelect(View view) {
BaseDaoNewImpl baseDao = BaseDaoFactory.getOurInstance().getBaseDao(
BaseDaoNewImpl.class, Person.class);
Person where = new Person();
where.setId(1);
List<Person> query = baseDao.query(where);
Log.i("dn_alan", query.size() + "");
}