1. 程式人生 > >Android 快速開發系列 ORMLite 框架最佳實踐

Android 快速開發系列 ORMLite 框架最佳實踐

               

上一篇已經對ORMLite框架做了簡單的介紹:Android ORMLite 框架的入門用法~~本篇將介紹專案可能會使用到的一些用法,也為我們的使用ORMLite框架總結出一個較合理的用法。

通過上一篇的瞭解,我們使用ORMLite,需要自己寫一個DatabaseHelper去繼承OrmLiteSqliteOpenHelper,下面我們首先給出一個我認為比較靠譜的Helper的寫法:

1、DatabaseHelper

package com.zhy.zhy_ormlite.db;import java.sql.SQLException;import java.util.HashMap;import java.util.Map;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;import com.j256.ormlite.dao.Dao;import com.j256.ormlite.support.ConnectionSource;import com.j256.ormlite.table.TableUtils;import com.zhy.zhy_ormlite.bean.Article;import com.zhy.zhy_ormlite.bean.Student;import com.zhy.zhy_ormlite.bean.User;public  class DatabaseHelper extends OrmLiteSqliteOpenHelper{ private static final String TABLE_NAME = "sqlite-test.db"; private Map<String, Dao> daos = new HashMap<String, Dao>(); private DatabaseHelper(Context context) {  super(context, TABLE_NAME, null, 4); } @Override public void onCreate(SQLiteDatabase database,   ConnectionSource connectionSource) {  try  {   TableUtils.createTable(connectionSource, User.class);   TableUtils.createTable(connectionSource, Article.class);   TableUtils.createTable(connectionSource, Student.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);   TableUtils.dropTable(connectionSource, Student.class, true);   onCreate(database, connectionSource);  } catch (SQLException e)  {   e.printStackTrace();  } } private static DatabaseHelper instance; /**  * 單例獲取該Helper  *   * @param context  * @return  */ public static synchronized DatabaseHelper getHelper(Context context) {  context = context.getApplicationContext();  if (instance == null)  {   synchronized (DatabaseHelper.class)   {    if (instance == null)     instance = new DatabaseHelper(context);   }  }  return instance; } public synchronized Dao getDao(Class clazz) throws SQLException {  Dao dao = null;  String className = clazz.getSimpleName();  if (daos.containsKey(className))  {   dao = daos.get(className);  }  if (dao == null)  {   dao = super.getDao(clazz);   daos.put(className, dao);  }  return dao; } /**  * 釋放資源  */ @Override public void close() {  super.close();  for (String key : daos.keySet())  {   Dao dao = daos.get(key);   dao = null;  } }}
1、整個DatabaseHelper使用單例只對外公佈出一個物件,保證app中只存在一個SQLite Connection , 參考文章:http://www.touchlab.co/2011/10/single-sqlite-connection/

2、我們對每個Bean建立一個XXXDao來處理當前Bean的資料庫操作,當然真正去和資料庫打交道的物件,通過上面程式碼中的getDao(T t)進行獲取

getDao為一個泛型方法,會根據傳入Class物件進行建立Dao,並且使用一個Map來保持所有的Dao物件,只有第一次呼叫時才會去呼叫底層的getDao()。

2、Bean的Dao

