您的位置:首页 > 数据库

移动架构29_面向对象式手写数据库架构设计一(基本框架与插入数据)

2017-10-26 22:28 609 查看
Demo地址:

https://gitee.com/YuBaoZi/codes/nujo5r0xsvdyte9pq7lha77

建议:代码很简单,建议敲两边就能理解了,很实用;

效果:



数据库查看工具:SqliteLookup

一、需求:

设计一个数据库框架,数据库的位置自定义,数据库中每张表的创建和表插入、修改数据逻辑要求能够统一

二、原理分析:

1、不用DataBaseOpenHelper,而用SQLiteDatabase.openOrCreateDatabase来创建数据库,指定一个数据库的位置

2、每张表对应一个业务Bean,插入数据就是插入一个业务Bean;

3、提供数据库操作类的工厂类,根据传入的类型,生成对应的数据库操作类,完成数据库的初始化;

4、定义一个数据库操作接口:定义插入,修改的数据的行为;

定义一个抽象类(表操作类)实现这个接口:封装所有具体操作数据类的同一行为

(1)一个抽象的创建表的方法(因为不同表的创建具体实现不同);

(2)根据业务Bean生成一个表中列名和Bean的方法名对应的集合,由于实际中成员名和列名可能不同,通过注解实现“翻译;

(3)解析业务Bean,将业务Bean中的成员名和(2)中的集合比对,生成一个列名对应Bean的value的集合

(4)将(3)中的集合转换成数据库识别的内容(contentValue),设置到数据库中去

5、具体业务中:不同的业务逻辑对应一个业务Bean(用注解解释),和一个表具体操作类(实现抽象的表操作类,定义具体的表创建方法)

技术点:SQLiteDatabase、反射、泛型、注解、数据库语句拼接、模板设计模式、工厂设计模式、单例模式

工厂类:接受泛型,产生对应的数据库操作类

-> 接口:封装插入、修改的行为、限制类型;

-> 数据库抽象操作类:解析传入的业务Bean,生成可以直接进行数据库操作的API;定义操作类的类型

-> 具体操作类:实现具体不同表的创建

-> Bean:表的表名、列名封装;方便具体的业务操作;





三、代码

1、定义表名和列名对应的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
String value();
}


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbFiled {
String value();
}


2、插入

public interface IBaseDao<T> {
//插入数据
Long insert(T entity);
Long update(T entity,T where);
}


3、封装解析业务Bean(也就是T类型)的抽象类

public abstract class BaseDao<T> implements  IBaseDao<T> {
/**]
* 持有数据库操作类的引用
*/
private SQLiteDatabase database;
/**
* 保证实例化一次
*/
private boolean isInit=false;
/**
* 持有操作数据库表所对应的java类型
* User
*/
private Class<T> entityClass;
/**
* 维护这表名与成员变量名的映射关系
* key---》表名
* value --》Field
*/
private HashMap<String,Field> cacheMap;

private String tableName;
/**
* @param entity
* @param sqLiteDatabase
* @return
* 实例化一次
*/
protected synchronized boolean init(Class<T> 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()))
{
database.execSQL(createTable());
}
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 colmunName:columnNames)
{
/**
* 如果找到对应的Filed就赋值给他
* User
*/
Field colmunFiled=null;
for (Field field:colmunFields)
{
String fieldName=null;
if(field.getAnnotation(DbFiled.class)!=null)
{
fieldName=field.getAnnotation(DbFiled.class).value();
}else
{
fieldName =field.getName();
}
/**
* 如果表的列名 等于了  成员变量的注解名字
*/
if(colmunName.equals(fieldName))
{
colmunFiled= field;
break;
}
}
//找到了对应关系
if(colmunFiled!=null)
{
cacheMap.put(colmunName,colmunFiled);
}
}

}catch (Exception e)
{

}finally {
cursor.close();
}

}

@Override
public Long insert(T entity) {
Map<String,String> map=getValues(entity);
ContentValues values=getContentValues(map);
Long result =database.insert(tableName,null,values);
return result;
}
/**
* 讲map 转换成ContentValues
* @param map
* @return
*/
private ContentValues getContentValues(Map<String, String> map) {
ContentValues contentValues=new ContentValues();
Set keys=map.keySet();
Iterator<String> iterator=keys.iterator();
while (iterator.hasNext())
{
String key=iterator.next();
String value=map.get(key);
if(value!=null)
{
contentValues.put(key,value);
}
}

return contentValues;
}

private Map<String, String> getValues(T entity) {
HashMap<String,String> result=new HashMap<>();
Iterator<Field> filedsIterator=cacheMap.values().iterator();
/**
* 循环遍历 映射map的  Filed
*/
while (filedsIterator.hasNext())
{
/**
*
*/
Field colmunToFiled=filedsIterator.next();
String cacheKey=null;
String cacheValue=null;
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 (IllegalAccessException e) {
e.printStackTrace();
}
result.put(cacheKey,cacheValue);
}

return result;
}

@Override
public Long update(T entity, T where) {
return null;
}

/**
* 创建表
* @return
*/
protected  abstract  String createTable();
}


4、提供数据库操作类的工厂类:

public class BaseDaoFactory {
private String sqliteDatabasePath;
private SQLiteDatabase database;
private static BaseDaoFactory instance = new BaseDaoFactory();

private BaseDaoFactory(){
sqliteDatabasePath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/teacher.db";
openDatabase();
}

private void openDatabase() {
this.database = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath,null);
}

public static BaseDaoFactory getInstance(){
return instance;
}

public synchronized <T extends BaseDao<M>,M> T getDataHeper(Class<T> clazz,Class<M> entityClass){
T baseDao = null;
try {
baseDao = clazz.newInstance();
baseDao.init(entityClass,database);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return  baseDao;
}
}


5、具体实现:

@DbTable("tb_user")
public class User {
public User(String name,String password){
this.name = name;
this.password = password;
}

public User(){
}

@DbFiled("name")
public String name;

@DbFiled("password")
public String password;
}

public class UserDao extends BaseDao {
@Override
protected String createTable() {
return "create table if not exists tb_user(name varchar(20),password varchar(10))";
}
}


6 调用

IBaseDao<User> baseDao = BaseDaoFactory.getInstance().getDataHeper(UserDao.class,User.class);
User user = new User("teacher","123456");
baseDao.insert(user);


四、总结

由于提供了操作dao类,我们在插入数据的时候不需要在去获取数据库对象,拼接sql要语句,直接传入一个对象即可,十分简便:

User user = new User(“teacher”,”123456”);

baseDao.insert(user);

拓展性很强,当需要重新定义一个表的时候,只需要一下几步:

1、定义Bean:

@DbTable("tb_down")
@DbTable("tb_down")
public class DownFile {
public DownFile(String time,String path){
this.time = time;
this.path = path;
}

public DownFile(){
}

@DbFiled("tb_time")
public String time;

@DbFiled("tb_path")
public String path;
}


2、定义数据操作类:

public class DownDao extends BaseDao {
@Override
protected String createTable() {
return "create table if not exists tb_down(tb_time varchar(20),tb_path varchar(10))";
}
}


3、使用:

BaseDao<DownFile> fileBaseDao = BaseDaoFactory.getInstance().getDataHeper(DownDao.class,DownFile.class);
fileBaseDao.insert(new DownFile("2013.1.3","data/data/apth"));


五、补充

注解的使用:为了更好的扩展性,

colmunToFiled.getAnnotation(DbFiled.class):以DownFile为例,表示成员如time,它的对应的注解值为tb_time,也就是表中的列名tb_time;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