通过Java annotation以及反射机制实现不同类型通用的数据库访问接口
在日常开发中会遇到这种情况:
多类对象需要保存到数据库中,每类对象都要创建一个表,创建表时的字段、索引序号、字段类型都要一一对应,
如果保存到数组中,当需要增减字段就要更改数组,一是繁琐,二是很容易搞错序号导致程序运行错误,三是代码复用很难做到。
为了解决上述几点问题,在实践摸索中想出了通过annotation来解决的方法。
其原理是:
创建表时:需要表名、字段名、字段类型
保存数据时:需要表名、字段名、字段对应的值
读取数据时:需要表名、字段索引、保存值的变量
只要在进行以上操作时能提供所需要的信息,那么就能进行操作,这些信息有些可以通过类信息获得,有些则需要通过annotation传入。
1.创建annotation类,只要被此annotation类修饰的成员变量都认为是需要保存到表中的。
/** * 使用此annotation指定一个成员变量为数据库的一个字段。 */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface DbField {boolean primaryKey() default false;boolean notNull() default false;String fieldName() default "##default"; }public class SomeThing {@DbFieldpublic String field_all_default;@DbField (primaryKey = true,notNull = true,fieldName = "specified_field_name")public long field_specified_primary_key;@DbFieldpublic boolean field_boolean;public int not_db_field;}private void createTbl(SQLiteDatabase db, String tblName, Class<?> clazz);
for( Field f : clazz.getFields()) {if(f.isAnnotationPresent(DbField.class)) { //如果被DbField annotation标注了认为是要保存到数据表中if(!first_field) {sql.append(", ");}DbField annotation = f.getAnnotation(DbField.class);String fieldName = annotation.fieldName();if(fieldName.equals(DEFAULT_FIELD_NAME)) {fieldName = f.getName(); //如果是约定的默认字段名,那么用FieldName作为字段名}sql.append(fieldName);sql.append(' ');sql.append(clazzToDbTypeString(f.getType()));//根据字段的类型得到保存到数据表中的类型if(annotation.primaryKey()) {//判断是不是主keysql.append(" PRIMARY KEY");}if(annotation.notNull()) {//判断是不是not nullsql.append(" NOT NULL");}first_field = false;}}private String clazzToDbTypeString(Class<?> clazz) {if( clazz == String.class || clazz == Character.class || clazz == char.class || clazz == Boolean.class || clazz == boolean.class) {return "TEXT";}else if(clazz == Integer.class || clazz == int.class || clazz == Long.class || clazz == long.class|| clazz == Short.class || clazz == short.class) {return "INTEGER";}else if(clazz == Float.class || clazz == float.class || clazz == Double.class || clazz == double.class) {return "REAL";}else {return "BLOB";}}ContentValues v = new ContentValues();for( Field f : object.getClass().getFields() ) {if(f.isAnnotationPresent(DbField.class)) { //如果被标注,认为要保存到数据表中DbField annotation = f.getAnnotation(DbField.class);String fieldName = annotation.fieldName();if(fieldName.equals(DEFAULT_FIELD_NAME)) {fieldName = f.getName(); //如果是约定的默认字段名,那么用FieldName作为字段名}v.put(fieldName, f.get(object).toString()); //将值和字段名配对保存}}public Object cursorToObject(Cursor cursor, Class<?> clazz) throws Exception {Constructor<?> ct = clazz.getConstructor((Class[])null);Object sg = ct.newInstance((Object[])null);//用无参构造函数构造对象for( Field f : clazz.getFields() ) {if(f.isAnnotationPresent(DbField.class)) { //被标注,从表中获取值DbField annotation = f.getAnnotation(DbField.class);String fieldName = annotation.fieldName();if(fieldName.equals(DEFAULT_FIELD_NAME)) {fieldName = f.getName(); //获取字段名}int index = cursor.getColumnIndex(fieldName); //字段名转为字段索引Class<?> fieldClass = f.getType();//根据成员变量的类型,将从表中读取的数据转换if(fieldClass == Integer.class || fieldClass == int.class) {f.setInt(sg, cursor.getInt(index));}else if(fieldClass == Long.class || fieldClass == long.class) {f.setLong(sg, cursor.getLong(index));}else if(fieldClass == String.class ) {f.set(sg, cursor.getString(index));}else if(fieldClass == Boolean.class || fieldClass == boolean.class) {f.setBoolean(sg, Boolean.valueOf(cursor.getString(index)));}}}return sg;}