对于属性 id 表单名的注解
package com.example.dbcommon.dao.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 指定了实体的字段与数据库中表中列的对应关系
* @author Administrator
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
/**
* 数据库中的表名
* @return
*/
String value();
}
package com.example.dbcommon.dao.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标志主键
* @author Administrator
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ID {
boolean autoincrement();
}
package com.example.dbcommon.dao.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 制定了将实体与数据库表中的对应关系
* @author Administrator
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
/**
* 数据库中的表名
* @return
*/
String value();
}
实体类。可以直接在实体类注解,这样就知道了类的属性对应表的那个字段以及是否是id并且是否自增长等
package com.example.dbcommon.dao.domain;
import com.example.dbcommon.dao.DBHelper;
import com.example.dbcommon.dao.annotation.Column;
import com.example.dbcommon.dao.annotation.ID;
import com.example.dbcommon.dao.annotation.TableName;
@TableName(DBHelper.TABLE_NEWS_NAME)
public class News {
//主键:明确,值的获取
@ID(autoincrement=true)
@Column(DBHelper.TABLE_ID)
private int id;
@Column(DBHelper.TABLE_NEWS_TITLE)
private String title;
@Column(DBHelper.TABLE_NEW_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;
}
}
CRUD操作通用接口
package com.example.dbcommon.dao;
import java.io.Serializable;
import java.util.List;
/**
* 实体操作的通用接口
* @author Administrator
*
*/
public interface DAO<M> {
long insert(M m);
int delete(Serializable id); //int long String
int update(M m);
List<M> findAll();
}
继承接口实现通用操作(基于所有类)
cursor.close()不关的话,最多提供300多个游标,超过了不报错,但是游标是空的,即便查出数据了也是空的
可以获取当前运行的类
通过class可以获取支持泛型的父类,也可以获取父类泛型的类型
通过class可以获取注解的value值,也可以设置value的值
通过class可以遍历所有字段
//问题1:表名的获取
//解决方案1:获取到实体,字母小写就是表名。。不过定义实体的名称被受限制
//解决方案2:利用注解,实体名称和数据库表名称脱离关系
package com.example.dbcommon.dao;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import com.example.dbcommon.dao.annotation.Column;
import com.example.dbcommon.dao.annotation.ID;
import com.example.dbcommon.dao.annotation.TableName;
import android.R.id;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.provider.ContactsContract.Data;
import android.util.Log;
public abstract class DaoSupportt<M> implements DAO<M>{
private static final String TAG = "DaoSupportt";
protected Context context;
protected DBHelper helper;
protected SQLiteDatabase db;
public DaoSupportt(Context context) {
super();
this.context = context;
this.helper = new DBHelper(context);
this.db = helper.getWritableDatabase();
}
@Override
public long insert(M m) {
// TODO Auto-generated method stub
ContentValues values = new ContentValues();
fillColumn(m,values);//数据源第一个参数,导入的目标第二个参数
return db.insert(getTableName(), null, values);
}
@Override
public int delete(Serializable id) {
// TODO Auto-generated method stub
return db.delete(getTableName(), DBHelper.TABLE_ID + " =?", new String[]{id.toString()});
}
@Override
public int update(M m) {
// TODO Auto-generated method stub
ContentValues values = new ContentValues();
fillColumn(m,values);//数据源第一个参数,导入的目标第二个参数
return db.update(getTableName(), values, DBHelper.TABLE_ID + " =? ", new String[]{getid(m)});
}
public List<M> findByCondition(String[] columns, String selection,
String[] selectionArgs, String orderBy){
return findByCondition(columns, selection, selectionArgs, null, null, orderBy);
}
/**
* 条件查询
* @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 = getInstrance();
fillField(cursor,m);
result.add(m);
}
//不关的话,最多提供300多个游标,超过了也不报错,但是提供的游标是空的,即便查出数据了也是空的
cursor.close();
}
return result;
}
@Override
public List<M> findAll() {
// TODO Auto-generated method stub
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 = getInstrance();
fillField(cursor,m);
result.add(m);
}
//不关的话,最多提供300多个游标,超过了也不报错,但是提供的游标是空的,即便查出数据了也是空的
cursor.close();
}
return result;
}
//问题1:表名的获取
//解决方案1:获取到实体,字母小写就是表名。。不过定义实体的名称被受限制
//解决方案2:利用注解,实体名称和数据库表名称脱离关系
private String getTableName(){
//获取实体
M m = getInstrance();
//获取实体的注解,依据value里设置值确定操作的数据库表
//如果需要在运行时获取到注解的信息,设置存活的时间
TableName tableName = m.getClass().getAnnotation(TableName.class);//annotationType:注解的类型
if(tableName != null){
return tableName.value();
}
return "";
}
//问题5:实体的对象创建
private M getInstrance(){
//实体是何时确定的?
//1:是那个孩子调用的该方法-那个孩子在运行
//jdk解释:返回此Object的运行时类
Class clazz = getClass();
//class com.ithm.dbhm28.dao.impl.NewsDaoImpl
// Log.i(TAG, clazz.toString());
//2:获取该孩子的父类(支持泛型的父类)
// clazz.getSuperclass();//class com.ithm.dbhm28.dao.base.DAOSupport
Type superclass = clazz.getGenericSuperclass();//com.ithm.dbhm28.dao.base.DAOSupport<com.ithm.dbhm28.dao.domain.News>
//泛型实现接口,只要是泛型都会实现这个接口(参数化的类型),规定了泛型的通用操作
if(superclass != null && superclass instanceof ParameterizedType){
//[class com.ithm.dbhm28.dao.domain.News]
Type[] arguments = ((ParameterizedType)superclass).getActualTypeArguments();
try {
return (M) ((Class)arguments[0]).newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//3:获取到泛型中的参数
return null;
}
//问题2:如何将实体中的数据,按照对应关系导入到数据库表中
private void fillColumn(M m, ContentValues values) {
// TODO Auto-generated method stub
// values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
// //此处省略N行代码
// m.getClass().getFields();//获取类的所有public字段
Field[] fields = m.getClass().getDeclaredFields();//获取类的所有字段
for(Field item : fields){
//要有权限 要不然一定会进入IllegalArgumentException这报没有权限的异常
item.setAccessible(true);
Column column = item.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.autoincrement()){
//是主键就不要给数据,一旦给予数据,数据库就认为程序猿自己接管主键数据,数据库不再自增添加
}else{
values.put(key, value);
}
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//问题4:明确实体中主键,获取到主键中封装的值
private String getid(M m) {
// TODO Auto-generated method stub
Field[] fields = m.getClass().getDeclaredFields();
for(Field item : fields){
item.setAccessible(true);
ID id = item.getAnnotation(ID.class);
if(id != null){
try {
return item.get(m).toString();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
//问题3:将数据表中列的数据,按照对应关系导入到实体中
private void fillField(Cursor cursor, M m) {
// TODO Auto-generated method stub
// int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_TITLE);
// String title = cursor.getString(columnIndex);
// news.setTitle(title);
// 此处省略N行代码
Field[] fields = m.getClass().getDeclaredFields();
for(Field item : fields){
item.setAccessible(true);
Column column = item.getAnnotation(Column.class);
if(column != null){
int columnIndex = cursor.getColumnIndex(column.value());
String value = cursor.getString(columnIndex);
//在News里面 id是int类型 在这里是String类型,所以填充数据会出错
try {
if(item.getType() == int.class){
item.set(m, Integer.parseInt(value));
}else if(item.getType() == Data.class){
//字符串转换成时间
}else{
item.set(m, value);
}
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
接口类实现特定的类的通用CRUD接口,接口约束规范化
package com.example.dbcommon.dao;
import java.util.List;
import com.example.dbcommon.dao.domain.News;
/**
* 新闻表操作的接口
* @author Administrator
*
*/
public interface NewsDao extends DAO<News>{
// long insert(News news);
// int delete(int id);
// int update(News news);
// List<News> findAll();
}
具体类实现特定的类的CRUD接口
package com.example.dbcommon.dao.impl;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.example.dbcommon.dao.DBHelper;
import com.example.dbcommon.dao.DaoSupportt;
import com.example.dbcommon.dao.NewsDao;
import com.example.dbcommon.dao.domain.News;
public class NewsDaoImpl extends DaoSupportt<News> implements NewsDao {
public NewsDaoImpl(Context context) {
super(context);
}
/*@Override
public long insert(News news) {
// TODO Auto-generated method stub
ContentValues values = new ContentValues();
values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
//此处省略N行代码
return db.insert(DBHelper.TABLE_NEWS_NAME, null, values);
}
@Override
public int delete(int id) {
// TODO Auto-generated method stub
return db.delete(DBHelper.TABLE_NEWS_NAME, DBHelper.TABLE_ID + " =?", new String[]{id+""});
}
@Override
public int update(News news) {
// TODO Auto-generated method stub
ContentValues values = new ContentValues();
values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
//此处省略N行代码
return db.update(DBHelper.TABLE_NEWS_NAME, values, DBHelper.TABLE_ID + " =? ", new String[]{news.getId() + ""});
}
@Override
public List<News> findAll() {
// TODO Auto-generated method stub
List<News> result = null;
Cursor cursor = db.query(DBHelper.TABLE_NEWS_NAME, null, null, null, null, null, null);
if(cursor != null){
result = new ArrayList<News>();
while(cursor.moveToNext()){
News news = new News();
int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_TITLE);
String title = cursor.getString(columnIndex);
news.setTitle(title);
//此处省略N行代码
result.add(news);
}
//不关的话,最多提供300多个游标,超过了也不报错,但是提供的游标是空的,即便查出数据了也是空的
cursor.close();
}
return null;
}*/
}
进行测试
package com.example.dbcommon.test;
import java.util.List;
import com.example.dbcommon.dao.DBHelper;
import com.example.dbcommon.dao.domain.News;
import com.example.dbcommon.dao.impl.NewsDaoImpl;
import android.test.AndroidTestCase;
public class DBTest extends AndroidTestCase {
public void createTable(){
DBHelper hbHelper = new DBHelper(getContext());
hbHelper.getWritableDatabase();
}
public void testInsert(){
NewsDaoImpl impl = new NewsDaoImpl(getContext());
News m = new News();
m.setTitle("测试标题一");
m.setSummary("测试摘要一");
impl.insert(m);
}
public void testDelete(){
NewsDaoImpl impl = new NewsDaoImpl(getContext());
impl.delete(0);
}
public void testUpdate(){
NewsDaoImpl impl = new NewsDaoImpl(getContext());
News m = new News();
m.setId(2);
m.setTitle("hm28N");
m.setSummary("PP");
impl.update(m);
}
public void testFindAll(){
NewsDaoImpl impl = new NewsDaoImpl(getContext());
List<News> findAll = impl.findAll();
System.out.println();
}
}
数据库的组件。
对于数据库不同version的更新值得借鉴。
package com.example.dbcommon.dao;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.v7.internal.widget.ActivityChooserModel.HistoricalRecord;
public class DBHelper extends SQLiteOpenHelper {
private static final String NAME = "ithm.db";
private static final int START_VERSION = 1;
private static final int CURRENT_VERSION = 2;
public DBHelper(Context context) {
super(context, NAME, null, CURRENT_VERSION);
// TODO Auto-generated constructor stub
}
//新闻表: 主键 + 标题 + 摘要
public static final String TABLE_ID = "id";
public static final String TABLE_NEWS_NAME = "news";
public static final String TABLE_NEWS_TITLE = "titile";
public static final String TABLE_NEW_SUMMARY = "summary";
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("CREATE TABLE" + TABLE_NEWS_NAME + "(" + //
TABLE_ID + " integer primary key autoincrement, " +//
TABLE_NEWS_TITLE + "varchar(50), " + //
TABLE_NEW_SUMMARY + "varchar(200))"//
);
onUpgrade(db, START_VERSION, CURRENT_VERSION);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
//更新
//数据库版本管理:1.0(一张表),2.0(两张表)
//模拟两种用户
//老用户:1.0
//新用户:2.0
//老用户会更新,没有问题,新用户只有更新后的表,更新前的表就没有
//希望:不需要总是考虑老用户,每次升级数据库操作是什么
switch(oldVersion){
case START_VERSION:
case CURRENT_VERSION:
case 3:
case 4:
break;
}
}
}