安卓获取mysql数据封装方法_android自己动手封装Sqllite数据库

本文介绍了如何在Android中创建一个面向对象的数据库框架,使用泛型、反射、注解等技术,实现简单易用的增删改查接口。详细讲解了创建表、获取表名和字段、工厂类以及数据库初始化的过程,并提供了测试用例。
摘要由CSDN通过智能技术生成

废话:

android数据库框架现在特别多,比如greenDAO,litepal都是各个大神封装好的,有时候自己只知道用,却不知道实现原理,于是自己准备动手撸一个。此框架是面向对象式数据库框架设计

对于用户而言,调用一句话就可以实现增删改查的代码才是好代码,基于这种考虑,我们的框架必不可少使用泛型、反射、注解、等操作,建议大家先了解一下这些java的基本操作。

首先创建一个Bean类(模拟用户插入数据库的类)

//比如说User表 字段:id name password

public class User {

private Integer id;

private String name;

private String password;

}

既然是创建表,用户想自定义自己的字段名称和表名,那么我们就用注解去标志,在创建表或字段的时候,如果有注解,那么就取注解中的名称,反之直接取bean中默认的字段名称。

新建表名的注解

/**

*

* 用来控制表名叫做什么的注解类

*/

@Target(ElementType.TYPE)//注解方法的

@Retention(RetentionPolicy.RUNTIME)//保留位置 必须运行时 执行过程 获取相关信息

public @interface DbTable {

String value();

}

/**

*

* 用来控制字段名称叫做什么的注解类

*/

@Target(ElementType.FIELD)//注解方法的

@Retention(RetentionPolicy.RUNTIME)//保留位置 必须运行时 执行过程 获取相关信息

public @interface DbField {

String value();

}

那么我们的Bean类 如果想自定义表名,和自定义一些字段名称,可以改成

@DbTable("tb_user")

public class User {

@DbField("_id")

private Integer id;

private String name;

private String password;

}

好了,接下来,我们提供一个顶层接口类,此类的意义在于,给用户提供整体功能的一个接口类 我们封装增、删、改、查的具体实现,用户只需调用想用的功能,传入一些定制化参数

/**

* 封装操作功能的接口类

*/

public interface IBaseDao {

long insert(T entity);

// int delete(String where);

//

// long update(T entity, String where);

//

// List query(String where);

//

// List query(String where, String orderBy, Integer startIndex, Integer limit);

//

// List query(String sql);

}

然后,我们则实现insert操作。插入操作,我们必须先自动创建一个表,并且类中持有数据库的引用,对数据库操作,然后我们获取表名,字段,因为字段是外部传入,我们可以使用T泛型拿到,但是不灵活,此处我使用了Class拿到字节码进行操作 更方便。

/**

* 1.完成自动建表功能

*/

public class BaseDao implements IBaseDao {

//持有数据库的引用 对于数据库操作

private SQLiteDatabase sqLiteDatabase;

//表名

private String tableNmae;

//持有操作数据库所对应的java类型

private Class entityClass;

//标识:用来标识是否做过初始化操作

private boolean isInit = false;

//框架内部逻辑,最好不要提供构造方法给调用层调用

// public BaseDao(SQLiteDatabase sqLiteDatabase, Class entityClass) {

// this.sqLiteDatabase = sqLiteDatabase;

// this.entityClass = entityClass;

// }

public boolean init(SQLiteDatabase sqLiteDatabase, Class entityClass) {

this.sqLiteDatabase = sqLiteDatabase;

this.entityClass = entityClass;

//根据传入的entityClass来建立表,只需建立一次

if (!isInit) {

isInit = true;

//自动建表

//取到表名 判断表上有没有自定义注解类名

if (entityClass.getAnnotation(DbTable.class) == null) {

//反射到类名

tableNmae = entityClass.getSimpleName();

} else {

//取注解上的名字

tableNmae = entityClass.getAnnotation(DbTable.class).value();

}

if (!sqLiteDatabase.isOpen()) {

return false;//数据库没打开 初始化失败

}

//执行建表操作

//create table if not exists tb_user(_id integer,name varchar2(20),password varchar(20))

//单独用个方法生成create命令

String createTableSql = getCreateTableSql();

sqLiteDatabase.execSQL(createTableSql);

}

return isInit;

}

}