package com.zhy.zhy_ormlite.db;import java.sql.SQLException;import android.content.Context;import com.j256.ormlite.dao.Dao;import com.zhy.zhy_ormlite.bean.User;public class UserDao{ private Context context; private Dao<User, Integer> userDaoOpe; private DatabaseHelper helper; public UserDao(Context context) {  this.context = context;  try  {   helper = DatabaseHelper.getHelper(context);   userDaoOpe = helper.getDao(User.class);  } catch (SQLException e)  {   e.printStackTrace();  } } /**  * 增加一個使用者  * @param user  */ public void add(User user) {  try  {   userDaoOpe.create(user);  } catch (SQLException e)  {   e.printStackTrace();  } }//...other operations}
我們的所有的XXXDao遵循以上的風格~

好了,基本瞭解了我們的程式碼的結構~~ps:如果覺得不合理可以留言指出,如果覺得不能接收,直接忽略。。。

3、ORMLite外來鍵引用

現在我們有兩張表一張User,一張Article; 

Article中當然需要儲存User的主鍵,作為關聯~~那麼在ORMLite中如何做到呢?

可能有人會直接在Article中宣告一個int型別userId屬性,當作普通屬性處理搞定,這種做法並沒有做,但是沒有體現出面向物件的思想。

面向物件是這樣的:Article屬於某個User

類這麼定義:

package com.zhy.zhy_ormlite.bean;import com.j256.ormlite.field.DatabaseField;import com.j256.ormlite.table.DatabaseTable;@DatabaseTable(tableName = "tb_article")public class Article{ @DatabaseField(generatedId = true) private int id; @DatabaseField private String title; @DatabaseField(canBeNull = true, foreign = true, columnName = "user_id") private User user; 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 User getUser() {  return user; } public void setUser(User user) {  this.user = user; } @Override public String toString() {  return "Article [id=" + id + ", title=" + title + ", user=" + user    + "]"; }}
不會去定義一個int型別的userId,而是直接定義一個User成員變數,表示本Article屬於該User;

然後在User user屬性上新增:@DatabaseField(canBeNull = true, foreign = true, columnName = "user_id")

canBeNull -表示不能為null;foreign=true表示是一個外來鍵;columnName 列名

User類暫且就兩個屬性:

package com.zhy.zhy_ormlite.bean;import com.j256.ormlite.field.DatabaseField;import com.j256.ormlite.table.DatabaseTable;@DatabaseTable(tableName = "tb_user")public class User { @DatabaseField(generatedId = true) private int id; @DatabaseField(columnName = "name") private String name; public User() { } 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; } @Override public String toString() {  return "User [id=" + id + ", name=" + name     + "]"; }  }
現在看我們的ArticleDao
package com.zhy.zhy_ormlite.db;import java.sql.SQLException;import java.util.List;import android.content.Context;import com.j256.ormlite.dao.Dao;import com.zhy.zhy_ormlite.bean.Article;import com.zhy.zhy_ormlite.bean.User;public class ArticleDao{ private Dao<Article, Integer> articleDaoOpe; private DatabaseHelper helper; @SuppressWarnings("unchecked") public ArticleDao(Context context) {  try  {   helper = DatabaseHelper.getHelper(context);   articleDaoOpe = helper.getDao(Article.class);  } catch (SQLException e)  {   e.printStackTrace();  } } /**  * 新增一個Article  * @param article  */ public void add(Article article) {  try  {   articleDaoOpe.create(article);  } catch (SQLException e)  {   e.printStackTrace();  } } /**  * 通過Id得到一個Article  * @param id  * @return  */ @SuppressWarnings("unchecked") public Article getArticleWithUser(int id) {  Article article = null;  try  {   article = articleDaoOpe.queryForId(id);   helper.getDao(User.class).refresh(article.getUser());  } catch (SQLException e)  {   e.printStackTrace();  }  return article; }  /**  * 通過Id得到一篇文章  * @param id  * @return  */ public Article get(int id) {  Article article = null;  try  {   article = articleDaoOpe.queryForId(id);  } catch (SQLException e)  {   e.printStackTrace();  }  return article; } /**  * 通過UserId獲取所有的文章  * @param userId  * @return  */ public List<Article> listByUserId(int userId) {  try  {   return articleDaoOpe.queryBuilder().where().eq("user_id", userId)     .query();  } catch (SQLException e)  {   e.printStackTrace();  }  return null; }}
接下來看我們的測試類:
public class OrmLiteDbTest extends AndroidTestCase{ public void testAddArticle() {  User u = new User();  u.setName("張鴻洋");  new UserDao(getContext()).add(u);  Article article = new Article();  article.setTitle("ORMLite的使用");  article.setUser(u);  new ArticleDao(getContext()).add(article); } public void testGetArticleById() {  Article article = new ArticleDao(getContext()).get(1);  L.e(article.getUser() + " , " + article.getTitle()); } public void testGetArticleWithUser() {  Article article = new ArticleDao(getContext()).getArticleWithUser(1);  L.e(article.getUser() + " , " + article.getTitle()); }  public void testListArticlesByUserId() {  List<Article> articles = new ArticleDao(getContext()).listByUserId(1);  L.e(articles.toString()); }
分別測試,新增一個Article;通過Id獲取一個Article;通過Id獲取一個Article且攜帶User;通過userId獲取所有的Article;

主要看第三個:通過Id獲取一個Article且攜帶User,testGetArticleWithUser(id)

如何值傳一個Article的Id,然後能夠拿到Article物件,且內部的user屬性直接賦值呢?

兩種方式:

1、即上述寫法

article = articleDaoOpe.queryForId(id);   helper.getDao(User.class).refresh(article.getUser());
2、在user屬性的註解上:@DatabaseField(canBeNull = true, foreign = true, columnName = "user_id", foreignAutoRefresh = true)

新增foreignAutoRefresh =true,這樣;當呼叫queryForId時,拿到Article物件則直接攜帶了user;

4、關聯一個集合

每個User關聯一個或多個Article,如果我在User中宣告一個Collection<Article> articles,我能否在查詢User的時候,一併能夠獲取到articles的值呢?

答案是可以的。在User中新增如下屬性,且註解如下:

@ForeignCollectionFieldprivate Collection<Article> articles;

我們在UserDao中書寫查詢User的程式碼:

public User get(int id) {  try  {   return userDaoOpe.queryForId(id);  } catch (SQLException e)  {   e.printStackTrace();  }  return null ; }
測試程式碼:
public void testGetUserById() {  User user = new UserDao(getContext()).get(1);  L.e(user.getName());  if (user.getArticles() != null)   for (Article article : user.getArticles())   {    L.e(article.toString());   } }
輸出:
09-07 22:49:06.484: E/zhy(7293): 張鴻洋09-07 22:49:06.484: E/zhy(7293): Article [id=1, title=ORMLite的使用]
可以看到,我們通過一個queryForId,成功的獲取了User,以及User關聯的所有的Articles;

5、條件查詢QueryBuilder的使用

上述程式碼其實已經用到了簡單的條件查詢了:

1、簡單的where等於

articleDaoOpe.queryBuilder().where().eq("user_id", userId).query();直接返回Article的列表

2、where and 

