数据库的使用流程:
①在dao包中建立数据库的帮助类,在帮助类中建表
public class DBHelper extends SQLiteOpenHelper {
private static final String NAME = "uc.db";
private static final int START_VERSION = 1;
private static final int HISTORY_VERSION = 2;
private static final int CURRENT_VERSION = 3;
// 新闻表:主键 + 标题 + 摘要
public static final String TABLE_ID = "_id";
public static final String TABLE_NEWS_NAME = "news";// 新闻表名
public static final String TABLE_NEWS_TITLE = "title";// 新闻标题
public static final String TABLES_NEWS_SUMMARY = "summary";// 新闻摘要
public DBHelper(Context context) {
super(context, NAME, null, CURRENT_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 建表
String sql = "create table" + TABLE_NEWS_NAME + "(" + TABLE_ID
+ "integer primary key autoincrement," + TABLE_NEWS_TITLE
+ "varchar(50)," + TABLES_NEWS_SUMMARY + "varchar(200))";
db.execSQL(sql);
onUpgrade(db, START_VERSION, CURRENT_VERSION);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 更新
// 数据库版本管理
switch (oldVersion) {
case START_VERSION:// 从1.0版本升级到3.0版本
String sql = "create table" + "book" + "(" + TABLE_ID
+ "integer primary key autoincrement," + TABLE_NEWS_TITLE
+ "varchar(50)," + TABLES_NEWS_SUMMARY + "varchar(200))";
db.execSQL(sql);
case HISTORY_VERSION:// 从2.0版本升级到3.0版本
sql = "create table" + "class" + "(" + TABLE_ID
+ "integer primary key autoincrement," + TABLE_NEWS_TITLE
+ "varchar(50)," + TABLES_NEWS_SUMMARY + "varchar(200))";
db.execSQL(sql);
case CURRENT_VERSION:
// 更新表、删除表等
case 4:
// 更新表、删除表等
case 5:
// 更新表、删除表等
break;
}
}
}
②在domain包中建数据库保存信息的业务bean(实体)
改进:在②的基础上,在dao.annotation包中增加注解,以便在程序运行时获得表名,主键和字段。
/**
* 指定了实体与数据库中表的对应关系
* @author HP1
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
/**
* 数据库中表名
* @return
*/
String value();
}
/**
* 标识主键
* @author HP1
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ID {
/**
* 主键是否自增
* @return
*/
boolean autoincrment();
}
/**
* 指定了实体的字段与数据库中表中列的对应关系
* @author HP1
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
/**
* 数据库中列名
* @return
*/
String value();
}
/**
* 新闻的业务bean
* @author HP1
*
*/
@TableName(DBHelper.TABLE_NEWS_NAME)
public class News {
@ID(autoincrment=true)
@Column(DBHelper.TABLE_ID)
private int id;
@Column(DBHelper.TABLE_NEWS_TITLE)
private String title;
@Column(DBHelper.TABLES_NEWS_SUMMARY)
private String summary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
}
③在dao包中建操作表中信息的接口,在接口中添加需要实现的方法。
改进:把增删改查方法提取到dao.base包中的DAO接口中,③所建的接口继承自DAO接口。
/**
* 实体操作的通用接口
* @author HP1
*
*/
public interface DAO<M> {
/**
* 增加
* @param m
* @return
*/
long insert(M m);
/**
* 删除
* @param id
* @return
*/
int delete(Serializable id);// int long String
/**
* 修改
* @param m
* @return
*/
int update(M m);
/**
* 查询
* @return
*/
List<M> findAll();
}
/**
* 新闻表操作的接口
* @author HP1
*
*/
public interface NewsDao extends DAO<News> {
/*long insert(News news);
int delete(int id);
int update(News news);
List<News> findAll();*/
// 特有信息
}
④在dao.impl包中建表中信息的实现类,该类实现了③中所建的接口。
改进:把公共部分提取到dao.base包中的DAOSupport类中,该类实现了DAO接口,④中的类继承DAOSupport类。
public abstract class DAOSupport<M> implements DAO<M>{
protected Context context;
protected DBHelper helper;
protected SQLiteDatabase db;
public DAOSupport(Context context) {
super();
this.context = context;
helper = new DBHelper(context);
db = helper.getWritableDatabase();
}
@Override
public long insert(M m) {
ContentValues values = new ContentValues();
fillColumn(m,values);
return db.insert(getTableName(), null, values );
}
@Override
public int delete(Serializable id) {
return db.delete(getTableName(), DBHelper.TABLE_ID+" = ?", new String[]{id.toString()});
}
@Override
public int update(M m) {
ContentValues values = new ContentValues();
fillColumn(m, values);
return db.update(getTableName(), values, DBHelper.TABLE_ID+" = ?", new String[]{getID(m)});
}
/**
* 条件查询
* @param columns
* @param selection
* @param selectionArgs
* @param groupBy
* @param having
* @param orderBy
* @return
*/
public List<M> findByCondition( String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy){
List<M> result = null;
Cursor cursor = db.query(getTableName(), columns, selection, selectionArgs, groupBy, having, orderBy);
if(cursor != null){
result = new ArrayList<M>();
while(cursor.moveToNext()){
M m = getInstance();
fillField(cursor,m);
result.add(m);
}
cursor.close();
}
return result;
}
@Override
public List<M> findAll() {
List<M> result = null;
Cursor cursor = db.query(getTableName(), null, null, null, null, null, null);
if(cursor != null){
result = new ArrayList<M>();
while(cursor.moveToNext()){
M m = getInstance();
fillField(cursor,m);
result.add(m);
}
cursor.close();
}
return result;
}
/**
* 问题一:表名的获取
* @return
*/
private String getTableName(){
// 每个表对应一个具体实体
// 方案一:如果能够获取到实体,获取到实体的简单名称,首字母小写
// 方案二:利用注解,实体和数据库表的名称脱离关系
// 获取到实体--问题五
M m = getInstance();
// 获取实体的注解,依据value里设置的值确定操作的数据库表
// 如果需要在运行的时候获取到注解的信息,需要在注解的头上设置存活时间
TableName tableName = m.getClass().getAnnotation(TableName.class);// annotationType:注解的类型
if(tableName != null){
return tableName.value();
}
return "";
}
/**
* 问题二:如何将实体中的数据,按照对应关系导入到数据库表中
* @param m 数据源
* @param values 导入目标
*/
private void fillColumn(M m, ContentValues values) {
/*
values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
// 此处省略n行代码。。。
*
*/
Field[] fields = m.getClass().getDeclaredFields();
for(Field item:fields){
item.setAccessible(true);
Column column = item.getClass().getAnnotation(Column.class);
if(column != null){
String key = column.value();
try {
String value = item.get(m).toString();
// 如果该field是主键,并且是自增的,不能添加到集合
ID id = item.getAnnotation(ID.class);
if(id != null && id.autoincrment()){
// 不添加
}else{
values.put(key, value);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
* 问题三:如何将数据库表中列的数据,按照对应关系导入到实体中
* @param cursor 数据源
* @param m 导入目标
*/
private void fillField(Cursor cursor, M m) {
/*
* int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_TITLE);
String title = cursor.getString(columnIndex);
m.setTitle(title);
// 此处省略n行代码
*
*/
Field[] fields = m.getClass().getDeclaredFields();
for(Field item:fields){
item.setAccessible(true);
Column column = item.getClass().getAnnotation(Column.class);
if(column != null){
int columnIndex = cursor.getColumnIndex(column.value());
String value = cursor.getString(columnIndex);
try {
if(item.getType() == int.class){
item.set(m, Integer.parseInt(value));
}else if(item.getType() == Data.class){
// 把字符串转成时间再设置
}else{
item.set(m, value);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
* 问题四:明确实体中主键,获取到主键中封装的值
* @param m
* @return
*/
private String getID(M m) {
Field[] fields = m.getClass().getDeclaredFields();
for(Field item:fields){
item.setAccessible(true);
ID id = item.getClass().getAnnotation(ID.class);
if(id != null){
try {
return item.get(m).toString();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* 问题五:实体的对象创建
* @return
*/
private M getInstance(){
// 实体是何时确定的
// NewsDaoImpl extends DAOSupport<News>
// ①哪个孩子调用的该方法----哪个孩子在运行
// 返回此 Object 的运行时类
Class clazz = getClass();
// ②获取该孩子的父类(支持泛型的父类)
Type superclass = clazz.getGenericSuperclass();
// ③获取到泛型中的参数
// 所有的泛型都实现了(参数化的类型)接口,规定了泛型的通用操作
if(superclass != null && superclass instanceof ParameterizedType){
Type[] arguments = ((ParameterizedType)superclass).getActualTypeArguments();
try {
return (M) ((Class)arguments[0]).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
}
}
public class NewsDaoImpl extends DAOSupport<News> implements NewsDao {
public NewsDaoImpl(Context context) {
super(context);
}
}
通用数据库需要解决的问题:
问题一:表名的获取(insert,delete,update,find)
方案一:如果能够获取到实体,获取到实体的简单名称,首字母小写
方案二:利用注解,实体和数据库表的名称脱离关系。
问题二:如何将实体中的数据,按照对应关系导入到数据库表中(insert,update)
问题三:如何将数据库表中列的数据,按照对应关系导入到实体中(find)
问题四:明确实体中主键,获取到主键中封装的值(update)
问题五:实体的对象创建(find)