利用注解,自动生成建表语句&转换查询结果
2015-11-06 16:13
330 查看
利用注解,自动生成建表语句&转换查询结果
转载请注明出处:/article/10141210.html
需求
前段时间,为了加快开发效率,较少冗余重复的代码,写了一个自动生成建表语句和自动转换查询对象的工具类。最近有空整理出一部分,跟大家分享一下。自动生成建表语句
如我们有这么一个类映射一张表:people。public class People extends Object implements Serializable { private static final long serialVersionUID = 1L; private String name; private Integer age; private float weight; private Integer id; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public float getWeight() { return weight; } public void setWeight(float weight) { this.weight = weight; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Override public String toString() { return "People [name=" + name + ", age=" + age + ", weight=" + weight + ", id=" + id + "]"; } }
我们要写它的建表语句:CREATE TABLE people (id INTEGER PRIMARY KEY AUTOINCREMENT , age INTEGER(128) , name VARCHAR(16) , weight FLOAT(128) );
现在还是四个字段,如果字段多一些的表,那么SQL语句会非常长,写起来很麻烦。这时候,利用注解,就可以快速生成。
Annotation(注解):
就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。类、方法、变量、参数、包都可以被注解。
自定义注解:
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
注解还有其他的元注解等其他属性,网上有很多,大家有兴趣可以去深入了解下。
一张表有表名,字段(字段类型,长度),Id。我们定义三个注解分分别对应:table,Column,Id。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Id { /** * 别名 */ String idName() default "id"; /** * 是否自增 */ boolean increment() default true; /** * 长度 */ String length() default "125"; }
/** * * RetentionPolicy.RUNTIME:注解会在class字节码文件中存在,在运行时可以通过反射获取到 * * @Target 是Java的元注解(指修饰注解的注解)之一。用来指定注解修饰类的哪个成员。 ElementType.TYPE: 接口、类、枚举、注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Column { /** * 表示字段名 默认为“” */ String columnName() default ""; /** * 表示字段长度 默认为0 */ String lengtn() default "0"; /** * 是否大字符串 true的话对于数据库里的TEXT类型 不要在意命名 */ boolean isBigString() default false; }
/** * * 表名 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Table { /** * 表名 * * @return */ String tableName(); }
给我们的people加上注解:
@Table(tableName = "people") public class People extends Object implements Serializable { private static final long serialVersionUID = 1L; @Column(columnName = "name", lengtn = "16") private String name; @Column(columnName = "age", lengtn = "128") private Integer age; @Column(columnName = "weight", lengtn = "128") private float weight; @Id(increment = true, idName = "id") private Integer id; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public float getWeight() { return weight; } public void setWeight(float weight) { this.weight = weight; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Override public String toString() { return "People [name=" + name + ", age=" + age + ", weight=" + weight + ", id=" + id + "]"; } }
通过注解获取内容,比如表名:
public <T>String getTableName(Class<T> c) { String tableName = null; try { if (c.isAnnotationPresent(Table.class)) { // 提取表名 tableName = c.getAnnotation(Table.class) .tableName(); } } catch (Exception e) { e.printStackTrace(); } return tableName; }
注解解析:
1:getAnnotation(AnnotationName.class) 表示得到该 Target 某个 Annotation 的信息;
2:getAnnotations() 则表示得到该 Target 所有 Annotation;
3:isAnnotationPresent(AnnotationName.class) 表示该 Target 是否被某个 Annotation 修饰;
获取字段名和类型类似,先查询注解的内容,在根据注解的内容匹配表中字段的类型,如:String = VARCHAR;这里我们需要一个工具类,进行类型转换,这样就不需要再手动设置字段类型了。
public class Types { // 自定义的基本类型,和数据对应,最后组成字符串 public static final String VARCHAR = "VARCHAR"; public static final String INTEGER = "INTEGER"; public static final String BOOLEAN = "BOOLEAN"; public static final String LONG = "BIGINT"; public static final String TEXT = "TEXT"; public static final String BLOB = "BLOB"; public static final String FLOAT = "FLOAT"; public static final String DOUBLE = "DOUBLE"; public static final String DATE = "DATE"; public static final String TIME = "TIME"; // 默认长度 public static final String VARCHAR_LENGTH = "50"; public static final String INTEGER_LENGTH = "50"; public static final String LONG_LENGTH = "200"; public static final String TEXT_LENGTH = "null"; public static final String BLOB_LENGTH = "null"; public static final String FLOAT_LENGTH = "200"; public static final String DOUBLE_LENGTH = "200"; public static final String DATE_LENGTH = "null"; public static final String TIME_LENGTH = "null"; // 将类型 默认长度,放入集合 public static Map<String, String> map = new HashMap<String, String>(); static { map.put(VARCHAR, VARCHAR_LENGTH); map.put(INTEGER, INTEGER_LENGTH); map.put(BOOLEAN, "0"); map.put(LONG, LONG_LENGTH); map.put(TEXT, TEXT_LENGTH); map.put(BLOB, BLOB_LENGTH); map.put(FLOAT, FLOAT_LENGTH); map.put(DOUBLE, DOUBLE_LENGTH); map.put(DATE, DATE_LENGTH); map.put(TIME, TIME_LENGTH); }
开始拼接sql语句:
/** * 开始创建建表语句 */ protected <T> String createSql(Class<T> c) { // 表名 String tableName = mReflectUtil.getTableName(c); // 创建建表语句 StringBuilder sql = new StringBuilder("CREATE TABLE " + tableName + " ("); // 字段集合 ArrayList<String> columns = new ArrayList<String>(); // 带注释的Field集合 ArrayList<Field> list = mReflectUtil.getColumnList(c); try { for (Field mField : list) { // 字段名 String name = null; // 类型 String type = null; Annotation[] mAnnotations = mField.getDeclaredAnnotations(); if (mAnnotations.length == 0) { continue; } else { // 获取注解信息 Column mColumn = (Column) mAnnotations[0]; // 获得sql需要的别名 String nameC = mColumn.columnName(); // 获取字段的类型 String typeC = mField.getType().getName(); // 获取注解里的长度 String Clength = mColumn.lengtn(); // 是否是TEXT格式 boolean isBig = mColumn.isBigString(); if (nameC.length() == 0) { // 获得默认的字段名 name = mField.getName(); } else { name = nameC; } if (typeC.length() == 0) { // 获得默认的类型 type = mField.getType().toString(); } else { type = Types.getType(typeC, isBig); } // 没有设置长度 则获取默认的长度 if (Clength.length() < 0) { Clength = Types.map.get(type).toString(); } // 对于boolean 特殊处理 0代表false if ("0".equals(Clength)) { // 获取默认的长度 Clength = Types.map.get(type).toString(); } // 生成字段类型和长度sql语句 type = Types.getTypeSql(type, Clength); // 拼接成sql语句格式 加入集合 columns.add(name + " " + type); } } // 加入ID sql.append(mReflectUtil.getIdSql(c)); // 遍历集合 生成sql语句 for (String str : columns) { sql.append(" " + str + " ,"); } // 最终的创建表语句 String finalSql = sql.substring(0, sql.length() - 1) + " );"; return finalSql; } catch (Exception e) { e.printStackTrace(); } return ""; }
然后是我们常用的插入语句和查询表的数据:
protected <T> String insertObj(Object c) { String insertSql = null; boolean isBig; // 获取表名 String tableName = mReflectUtil.getTableName(c.getClass()); if (tableName != null) { // 要插入的别名 StringBuffer sqlStr = new StringBuffer("INSERT INTO "); // 要插入的别名的值 StringBuffer valueStr = new StringBuffer(" VALUES ("); // 所有带注解的字段 List<Field> list = mReflectUtil.getFieldList(c.getClass()); if (list != null && list.size() > 0) { // 开始拼接字符串 sqlStr.append(tableName + " ("); // 开始遍历所有带注释的字段 // 别名 String str = null; // 字段的值 Object fieldValue = null; try { for (Field field : list) { if (field.isAnnotationPresent(Id.class)) { Id mId = field.getAnnotation(Id.class); str = mId.idName(); } if (field.isAnnotationPresent(Column.class)) { Column anno = field.getAnnotation(Column.class); str = anno.columnName(); // 是否是text类型 if (anno.isBigString()) { isBig = true; } } // 获取字段的值 fieldValue = mReflectUtil.getFieldValue(c, field); // boleann 类型对应数据库的值 false =0 ture = 1 if ("false".equals(fieldValue)) { fieldValue = "0"; } if ("true".equals(fieldValue)) { fieldValue = "1"; } String type = field.getType().getName(); if (null == fieldValue || "".equals(fieldValue)) { // 根据对象的类型 获取数据库里匹配类型的默认值 fieldValue = null; } // 拼接要插入的别名的sql语句 sqlStr.append(str + ","); // 根据类型 设置sql语句中 不同的格式 if (field.getType().getName().indexOf("String") != -1 || field.getType().getName().indexOf("Date") != -1 || field.getType().getName().indexOf("Time") != -1) { // varchar 要加'' valueStr.append("'" + fieldValue + "',"); } else { // 其余直接拼接 valueStr.append(fieldValue + ","); } } } catch (Exception e) { e.printStackTrace(); } // 添加括号 insertSql = sqlStr.toString().substring(0, sqlStr.length() - 1) + ")" + valueStr.toString().substring(0, valueStr.length() - 1) + ");"; } } return insertSql; } /** * @Description: 自定义sql语句的查询 * @Date:2014年8月7日下午2:52:13 * @param mData * 数据库 * @param sql * sql语句 * @param mObject * 对象类 * @return * @return ArrayList<T> 集合 */ protected <T> ArrayList<T> getListBySql(SQLiteDatabase mData, String sql, Class<T> mObject) { ArrayList<T> list = new ArrayList<T>(); try { Cursor mCursor = mData.rawQuery(sql, null); list = mReflectUtil.cursorToList(mCursor, mObject); } catch (Exception e) { e.printStackTrace(); } return list; }
自动转换查询结果:
一般android里sqlite的查询结果都要经过转换。比如:
public List<People> convertFavorite(Cursor cursor) { List<People> list = new ArrayList<People>(); while (cursor.moveToNext()) { FavorityInfo mInfo = new People(); mInfo.setId(cursor.getInt(cursor .getColumnIndexOrThrow("id"))); mInfo.setName(cursor.getString(cursor .getColumnIndexOrThrow("name"))); mInfo.setAge(cursor.getString(cursor .getColumnIndexOrThrow("age"))); ....... list.add(mInfo); } return list; }
同样在字段多的情况下,写起来也非常耗时间和重复,我们也可以用注解来转换。
public <T> ArrayList<T> cursorToList(Cursor mCursor, Class<T> c) { ArrayList<T> list = new ArrayList<T>(); // 获取所有方法 Method methods[] = c.getDeclaredMethods(); ArrayList<Field> listFields = getFieldList(c); HashMap<String, String> map = getColumnMap(c); String type = null; try { if (mCursor != null) { while (mCursor.moveToNext()) { // 新建一个该类型的对象 Object mObject = c.newInstance(); // 遍历方法 for (Method method : methods) { // 获取方法的小写名字 String mName = method.getName().toLowerCase(); for (Field mField : listFields) { // 对象里的字段名 String name = mField.getName(); // 如果是byte[] 则要去掉[] if (name.indexOf("[]") != -1) { name.replaceAll("[]", ""); } if (mName.equals("set" + name.toLowerCase())) { // 如果方法名跟字段名匹配 // 先找出该字段的类型 // 根据类型 和 别名 设置值 type = mField.getType().getName(); if (type.indexOf("String") != -1) { method.invoke(mObject, mCursor .getString(mCursor .getColumnIndexOrThrow(map .get(name)))); } // int类型 if (type.indexOf("int") != -1 || type.indexOf("Integer") != -1) { method.invoke(mObject, mCursor .getInt(mCursor .getColumnIndexOrThrow(map .get(name)))); } // lang类型 if (type.indexOf("Long") != -1 || type.indexOf("long") != -1) { method.invoke(mObject, mCursor .getLong(mCursor .getColumnIndexOrThrow(map .get(name)))); } // boolean 类型 if (type.indexOf("boolean") != -1) { method.invoke( mObject, mCursor.getLong(mCursor .getColumnIndexOrThrow(map .get(name))) == 0 ? false : true); } // byte 类型 if (type.indexOf("byte") != -1 || type.indexOf("Byte") != -1) { method.invoke(mObject, (byte) (mCursor .getInt(mCursor .getColumnIndexOrThrow(map .get(name))))); } // byte[]类型 if (type.indexOf("[B") != -1) { method.invoke(mObject, (mCursor .getBlob(mCursor .getColumnIndexOrThrow(map .get(name))))); } // double 类型 if (type.indexOf("double") != -1 || type.indexOf("Double") != -1) { method.invoke(mObject, mCursor .getDouble(mCursor .getColumnIndexOrThrow(map .get(name)))); } // float 类型 if (type.indexOf("float") != -1 || type.indexOf("Float") != -1) { method.invoke(mObject, mCursor .getFloat(mCursor .getColumnIndexOrThrow(map .get(name)))); } // 时间类型 if (type.indexOf("Date") != -1) { String value = mCursor.getString(mCursor .getColumnIndex(map.get(name))); SimpleDateFormat formatter = new SimpleDateFormat( "yyyy-MM-dd"); ParsePosition pos = new ParsePosition(0); Date strtodate = formatter .parse(value, pos); method.invoke(mObject, strtodate); } } } } list.add((T) mObject); } } } catch (Exception e) { e.printStackTrace(); } finally { mCursor.close(); } return list; }
这样,只要传入对象,就可以自动转换成我们需要的数据了。
总结###:
在实际项目中,这些方法能帮助我们快速开发,基本满足普通的增删改查和建表。但一些复杂的建表语句暂时还不支持,还需要完善功能,另外附上demo;demo
相关文章推荐
- 常用maven jar 依赖
- AndroidStudio下加入百度地图的使用(四)——路线规划
- 自定义TabBarController 字体方法
- 修改WSAD的默认工作区(转)
- JSP,Servlet,JSF 的区别
- android 之输入法
- Replacement for deprecated sizeWithFont: in iOS 7?
- 语义化版本
- [LeetCode] Move Zeroes
- android第一个空白的项目
- file命令
- PrintStream打印流
- Linux学习笔记之学习网站
- 将Emacs打造成阅读源代码的神器
- nginx url转发的一种方式
- 中国2015新互联网公司300强系列
- CRT已经成功连接服务器,但是界面显示空白,无任何显示
- 使用一个map映射出两个对象,再把两者关系对应起来
- servlet(验证码)
- C语言练习作业(一)