ORM概念
对象关系映射(Object Relational Mapping),通俗来讲就是建立关系型数据库与业务实体对象之间作一个映射关系。对于Java后端来说,例如mybatis、hibernate等,在Android平台下,常见的数据库映射框架有 GreenDAO、Realm等。废话不多说,进入正题!
实体模型建立
既然是映射,自然需要对数据库表的字段结构与实体类之间建立一个连接关系。我们采用注解,来通过实体类生成一个表的结构。
注解列
ID
通过 @Id
注解来标识表的ID字段。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
}
主键
通过 @Unique
注解来标识一个主键。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Unique {
}
自增长
通过 @AutoIncrement
注解来标识一个自增长的键。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoIncrement {
}
数据表的列
通过 @Column
注解来标识数据表的一个列,默认不能为null,可通过 nullable 属性修改。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
boolean nullable() default false;
}
实体基类
首先,每一个表都有一个唯一的id,所以我们建立的基类里,包含了这个自增长的id。
public abstract class BaseEntity {
@Id
@AutoIncrement
private Integer id; // 通过 @Id 与 @AutoIncrement 注解标识为自增长id
public BaseEntity() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) throws DBException {
if (id < 1) {
throw new DBException(ErrMsg.ERR_INVALID_ID);
}
this.id = id;
}
}
每一个继承实体基类的实体类,都必须生成他的 get
和 set
方法,对于 boolean
来说,为了便于存储与操作,我们在数据库应当把它转换为 1 和 0 的模式。例如:
public class BookConfig extends BaseEntity {
@Column
protected String book_num;
@Column
protected String setting_key;
@Column(nullable = true)
protected String setting_value = "";
@Column
protected int status;
@Column(nullable = true)
protected int isModify = 1;
public BookConfig() {
}
public BookConfig(String book_num, String setting_key) {
this.book_num = book_num;
this.setting_key = setting_key;
}
public String getBook_num() {
return book_num;
}
public void setBook_num(String book_num) {
this.book_num = book_num;
}
public String getSetting_key() {
return setting_key;
}
public void setSetting_key(String setting_key) {
this.setting_key = setting_key;
}
public String getSetting_value() {
return setting_value;
}
public void setSetting_value(String setting_value) {
this.setting_value = setting_value;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getIsModify() {
return isModify;
}
public void setIsModify(int isModify) {
this.isModify = isModify;
}
}
这样,就创建了一个实体类了。为什么要去生成他的 get
和set
方法呢?这是因为我们需要通过反射来取出每一个字段,然后通过 get
和set
方法去设置字段的值。后面还会提到!
实体类映射成表结构
有个实体类之后,我们就需要把这个实体类转换成一个数据表,通过 @Column
注解去寻找实体类的每一个字段,然后拼接生成对应的SQL语句。
/**
* 生成建表的SQL语句
*
* @param clazz 实体类
* @param strTableName 表名字
* @return
*/
public static String genCreateTableSQL(Class clazz, String strTableName) {
boolean isHaveKeyId = false;
boolean isHaveColumnAnnotation = false;
String strIdName = null;
// 反射获取实体类的所有字段
Field[] fields = Utils.getDeclaredField(clazz);
if (fields.length <= 0)
return null;
StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS " + strTableName + " ( ");
StringBuilder sbUnique = new StringBuilder("");
for (Field field : fields) {
// 添加 自增长id
if (field.isAnnotationPresent(Id.class)) {
// 一个表只能有一个id
if (isHaveKeyId)
continue;
isHaveColumnAnnotation = true;
Id id = field.getAnnotation(Id.class);
strIdName = getColumnName(field);
sb.append(strIdName);
sb.append(" ");
// type
sb.append(fieldType2DBType(field.getGenericType().toString()));
sb.append(" ");
// primary key
sb.append("NOT NULL PRIMARY KEY ");
// is auto-increment
if (field.isAnnotationPresent(AutoIncrement.class)
&& (field.getGenericType().toString().equals("class java.lang.Integer") || field
.getGenericType().toString().equals("int"))) {
sb.append("AUTOINCREMENT");
}
sb.append(",");
isHaveKeyId = true;
} else if (field.isAnnotationPresent(Column.class)) {
// 添加其他列
Column col = field.getAnnotation(Column.class);
String strColName = getColumnName(field);
sb.append(strColName);
sb.append(" ");
// 数据类型
sb.append(fieldType2DBType(field.getGenericType().toString()));
// 是否可以为空
boolean canBeNull = col.nullable();
if (!canBeNull) {
sb.append(" NOT NULL");
}
sb.append(",");
// is unique
if (field.isAnnotationPresent(Unique.class)) {
sbUnique.append("CONSTRAINT \"u" + strColName.toLowerCase() + "\" UNIQUE (\"" + strColName + "\"),");
}
isHaveColumnAnnotation = true;
}
}
// nothing
if (!isHaveColumnAnnotation && !isHaveKeyId)
return null;
// unique
sb.append("CONSTRAINT \"u" + strIdName.toLowerCase() + "\" UNIQUE (\"" + strIdName + "\")");
if (sbUnique.length() > 0) {
sb.append(",");
sb.append(sbUnique.deleteCharAt(sbUnique.length() - 1));
}
// end
sb.append(" )");
return sb.toString();
}
/**
* Java属性类型转换为数据库数据类型
*
* @param fieldGenericTyp
* @return
*/
private static String fieldType2DBType(String fieldGenericTyp) {
if (fieldGenericTyp.equals("class java.lang.String")) {
return "VARCHAR";
} else if (fieldGenericTyp.equals("class java.lang.Boolean") || fieldGenericTyp.equals("boolean")
|| fieldGenericTyp.equals("class java.lang.Integer") || fieldGenericTyp.equals("int")
|| fieldGenericTyp.equals("class java.lang.Long") || fieldGenericTyp.equals("long")
|| fieldGenericTyp.equals("class java.lang.Short") || fieldGenericTyp.equals("short")
|| fieldGenericTyp.equals("class java.lang.Byte") || fieldGenericTyp.equals("byte")) {
return "INTEGER";
} else if (fieldGenericTyp.equals("class [B")) {
return "BLOB";
} else if (fieldGenericTyp.equals("class java.lang.Float") || fieldGenericTyp.equals("float")) {
return "float";
} else if (fieldGenericTyp.equals("class java.lang.Double") || fieldGenericTyp.equals("double")) {
return "double";
}
return null;
}
这样的话,我们只要调用以下代码,就可以生成SQL语句。例如:
String sql = ORMUtils.genCreateTableSQL(BookConfig.class, "book_config");
数据库操作封装
表创建完之后,我们就需要封装一些基本数据库操作的方法,也就是增删改查。 首先,建立一个操作方法接口:
public interface IDAO<T extends BaseEntity> {
/**
* get the sqlite database object
*
* @return
*/
SQLiteDatabase getDatabase();
/**
* init table name and entityClass
*
* @param tableName
* @param clazz
*/
void initTable(String tableName, Class<T> clazz);
/**
* get count of entities
*
* @return
* @throws DBException
*/
long getCount() throws DBException;
boolean isTableExist(String tableName) throws DBException;
/**
* check table exists
*
* @return
* @throws DBException
*/
boolean isTableExist() throws DBException;
/**
* create table
*
* @throws DBException
*/
void createTable() throws DBException;
/**
* create table
*
* @param tableName table name
* @throws DBException
*/
<T> void createTable(Class<T> entityClass, String tableName) throws DBException;
/**
* drop table
*
* @throws DBException
*/
void dropTable() throws DBException;
/**
* drop all table
*
* @throws DBException
*/
void dropAllTable() throws DBException;
/**
* save database entity
*
* @param entity
* @throws DBException
*/
void save(T entity) throws DBException;
/**
* delete database entity by id(primary key)
*
* @param id
* @throws DBException
*/
void delete(int id) throws DBException;
/**
* delete database entity by ids(primary key)
*
* @param ids
* @throws DBException
*/
void delete(int[] ids) throws DBException;
/**
* delete all data
*
* @throws DBException
*/
void deleteAll() throws DBException;
/**
* update entity
*
* @param entity
* @throws DBException
*/
void update(T entity) throws DBException;
/**
* update entity by a condition string
*
* @param condition part of the update SQL command after keyword 'WHERE'
* (i.e."UPDATE Person SET age = 35 WHERE condition")
* (e.g. condition -- "name = 'Richard' OR name = 'Jefferson'")
* @param entity
* @throws DBException
*/
void updateByCondition(String condition, T entity) throws DBException;
/**
* find entity by id
*
* @param id
* @return
* @throws DBException
*/
T find(int id) throws DBException;
/**
* find last entity
*
* @return
* @throws DBException
*/
T findLastEntity() throws DBException;
/**
* find entities by a condition string
*
* @param condition part of the select SQL command after keyword 'WHERE'
* (i.e."SELECT * FROM Person WHERE condition")
* (e.g. condition -- "name = 'Richard' OR name = 'Jefferson'")
* @return
*/
List<T> findByCondition(String condition) throws DBException;
/**
* find all entities
*
* @return
* @throws DBException
*/
List<T> findAll() throws DBException;
}
接口主要封装了一些比较常用的增删改查方法,当然了,特别复杂的操作,还是需要配合SQL语句来进行执行,但是这样已经可以满足一些常用的基本操作了。
接着我们需要来具体实现这一系列方法:
public class DAO<T extends BaseEntity> extends SQLiteOpenHelper implements IDAO<T> {
private Context mContext;
private String mDBName;
private String mTableName;
private int mVersion;
private SQLiteDatabase db;
private Field[] fields;
private Class<T> entityClass;
public DBUpdateInfo info = new DBUpdateInfo();
/**
* constructor. only for operate the database and cannot operate the specified table.
*
* @param context to use to open or create the database
* @param dbName of the database name, it will be stored in /data/data/%package_name%/files
* @param version number of the database (starting at 1)
*/
public DAO(Context context, String dbName, int version) {
this(context, dbName, null, null, version);
}
/**
* constructor
*
* @param context to use to open or create the database
* @param dbName of the database name, it will be stored in /data/data/%package_name%/files
* @param tableName the name of table to needs to be operated
* @param entityClass class for entity
* @param version number of the database (starting at 1)
*/
public DAO(Context context, String dbName, String tableName, Class<T> entityClass, int version) {
super(context, dbName, null, version);
this.mContext = context;
this.mDBName = dbName;
this.mTableName = tableName;
this.mVersion = version;
this.db = getWritableDatabase();
this.fields = Utils.getDeclaredField(entityClass);
this.entityClass = entityClass;
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
info.isUpdate = true;
info.from = oldVersion;
info.to = newVersion;
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
info.isUpdate = true;
info.from = oldVersion;
info.to = newVersion;
}
@Override
public void initTable(String tableName, Class<T> clazz) {
this.mTableName = tableName;
this.entityClass = clazz;
this.fields = Utils.getDeclaredField(entityClass);
}
@Override
public SQLiteDatabase getDatabase() {
return db == null ? (db = getWritableDatabase()) : db;
}
@Override
public long getCount() throws DBException {
if (TextUtils.isEmpty(mTableName))
return 0;
try {
openDB(true);
String sql = "SELECT COUNT(*) FROM " + mTableName;
LogUtils.d(sql);
Cursor cursor = db.rawQuery(sql, null);
cursor.moveToFirst();
long total = cursor.getLong(0);
return total;
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_GET_COUNT);
} finally {
closeDB();
}
}
@Override
public boolean isTableExist(String tableName) throws DBException {
boolean isExist = false;
if (TextUtils.isEmpty(tableName))
return isExist;
try {
openDB(false);
String sql = "SELECT COUNT(*) FROM Sqlite_master WHERE TYPE ='table' AND NAME ='" + tableName.trim() + "' ";
LogUtils.d(sql);
Cursor cursor = db.rawQuery(sql, null);
if (cursor.moveToNext()) {
int count = cursor.getInt(0);
if (count > 0) {
isExist = true;
}
}
return isExist;
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_IS_TABLE_EXISTS);
} finally {
closeDB();
}
}
@Override
public boolean isTableExist() throws DBException {
return !TextUtils.isEmpty(mTableName) && isTableExist(mTableName);
}
@Override
public void createTable() throws DBException {
createTable(entityClass, mTableName);
}
@Override
public <T> void createTable(Class<T> entityClass, String tableName) throws DBException {
try {
openDB(true);
String sql = ORMUtils.genCreateTableSQL(entityClass, tableName);
LogUtils.i(sql);
db.execSQL(sql);
} catch (Exception e) {
e.printStackTrace();
throw new DBException(ErrMsg.ERR_CREATE_TABLE, e);
} finally {
closeDB();
}
}
@Override
public void dropTable() throws DBException {
try {
openDB(true);
String sql = "DROP TABLE " + mTableName;
LogUtils.d(sql);
db.beginTransaction();
db.execSQL(sql);
db.setTransactionSuccessful();
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_DROP_TABLE, e);
} finally {
db.endTransaction();
closeDB();
}
}
@Override
public void dropAllTable() throws DBException {
String strTabSql = "select name from sqlite_master where type='table' order by name";
LogUtils.d("dropAllTable:" + strTabSql);
try {
openDB(false);
db.beginTransaction();
Cursor cursor = db.rawQuery(strTabSql, null);
while (cursor.moveToNext()) {
String name = cursor.getString(0);
if (name.equals("sqlite_sequence")) {
continue;
}
String sql = "DROP TABLE " + name;
LogUtils.d(sql);
db.execSQL(sql);
}
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
throw new DBException(ErrMsg.ERR_DROP_TABLE, e);
} finally {
db.endTransaction();
closeDB();
}
}
@Override
public void save(T entity) throws DBException {
ContentValues values;
try {
openDB(true);
values = Utils.putValue(fields, entity);
if (values == null || values.size() <= 0) {
throw new DBException(ErrMsg.ERR_SAVE_PARAM);
}
long flag = db.insert(mTableName, null, values);
if (flag < 1) {
throw new DBException(ErrMsg.ERR_SAVE_PARAM);
}
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_SAVE_PARAM, e);
} finally {
closeDB();
}
}
@Override
public void delete(int id) throws DBException {
delete(new int[]{id});
}
@Override
public void delete(int[] ids) throws DBException {
int length = ids == null ? 0 : ids.length;
if (length <= 0) {
throw new DBException(ErrMsg.ERR_DEL_PARAM);
}
Field pkField = ORMUtils.getPKField(fields);
if (pkField == null) {
throw new DBException(ErrMsg.ERR_GET_PRIMARY_KEY);
}
Class pkType = pkField.getType();
String columnName = ORMUtils.getColumnName(pkField);
try {
openDB(true);
StringBuilder sbSql = new StringBuilder("DELETE FROM " + mTableName + " WHERE " + columnName);
if (ids.length == 1) {
sbSql.append(" = ");
} else {
sbSql.append(" in (");
}
String strSep = "";
if (pkType == String.class) {
strSep = "'";
}
StringBuilder strEntityIds = new StringBuilder("");
for (Serializable id : ids) {
strEntityIds.append(strSep);
strEntityIds.append(id);
strEntityIds.append(strSep);
strEntityIds.append(",");
}
strEntityIds.deleteCharAt(strEntityIds.length() - 1);
sbSql.append(strEntityIds.toString());
if (ids.length > 1) {
sbSql.append(")");
}
LogUtils.d(sbSql.toString());
db.execSQL(sbSql.toString());
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_DEL, e);
} finally {
closeDB();
}
}
@Override
public void deleteAll() throws DBException {
try {
openDB(true);
String delSql = "DELETE FROM " + mTableName;
String revSeqSql = "UPDATE SQLITE_SEQUENCE SET SEQ=0 WHERE NAME='" + mTableName + "'";
db.beginTransaction();
db.execSQL(delSql);
db.execSQL(revSeqSql);
db.setTransactionSuccessful();
LogUtils.d(delSql);
LogUtils.d(revSeqSql);
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_DEL, e);
} finally {
db.endTransaction();
closeDB();
}
}
@Override
public void update(T entity) throws DBException {
if (entity == null) {
throw new DBException(ErrMsg.ERR_UPDATE_PARAM);
}
Field pkField = ORMUtils.getPKField(fields);
if (pkField == null) {
throw new DBException(ErrMsg.ERR_GET_PRIMARY_KEY);
}
Object pkValue = Utils.getPropValue(entity, pkField, entityClass);
if (pkValue == null) {
throw new DBException(ErrMsg.ERR_GET_PRIMARY_KEY_VALUE);
}
ContentValues values;
try {
openDB(true);
values = Utils.putValue(fields, entity);
if (values.size() <= 0) {
throw new DBException(ErrMsg.ERR_UPDATE_PARAM);
}
int flag = db.update(mTableName, values, pkField.getName() + "=?",
new String[]{String.valueOf(pkValue)});
if (flag < 1) {
throw new DBException(ErrMsg.ERR_UPDATE_PARAM);
}
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_UPDATE, e);
} finally {
closeDB();
}
}
@Override
public void updateByCondition(String condition, T entity) throws DBException {
if (entity == null) {
throw new DBException(ErrMsg.ERR_UPDATE_PARAM);
}
ContentValues values;
try {
openDB(true);
values = Utils.putValue(fields, entity);
values.remove("id");
if (values.size() <= 0) {
throw new DBException(ErrMsg.ERR_UPDATE_PARAM);
}
int flag = db.update(mTableName, values, condition, null);
if (flag < 1) {
throw new DBException(ErrMsg.ERR_UPDATE_PARAM);
}
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_UPDATE, e);
} finally {
closeDB();
}
}
@Override
public T find(int id) throws DBException {
Field pkField = ORMUtils.getPKField(fields);
if (pkField == null) {
throw new DBException(ErrMsg.ERR_GET_PRIMARY_KEY);
}
String columnName = ORMUtils.getColumnName(pkField);
try {
openDB(true);
String sql = "SELECT * FROM " + mTableName + " WHERE " + columnName + "=?";
LogUtils.d(sql);
Cursor cursor = db.rawQuery(sql, new String[]{id + ""});
List<T> objList = Utils.cursor2Entity(entityClass, cursor);
if (objList != null && objList.size() > 0) {
return objList.get(0);
}
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_FIND, e);
} finally {
closeDB();
}
return null;
}
@Override
public T findLastEntity() throws DBException {
Field pkField = ORMUtils.getPKField(fields);
if (pkField == null) {
throw new DBException(ErrMsg.ERR_GET_PRIMARY_KEY);
}
String columnName = ORMUtils.getColumnName(pkField);
try {
openDB(true);
String sql = "SELECT * FROM " + mTableName + " ORDER BY " + columnName + " desc LIMIT 1";
LogUtils.d(sql);
Cursor cursor = db.rawQuery(sql, new String[]{});
List<T> objList = Utils.cursor2Entity(entityClass, cursor);
if (objList != null && objList.size() > 0) {
return objList.get(0);
}
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_FIND, e);
} finally {
closeDB();
}
return null;
}
@Override
public List<T> findByCondition(String condition) throws DBException {
if (TextUtils.isEmpty(condition)) {
throw new DBException(null);
}
try {
openDB(true);
String sql = "SELECT * FROM " + mTableName + " WHERE " + condition;
LogUtils.d(sql);
Cursor cursor = db.rawQuery(sql, new String[]{});
List<T> objList = Utils.cursor2Entity(entityClass, cursor);
if (objList != null && objList.size() > 0) {
return objList;
}
} catch (Exception e) {
throw new DBException(null);
} finally {
closeDB();
}
return null;
}
@Override
public List<T> findAll() throws DBException {
try {
openDB(true);
String sql = "SELECT * FROM " + mTableName;
LogUtils.d(sql);
Cursor cursor = db.rawQuery(sql, new String[]{});
List<T> objList = Utils.cursor2Entity(entityClass, cursor);
if (objList != null && objList.size() > 0) {
return objList;
}
} catch (Exception e) {
throw new DBException(ErrMsg.ERR_FIND, e);
} finally {
closeDB();
}
return null;
}
/**
* Create and/or open a database that will be used for reading and writing.
*
* @param checkTable True if need check mTableName, false otherwise
* @throws DBException
*/
private void openDB(boolean checkTable) throws DBException {
if (checkTable && TextUtils.isEmpty(mTableName)) {
throw new DBException(ErrMsg.ERR_IS_TABLE_EXISTS);
}
db = getWritableDatabase();
}
/**
* Releases a reference to the database
* closing the database if the last reference was released.
*/
private void closeDB() {
if (db != null && db.isOpen()) {
db.close();
}
}
/**
* database version update information
*/
public class DBUpdateInfo {
public boolean isUpdate = false;
public int from;
public int to;
}
}
那么,数据库查询出来的行,如何转换为实体呢? 就是通过实体类的 set
方法去赋值。
/**
* 数据库查询->实体对象
*/
public static <T extends BaseEntity> List<T> cursor2Entity(Class<T> clazz, Cursor cursor) throws DBException {
List<T> objList = new ArrayList<>();
Field[] fields = getDeclaredField(clazz);
try {
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
T obj = clazz.newInstance();
for (int i = 0; i < cursor.getColumnCount(); i++) {
String strColName = cursor.getColumnName(i);
for (Field field : fields) {
if (field.getName().equals(strColName)) {
strColName = toUpperCaseFirstOne(strColName);
if (cursor.getType(i) == Cursor.FIELD_TYPE_NULL) {
continue;
} else if (cursor.getType(i) == Cursor.FIELD_TYPE_FLOAT) {
clazz.getMethod("set" + strColName, field.getType()).invoke(obj,
cursor.getFloat(i));
} else if (cursor.getType(i) == Cursor.FIELD_TYPE_INTEGER) {
if (field.getGenericType().toString().equals("class java.lang.Boolean")
|| field.getGenericType().toString().equals("boolean")) {
// e.g. boolean isOk; public boolean isOk(){ return isOk; } public void setOk(){}
clazz.getMethod("set" + strColName.replaceFirst("Is", ""), field.getType()).invoke(obj,
cursor.getInt(i) == 1 ? true : false);
} else if (field.getGenericType().toString().equals("class java.lang.Integer")
|| field.getGenericType().toString().equals("int")) {
clazz.getMethod("set" + strColName, field.getType()).invoke(obj,
cursor.getInt(i));
} else if (field.getGenericType().toString().equals("class java.lang.Long")
|| field.getGenericType().toString().equals("long")) {
clazz.getMethod("set" + strColName, field.getType()).invoke(obj,
(long) cursor.getInt(i));
} else if (field.getGenericType().toString().equals("class java.lang.Short")
|| field.getGenericType().toString().equals("short")) {
clazz.getMethod("set" + strColName, field.getType()).invoke(obj,
(short) cursor.getInt(i));
} else if (field.getGenericType().toString().equals("class java.lang.Byte")
|| field.getGenericType().toString().equals("byte")) {
clazz.getMethod("set" + strColName, field.getType()).invoke(obj,
(byte) cursor.getInt(i));
}
} else if (cursor.getType(i) == Cursor.FIELD_TYPE_STRING) {
clazz.getMethod("set" + strColName, field.getType()).invoke(obj,
cursor.getString(i));
} else if (cursor.getType(i) == Cursor.FIELD_TYPE_BLOB) {
clazz.getMethod("set" + strColName, field.getType()).invoke(obj,
cursor.getBlob(i));
} else {
throw new DBException(null);
}
break;
}
}
}
objList.add(obj);
cursor.moveToNext();
}
return objList;
}
} catch (Exception e) {
e.printStackTrace();
throw new DBException(null);
}
return objList;
}
相反的,更新保存数据的时候,我们需要从实体对象取值。
public static <T> ContentValues putValue(Field[] fields, T entity) throws DBException {
ContentValues values = new ContentValues();
for (Field field : fields) {
if (!field.isAccessible())
field.setAccessible(true);
String strColName = ORMUtils.getColumnName(field);
if (strColName == null)
continue;
try {
if (field.getGenericType().toString().equals("class java.lang.String")) {
values.put(strColName, (String) (field.get(entity)));
} else if (field.getGenericType().toString().equals("class java.lang.Boolean")
|| field.getGenericType().toString().equals("boolean")) {
values.put(strColName, (((Boolean) (field.get(entity))) ? 1 : 0));
} else if (field.getGenericType().toString().equals("class java.lang.Byte")
|| field.getGenericType().toString().equals("byte")) {
values.put(strColName, (Byte) field.get(entity));
} else if (field.getGenericType().toString().equals("class [B")) {
values.put(strColName, (byte[]) field.get(entity));
} else if (field.getGenericType().toString().equals("class java.lang.Double")
|| field.getGenericType().toString().equals("double")) {
values.put(strColName, (Double) field.get(entity));
} else if (field.getGenericType().toString().equals("class java.lang.Float")
|| field.getGenericType().toString().equals("float")) {
values.put(strColName, (Float) field.get(entity));
} else if (field.getGenericType().toString().equals("class java.lang.Integer")
|| field.getGenericType().toString().equals("int")) {
values.put(strColName, (Integer) field.get(entity));
} else if (field.getGenericType().toString().equals("class java.lang.Long")
|| field.getGenericType().toString().equals("long")) {
values.put(strColName, (Long) field.get(entity));
} else if (field.getGenericType().toString().equals("class java.lang.Short")
|| field.getGenericType().toString().equals("short")) {
values.put(strColName, (Short) field.get(entity));
} else {
throw new DBException(null);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new DBException(null);
}
}
return values;
}
到这一步,映射关系就基本结束了。 我们可以通过以下方式调用:
DAO<T> dao = new DAO<>(mContext, getDatabaseFullName(dbName), tableName, clazz, dbVer);
if (!TextUtils.isEmpty(tableName) && !dao.isTableExist()) {
// 如果表不存在,则创建!
dao.createTable();
}
dao.findAll(); // 查询所有
dao.findLastEntity(); // 查询最后一条数据
// ...
数据库升级
细心的童鞋会发现,DBUpdateInfo
这个类,就是用来表示数据库版本是否发生了变化,所以我们可以通过监听的方式,去判断数据库版本是否升级。
if (dao.info.isUpdate) {
dao.info.isUpdate = false;
if (listener != null) {
listener.onUpdate(dao, dao.info.from, dao.info.to);
}
}
到此,封装就基本结束了。细化的代码请移步Github:easyDao
感谢阅读~~