利用Annotation实现Android sqlite框架

来源:互联网 发布:笔记本能开淘宝店铺 编辑:程序博客网 时间:2024/06/07 01:03

结合Annotation类的学习写了一个SQLite的框架,对数据库的操作转化为对java对象的操作,摆脱开发过程中代码与表操作混在一起的问题,其实Annotation在框架设计当中用到的比较多,常见的就是我们在方法上加@注解,我们来看看如何自定义一个Annotation类来使用它


源码及DEMO地址:https://github.com/xiaoqi0716/LightSqlite


介绍框架之前先看来看它的用法:比如我们有一个数据库用来存储学生、老师的信息,那么我们在使用时可以这么来做

写一个类继承AbstractDBHelper,并实现相就的方法

public class PersonalDB extends AbstractDBHelper {    private final String DB_NAME = "MY_DB.db"; //数据库名    private final int DB_VERSION = 1; //数据库版本        public PersonalDB(Context context) {        super(context);    }    @Override    public String getDataBaseName() {        return DB_NAME;    }    @Override    public int getDataBaseVersion() {        return DB_VERSION;    }    //返回MY_DB.db数据库所有的表,后续增加表直接在这里添加    @Override    public List<AbstractTable<?>> getTables() {        List<AbstractTable<?>> list = new ArrayList<AbstractTable<?>>();        list.add(StudentTable.getInstance()); //学生表        list.add(TeacherTable.getInstance());  //老师表        return list;    }}

然后建立一个学生表对应的数据类

public class StudentItem {    public static final String FIELD_S_ID = "s_id";    public static final String FIELD_S_NAME = "s_name";    public static final String FIELD_S_AGE = "age";    public static final String FIELD_S_CLASS = "class";    @Column(name = FIELD_S_ID, unique = true, index = true, notNull = true)    private int id;    @Column(name = FIELD_S_NAME, notNull = true)    private String name;    @Column(name = FIELD_S_AGE, notNull = true)    private int age;    @Column(name = FIELD_S_CLASS)    private String inClass;    //GET and SET    .....}


接下来,建立学生表对应的类,该类继承AbstractTable,使用泛型实现类StudentItem
public class StudentTable extends AbstractTable<StudentItem> {    private final static String TABLE_NAME = "student"; // 学生表    @Override    public String getTableName() {        return TABLE_NAME;    }    // 当数据库版本号更新时,对该表的操作    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    }}

老师表就不写了,与StudentTable写法一样,完成上面几步操作就可以使用了,最后看盾如何使用:

PersonalDB db = new PersonalDB(context);

db.open();

上面两条语句就初始了一个数据库打开操作,接下来如何对表进行操作:

StudentTable.getInstance().add(......);  //增

StudentTable.getInstance().find(......);  //查询

StudentTable.getInstance().set(......);  //修改

StudentTable.getInstance().remove(......);  //删除

增、删、改、查各有多种API实现来使用,是不是很简单。


框架设计思路:
数据库概念大家都比较熟悉,一个数据库下面会有多张表,这属于一对多的情况,那么体现在框架中就是一个数据库类当中,需要为它配置表对应的类对象,由数据库这个类对管理所有表。

在处理数据时,按照我们平时的习惯,应该有model类去管理一个数据模型,比如上文用的学生、老师,他们都有自己的属性,其实model类中的属性是最能体现这个表中有哪些字段,比如StudentItem这个model类,它里面有学生ID、姓名、年龄、年级等,那么对应的表也是这些字段,那么怎么才能把这些类的属性关联到表中每一个字段上呢,这里我用到了java中一个特殊的类,Annotation类,这个类其它并不陌生,比如我们平时继承父类的一些方法,开发环境往往会加上@Overide这个注解,这个实际上就一个Annotation类,关于这个类的细节这里不做详细介绍,这里主要讲怎么使用它来完成我们的框架,代码献上

我们主要看一下AbstractDBHelper类中一个创建表的方法,其它逻辑请查看源码

