首页 > 系统 > Android > 正文

android——框架的实现系列(数据库操作)

2019-11-09 15:55:23
字体:
来源:转载
供稿:网友

           原创文章,转载请注明出处

           请先阅读《android——仿mybatis的半自动数据库映射的实现 》

                           《 android——框架的实现系列(依赖注入的实现) 》

          这里在实现了依赖注入的基础上,对之前的仿mybatis实现进行改进。

          实现目标

          1、通过注释指定数据库,建表,更新版本,比如:

@DB(db_name="myTask.db",db_version=13,db_xml_id=R.xml.db)public class BaseHelper extends DBHelper{	public BaseHelper(Context context) {		super(context);	}}               只需要添加该注释,并且继承DBHelper类即可

               这里的db_name表示数据库名,db_version表示版本,如果版本增加会自动更新数据库表,db_xml_id为一个xml,用来说明表结构,需要用户自行配置

               表结构配置文件举例说明,我们看一下R.xml.db的内容

<?xml version="1.0" encoding="UTF-8"?><db>        <table name="t_task">	    <colunm name="id" type="varchar(64)" PRimaryKey="true"/>	    <colunm name="title" type="varchar(30)"/>	    <colunm name="brief" type="text"/>	    <colunm name="beginTime" type="datetime"/>	    <colunm name="repeat" type="varchar(30)"/>	    <colunm name="requestCode" type="varchar(30)"/>	    <colunm name="isRun" type="varchar(8)"/>	</table>    </db>          2、对sql进行管理,并且将查询的结果封装为对象。调用sql的工具类可以依赖注入

           所有的sql可以用一个xml文件进行管理

           比如一个叫R.xml.task的xml文件如下

          

<?xml version="1.0" encoding="UTF-8"?><mapper namespace="com.xf.ztime.task.mapper.TaskMapper">	<select id="findTask" resultType="com.xf.ztime.task.model.TaskVo">	    select id,title,brief,isRun,beginTime,repeat,requestCode	    from t_task	    limit ${rows} offset ${offset}	</select>		<select id="findById" resultType="com.xf.ztime.task.model.TaskVo">	    select id,title,brief,isRun,beginTime,repeat,requestCode	    from t_task		where id='${id}'	</select>		<select id="findByRequestCode" resultType="com.xf.ztime.task.model.TaskVo">	    select id,title,brief,isRun,beginTime,repeat,requestCode	    from t_task		where requestCode='${requestCode}'	</select>		<insert id="insert">	    insert into t_task	    (id,title,brief,beginTime,repeat,requestCode,isRun)	    values('${id}','${title}','${brief}','${beginTime}','${repeat}','${requestCode}','${isRun}')	</insert>		<update id="update">	    update t_task	    set title='${title}',	        brief='${brief}',	        beginTime='${beginTime}',	        repeat='${repeat}'	    where id='${id}'	</update>		<update id="updateRun">	    update t_task	    set isRun='${isRun}'	    where id='${id}'	</update>		<delete id="delete">	    delete from t_task	    where id='${id}'	</delete>	</mapper>               这里和mybatis很类似,就不解释了。mapper标签指定了工具类,该工具类用户只需要定义其接口即可,方法名为语句的id,比如:

             

public interface TaskMapper {	List<TaskVo> findTask(TaskVo vo);	List<TaskVo> findById(TaskVo vo);	List<TaskVo> findByRequestCode(TaskVo vo);	void insert(TaskVo vo);	void update(TaskVo vo);	void updateRun(TaskVo vo);	void delete(TaskVo vo);	}               在需要操作数据库的地方可以依赖注入mapper,比如在TaskService类中:

