在android开发中,难免会对数据库进行操作,如果说每次要自己去写一些具体的数据库操作语句来操作数据库,我个人感觉非常的麻烦,所以今天就写了一个操作数据库的封装类,是需要调用一些基本的增、删、改、查等,而且创建表格,插入数据都不需要我们去写一些操作语句,只需要将你需要插入的表格,写成一个实体类,然后将要插入的数据作为该类的属性,然后保存数据到该实体类中,就能简单的实现我们的数据库基本操作了,多说不意,下面直接看代码吧,所有的解释都在代码中注释了。
IOpenHelper.java(所有要操作的功能,全部写在这个接口中,功能可以任意添加,任意删除,当然了,要有实际用处)
public interface IOpenHelper {
void save(Object obj); //保存数据
void saveAll(Collection collection); //保存所有数据
<T> List<T> queryAll(Class<T> table); //根据类名(表名)查找所有的数据
<T> List<T> queryAll(Class<T> table, String order); //通过排序的方式查找所有的数据
<T> List<T> queryAll(Class<T> table, String order, int limit); //通过排序和一夜显示多少条数据来查询所有的数据
<T> T queryById(Class<T> table, Object id);//通过id查找对应的数据
void clear(Class table); //清空对应表名中的所有数据
void delete(Object obj); //删除对应的数据;
void deleteAll(Collection collection); // 删除集合中所有的数据
}
MyOpenHelper.java(具体实现类)
public class MyOpenHelper extends SQLiteOpenHelper implements IOpenHelper {
public MyOpenHelper(Context context, String name) {
super(context, name, null, 1);
}
/**
* 初始化数据库,一般我们都在这里写语句,现在我们自己封装了方法,就不需要在这里写
*
* @param db
*/
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
/**
* 保存对应的对象到数据库,该类的类名就是插入到数据库的表名
* @param obj 插入到数据库的对象
*/
@Override
public void save(Object obj) {
//获取类类型
Class> table = obj.getClass();
//创建对应的表
createTableIfNotExists(table);
//具体实现保存数据方法
save(obj, table, getWritableDatabase());
}
/**
* 保存数据的主要操作
* @param obj 数据库对象
* @param table 对象类类型
* @param db 操作数据库
*/
private void save(Object obj, Class> table, SQLiteDatabase db) {
//将一个对象中的所有字段添加到该数据集中
ContentValues contentValues = new ContentValues();
//通过反射获取一个类中的所有属性
Field[] declaredFields = table.getDeclaredFields();
//遍历所有的属性
for (Field field : declaredFields) {
//获取对应的修饰类型
int modifiers = field.getModifiers();
//如果不是静态的就插入到数据库
if (!Modifier.isStatic(modifiers)) {
//设置一下数据访问权限为最高级别,也就是public
field.setAccessible(true);
try {
//将每一个字段的信息保存到数据集中
contentValues.put(field.getName(), field.get(obj) + "");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
//对于一般的数据操作,我们采用通常是insert来插入数据,但是为了防止同一个对象的数据进行刷新,所以采用直接替换掉
db.replace(table.getName().replaceAll("\\.", "_"), null, contentValues);
}
/**
* 这里是保存统一对象的多个数据,通过获取集合中的对象,来保存所有的数据
* @param collection
*/
@Override
public void saveAll(Collection collection) {
//如果集合为空直接不需要操作
if (collection.isEmpty()) {
return;
}
SQLiteDatabase db = getWritableDatabase();
//获取该集合的一个对象即可,因为一个集合中保存的都是同一个对象
Object next = collection.iterator().next();
//然后创建该对象所对应的表
createTableIfNotExists(next.getClass());
//这里为了提高效率采用了事务处理方式,对于事务这里不做过多的讲解
db.beginTransaction();
for (Object o : collection) {
save(o);
}
//设置事务为成功状态
db.setTransactionSuccessful();
//当事务结束,才会一次性执行上面for中的所有save方法,如果该事务没有结束,则for中的save方法一个都不会执行
db.endTransaction();
}
/**
* 通过表名,查询所有的数据,表名对应于类名
* @param table 类类型
* @param 泛型参数,任意类型
* @return
*/
@Override
public List queryAll(Class table) {
//如果该表不存在数据库中,则不需要进行操作
if (!isTableExists(table)) {
return null;
}
SQLiteDatabase db = getReadableDatabase();
//获取表名,因为表名是采用完全包名的形式存储,按照表名规则,不允许有 "." 的存在,所以采用"_"进行替换
String tableName = table.getName().replaceAll("\\.", "_");
//通过表名查询所有的数据
Cursor cursor = db.query(tableName, null, null, null, null, null, null);
//通过initList拿到对应的数据
List result = initList(table, cursor);
//关闭游标
cursor.close();
//返回结果
return result;
}
/**
* 通过指定的顺序返回所有查询的结果
* @param table 类类型
* @param orderBy 指定顺序
* @param 泛型参数
* @return
*/
@Override
public List queryAll(Class table, String orderBy) {
//这里所有的操作和上面类似,就不依依介绍了
if (!isTableExists(table)) {
return null;
}
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(table.getName().replaceAll("\\.", "_"), null, null, null, null, null, orderBy);
List result = initList(table, cursor);
return result;
}
/**
* 通过指定的顺序和查询多少页来查询所有的数据
* @param table 类类型
* @param orderBy 指定顺序
* @param limit 指定的页数
* @param
* @return
*/
@Override
public List queryAll(Class table, String orderBy, int limit) {
//如上雷同,不做介绍
if (!isTableExists(table)) {
return null;
}
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(table.getName().replaceAll("\\.", "_"), null, null, null, null, null, orderBy, String.valueOf(limit));
List result = initList(table, cursor);
return result;
}
/**
* 通过id来查询对应的数据
* @param table
* @param id
* @param
* @return
*/
@Override
public T queryById(Class table, Object id) {
Field idField = null;
//获取属性id
idField = getFieldId(table);
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(table.getName().replaceAll("\\.", "_"),
null,
(idField == null ? "_id" : idField.getName()) + " = ?", //判断,如果对应的类中存在id,则通过该类中的id查找数据,如果不存在id就采用使用默认的_id来查询数据
new String[]{String.valueOf(id)}, null, null, null);
List list = initList(table, cursor);
if (list.isEmpty()) {
return null;
} else {
return list.get(0);//这里是通过id查询数据,因为id唯一,所以查到的数据最多也就一个,直接返回list.get(0)
}
}
/**
* 通过表名清空所有的数据
* @param table 类类型
*/
@Override
public void clear(Class table) {
SQLiteDatabase db = getWritableDatabase();
db.delete(table.getName().replaceAll("\\.","_"), null, null);
}
/**
* 删除数据
* @param obj 指定对象(表)中的数据
*/
@Override
public void delete(Object obj) {
SQLiteDatabase db = getWritableDatabase();
delete(obj, db);
}
/**
* 主要删除操作,主要是通过id来删除,因为删除一条操作必须有一个唯一列项
* @param obj 指定对象(表)中的数据
* @param db
*/
private void delete(Object obj, SQLiteDatabase db){
//首先获取该类中的id,如果有就会获取到
Field idField = getFieldId(obj.getClass());
//如果不存在属性id,就不需要删除
if (idField != null) {
idField.setAccessible(true);
try {
db.delete(obj.getClass().getName().replaceAll("\\.", "_"),
idField.getName() + " = ?",
new String[]{idField.get(obj).toString()});
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
/**
* 删除集合中所有的对象数据
* @param collection
*/
@Override
public void deleteAll(Collection collection) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
for (Object o : collection) {
delete(o, db);
}
db.setTransactionSuccessful();
db.endTransaction();
}
/**
* 这个方法的主要功能是将数据中查询到的数据放到集合中。
* 类似于我们查询到对应的数据重新封装到一个对象中,然后把这个对象
* 放入集合中。这样就能拿到我们的数据集了
* @param table
* @param cursor
* @param
* @return
*/
private List initList(Class table, Cursor cursor) {
List result = new ArrayList<>();
//这里可能大家不了解,这是Gson为我们提供的一个通过JDK内部API 来创建对象实例,这里不做过多讲解
UnsafeAllocator allocator = UnsafeAllocator.create();
while (cursor.moveToNext()) {
try {
//创建具体的实例
T t = allocator.newInstance(table);
boolean flag = true;
//遍历所有的游标数据
for (int i = 0; i < cursor.getColumnCount(); i++) {
//每次都去查找该类中有没有自带的id,如果没有,就不应该执行下面的语句
//因为下面获取属性名时,有一个异常抛出,要是找不到属性就会结束这个for循环
//后面的所有数据就拿不到了,只要检测到没有id,就不需要再检测了。
if(flag){
Field fieldId = getFieldId(table);
if(fieldId == null){
flag = !flag;
continue;
}
}
//通过列名获取对象中对应的属性名
Field field = table.getDeclaredField(cursor.getColumnName(i));
//获取属性的类型
Class> type = field.getType();
//设置属性的访问权限为最高权限,因为要设置对应的数据
field.setAccessible(true);
//获取到数据库中的值,由于sqlite是采用若语法,都可以使用getString来获取
String value = cursor.getString(i);
//通过判断类型,保存到指定类型的属性中,这里判断了我们常用的数据类型。
if (type.equals(Byte.class) || type.equals(Byte.TYPE)) {
field.set(t, Byte.parseByte(value));
} else if (type.equals(Short.class) || type.equals(Short.TYPE)) {
field.set(t, Short.parseShort(value));
} else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) {
field.set(t, Integer.parseInt(value));
} else if (type.equals(Long.class) || type.equals(Long.TYPE)) {
field.set(t, Long.parseLong(value));
} else if (type.equals(Float.class) || type.equals(Float.TYPE)) {
field.set(t, Float.parseFloat(value));
} else if (type.equals(Double.class) || type.equals(Double.TYPE)) {
field.set(t, Double.parseDouble(value));
} else if (type.equals(Character.class) || type.equals(Character.TYPE)) {
field.set(t, value.charAt(0));
} else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) {
field.set(t, Boolean.parseBoolean(value));
} else if (type.equals(String.class)) {
field.set(t, value);
}
}
result.add(t);
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
/**
* 判断表格是否存在
* @param table
* @return
*/
private boolean isTableExists(Class table) {
SQLiteDatabase db = getReadableDatabase();
//查询表是否存在
Cursor cursor = db.query("sqlite_master", null, "type = 'table' and name = ?", new String[]{table.getName().replaceAll("\\.", "_")}, null, null, null);
boolean isExists = cursor.getCount() > 0;
cursor.close();
return isExists;
}
/**
* 如果表格不存在就创建该表。如果存在就不创建
* @param table
*/
private void createTableIfNotExists(Class table) {
if (!isTableExists(table)) {
SQLiteDatabase db = getWritableDatabase();
StringBuilder builder = new StringBuilder();
builder.append("CREATE TABLE IF NOT EXISTS ");
builder.append(table.getName().replaceAll("\\.", "_"));
builder.append(" (");
Field id = getFieldId(table);
if (id == null) {
builder.append("_id Integer PRIMARY KEY AUTOINCREMENT,");
} else {
builder.append(id.getName()).append(" PRIMARY KEY, ");
}
for (Field field : table.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (!field.equals(id) && !Modifier.isStatic(modifiers)) {
builder.append(field.getName()).append(",");
}
}
builder.deleteCharAt(builder.length() - 1);
builder.append(")");
db.execSQL(builder.toString());
}
}
/**
* 获取对象属性中的id字段,如果有就获取,没有就不获取
* @param table
* @return
*/
private Field getFieldId(Class table) {
Field fieldId = null;
try {
fieldId = table.getDeclaredField("id");
if (fieldId == null) {
table.getDeclaredField("_id");
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return fieldId;
}
}
DatabaseUtils.java(最终,我们只需要通过这个类来操作数据库即可)
public class DatabaseUtils {
private static MyOpenHelper mHelper;
private DatabaseUtils(){
}
/**
* 一般来说这里的initHelper放到application中去初始化
* 当然也可以在项目运行阶段初始化
*/
public static void initHelper(Context context,String name){
if(mHelper == null){
mHelper = new MyOpenHelper(context,name);
}
}
public static MyOpenHelper getHelper(){
if(mHelper == null){
new RuntimeException("MyOpenHelper is null,No init it");
}
return mHelper;
}
}
下面来通过几个列子来测试下:
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//必须先初始化
DatabaseUtils.initHelper(this,"user.db");
//创建学生类
Student student1 = new Student("张三","1001",12);
//将学生类保存到数据库
DatabaseUtils.getHelper().save(student1);
}
}
Student.java
public class Student {
//姓名
private String name;
//学号
private String nubmer;
//年龄
private int age;
public Student() {
}
public Student(String name, String nubmer, int age) {
this.name = name;
this.nubmer = nubmer;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNubmer() {
return nubmer;
}
public void setNubmer(String nubmer) {
this.nubmer = nubmer;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后,我们看看结果吧:
通过adb shell命令我们可以看到,我们对应的数据已经插入到数据库了,是不是很简单,我们只需要调用DatabaseUtils.getHelper().save(student1);
就可以保存对应的数据到数据库了,下面我们测试一下插入多条操作。
添加如下语句
List list = new ArrayList<>();
list.add(new Student("李四","1002",13));
list.add(new Student("王五","1003",23));
list.add(new Student("赵六","1004",21));
list.add(new Student("钱七","1005",20));
DatabaseUtils.getHelper().saveAll(list);
结果如下:
这样多条数据也就插入了,非常方便吧。由于Student类中本身没有自带id,所以会使用系统默认的_id来进行自动增长,才会导致出现两条相同的张三数据,下面再来测试一下查询操作:
List list = DatabaseUtils.getHelper().queryAll(Student.class);
Log.d("TAG", "onCreate: " + list.size());
for (Student student : list) {
Log.d("TAG", "onCreate: " + student);
}
打印结果如下:
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: 6
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='张三', nubmer='1001', age=12}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='张三', nubmer='1001', age=12}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='李四', nubmer='1002', age=13}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='王五', nubmer='1003', age=23}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='赵六', nubmer='1004', age=21}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='钱七', nubmer='1005', age=20}