数据库的使用流程:
①在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)