public class TaskService implements Serializable{@Table(value=R.xml.task)	private TaskMapper taskMapper;}              @Table注释的value指定sql语句管理的xml

              到此为止,就可以用taskMapper进行数据库操作了,而不需要像之前那样需要继承BaseService类

              实现方法

              如何实现以上功能呢?

              1、DaoHelper类

              该类是真正做事情的劳动力,所有数据库的操作将由它来完成。因此它也是所有类中最核心的类。

              它完成如下工作:

               a、对xml进行解析,获得sql语句进行建表,删表。增删改查操作。

               b、对查询结果封装成对象。

               c、通过动态代理,把自己绑定成用户定义的mapper接口,从而自动实现接口。

             

              2、DBHelper类

              该类用于管理数据库的创建,版本升级,它继承自SQLiteOpenHelper类

              对于建表,删表工作,它将转交给DaoHelper类来完成

              那么,@DB注释中的数据库名,版本号和表配置文件如何获得呢,请看下面的类

              3、BeanHelper类

              还记得该类吗,上篇文章中介绍过,它负责所有依赖注入的类的实例化工作。

              在这里,它将会有一个新的工作,把@DB注释中指定的数据库名,版本号,表配置文件告诉DBHelper类。

              同样,该工作将在static静态块中进行,保证框架一开始就能保证数据库的状态。

             4、IocHelper类

             最后一个问题,如何通过@Table注释依赖注入mapper,则将由IocHelper类负责。

             该类的类名起的有问题,因为我们还有一个类叫做InjectHelper类。

             在上一篇文章中我们知道InjectHelper类负责标注了@Compoent的注释的依赖注入。为什么这里又有一个类似的类。原因是刚开始写框架的时候只想着做数据库方面的,因此定义了IocHelper类,后来又做了@Compoent,没有把关系处理好。

              题外话说完,该类通过反射扫描字段中是否有@Table注释,如果有则通过动态代理把daoHelper做为mapper进行注入。

           到此为止就已经把思路完全介绍完了

           看一下代码吧

           DaoHelper类

