android——仿mybatis的半自动数据库映射的实现
2017-01-19 15:55
453 查看
由于一直是做web端,最近接触android,操作数据库的时候怀念mybatis,因此很简单的做了个功能有限的仿造。在此来探讨一下。
mybatis对于sql还是需要写的,只是自动映射字段到结果对象,所以是半自动,以此来体现其灵活性。而对于sql的管控完全分离到了xml之中。其中最令人印象深刻的地方之一应该是通过一个mapper接口去和xml进行管理。而这个mapper接口你只需要定义方法,系统会自动实现你的接口。
动态实现接口,其实原理是动态代理。通过把该接口代理为一个操作数据库的工具类,并通过xml解析,把sql传入到工具类执行sql,再通过反射进行对象映射即可。
我们来看一下我的实现吧,为了理解,先看一下xml
1、用来建表,删除表的xml
2、sql操作的xml
3、动态代理核心代码
最后,使用mybatis的时候我们通过IOC将我们定义的mapper接口注入到service层,在其中已经将mapper换成了代理类。
我这里没有用IOC,通过一个父类,识别传入的泛型来替换代理
最后见证一下奇迹,看看怎么使用
1、只需要定义一个接口,不需要实现,方法名和xml中的id一致即可
2、需要调用该接口的类继承BaseService该类即可,泛型为你需要用到的mapper接口
如果我要执行xml的sql语句,只需要调用该接口方法即可,比如
由于我们的TaskMapper没有通过代码实现,而是在运行时自动实现,所以LogCat会抛一个异常警告我们有接口没有实现,不过完全没有关系
mybatis对于sql还是需要写的,只是自动映射字段到结果对象,所以是半自动,以此来体现其灵活性。而对于sql的管控完全分离到了xml之中。其中最令人印象深刻的地方之一应该是通过一个mapper接口去和xml进行管理。而这个mapper接口你只需要定义方法,系统会自动实现你的接口。
动态实现接口,其实原理是动态代理。通过把该接口代理为一个操作数据库的工具类,并通过xml解析,把sql传入到工具类执行sql,再通过反射进行对象映射即可。
我们来看一下我的实现吧,为了理解,先看一下xml
1、用来建表,删除表的xml
<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操作的xml
<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>
3、动态代理核心代码
/** * * 仿造mybaits,通过动态代理将xml和mapper绑定,并解析xml执行sql * * @author 吴林峰 * */ public class Loader implements InvocationHandler{ private static String TAG="Loader"; private Helper 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 Loader(){ } public Loader(Activity context){ this.helper=new Helper(context); this.context=context; } @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 Helper(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 Helper getHelper() { return helper; } public void setHelper(Helper helper) { this.helper = helper; } }
最后,使用mybatis的时候我们通过IOC将我们定义的mapper接口注入到service层,在其中已经将mapper换成了代理类。
我这里没有用IOC,通过一个父类,识别传入的泛型来替换代理
/** * * 在实例化的时候自动实现mapper接口 * * @author 吴林峰 * * @param <T> mapper接口 */ public class BaseService<T> { protected Context context; protected T t; protected BaseService(Context context,HelperCreator hc){ this.context=context; Loader loader=new Loader(); loader.setXmlId(R.xml.task); loader.setHelper(hc.getHelper()); loader.setContext(context); try { Object newProxyInstance = Proxy.newProxyInstance( loader.getMapper().getClassLoader(), new Class[] {loader.getMapper()}, loader); this.t=(T) newProxyInstance; } catch (Exception e) { e.printStackTrace(); } } }
最后见证一下奇迹,看看怎么使用
1、只需要定义一个接口,不需要实现,方法名和xml中的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); }
2、需要调用该接口的类继承BaseService该类即可,泛型为你需要用到的mapper接口
public class TaskService extends BaseService<TaskMapper>这个时候,TaskService中就有了一个名为t的成员变量,该变量已经自动实现了TaskMapper接口
如果我要执行xml的sql语句,只需要调用该接口方法即可,比如
public List<TaskVo> getTask() throws Exception{ TaskVo vo=new TaskVo(); vo.setRows(100000); vo.setOffset(0); List<TaskVo> vos=t.findTask(vo); for(TaskVo v : vos){ Log.d(TAG, "requestCode:"+v.getRequestCode()); } return vos; }
由于我们的TaskMapper没有通过代码实现,而是在运行时自动实现,所以LogCat会抛一个异常警告我们有接口没有实现,不过完全没有关系
相关文章推荐
- myBatis配置实现数据库字段下划线映射到java对象的驼峰式命名属性
- myBatis配置实现数据库字段下划线映射到java对象的驼峰式命名属性
- 利用反射+特性实现简单的实体映射数据库操作类(还未做自动生成SQL语句部分)
- Ibatis和Hibernate 数据库分表(动态表名映射)的实现方法
- Android SQLite数据库 实现 创建表 和增删改查分页
- Android实现短信应用手动添加入数据库
- Android手机客户端通过JSP实现与Tomcat服务器端通信(Msql数据库,Json作为载体)--服务端代码
- 用Java实现自己的数据库OR映射框架
- android 玩转ContentProvider之一--实现ContentProvider操作数据库
- android实现短信发送以及存入系统数据库
- android如何使用BroadcastReceiver后台实现来电通话记录的监听并存取到sqllite数据库通过Contentprovilder实现接口
- Ibatis和Hibernate 数据库分表(动态表名映射)的实现方法
- c#利用反射+特性实现简单的实体映射数据库操作类实现自动增删改查(三)带源码写好的持久层
- c#利用反射+特性实现简单的实体映射数据库操作类实现自动增删改查(一)
- 调用Android(Froyo)中Contacts的数据库,实现联系人与分组的增删改查--附效果图及源码
- (摘自CSDN的koy0755)一步一步实现数据库到类的自动化映射(二) 类层次的设计 类的实现
- c#利用反射+特性实现简单的实体映射数据库操作类(表与类的映射)
- Mybatis高级映射之-数据库字段跟实体类名称不一致时的处理
- Hibernate3.6中用Annotation来实现实体类与数据库表的映射关系
- Android中Spinner下拉列表(使用自定义Adapter实现,显示数据取自联系人数据库)