Android ORM資料庫之OrmLite使用架構及源碼分析__資料庫

來源:互聯網
上載者:User

一、簡介

 OrmLite是一個資料庫架構,這個可以讓我們快速實現資料庫操作,避免頻繁手寫sql,提高我們的開發效率,減少出錯的機率。
 首先可以去它的官網看看www.ormlite.com,它的英文全稱是Object Relational Mapping,意思是對象關係映射;如果接觸過Java EE開發的,一定知道Java Web開發就有一個類似的資料庫映射架構——Hibernate。簡單來說,就是我們定義一個實體類,利用這個架構,它可以幫我們吧這個實體映射到我們的資料庫中,在Android中是SQLite,資料中的欄位就是我們定義實體的成員變數。
優點
1)輕量級
2)使用簡單,易上手
3)封裝完善
4)文檔全面
缺點
1)基於反射,效率較低
2)缺少中文翻譯文檔

二、運用

1. 整合
 首先到Ormlite官網下載Android的架包:http://ormlite.com/releases/。

 可以看到,目前最新版本為4.49,對於Android的架包為: ormlite-android-4.48.jar 和 ormlite-core-4.48.jar。把兩個jar拷貝到libs下,一般Gradle裡面都會包含編譯libs:

compile fileTree(dir: 'libs', include: ['*.jar'])


 編譯後就可以看到以上架包結構。

2.配置Bean類

 資料庫肯定離不開Bean類,先來看看我建的一個User類:

@DatabaseTable(tableName = "user")public class User {    @DatabaseField(generatedId = true)    private int id;    @DatabaseField(columnName = "name")    private String name;    @DatabaseField(canBeNull = false, columnName = "desc")    private String desc;    /*關鍵啊,一定要加, 為每個class添加一個無參的構造器,並且構造器在包內是可見的*/    public User() {}    public User(String name, String desc) {        this.name = name;        this.desc = desc;    }    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getDesc() {        return desc;    }    public void setDesc(String desc) {        this.desc = desc;    }}

 首先在User類上添加@DatabaseTable(tableName = “user”),標明這是資料庫中的一張表,標明為user;然後分別在屬性上添加@DatabaseField(columnName = “name”) ,columnName的值為該欄位在資料中的列名@DatabaseField(generatedId = true) ,generatedId 表示id為主鍵且自動產生,canBeNull表示該屬性的內容不可為空,下面來介紹更多常用的屬性設定:

成員名 資料類型 描述
generatedId Boolean 欄位是否自動增加。預設為false。類中的一個成員變數設定了這個值,它告訴資料庫每添加一條新記錄都自動增加id。當一個有generatedid的對象被建立時使用Dao.create()方法,資料庫將為記錄產生一個id,它會被返回並且被create方法設定進對象。
columnName String 資料庫的列名。如果你沒有設定這個成員名,會用標準的形式代替它。
canBeNull Boolean 欄位是否能被分配null值。預設是true。如果你設定成false,那麼你每次在資料庫中插入資料是都必須為這個欄位提供值。
dataType 欄位的資料類型。通常情況下,資料類型是從java類的成員變數擷取的,並不需要進行特殊指出。它相當於是SQL的資料類型。
defaultValue String 當我們在表中建立新的記錄時的一個欄位的預設值。預設情況下是沒有這個值的。
width Integer 欄位的寬度,主要用於字串欄位。預設是0,意味著採用預設的資料類型和具體的資料庫的預設情況。
id Boolean 這個欄位是否是id,預設是false。在一個class中只有一個成變數可以有這個值。
columnName String 資料庫的列名。如果你沒有設定這個成員名,會用標準的形式代替它。

3.封裝Dao類
 在普通Sqlite使用過程中,我們都會自己寫個Helper類來繼承SQLiteOpenHelper,然后里面封裝一些方法來操作資料庫,OrmLite也一樣,但我們需要繼承的是OrmLiteSqliteOpenHelper:

