Android 數(shù)據(jù)庫框架ormlite 使用精要
前言
本篇博客記錄一下筆者在實際開發(fā)中使用到的一個數(shù)據(jù)庫框架,這個可以讓我們快速實現(xiàn)數(shù)據(jù)庫操作,避免頻繁手寫sql,提高我們的開發(fā)效率,減少出錯的機率。
ormlite是什么?
首先可以去它的官網(wǎng)看看www.ormlite.com,它的英文全稱是Object Relational Mapping,意思是對象關系映射;如果接觸過Java EE開發(fā)的,一定知道Java Web開發(fā)就有一個類似的數(shù)據(jù)庫映射框架——Hibernate。簡單來說,就是我們定義一個實體類,利用這個框架,它可以幫我們吧這個實體映射到我們的數(shù)據(jù)庫中,在Android中是SQLite,數(shù)據(jù)中的字段就是我們定義實體的成員變量。
為什么要用ormlite?
先說說優(yōu)點
- 輕量級
- 使用簡單,易上手
- 封裝完善
- 文檔全面
缺點
- 基于反射,效率較低
- 缺少中文翻譯文檔
如何使用?
導入jar包到項目libs文件夾下
到http://ormlite.com/releases/下載相應版本的jar,下載最新的,目前是最新版本4.49。我們下載穩(wěn)定的4.48即可。

