您的位置:首页 > 移动开发 > Android开发

android——仿mybatis的半自动数据库映射的实现

2017-01-19 15:55 453 查看
由于一直是做web端,最近接触android,操作数据库的时候怀念mybatis,因此很简单的做了个功能有限的仿造。在此来探讨一下。

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会抛一个异常警告我们有接口没有实现,不过完全没有关系
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android
相关文章推荐