65.9K
CodeProject 正在变化。 阅读更多。
Home

Android SQLite 注解

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (8投票s)

2013 年 8 月 29 日

CPOL

2分钟阅读

viewsIcon

39962

downloadIcon

1054

在 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;
} 

mDBSQLiteDatabase 对象。 这种插入方式的优势是什么? 您只需要对基本方法进行单元测试,然后就什么都不用做了。

摘要

通过上述示例,您可以看到 Java 注解将帮助我们按照 ORM 方式编写 SQLite 代码。 关于细节,您可以参考我在文章中附带的源代码。 我在这里放了类图,以便于理解。

 

历史 

  • 2013-08-29:首次创建。
© . All rights reserved.