获取到了表名,建表之前,获取传入实体类所有成员变量类型。

private String getCreateTableSql() {

StringBuffer stringBuffer = new StringBuffer();

stringBuffer.append("create table if not exists ");

stringBuffer.append(tableNmae + "(");

//反射得到所有的成员变量

Field[] fields = entityClass.getDeclaredFields();

for (Field field : fields) {

Class type = field.getType();//拿到成员类型

if (field.getAnnotation(DbField.class) != null) {

if (type == String.class) {

stringBuffer.append(field.getAnnotation(DbField.class).value() + " TEXT,");

} else if (type == Integer.class) {

stringBuffer.append(field.getAnnotation(DbField.class).value() + " INTEGER,");

} else if (type == Long.class) {

stringBuffer.append(field.getAnnotation(DbField.class).value() + " BIGINT,");

} else if (type == Double.class) {

stringBuffer.append(field.getAnnotation(DbField.class).value() + " DOUBLE,");

} else if (type == byte[].class) {

stringBuffer.append(field.getAnnotation(DbField.class).value() + " BLOB,");

} else {

//不支持的类型号

continue;

}

} else {

if (type == String.class) {

stringBuffer.append(field.getName() + " TEXT,");

} else if (type == Integer.class) {

stringBuffer.append(field.getName() + " INTEGER,");

} else if (type == Long.class) {

stringBuffer.append(field.getName() + " BIGINT,");

} else if (type == Double.class) {

stringBuffer.append(field.getName() + " DOUBLE,");

} else if (type == byte[].class) {

stringBuffer.append(field.getName() + " BLOB,");

} else {

//不支持的类型号

continue;

}

}

//去掉最后一个逗号

if (stringBuffer.charAt(stringBuffer.length() - 1) == ',') {

stringBuffer.deleteCharAt(stringBuffer.length() - 1);

}

stringBuffer.append(")");

return stringBuffer.toString();

}

到此,此处我们的建表操作已经写好,但是如何提供给用户使用呢,那么我们将再写一个工厂类,用户调用工厂类,工厂类调用实现方法,则实现创表操作。

public class BaseDaoFactory {

private static final BaseDaoFactory ourInstance = new BaseDaoFactory();

public static BaseDaoFactory getOurInstance() {

return ourInstance;

}

private SQLiteDatabase sqLiteDatabase;

//定义建数据数据的路径

//建议写在SD卡中 好处APP删了 下次安装 数据还在

private String sqliteDatabasePath;

private BaseDaoFactory() {

//内置sd卡路径

String sqlitePath = createSDCardOrOpenDir() + "/dao.db";

if (sqlitePath == null) {

//SD卡不存在

return;

}

sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqlitePath, null);

}

public String createSDCardOrOpenDir() {

if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {

// 创建一个文件夹对象,赋值为外部存储器的目录

File sdcardDir = Environment.getExternalStorageDirectory();

//得到一个路径,内容是sdcard的文件夹路径和名字

String path = sdcardDir.getPath() + "/dbImage";

File newpath = new File(path);

if (!newpath.exists()) {

//若不存在,创建目录,可以在应用启动的时候创建

newpath.mkdirs();

}

return path;

} else {

return null;

}

}

public BaseDao getBaseDao(Class entityClass) {

BaseDao baseDao = null;

try {

baseDao = BaseDao.class.newInstance();

baseDao.init(sqLiteDatabase, entityClass);

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

return baseDao;

}

//此处测试一下 测试

BaseDao baseDao = BaseDaoFactory.getOurInstance().getBaseDao(User.class);

Toast.makeText(this, "执行成功", Toast.LENGTH_SHORT);

CRUD操作 必不可少需要成员变量获取字段和值等,此处反射太耗时,我们整体写一个缓存参数,将成员变量和数据库的字段缓存起来,方便下次对对象操作可以直接从缓存里取数据

//CacheMap 键值对 对应着 字段 》成员变量

private HashMap cacheMap;

private void initCacheMap() {

//获取所有列名 空表

String sql = "select * from " + tableNmae + " limit 1,0";

Cursor cursor = sqLiteDatabase.rawQuery(sql, null);

String[] columnNames = cursor.getColumnNames();//获取所有字段名字

//获取成员变量

Field[] fields = entityClass.getDeclaredFields();

for (Field field : fields) {

field.setAccessible(true);//把字段设置可写;

}

for (String columnName : columnNames) {//循环字段

Field columField = null;

for (Field field : fields) {//循环成员变量

String fieldName = null;

if (field.getAnnotation(DbField.class) != null) {

fieldName = field.getAnnotation(DbField.class).value();

} else {

fieldName = field.getName();

}

if (fieldName.equals(columnName)) {//

columField = field;

break;

}

}

cacheMap.put(columnName, columField);

}

//把字段和成员变量映射起来

}

并且将初始化操作 放入init方法中。

Insert方法定义

在顶层接口中,定义一个insert(T entity)方法 在BaseDao去实现

普通插入方法: sqLiteDatabase.insert(tableNmae,null,contentValues)

contentValues对应的则是插入对象的数据。

封装思路:第一步先从cacheMap获取数据 并且将值传入一个MAP,然后将MAP再封装到ContentValues,此处之所以不直接用cacheMAp封装到ContentValues,因为后面会时常用到Map。

private Map getValues(T entity) {

Map resultMap = new HashMap<>();

//获取传入类的key values

Iterator iterator = cacheMap.values().iterator();

while (iterator.hasNext()) {

Field field = iterator.next();

try {

Object object = field.get(entity);//获取成员变量的值

String value = object.toString();

if (object == null) {

continue;

}

String key = null;

//获取key

if (field.getAnnotation(DbField.class)!= null) {

key = field.getAnnotation(DbField.class).value();

} else {

key = field.getName();

}

if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {

resultMap.put(key, value);

}

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

return resultMap;

}

private ContentValues getContentValues(Map map) {

ContentValues contentValues = new ContentValues();

Set keys = map.keySet();

Iterator iterator = keys.iterator();

while (iterator.hasNext()) {

String key = iterator.next();

String value = map.get(key);

if (value != null) {

contentValues.put(key, value);

}

}

return contentValues;

}

BaseDao实现类就可以

public long insert(T entity) {

Map map = getValues(entity);

ContentValues contentValues = getContentValues(map);

long result = sqLiteDatabase.insert(tableNmae, null, contentValues);

return result;

}

测试:

public void insertObject(View view) {

BaseDao baseDao = BaseDaoFactory.getOurInstance().getBaseDao(Person.class);

baseDao.insert(new Person("lcx",24));

Toast.makeText(this, "执行成功", Toast.LENGTH_SHORT).show();

}

这是插入操作,接下来 我们可以进行 修改+删除操作

修改

正常修改操作:

sqLiteDatabase.update(tableNmae,contentvalues,"name=?",new String[] {"lcx"})

那么我们定义一个方法

public long update(T entity, T where) {}

entity 传入修改的对象

where 代表修改的条件

tatableNmae获取得到,contentvalues也可以获取到,通过cacheMap获取缓存的成员变量,再通过成员获取传入entity的值,最后封装到ContentValues,

和insert一个道理,然后具体条件,我们将定义一个内部类,写个构造,传入修改的MAp值,这样我们就可以得到 “name=?" 和条件的值

public class Condition {

private String whereCause;

private String[] whereArgs;

public Condition(Map whereCause) {

ArrayList arrayList = new ArrayList();

StringBuilder stringBuilder = new StringBuilder();

stringBuilder.append("1=1");

//取所有字段值

Set keys = whereCause.keySet();

Iterator iterator = keys.iterator();

while (iterator.hasNext()) {

String key = (String) iterator.next();

String value = whereCause.get(key);

if (value != null) {

stringBuilder.append(" and " + key + "=?");

arrayList.add(value);

}

}

this.whereCause = stringBuilder.toString();

this.whereArgs = (String[]) arrayList.toArray(new String[arrayList.size()]);

}

;

最后我们的修改操作代码 是这样的

public long update(T entity, T where) {

// UPDATE Person SET FirstName = 'Fred' WHERE LastName = 'Wilson'

// sqLiteDatabase.update(tableNmae,contentvalues,"name=?",new String[] {"lcx"})

long result = -1;

Map map = getValues(entity);

ContentValues contentValues = getContentValues(map);

//定义内部类 用来拼接修改参数及条件

Map whereCause = getValues(where);

Condition condition = new Condition(whereCause);

sqLiteDatabase.update(tableNmae, contentValues, condition.whereCause, condition.whereArgs);

return result;

}

删除

既然修改出来了 删除就更简单啦

@Override

public long delete(T where) {

// sqLiteDatabase.delete(tableNmae,whrerClause,whereArgs);

Map map = getValues(where);

Condition condition = new Condition(map);

long result = sqLiteDatabase.delete(tableNmae, condition.whereCause, condition.whereArgs);

return result;

查询

查询的话 相比较删除和修改稍微复杂一些,因为涉及查询到的数据动态转化集合对象。

正常查询

sqLiteDatabase.query(tableName,null,"id=?",new String[],null,null,orderBy,"1,5");

封装:

@Override

public List query(T where) {

return query(where, null, null, null);

}

@Override

public List query(T where, String orderBy, Integer startIndex, Integer limit) {

// sqLiteDatabase.query(tableName,null,"id=?",new String[],null,null,orderBy,"1,5");

Map map = getValues(where);

String limitString = null;

if (startIndex != null && limit != null) {

limitString = startIndex + " , " + limit;

}

Condition condition = new Condition(map);

Cursor cursor = sqLiteDatabase.query(tableNmae, null, condition.whereCause, condition.whereArgs, null, null, orderBy, limitString);

//定义一个用来解析游标的方法

List result = getResult(cursor, where);

return result;

}

主要涉及到对象转换。

循环查询出的游标,实例化传入对象,然后获取缓存的所有Map 字段-成员变量,然后循环,得到数据库字段,获取字段在游标位置,获取成员变量,通过成员变量获取属性,判断并进入对应各个属性,动态设置Field的值。最后将对象添加集合 并返回集合数据

//obj是用来表示User类的结构

private List getResult(Cursor cursor, T obj) {

ArrayList list = new ArrayList();

Object item = null;

while (cursor.moveToNext()) {

try {

item = obj.getClass().newInstance();//new User();

Iterator iterator = cacheMap.entrySet().iterator();//字段-成员变量

while (iterator.hasNext()) {

Map.Entry entry = (Map.Entry) iterator.next();

//取列名

String columnName = (String) entry.getKey();

//然后以列名拿到列名在游标中的位置

Integer columnIndex = cursor.getColumnIndex(columnName);

Field field = (Field) entry.getValue();

Class type = field.getType();

if (columnIndex != -1) {

if (type == String.class) {

field.set(item, cursor.getString(columnIndex));//set(Object obj, Object value) : 向obj对象的这个Field设置新值value

} else if (type == Double.class) {

field.set(item, cursor.getDouble(columnIndex));

} else if (type == Integer.class) {

field.set(item, cursor.getInt(columnIndex));

} else if (type == Long.class) {

field.set(item, cursor.getLong(columnIndex));

} else if (type == byte[].class) {

field.set(item, cursor.getBlob(columnIndex));

} else {

continue;

}

}

}

list.add(item);

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

cursor.close();

return list;

}

此处,CURD 雏形已成,扩展一下,将工厂类生产BaseDao的扩展下,传入俩泛型,M代表传入的User对象 T代表BaseDao,为了更灵活调用。

public ,M> T getBaseDao(Class daoClass,Class entityClass){

BaseDao baseDao=null;

try {

baseDao=daoClass.newInstance();

baseDao.init(sqLiteDatabase,entityClass);

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

return (T)baseDao;

}

记得测试下

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

verifyStoragePermissions(this);

}

public void insertObject(View view) {

IBaseDao baseDao = BaseDaoFactory.getOurInstance().getBaseDao(BaseDaoNewImpl.class, User.class);

baseDao.insert(new User(6, "abcd", "123"));

Toast.makeText(this, "执行成功!", Toast.LENGTH_SHORT).show();

}

private static final int REQUEST_EXTERNAL_STORAGE = 1;

private static String[] PERMISSIONS_STORAGE = {

"android.permission.READ_EXTERNAL_STORAGE",

"android.permission.WRITE_EXTERNAL_STORAGE"};

public static void verifyStoragePermissions(Activity activity) {

try {

//检测是否有写的权限

int permission = ActivityCompat.checkSelfPermission(activity,

"android.permission.WRITE_EXTERNAL_STORAGE");

if (permission != PackageManager.PERMISSION_GRANTED) {

// 没有写的权限,去申请写的权限,会弹出对话框

ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);

}

} catch (Exception e) {

e.printStackTrace();

}

}

public void updateObject(View view) {

BaseDaoNewImpl baseDao = BaseDaoFactory.getOurInstance().getBaseDao(BaseDaoNewImpl.class, User.class);

//update tb_user where name='jett' set password='1111'

User user = new User();

user.setName("abcd");

User where = new User();

where.setId(6);

baseDao.update(user, where);

Toast.makeText(this, "执行成功!", Toast.LENGTH_SHORT).show();

}

public void deleteObject(View view) {

BaseDao baseDao = BaseDaoFactory.getOurInstance().getBaseDao(BaseDaoNewImpl.class, User.class);

User where = new User();

where.setName("abcd");

where.setId(6);

baseDao.delete(where);

}

public void queryObject(View view) {

BaseDaoNewImpl baseDao = BaseDaoFactory.getOurInstance().getBaseDao(BaseDaoNewImpl.class, User.class);

User where = new User();

where.setName("abcd");

List list = baseDao.query(where);

Log.i("jett", "listsize=" + list.size());

for (int i = 0; i < list.size(); i++) {

System.out.println(list.get(i) + " i=" + i);

}

}

}

多用户分库

接下来,我们设计一个多用户分库功能。就是说每一个用户数据库都是独立的,这些大厂设计都是这样的,每一个用户对应不同数据库。

首先 为了方便BaseDao不生成多个实例,我们把原本生成数据库实例的方法改下

设计一个数据库连接池

private Map hashMap = Collections.synchronizedMap(new HashMap());

public , M> T getBaseDao(Class daoClass, Class entityClass) {

BaseDao baseDao = null;

try {

if (hashMap.get(daoClass.getSimpleName()) != null) {

return (T) hashMap.get(daoClass.getSimpleName());

}

baseDao = BaseDao.class.newInstance();

baseDao.init(sqLiteDatabase, entityClass);

hashMap.put(daoClass.getSimpleName(), baseDao);

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

return (T) baseDao;

}

分库的实现步骤

获取当前登录用户 以及重写一个插入方法(此方法可在新用户登录时候调用 把其余用户状态设置离线状态 新用户为在线状态)

public class UserDao extends BaseDao {

@Override

public long insert(User entity) {

//查询表中所有的用户记录

List list = query(new User());

User where = null;

for (User user : list) {

where = new User();

where.setId(user.getId());

user.setStatus(0);

Log.i("lcx", "用户" + user.getName() + "更改为未登录状态");

update(user, where);

}

Log.i("lcx", "用户" + entity.getName() + "登录");

entity.setStatus(1);

return super.insert(entity);

}

/**

* 得到当前的登录用户

*/

public User getCurrLoginUser() {

User user = new User();

user.setStatus(1);

List list = query(user);

if (list.size() > 0) {

return list.get(0);

}

return null;

}

}

定义一个枚举类 用来产生当前用户的数据库地址

用于产生私有数据库在存放位置

/**

* 用于产生私有数据库在存放位置

*/

public enum PrivateDataBaseEnums {

database("");

private String value;

PrivateDataBaseEnums(String value) {

}

//用于产生路径

public String getValue() {

UserDao userDao = BaseDaoFactory.getOurInstance().getBaseDao(UserDao.class, User.class);

if (userDao != null) {

User currUser = userDao.getCurrLoginUser();

if (currUser != null) {

File sdcardDir = Environment.getExternalStorageDirectory();

//得到一个路径,内容是sdcard的文件夹路径和名字

String path = sdcardDir.getPath() + "/dbImage";

File file = new File(path);

// File file = new File("/data/data/com.example.a48608.ls4_databaseframework_20180307");

if (!file.exists()) {

file.mkdirs();

}

///data/data/com.example.a48608.ls4_databaseframework_20180307/n0003_login.db

return file.getAbsolutePath() + "/" + currUser.getId() + "_login.db";

}

}

return null;

}

}

定义一个工厂类 生产具体用户的数据库信息

//这个用来生成不同对象的数据库地址

public , M> T getSubDao(Class daoClass, Class entityClass) {

BaseDao baseDao=null;

if(hashMap.get(PrivateDataBaseEnums.database.getValue())!=null){

return (T) hashMap.get(PrivateDataBaseEnums.database.getValue());

}

Log.i("jett","生成数据库文件的位置:"+PrivateDataBaseEnums.database.getValue());

subSqliteDatabase=SQLiteDatabase.openOrCreateDatabase(PrivateDataBaseEnums.database.getValue(),null);

try {

baseDao=daoClass.newInstance();

baseDao.init(subSqliteDatabase,entityClass);

hashMap.put(PrivateDataBaseEnums.database.getValue(),baseDao);

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

return (T) baseDao;

}

测试

BaseDao userDao;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

userDao = BaseDaoFactory.getOurInstance().getBaseDao(UserDao.class, User.class);

}

/*模拟用户登录的 此处的insert是UserDao的insert 就是说把当前用户User为在线状态 其余为离线状态**/

public void clickLogin(View view) {

//服务器传回的信息

User user = new User();

user.setName("张三" + (++i));

user.setPassword("123456");

user.setId("N00" + i);

//数据插入

userDao.insert(user);

Toast.makeText(this, "执行成功!", Toast.LENGTH_SHORT).show();

}

/*执行这一步之前 我们已经知道了 当前用户,也已经实例化了UserDAO,那么根据这两点 执行枚举方法 得到当前用户数据库地址,然后打开或者创建当前用户数据库, 并且初始化。 接下来进行一个新增动作 就可以查看当前用户的数据库插入的表数据*/

public void clickSubInsert(View view) {

Photo photo = new Photo();

photo.setPath("data/data/my.jpg");

photo.setTime(new Date().toString());

PhotoDao photoDao = BaseDaoSubFactory.getOurInstance().getSubDao(PhotoDao.class, Photo.class);

photoDao.insert(photo);

Toast.makeText(this, "执行成功!", Toast.LENGTH_SHORT).show();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值