自定义ORM框架

一、ORM简单科普


所谓ORM,即对象-关系映射(Object/Relation Mapping),方便我们以操作对象的方式去操作关系型数据库。

在平时的开发过程中,大家一定会或多或少地接触到SQLite。然而在使用它时,我们往往需要做许多额外的工作,像编写 SQL 语句与解析查询结果等。

假如我们有这样一个对象需要存在数据库:

@Table
public class Person {
    @Column
    private int id;

    @Check("name!='Fucker'")
    @Column
    private String name;

    @Default
    @Column
    private double height = 180;

    @Column
    private int age;

    @Default
    @NotNull
    @Column
    private String job = "IT";
}

那么我们在建表时,需要写这样的sql语句:

create table if not exists Person(
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT CHECK(name!='Fucker'),
    height REAL DEFAULT 180.0,
    age INTEGER,
    job TEXT DEFAULT IT NOT NULL);

然后在查询后,我们又需要对Curcor进行遍历取值,然后set到对象中去,很麻烦有木有?

while (cursor.moveToNext()) {
    int nameColumnIndex = cursor.getColumnIndex("filedName");
    String value = cursor.getString(nameColumnIndex);
}

一不小心sql拼错了,或者cursor取字段时字段名写错了,就GG了啊!

于是,各种ORM框架就出来了,通过注解和反射将生成建表sql、解析cursor成对象,都自动化了,这大大方便了我们这些懒人了。

但是,现在的ORM框架大多在写查询语句时,感觉有点过度封装了,有时候,使用ORM框架去做条件查询,甚至还不如自己去写查询的sql!

为了解决这个问题呢,本人封装了一套自己的ORM框架,借鉴了Guava的字符串操作库的Fluent链式接口的思想,将写查询语句方便了一点点,既尽量减少我们写原生sql语句容易拼错的问题,也不像有的ORM框架不方便做复杂的条件查询。

当然,框架还在不断的完善中(索引和多表关联暂时都还没加),如果你觉得我下面的封装有哪里不合理,欢迎和我讨论!@QQ:630709658

二、框架的测试类:


测试场景:

  1. 执行自定义的Sql
  2. 表操作:建表、删表、备份、存在判断
  3. 插入
  4. 删除
  5. 查询
  6. 更新
  7. 事务
package com.che.baseutil.sqlite;

import android.app.Application;

import com.che.base_util.LogUtil;
import com.che.baseutil.BuildConfig;
import com.che.baseutil.table.Person;
import com.che.baseutil.table.Teacher;
import com.che.fast_orm.DBHelper;
import com.che.fast_orm.helper.DBException;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;

import java.util.ArrayList;
import java.util.List;

import static com.google.common.truth.Truth.assertThat;

/**
 * 作者:余天然 on 16/9/16 下午10:17
 */
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class,
        sdk = 21,
        manifest = "src/main/AndroidManifest.xml",
        packageName = "com.che.baseutil",
        resourceDir = "res")
public class DBTestClient {

    private DBHelper dbHelper;//数据库辅助类

    @Before
    public void setUp() throws DBException {
        ShadowLog.stream = System.out;
        Application application = RuntimeEnvironment.application;
        dbHelper = new DBHelper(application, "mydb", 1);
        //删除表
        dbHelper.drop(Person.class);
        //创建表
        dbHelper.create(Person.class);
        //初始化数据,方便之后操作
        initData();
    }

    /**
     * 插入
     */
    public void initData() {
        try {
            //插入多条数据
            List<Person> persons = new ArrayList<>();
            persons.add(new Person("Fishyer", 23));
            persons.add(new Person("Stay", 23));
            persons.add(new Person("Ricky"));
            persons.add(new Person("Stay", 23));
            persons.add(new Person("Fuck", 24));
            persons.add(new Person("Albert"));
            dbHelper.insertAll(persons);

            //插入单条数据
            Person untitled = new Person();
            untitled.setAge(21);
            untitled.setHeight(200);
            dbHelper.insert(untitled);
        } catch (DBException e) {
            LogUtil.print("数据库异常:" + e.getMessage());
        }
    }

