Android通用DAO(数据访问对象)设计和实现

1、前言:

在Android的应用开发中,往往会涉及到许多数据的存储和交互,其中,内嵌的sqlite数据库会作为首选的方案,在一些涉及比较多的数据交互情境中,通常表现为表的数量比较多,我们就有必要开发一个比较通用的数据访问框架了。

2、设计目标:

(1):能够方便地创建表和对象的对应关系.[这里我们以注解的方式实现]

(2):能够以统一的方式方便的进行数据的增删查改.

也就是能够使用类似Hibernate等框架的使用: 如增加一条记录的使用习惯:

  TUser user = new TUser(); user.setId(“id”); userDao.insert(user).

3、实现结果初体验:

(1):描述表和对象关系:

@Table(name = "t_user")
public class TUser {

	@Column(name="user_id",type=Column.TYPE_BOOLEAN,isPrimaryKey=true)
	private Integer userId;
	
	@Column(name="user_name",type=Column.TYPE_STRING)
	private String userName;

	/**
	 * 必须提供无参构造函数
	 */
	public TUser() {}
	
	public TUser(String userName) {
		this.userName = userName;
	}
        
        //getter

        //setter 
	
}

(2):增删查改

//Insert An Object
		IBaseDao<TUser> userDao = DaoFactory.createGenericDao(this, TUser.class);
		userDao.insert(new TUser("AAAA"));
		
		//Insert Object List
		List<TUser> insertUserList = new ArrayList<TUser>();
		for(int i = 0; i<10;++i){
			insertUserList.add(new TUser("BBB"+i));
		}
		userDao.batchInsert(insertUserList);
		
		//只有一条记录的查找
		List<TUser> userList = userDao.queryByCondition("user_name=?", "AAAA");
		
		//InsertOrUpdate
		userDao.insertOrUpdate(new TUser("AAAA"), "user_name"); //update where user_name='AAAA'
		userDao.insertOrUpdate(new TUser("CCCC"), "user_name"); //insert CCCC

实现结果符合了设计目标,用起来还是很方便的.

4、实现类和接口说明:

以下内容应结合代码看.[com.darcy.dao]包内,完整的代码请点击这里获取。

(1)总览:

.DaoFactory: 提供了集中获取数据操作对象工厂,并缓存该对象.

.DBUtils: 封装了Android API提供的数据库实现

.IBaseDao: 面向用户的接口

.GenericDao: IBaseDao的实现类

.GenericDao.DBTransction: 封装了对事务的操作.

.QueryResult: 对查询结果一行记录的封装类.封装后的类可以尽最大努力取到你想要的值,如数据库中存储1.0为text类型,则通过QueryResult.getIntProperty()可以获得正确的整型值1

.SqlHelper: 拼接SQL语句AND两个对等对象之间直交换的辅助类

.Table: 表和对象的映射注解类

(2)接口设计:
 

public interface IBaseDao<T> {
	
	/**
	 * 创建表
	 */
	void createTable();

	/**
	 * 插入一条记录
	 * 
	 * @param model
	 * @return
	 */
	boolean insert(T model);

	/**
	 * 插入多条记录
	 * 
	 * @param model
	 * @return
	 */
	boolean batchInsert(List<T> dataList);

	/**
	 * 更新记录
	 * 
	 * @param model
	 * @param whereClause
	 * @param whereArgs
	 * @return
	 */
	boolean update(T model, String whereClause, String... whereArgs);

	/**
	 * 决定是否insert或者Update
	 * @param model
	 * @param bindColumnNames 绑定的列名 ,默认是主键
	 * @return
	 */
	boolean insertOrUpdate(T model,String... bindColumnNames);
	
	/**
	 * 删除记录
	 * 
	 * @param whereClause
	 * @param whereArgs
	 * @return
	 */
	boolean delete(String whereClause, String... whereArgs);


	/**
	 * 删除全部记录
	 * @return
	 */
	boolean deleteAll();
	
	/**
	 * 根据条件查询
	 * 
	 * @param whereClause
	 * @param whereArgs
	 * @return
	 */
	List<T> queryByCondition(String selection, String... selectionArgs);

	/**
	 *  根据条件查询
	 * @param columns
	 * @param selection
	 * @param orderBy
	 * @param selectionArgs
	 * @return
	 */
	List<T> queryByCondition(String[] columns, String selection,
			String orderBy, String... selectionArgs);

	/**
	 * 根据条件查询
	 * @param columns
	 * @param selection
	 * @param groupBy
	 * @param having
	 * @param orderBy
	 * @param selectionArgs
	 * @return
	 */
	List<T> queryByCondition(String[] columns, String selection,
			String groupBy, String having, String orderBy,
			String... selectionArgs);
	
	/**
	 * 只有唯一一条记录的查询
	 * 
	 * @return 如果没有则返回null
	 */
	T queryUniqueRecord(String selection,String... selectionArgs);
	
	/**
	 * 自定义查询
	 * @param sql
	 * @param bindArgs
	 * @return
	 */
	List<QueryResult> execQuerySQL(String sql, String... bindArgs);
	
	
	/**
	 * 执行Insert/Update/Delete等其他非查询SQL
	 * @param sql
	 * @param bindArgs
	 * @return
	 */
	boolean execUpdateSQL(String sql, Object... bindArgs);
}

为什么这么多queryByCondition? 最小接口原则,这样在用的时候提供更多选择,代码可变得更加精简一些.

为什么提供execQuerySQL: 为了支持一些复杂的查询,如连接表查询, 否则可能就要在代码中写很多此查询才能做到.

同理execUpdateSQL:

不合理的地方:实际上这里可以查询任何表,破坏了封装性。

(3)接口实现(GenericDao):

createTable: 关键方法在SqlHelper.getCreateTableSQL中,这里通过Java反射机制对采集传进来的Class信息,然后拼接成创建表的SQL语句.

insert | update :关键方法:SqlHelper.parseModelToContentValues,其关键点也是在于利用Java反射机制来把model中的值转化到ContentValue中.

queryByCondition: 也是同理.

insertOrUpdate(T model,String… bindColumnNames):这里关键说一下insertOrUpdate的实现,创建表的时候我们先保存好该表的主键,以后调用该方法时:如果model中的主键值已经存在,则update,反之则insert,以上是不带参数的情况,如果后面带了列名,则根据列名进行联合查询该记录是否存在决定insert Or update。

5、总结:

这个是今年在工作中其中的一点总结,在想方设法提供代码的可重用性是便做了这个东西,但是,时间太赶,所以就本着够用的原则先用着,代码还有很多要完善的地方,但在该基础上再进行构建一些新的支持应该就比较容易了。

   

    原文作者:移动开发
    原文地址: https://my.oschina.net/daxia/blog/193380
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