数据库的作用:
在Android开发中,数据库在小中型的app中使用并不广泛,但对于数据管理的方便性是其他数据存储结构工具无法取代的,特点:数据集中管理,控制冗余,提高数据的利用率和一致性,有利于程序的开发和维护。
数据库的设计:
数据库设计的三大范式:
第一范式:原子性
第二范式:唯一性
第三范式:避免冗余性
Android数据库架构分析
如何自动创建数据库、自动创建数据库表
通过创建自定义注解,将表名应用为类名,数据库字段设为类的全局变量,创建bean对象,运用反射得到类名和字段,创建sql语句,自动创建数据可
// 得到User对应表名
@DbTable("tb_user")
public class User {
// 得到User对象对应列名
@DbField("u_id")
private Integer id;
private String name;
private String password;
private Integer status;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public User(){}
public User(Integer id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
创建db
public class BaseDaoFactory {
private static final BaseDaoFactory instance = new BaseDaoFactory();
public static BaseDaoFactory getInstance(){
return instance;
}
private SQLiteDatabase sqLiteDatabase;
private String sqlitePath;
// 设计要给数据库连接池,new 容器,只要new个一次,下次就不会再创建了。考虑多线程的问题
protected Map<String,BaseDao> map = Collections.synchronizedMap(new HashMap<String, BaseDao>());
protected BaseDaoFactory(){
sqlitePath = "data/data/com.neteasedb/ne.db";
sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqlitePath,null);
}
// 生产basedao对象
public <T extends BaseDao<M>,M> T getBaseDao(Class<T> daoClass,Class<M> entityClass){
BaseDao baseDao = null;
if(map.get(daoClass.getSimpleName()) != null){
return (T)map.get(daoClass.getSimpleName());
}
try {
baseDao = daoClass.newInstance();
baseDao.init(sqLiteDatabase,entityClass);
map.put(daoClass.getSimpleName(),baseDao);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return (T)baseDao;
}
}
如何让用户在使用的时候非常的方便、 数据库的查询,修改,删除
创建SQLiteDatabase持有数据库的引用,创建一个缓存空间,避免频繁引用反射,耗费内存。
public class BaseDao<T> implements IBaseDao<T>{
// 持有数据库操作的引用
private SQLiteDatabase sqLiteDatabase;
// 表名
private String tableName;
// 操作数据库所对应的java类型
private Class<T> entityClass;
// 标识,用来标识是否已经做过初始化
private boolean isInit = false;
// 定义一个缓存空间(key 字段名 value 成员变量)
private HashMap<String,Field> cacheMap;
public boolean init(SQLiteDatabase sqLiteDatabase,Class<T> entityClass){
this.sqLiteDatabase = sqLiteDatabase;
this.entityClass = entityClass;
if(!isInit){
// 根据传入的Class进行数据表的创建 本例子中对应的是User对象;
DbTable dt = entityClass.getAnnotation(DbTable.class);
if(dt != null && !"".equals(dt.value())){
tableName = dt.value();
}else{
tableName = entityClass.getName();
}
if(!sqLiteDatabase.isOpen()){
return false;
}
String createTableSql = getCreateTableSql();
sqLiteDatabase.execSQL(createTableSql);
cacheMap = new HashMap<>();
initCacheMap();
isInit = true;
}
return isInit;
}
private void initCacheMap() {
// 取得所有的列名
String sql = "select * from "+tableName+" limit 1,0";
Cursor cursor = sqLiteDatabase.rawQuery(sql,null);
String[] columnNames = cursor.getColumnNames();
// 获取所有的成员变量
Field[] colunmnFields = entityClass.getDeclaredFields();
// 将字段访问权限打开
for(Field field : colunmnFields){
field.setAccessible(true);
}
for(String columnName:columnNames){
Field columnField=null;
for(Field field:colunmnFields){
String fieldName=null;
if(field.getAnnotation(DbField.class)!=null){
fieldName=field.getAnnotation(DbField.class).value();
}else{
fieldName=field.getName();
}
if(columnName.equals(fieldName)){
columnField=field;
break;
}
}
if(columnField!=null){
cacheMap.put(columnName,columnField);
}
}
}
private String getCreateTableSql() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("create table if not exists ");
stringBuilder.append(tableName+"(");
// 反射得到所有的成员变量
Field[] fields = entityClass.getDeclaredFields();
for(Field field : fields){
Class type = field.getType();
DbField dbField = field.getAnnotation(DbField.class);
if(dbField != null && !"".equals(dbField.value())){
if(type == String.class){
stringBuilder.append(dbField.value()+" TEXT,");
}else if(type== Integer.class){
stringBuilder.append(dbField.value()+" INTEGER,");
}else if(type== Long.class){
stringBuilder.append(dbField.value()+" BIGINT,");
}else if(type== Double.class){
stringBuilder.append(dbField.value()+" DOUBLE,");
}else if(type==byte[].class){
stringBuilder.append(dbField.value()+" BLOB,");
}else{
//不支持的类型号
continue;
}
}else{
if(type== String.class){
stringBuilder.append(field.getName()+" TEXT,");
}else if(type== Integer.class){
stringBuilder.append(field.getName()+" INTEGER,");
}else if(type== Long.class){
stringBuilder.append(field.getName()+" BIGINT,");
}else if(type== Double.class){
stringBuilder.append(field.getName()+" DOUBLE,");
}else if(type==byte[].class){
stringBuilder.append(field.getName()+" BLOB,");
}else{
//不支持的类型号
continue;
}
}
}
if(stringBuilder.charAt(stringBuilder.length()-1)==','){
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
stringBuilder.append(")");
return stringBuilder.toString();
}
@Override
public long insert(T entity) {
// user 对象 转换为contentvalues new User(id 1,name = "netease","password");
Map<String,String> map = getValues(entity);
ContentValues values = getContentValues(map);
return sqLiteDatabase.insert(tableName,null,values);
}
@Override
public long update(T entity, T where) {
// 将传进来的对象 将成员变量和成员变量的值 转为map
Map map = getValues(entity);
ContentValues values = getContentValues(map);
Map whereMap = getValues(where);
Condition condition = new Condition(whereMap);
return sqLiteDatabase.update( tableName,values,condition.whereCause,condition.whereArgs);
}
@Override
public int delete(T where) {
Map map = getValues(where);
Condition condition = new Condition(map);
return sqLiteDatabase.delete(tableName,condition.whereCause,condition.whereArgs);
}
@Override
public List<T> query(T where) {
return query(where,null,null,null);
}
@Override
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
Map map = getValues(where);
// select * from tableName limit 0,10;
String limitString = null;
if(startIndex != null && limit != null){
limitString = startIndex+" , "+limit;
}
// String seclections = "where 1=1 and id=? and name=?";
// String selectionArgs = String[]{,""};
// select * from tableName where id=? and name=?
Condition condition = new Condition(map);
Cursor cursor = sqLiteDatabase.query(tableName,null,condition.whereCause,condition.whereArgs,null,null,
orderBy,limitString);
// 定义一个解析游标的方法
List<T> result = getResult(cursor,where);
return result;
}
private List<T> getResult(Cursor cursor, T obj) {
ArrayList list = new ArrayList();
Object item = null;// User user = null;
while (cursor.moveToNext()){
try {
item = obj.getClass().newInstance(); // user = new User(); user.setId(cursor.getId);
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();
// cursor.getString(columnIndex);
if(columnIndex != -1){
if(type == String.class){
// User user = new User
// user.setId(1); id.set(user,1);
field.set(item,cursor.getString(columnIndex));
}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 (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
cursor.close();
return list;
}
private class Condition{
private String whereCause;
private String[] whereArgs;
public Condition(Map<String,String> whereMap){
ArrayList list = new ArrayList();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("1=1");
// 获取所有的字段名
Set keys = whereMap.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
String key = (String)iterator.next();
String value = whereMap.get(key);
if(value != null){
stringBuilder.append(" and "+key+" =?");
list.add(value);
}
}
this.whereCause = stringBuilder.toString();
this.whereArgs = (String[])list.toArray(new String[list.size()]);
}
}
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> map = new HashMap<>();
// 得到所有的成员变量,user的成员变量
Iterator<Field> fieldIterator = cacheMap.values().iterator();
while (fieldIterator.hasNext()){
Field field = fieldIterator.next();
field.setAccessible(true);
// 获取成员变量的值
try {
Object object = field.get(entity);
if(object == null){
continue;
}
String value = object.toString();
// 获取列名
String key = null;
DbField dbField = field.getAnnotation(DbField.class);
if( dbField != null && !"".equals(dbField.value())){
key = dbField.value();
}else{
key = field.getName();
}
if(!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)){
map.put(key,value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
}
数据库分库
应用场景:大型app中,当有多个账号需要切换时。
有点:数据隔离,互不打扰。便于数据的管理。
缺点:会消耗内存,不过相比于有点,这点内存还是可以接受的
数据库的升级
原理 利用xml文件进行升级
步骤
1.先把数据备份,重命名保存在当前位置,防止数据丢失,以便升级后将数据重新加入到升级后的表中
2.重新创建数据库表
3.将重命名之后的表里的数据导入到新建的表里
4.将之前备份的表删除
5.利用xml升级必须知道之前的表结构和了解xml,所以只能针对自己的项目进行升级
网友评论