public class DatabaseHelper extends OrmLiteSqliteOpenHelper {    private static final String TABLE_NAME = "demo.db";    private static Context mApplicationContext;    private static DatabaseHelper instance;    private Map<String, Dao> daoMaps = new HashMap<>();    private DatabaseHelper(Context context) {        super(context, TABLE_NAME, null, 2);    }    @Override    public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {        try {            TableUtils.createTable(connectionSource, User.class);            TableUtils.createTable(connectionSource, Article.class);        } catch (SQLException e) {            e.printStackTrace();        }    }    @Override    public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) {        try {            TableUtils.dropTable(connectionSource, User.class, true);            TableUtils.dropTable(connectionSource, Article.class, true);            onCreate(database, connectionSource);        } catch (SQLException e) {            e.printStackTrace();        }    }    public static void initOrmLite(Context context) {        mApplicationContext = context;        getInstance();    }    /**     * 單例擷取該Helper     */    public static DatabaseHelper getInstance() {        if (instance == null) {            synInit(mApplicationContext);        }        return instance;    }    private synchronized static void synInit(Context context) {        if (instance == null) {            instance = new DatabaseHelper(context);        }    }    /**     * 獲得Dao     *     * @return     * @throws SQLException     */    public synchronized Dao getDao(Class clazz) throws SQLException {        Dao dao;        String className = clazz.getSimpleName();        if (daoMaps.containsKey(className)) {            dao = daoMaps.get(className);        } else {            dao = super.getDao(clazz);            daoMaps.put(className, dao);        }        return dao;    }    /**     * 釋放資源     */    @Override    public void close() {        super.close();        for (String key : daoMaps.keySet()) {            Dao dao = daoMaps.get(key);            dao = null;        }    }}

 這裡需要實現兩個方法:
建立表,我們直接使用ormlite提供的TableUtils.createTable(connectionSource, User.class);進行建立。

onCreate(SQLiteDatabase database,ConnectionSource connectionSource)

更新表:使用ormlite提供的TableUtils.dropTable(connectionSource, User.class, true);進行刪除操作~

onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion)

 我這裡封裝的是單例,所以一般最好在Application的OnCreate方法初始化:

DatabaseHelper.initOrmLite(this);

 我看了很多人的寫法,都喜歡在Helper裡面寫各種方法,擷取各種Dao來操作資料庫,要是應用資料複雜些,就會有很多很多的方法,複雜不容易管理,就像一個Activity寫了幾千行代碼,讓我是沒興趣去往下看方法,找方法也麻煩。所以我再這個Helper裡面唯寫了個簡單的getDao方法,通過傳入是Bean類找到它對應的Dao類,然後只操作對應的Dao類,這樣就做出了分離;而且可以發現我使用了一個Map將Dao存起來,做了個緩衝。下面來看看我封裝的Dao類代碼:

public class UserDao {    public static UserDao mUserDaoInstance;    private Dao<User, Integer> mUserDao;    public UserDao() {        try {            mUserDao = DatabaseHelper.getInstance().getDao(User.class);        } catch (SQLException e) {            e.printStackTrace();        }    }    public static UserDao getInstance() {        if (mUserDaoInstance == null) {            mUserDaoInstance = new UserDao();        }        return mUserDaoInstance;    }    /**     * 單條插入資料     */    public void insertUser(User user) {        try {            mUserDao.create(user);        } catch (SQLException e) {            e.printStackTrace();        }    }    /**     * 多條插入資料     */    public void insertUsers(List<User> users) {        try {            mUserDao.create(users);        } catch (SQLException e) {            e.printStackTrace();        }    }    /**     * 查詢所有資料     */    public List<User> queryAllUser() {        List<User> users = new ArrayList<>();        try {            users = mUserDao.queryForAll();        } catch (SQLException e) {            e.printStackTrace();        }        return users;    }    /**     * 通過id查詢資料     */    public User queryUserById(int id) {        User user = null;        try {            user = mUserDao.queryForId(id);        } catch (SQLException e) {            e.printStackTrace();        }        return user;    }    /**     * 刪除該id的資料     */    public void deleteUserById(int id) {        try {            mUserDao.deleteById(id);        } catch (SQLException e) {            e.printStackTrace();        }    }    /**     * 刪除這些id的資料     */    public void deleteUserByIds(List<Integer> ids) {        try {            mUserDao.deleteIds(ids);        } catch (SQLException e) {            e.printStackTrace();        }    }    /**     * 刪除所有     */    public void deleteAllUser() {        try {            mUserDao.deleteBuilder().delete();        } catch (SQLException e) {            e.printStackTrace();        }    }    /**     * 更新當前實體類資料     */    public void updateUser(User user) {        try {            mUserDao.update(user);        } catch (SQLException e) {            e.printStackTrace();        }    }    /**     * 更新當前資料的id     */    public void updateUserById(User user, int id) {        try {            mUserDao.updateId(user, id);        } catch (SQLException e) {            e.printStackTrace();        }    }    /**     * 自訂查詢     */    public List<User> queryBy() throws SQLException {        QueryBuilder<User, Integer> queryBuilder = mUserDao                .queryBuilder();        Where<User, Integer> where = queryBuilder.where();        where.eq("id", 1);        where.and();        where.eq("name", "xxx");        //或者        mUserDao.queryBuilder().                where().                eq("id", 1).and().                eq("name", "xxx");        return queryBuilder.query();    }}

