Android資料庫ORM框架用法、原始碼和效能比較分析
基本用法
LitePal
LitePal是一款開源的Android資料庫框架,它採用了物件關係對映(ORM)的模式,LitePal很“輕”,jar包只有100k不到,使用起來也比較簡單,原始碼地址為Github地址。
首先需要引入lib,可以通過gradle引入也可以將下載的litepal.jar包直接放入libs目錄下。然後需要在assets目錄下新建一個litepal.xml檔案,檔名稱不能隨意更改,程式碼如下:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value ="demo" ></dbname>
<version value="1" ></version>
<list>
<mapping class="com.example.databasetest.model.News"></mapping>
</list>
</litepal>
用於設定資料庫的名字,用於設定資料庫的版本號,用於設定所有的對映模型,用於存放具體的Model類,必須是完整的類名。
然後還需要配置LitePalApplication,由於操作資料庫時需要用到Context,而我們顯然不希望在每個介面中都去傳一遍這個引數,那樣操作資料庫就顯得太繁瑣了。因此,LitePal使用了一個方法來簡化掉Context這個引數,只需要在AndroidManifest.xml中配置一下LitePalApplication,所有的資料庫操作就都不用再傳Context了。如果需要自定義application檔案,只需繼承LitePalApplication即可。
接下來需要建立Model類
public class News extends DataSupport {
private int id;
private String title;
private String content;
private Date publishDate;
private int commentCount;
// 自動生成get、set方法
...
}
繼承了DataSupport類之後,這些實體類就擁有了進行CRUD操作的能力
News news = new News();
news.setTitle("這是一條新聞標題");
news.setContent("這是一條新聞內容");
news.setPublishDate(new Date());
news.save();
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc").find(News.class);
LitePal還提供了直接用sql原句實現查詢的api方法,擴充套件性也比較好。
AFinal
AFinal是一個Android的orm、ioc快速開發框架,裡面包含了四大功能:控制元件的id繫結和事件繫結功能;網路圖片的顯示功能(裡面包含了強大的快取框架);資料庫sqlite的操作功能;http資料的讀取功能(支援ajax方式讀取),可從Github AFinal獲取原始碼。資料庫FinalDb是其中的一個元件,使用比較簡單。
首先需要建立model類,承擔物件與資料庫表的對映功能。
@Table(name="table_user")
public class User {
@id
private int id;
private String name;
private String email;
private Date birth;
// getter and setter不能省略
...
}
宣告@Table註解,表示表名稱為table_user;@id表示id作為該表自動增長的主鍵,接下來就可以直接使用了。
FinalDb db = FinalDb.create(this, "afinal_db");
User user = new User();
user.setEmail("[email protected]");
user.setName("探索者");
user.setBirth(new Date());
db.save(user);
String name = "探索者";
List<User> userList = db.findAllByWhere(User.class, "name="' + name + "'" );//查詢使用者名稱為探索者的使用者
if(userList.size() > 0){
User other = userList.get(0);
other.setEmail("customer@youzan.com");
db.update(other);
}
以上就是AFinal中關於資料庫的簡單用法,不需要額外的配置,api介面也比較簡單易用。
greenDAO
greenDAO與上述兩種ORM框架不同,其原理不是根據反射進行資料庫的各項操作,而是一開始就人工生成業務需要的Model和DAO檔案,業務中可以直接呼叫相應的DAO檔案進行資料庫操作,從而避免了因反射帶來的效能損耗和效率低下。但是由於需要人工生成model和DAO檔案,所以greenDAO的配置就略顯複雜。
首先需要新建一個java工程來生成DAO類檔案,該工程需要匯入greendao-generator.jar和freemarker.jar檔案到專案中,github官方原始碼中已經提供了該工程。
public class ExampleDaoGenerator
{
public static void main(String[] args) throws Exception
{
Schema schema = new Schema(3, "de.greenrobot.daoexample");
addNote(schema);
addCustomerOrder(schema);
new DaoGenerator().generateAll(schema, "../DaoExample/src-gen");
}
private static void addNote(Schema schema)
{
Entity note = schema.addEntity("Note");
note.addIdProperty();
note.addStringProperty("text").notNull();
note.addStringProperty("comment");
note.addDateProperty("date");
}
private static void addCustomerOrder(Schema schema)
{
Entity customer = schema.addEntity("Customer");
customer.addIdProperty();
customer.addStringProperty("name").notNull();
Entity order = schema.addEntity("Order");
order.setTableName("ORDERS"); // "ORDER" is a reserved keyword
order.addIdProperty();
Property orderDate = order.addDateProperty("date").getProperty();
Property customerId = order.addLongProperty("customerId").notNull().getProperty();
order.addToOne(customer, customerId);
ToMany customerToOrders = customer.addToMany(order, customerId);
customerToOrders.setName("orders");
customerToOrders.orderAsc(orderDate);
}
}
在main方法中,
Schema schema = new Schema(3, "de.greenrobot.daoexample");
該方法第一個引數用來更新資料庫版本號,第二個引數為要生成的DAO類所在包路徑。然後進行建表和設定要生成DAO檔案的目標工程的專案路徑。
addNote(schema);
addCustomerOrder(schema);
new DaoGenerator().generateAll(schema, "../DaoExample/src-gen");
其中src-gen這個目錄名需要在執行前手動建立,否則會報錯。執行後出現以下的提示說明DAO檔案自動生成成功了,重新整理一下DaoExample專案即可看到。
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/NoteDao.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/Note.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/CustomerDao.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/Customer.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/OrderDao.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/Order.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/DaoMaster.java
Written /Users/duanyangyang/Documents/Android/greenDAO/DaoExample/src/main/java/de/greenrobot/daoexample/DaoSession.java
Processed 3 entities in 268ms
然後具體的使用就比較簡單了
DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
db = helper.getWritableDatabase();
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();
String textColumn = NoteDao.Properties.Text.columnName;
String orderBy = textColumn + " COLLATE LOCALIZED ASC";
cursor = db.query(noteDao.getTablename(), noteDao.getAllColumns(), null, null, null, null, orderBy);
String[] from = { textColumn, NoteDao.Properties.Comment.columnName };
int[] to = { android.R.id.text1, android.R.id.text2 };
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursor, from,
to);
setListAdapter(adapter);
三種資料庫框架效能比較
為了比較三種資料庫框架的效能,首先選擇同一款手機華為Mate7,然後從以下幾個方面分析比較:
迴圈插入10000次,每次插入一條資料,三種框架用時為:
LitePal 155313ms;
AFinal 110601ms;
greedDAO 98238ms;
批量插入10000條資料,三種框架用時為:
LitePal 12882ms;
AFinal 2783ms;
greedDAO 623ms;
迴圈更新10000次,每次更新一條資料,三種框架用時為:
LitePal 152395ms;
AFinal 4123ms;
greedDAO 97840ms;
批量更新10000條資料,三種框架用時為:
LitePal 12234ms;
AFinal 1585ms;
greedDAO 739ms;
迴圈查詢10000條資料,每次查詢一條資料,三種框架用時為:
LitePal 13806ms;
AFinal 4117ms;
greedDAO 31923ms;
批量查詢10000條資料,三種框架用時為:
LitePal 6863ms;
AFinal 1585ms;
greedDAO 149ms;
根據以上資料可以得出以下幾個結論:
- 不管是批量插入、更新、查詢,greenDAO都是用時最短,執行速度最快的;
- 大部分資料庫操作的情況下,LitePal都是用時最長的,只有在迴圈查詢時表現比greenDAO好
- AFinal對於很多批量操作沒有提供相應的api方法
- 不管對於哪個框架,批量操作比迴圈操作用時都要短很多
下面是以greenDAO為例的測試原始碼
private void testInsertQueryTime(final int count){
noteList1 = new ArrayList<Note>();
for (int i = 0; i < count; i++){
Note note = new Note(null, "title", "comment", new Date());
noteList1.add(note);
}
noteList2 = new ArrayList<Note>();
for (int i = 0; i < count; i++){
Note note = new Note(null, "title", "comment", new Date());
noteList2.add(note);
}
new Thread(){
@Override
public void run() {
super.run();
long time1 = System.currentTimeMillis();
for(Note note : noteList1){
noteDao.insert(note);
}
// 迴圈插入10000次,每次插入一條資料
long time = System.currentTimeMillis() - time1;
Log.d("NoteActivity", "greedDAO one by one insert " + noteList1.size() + " data, use time " + time + "ms");
// 批量插入10000條資料
long time2 = System.currentTimeMillis();
noteDao.insertInTx((Note[])noteList2.toArray(new Note[noteList2.size()]));
long time3 = System.currentTimeMillis() - time2;
Log.d("NoteActivity", "greedDAO batch insert " + noteList2.size() + " data, use time " + time3 + "ms");
// 迴圈查詢10000條資料,每次查詢一條資料
long time4 = System.currentTimeMillis();
for(int i = 0; i < count; i++){
noteDao.queryBuilder().limit(1).list();
}
long time5 = System.currentTimeMillis() - time4;
Log.d("NoteActivity", "greedDAO one by one query " + count + " data, use time " + time5 + "ms");
// 批量查詢10000條資料
long time6 = System.currentTimeMillis();
List<Note> list2 = noteDao.queryBuilder().limit(10000).offset(-1).list();
long time7 = System.currentTimeMillis() - time6;
Log.d("NoteActivity", "greedDAO batch query " + list2.size() + " data, use time " + time7 + "ms");
// 迴圈更新10000次,每次更新一條資料
long time8 = System.currentTimeMillis();
for (Note note : noteList1){
note.setText("update_title");
note.setComment("update_comment");
noteDao.update(note);
}
// 批量更新10000條資料
long time9 = System.currentTimeMillis() - time8;
Log.d("NoteActivity", "greedDAO one by one update " + count + " data, use time " + time9 + "ms");
for (Note note : noteList2){
note.setText("update_title");
note.setComment("update_comment");
}
long time10 = System.currentTimeMillis();
noteDao.updateInTx(noteList2);
long time11 = System.currentTimeMillis() - time10;
Log.d("NoteActivity", "greedDAO batch update " + count + " data, use time " + time11 + "ms");
}
}.start();
}
三種資料庫框架的原理和原始碼分析
LitePal
LitePal通過LitePal.xml檔案獲取資料庫的名稱、版本號以及表,然後建立資料庫和表,要執行增刪改查操作時,就會根據資料model進行引數拼裝,最後呼叫系統原生的資料庫操作。解析litepal.xml檔案方法原始碼如下:
private InputStream getConfigInputStream() throws IOException {
AssetManager assetManager = LitePalApplication.getContext().getAssets();
String[] fileNames = assetManager.list("");
if (fileNames != null && fileNames.length > 0) {
for (String fileName : fileNames) {
if (Const.LitePal.CONFIGURATION_FILE_NAME.equalsIgnoreCase(fileName)) {
return assetManager.open(fileName, AssetManager.ACCESS_BUFFER);
}
}
}
throw new ParseConfigurationFileException(
ParseConfigurationFileException.CAN_NOT_FIND_LITEPAL_FILE);
}
void usePullParse() {
try {
LitePalAttr litePalAttr = LitePalAttr.getInstance();
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(getConfigInputStream(), "UTF-8");
int eventType = xmlPullParser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
String nodeName = xmlPullParser.getName();
switch (eventType) {
case XmlPullParser.START_TAG: {
if (NODE_DB_NAME.equals(nodeName)) {
String dbName = xmlPullParser.getAttributeValue("", ATTR_VALUE);
litePalAttr.setDbName(dbName);
} else if (NODE_VERSION.equals(nodeName)) {
String version = xmlPullParser.getAttributeValue("", ATTR_VALUE);
litePalAttr.setVersion(Integer.parseInt(version));
} else if (NODE_MAPPING.equals(nodeName)) {
String className = xmlPullParser.getAttributeValue("", ATTR_CLASS);
litePalAttr.addClassName(className);
} else if (NODE_CASES.equals(nodeName)) {
String cases = xmlPullParser.getAttributeValue("", ATTR_VALUE);
litePalAttr.setCases(cases);
}
break;
}
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (XmlPullParserException e) {
throw new ParseConfigurationFileException(
ParseConfigurationFileException.FILE_FORMAT_IS_NOT_CORRECT);
} catch (IOException e) {
throw new ParseConfigurationFileException(ParseConfigurationFileException.IO_EXCEPTION);
}
}
LitePal框架會根據model自動建立好相應的資料表,以及表資料型別和非空約束等。
protected String generateCreateTableSQL(String tableName, List<ColumnModel> columnModels,
boolean autoIncrementId) {
StringBuilder createTableSQL = new StringBuilder("create table ");
createTableSQL.append(tableName).append(" (");
if (autoIncrementId) {
createTableSQL.append("id integer primary key autoincrement,");
}
if (columnModels.size() == 0) {
createTableSQL.deleteCharAt(createTableSQL.length() - 1);
}
boolean needSeparator = false;
for (ColumnModel columnModel : columnModels) {
if (columnModel.isIdColumn()) {
continue;
}
if (needSeparator) {
createTableSQL.append(", ");
}
needSeparator = true;
createTableSQL.append(columnModel.getColumnName()).append(" ").append(columnModel.getColumnType());
if (!columnModel.isNullable()) {
createTableSQL.append(" not null");
}
if (columnModel.isUnique()) {
createTableSQL.append(" unique");
}
String defaultValue = columnModel.getDefaultValue();
if (!TextUtils.isEmpty(defaultValue)) {
createTableSQL.append(" default ").append(defaultValue);
}
}
createTableSQL.append(")");
LogUtil.d(TAG, "create table sql is >> " + createTableSQL);
return createTableSQL.toString();
}
要執行增刪改查操作的資料model都會繼承DataSupport,以查詢方法findAll()為例,DataSupport會先呼叫QueryHandler.onFindAll()方法,然後最後會執行到QueryHandler.query方法,該方法傳入的引數有類名、列名稱、查詢條件、排序等等,經過處理後呼叫SDLiteDatabase.query方法,最後將查詢得到的資料轉換成List並返回,原始碼如下:
protected <T> List<T> query(Class<T> modelClass, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having, String orderBy, String limit,
List<AssociationsInfo> foreignKeyAssociations) {
List<T> dataList = new ArrayList<T>();
Cursor cursor = null;
try {
List<Field> supportedFields = getSupportedFields(modelClass.getName());
String tableName = getTableName(modelClass);
String[] customizedColumns = getCustomizedColumns(columns, foreignKeyAssociations);
cursor = mDatabase.query(tableName, customizedColumns, selection, selectionArgs,
groupBy, having, orderBy, limit);
if (cursor.moveToFirst()) {
SparseArray<QueryInfoCache> queryInfoCacheSparseArray = new SparseArray<QueryInfoCache>();
do {
T modelInstance = (T) createInstanceFromClass(modelClass);
giveBaseObjIdValue((DataSupport) modelInstance,
cursor.getLong(cursor.getColumnIndexOrThrow("id")));
setValueToModel(modelInstance, supportedFields, foreignKeyAssociations, cursor, queryInfoCacheSparseArray);
if (foreignKeyAssociations != null) {
setAssociatedModel((DataSupport) modelInstance);
}
dataList.add(modelInstance);
} while (cursor.moveToNext());
queryInfoCacheSparseArray.clear();
}
return dataList;
} catch (Exception e) {
e.printStackTrace();
throw new DataSupportException(e.getMessage());
} finally {
if (cursor != null) {
cursor.close();
}
}
}
可以看到LitePal不管是建立資料庫、表還是執行增刪改查都是根據Model的類名和屬性名,每次都需要進行反射拼裝然後呼叫Android原生的資料庫操作或者直接執行sql語句。
AFinal
AFinal本質上也是利用java的反射原理實現物件與資料表的對映的,實現上和LitePal有很多不同。AFinal並未提前建立資料庫和表,而是在第一次執行增刪改查方法時,先判斷資料庫和表是否存在,如果不存在的話就根據model執行sql語句進行建立,建立完成後再拼裝具體的操作sql語句,完成相應的增刪改查操作。相關原始碼如下:
public void save(Object entity) {
checkTableExist(entity.getClass());
exeSqlInfo(SqlBuilder.buildInsertSql(entity));
}
private void checkTableExist(Class<?> clazz) {
if (!tableIsExist(TableInfo.get(clazz))) {
String sql = SqlBuilder.getCreatTableSQL(clazz);
debugSql(sql);
db.execSQL(sql);
}
}
public static String getCreatTableSQL(Class<?> clazz) {
TableInfo table = TableInfo.get(clazz);
Id id = table.getId();
StringBuffer strSQL = new StringBuffer();
strSQL.append("CREATE TABLE IF NOT EXISTS ");
strSQL.append(table.getTableName());
strSQL.append(" ( ");
Class<?> primaryClazz = id.getDataType();
if (primaryClazz == int.class || primaryClazz == Integer.class
|| primaryClazz == long.class || primaryClazz == Long.class) {
strSQL.append(id.getColumn()).append(" INTEGER PRIMARY KEY AUTOINCREMENT,");
} else {
strSQL.append(id.getColumn()).append(" TEXT PRIMARY KEY,");
}
Collection<Property> propertys = table.propertyMap.values();
for (Property property : propertys) {
strSQL.append(property.getColumn());
Class<?> dataType = property.getDataType();
if (dataType == int.class || dataType == Integer.class
|| dataType == long.class || dataType == Long.class) {
strSQL.append(" INTEGER");
} else if (dataType == float.class || dataType == Float.class
|| dataType == double.class || dataType == Double.class) {
strSQL.append(" REAL");
} else if (dataType == boolean.class || dataType == Boolean.class) {
strSQL.append(" NUMERIC");
}
strSQL.append(",");
}
Collection<ManyToOne> manyToOnes = table.manyToOneMap.values();
for (ManyToOne manyToOne : manyToOnes) {
strSQL.append(manyToOne.getColumn())
.append(" INTEGER")
.append(",");
}
strSQL.deleteCharAt(strSQL.length() - 1);
strSQL.append(" )");
return strSQL.toString();
}
greenDAO
greenDAO與上述兩種ORM框架不同,其原理不是根據反射進行資料庫的各項操作,而是一開始就人工生成業務需要的Model和DAO檔案,業務中可以直接呼叫相應的DAO檔案進行資料庫的增刪改查操作,從而避免了因反射帶來的效能損耗和效率低下。以查詢為例,其程式碼如下:
DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
db = helper.getWritableDatabase();
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();
cursor = db.query(noteDao.getTablename(), noteDao.getAllColumns(), null, null, null, null, orderBy);
首先先建立資料庫,然後在SQLiteOpenHelper.onCreate方法中根據已生成的model建立所有的表,而db.query其實就是Android原生的查詢操作,只不過引數是經過DAO檔案處理過的,無需手動匹配。其他資料庫操作與查詢雷同。
相關推薦
Android資料庫ORM框架用法、原始碼和效能比較分析
基本用法 LitePal LitePal是一款開源的Android資料庫框架,它採用了物件關係對映(ORM)的模式,LitePal很“輕”,jar包只有100k不到,使用起來也比較簡單,原始碼地址為Github地址。 首先需要引入lib,可以通過g
Android 資料庫ORM框架——GreenDao
最近在對開發專案的效能進行優化。由於專案裡涉及了大量的快取處理和資料庫運用,需要對資料庫進行頻繁的讀寫、查詢等操作。因此首先想到了對整個專案的資料庫框架進行優化。 原先使用android本身內建的sqllite,也就是用的最基本的SQLiteOpenHelper方法,這種方法對自己來說比較方便易懂。但是
Android框架MVC、MVP和MVVM探究(圖解+案例+附原始碼)
1、介紹 MVC、MVP、MVVM這些模式是為了解決開發過程中的實際問題而提出來的,目前作為主流的幾種架構模式而被廣泛使用。本文程式碼 2、瞭解並區分MVC,MVP,MVVM 2.1 MVC MVC,(Model View Controller)
Android常用資料庫ORM框架ORMlite和GreenDao比較
一、關於ORM 物件關係對映(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程式技術,用於實現面向物件程式語言裡不同型別系統的資料之間的轉換。從效果上說,它其實是建立了一個可在程式語言裡使用的“虛擬物件資料庫”
Android資料庫高手祕籍 二 建立表和LitePal的基本用法
上一篇文章中我們學習了一些Android資料庫相關的基礎知識,和幾個頗為有用的SQLite命令,都是直接在命令列操作的。但是我們都知道,資料庫是要和程式結合在一起使用的,單獨對一個數據庫去進行増刪改查操作並沒有什麼意義,因此今天我們就來學習一下如何在Android程式當中去操作
Android資料庫高手祕籍(二)——建立表和LitePal的基本用法
上一篇文章中我們學習了一些Android資料庫相關的基礎知識,和幾個頗為有用的SQLite命令,都是直接在命令列操作的。但是我們都知道,資料庫是要和程式結合在一起使用的,單獨對一個數據庫去進行増刪改查操作並沒有什麼意義,因此今天我們就來學習一下如何在Android程式當中去操
Android 資料庫ORM開源框架之greenDAO
我相信,在平時的開發過程中,大家一定會或多或少地接觸到 SQLite。然而在使用它時,我們往往需要做許多額外的工作,像編寫 SQL 語句與解析查詢結果等。所以,適用於 Android 的ORM 框架也就孕育而生了,現在市面上主流的框架有 OrmLite、Suga
Android開源:資料庫ORM框架GreenDao學習(三)封裝工具類使用
部落格轉載地址:http://www.it165.net/pro/html/201401/9026.html 上一篇中講解了基本的增刪改查,本篇繼續講解 QureyBuilder 使用,及工具類封裝使用 一、使用QureyBuilder實現表的增刪改查
Android 圖片載入框架Picasso基本使用和原始碼完全解析
寫在之前 原本打算是每週更新一篇博文,同時記錄一週的生活狀態,但是稍微工作忙一點就顧不上寫部落格了。悲催 還是說下最近的狀況,最近兩週一直在接公司申請的計費點, 沃商店,銀貝殼,微信等等,然後就是不停的被人催促催促,真是一個頭兩個大。在這期間專案組還搞了個A
手把手帶你擼一套Android簡易ORM框架
ORM概念 實體模型建立 註解列 ID 主鍵 自增長 資料表的列
Android 訊息機制:Handler、MessageQueue 和 Looper
在這篇文章中,我們將會討論 Android 的訊息機制。提到 Handler,有過一些 Android 開發經驗的都應該很清楚它的作用,通常我們使用它來通知主執行緒更新 UI。但是 Handler 需要底層的 MessageQueue 和 Looper 來支援才能運作。這篇文章中,我們將會討論它們三個之間的關
Android 記憶體快取框架 LruCache 的原始碼分析
LruCache 是 Android 提供的一種基於記憶體的快取框架。LRU 是 Least Recently Used 的縮寫,即最近最少使用。當一塊記憶體最近很少使用的時候就會被從快取中移除。在這篇文章中,我們會先簡單介紹 LruCache 的使用,然後我們會對它的原始碼進行分析。 1、基本的使用示例
資料庫直接生成實體、dao和mapper
使用Mybatis Generator <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.o
Android訊息機制(Handler、MessageQueue和Looper三者的工作原理)
Android的訊息機制主要是指Handler的執行機制以及Handler所附帶的MessageQueue和Looper的工作過程。messagequeue意思是訊息佇列,它內部儲存一組訊息,有插入和刪除的功能,其實內部是以單鏈表的形式來實現佇列功能的。looper的意思是迴圈,它的主要功能是迴
Android——EditText自定義邊框、圓角和其常用屬性總結
看下效果圖: 執行步驟: 首先在/res/layout資料夾下建立custom_et_layout.xml佈局檔案,原始碼如下: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:
Android深入學習之requestLayout、invalidate和postInvalidate的區別
Android開發離不開介面的重繪和更新,尤其是自定義控制元件更是經常遇到重繪更新介面的場景,下面具體介紹一下重繪介面幾個方法的區別: (1)、requestLayout:
python Scrapy框架1—框架流程、結構和一個簡單的例子
python爬蟲學習_Scrapy框架1—框架流程、結構和一個簡單的例子 框架圖 Scrapy Engine(引擎): 負責Spider、ItemPipeline、Downloader、Scheduler中間的通訊,訊號、資料傳遞等。 Scheduler(排程器)
java io系列14:DataInputStream(資料輸入流)的認知、原始碼和示例
本章介紹DataInputStream。我們先對DataInputStream有個大致認識,然後再深入學習它的原始碼,最後通過示例加深對它的瞭解。 轉載請註明出處:http://www.cnblogs.com/skywang12345/p/io_14.html DataInputStream
Android依賴注入框架三、AndroidAnnotations
AndroidAnnotations是一個能夠讓你快速進行Android開發的開源框架,它能讓你專注於真正重要的地方。使程式碼更加精簡,使專案更容易維護。相比原生的Android App程式碼量,幾乎可以少一半。 用com.github.barteksc:and
Oracle資料庫中的函式、檢視和索引
1. Oracle函式 Oracle SQL 提供了用於執行特定操作的專用函式。這些函式大大增強了 SQL 語言的功能。函式可以接受零個或者多個輸入引數,並返回一個輸出結果。 Oracle 資料庫中主要使用兩種型別的函式: 1. 單行函式: 對每一個函式應用在表的記錄中時,只能輸入一