Android之数据库框架(工厂模式+单例模式搭建)

一、简述

移动端使用的数据库是Sqlite,这种小型的数据库很适合移动端存储大量的数据。数据库框架可以简化数据库代码,只需对对象进行赋值操作,方便处理复杂的业务逻辑。以下做一个简易的数据库框架,使用设计模式、泛型、注解、反射来实现。

二、数据库操作

框架表现层不涉及任何sql语句,直接操作的是数据对象,但具体的数据类型在这个接口中并不清楚,所以使用泛型来表示。先设计一个数据库表的Dao类的公共接口,用来声明数据库增删改查的操作。
IBaseDao.java

/**
 * 基本的数据操作
 */
public interface IBaseDao<M> {

    Long insert(M entity);

    Integer delete(M where);

    Integer update(M entitiy, M where);

    List<M> query(M where);

    List<M> query(M where, String orderBy);

    List<M> query(M where, String orderBy, Integer page, Integer pageCount);

}

三、Dao类工厂

一个数据库含有多个表,所以Dao类会有多个,而对数据库的操作的结构相同,为了减少代码冗余及提高代码的封装性设计一个Dao类工厂,用于打开数据库,生产对应的表操作对象。

使用SQLiteOpenHelper来创建数据库,默认会将数据库文件创建到/data/data/包名/databases目录下,当应用被删除时,数据库也将同应用一起被删除。但是在用户重装安装App时,可以使用之前的数据库信息,此时就可以自定义数据库的存放位置,在构造函数中会使用到该路径对数据库进行创建,在Dao类工厂实例化之前先对其(mDbPath)进行赋值。一般用init()方法对框架中必需的变量进行赋值。

静态内部类单例综合了饿汉式和懒汉式单例的优点,即调用时创建单例,效率高且没有线程安全问题。当在调用getInstance()方法创建工厂单例时,静态内部类Instance才会被加载,同时初始化内部类属性INSTANCE,即初始化外部类BaseDaoFactory对象(Dao工厂),因为该静态内部类只会加载一次,所以该INSTANCE对象也只会被创建一次。

Dao工厂提供一个public方法来供外界获取需要的Dao类,而外界只需要传入具体Dao类和数据实体类对应的class即可,所以此处用了方法声明泛型,如下,

public <T extends BaseDao<M>, M> T getDataHelper(Class<T> clazz, Class<M> entity)

传入的参数必须是BaseDao的子类,参数可以在方法体内调用父类BaseDao的方法。
BaseDaoFactory.java

/**
 * @描述 Dao类工厂(负责打开数据库,生产对应的表操作对象)
 */
public class BaseDaoFactory {

    private static String mDbPath;
    private SQLiteDatabase mDatabase;

    //静态内部类
    private static class Instance {
        public static BaseDaoFactory INSTANCE = new BaseDaoFactory();
    }
	
    //创建工厂单例,加载静态内部类Instance,初始化内部类属性INSTANCE(初始化外部类BaseDaoFactory对象)
    public static BaseDaoFactory getInstance() {
        return Instance.INSTANCE;
    }

    // 初始化数据库位置
    public static void init(String dbPath) {
        mDbPath = dbPath;
    }
    
	// 单例模式,一般会将构造函数私有化,以保证不会被外界初始化。
    private BaseDaoFactory() {
        if (TextUtils.isEmpty(mDbPath)) {
            throw new RuntimeException("在使用BaseDaoFactory之前,请调用BaseDaoFactory.init()初始化好数据库路径。");
        }
        // 打开数据库,得到数据库对象
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDbPath, null);
    }

    //声明泛型的方式
    public <T extends BaseDao<M>, M> T getDataHelper(Class<T> clazz, Class<M> entity) {
        T baseDao = null;
        try {
            baseDao = clazz.newInstance();
            baseDao.init(mDatabase, entity);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return baseDao;
    }
}

四、数据库操作封装

BaseDao实现数据库操作的接口IBaseDao,封装了对数据库操作的方法以提供外界的调用。
注意:外界不需要知道的属性和方法要私有化。
BaseDao.java

/**
 * @描述 Dao类的基类,包含数据库的真实操作
 */