@Componentpublic class DaoHelper implements InvocationHandler{	private static String TAG="Loader";	private DBHelper helper;	public static String dbPath;	private static final String MAPPER_NAMESPACE="namespace";	private static final String ID="id";	private static final String TABLE_NAME="name";	private static final String COLUNM_NAME="name";	private static final String COLUNM_TYPE="type";	private static final String COLUNM_PRIMARY="primaryKey";	private static final String COLUNM_AUTOINCREMENT="autoincrement";	private static final String RESULT_TYPE="resultType";		private Context context;	private XmlResourceParser xmlParser;	private int xmlId;	public static int dbXmlId;	private SQLiteDatabase db;	private Dao dao;	public DaoHelper(){			}		public DaoHelper(Activity context){		this.helper=new DBHelper(context);		this.context=context;	}		/**	 * 	 * 动态代理,实际执行的是Dao类的操作	 * 	 */	@Override	public Object invoke(Object proxy, Method method, Object[] args)			throws Throwable {		Object result=null;		if (Object.class.equals(method.getDeclaringClass())) {			try {				result=method.invoke(this, args);			} catch (Throwable t) {				t.printStackTrace();			}		} else {			result=run(method, args);		}		return result;	}		/**	 * 	 * 获得xml关联的接口	 * 	 * @return	 * @throws XmlPullParserException	 * @throws ClassNotFoundException	 * @throws IOException	 */	public Class getMapper() throws XmlPullParserException, ClassNotFoundException, IOException{		Class c=null;		Resources res = context.getResources();     		xmlParser = res.getXml(xmlId);		int eventType = xmlParser.getEventType();  	    while (eventType != XmlResourceParser.END_DOCUMENT) {  	    	 if (eventType == XmlResourceParser.START_TAG) {	    		 String tagName = xmlParser.getName();	    		 if(tagName.equals("mapper")){	    			 int count=xmlParser.getAttributeCount();	    			 for(int i=0;i<count;i++){	    				if(xmlParser.getAttributeName(i).equals(MAPPER_NAMESPACE)) {	    					c=Class.forName(xmlParser.getAttributeValue(i));	    				}	    			 }	    		 }	    	 }	    	 eventType = xmlParser.next();  	    }	    return c;	}		public Object run(Method method,Object[] args) throws Exception{		if(dao==null)			dao=new Dao();		Object result=null;		String name=method.getName();		//初始化数据库表,如果存不存在则建表		openDB();		result=dao.exec(name,args);		return result;	}		public void initTable(SQLiteDatabase db) throws XmlPullParserException, IOException{		Resources res = context.getResources();   		xmlParser = res.getXml(dbXmlId);		int eventType = xmlParser.getEventType();  		    // 判断是否到了文件的结尾                while (eventType != XmlResourceParser.END_DOCUMENT) {              	//启始标签            	 if (eventType == XmlResourceParser.START_TAG) {            		 String tagName = xmlParser.getName();            		 if(tagName.equals("table")){            	   		 StringBuffer sb=new StringBuffer();	            		 sb.append("create table if not exists ");            			 int count=xmlParser.getAttributeCount();            			 for(int i=0;i<count;i++){            				 //表名            				 if(xmlParser.getAttributeName(i).equals(TABLE_NAME)){            					 sb.append(xmlParser.getAttributeValue(i)+"( ");            				 }            			 }            			 int e=xmlParser.next();            			 if(e==XmlResourceParser.START_TAG){            				 tagName = xmlParser.getName();            				 //字段            				 while(tagName.equals("colunm")){            					if(e==XmlResourceParser.START_TAG){	    	            			sb.append(xmlParser.getAttributeValue(null, COLUNM_NAME)+" ");	    	            			sb.append(xmlParser.getAttributeValue(null, COLUNM_TYPE)+" ");	    	            			String isPrimary=xmlParser.getAttributeValue(null, COLUNM_PRIMARY);	    	            			if(isPrimary!=null && isPrimary.equals("true"))	    	            				sb.append("primary key ");	    	            			String isAutoIncrement=xmlParser.getAttributeValue(null, COLUNM_AUTOINCREMENT);	    	            			if(isAutoIncrement!=null && isAutoIncrement.equals("true"))	    	            				sb.append("autoincrement ");	    	            			sb.append(",");            					}    	            			e=xmlParser.next();    	            			tagName = xmlParser.getName();            				 }            				 String s=sb.substring(0,sb.length()-1);            				 s=s+")";            		         Log.d(TAG, "建表语句为:"+s);            		         db.execSQL(s);            			 }            		 }            	 }            	 //移到下一个标签            	 eventType = xmlParser.next();                }	}		public void dropTable(SQLiteDatabase db) throws XmlPullParserException, IOException{		Resources res = context.getResources();   		xmlParser = res.getXml(dbXmlId);		int eventType = xmlParser.getEventType();  		    // 判断是否到了文件的结尾                while (eventType != XmlResourceParser.END_DOCUMENT) {              	//启始标签            	 if (eventType == XmlResourceParser.START_TAG) {            		 String tagName = xmlParser.getName();            		 if(tagName.equals("table")){            	   		 StringBuffer sb=new StringBuffer();	            		 sb.append("drop table if exists ");            			 int count=xmlParser.getAttributeCount();            			 for(int i=0;i<count;i++){            				 //表名            				 if(xmlParser.getAttributeName(i).equals(TABLE_NAME)){            					 sb.append(xmlParser.getAttributeValue(i));            				 }            			 }            			 db.execSQL(sb.toString());            		 }            	 }            	 //移到下一个标签            	 eventType = xmlParser.next();                }	}		private void openDB(){		if(helper==null)			this.helper=new DBHelper(context);		db=helper.getReadableDatabase();	}		class Dao{		public Object exec(String name,Object[] args) throws Exception{			Object result = null;			//解析xml			Resources res = context.getResources();   			xmlParser = res.getXml(xmlId);		    int eventType = xmlParser.getEventType();  		    // 判断是否到了文件的结尾                while (eventType != XmlResourceParser.END_DOCUMENT) {              	//启始标签            	 if (eventType == XmlResourceParser.START_TAG) {            		 String tagName = xmlParser.getName();            		 if(tagName.equals("select")){            			 //id            			 String id=xmlParser.getAttributeValue(null, ID);            			 if(name.equals(id)){	            			 //返回类型	        				 String type=xmlParser.getAttributeValue(null, RESULT_TYPE);	            			 //获得sql语句	            			 eventType = xmlParser.next();  	            			 String sql=xmlParser.getText();	            			 //绑定参数	            			 sql=bindArgs(sql,args);	            			 Cursor cursor=db.rawQuery(sql,new String[]{});	        				 //映射结果	        				 if(!type.equals("java.util.Map")){	        					 List list=new ArrayList();	        					 Class c=Class.forName(type);	        					 while(cursor.moveToNext()){		        					 Object o=c.newInstance();		        					 List<Field> fs=getDeclaredField(o);		        					 for(Field f : fs){		        						 String fName=f.getName();		        						 if(fName.equals("serialVersionUID"))		        							 continue;		        						 Method m=getDeclaredMethod(o,"set"+fName.substring(0,1).toUpperCase()+fName.substring(1),f.getType());		        						 if(f.getType()==String.class){		        							 try{		        								 m.invoke(o, cursor.getString(cursor.getColumnIndexOrThrow(fName)));		        							 }catch(Exception e){		        								 m.invoke(o, "");		        							 }		        						 }		        						 if(f.getType()==Integer.class){		        							 try{		        							 	m.invoke(o, cursor.getInt(cursor.getColumnIndexOrThrow(fName)));			        						 }catch(Exception e){		        								 m.invoke(o, 0);		        							 }		        						 }		        						 if(f.getType()==Float.class){		        							 try{		        							 	m.invoke(o, cursor.getFloat(cursor.getColumnIndexOrThrow(fName)));			        						 }catch(Exception e){		        								 m.invoke(o, 0);		        							 }		        						 }		        						 if(f.getType()==Double.class){		        							 try{		        							 	m.invoke(o, cursor.getDouble(cursor.getColumnIndexOrThrow(fName)));			        						 }catch(Exception e){		        								 m.invoke(o, 0);		        							 }		        						 }		        					 }		        					 list.add(o);	        					 }	        					 result=list;	        				 }	        				 else{	        					 	        				 }	        				 return result;            			 }            		 }else if(tagName.equals("insert")){            			 String id=xmlParser.getAttributeValue(null, ID);            			 if(name.equals(id)){	            			 //获得sql语句	            			 eventType = xmlParser.next();  	            			 String sql=xmlParser.getText();	            			 //绑定参数	            			 sql=bindArgs(sql,args);	            			 db.execSQL(sql);            			 }            		 }else if(tagName.equals("update")){            			 String id=xmlParser.getAttributeValue(null, ID);            			 if(name.equals(id)){	            			 //获得sql语句	            			 eventType = xmlParser.next();  	            			 String sql=xmlParser.getText();	            			 //绑定参数	            			 sql=bindArgs(sql,args);	            			 db.execSQL(sql);            			 }            		 }else if(tagName.equals("delete")){            			 String id=xmlParser.getAttributeValue(null, ID);            			 if(name.equals(id)){	            			 //获得sql语句	            			 eventType = xmlParser.next();  	            			 String sql=xmlParser.getText();	            			 //绑定参数	            			 sql=bindArgs(sql,args);	            			 db.execSQL(sql);            			 }            		 }            	 }            	 //移到下一个标签            	 eventType = xmlParser.next();                }			return result;		}				public Cursor query(){			return null;		}	}		/**	 * 	 *  绑定参数	 * 	 * @param sql	 * @param args	 * @return	 * @throws IllegalaccessException	 * @throws IllegalArgumentException	 * @throws InvocationTargetException	 */	private String bindArgs(String sql,Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{		 if(!args[0].getClass().getName().equals(Map.class.getName())){			 Class argc=args[0].getClass();			 List<Field> fs=getDeclaredField(args[0]);			 for(Field f: fs){				 String fName=f.getName();				 Method m=getDeclaredMethod(args[0],"get"+fName.substring(0,1).toUpperCase()+fName.substring(1));				 //Log.d(TAG, "解析字段:"+fName);				 String fValue="";				 if(!fName.equals("serialVersionUID"))					fValue=String.valueOf(m.invoke(args[0]));				 sql=sql.replace("${"+fName+"}", fValue);			 }		 }		 //绑定参数,参数类型为map		 else{			 		 }		 return sql;	}		private List<Field> getDeclaredField(Object object){          List<Field> fa = new ArrayList<Field>() ;                    Class<?> clazz = object.getClass() ;                    for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {              try {                  Field[] fs=clazz.getDeclaredFields();                for(Field f : fs){                	fa.add(f);                }            } catch (Exception e) {              	            }           }                return fa;      }     		private Method getDeclaredMethod(Object object, String methodName, Class<?> ... parameterTypes){          Method method = null ;                    for(Class<?> clazz = object.getClass() ; clazz != Object.class ; clazz = clazz.getSuperclass()) {              try {                  method = clazz.getDeclaredMethod(methodName, parameterTypes) ;                  return method ;              } catch (Exception e) {                             }          }                    return null;      }  	public Context getContext() {		return context;	}	public void setContext(Context context) {		this.context = context;	}	public int getXmlId() {		return xmlId;	}	public void setXmlId(int xmlId) {		this.xmlId = xmlId;	}	public DBHelper getHelper() {		return helper;	}	public void setHelper(DBHelper helper) {		this.helper = helper;	}	}

