大致分為以下幾個方面: 一些查詢指令整理 使用SQL語句進行特殊查詢 檢測表欄位是否存在 資料庫升級 資料庫表欄位賦初始值 一、查詢指令整理 1.鏈式執行的指令 一般的查詢語句會在中間xxx的位置加上各種判斷和過濾的方法指令,除了最後的終結指令list()或unique()返回的是集合或業務對象,其 ...
大致分為以下幾個方面:
- 一些查詢指令整理
- 使用SQL語句進行特殊查詢
- 檢測表欄位是否存在
- 資料庫升級
- 資料庫表欄位賦初始值
一、查詢指令整理
1.鏈式執行的指令
return mDaoSession.getUserDao().queryBuilder(). XXX. XXX. XXX. list();
一般的查詢語句會在中間xxx的位置加上各種判斷和過濾的方法指令,除了最後的終結指令list()或unique()返回的是集合或業務對象,其他的都是返回QueryBuilder對象,大多數情況下XXX的位置都是填上where語句,還有一些其他和sql關聯的語句便於使用。
“whereOr” where語句裡面寫的條件都是用“且”連接,whereOr里的語句使用“或”連接
“distinct” 直接過濾掉重負欄位
“limit” 分頁n個一頁,一般和offset結合使用
“offset” 忽略查詢出的前n條結果
“orderAsc” 以欄位升序排序
“orderDesc”以欄位降序
“preferLocalizedStringOrder” 本地化字元串排序
“orderCustom” 自定義排序 裡面需要傳兩個參數: 一個屬性 和對應的排序方案 ASC 或是 DESC
“orderRaw” 也是自定義排序, 把欄位和 排序方案 寫在一個字元串傳入
“stringOrderCollation” 也是自定義排序 可以合併多個升降排序方案 以日期升序 且 價格降序
2.條件里的指令
return mDaoSession.getUserDao().queryBuilder(). where(UserDao.Properties.UserId.in(userIdList), UserDao.Properties.UserAge.eq(19)). list();
一個簡單的where語句大概是這樣,在where的括弧裡面可以併列的寫很多條件,其中全部以“且” 來連接。除了上面的“in”和“eq”還有很多其他判斷條件
“notEq” 和eq相反,別傻傻在再去外面敲“!”取反
“notIn” 同上
“or” 或者
“like” 就是sql語句的LIKE "%"+string+"%"
“between” 也就是BETWEEN ? AND ? 可以取兩個值的區間 (但是這條語句要慎用,不同的資料庫不一樣,有的是A<條件<B,有的是A<=條件<=B)
“gt” 相當於 >
“ge”相當於 >=
“lt” 相當於 <
“le”相當於 <=
“isNull” 為空
“notIsNull” 不為空
二、使用SQL語句進行特殊查詢
一般遇到普通的增刪改查操作無法輕易實現的功能,會使用這種rawQuery的方式。 我經常遇到的也就是兩種場景:
1.使用SELECT DISTINCT
常用與一對多關係,假設圖書館現在有個“用戶存書表” 有的用戶有20本書,表裡就會有20條他的數據,每條對應一本不同的書。
這時假設有個需求,查出這個表中,所有的用戶名字,不許重覆。 這時候用普通的查詢指令就會非常麻煩了,需要使用SELECT DISTINCT指令查出某列名所有不重覆的條目。
String queryString = "SELECT DISTINCT " + UserBookDao.Properties.UserName.columnName + " FROM " + UserBookDao.TABLENAME + " ORDER BY " + UserBookDao.Properties.CreatedTime + " DESC " + " LIMIT " + page * LIMIT_NUM + " , " + LIMIT_NUM; ArrayList<String> result = new ArrayList<>(); Cursor c = mDaoSession.getDatabase().rawQuery(queryString,new String[]{}); try { if (c != null) { if (c.moveToFirst()) { do { result.add(c.getString(0)); } while (c.moveToNext()); } } } finally { if (c != null) { c.close(); } }
大概代碼的畫風就是這個樣子,先拼接出一個符合sql語法的字元串,上面也隨機加了一寫其他的操作指令欄位排序和分頁,使得查詢指令看上去更加完整,然後使用游標來接收上面的查詢結果。
可能大多數查詢的時候會帶上一些參數,比如where XX = XX 的過濾條件,假設有個需求需要在之前的查詢用戶需求上加上對出版社和圖書價格的限制,則查詢方式如下
String queryString = "SELECT DISTINCT " + UserBookDao.Properties.UserName.columnName + " FROM " + UserBookDao.TABLENAME // 董鉑然博客園 + " WHERE " + UserBookDao.Properties.Publisher.columnName + " = ?" + " AND " + UserBookDao.Properties.Price.columnName + " > ?" + " ORDER BY " + UserBookDao.Properties.CreatedTime + " DESC "; ArrayList<String> result = new ArrayList<>(); Cursor c = mDaoSession.getDatabase().rawQuery(queryString, new String[]{"某出版社"), String.valueOf(100.00)}); try { if (c != null) { if (c.moveToFirst()) { do { result.add(c.getString(0)); } while (c.moveToNext()); } } } finally { if (c != null) { c.close(); } }
帶上參數的查詢字元串需要在上面使用問號占位,然後在下麵用rawQuery帶參數的api里填上相關的入參。
2. SELECT同時查詢多個欄位
還是用這個查書的例子,假設一下要查“書名”、“出版社”、“價格” 三個欄位
String queryString = "SELECT " + UserBookDao.TABLENAME + "." + UserBookDao.Properties.BookName.columnName + "," + UserBookDao.TABLENAME + "." + UserBookDao.Properties.Publisher.columnName + "," + UserBookDao.TABLENAME + "." + UserBookDao.Properties.Price.columnName + " " + "FROM " + UserBookDao.TABLENAME + " " + "WHERE" + UserBookDao.Properties.Price + " > 100.00 "; Cursor cursor = null; try { cursor = session.getDatabase().rawQuery(queryString,new String[]{}); if (cursor == null) { return payMap; } // 取出三個欄位分別對應的索引,下麵再對著索引去取值 int nameIndex = cursor.getColumnIndex(UserBookDao.Properties.BookName.columnName); int publisherIndex = cursor.getColumnIndex(UserBookDao.Properties.Publisher.columnName); int priceIndex = cursor.getColumnIndex(UserBookDao.Properties.Price.columnName); if (nameIndex != -1 && publisherIndex != -1 && priceIndex != -1) { while (cursor.moveToNext()) { String name = cursor.getString(nameIndex); String publisher = cursor.getString(publisherIndex); Double price = cursor.getDouble(priceIndex); // 這裡取到三個欄位 自己是存模型還是字典 自己處理。 } } } finally { if (null != cursor) { cursor.close(); } }
下麵可以一次性的取出三個所需欄位進行使用,需要先得到欄位對應索引,然後再對著索引取值。
三、檢測表欄位是否存在
private boolean hasColumn(SQLiteDatabase db, String tableName, String column) { if (TextUtils.isEmpty(tableName) || TextUtils.isEmpty(column)) { return false; } Cursor cursor = null; try { cursor = db.query(tableName, null, null, null, null, null, null); if (null != cursor && cursor.getColumnIndex(column) != -1) { return true; } } finally { if (null != cursor) { cursor.close(); } } return false; }
和上面取游標的方式類似,取出的列索引值如果不是-1,則代表能夠取到這個欄位返回true,否則和入參非法一起返回fasle。
四、資料庫升級
這邊會調用上面判斷列名是否已經存在的方法。
然後重寫父類的這個onUpgrade方法
private static class DemoOpenHelper extends DaoMaster.OpenHelper { public DemoOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { super(context, name, factory); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 資料庫的版本控制 可以隨著版本疊加不斷增加差值 if (oldVersion < 2) { if (!hasColumn(db, UserBookDao.TABLENAME, UserBookDao.Properties.Author.columnName)) { String sql = "alter table " + UserBookDao.TABLENAME + " add COLUMN " + UserBookDao.Properties.Author.columnName + " TEXT"; db.execSQL(sql); } if (!hasColumn(db, UserBookDao.TABLENAME, UserBookDao.Properties.Type.columnName)) { String sql = "alter table " + UserBookDao.TABLENAME + " add COLUMN " + UserBookDao.Properties.Type.columnName + " INTEGER"; db.execSQL(sql); } } } }
除了上面的修改表,如果改動太多或是換了表明,還可以直接刪了重建(這麼做的話之前的數據也就刪了)
if (oldVersion < 3) { UserDao.dropTable(new StandardDatabase(db),true); UserStudentDao.createTable(new StandardDatabase(db),true); }
五、資料庫表欄位賦初始值
有些欄位的初始值如果你不希望是0,或是空字元串,可以賦初始值。現在的賦值初始值就分為兩種情況了
1.建表時附初始值
在3.0以前的版本 還是要寫一個module,裡面寫類似代碼來建表的
private static void addUser(Schema schema) { Entity user = schema.addEntity("User"); user.addIdProperty(); user.addStringProperty("name").notNull().defValue("\"jack\""); user.addStringProperty("address"); user.addStringProperty("teacher"); user.addIntProperty("age").primJavaType().defValue("17"); }
在3.0之後推行用註解和直接寫Entity的寫法,所以可以直接在Entity的類里指定
@Entity public class Student { @Id(autoincrement = true) private long id; //主鍵 private String name; private String schoolTime = "09-01"; //開學時間預設都是9月1日 private int age = 19; // 剛上大學的預設都是19歲 // 下麵生成的getter 和setter省略 。。。 }
2.資料庫升級時給升級欄位賦初始值
上面說的的資料庫升級,是需要寫一條alter table的sql語句,可以直接拼接到這一行後面
// 上面判斷該列名是否存在 // ... String sql = "alter table " + UserBookDao.TABLENAME + " add COLUMN " + UserBookDao.Properties.Type.columnName + " INTEGER" + " NOT NULL DEFAULT(-1) "; // 直接拼接在語句最後 董鉑然博客園 db.execSQL(sql); // ...
註:以前聽過一種說法是直接在UserDao這種生成的類里直接在生成的創建語句後面拼接DEFAULT,這裡非常反對, 首先人家類名明確表明 // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. 並且有些人的代碼可能設置的是手動生成,但我們的項目就在gradle里設置了每次build都會先自動生成一下,這種情況每次都會覆蓋。
開發中一些整理,歡迎討論。