 QueryBuilder<Article, Integer> queryBuilder = articleDaoOpe     .queryBuilder();   Where<Article, Integer> where = queryBuilder.where();   where.eq("user_id", 1);   where.and();   where.eq("name", "xxx");   //或者   articleDaoOpe.queryBuilder().//     where().//     eq("user_id", 1).and().//     eq("name", "xxx");
上述兩種都相當與:select * from tb_article where user_id = 1 and name = 'xxx' ; 

3、更復雜的查詢

where.or(     //     where.and(//       where.eq("user_id", 1), where.eq("name", "xxx")),     where.and(//       where.eq("user_id", 2), where.eq("name", "yyy")));
select * from tb_article where ( user_id = 1 and name = 'xxx' )  or ( user_id = 2 and name = 'yyy' )  ;

好了,再複雜的查詢估計也能夠湊出來了~~

6、updateBuilder、deleteBuilder

使用queryBuilder是因為我們希望執行完成查詢直接返回List<Bean>集合;

對於Update我們並不關注返回值,直接使用

articleDaoOpe.updateRaw(statement, arguments);傳入sql和引數即可~~

何必在那articleDaoOpe.updateBuilder().updateColumnValue("name","zzz").where().eq("user_id", 1);這樣的痛苦呢~~~

同理還有deleteBuilder還是建議直接拼寫sql,當然很簡單的除外,直接使用它的API~

7、事務操作

在我們的Dao中直接寫如下程式碼:

//事務操作  TransactionManager.callInTransaction(helper.getConnectionSource(),    new Callable<Void>()    {     @Override     public Void call() throws Exception     {      return null;     }    });

8、其他操作

1、當Bean繼承BaseDaoEnabled時,可以使用bean.create(bean);bean.update(bean)一類操作

例如:

Student extends BaseDaoEnabled<Student, Integer>

Dao dao = DatabaseHelper.getHelper(getContext()).getDao(Student.class);Student student = new Student();student.setDao(dao);student.setName("張鴻洋");student.create();

前提dao需要手動設定,如果dao為null會報錯,尼瑪,我覺得一點用沒有。。。

2、Join

QueryBuilder<Article, Integer> articleBuilder = articleDaoOpe     .queryBuilder();   QueryBuilder userBuilder = helper.getDao(User.class).queryBuilder();   articleBuilder.join(userBuilder);
Article與User做Join操作;

本篇主要想介紹在專案中如何寫DataBaseHelper已經如何寫BeanDao,以及列出了在專案中可能會用到的ORMLite的功能,如果需要詳細瞭解,還請看ORMLite官方文件,原始碼中也會提供~~