public abstract class BaseDao<M> implements IBaseDao<M> {

    private SQLiteDatabase mDatabase;
    private Class<M> mEntityClass;
    private String mTbName;
    private Map<String, Field> mFieldMap;

    /**
     * 初始化表操作对象,一般包括:创建表、获取表字段与类字段的映射关系
     * @param database
     * @param entity
     * @return
     */
    protected boolean init(SQLiteDatabase database, Class<M> entity) {
        mDatabase = database;
        mEntityClass = entity;
        if (!database.isOpen()) {
            return false;
        }

        // 获取表名
        TbName tbName = entity.getAnnotation(TbName.class);
        mTbName = tbName == null ? entity.getSimpleName() : tbName.value();

        // 获取表映射字段
        if (!genFieldMap()) {
            return false;
        }

        // 创建数据库
        if (!createTable(database)) {
            return false;
        }
        return true;
    }

    /**
     * 创建表(可以被子类重写,方便灵活扩展)
     */
    protected boolean createTable(SQLiteDatabase database) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Field> entry : mFieldMap.entrySet()) {
            String columnName = entry.getKey();
            Field field = entry.getValue();
            TbField tbField = field.getAnnotation(TbField.class);
            int length = tbField == null ? 255 : tbField.length();
            String type = "";
            Class<?> fieldType = field.getType();
            if (fieldType == String.class) {
                type = "varchar";
            } else if (fieldType == int.class || fieldType == Integer.class) {
                type = "INTEGER";
            } else if (fieldType == double.class || fieldType == Double.class) {
                type = "double";
            } else if (fieldType == float.class || fieldType == Float.class) {
                type = "float";
            }
            if (TextUtils.isEmpty(type)) {
                Log.e("sqltest", type.getClass().getName() + "是不支持的字段");
            } else {
                sb.append(columnName + " " + type + "(" + length + "),");
            }
        }
        sb.append("tb_id INTEGER PRIMARY KEY autoincrement,");//主键
        sb.deleteCharAt(sb.lastIndexOf(","));
        String s = sb.toString();
        if (TextUtils.isEmpty(s)) {
            Log.e("sqltest", "获取不到表字段信息");
            return false;
        }
        String sql = "create table if not exists " + mTbName + " (" + s + ") ";
        Log.e("sqltest", sql);
        database.execSQL(sql);
        return true;
    }

    private boolean genFieldMap() {
        mFieldMap = new HashMap<>();
        Field[] fields = mEntityClass.getFields();// 得到类中的public字段,包括父类。
//        Field[] fields = mEntityClass.getDeclaredFields();// 得到类中声明的字段(不管是public、protected、private),不包括父类。
        if (fields == null || fields.length == 0) {
            Log.e("sqltest", "获取不到类中字段");
            return false;
        }
        for (Field field : fields) {
            field.setAccessible(true);
            TbField tbField = field.getAnnotation(TbField.class);
            mFieldMap.put(tbField == null ? field.getName() : tbField.value(), field);
        }
        return true;
    }

    @Override
    public Long insert(M entity) {
        try {
            Map<String, String> values = getValues(entity);
            ContentValues cv = getContentValues(values);
            return mDatabase.insert(mTbName, null, cv);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return 0L;
    }

    @Override
    public Integer delete(M where) {
        try {
            Map<String, String> whereMap = getValues(where);
            Condition condition = new Condition(whereMap);
            return mDatabase.delete(mTbName, condition.whereClause, condition.whereArgs);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public Integer update(M entitiy, M where) {
        try {
            Map<String, String> values = getValues(entitiy);
            ContentValues cv = getContentValues(values);
            Map<String, String> whereMap = getValues(where);
            Condition condition = new Condition(whereMap);
            return mDatabase.update(mTbName, cv, condition.whereClause, condition.whereArgs);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public List<M> query(M where) {
        return query(where, null);
    }

    @Override
    public List<M> query(M where, String orderBy) {
        return query(where, orderBy, null, null);
    }

    @Override
    public List<M> query(M where, String orderBy, Integer page, Integer pageCount) {
        List<M> list = null;
        Cursor cursor = null;
        try {
            String limit = null;
            if (page != null && pageCount != null) {
                int startIndex = --page;
                limit = (startIndex < 0 ? 0 : startIndex) + "," + pageCount;
            }
            if (where != null) {
                Map<String, String> whereMap = getValues(where);
                Condition condition = new Condition(whereMap);
                cursor = mDatabase.query(mTbName, null, condition.whereClause, condition.whereArgs, null, null, orderBy, limit);
            } else {
                cursor = mDatabase.query(mTbName, null, null, null, null, null, orderBy, limit);
            }

            list = getDataList(cursor);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
                cursor = null;
            }
        }
        return list;
    }

    /**
     * 将对象中的属性转成键值对
     */
    private Map<String, String> getValues(M entity) throws IllegalAccessException {
        Map<String, String> result = new HashMap<>();
        for (Map.Entry<String, Field> entry : mFieldMap.entrySet()) {
            Object value = entry.getValue().get(entity);
            result.put(entry.getKey(), value == null ? "" : value.toString());
        }
        return result;
    }

    /**
     * 将键值对转成ContentValues
     */
    private ContentValues getContentValues(Map<String, String> values) {
        ContentValues cv = new ContentValues();
        for (Map.Entry<String, String> val : values.entrySet()) {
            cv.put(val.getKey(), val.getValue());
        }
        return cv;
    }

    /**
     * 通过游标,将表中数据转成对象集合
     */
    private List<M> getDataList(Cursor cursor) throws IllegalAccessException, InstantiationException {
        if (cursor != null) {
            List<M> result = new ArrayList<>();
            // 遍历游标,获取表中一行行的数据
            while (cursor.moveToNext()) {
                // 创建对象
                ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();// 获取当前new的对象的 泛型的父类 类型
                Class<M> clazz = (Class<M>) pt.getActualTypeArguments()[0];// 获取第一个类型参数的真实类型
                M item = clazz.newInstance();

                // 遍历表字段,使用游标一个个取值,赋值给新创建的对象。
                Iterator<String> iterator = mFieldMap.keySet().iterator();
                while (iterator.hasNext()) {
                    // 找到表字段
                    String columnName = iterator.next();
                    // 找到表字段对应的类属性
                    Field field = mFieldMap.get(columnName);

                    // 根据类属性类型,使用游标获取表中的值
                    Object val = null;
                    Class<?> fieldType = field.getType();
                    if (fieldType == String.class) {
                        val = cursor.getString(cursor.getColumnIndex(columnName));
                    } else if (fieldType == int.class || fieldType == Integer.class) {
                        val = cursor.getInt(cursor.getColumnIndex(columnName));
                    } else if (fieldType == double.class || fieldType == Double.class) {
                        val = cursor.getDouble(cursor.getColumnIndex(columnName));
                    } else if (fieldType == float.class || fieldType == Float.class) {
                        val = cursor.getFloat(cursor.getColumnIndex(columnName));
                    }

                    // 反射给对象属性赋值
                    field.set(item, val);
                }
                // 将对象添加到集合中
                result.add(item);
            }
            return result;
        }
        return null;
    }

    class Condition {
        public Condition(Map<String, String> whereMap) {

            StringBuilder sb = new StringBuilder();
            List<String> list = new ArrayList<>();

            for (Map.Entry<String, String> entry : whereMap.entrySet()) {
                if (!TextUtils.isEmpty(entry.getValue())) {
                    sb.append("and " + entry.getKey() + "=? ");
                    list.add(entry.getValue());
                }
            }
            this.whereClause = sb.delete(0, 4).toString();
            this.whereArgs = list.toArray(new String[list.size()]);
        }
        String whereClause;
        String[] whereArgs;
    }
}

1、自定义注解:TbName和TbField

自定义注解对BaseDao进行解析。比如User类的类名对应表的表名,有可能是user,也有可能是t_user,那开发者就可以使用框架提供的注解(TbName)来进行自定义表名了,同理,类中的属性名对应表的字段名也是如此,不过对于表的初始化还需要知道表字段的长度,所以表字段注解(TbField)还多了一个length属性。
注意:
1.注解运行时必须可见,即Retention的值必须是RetentionPolicy.RUNTIME。
2.ElementType.TYPE作用于类上,ElementType.FIELD作用于类的属性上。
TbName.java

/**
 * 表名注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TbName {
    String value();
}

TbField.java

/**
 * 表字段注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TbField {
    String value();
    int length();
}

分析:

2、Dao类初始化

使用BaseDao的init()方法,来对Dao类进行一些通用的初始化工作。先使用注解TbName,表名以TbName注解中的值为表名,否则以类名作为表名。

 /**
     * 初始化表操作对象,一般包括:创建表、获取表字段与类字段的映射关系
     * @param database
     * @param entity
     * @return
     */
    protected boolean init(SQLiteDatabase database, Class<M> entity) {
        mDatabase = database;
        mEntityClass = entity;
        if (!database.isOpen()) {
            return false;
        }

        // 获取表名
        TbName tbName = entity.getAnnotation(TbName.class);
        mTbName = tbName == null ? entity.getSimpleName() : tbName.value();

        // 获取表映射字段
        if (!genFieldMap()) {
            return false;
        }

        // 创建数据库
        if (!createTable(database)) {
            return false;
        }
        return true;
    }

1)获取表字段与类字段的映射关系

类的属性名可能会与表的字段名不同,而BaseDao中的很多后续操作都会跟这两者打交道,所以,在BaseDao的初始化过程中将这两者的关系使用Map进行保存,方便后续的各种操作。

private boolean genFieldMap() {
        mFieldMap = new HashMap<>();
        Field[] fields = mEntityClass.getFields();// 得到类中的public字段,包括父类。
//        Field[] fields = mEntityClass.getDeclaredFields();// 得到类中声明的字段(不管是public、protected、private),不包括父类。
        if (fields == null || fields.length == 0) {
            Log.e("sqltest", "获取不到类中字段");
            return false;
        }
        for (Field field : fields) {
            field.setAccessible(true);
            TbField tbField = field.getAnnotation(TbField.class);
            mFieldMap.put(tbField == null ? field.getName() : tbField.value(), field);
        }
        return true;
    }

Field[] fields = mEntityClass.getFields();// 得到类中的public字段,包括父类。
Field[] fields = mEntityClass.getDeclaredFields();// 得到类中声明的字段(不管是public、protected、private),不包括父类。
field.setAccessible(true);// 将私有属性或final属性可以被访问

2)创建表

Dao类的初始化工作也包括了表的创建。一方面,因为不能在每次创建并初始化Dao类时都去重新创建一次表,所以这里就用到了sql语句中的 if not exists 关键字来避免重复创建表的问题。另一方面,其实创建表的sql语句是一种模板,两个不同的表在使用sql创建时,无非就是表名、字段名、字段类型和字段长度不同,而恰好,这些不同的元素可以使用反射+TbField注解来获取,从而实现sql语句的动态拼接,结合上一步得到的表字段与类字段的映射关系(mFieldMap)。

/**
     * 创建表(可以被子类重写,方便灵活扩展)
     */
    protected boolean createTable(SQLiteDatabase database) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Field> entry : mFieldMap.entrySet()) {
            String columnName = entry.getKey();
            Field field = entry.getValue();
            TbField tbField = field.getAnnotation(TbField.class);
            int length = tbField == null ? 255 : tbField.length();
            String type = "";
            Class<?> fieldType = field.getType();
            if (fieldType == String.class) {
                type = "varchar";
            } else if (fieldType == int.class || fieldType == Integer.class) {
                type = "INTEGER";
            } else if (fieldType == double.class || fieldType == Double.class) {
                type = "double";
            } else if (fieldType == float.class || fieldType == Float.class) {
                type = "float";
            }
            if (TextUtils.isEmpty(type)) {
                Log.e("sqltest", type.getClass().getName() + "是不支持的字段");
            } else {
                sb.append(columnName + " " + type + "(" + length + "),");
            }
        }
        sb.append("tb_id INTEGER PRIMARY KEY autoincrement,");//主键
        sb.deleteCharAt(sb.lastIndexOf(","));
        String s = sb.toString();
        if (TextUtils.isEmpty(s)) {
            Log.e("sqltest", "获取不到表字段信息");
            return false;
        }
        String sql = "create table if not exists " + mTbName + " (" + s + ") ";
        Log.e("sqltest", sql);
        database.execSQL(sql);
        return true;
    }

