在项目开发中都会碰要将一些数据缓存在本地,SharedPreferences、流的方式写入文件、数据库等方式都可实现,在这些方式中,数据库相对来说要繁琐些,使用的频率相应的也会少些,刚好这段时间在学习数据库,故将所学记录于此。
一般情况下都会将数据库存储在data/目录下,这里是将数据库存在的sd里面,不是data/目录下,在学习中涉及到这些方面的知识:
1、泛型
2、注解
3、反射
4、数据库拼接语句
5、单例等设计模式
6、Android6.0权限适配
实现了增、删、改、查功能,同时适配了Android6.0权限问题
增:实现了单条数据插入和批量插入,批量插入5000千条数据耗时500多毫秒
查:实现了本地分页查询及提供了多条件查询的接口
代码实现:
/**
* Created by Administrator on 2017/11/10.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbFiled {
String value();
}
/**
* Created by Administrator on 2017/11/10.
* 表名注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
String value();
}
上面这两个是注解类,DbFiled主要用于数据模型的注解,DbTable用于数据库表名的注解;
public interface IBaseDao {
/**
* 插入数据库
* @param entity 插入的数据对象
* @return
*/
Long insert(T entity);
/**
* 批量插入数据库
* @param entity 插入的数据对象
* @return
*/
void insert(List entity);
/**
* 更新数据库
* @param entity 更新的数据
* @param where 条件
* @return
*/
int update(T entity,T where);
/**
* 删除数据库
* @param entity
* @return
*/
int delete(T entity);
/**
* 查询数据
* @param where 查询条件
* @return
*/
List query(T where);
/**
* 查询数据
* @param where 查询条件
* @param orderBy 查询排序
* @param startIndex 开始的位置
* @param limit 查询限制条件
* @return
*/
List query(T where, String orderBy, Integer startIndex, Integer limit);
/**
* 查询数据 用于多条件查询
* @param sql 查询语句
* @return
*/
List query(String sql);
}
数据库接口,增、删、改、查等方法都定义在这里,具体的让实现类去实现;
public class BaseDaoFactory {
private String sqliteDatabasePath;
private SQLiteDatabase sqLiteDatabase;
private static BaseDaoFactory instance = new BaseDaoFactory();
public BaseDaoFactory() {
sqliteDatabasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/teacher.db";
openDatabase();
}
public synchronized , M> T
getDataHelper(Class clazz, Class entityClass) {
BaseDao baseDao = null;
try {
baseDao = clazz.newInstance();
baseDao.init(entityClass, sqLiteDatabase);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return (T) baseDao;
}
private void openDatabase() {
this.sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null);
}
public static BaseDaoFactory getInstance() {
return instance;
}
}
BaseDaoFactory类主要实现数据库的初始化和开启及储存路径的初始化;
public abstract class BaseDao implements IBaseDao {
/**
* 保证实例化一次
*/
private boolean isInit = false;
/**
* 持有操作数据库表所对应的java类型
* User
*/
private Class entityClass;
private String tableName;
/**
* ]
* 持有数据库操作类的引用
*/
private SQLiteDatabase database;
/**
* 维护这表名与成员变量名的映射关系
* key---》表名
* value --》Field
*/
private HashMap cacheMap;
protected synchronized boolean init(Class entity, SQLiteDatabase sqLiteDatabase) {
if (!isInit) {
entityClass = entity;
database = sqLiteDatabase;
//获取数据库表名
if (entity.getAnnotation(DbTable.class) == null) {
tableName = entity.getClass().getSimpleName();
} else {
tableName = entity.getAnnotation(DbTable.class).value();
}
//判断数据库是否打开
if (!database.isOpen()) {
return false;
}
if (!TextUtils.isEmpty(createTable(entity,tableName))) {
//执行建表语句
database.execSQL(createTable(entity,tableName));
}
cacheMap = new HashMap<>();
//缓存维护映射关系
initCacheMap();
isInit = true;
}
return isInit;
}
/**
* 维护映射关系
*/
private void initCacheMap() {
String sql="select * from "+this.tableName+" limit 1 , 0";
Cursor cursor=null;
try {
cursor=database.rawQuery(sql,null);
//表的列名数组
String[] columnNames = cursor.getColumnNames();
//拿到Filed数组
Field[] colmunFields = entityClass.getFields();
for (Field filed : colmunFields) {
//设置私有可以访问
filed.setAccessible(true);
}
//开始找对应关系
for (String columnName : columnNames) {
//如果找到对应的Field就赋值给他
Field columnFiled=null;
for (Field filed : colmunFields) {
String filedName="";
if(filed.getAnnotation(DbFiled.class)!=null){
filedName=filed.getAnnotation(DbFiled.class).value();
}else{
filedName=filed.getName();
}
//如果表的列名等于了成员变量的注解名字
if(columnName.equals(filedName)){
columnFiled=filed;
break;
}
}
//找到了对应关系
if(columnFiled!=null){
cacheMap.put(columnName,columnFiled);
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭游标
cursor.close();
}
}
@Override
public Long insert(T entity) {
Map map=getValues(entity);
ContentValues values=getContentValues(map);
Long insert = database.insert(tableName, null, values);
return insert;
}
@Override
public void insert(List entity) {
//批量插入采用事务
database.beginTransaction();
for (T data : entity) {
insert(data);
}
database.setTransactionSuccessful();
database.endTransaction();
}
@Override
public int update(T entity, T where) {
int result=-1;
Map values = getValues(entity);
//将条件对象转成map
Map whereValue = getValues(where);
Condition codition=new Condition(whereValue);
ContentValues contentValues = getContentValues(values);
result=database.update(tableName,contentValues,codition.getWhereClause(),codition.getWhereArgs());
return result;
}
@Override
public int delete(T entity) {
Map values = getValues(entity);
Condition condition=new Condition(values);
int result=database.delete(tableName,condition.getWhereClause(),condition.getWhereArgs());
return result;
}
@Override
public List query(T where, String orderBy, Integer startIndex, Integer limit) {
Map values = getValues(where);
String limitString="";
if(startIndex!=null&&limit!=null){
limitString=startIndex+" , "+limit;
}
Condition condition=new Condition(values);
Cursor cursor=database.query(tableName,null,condition.getWhereClause(),condition.getWhereArgs(),
null,null,orderBy,limitString);
List result=getResult(cursor,where);
//关闭游标
cursor.close();
return result;
}
@Override
public List query(T where) {
return query(where,null,null,null);
}
/**
* 根据查询条件获取查询结果
* @param cursor 数据库游标
* @param where 查询条件
* @return 根据查询条件返回的结果
*/
private List getResult(Cursor cursor, T where) {
List list=new ArrayList();
Object item;
while (cursor.moveToNext()){
try {
item=where.getClass().newInstance();
//遍历缓存的映射关系
Iterator> iterator = cacheMap.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry entry = iterator.next();
//得到列名
String colomunName = entry.getKey();
//然后以列名拿到 列名在游标的位置
Integer columnIndex = cursor.getColumnIndex(colomunName);
Field field = entry.getValue();
Class> type = field.getType();
if(columnIndex!=-1){
//反射赋值
if(type==String.class){
field.set(item,cursor.getString(columnIndex));
}else if(type==Integer.class){
field.set(item,cursor.getInt(columnIndex));
}else if(type==Double.class){
field.set(item,cursor.getDouble(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 (Exception e){
e.printStackTrace();
}
}
return list;
}
/**
* 封装修改语句
*/
class Condition{
//查询条件
//username=? && passwrod=?
private String whereClause;
private String [] whereArgs;
public Condition(Map whereClause){
List list=new ArrayList();
StringBuilder sb=new StringBuilder();
sb.append(" 1=1 ");
Set keys = whereClause.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
String key = iterator.next();
String value = whereClause.get(key);
if(value!=null){
//拼接条件查询语句
sb.append(" and "+key+" =?");
list.add(value);
}
}
this.whereClause=sb.toString();
this.whereArgs= (String[]) list.toArray(new String[list.size()]);
}
public String getWhereClause() {
return whereClause;
}
public String[] getWhereArgs() {
return whereArgs;
}
}
/**
* 将缓存的map数据转成ContentValues
* @param map
* @return
*/
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;
}
/**
* 根据数据对象和数据库表字段,将数据转成key value的形式
* @param entity 数据对象
* @return 转换后获取到的数据
*/
private Map getValues(T entity) {
Map result=new HashMap<>();
//遍历缓存数据,并进行映射
Iterator fieldIterator = cacheMap.values().iterator();
while (fieldIterator.hasNext()){
Field colmunToFiled = fieldIterator.next();
String cacheKey="";
String cacheValue="";
if(colmunToFiled.getAnnotation(DbFiled.class)!=null){
cacheKey=colmunToFiled.getAnnotation(DbFiled.class).value();
}else{
cacheKey=colmunToFiled.getName();
}
try {
if(null==colmunToFiled.get(entity)){
continue;
}
cacheValue=colmunToFiled.get(entity).toString();
}catch (Exception e){
e.printStackTrace();
}
result.put(cacheKey,cacheValue);
}
return result;
}
/**
* 创建表
*
* @return
*/
protected abstract String createTable(Class entity,String tableName);
}
增、删、改、查功能的实现都在BaseDao类中,并提供了一个createTable建表的抽象方法,由具体子类来实现;
public class UserDao extends BaseDao {
@Override
protected String createTable(Class entity,String tableName) {
//创建表 动态创建数据库表
StringBuffer sb = new StringBuffer();
sb.append("create table if not exists ")
.append(tableName)
.append(" ( id integer primary key autoincrement, ");
Field[] fields = entity.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
String type = field.getType().getSimpleName();
//进行转换 int->integer string->text
sb.append(name).append(getColumnType(type)).append(", ");
}
sb.replace(sb.length() - 2, sb.length(), ")");
String createTableSql = sb.toString();
Log.e("create table", "表语句-->" + createTableSql);
// return "create table if not exists tb_user(username varchar(20),password varchar(20))";
return createTableSql;
}
private String getColumnType(String type) {
String value = "";
if (type.contains("String")) {
value = " text";
} else if (type.contains("int")) {
value = " integer";
} else if (type.contains("boolean")) {
value = " boolean";
} else if (type.contains("float")) {
value = " float";
} else if (type.contains("double")) {
value = " double";
} else if (type.contains("char")) {
value = " varchar";
} else if (type.contains("long")) {
value = " long";
}
return value;
}
@Override
public List query(String sql) {
//用于多条件查询
return null;
}
}
UserDao具体的建表类,这里建表采用的是动态建表语句建表;
@DbTable("tb_user")
public class User {
public String username;
public String password;
public User(){
//这里需要提供无参构造,用于反射
}
public User(String name,String pwd){
this.password=pwd;
this.username=name;
}
}
下面就是具体的调用:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
IBaseDao baseDao;
private static final int REQUEST_DODE=1000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PermissionHelper.with(MainActivity.this).
requestPermission(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}).
requestCode(REQUEST_DODE).
request();
}
@PermissionSuccess(requestCode =REQUEST_DODE)
private void dbSuccess(){
baseDao= BaseDaoFactory.getInstance().getDataHelper(UserDao.class,User.class);
}
@PermissionFail(requestCode =REQUEST_DODE)
private void dbFail(){
Toast.makeText(this, "sd卡申请权限", Toast.LENGTH_SHORT).show();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionHelper.requestPermissionsResult(this,requestCode,permissions,grantResults);
}
/**
* 插入数据
* @param view
*/
public void insertData(View view){
User user=new User("李四","1234567898");
baseDao.insert(user);
}
/**
* 插入批量数据
* @param view
*/
public void insertList(View view){
long startTime = System.currentTimeMillis();
List list=new ArrayList<>();
for(int i=0;i<5000;i++){
User user=new User("张三","1234567890");
list.add(user);
}
baseDao.insert(list);
long endTime = System.currentTimeMillis();
Log.e("time","耗时:"+(endTime-startTime));
}
/**
* 更新数据库指定数据
* @param view
*/
public void updatDB(View view){
User user=new User();
user.username="李四";
User where=new User();
where.username="王五";
baseDao.update(where,user);
}
/**
* 删除数据库指定数据
* @param view
*/
public void deleteDB(View view){
User where=new User();
where.username="王五";
baseDao.delete(where);
}
/**
* 数据库查询数据
* @param view
*/
public void queryDB(View view){
User user=new User();
user.username="李四";
List query = baseDao.query(user);
Log.e(TAG,"数据库查询数据"+query.size());
for (User user1 : query) {
Log.e(TAG,"姓名:"+user1.username+"密码:"+user1.password);
}
}
/**
* 数据库分页查询数据
* @param view
*/
public void queryDB1(View view){
User user=new User();
user.username="张三";
List query = baseDao.query(user,"",10,20);
Log.e(TAG,"数据库查询数据"+query.size());
for (User user1 : query) {
Log.e(TAG,"姓名:"+user1.username+"密码:"+user1.password);
}
}
}
源码地址:
http://pan.baidu.com/s/1c2D8MMs