原创文章,转载请注明出处
请先阅读《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(); }
新闻热点
疑难解答