3、数据库的操作

1)增

如果使用原生的SQLiteDatabase将数据插入到表中,需要将数据先封装成ContentValues对象,再调用其insert()方法来执行数据插入操作。
而现在需要将数据实体转成ContentValues对象,再使用SQLiteDatabase的insert()方法来执行插入,结合键值对,对SQLiteDatabase封装步骤如下:
1.将对象中的属性转成键值对values。
2.将键值对values转成ContentValues对象。
3.使用SQLiteDatabase的insert()方法进行数据插入。

@Override
public Long insert(M entity) {
    try {
        Map<String, String> values = getValues(entity);
        ContentValues cv = getContentValues(values);
        return mDatabase.insert(mTbName, null, cv);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return 0L;
}

/**
 * 将对象中的属性转成键值对(列名--值)
 */
private Map<String, String> getValues(M entity) throws IllegalAccessException {
    Map<String, String> result = new HashMap<>();
    for (Map.Entry<String, Field> entry : mFieldMap.entrySet()) {
        Object value = entry.getValue().get(entity);
        result.put(entry.getKey(), value == null ? "" : value.toString());
    }
    return result;
}

/**
 * 将键值对转成ContentValues
 */
private ContentValues getContentValues(Map<String, String> values) {
    ContentValues cv = new ContentValues();
    for (Map.Entry<String, String> val : values.entrySet()) {
        cv.put(val.getKey(), val.getValue());
    }
    return cv;
}

2)删

