转载请注明出处: http://blog.csdn.net/forwardyzk/article/details/43673577
下面我们写一个操作数据库框架的示例,从而熟悉注解,反射,泛型的使用。
通过实体类创建和删除操作,因为操作数据库中的表不同,那么对应的实体类也不同,我们需要使用泛型来指定操作的实体类。使用注解使实体类和数据库表列一一对应。
DbHelper.java
public class DbHelper extends SQLiteOpenHelper {
private static String NAME = "db.db";
private static int VERSION = 1;
public DbHelper(Context context) {
super(context, NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
Dao.java
public interface Dao<T> {
/**
* 插入数据
*
* @param t
*/
public long insert(T t);
/**
* 通过id删除数据
*
* @param id
*/
public int delete(Serializable id);
/**
* 通过id更新数据
*
* @param t
*/
public int update(T t);
/**
* 查询数据中所有数据
*
* @return
*/
public List<T> finadAll();
/**
* 创建表
*/
public void createTable();
}
在其实现类DaoImp.java
其部分代码
public abstract class DaoImp<T> implements Dao<T> {
private SQLiteDatabase db;
public DaoImp(DbHelper helper) {
super();
db = helper.getWritableDatabase();
}
@Override
public long insert(T t) {
if (!isTableExists()) {
return -1;
}
ContentValues values = new ContentValues();
fillContentValues(t, values);// 第一个参数标示数据的来源;第二个参数设置给谁
// 此处省略n行代码
return db.insert(getTableName(), null, values);
}
@Override
public int delete(Serializable id) {
if (!isTableExists()) {
return -1;
}
return db.delete(getTableName(), getIdName(null) + "=?",
new String[] { id.toString() });
}
@Override
public int update(T t) {
if (!isTableExists()) {
return -1;
}
ContentValues values = new ContentValues();
fillContentValues(t, values);
return db.update(getTableName(), values, getIdName(t) + "=?",
new String[] { getId(t) });
}
public boolean isTableExists() {
String sql = "select count(*) as c from sqlite_master where type ='table' and name ='"
+ getTableName() + "';";
Cursor cursor = db.rawQuery(sql, null);
if (cursor.moveToNext()) {
int count = cursor.getInt(0);
if (count > 0) {
return true;
}
}
return false;
}
这是查询数据库中所有的表,看指定的表是否存在。
通过bean创建表,使用的注解使实体类和表中列一一对应起来。
指定表名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableId {
String value();
boolean isAutoIncrease();
}
指定列注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
下面以一个Book.java实体类为示例,看看这些自定义注解的使用。
@TableName("book")
public class Book {
@TableId(value = "serialnum", isAutoIncrease = true)
@Column("serialnum")
private int serialNum;
@Column("name")
private String name;
@Column("des")
private String des;
public Book() {
super();
}
public Book(String name, String des) {
super();
this.name = name;
this.des = des;
}
public Book(int serialNum, String name, String des) {
super();
this.serialNum = serialNum;
this.name = name;
this.des = des;
}
public int getSerialNum() {
return serialNum;
}
public void setSerialNum(int serialNum) {
this.serialNum = serialNum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
@Override
public String toString() {
return "Book [serialNum=" + serialNum + ", name=" + name + ", des="
+ des + "]";
}
}
在DaoImpl.java中创建表
@Override
public void createTable() {
String tableName = getTableName();
if (TextUtils.isEmpty(tableName)) {
return;
}
if (isTableExists()) {
return;
}
Map<String, String> columns = columnNames();
StringBuffer sb = new StringBuffer();
sb.append("CREATE TABLE ");
sb.append(tableName);
sb.append("(");
boolean flag = true;
if (columns != null) {
Set<String> keySet = columns.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
String type = columns.get(name);
if (flag) {
sb.append(name).append(" ").append(type);
flag = false;
} else {
sb.append(",").append(name).append(" ").append(type);
}
}
}
sb.append(")");
db.execSQL(sb.toString());
}
获取列明和列类型map。
public Map<String, String> columnNames() {
Map<String, String> columns = null;
T t = getInstance();
Field[] fields = t.getClass().getDeclaredFields();
columns = new HashMap<String, String>();
for (Field field : fields) {
field.setAccessible(true);
Column columAn = field.getAnnotation(Column.class);
if (columAn != null) {
TableId idAn = field.getAnnotation(TableId.class);
if (idAn != null) {
if (idAn.isAutoIncrease()) {
columns.put(idAn.value(),
"integer primary key autoincrement");
} else {
columns.put(idAn.value(), "varchar(50)");
}
} else {
columns.put(columAn.value(), "varchar(50)");
}
}
}
return columns;
}
通过获取实体类中注解的类型,通过field.getAnnotation(Column.class);获取字段上的注解,如果是Column是列,获取其值,就是列名称。如果是TableId表示表的主键,其值就是是否自定增长。
获取表名
/**
* 获取表名字
*
* @return
*/
public String getTableName() {
T t = getInstance();
TableName tableName = t.getClass().getAnnotation(TableName.class);
if (tableName != null) {
return tableName.value();
}
return null;
}
获取TableName注解。来获取表名。
创建实体类对象。
/**
* 创建实体的对象
*
* @return
*/
public T getInstance() {
Class clazz = getClass();// 正在运行
Type genericSuperclass = clazz.getGenericSuperclass();// 获取支持泛型的父类
if (genericSuperclass instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass)
.getActualTypeArguments();
Class target = (Class) actualTypeArguments[0];
try {
return (T) target.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
在插入数据时候,通过传入bean对象,然后向对应的列中插入数据。
private void fillContentValues(T m, ContentValues values) {
Field[] fields = m.getClass().getDeclaredFields();
for (Field item : fields) {
item.setAccessible(true);// 施暴
Column column = item.getAnnotation(Column.class);
if (column != null) {
TableId id = item.getAnnotation(TableId.class);
if (id != null) {
// 主键+自增
if (id.isAutoIncrease()) {
// 不需要设置数据到ContentValues
} else {
String key = column.value();
try {
String value = item.get(m).toString();// m.field
values.put(key, value);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
} else {
String key = column.value();
try {
String value = item.get(m).toString();// m.field
values.put(key, value);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
获取bean的字段数组,先进行暴力反射,反正字段是私有字段,先判断字段上是否有Column注解,如果有,表示此字段是和列对应,其注解对应的值就是表示的列名,然后通过field.get(m)获取对象其字段对应的值,然后条件到ContentValues对象中,values.put(key, value);key是注解 Column对应的值,value是通过反射获取对象的值。
在这里还判断是主键注解TableId,如果是自增,那么不需要添加到values中,否则就向正常的列一样,也要添加到values中。
private void fillFields(Cursor cursor, T m) {
Field[] fields = m.getClass().getDeclaredFields();
for (Field item : fields) {
item.setAccessible(true);
Column column = item.getAnnotation(Column.class);
if (column != null) {
String columnName = column.value();
int columnIndex = cursor.getColumnIndex(columnName);
String value = cursor.getString(columnIndex);
// 主键+自增:int long
if (item.getType() == int.class) {
try {
item.set(m, Integer.parseInt(value));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else if (item.getType() == long.class) {
try {
item.set(m, Long.parseLong(value));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
try {
item.set(m, value);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
将游标中的查询出来的值,封装到bean中,即实体类中。
获取bean中的Column注解,然后获取其值(类名),通过columnvalue=cursor.getColumnIndex(columnName)获取此列在游标中的值,然后通过field.set(bean,columnvalue),设置给传进来的bean对象字段值。
private String getId(T t) {
if (t == null) {
t = getInstance();
}
Field[] fields = t.getClass().getDeclaredFields();
for (Field item : fields) {
item.setAccessible(true);
TableId id = item.getAnnotation(TableId.class);
if (id != null) {
try {
return item.get(t).toString();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return null;
}
获取主键的对应的列名,如果前面看通了,那么这个方法自然也就明白。其实就是再次判断bean类字段上的注解。如果是TableId,那么就获取其值,就是表中列对应的列名。
下面介绍一个使用步骤:
BookImpl.java
public class BookImpl extends DaoImp<Book> {
public BookImpl(DbHelper helper) {
super(helper);
}
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/create_table"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="创建表" />
<Button
android:id="@+id/find_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="查询" />
<Button
android:id="@+id/insert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="插入" />
<Button
android:id="@+id/update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="修改" />
<Button
android:id="@+id/delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="删除" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
MainActivity.java
public class MainActivity extends Activity implements OnClickListener {
private DaoImp<Book> bookDao;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
DbHelper helper = new DbHelper(MainActivity.this);
bookDao = new BookImpl(helper);
}
private void initView() {
Button create_table = (Button) findViewById(R.id.create_table);
create_table.setOnClickListener(this);
Button delete = (Button) findViewById(R.id.delete);
delete.setOnClickListener(this);
Button update = (Button) findViewById(R.id.update);
update.setOnClickListener(this);
Button insert = (Button) findViewById(R.id.insert);
insert.setOnClickListener(this);
Button find_all = (Button) findViewById(R.id.find_all);
tv = (TextView) findViewById(R.id.tv);
find_all.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.create_table:
createTable();
break;
case R.id.find_all:
findAll();
break;
case R.id.insert:
insert();
break;
case R.id.delete:
delete();
break;
case R.id.update:
update();
break;
default:
break;
}
}
private void update() {
bookDao.update(new Book(1, "updateName", "updateDes"));
}
private void delete() {
bookDao.delete(2);
}
private void insert() {
Book b1 = new Book("第一本", "第一本描述");
Book b2 = new Book("第二本", "第二本描述");
Book b3 = new Book("第三本", "第三本描述");
bookDao.insert(b1);
bookDao.insert(b2);
bookDao.insert(b3);
}
private void findAll() {
List<Book> finadAll = bookDao.finadAll();
StringBuffer sb = new StringBuffer();
tv.setText("");
if (finadAll != null) {
for (Book book : finadAll) {
sb.append(book.toString()).append(" \n ");
}
tv.setText(sb.toString());
}
}
private void createTable() {
bookDao.createTable();
}
}
DaoImp<Book> bookDao = new BookImpl(helper);获取book表操作类
bookDao.createTable();创建表
bookDao.insert(b3);插入一条记录
bookDao.delete(2);删除id为2的记录
bookDao.update(new Book(1, "updateName", "updateDes"));更新id为1的记录。
源码下载: http://download.csdn.net/detail/forwardyzk/8437161
效果图: