原创文章,转载请注明出处
请先阅读《android——仿mybatis的半自动数据库映射的实现 》
这里在实现了依赖注入的基础上,对之前的仿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类
@Component
public 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();
}