一、简述
移动端使用的数据库是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查看。