 private void createTable(SQLiteDatabase db, AbstractTable<?> table) {            StringBuilder sql = new StringBuilder();            sql.append("CREATE TABLE IF NOT EXISTS ");            String tableName = table.getTableName();            sql.append(tableName).append(" (");            Class<?> tableCls = null;            Type t = table.getClass().getGenericSuperclass();            if (t != null && t instanceof ParameterizedType) {                Type[] type = ((ParameterizedType) t).getActualTypeArguments();                tableCls = (Class<?>) type[0];            }            Field[] fields = tableCls.getDeclaredFields();            for (Field field : fields) {                field.setAccessible(true);                if (field.isAnnotationPresent(Column.class)) {                    Column ano = field.getAnnotation(Column.class);                    String fieldName = ano.name();                    sql.append(fieldName).append(" ");                    sql.append(getFieldType(field)).append(" ");                    if (ano.primaryKey()) {                        sql.append("PRIMARY KEY").append(" ");                    }                    if (ano.autoIncrement()) {                        sql.append("AUTOINCREMENT").append(" ");                    }                    if (ano.unique()) {                        sql.append("UNIQUE").append(" ");                    }                    if (ano.notNull()) {                        sql.append("NOT NULL").append(" ");                    }                    if (!TextUtils.isEmpty(ano.defaultVal())) {                        String fieldType = getFieldType(field);                        if ("TEXT".equals(fieldType)) {                            sql.append("default").append(" ").append("'").append(ano.defaultVal()).append("'").append(" ");                        } else {                            sql.append("default").append(" ").append(ano.defaultVal()).append(" ");                        }                    }                    sql.append(", ");                }            }            sql.deleteCharAt(sql.length() - 2);            sql.append(")");            LogUtils.v(sql.toString());            try{                db.execSQL(sql.toString());            }catch (Exception e) {                e.printStackTrace();            }            for (Field field : fields) {                field.setAccessible(true);                if (field.isAnnotationPresent(Column.class)) {                    Column an = field.getAnnotation(Column.class);                    if (an.index()) {                        String sqlStr = "CREATE INDEX IF NOT EXISTS " + table.getTableName() + "_" + an.name() + "_index ON " + table.getTableName() + "(" + an.name() + ")";                        LogUtils.v(sqlStr);                        try{                            db.execSQL(sqlStr);                        }catch (Exception e) {                            e.printStackTrace();                        }                    }                }            }            table.onCreateTrigger(db);        }



该方法接收两个参数,其中一个是继承AbstractTable类的实例,在方法内部通过Type t = table.getClass().getGenericSuperclass();,Type[] type = ((ParameterizedType) t).getActualTypeArguments(); Type是Java 中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。ParameterizedType参数化类型,就是所说的泛型,(Class<?>) type[0]拿到了传入泛型的真实类型,type它是一个数组,由于我们只用到一个参数,如上面看的StudentTable类,所以只取第0个,就是StudentItem这个类。当我们拿到了StudentItem我们就可以对它进行反射,field.isAnnotationPresent(Column.class);这句来判断某些属性是否加上了@Column注解,如果有才认为是字段相关的属性,Column ano = field.getAnnotation(Column.class);拿到了Column注解这个类的实例,后面就是对@Column中参数进行解析了。

我们来看看@Column这个是怎么定义的呢

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Column {    String name();    boolean primaryKey() default false;    boolean autoIncrement() default false;    boolean unique() default false;    boolean index() default false;    boolean notNull() default false;    String defaultVal() default "";}

Column 是一个自定义Annotation类,@interface是自定义Annotation的写法,这个类本身也有两个注解修饰

@Target(ElementType.METHOD) 修饰的注解表示该注解只能用来修饰在方法上。
@Target(ElementType.FIELD)表示@Column这个注解只能用在属性上
@Retention(RetentionPolicy.CLASS)修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候;
@Retention(RetentionPolicy.SOURCE )修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中;
@Retention(RetentionPolicy.RUNTIME )修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时,

本框架中的逻辑是在运行时使用,所以使用RetentionPolicy.RUNTIME

利用Annotation来实现表和类的属性关联,减少了在开发过程中不用刻意地去再定义一次字段的属性值了,充分利用现有的可用的数据


源码及DEMO地址:https://github.com/xiaoqi0716/LightSqlite



0 0