要实现删除表数据功能,需要使用到SQLiteDatabase的delete()方法,其中whereClause和whereArgs是关键。在表现层需要将删除条件使用数据实体进行封装,而框架内部则是对传入的数据实体进行解析,将对象中属性值不为null的属性拿出来作为删除的条件(这也意味着常见的数据类型不能用了,如int,但可以使用Integer来替换),可分为两步:
1.将对象中的属性转成键值对whereMap。
2.使用Condition类的构造函数对whereMap中value不为null的键值对取出来拼接。

@Override
public Integer delete(M where) {
    try {
        Map<String, String> whereMap = getValues(where);
        Condition condition = new Condition(whereMap);
        return mDatabase.delete(mTbName, condition.whereClause, condition.whereArgs);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return 0;
}

class Condition {
    public Condition(Map<String, String> whereMap) {

        StringBuilder sb = new StringBuilder();
        List<String> list = new ArrayList<>();

        for (Map.Entry<String, String> entry : whereMap.entrySet()) {
            if (!TextUtils.isEmpty(entry.getValue())) {
                sb.append("and " + entry.getKey() + "=? ");
                list.add(entry.getValue());
            }
        }
        this.whereClause = sb.delete(0, 4).toString();
        this.whereArgs = list.toArray(new String[list.size()]);
    }
    String whereClause;
    String[] whereArgs;
}

whereClause是删除条件,是个字符串,需要使用?来作为占位符,多个条件需要使用and关键字连接,如:name=? and password=?
whereArgs则是对whereClause中占位符进行数值替换的字体串数组,如:new String[]{“liaoyanxia”,“123456”}

3)改