    /**
     * 自定义Sql
     */
    @Test
    public void testSql() throws DBException {
        dbHelper.execSQL("drop table if exists Person");
        dbHelper.execSQL("create table if not exists Person(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT,age INTEGER DEFAULT 100)");
        dbHelper.execSQL("insert into Person (age) values (21)");
        dbHelper.execSQL("insert into Person (name,age) values ('Fishyer',23)");
    }

    /**
     * 表操作
     */
    @Test
    public void testTable() throws DBException {
        //删除表: drop table if exists Teacher
        dbHelper.drop(Teacher.class);
        //断言表不存在:select count(*) from sqlite_master where type='table' and name='Teacher'
        assertThat(dbHelper.isExist(Teacher.class)).isEqualTo(false);
        //创建表:create table if not exists Teacher(id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,age INTEGER,course TEXT)
        dbHelper.create(Teacher.class);
        //断言表存在:
        assertThat(dbHelper.isExist("Teacher")).isEqualTo(true);
    }

    /**
     * 事务
     */
    @Test
    public void testTransaction() throws DBException {
        Person person = new Person("Fishyer", 23);
        dbHelper.beginTransaction();
        for (int i = 0; i < 100; i++) {
            //insert or replace into Person (name,height,age) values ('Fishyer',180.0,23)
            dbHelper.insert(person);
        }
        dbHelper.endTransaction();
    }

    /**
     * 删除
     */
    @Test
    public void testDelete() throws DBException {
        //删除指定数据(不推荐,建议使用条件删除):delete from Person where name='Stay' and height=180.0 and age=-1;
        dbHelper.deleteObj(new Person("Stay"));

        //删除所有数据:delete from Person
        dbHelper.deleteAll(Person.class);

        //条件删除:delete from Person where name='Stay'
        dbHelper.delete(Person.class).where("name=Stay").execute();
    }

    @Test
    public void testQuery() throws DBException {
        //查询所有数据: select * from Person
        dbHelper.queryAll(Person.class);

        //查询指定数据: select * from Person where name='Stay'
        dbHelper.queryObj(new Person("Stay"));
    }

    @Test
    public void testSelect() throws DBException {
        //条件查询1:select * from Person where age>='21' order by name
        dbHelper.select(Person.class).whereInt("age>=21").orderBy("name").query();

        //条件查询2:select * from Person order by age desc
        dbHelper.select(Person.class).where("name=Stay").append("order by id").desc().query();

        //条件查询3:select * from Person where age='23' order by name
        dbHelper.select(Person.class).whereInt("age=23").orderBy("id").query();

        //去重查询:select distinct * from Person order by age desc
        dbHelper.distinct(Person.class).whereInt("age=23").orderBy("id").query();
    }

    @Test
    public void testBackup() throws DBException {
        //建立备份:create table Student_bak as select *from Person
        dbHelper.bak(Person.class);

        //查询备份:select * from Student_bak
        dbHelper.queryBak(Person.class);
    }

    @Test
    public void testUpdate() throws DBException {
        //更新数据:update Person set age=99 where name='Fishyer'
        dbHelper.update(Person.class).setInt("age=99").where("name=Fishyer").execute();

        dbHelper.queryAll(Person.class);
    }

    @Test
    public void testIndex() throws DBException {
        // TODO: 16/9/17 添加索引
    }

    @Test
    public void testMap() throws DBException {
        // TODO: 16/9/17 添加多表关联
    }

}

三、ORM框架的封装之路:


这个框架,与其说是设计出来的,倒不如说是不断重构出来的。一开始,我是想写一个工具类,后来不断的拓展和优化,结果,就变成框架了。


fast-orm库的项目结构

1.ORM工具类:

public class DBHelper extends SQLiteOpenHelper {

    /**
     * 构造函数,必须实现
     *
     * @param context 上下文
     * @param name    数据库名称
     * @param version 当前数据库版本号
     */
    public DBHelper(Context context, String name, int version) {
        super(context, name, null, version);
    }

    //数据库第一次创建时会调用,一般在其中创建数据库表
    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    //当数据库需要修改的时候,Android系统会主动的调用这个方法。
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    //基本修改命令
    public void execSQL(String sql) throws DBException {
        try {
            sql += ";";
            LogUtil.print(sql);
            getWritableDatabase().execSQL(sql);
        } catch (Exception e) {
            e.printStackTrace();
            throw new DBException(e.getMessage());
        }
    }