繼承OrmLiteSqliteOpenHelper類定義數(shù)據(jù)庫幫助類
package com.devilwwj.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.devilwwj.ormlite.model.Img;
import com.devilwwj.ormlite.model.PackageInfo;
import com.devilwwj.ormlite.model.Photographer;
import com.devilwwj.ormlite.model.Theme;
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;
/**
* 功能:數(shù)據(jù)庫幫助類
* @author devilwwj
*
*/
public class DBHelper extends OrmLiteSqliteOpenHelper {
/**
* 數(shù)據(jù)庫名字
*/
private static final String DB_NAME = "test.db";
/**
* 數(shù)據(jù)庫版本
*/
private static final int DB_VERSION = 1;
/**
* 用來存放Dao的地圖
*/
private Map<String, Dao> daos = new HashMap<String, Dao>();
private static DBHelper instance;
/**
* 獲取單例
* @param context
* @return
*/
public static synchronized DBHelper getHelper(Context context) {
context = context.getApplicationContext();
if (instance == null) {
synchronized (DBHelper.class) {
if (instance == null) {
instance = new DBHelper(context);
}
}
}
return instance;
}
/**
* 構造方法
* @param context
*/
public DBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
/**
* 這里創(chuàng)建表
*/
@Override
public void onCreate(SQLiteDatabase sqliteDatabase, ConnectionSource connectionSource) {
// 創(chuàng)建表
try {
TableUtils.createTable(connectionSource, PackageInfo.class);
TableUtils.createTable(connectionSource, Photographer.class);
TableUtils.createTable(connectionSource, Theme.class);
TableUtils.createTable(connectionSource, Img.class);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 這里進行更新表操作
*/
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource, int oldVersion,
int newVersion) {
try
{
TableUtils.dropTable(connectionSource, PackageInfo.class, true);
TableUtils.dropTable(connectionSource, Photographer.class, true);
TableUtils.dropTable(connectionSource, Theme.class, true);
TableUtils.dropTable(connectionSource, Img.class, true);
onCreate(sqLiteDatabase, connectionSource);
} catch (SQLException e)
{
e.printStackTrace();
}
}
/**
* 通過類來獲得指定的Dao
*/
public synchronized Dao getDao(Class clazz) throws SQLException {
Dao dao = null;
String className = clazz.getSimpleName();
if (daos.containsKey(className)) {
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;
}
}
}
定義實體類Bean,代表一張表
創(chuàng)建上面用到的Bean,在ormlite中,它代表數(shù)據(jù)庫中的一張表,我們所定義的所有成員變量均可為表中的字段,只要我們按照它提供的注解方式來指定成員變量屬性。
舉個栗子:
package com.devilwwj.ormlite.model;
import java.io.Serializable;
import com.j256.ormlite.dao.ForeignCollection;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.table.DatabaseTable;
/**
* 套餐
* @author wwj_748
*
*/
@DatabaseTable
public class PackageInfo implements Serializable{
@DatabaseField(id = true)
public int id;
@DatabaseField
public String pid;
@DatabaseField
public String photographerId;
@DatabaseField
public String name;
@DatabaseField()
public int cost;
@DatabaseField
public String description;
@DatabaseField
public String detail;
// 一個套餐可以對應多個主題
@ForeignCollectionField(eager = true) // 必須
public ForeignCollection<Theme> themes;
// 外部對象,一個套餐只對應一個攝影師,一個攝影師可以對應多個套餐
@DatabaseField(foreign = true)
public Photographer photographer;
@Override
public String toString() {
return "Package [id=" + id + ", pid=" + pid + ", photographerId="
+ photographerId + ", name=" + name + ", cost=" + cost
+ ", description=" + description + ", detail=" + detail + "]";
}
}
上面定義了一個套餐對象,我們來看一下它所用到的幾個注解:
@DatabaseTable:表示定義了一個數(shù)據(jù)表,如果不指定名字,在Android中會以類名作為表名,如packageInfo就是SQLite數(shù)據(jù)庫中的表名,我們也可以指定表名,@DatabaseTable(tableName = "tb_package") 。
DatabaseField:表示定義了數(shù)據(jù)中的一個字段,id表示數(shù)據(jù)中的一個主鍵,如果指定為generatedId,表示自動增長id,我們不需要給它賦值。其他字段,可以使用columnName來指定字段名,canBeNull表示是否為空,這些賦值可以按照以下來指定
-(id = true, canBeNull = false)
- (columnName = "name")
還有更多的注解用法,可以到官網(wǎng)查看它提供的文檔,非常清楚詳盡了,筆者這里不多說。
ormlite的外鍵約束(一對一、一對多、多對多關系)
使用這個框架需要比較注意的一點就是外鍵約束,這里筆者只討論一對一、一對多的情況。
上一節(jié)我們定義了PackageInfo這個實體,里面有這樣的定義:
// 一個套餐可以對應多個主題
@ForeignCollectionField(eager = true) // 必須
public ForeignCollection<Theme> themes;
// 外部對象,一個套餐只對應一個攝影師,一個攝影師可以對應多個套餐
@DatabaseField(foreign = true)
public Photographer photographer;
這里就用到了外鍵的約束,我們來分析一下:
一個套餐對應多個主題:1:n的關系
一個套餐對應一個攝影師:1:1的關系
在n的一方,我們可以使用@ForeignCollectionField這樣的注解,eager = true表示可以進行懶加載。
如果是一對一,我們還是用@DatabaseField注解,但要指定(foreign = true)表示是一個外鍵。
現(xiàn)在我們看一下多的一方是怎么定義的:
package com.devilwwj.ormlite.model;
import java.io.Serializable;
import com.j256.ormlite.dao.ForeignCollection;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.table.DatabaseTable;
/**
* 攝影主題
*
* @author wwj_748
*
*/
@DatabaseTable
public class Theme implements Serializable{
@DatabaseField(id = true)
public String id;
@DatabaseField
public String tid;
@DatabaseField
public String photographerId;
@DatabaseField
public String packageId; // 隸屬套餐
@DatabaseField
public String status; // 后臺審核狀態(tài)
@DatabaseField
public String title; // 標題
@DatabaseField
public String coverId; // 封面Id
@DatabaseField
public String coverUrl; // 封面img
@DatabaseField
public String detail; // 詳情
@DatabaseField
public int photoCount; // 圖片個數(shù)
@DatabaseField
public String photos; //圖集
@DatabaseField
public String createTime; // 上傳時間
@DatabaseField
public String recordTime; // 拍攝時間
@DatabaseField
public double cost; // 花費
@DatabaseField
public String tags; // 標簽
@DatabaseField
public String address;// 地址
@DatabaseField
public String loacationCode; // 位置代碼
@DatabaseField
public int popularCount; // 熱度
@DatabaseField(defaultValue = "0")
public int favStatus; // 收藏狀態(tài)
// 外部對象字段
@DatabaseField(foreign = true, foreignAutoRefresh = true)
public PackageInfo mPackage;
@DatabaseField(foreign = true, foreignAutoRefresh = true)
public Photographer photographer;
/**
* 這里需要注意的是:屬性類型只能是ForeignCollection<T>或者Collection<T>
* 如果需要懶加載(延遲加載)可以在@ForeignCollectionField加上參數(shù)eager=false
* 這個屬性也就說明一個部門對應著多個用戶
*/
@ForeignCollectionField(eager = true)
public ForeignCollection<Img> imgs;
}
我們這里不關注其他字段,關注它的外鍵字段,前面我們說到,一個套餐對應多個主題,所以我們在主題這個實體類中也需要定義一個關聯(lián)套餐的字段。
// 外部對象字段
@DatabaseField(foreign = true, foreignAutoRefresh = true)
public PackageInfo mPackage;
注:要實現(xiàn)一對多關系,一定要這樣定義,不然會出錯。
定義Bean的DAO,對數(shù)據(jù)庫進行增、刪、改、查
這里筆者舉個例子,大家以后開發(fā)根據(jù)這樣來添加相應的業(yè)務邏輯方法:
package com.devilwwj.ormlite.dao;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import com.devilwwj.ormlite.db.DBHelper;
import com.devilwwj.ormlite.model.Theme;
import com.j256.ormlite.dao.Dao;
/**
* 定義數(shù)據(jù)訪問對象,對指定的表進行增刪改查操作
* @author devilwwj
*
*/
public class ThemeDao {
private Dao<Theme, Integer> themeDao;
private DBHelper dbHelper;
/**
* 構造方法
* 獲得數(shù)據(jù)庫幫助類實例,通過傳入Class對象得到相應的Dao
* @param context
*/
public ThemeDao(Context context) {
try {
dbHelper = DBHelper.getHelper(context);
themeDao = dbHelper.getDao(Theme.class);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 添加一條記錄
* @param theme
*/
public void add(Theme theme) {
try {
themeDao.create(theme);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 刪除一條記錄
* @param theme
*/
public void delete(Theme theme) {
try {
themeDao.delete(theme);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 更新一條記錄
* @param theme
*/
public void update(Theme theme) {
try {
themeDao.update(theme);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 查詢一條記錄
* @param id
* @return
*/
public Theme queryForId(int id) {
Theme theme = null;
try {
theme = themeDao.queryForId(id);
} catch (SQLException e) {
e.printStackTrace();
}
return theme;
}
/**
* 查詢所有記錄
* @return
*/
public List<Theme> queryForAll() {
List<Theme> themes = new ArrayList<Theme>();
try {
themes = themeDao.queryForAll();
} catch (SQLException e) {
e.printStackTrace();
}
return themes;
}
}
上面筆者定義了一個Dao類,用來進行數(shù)據(jù)訪問的,定義了增加、刪除、更新、查詢幾個方法,我們在應用中就可以使用這個幾個方法來幫助我們完成相關的操作。
具體使用方法:
Theme theme = new Theme();
// 賦值
theme.id = 1;
theme.title = "主題";
theme.detail = "主題詳情";
new ThemeDao(context).add(theme);
ormlite的批處理操作
/**
* 轉化json對象為數(shù)據(jù)庫對象
* @param context
* @param theme
* @return
* @throws SQLException
* @throws Exception
*/
public static Theme ConvertTheme(Context context, final JSONObject theme) throws SQLException, Exception {
JSONObject photographerObj = theme.getJSONObject("photographer");
JSONObject packageObj = theme.getJSONObject("package");
ThemeDao themeDao = new ThemeDao(context);
PhotographDao photographDao = new PhotographDao(context);
// 根據(jù)id查詢攝影師
Photographer mPhotographer = photographDao.queryForId(theme.optInt("photographerId"));
if (mPhotographer == null)
mPhotographer = new Photographer();
mPhotographer.id = theme.optString("photographerId");
mPhotographer.name = photographerObj.optString("nickname");
mPhotographer.serviceArea = photographerObj.optString("serviceArea");
mPhotographer.avatar = photographerObj.optString("avatar");
// 這里創(chuàng)建或更新攝影師
photographDao.createOrUpdate(mPhotographer);
PackageDao packageDao = new PackageDao(context);
// 根據(jù)id查詢套餐
PackageInfo mPackage = packageDao.queryForId(packageObj.optInt("id"));
if (mPackage == null)
mPackage = new PackageInfo();
mPackage.id = packageObj.optInt("id");
mPackage.name = packageObj.optString("title");
mPackage.cost = packageObj.optInt("cost");
mPackage.detail = packageObj.optString("detail");
// 這里創(chuàng)建或更新套餐
packageDao.createOrUpdate(mPackage);
// 根據(jù)id查詢作品
Theme mThemeTmp = themeDao.queryForId(
theme.optInt("id"));
if (mThemeTmp == null)
mThemeTmp = new Theme();
final Theme mTheme = mThemeTmp;
mTheme.id = theme.optString("id");
mTheme.title = theme.optString("title");
// mTheme.coverId = theme.optString("place");
// mTheme.coverUrl = theme.optString("coverUrl");
mTheme.photographerId = theme.optString("photographerId");
mTheme.detail = theme.optString("detail");
// mTheme.cost = theme.optDouble("cost");
// mTheme.recordTime = theme.optString("recordTime");
mTheme.favStatus = theme.optInt("isFav");
mTheme.photoCount = theme.optInt("photoCount");
mTheme.tags = theme.optString("tags");
mTheme.packageId = theme.optString("packageId");
// 同步更新
mTheme.photographer = mPhotographer;
mTheme.mPackage = mPackage;
// 創(chuàng)建或更新主題
themeDao.createOrUpdate(mTheme);
final ImgDao mDao = new ImgDao(context);
Dao<Img, Integer> imgDao = mDao.getImgDao();
// 執(zhí)行批處理操作
imgDao.callBatchTasks(new Callable<Void>() {
@Override
public Void call() throws Exception {
JSONArray imgs = theme.getJSONArray("photos");
for (int i = 0; i < imgs.length(); i++) {
JSONObject jsonObject = imgs.getJSONObject(i);
Img mImg = mDao.queryForId(jsonObject.optInt("id"));
if (mImg == null)
mImg = new Img();
mImg.id = jsonObject.optString("id");
mImg.isCover = jsonObject.optInt("isCover");
mImg.imgUrl = jsonObject.optString("url");
mImg.theme = mTheme;
mDao.createOrUpdate(mImg);
}
return null;
}
});
return mTheme;
}
上面的代碼就是把我們從服務端獲取的json對象進行數(shù)據(jù)轉化,我們對json數(shù)據(jù)進行解析,得到相應的數(shù)據(jù)庫對象,再進行創(chuàng)建或更新的操作,如果涉及到較多的插入,就可以使用ormlite為我們提供的批處理回調方法,具體看代碼。
在Android中使用
我們通過網(wǎng)絡請求得到的json對象,然后直接調用上面寫好的轉化方法,這樣我們就可以實現(xiàn)數(shù)據(jù)存儲了。
private void getTheme(final Theme mTheme) {
DataFetcher.getHttpRequestAsync(
HttpRequest.getThemeInfoUrl(getActivity(), mTheme.id),
new JsonResponseHandler(getActivity(),
getString(R.string.tip_requesing_info)) {
@Override
public void onSuccess(int statusCode, Header[] headers,
JSONObject response) {
super.onSuccess(statusCode, headers, response);
LogUtils.i("info", response.toString());
JSONObject jsonObject = response.optJSONObject("msg");
try {
Converter.ConvertTheme(jsonObject,
((BaseActivity) getActivity()).getHelper());
// 跳轉到詳情頁
Intent intent = new Intent();
intent.putExtra("tid", mTheme.id);
intent.setClass(getActivity(),
ThemeInfolActivity.class);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(int statusCode, Header[] headers,
String responseString, Throwable throwable) {
super.onFailure(statusCode, headers, responseString,
throwable);
if (mTheme.detail != null) {
Intent intent = new Intent();
intent.putExtra("tid", mTheme.id);
intent.setClass(getActivity(), ThemeInfolActivity.class);
startActivity(intent);
} else {
Toast.makeText(getActivity(), responseString,
Toast.LENGTH_SHORT).show();
}
}
});
}
注:這里筆者使用的是android-async-http這個網(wǎng)絡庫。