         DBHelper类

public class DBHelper extends SQLiteOpenHelper {	public static String DB_NAME = "myTask.db";    public static int DB_VERSION = 1;     public static int DB_RESOURCE;    private Context context;    private DaoHelper daoHelper;        public DBHelper(Context context) {        this(context, DB_NAME, null, DB_VERSION);    }    private DBHelper(Context context, String name, CursorFactory factory, int version) {        super(context, name, factory, version);        this.context=context;    }    @Override    public void onCreate(SQLiteDatabase db) {    	try {    		createTable(db);		} catch (Exception e) {			e.printStackTrace();		}    }    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    	try {    		dropTable(db);    		createTable(db);		} catch (Exception e) {			e.printStackTrace();		}    }        @Override    protected void finalize() throws Throwable {        this.close();        super.finalize();    }	public DaoHelper getDaoHelper() {		return daoHelper;	}	public void setDaoHelper(DaoHelper daoHelper) {		this.daoHelper = daoHelper;	}		private void createTable(SQLiteDatabase db) throws XmlPullParserException, IOException{		DaoHelper.dbXmlId=DB_RESOURCE;		daoHelper=new DaoHelper();		daoHelper.setContext(context);		daoHelper.initTable(db);	}	private void dropTable(SQLiteDatabase db) throws XmlPullParserException, IOException{		DaoHelper.dbXmlId=DB_RESOURCE;		daoHelper=new DaoHelper();		daoHelper.setContext(context);		daoHelper.dropTable(db);	}	}