    //基本查询命令
    public Cursor rawQuery(String sql) throws DBException {
        Cursor cursor = null;
        try {
            sql += ";";
            LogUtil.print(sql);
            cursor = getReadableDatabase().rawQuery(sql, null);
        } catch (Exception e) {
            e.printStackTrace();
            throw new DBException(e.getMessage());
        }
        return cursor;
    }

    /**
     * 表操作命令
     */
    public void create(Class<?> clazz) throws DBException {
        String createSql = SqlGenerater.create(clazz);
        execSQL(createSql);
    }

    public void drop(Class<?> clazz) throws DBException {
        String dropSql = SqlGenerater.drop(clazz);
        execSQL(dropSql);
    }

    public <T> void bak(Class<T> clazz) throws DBException {
        String bakSql = SqlGenerater.bak(clazz);
        execSQL(bakSql);
    }

    public <T> boolean isExist(Class<T> clazz) throws DBException {
        return isExist(ReflectHelper.getTableName(clazz));
    }

    public boolean isExist(String tableName) throws DBException {
        Cursor cursor = rawQuery("select count(*) from sqlite_master where type='table' and name='" + tableName + "'");
        if (cursor.moveToNext()) {
            int count = cursor.getInt(0);
            if (count > 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 新增
     */
    public <T> void insert(T t) throws DBException {
        String insertSql = SqlGenerater.insert(t);
        execSQL(insertSql);
    }

    public <T> void insertAll(List<T> list) throws DBException {
        getWritableDatabase().beginTransaction();
        for (T t : list) {
            insert(t);
        }
        getWritableDatabase().setTransactionSuccessful();
        getWritableDatabase().endTransaction();
    }

    /**
     * 删除
     */
    public <T> void deleteObj(T t) throws DBException {
        String whereSql = SqlGenerater.deleteObj(t);
        execSQL(whereSql);
    }

    public <T> void deleteAll(Class<T> clazz) throws DBException {
        String deleteAllSql = SqlGenerater.deleteAll(clazz);
        execSQL(deleteAllSql);
    }

    /**
     * 查询
     */
    public <T> List<T> queryObj(T t) throws DBException {
        String whereSql = SqlGenerater.queryObj(t);
        Cursor cursor = rawQuery(whereSql);
        return (List<T>) ReflectHelper.parseCursor(cursor, t.getClass());
    }

    public <T> List<T> queryAll(Class<T> clazz) throws DBException {
        String queryAllSql = SqlGenerater.queryAll(clazz);
        Cursor cursor = rawQuery(queryAllSql);
        return ReflectHelper.parseCursor(cursor, clazz);
    }

    public <T> List<T> queryBak(Class<T> clazz) throws DBException {
        String selectAllSql = SqlGenerater.queryBak(clazz);
        Cursor cursor = rawQuery(selectAllSql);
        return ReflectHelper.parseCursor(cursor, clazz);
    }

    /**
     * 创建连接符编辑器
     */
    //查询
    public <T> ConnectBuilder<T> select(Class<T> clazz) throws DBException {
        return new ConnectBuilder(this, clazz, "select * from " + ReflectHelper.getTableName(clazz));
    }

    //去重查询
    public <T> ConnectBuilder<T> distinct(Class<T> clazz) throws DBException {
        return new ConnectBuilder(this, clazz, "select distinct * from " + ReflectHelper.getTableName(clazz));
    }

    //删除
    public <T> ConnectBuilder<T> delete(Class<T> clazz) throws DBException {
        return new ConnectBuilder(this, clazz, "delete from " + ReflectHelper.getTableName(clazz));
    }

    //修改
    public <T> ConnectBuilder<T> update(Class<T> clazz) throws DBException {
        return new ConnectBuilder(this, clazz, "update " + ReflectHelper.getTableName(clazz));
    }

    /**
     * 连接符编辑器-执行,无返回值
     */
    public <T> void execute(ConnectBuilder<T> builder) throws DBException {
        execSQL(builder.sql);
    }

    /**
     * 编辑器-查询,有返回值
     */
    public <T> List<T> query(ConnectBuilder<T> builder) throws DBException {
        Cursor cursor = rawQuery(builder.sql);
        return ReflectHelper.parseCursor(cursor, builder.clazz);
    }

    /**
     * 开启事务
     */
    public void beginTransaction() {
        getReadableDatabase().beginTransaction();
    }

    /**
     * 关闭事务
     */
    public void endTransaction() {
        getReadableDatabase().setTransactionSuccessful();
        getReadableDatabase().endTransaction();
    }

}

2.SQL语句封装

在上面的工具类中,大家可以看到我的封装,主要就是将创建sql语句的过程进行了封装,主要从2个方面入手:

  1. SqlGenerater:Sql语句生成器
    这个类主要就是根据类信息、对象信息生成一个sql语句,交给DBHelper处理,适合一些模式化的sql,例如:create、insert等

2.ConnectBuilder:连接符编辑器
这个类主要就是为了方便写查询sql,将where、and、set等,通过链式调用拼接起来,合成一条sql,和写原生的sql差不多,不过又尽可能避免了写原生sql时一不小心哪里少打了空格等问题

Sql语句生成器:

public class SqlGenerater {

    public final static String BAK_SUFFIX = "_bak";//备份的后缀

    /**
     * 生成create语句
     * <p>
     * 格式:create table Student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age TEXT)
     */
    public static String create(Class<?> clazz) {
        TableWrapper wrapper = ReflectHelper.parseClass(clazz);
        //拼接:create table Student(id INTEGER PRIMARY KEY AUTOINCREMENT,
        StringBuilder sb = new StringBuilder("create table if not exists " + wrapper.name);
        //拼接:(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age TEXT)
        sb.append(TypeConverter.zipNameType(wrapper));
        return sb.toString();
    }

    /**
     * 生成drop语句
     * <p>
     * 格式:drop table if exists Student;
     */
    public static String drop(Class<?> clazz) {
        StringBuilder sb = new StringBuilder("drop table if exists " + ReflectHelper.getTableName(clazz));
        return sb.toString();
    }

    /**
     * 生成insert语句
     * <p>
     * 格式:insert or replace into Student (name,age) values ('Fishyer',23)
     */
    public static <T> String insert(T t) {
        TableWrapper wrapper = ReflectHelper.parseObject(t);
        //拼接:insert into Student
        StringBuilder sb = new StringBuilder("insert or replace into " + wrapper.name + " ");
        //拼接:(name,age)
        sb.append(TypeConverter.zipName(wrapper));
        //拼接: values
        sb.append(" values ");
        //拼接:('Fishyer',23)
        sb.append(TypeConverter.zipValue(wrapper));
        return sb.toString();
    }

    /**
     * 生成queryAll语句
     * <p>
     * 格式:select * from Student
     */
    public static String queryAll(Class<?> clazz) {
        StringBuilder sb = new StringBuilder("select * from " + ReflectHelper.getTableName(clazz));
        return sb.toString();
    }

    /**
     * 生成deleteAll语句
     * <p>
     * 格式:delete from Student
     */
    public static String deleteAll(Class<?> clazz) {
        StringBuilder sb = new StringBuilder("delete from " + ReflectHelper.getTableName(clazz));
        return sb.toString();
    }


    /**
     * 生成queryObj语句
     * <p>
     * 格式:select * from Student where name='Fishyer' and age=23
     */
    public static <T> String queryObj(T t) {
        TableWrapper wrapper = ReflectHelper.parseObject(t);
        //拼接:select * from Student
        StringBuilder sb = new StringBuilder("select * from " + wrapper.name);
        //拼接: where name='Fishyer' and age=23
        sb.append(TypeConverter.zipConnNameValue(wrapper));
        return sb.toString();
    }

    /**
     * 生成deleteObj语句
     * <p>
     * 格式:delete from Student where name='Fishyer' and age=23
     */
    public static <T> String deleteObj(T t) {
        TableWrapper wrapper = ReflectHelper.parseObject(t);
        //拼接:select * from Student
        StringBuilder sb = new StringBuilder("delete from " + wrapper.name);
        //拼接: where name='Fishyer' and age=23
        sb.append(TypeConverter.zipConnNameValue(wrapper));
        return sb.toString();
    }

    /**
     * 生成bak语句
     * <p>
     * 格式:create table Student2 as select *from Student
     */
    public static <T> String bak(Class<T> clazz) {
        String table = ReflectHelper.getTableName(clazz);
        String tableBak = table + BAK_SUFFIX;
        StringBuilder sb = new StringBuilder("create table " + tableBak + " as select *from " + table);
        return sb.toString();
    }

    /**
     * 生成queryBak语句
     * <p>
     * 格式:select * from Student
     */
    public static String queryBak(Class<?> clazz) {
        StringBuilder sb = new StringBuilder("select * from " + ReflectHelper.getTableName(clazz) + BAK_SUFFIX);
        return sb.toString();
    }

}

连接符编辑器:

public class ConnectBuilder<T> {
    public DBHelper dbHelper;//用于调用终止连接符:query和execute
    public Class<T> clazz;//用于解析Cursor
    public String sql;

    public ConnectBuilder(DBHelper dbHelper, Class<T> clazz, String sql) {
        this.dbHelper = dbHelper;
        this.clazz = clazz;
        this.sql = sql;
    }

    /**
     * where 连接符
     * <p>
     * 1、where的默认比较符是=,如果是其它符号,需在第二个参数说明
     * 2、where与whereInt的区别在于:是否给后面的值加单引号
     */
    public ConnectBuilder<T> where(String s) {
        return where(s, "=");
    }

    public ConnectBuilder<T> where(String s, String operation) {
        this.sql = sql + (" where " + TypeConverter.addQuote(s, operation));
        return this;
    }

    public ConnectBuilder<T> whereInt(String s) {
        this.sql = sql + (" where " + s);
        return this;
    }


    /**
     * and 连接符
     */
    public ConnectBuilder<T> and(String s) {
        return and(s, "=");
    }

    public ConnectBuilder<T> and(String s, String operation) {
        this.sql = sql + (" and " + TypeConverter.addQuote(s, operation));
        return this;
    }

    public ConnectBuilder<T> andInt(String s) {
        this.sql = sql + (" and " + s);
        return this;
    }

    /**
     * set 连接符
     */
    public ConnectBuilder<T> set(String s) {
        return where(s, "=");
    }

    public ConnectBuilder<T> set(String s, String operation) {
        this.sql = sql + (" set " + TypeConverter.addQuote(s, operation));
        return this;
    }

    public ConnectBuilder<T> setInt(String s) {
        this.sql = sql + (" set " + s);
        return this;
    }

    /**
     * order by 连接符
     */
    public ConnectBuilder<T> orderBy(String field) {
        this.sql = sql + (" order by " + field);
        return this;
    }

    /**
     * desc 连接符
     */
    public ConnectBuilder<T> desc() {
        this.sql = sql + (" desc");
        return this;
    }

    /**
     * append 连接符
     * <p>
     * 代表一个空格
     */
    public ConnectBuilder<T> append(String s) {
        this.sql = sql + (" " + s);
        return this;
    }

    /**
     * 执行Sql语句,查询,有返回值
     *
     * @return
     */
    public List<T> query() throws DBException {
        return dbHelper.query(this);
    }

    /**
     * 执行Sql语句,非查询,无返回值
     *
     * @return
     */
    public void execute() throws DBException {
        dbHelper.execute(this);
    }

}

3.反射辅助类:

为了上面的SqlGenerater能生成正确的sql,我们需要用到注解和反射。

通过注解,我们在一个类中(例如上面的Person),标明了我们根据这个类去创建表时所需要的参数。

通过反射,我们可以在运行时获取到这些参数,交给SqlGenerater。

public class ReflectHelper {


    /**
     * 直接反射,获取字段值
     */
    private static <T> Object getFieldValue(T t, Field field) {
        // TODO: 16/9/15 这里怎么将返回值自动强转成fieldType呢?求解!!!
        Object value = null;
        try {
            field.setAccessible(true);
            value = field.get(t);
            field.setAccessible(false);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return value;
    }

    /**
     * 解析数据库游标
     *
     * @param cursor
     * @param clazz
     * @return
     */
    public static <T> List<T> parseCursor(Cursor cursor, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        try {
            TableWrapper wrapper = ReflectHelper.parseClass(clazz);
            while (cursor.moveToNext()) {
                T t = clazz.newInstance();
                int pos = 0;
                for (String filedName : wrapper.filedList) {
                    Class<?> type = wrapper.typeList.get(pos);
                    Object value = getCursorValue(cursor, filedName, type);
                    Field field = clazz.getDeclaredField(filedName);
                    field.setAccessible(true);
                    field.set(t, value);
                    field.setAccessible(false);
                    pos++;
                }
                LogUtil.print("-->:" + t.toString());
                list.add(t);
            }
            cursor.close();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 解析类或对象的信息
     */
    private static <T> TableWrapper parse(Class<?> clazz, T t) {
        List<String> filedList = new ArrayList<>();//字段名
        List<Class<?>> typeList = new ArrayList<>();//字段类型
        List<String> constraintList = new ArrayList<>();//字段约束(一个列的约束可能不止一个)
        List<Object> valueList = new ArrayList<>();//字段值
        //判断是否存在表注解
        if (!clazz.isAnnotationPresent(Table.class)) {
            throw new RuntimeException(clazz.getName() + "没有添加表注解");
        }

        int column = 0;
        //遍历所有的字段
        for (Field field : clazz.getDeclaredFields()) {
            //判断是否存在列注解
            if (field.isAnnotationPresent(Column.class)) {
                column++;
                String fieldName = ReflectHelper.getColumnName(field); //获取字段名
                Class<?> fieldType = field.getType();//获取字段类型
                Object fieldValue = t == null ? null : getFieldValue(t, field);//获取字段值

                //非创建时,忽略id字段
                if (t != null && fieldName.toLowerCase().equals("id".toLowerCase())) {
                    continue;
                }

                //创建表时,添加字段约束
                if (t == null) {
                    addConstraint(clazz, field, constraintList);
                }

                //插入数据时,忽略空字段
                if (t != null && fieldValue == null) {
                    continue;
                }

                //添加字段名、字段类型、字段值到列表中
                filedList.add(fieldName);
                typeList.add(fieldType);
                valueList.add(fieldValue);
            }
        }
        if (column == 0) {
            throw new RuntimeException(clazz.getName() + "表中没有添加任何列注解");
        }
        if (t != null && filedList.isEmpty()) {
            throw new RuntimeException(clazz.getName() + "表中对象所有列均为空");
        }
        return new TableWrapper(getTableName(clazz), filedList, typeList, constraintList, valueList);
    }

    /**
     * 获取表名
     *
     * @param clazz
     * @return
     */
    public static String getTableName(Class<?> clazz) {
        Table annotation = clazz.getAnnotation(Table.class);
        String value = annotation.value();
        return TextUtils.isEmpty(value) ? clazz.getSimpleName() : value;

    }

    /**
     * 获取列名
     *
     * @param field
     * @return
     */
    private static String getColumnName(Field field) {
        Column annotation = field.getAnnotation(Column.class);
        String value = annotation.value();
        return TextUtils.isEmpty(value) ? field.getName() : value;
    }


    /**
     * 添加字段约束
     *
     * @param clazz
     * @param field
     * @param list
     */
    private static <T> void addConstraint(Class<T> clazz, Field field, List<String> list) {
        StringBuffer sb = new StringBuffer();
        //遍历该字段的所有注解
        for (Annotation item : field.getDeclaredAnnotations()) {
            if (item instanceof NotNull) {
                sb.append(Constraint.NOT_NULL);
            } else if (item instanceof Default) {
                String value = getDefaultValue(clazz, field);
                sb.append(Constraint.DEFAULT + " " + value);
            } else if (item instanceof Unique) {
                sb.append(Constraint.UNIQUE);
            } else if (item instanceof Check) {
                Check annotation = field.getAnnotation(Check.class);
                String value = annotation.value();
                sb.append(Constraint.CHECK + "(" + value + ")");
            } else {
                sb.append("");
            }
        }
        list.add(sb.toString());
    }

    /**
     * 获取列的默认值
     *
     * @param clazz
     * @param field
     * @return
     */
    private static <T> String getDefaultValue(Class<T> clazz, Field field) {
        try {
            T t = clazz.newInstance();
            return getFieldValue(t, field).toString();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("获取列的默认值异常");
    }

    /**
     * 解析对象的信息
     */
    public static <T> TableWrapper parseObject(T t) {
        return parse(t.getClass(), t);
    }

    /**
     * 解析类的信息
     */
    public static TableWrapper parseClass(Class<?> clazz) {
        return parse(clazz, null);
    }

    /**
     * 解析列表的信息
     */
    public static <T> List<TableWrapper> parseList(List<T> list) {
        List<TableWrapper> wrappers = new ArrayList<>();
        for (T t : list) {
            wrappers.add(parse(t.getClass(), t));
        }
        return wrappers;
    }

    /**
     * 获取数据库Cursor的值
     * <p>
     * 例如:'Stay',23
     */
    private static Object getCursorValue(Cursor cursor, String filedName, Class<?> type) {
        while (cursor.moveToNext()) {
            int nameColumnIndex = cursor.getColumnIndex("filedName");
            String value = cursor.getString(nameColumnIndex);
        }

        //文本
        if (type == String.class) {
            return cursor.getString(cursor.getColumnIndex(filedName));
        }
        // TODO: 16/9/15 获取整数时,如果数据库存的是null,这里会自动变成0,是个问题!
        //整数
        else if (type == int.class) {
            return cursor.getInt(cursor.getColumnIndex(filedName));
        } else if (type == Integer.class) {
            return cursor.getInt(cursor.getColumnIndex(filedName));
        } else if (type == long.class) {
            return cursor.getLong(cursor.getColumnIndex(filedName));
        } else if (type == Long.class) {
            return cursor.getLong(cursor.getColumnIndex(filedName));
        } else if (type == boolean.class) {
            int anInt = cursor.getInt(cursor.getColumnIndex(filedName));
            return anInt == 0 ? false : true;
        } else if (type == Boolean.class) {
            int anInt = cursor.getInt(cursor.getColumnIndex(filedName));
            return anInt == 0 ? false : true;
        }
        //实数
        else if (type == float.class) {
            return cursor.getFloat(cursor.getColumnIndex(filedName));
        } else if (type == Float.class) {
            return cursor.getFloat(cursor.getColumnIndex(filedName));
        } else if (type == double.class) {
            return cursor.getDouble(cursor.getColumnIndex(filedName));
        } else if (type == Double.class) {
            return cursor.getDouble(cursor.getColumnIndex(filedName));
        }
        //输入形式
        else {
            return " BLOB";
        }
    }

}

**4.其余辅助类: **

表信息包装类:
这个其实就是通过ReflectHelper将一个Class<T> 解析成这个TableWrapper,它是那些写SQL的参数的载体

public class TableWrapper {
    public String name;//类名
    public List<String> filedList;//字段名
    public List<Class<?>> typeList;//字段类型
    public List<String> constraintList;//字段约束
    public List<Object> valueList;//字段值

    public TableWrapper(String name, List<String> filedList, List<Class<?>> typeList, List<String> constraintList, List<Object> valueList) {
        this.name = name;
        this.filedList = filedList;
        this.typeList = typeList;
        this.constraintList = constraintList;
        this.valueList = valueList;
    }
}

类型转换器
因为不同sql命令的参数的格式不一样,这里就是为了方便处理从class的field到table的column之间的转换

public class TypeConverter {

    //wrapper --> (name,age)
    public static String zipName(TableWrapper wrapper) {
        StringBuilder sb = new StringBuilder();
        sb.append("(");
        for (int i = 0; i < wrapper.filedList.size(); i++) {
            String filed = wrapper.filedList.get(i);
            sb.append(filed);
            if (i != wrapper.filedList.size() - 1) {
                sb.append(",");
            }
        }
        sb.append(")");
        return sb.toString();
    }

    //wrapper --> ('Fishyer',23)
    public static String zipValue(TableWrapper wrapper) {
        StringBuilder sb = new StringBuilder();
        sb.append("(");
        for (int j = 0; j < wrapper.filedList.size(); j++) {
            Class<?> type = wrapper.typeList.get(j);
            Object value = wrapper.valueList.get(j);
            sb.append(TypeConverter.getInsertValue(type, value));
            if (j != wrapper.typeList.size() - 1) {
                sb.append(",");
            }
        }
        sb.append(")");
        return sb.toString();
    }

    //wrapper --> (name TEXT NOT NULL, age TEXT)
    public static String zipNameType(TableWrapper wrapper) {
        StringBuilder sb = new StringBuilder();
        sb.append("(");
        for (int i = 0; i < wrapper.filedList.size(); i++) {
            String filed = wrapper.filedList.get(i);
            String type = TypeConverter.getCreateType(filed, wrapper.typeList.get(i));
            String constraint = wrapper.constraintList.get(i);
            sb.append(filed + type + constraint);
            if (i != wrapper.filedList.size() - 1) {
                sb.append(",");
            }
        }
        sb.append(")");
        return sb.toString();
    }

    //wrapper --> where name='Fishyer' and age=23
    public static String zipConnNameValue(TableWrapper wrapper) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < wrapper.filedList.size(); i++) {
            if (i == 0) {
                sb.append(" where ");
            } else {
                sb.append(" and ");
            }
            String filed = wrapper.filedList.get(i);
            Class<?> type = wrapper.typeList.get(i);
            Object value = wrapper.valueList.get(i);
            sb.append(filed + "=" + TypeConverter.getInsertValue(type, value));
        }
        return sb.toString();
    }

    /**
     * 获取create时的存储类型
     * <p>
     * 例如:TEXT、INTEGER
     */
    private static String getCreateType(String field, Class<?> type) {
        //主键
        if (field.toLowerCase().equals("id".toLowerCase())) {
            return " INTEGER PRIMARY KEY AUTOINCREMENT";
        }
        //文本
        if (type == String.class) {
            return " TEXT";
        }
        //整数
        else if (type == int.class) {
            return " INTEGER";
        } else if (type == Integer.class) {
            return " INTEGER";
        } else if (type == long.class) {
            return " INTEGER";
        } else if (type == Long.class) {
            return " INTEGER";
        } else if (type == boolean.class) {
            return " INTEGER";
        } else if (type == Boolean.class) {
            return " INTEGER";
        }
        //实数
        else if (type == float.class) {
            return " REAL";
        } else if (type == Float.class) {
            return " REAL";
        } else if (type == double.class) {
            return " REAL";
        } else if (type == Double.class) {
            return " REAL";
        }
        //输入形式
        else {
            return " BLOB";
        }
    }

    /**
     * 获取Insert时的存储值
     * <p>
     * 例如:'Stay',23 (主要就是为了给String加单引号)
     */
    private static String getInsertValue(Class<?> type, Object value) {
        if (type == String.class) {
            return "'" + value + "'";
        }
        else if (type == int.class) {
            return value.toString();
        }else {
            return value.toString();
        }
    }

    /**
     * 给字段加单引号
     * <p>
     * 例:Fishyer --> 'Fishyer'
     */
    public static String addQuote(String s, String operation) {
        String[] strings = s.split(operation);
        return strings[0] + operation + "'" + strings[1] + "'";
    }

}

各种注解标识:

@Table:表注解,默认是类名,也可以自定义表名
@Column:列注解,默认是字段名,也可自定义列名
@Unique:唯一性约束
@NotNull:非空约束
@Default:默认值约束
@Check:条件约束

约束符号常量

public class Constraint {
    public static final String NOT_NULL = " NOT NULL";
    public static final String DEFAULT = " DEFAULT";
    public static final String UNIQUE = " UNIQUE";
    public static final String CHECK = " CHECK";
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,057评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,504评论 18 399
  • 这部分主要是开源Java EE框架方面的内容,包括Hibernate、MyBatis、Spring、Spring ...
    杂货铺老板阅读 1,316评论 0 2
  • 一.前言 现在直播SDK相当多,有收费一条龙的,也有开源的,对于大部分非专业做直播的公司,只是项目中部分模块有直播...
    朱晓辉阅读 10,320评论 4 18
  • 语文考试刚刚结束,各地的作文题目就已经新鲜出炉了。今年上海的作文题目最为有趣,颇具创新性和思辨性,同时也最有难度。...
    李金子阅读 1,294评论 5 14