`

浅谈Android数据库CRUD操作的封装与实现(二)【Android Hibernate,永别了SQL!】

阅读更多

用了一整天的时间把昨天遗留下来的问题解决了。

既然是要做一个类似Hibernate的工具,最主要的就是尽量减少SQL语句的编写。我采用反向强力解析实体类的办法,实现了自动构建SQL语句的功能。

现在有三个核心类:

 

 

EntityDao是一个泛型接口,定义了所有应该实现的方法。SimpleDao是抽象类,对接口功能进行实现,真正实体的DAO只要继承SimpleDao就可以拥有所有功能。无需任何额外实现、无需任何SQL语句!

首先,看看我的BeanTools。它主要负责对实体进行强力解析,废话不多说,上代码:

 

/**
 * Bean工具类
 * @author EwinLive
 *
 */
public abstract class BeanTools {
	/**
	 * 获取第一个泛型类
	 */
	public static Class<?> getGenericClass(Class<?> clazz) {
		return getGenericClass(clazz, 0);
	}

	/**
	 * 获取泛型类
	 */
	public static Class<?> getGenericClass(Class<?> clazz, int index) throws IndexOutOfBoundsException {
		Type genType = clazz.getGenericSuperclass();
		if (!(genType instanceof ParameterizedType)) {
			return Object.class;
		}
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		if (index >= params.length || index < 0) {
			throw new IndexOutOfBoundsException("Index: " + index + ", Size of Parameterized Type: " + params.length);
		}
		return (Class<?>) params[index];
	}

	/**
	 * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
	 */
	public static void setFieldValue(final Object object, final String fieldName, final Object value) {
		Field field = getDeclaredField(object, fieldName);

		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		}

		makeAccessible(field);

		try {
			field.set(object, value);
		} catch (IllegalAccessException e) {
			//logger.error("不可能抛出的异常:{}", e.getMessage());
		}
	}

	/**
	 * 强行设置Field可访问.
	 */
	protected static void makeAccessible(final Field field) {
		if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
			field.setAccessible(true);
		}
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredField.
	 * 
	 * 如向上转型到Object仍无法找到, 返回null.
	 */
	protected static Field getDeclaredField(final Object object, final String fieldName) {
		//Assert.notNull(object, "object不能为空");
		//Assert.hasText(fieldName, "fieldName");
		for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(fieldName);
			} catch (NoSuchFieldException e) {
				// Field不在当前类定义,继续向上转型
			}
		}
		return null;
	}

	/**
	 * 转换字符串到相应类型.
	 * 
	 * @param value 待转换的字符串
	 * @param toType 转换目标类型
	 */
	public static Object convertStringToObject(String value, Class<?> toType) {
		if (StringTools.isNotEmpty(value)) {
			return ConvertUtils.convert(value, toType);
		} else {
			return null;
		}
	}

	/**
	 * 强行获取私有属性的值
	 */
	public static Object getPrivateProperty(Object object, String propertyName) throws IllegalAccessException, NoSuchFieldException {
		//Assert.notNull(object);
		//Assert.hasText(propertyName);
		Field field = object.getClass().getDeclaredField(propertyName);
		field.setAccessible(true);
		return field.get(object);
	}

	/**
	 * 强行设置私有属性的值
	 */
	public static void setPrivateProperty(Object object, String propertyName, Object newValue) throws IllegalAccessException, NoSuchFieldException {
		//Assert.notNull(object);
		//Assert.hasText(propertyName);
		Field field = object.getClass().getDeclaredField(propertyName);
		field.setAccessible(true);
		field.set(object, newValue);
	}
	
	/**
	 * 获取所有字段
	 * @param entityClass 实体的类型
	 * @return data 
	 * 			返回包含两个数组的HashMap,可参考以下使用方法:
	 * 			String[] fieldName = (String[]) data.get("fieldName");
	 * 			Class<?>[] fieldType = (Class<?>[]) data.get("fieldType");
	 */
	public static HashMap<Object, Object> getAllFiled(Class<?> entityClass){
		HashMap<Object, Object> data = new HashMap<Object, Object>();
		
		Field[]  fields = entityClass.getDeclaredFields();
		String[] fieldName = new String[fields.length];
		Class<?>[] fieldType = new Class<?>[fields.length];
		
		for(int i=0; i<fields.length; i++){
			fieldName[i] = fields[i].getName();//组装名称数组
			fieldType[i] = fields[i].getType();//组装类型数组
		}
		
		data.put("fieldName", fieldName);
		data.put("fieldType", fieldType);
		
		return data;
	}
}
 

呵呵,有了它就可以无视神马private、神马protected了,所向无敌!

EntityDao接口就不多说了,现在我们来实现SimpleDao,这是重头戏。我的思路是根据子类具体提供的实体类型进行强力解析,获取所有字段以及字段的类型。完全自动构建SQl语句。由于保存、更新实体的语句变化不会很大,我只在实例化DAO对象时进行初始化。

 

/**
 * 实现了EntityDao接口,其他实体DAO只要继承它即可拥有所有强大功能。
 * @author EwinLive
 *
 * @param <T>
 * @param <PK>
 */
public abstract class SimpleDao<T, PK extends Serializable> implements EntityDao<T, PK> {
	/**
	 * 实体的类型
	 */
	protected Class<T> entityClass;
	
	/**
	 * 表名
	 */
	protected String tableName;
	
	/**
	 * 数据库管理器
	 */
	protected DataBaseHelper dbHelper;
	
	/**
	 * 保存实体所要执行的SQL语句
	 * 只在创建对象时初始化。
	 */
	protected String saveSql;
	
	/**
	 * 更新实体所要执行的SQL语句
	 * 只在创建对象时初始化。
	 */
	protected String updateSql;
	
	/**
	 * 字段在数据表中所对应的列的索引
	 * 只在创建对象时初始化。
	 */
	protected int[] fieldPostion;
	
	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public DataBaseHelper getDbHelper() {
		return dbHelper;
	}

	public void setDbHelper(DataBaseHelper dbHelper) {
		this.dbHelper = dbHelper;
	}

	public String getSaveSql() {
		return saveSql;
	}

	public void setSaveSql(String saveSql) {
		this.saveSql = saveSql;
	}

	public String getUpdateSql() {
		return updateSql;
	}

	public void setUpdateSql(String updateSql) {
		this.updateSql = updateSql;
	}
	
	/**
	 * 专属构造器
	 * 可通过子类的范型定义取得对象类型Class.
	 * @param tableName 实体对应的表名
	 * @param context 设备上下文,通常是一个Activity对象
	 */
	@SuppressWarnings("unchecked")
	public SimpleDao(String tableName, Context context) {
		this.entityClass = (Class<T>) BeanTools.getGenericClass(getClass());
		this.tableName = tableName;
		this.dbHelper = new DataBaseHelper(context);
		this.saveSql = initSaveSql();
		this.updateSql = initUpdateSql();
		this.fieldPostion = initFieldPostion();
	}

	@Override
	public void save(T entity) throws Exception {
		dbHelper.getReadableDatabase().execSQL(saveSql, getSaveValue(entity));
	}

	
	@SuppressWarnings("unused")
	@Override
	public void remove(PK... ids) {
		if(ids.length > 0){
			StringBuffer sb = new StringBuffer();
			for(PK id : ids){
				sb.append('?').append(',');
			}
			sb.deleteCharAt(sb.length() - 1);
			dbHelper.getReadableDatabase().execSQL("delete from "+ tableName +" where id in(" + sb + ")", (Object[]) ids);
		}
	}

	@Override
	public void upDate(T entity) throws Exception {
		dbHelper.getReadableDatabase().execSQL(updateSql, getUpdateValue(entity));
	}

	@Override
	public T find(PK id) {
		Cursor cursor = dbHelper.getReadableDatabase()
									.rawQuery("select * from " + tableName + " where id=?", new String[]{String.valueOf(id)});
		cursor.moveToNext();
		return getEntityFromCursor(cursor);
	}

	@Override
	public List<T> getScroolData(Integer startResult, Integer maxResult){
		List<T> list = new ArrayList<T>(0);
		Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from " + tableName + " limit ?, ?", 
				new String[]{String.valueOf(startResult), String.valueOf(maxResult)});
		while(cursor.moveToNext()){
			list.add(getEntityFromCursor(cursor));
		}
		return list;
	}

	@Override
	public Long getCount() {
		Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select count(*) from " + tableName, 
				null);
		if(cursor.moveToNext())
			return cursor.getLong(0);
		return 0l;
	}
	
	/**
	 * 初始化保存实体所需的SQL语句
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	protected String initSaveSql(){	
		HashMap data = BeanTools.getAllFiled(entityClass);
		String[] fieldName = (String[]) data.get("fieldName");
		StringBuffer bufferName = new StringBuffer();
		StringBuffer bufferExpr = new StringBuffer();
		
		for(String tmp : fieldName){
			bufferName.append(tmp).append(',');
			bufferExpr.append("?,");
		}
	
		//去除id字段及其属性值
		bufferName.delete(bufferName.indexOf("id"), bufferName.indexOf("id")+3);
		bufferExpr.delete(0, 2);

		//去除多余的分隔符
		bufferName.deleteCharAt(bufferName.length()-1);
		bufferExpr.deleteCharAt(bufferExpr.length()-1);
		
		String sql = "insert into "
			+ tableName
			+ "(" + bufferName.toString() + ") values(" + bufferExpr.toString() + ")";
		
		return sql;
	}
	
	/**
	 * 初始化更新实体所需的SQL语句
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	protected String initUpdateSql(){	
		HashMap data = BeanTools.getAllFiled(entityClass);
		String[] fieldName = (String[]) data.get("fieldName");
		
		StringBuffer sqlBuffer = new StringBuffer();
		sqlBuffer.append("update "+ tableName + " set ");
		for(String tmp : fieldName){
			sqlBuffer.append(tmp).append("=?, ");
		}
	
		//去除id字段及其属性值
		sqlBuffer.delete(sqlBuffer.indexOf(" id=?"), sqlBuffer.indexOf("id") + 5);
		sqlBuffer.deleteCharAt(sqlBuffer.length()-2);
		sqlBuffer.append("where id =?");
		
		return sqlBuffer.toString();
	}
	
	/**
	 * 获取保存实体所需的值
	 * @param entity
	 * @return
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	@SuppressWarnings("rawtypes")
	protected Object[] getSaveValue(T entity) throws IllegalAccessException, NoSuchFieldException{
		HashMap data = BeanTools.getAllFiled(entityClass);
		String[] fieldName = (String[]) data.get("fieldName");
		Object[] values;
		
		int length = fieldName.length;
		values = new Object[length-1];
		int j=0;
		for(int i=0; i<length; i++){
			if("id".equals(fieldName[i].toString())){
				continue;//跳过ID字段
			}
			values[j++] = BeanTools.getPrivateProperty(entity, fieldName[i]);
		}
		return values;
	}
	
	/**
	 * 获取更新实体所需的值
	 * @param entity
	 * @return
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	@SuppressWarnings("rawtypes")
	protected Object[] getUpdateValue(T entity) throws Exception{
		HashMap data = BeanTools.getAllFiled(entityClass);
		String[] fieldName = (String[]) data.get("fieldName");
		Object[] values;
		
		int length = fieldName.length;
		values = new Object[length-1];
		int j=0;
		int id=0;

		for(int i=0; i<length; i++){
			if("id".equals(fieldName[i].toString())){
				id = (Integer) BeanTools.getPrivateProperty(entity, fieldName[i]);
				continue;//跳过ID字段
			}
			values[j++] = BeanTools.getPrivateProperty(entity, fieldName[i]);
		}
		
		
		Object[] values2 = new Object[length];
		System.arraycopy(values, 0, values2, 0, values.length);
		values2[length-1] = id;
		
		return values2;
	}
	
	/**
	 * 初始化字段在数据表中 对应的索引
	 * @param cursor
	 */
	@SuppressWarnings("rawtypes")
	protected int[] initFieldPostion(){
		HashMap data = BeanTools.getAllFiled(entityClass);
		String[] fieldName = (String[]) data.get("fieldName");
		int length = fieldName.length;
		int[] postion = new int[length];
		Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from " + tableName + " limit ?, ?", new String[]{"0", "2"});
		for(int i =0; i<length; i++){
			postion[i] = cursor.getColumnIndex(fieldName[i]);
		}
		
		return postion;
	}
	
	/**
	 * 从游标中获取实体
	 * @param cursor 游标
	 * @return T 实体对象
	 */
	@SuppressWarnings("rawtypes")
	public T getEntityFromCursor(Cursor cursor){
		HashMap data = BeanTools.getAllFiled(entityClass);
		String[] fieldName = (String[]) data.get("fieldName");
		Class<?>[] fieldType = (Class<?>[]) data.get("fieldType");
		int length = fieldName.length;
		
		T entity = null;
		String db_data;
		String fieldTypeName;
		try {
			entity = entityClass.newInstance();
			for(int i=0;i<length;i++){
				fieldTypeName = fieldType[i].getSimpleName();
				db_data = cursor.getString(fieldPostion[i]);
				if(null != db_data){
					if("String".equals(fieldTypeName)){
						BeanTools.setFieldValue(entity, fieldName[i], db_data);
					}else if("int".equals(fieldTypeName)){
						BeanTools.setFieldValue(entity, fieldName[i], Integer.parseInt(db_data));
					}else if("long".equals(fieldTypeName)){
						BeanTools.setFieldValue(entity, fieldName[i], Long.getLong(db_data));
					}
					else if("float".equals(fieldTypeName)){
						BeanTools.setFieldValue(entity, fieldName[i],Float.parseFloat(db_data));
					}
				}
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return entity;
	}
}
 

 

好了现在大部分工作都已经完成了。在你的项目中,有多少个实体就创建几个DAO吧,只要继承SimpleDao就行了!(注意填上具体对应的实体类型)比如:

 

public class SimpleMessageDao extends SimpleDao<SimpleMessage, Integer> {
	/**
	 * 定义该实体对应数据库的表名
	 */
	public static final String TABLE_NAME = "simple_message";
	
	/**
	 * 简化的构造器
	 * @param context
	 */
	public SimpleMessageDao(Context context) {
		this(TABLE_NAME, context);
	}
	/**
	 * 重写父类的构造器
	 * @param tableName
	 * @param context
	 */
	public SimpleMessageDao(String tableName, Context context) {
		super(TABLE_NAME, context);
	}
}
   

对,仅仅是继承就足够了!

你现在就可以随便 new XXXDao(context)了,他已经可以帮你进行增删改查的操作。是不是忒方便了!

 

 

 

 

============================完整原创,你可以随意使用其中的代码,但请遵循开源精神!============================

2
0
分享到:
评论
9 楼 zhao_xiaolu 2014-12-01  
initFieldPostion 为什么返回的总是-1 呢,什么原因啊
8 楼 yydriver 2012-07-03  
写的很不错,不过缺少两个文件啊
DataBaseHelper
ConvertUtils
7 楼 yanga520 2011-12-14  
DataBaseHelper.java这个文件咋的没有,是不是忘记了
6 楼 leo852drv 2011-11-19  
的确很强大,省了居多代码,谢谢。。。
5 楼 lovehzh 2011-06-09  
不错的东西,现在正在弄这个数据库的封装,先谢谢了!
4 楼 seatide 2011-03-09  
虽然提高了开发效率,但是反射所用时间几乎是平常调用的100倍以上,特别是android手机资源不多的情况下。   很纠结,但是这样的确可以省掉很多代码
3 楼 lvan-y 2011-02-22  
缺少了DataBaseHelper.java 文件!
2 楼 EwinLive 2010-12-07  
稍纵即逝 写道
实用价值很高,正好可以拿来用,谢谢……

不过有点郁闷,用反射导致私有变量都能访问了……几乎都是public……这很暴力,很邪恶,破坏了java的封装特性……不过很实用,好纠结……


其实这个版本还是很低级的,性能和安全还有待改进。如果你有什么改进意见一定要告述我啊。
现在把源码附上(有额外惊喜哦!)。望大家共同改进。
1 楼 稍纵即逝 2010-12-07  
实用价值很高,正好可以拿来用,谢谢……

不过有点郁闷,用反射导致私有变量都能访问了……几乎都是public……这很暴力,很邪恶,破坏了java的封装特性……不过很实用,好纠结……

相关推荐

    Hibernate与JDBC对于数据库CRUD操作性能示例

    Hibernate与JDBC对于数据库的性能操作对比事例,配置,更多信息资源

    android数据库的crud操作

    通过对`EntityDao`接口的设计以及具体实现`SimpleMessageDao`类的分析,我们可以看到在Android开发中,通过合理的抽象和封装可以大大简化数据库操作的复杂度,并提高代码的复用性和维护性。此外,利用事务处理还可以...

    利用享元模式封装数据库CRUD

    本篇文章将深入探讨如何利用享元模式来封装数据库的CRUD(Create、Read、Update、Delete)操作。 首先,我们需要理解享元模式的基本概念。享元模式是一种结构型设计模式,其核心思想是共享对象,通过共享已经存在的...

    Python数据库CRUD操作全指南:从连接到实现

    通过直接执行SQL语句或使用ORM框架,我们可以方便地实现CRUD操作。正确的事务管理和错误处理对于确保数据库操作的一致性和程序的健壮性至关重要。通过本文的示例,你应该能够掌握在Python中实现数据库CRUD操作的基本...

    android sqlite数据库封装 实现crud

    数据库的方式,常用在存储一系列的结构复杂的数据,轻量级的数据库SQlit使用起来还是比较简单,但是总想能像hibernate似的框架可以进行下封装,实现orm并且可以实现简单的rcud。项目中只有封装包,使用过程很简单,...

    Android sqlite数据库操作通用框架AHibernate

    - **Session:** 类似于 Hibernate 中的概念,是操作数据库的会话,负责对象的持久化和加载。 - **Criteria 查询:** 提供一种动态构建查询的方式,不需要提前知道 SQL 语句。 - **实体状态:** 包括瞬时态、持久态...

    Android之采用execSQL与rawQuery方法完成数据的添删改查操作详解

    代码如下:/* Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。...

    android sql封装工具类

    首先,"android sql封装工具类"是为了简化Android应用中对SQLite数据库的操作而设计的。通常,开发者需要编写大量的SQL语句和相关的CRUD(Create, Read, Update, Delete)操作,这不仅繁琐且容易出错。通过封装,...

    Android 对sqlite的封装,实现CRUD

    大家好,个人觉得用Sqlite数据库时,经常需要进行机械性的CRUD操作,故对其进行了一下封装,希望能起到抛砖引玉的作用。 目的:封装共有的CRUD 下面简单的说一下使用步骤,如果觉得多余,可以无视。 1. 实现自己...

    Java对数据库CRUD DEMO

    Java对数据库的CRUD(Create, Read, Update, Delete)操作是编程中常见的任务,尤其在Web应用开发中。这个DEMO提供了简化数据库交互的方法,避免了手动编写大量重复的SQL语句。以下是对这个"Java对数据库CRUD DEMO...

    jdbc资料(数据库crud操作)

    【jdbc资料(数据库crud操作)】 JDBC(Java Database Connectivity)是Java编程语言中用于与关系数据库交互的一种标准接口。它是由Sun Microsystems公司提出的,现在是Oracle公司的一部分,旨在为Java开发者提供一...

    android sqllite数据库crud+分页

    以上就是Android中SQLite数据库的CRUD操作和分页加载的基本实现。实际应用中,可能还需要考虑线程安全、事务处理、数据同步等问题。对于初学者来说,理解这些概念并能熟练运用,将为开发更复杂的Android应用打下坚实...

    Qt sqlite 数据库操作封装

    接着,打开数据库的操作与生成数据库类似,只需确保不指定数据库名称以创建新的,而是提供已存在的数据库路径。一旦数据库被打开,我们就可以通过QSqlQuery或QSqlTableModel等类执行SQL语句了。 对于执行SQL语句,...

    Android数据库OrmLite框架封装详解完美好友列表Demo

    4. **操作数据库**:使用`OrmLiteDao`接口实例,可以方便地执行数据库操作。例如,添加好友: ```java Dao, Integer&gt; friendDao = helper.getFriendDao(); Friend newFriend = new Friend(); newFriend.setName...

    传智播客JDBC_完成对数据库的CRUD操作.rar

    在“06_传智播客JDBC_完成对数据库的CRUD操作”中,可能详细讲解了以上概念,并通过实例演示了如何在Java程序中实现这些操作。学习这个教程,开发者能够熟练掌握JDBC的基础用法,进一步提升数据库操作能力。同时,...

    struts2与hibernate的整合实现数据的crud

    5. **DAO层设计**:创建Hibernate的Data Access Object(DAO)接口和实现类,这里包含对数据库的CRUD操作方法,比如使用Session对象执行SQL语句。 6. **Service层设计**:创建Service接口和实现类,将业务逻辑封装...

    基于hibernate的简单数据库实现

    通过Hibernate,我们可以将Java对象与数据库中的表进行映射,实现对象的CRUD(创建、读取、更新、删除)操作。 1. **对象关系映射(ORM)概念** ORM是将数据库中的表格数据映射到面向对象模型的一种技术。它允许...

    Hibernate基本增删改CRUD操作

    在Java开发中,Hibernate是一个非常流行的持久化框架,它简化了数据库操作,使得开发者无需直接编写SQL语句,就能实现对象与关系数据库之间的映射。本教程将深入讲解如何使用Hibernate进行基本的创建(Create)、...

    delphiXE7开发android直接操作SQL2008数据库

    在本主题中,我们将深入探讨如何使用Delphi XE7在Android环境中开发应用程序,以便直接操作远程的SQL Server 2008数据库。这涉及到客户端(Android应用)和服务器端的交互,以及对数据库的基本CRUD(创建、读取、...

    HIbernate免注解实现操作数据库 及Hibernate3连接SQL的BUG解决办法

    本篇将深入探讨如何在Hibernate中实现数据库操作而无需使用注解,以及如何解决在Hibernate 3中遇到的连接SQL的常见问题。 首先,让我们了解在Hibernate中免注解实现数据库操作的基本步骤: 1. **配置Hibernate**: ...

Global site tag (gtag.js) - Google Analytics