因为SQLiteDatabase的update()方法需要用到的参数有ContentValues对象,whereClause和whereArgs,其实就是将增和删的代码实现相加起来。

@Override
public Integer update(M entitiy, M where) {
    try {
        Map<String, String> values = getValues(entitiy);
        ContentValues cv = getContentValues(values);

        Map<String, String> whereMap = getValues(where);
        Condition condition = new Condition(whereMap);

        return mDatabase.update(mTbName, cv, condition.whereClause, condition.whereArgs);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return 0;
}

4)查

表数据查询还是用到了SQLiteDatabase,使用其query()方法来进行表数据查询,它的参数也比较多,这里就只封装三种查询:
1.将符合条件的表数据全部查询出来。
2.将符合条件的表数据查询出来,并可以排序。
3.将符合条件的表数据查询出来,除了可以排序,还可以分页查询。
需要注意的就是分页查询,因为SQLiteDatabase的第一页是从0开始的,而这里是表现层从1开始,所以框架代码中会对其进行自减处理。

@Override
public List<M> query(M where) {
    return query(where, null);
}

@Override
public List<M> query(M where, String orderBy) {
    return query(where, orderBy, null, null);
}

@Override
public List<M> query(M where, String orderBy, Integer page, Integer pageCount) {
    List<M> list = null;
    Cursor cursor = null;
    try {
        String limit = null;
        if (page != null && pageCount != null) {
            int startIndex = --page;
            limit = (startIndex < 0 ? 0 : startIndex) + "," + pageCount;
        }

        if (where != null) {
            Map<String, String> whereMap = getValues(where);
            Condition condition = new Condition(whereMap);
            cursor = mDatabase.query(mTbName, null, condition.whereClause, condition.whereArgs, null, null, orderBy, limit);
        } else {
            cursor = mDatabase.query(mTbName, null, null, null, null, null, orderBy, limit);
        }

        // 将查询出来的表数据转成对象集合
        list = getDataList(cursor);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } finally {
        if (cursor != null) {
            cursor.close();
            cursor = null;
        }
    }
    return list;
}