 我在這裡也寫成了單例模式,因為考慮到不止一個地方使用UserDao,使用代碼如下:

User user = new User("jianglei", "金陵小霸王");UserDao.getInstance().insertUser(user);

 其他的資料庫操作同理,這樣在維護一個資料庫表的一些操作就比較簡單了,在一個類裡面維護,想找哪個資料庫表的操作,就去對應類的Dao裡面去找就行了。

 看完OrmLite,大家可以再看看更簡單,效率更高的GreenDao架構,我也整理好了,大家可以對比下:http://blog.csdn.net/qq_19711823/article/details/51837032

 下面就是要講比較枯燥的源碼分析,沒有興趣的童鞋可以止步了。

三、源碼分析

1.建立表
 我們先來看看建立表的方法:

@Overridepublic void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {     try {         TableUtils.createTable(connectionSource, User.class);     } catch (SQLException e) {         e.printStackTrace();     }}

 大家可以跟蹤源碼,可以發現,最主要的是doCreateTable中的兩句話:

addCreateTableStatements(databaseType, tableInfo, statements, queriesAfter, ifNotExists);int stmtC = doStatements(connection, "create", statements, false,           databaseType.isCreateTableReturnsNegative(), databaseType.isCreateTableReturnsZero());

 先來看看addCreateTableStatements實現:

private static <T, ID> void addCreateTableStatements(DatabaseType databaseType, TableInfo<T, ID> tableInfo, List<String> statements, List<String> queriesAfter, boolean ifNotExists) throws SQLException {        StringBuilder sb = new StringBuilder(256);        sb.append("CREATE TABLE ");        if(ifNotExists && databaseType.isCreateIfNotExistsSupported()) {            sb.append("IF NOT EXISTS ");        }        databaseType.appendEscapedEntityName(sb, tableInfo.getTableName()); // 添加表名        sb.append(" (");        ArrayList additionalArgs = new ArrayList();        ArrayList statementsBefore = new ArrayList();        ArrayList statementsAfter = new ArrayList();        boolean first = true;        FieldType[] i$ = tableInfo.getFieldTypes(); // 擷取表的屬性        int arg = i$.length;        for(int i$1 = 0; i$1 < arg; ++i$1) {            FieldType fieldType = i$[i$1];            if(!fieldType.isForeignCollection()) {                if(first) {                    first = false;                } else {                    sb.append(", "); // 第一個參數之前不需要“,”                }                String columnDefinition = fieldType.getColumnDefinition();                if(columnDefinition == null) {                    databaseType.appendColumnArg(tableInfo.getTableName(), sb, fieldType, additionalArgs, statementsBefore, statementsAfter, queriesAfter);                } else {                    databaseType.appendEscapedEntityName(sb, fieldType.getColumnName());                    sb.append(' ').append(columnDefinition).append(' '); // 添加列名                }            }        }        databaseType.addPrimaryKeySql(tableInfo.getFieldTypes(), additionalArgs, statementsBefore, statementsAfter, queriesAfter);        databaseType.addUniqueComboSql(tableInfo.getFieldTypes(), additionalArgs, statementsBefore, statementsAfter, queriesAfter);        Iterator var16 = additionalArgs.iterator();        while(var16.hasNext()) {            String var15 = (String)var16.next();            sb.append(", ").append(var15);        }        sb.append(") "); // 拼裝結束        databaseType.appendCreateTableSuffix(sb);        statements.addAll(statementsBefore);        statements.add(sb.toString());        statements.addAll(statementsAfter);        addCreateIndexStatements(databaseType, tableInfo, statements, ifNotExists, false);        addCreateIndexStatements(databaseType, tableInfo, statements, ifNotExists, true);    }

 到這裡,大家終於看到了一些熟悉的字眼,那些就是資料庫最初的sql語句,這個函數主要是負責去產生我們要執行的sql語句。然後就是doStatements執行sql語句:

private static int doStatements(DatabaseConnection connection, String label, Collection<String> statements, boolean ignoreErrors, boolean returnsNegative, boolean expectingZero) throws SQLException {    int stmtC = 0;    for(Iterator i$ = statements.iterator(); i$.hasNext(); ++stmtC) {        String statement = (String)i$.next();        int rowC = 0;        CompiledStatement compiledStmt = null;        compiledStmt = connection.compileStatement(statement, StatementType.EXECUTE, noFieldTypes, -1);        rowC = compiledStmt.runExecute();        logger.info("executed {} table statement changed {} rows: {}", label,                                               Integer.valueOf(rowC), statement);         }       ...    return stmtC;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.