Android SQLite 注解
在 Android 中使用 ORM(对象关系映射)进行 SQLite 编码。
介绍
使用 Android,SQLite 是存储数据的数据库实现方式。 采用原始方式,我们几乎在所有情况下都必须编写 SQL 查询字符串。 当然,这没问题。 但是,您是否考虑过编码、修复错误和维护所需要花费的精力? 我确信,用原始方式做这件事的努力并不少。
您知道 Hibernate 框架吗? 我将通过 对象关系映射(ORM)来解决问题,这与 Hibernate. 相同
分析以进行比较
假设我们必须创建一个数据库,其中包含表 User,该表包含
- id:自动递增键。
- user_name:用户的姓名。
采用原始方式,要创建 User 表,我们必须重写 SQLiteOpenHelper ,并且实现以下代码
public class MySQLiteHelper extends SQLiteOpenHelper {
public static final String TABLE_USER = "user_tbl";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_USER_NAME = "user_name";
private static final String DATABASE_NAME = "my_db.db";
private static final int DATABASE_VERSION = 1;
// Database creation sql statement
private static final String DATABASE_CREATE = "create table " + TABLE_USER
+ "(" + COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_USER_NAME + " text not null);";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
并且如果您想插入一个 User,我们必须编写与下面相同的代码
public long insertUser(User user) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_USER_NAME, user.userName);
long insertId = database
.insert(MySQLiteHelper.TABLE_USER, null, values);
return insertId;
}
通过上述方式,我们可以编写代码来满足任何需求:获取、删除、更新...一切似乎都很好!
但是,如果您必须创建 20 个表,每个表有 20 个字段,会发生什么情况?
- 要创建 20 个表,您必须编写 20 个 sql。
- 要将一个 Model 对象插入到数据库中,您必须编写大量代码来插入、获取等等....
- 代码结构将难以控制。
根据我的经验,所有开发人员在控制这样的代码时总是遇到很多困难! 并且在完成一个项目的所有工作之后,它将一无所有。 如果有新的需求,我们必须重新编写所有代码。
如何用与 Hibernate 相同的方式解决这个问题? 请参考 此链接。 我认为这是 Java 注解的一个很好的教程。
SQLITE ORM
* ORM:对象关系映射
基于 Java 注解的知识,定义 Table 和 Column ,如下所示
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
/** Table name. */
String name();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
boolean isPrimaryKey() default false;
boolean isAutoincrement() default false;
/** Column type. */
String type() default "TEXT";
/** Column name. */
String name();
}
并定义一个 UserItem 如下
@Table(name = "user_tbl") public class UserItem { @Column(name = "userName", type = "TEXT") public String userName; public UserItem() { } }
上面的类是映射文件,这意味着
- UserItem 映射到 user_table。
- 属性 userName 映射到 user_table 的 userName 列。
要获取在类定义中具有注解 Table 的 Model 的表名,我们有函数
public static String getTableName(Class<?> tClass) {
Table annotationTable = tClass.getAnnotation(Table.class);
String table = tClass.getSimpleName();
if (annotationTable != null) {
if (!annotationTable.name().equals("")) {
table = annotationTable.name();
}
}
return table;
}
并且要获取在其属性中具有注解 Column 的 Model 的列列表,我们有函数
public String[] getColumns() {
boolean isHaveAnyKey = false;
List<String> columnsList = new ArrayList<String>();
for (Field field : tClass.getDeclaredFields()) {
Column fieldEntityAnnotation = field.getAnnotation(Column.class);
if (fieldEntityAnnotation != null) {
String columnName = getColumnName(field);
if (columnName != null)
columnsList.add(columnName);
if (fieldEntityAnnotation.isPrimaryKey()) {
isHaveAnyKey = true;
}
}
}
if (!isHaveAnyKey) {
columnsList.add("_id");
}
String[] columnsArray = new String[columnsList.size()];
return columnsList.toArray(columnsArray);
}
基于上述 2 种实用方法,很容易通过 Android sqlite 制作创建表的函数
public void createTable(SQLiteDatabase db) {
if (getTableName(tClass) == null) {
return;
}
StringBuffer sql = new StringBuffer("CREATE TABLE ");
sql.append(getTableName(tClass));
sql.append(" (_id INTEGER PRIMARY KEY AUTOINCREMENT");
for (Field field : tClass.getDeclaredFields()) {
Column fieldEntityAnnotation = field.getAnnotation(Column.class);
if (fieldEntityAnnotation != null) {
sql.append(", ");
sql.append(fieldEntityAnnotation.name());
sql.append(" ");
sql.append(fieldEntityAnnotation.type());
}
}
sql.append(");");
db.execSQL(sql.toString());
}
使用上述代码有什么优势? 您的工作仅限于映射 Model 文件,除此之外,什么都没有。
如何编写插入方法?
要将 Model 的值填充到 ContentValues,我们有
private ContentValues getFilledContentValues(Object object) throws IllegalAccessException { ContentValues contentValues = new ContentValues(); for (Field field : object.getClass().getDeclaredFields()) { Column fieldEntityAnnotation = field.getAnnotation(Column.class); if (fieldEntityAnnotation != null) { if (!fieldEntityAnnotation.isAutoincrement()) { putInContentValues(contentValues, field, object); } } } return contentValues; } public void putInContentValues(ContentValues contentValues, Field field, Object object) throws IllegalAccessException { if (!field.isAccessible()) field.setAccessible(true); // for private variables Object fieldValue = field.get(object); String key = getColumnName(field); if (fieldValue instanceof Long) { contentValues.put(key, Long.valueOf(fieldValue.toString())); } else if (fieldValue instanceof String) { contentValues.put(key, fieldValue.toString()); } else if (fieldValue instanceof Integer) { contentValues.put(key, Integer.valueOf(fieldValue.toString())); } else if (fieldValue instanceof Float) { contentValues.put(key, Float.valueOf(fieldValue.toString())); } else if (fieldValue instanceof Byte) { contentValues.put(key, Byte.valueOf(fieldValue.toString())); } else if (fieldValue instanceof Short) { contentValues.put(key, Short.valueOf(fieldValue.toString())); } else if (fieldValue instanceof Boolean) { contentValues.put(key, Boolean.parseBoolean(fieldValue.toString())); } else if (fieldValue instanceof Double) { contentValues.put(key, Double.valueOf(fieldValue.toString())); } else if (fieldValue instanceof Byte[] || fieldValue instanceof byte[]) { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream( outputStream); objectOutputStream.writeObject(fieldValue); contentValues.put(key, outputStream.toByteArray()); objectOutputStream.flush(); objectOutputStream.close(); outputStream.flush(); outputStream.close(); } catch (Exception e) { } } }
并且将实现 insert 方法
public long insert(T item) {
if (item != null) {
try {
ContentValues value = getFilledContentValues(item);
long id = mDB.insert(getTableName(tClass), null, value);
return id;
} catch (IllegalAccessException e) {
}
}
return -1;
}
mDB:SQLiteDatabase 对象。 这种插入方式的优势是什么? 您只需要对基本方法进行单元测试,然后就什么都不用做了。
摘要
通过上述示例,您可以看到 Java 注解将帮助我们按照 ORM 方式编写 SQLite 代码。 关于细节,您可以参考我在文章中附带的源代码。 我在这里放了类图,以便于理解。
历史
- 2013-08-29:首次创建。