/**
 * 通过游标,将表中数据转成对象集合
 */
private List<M> getDataList(Cursor cursor) throws IllegalAccessException, InstantiationException {
    if (cursor != null) {
        List<M> result = new ArrayList<>();
        // 遍历游标,获取表中一行行的数据
        while (cursor.moveToNext()) {

            // 创建对象
            ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();// 获取当前new的对象的 泛型的父类 类型
            Class<M> clazz = (Class<M>) pt.getActualTypeArguments()[0];// 获取第一个类型参数的真实类型
            M item = clazz.newInstance();

            // 遍历表字段,使用游标一个个取值,赋值给新创建的对象。
            Iterator<String> iterator = mFieldMap.keySet().iterator();
            while (iterator.hasNext()) {
                // 找到表字段
                String columnName = iterator.next();
                // 找到表字段对应的类属性
                Field field = mFieldMap.get(columnName);

                // 根据类属性类型,使用游标获取表中的值
                Object val = null;
                Class<?> fieldType = field.getType();
                if (fieldType == String.class) {
                    val = cursor.getString(cursor.getColumnIndex(columnName));
                } else if (fieldType == int.class || fieldType == Integer.class) {
                    val = cursor.getInt(cursor.getColumnIndex(columnName));
                } else if (fieldType == double.class || fieldType == Double.class) {
                    val = cursor.getDouble(cursor.getColumnIndex(columnName));
                } else if (fieldType == float.class || fieldType == Float.class) {
                    val = cursor.getFloat(cursor.getColumnIndex(columnName));
                }

                // 反射给对象属性赋值
                field.set(item, val);
            }
            // 将对象添加到集合中
            result.add(item);
        }
        return result;
    }
    return null;
}

五、测试

usingsqlite.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/insert"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:onClick="onClick_Insert"
        android:text="插入数据" />

    <Button
        android:id="@+id/update"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/insert"
        android:layout_centerHorizontal="true"
        android:onClick="onClick_Update"
        android:text="修改数据" />

    <Button
        android:id="@+id/delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/update"
        android:layout_centerHorizontal="true"
        android:onClick="onClick_Delete"
        android:text="删除数据" />

    <Button
        android:id="@+id/query1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/delete"
        android:layout_centerHorizontal="true"
        android:onClick="onClick_Query1"
        android:text="条件查询" />

    <Button
        android:id="@+id/query2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/query1"
        android:layout_centerHorizontal="true"
        android:onClick="onClick_Query2"
        android:text="ID正序查询" />

    <Button
        android:id="@+id/query3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/query2"
        android:layout_centerHorizontal="true"
        android:onClick="onClick_Query3"
        android:text="ID逆序查询" />

    <Button
        android:id="@+id/query4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/query3"
        android:layout_centerHorizontal="true"
        android:onClick="onClick_Query4"
        android:text="分页查询" />
</RelativeLayout>

UserDao.java
通过重写父类createTable()方法来更灵活的创建表,或自定义一些其它的方法来扩展其父类的功能。

public class UserDao extends BaseDao<User> {

}

实体类User.java

@TbName("tb_user")
public class User {

    @TbField(value = "tb_password", length = 30)
    public String password;

    @TbField(value = "tb_name", length = 30)
    public String username;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;

    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "[ username:" + this.username + ", password:" + this.getPassword() + "]";
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private UserDao mUserDao;
    private User mUser;
    private TextView mTvShow;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.usingsqlite);
        mTvShow=(TextView)findViewById(R.id.text);
        //初始化数据库
        BaseDaoFactory.init(new File(getFilesDir(), "usingsql.db").getAbsolutePath());
        mUserDao = BaseDaoFactory.getInstance().getDataHelper(UserDao.class, User.class);
    }

    //插入
    public void onClick_Insert(View view) {
        mUser = new User("liaoyanxia", "123456");
        Long insert = mUserDao.insert(mUser);
        Toast.makeText(getApplicationContext(), "添加了" + (insert != -1 ? 1 : 0) + "条数据", Toast.LENGTH_SHORT).show();
    }

    //删除
    public void onClick_Delete(View view) {
        User where = new User();
        where.setUsername("liaoyanxia");
        Integer delete = mUserDao.delete(where);
        Toast.makeText(getApplicationContext(), "删除了" + delete + "条数据", Toast.LENGTH_SHORT).show();
    }

    //修改
    public void onClick_Update(View view) {
        User user = new User("lyx", "654321");
        User where = new User();
        where.setUsername("liaoyanxia");
        Integer update = mUserDao.update(user, where);
        Toast.makeText(getApplicationContext(), "修改了" + update + "条数据", Toast.LENGTH_SHORT).show();
    }

    //条件查询:查找tb_name=liaoyanxia的数据
    public void onClick_Query1(View view) {
        User where = new User();
        where.setUsername("liaoyanxia");
        List<User> list = mUserDao.query(where);
        int query = list == null ? 0 : list.size();
        Toast.makeText(getApplicationContext(), "查出了" + query + "条数据", Toast.LENGTH_SHORT).show();
        for (User user : list) {
            Log.v("sqltest","onClick_Query1"+user);
        }
    }

    //按tb_id的正序查找
    public void onClick_Query2(View view) {
        List<User> list = mUserDao.query(null, "tb_id asc");
        int query = list == null ? 0 : list.size();
        Toast.makeText(getApplicationContext(), "查出了" + query + "条数据", Toast.LENGTH_SHORT).show();
        for (User user : list) {
            Log.v("sqltest","onClick_Query2"+user);
        }
    }

    //按tb_id的逆序查找
    public void onClick_Query3(View view) {
        List<User> list = mUserDao.query(null, "tb_id desc");
        int query = list == null ? 0 : list.size();
        Toast.makeText(getApplicationContext(), "查出了" + query + "条数据", Toast.LENGTH_SHORT).show();
        for (User user : list) {
            Log.v("sqltest","onClick_Query3"+user);
        }
    }

    //分页查找,从第一页开始,每页显示两条信息
    public void onClick_Query4(View view) {
        User where = new User();
        List<User> list = mUserDao.query(where, null, 1, 2);
        int query = list == null ? 0 : list.size();
        Toast.makeText(getApplicationContext(), "查出了" + query + "条数据", Toast.LENGTH_SHORT).show();
        for (User user : list) {
            Log.v("sqltest","onClick_Query4"+user);
        }
    }
}

结果
创建数据库成功
在这里插入图片描述
表创建成功
在这里插入图片描述
插入两条数据。
在这里插入图片描述
把tb_name为liaoyanxia的数据改为lyx。
在这里插入图片描述
修改之后查询应该只有lyx,修改成功。
在这里插入图片描述
再插入两条数据用于tb_id的逆序查询。
在这里插入图片描述
删除tb_name为liaoyanxia的数据。
在这里插入图片描述
用tb_id的正序查询,结果只有lyx,删除成功。
在这里插入图片描述
最后用Navicat查看。
在这里插入图片描述

相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页