通用数据库

数据库的使用流程:

①在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)

你可能感兴趣的:(通用数据库)