              BeanHelper类

public class BeanHelper<T> {	private static final Map<Class<?>,Object> BEAN_MAP=new HashMap<Class<?>,Object>();		static{		Set<Class<?>> classSet=ClassHelper.getBeanClassSet();		for(Class<?> c : classSet){			try {				if(DBHelper.class.isAssignableFrom(c)){					//把数据库名,版本信息,配置文件交给DBHelper,从而使数据库就绪					if(c.isAnnotationPresent(DB.class)){						DB db=c.getAnnotation(DB.class);						String dbName=db.db_name();						int dbVersion=db.db_version();						int resourceId=db.db_xml_id();						DBHelper.DB_NAME=dbName;						DBHelper.DB_VERSION=dbVersion;						DBHelper.DB_RESOURCE=resourceId;					}				}else{					//实例化需要依赖注入的对象					BEAN_MAP.put(c,c.newInstance());				}			} catch (InstantiationException e) {				e.printStackTrace();			} catch (IllegalAccessException e) {				e.printStackTrace();			} catch (IllegalArgumentException e) {				e.printStackTrace();			}		}	}		public static Map<Class<?>,Object> getBeanMap(){		return BEAN_MAP;	}		/**	 * 	 * 由于在service之中,动态代理的实例莫名其妙为null,因此先判断一下,如果为null就先依赖注入	 * 	 * @param cls	 * @return	 */	public static <T> T getBean(Class<T> cls){		T bean=(T) BEAN_MAP.get(cls);		bean=(T) IocHelper.mapperInject(cls,bean,BEAN_MAP);		return bean;	}		/*	@SuppressWarnings("unchecked")	public <T> T getBean(Class<T> c){		return (T) BEAN_MAP.get(c);	}	*/	}               IocHelper类

public class IocHelper {	private static final String TAG="IocHelper";		static{		Map<Class<?>,Object> beanMap=BeanHelper.getBeanMap();		if(!beanMap.isEmpty()){			for(Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()){				Class<?> cls=beanEntry.getKey();				Object bean=beanEntry.getValue();				mapperInject(cls,bean,beanMap);			}			for(LoadHelper.IocLinstener linstener : LoadHelper.getLinstener()){				Log.d(TAG, "--------------completeInject---------------------");				linstener.onInjectComplete();			}		}	}		public static Object mapperInject(Class<?> cls,Object bean,Map<Class<?>,Object> beanMap){		Field[] beanFields=cls.getDeclaredFields();		for(Field beanField : beanFields){			//依赖注入@Table注释的mapper			if(beanField.isAnnotationPresent(Table.class)){				beanField.setAccessible(true);				try {					//获取注解					Table table=beanField.getAnnotation(Table.class);					int tableResource=table.value();					//通过动态代理实现mapper接口					DaoHelper daoHelper=(DaoHelper) beanMap.get(DaoHelper.class);					daoHelper.setXmlId(tableResource);					daoHelper.setContext(applicationSelf.getContext());					daoHelper.setHelper(new DBHelper(ApplicationSelf.getContext()));					Object newProxyInstance = Proxy.newProxyInstance(							daoHelper.getMapper().getClassLoader(),							new Class[] {daoHelper.getMapper()}, daoHelper);					beanField.set(bean, newProxyInstance);				} catch (Exception e) {					e.printStackTrace();				} 			}		}		return bean;	}	}

             DB注释类

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface DB {    String db_name() default "myTask.db";    int db_version() default 1;    int db_xml_id();    }             Table注释类

@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Table {	int value();	}


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表