android官方架构room,Android 官方架构组件介绍之 Room(翻译)
持久庫Room
Room在SQLite上提供了一個抽象層,以便在利用SQLite的全部功能的同時使流暢的數據庫訪問。
需要處理一些重要的結構化數據的App通常會從本地的持久數據中受益匪淺。最常見的就是使用本地緩存,這樣的話下次如果設備無法聯網用戶也能瀏覽本地數據并進行更改。等下次聯網后再和服務器進行同步。
Android的Framework為了支持處理原始SQL而提供了SQLite這一強大的API,當時SQLite的API還是相對比較低級,在使用的時候需要花費大量的經歷:
沒有對原始SQL語句的編譯時驗證,隨著數據庫表格的更改,你需要更新相關SQL操作,而這個過程可能耗時且容易出錯。
你需要使用大量的樣板代碼在SQL查詢和Java數據對象之間進行轉換。
Room在為SQL提供抽象層的同時也會考慮到上述的問題。
下面是Room中三個主要組件:
Database:此組件用于創建數據庫的持有者,同時在類層級上使用注解來定義一系列的Entity,這些Entity對應著數據庫中的表格。Database類中的方法則用來獲取對應的DAO列表。Database是App層與底層SQLite之間的連接點。
在應用中要使用此組件的話需要繼承RoomDatabase。然后通過Room.databaseBuilder()或者Room.inMemoryDatabaseBuilder().獲得該類的實例。(講到這里其實讀者可以發現,這不就是GreenDao嗎?😂)。
Entity:此組件的一個實例表示數據庫的一行數據,對于每個Entity類來說,都會有對應的table被創建。想要這些Entity被創建,就需要寫在上面Database的注解參數entities列表中。默認Entity中的所有字段都會拿來創建表,除非在該字段上加上@Ignore注解。
注意:Entity默認都只有空的構造方法(如果DAO類可以訪問每個持久化字段),或者構造方法的參數與Entity中的字段的類型和名字相匹配。Room可以使用全字段構造方法,也可以使用部分字段構造方法。
DAO:這個組件用來表示具有Data Access Object(DAO)功能的類或接口。DAO類是Room的重要組件,負責定義訪問數據庫的方法。繼承RoomDatabase的類必須包含一個0參數且返回DAO類的方法。當在編譯期生成代碼的時候,Room會創建實現此DAO的類。
注意:通過使用DAO類而不是傳統的查詢接口來訪問數據庫,可以做到數據庫組件的分離。同時DAO可以在測試APP時支持Mock數據。
下面是其三者和數據庫的關系圖:
下面看一下簡單的實例,其包含一個Entity,一個Dao以及一個Database。
User.java
@Entity
public class User{
@PrimaryKey
private int uid;
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
// Getters and setters are ignored for brevity,
// but they're required for Room to work.
}
UserDao.java
@Dao
public interface UserDao{
@Query("SELECT * FROM user")
List getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
AppDatabase.java
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase{
public abstract UserDao userDao();
}
當創建完這些文件后,你就可以使用下面的方法來獲得被創建的AppDatabase實例:
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
注意:實例化AppDatabase對象時,應遵循單例設計模式,因為每個數據庫實例都相當昂貴,而且很少需要訪問多個實例。
Entity
當一個類被添加了@Entity注解并且在Database的@entities被引用,Room就會為其創建對應的數據庫。
默認情況Room會為Entity的每個字段創建對應的數據庫列,如果某個字段不想被創建的話可以使用@Ignore注解:
@Entity
class User{
@PrimaryKey
public int id;
public String firstName;
public String lastName;
@Ignore
Bitmap picture;
}
為了Room可以訪問到Entity的字段,你可以將這些字段聲明為public,或者可以給這些字段提供setter和getter方法。如果使用setter和getter的話,需要注意命名規則。具體參照Java Beans。
Primary key
每個Entity至少定義一個主鍵,即使你的Entity只有一個字段也是如此。定義主鍵使用@PrimaryKey。如果你想讓Room給你的Entity自動生成ID的話,可以使用@Primary的autoGenerate屬性。如果Entity具有復合主鍵的話,可以使用@Entity的primaryKeys屬性,參照下方代碼:
@Entity(primaryKeys = {"firstName", "lastName"})
class User{
public String firstName;
public String lastName;
@Ignore
Bitmap picture;
}
默認情況Room使用Entity的類名來作為數據庫的表名。如果想自定義表名,可以使用@Entity的tableName屬性,如下:
@Entity(tableName = "users")
class User{
...
}
注意:SQLite中的表名是大小寫不敏感的。
與上面的tableName類似,Room使用Entity的字段名來作為對應的列名,如果想要自定義類名,可以使用@ColumnInfo注解的name屬性,如下:
@Entity(tableName = "users")
class User{
@PrimaryKey
public int id;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
@Ignore
Bitmap picture;
}
索引及唯一性
在適當的字段上添加索引可以加快數據庫的訪問速度,要在Entity上添加索引可以使用@Entity的indices屬性,可以添加索引或組合索引:
@Entity(indices = {@Index("firstName"), @Index("last_name", "address")})
class User{
@PrimaryKey
public int id;
public String firstName;
public String address;
@ColumnInfo(name = "last_name")
public String lastName;
@Ignore
Bitmap picture;
}
有些情況下,數據庫中的某個字段或字段組合必須是唯一的,可以通過將@Index的屬性unique設置為ture來實現這一唯一性。以下代碼用于放置User表中出現姓名組合相同的數據。
@Entity(indices = {@Index(value = {"first_name", "last_name"},
unique = true)})
class User{
@PrimaryKey
public int id;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
@Ignore
Bitmap picture;
}
表間關系
由于SQLite是關系型數據庫,所以你可以指定對象之間的關系,但在Room中這是命令禁止的。
雖然在Room中的Entity不能有直接的引用關系,但Room任然支持在Entity間定義Foreign Key。
例如有個另一個Entity叫做Book,你可以使用@ForeignKey來定義它和User之間的關系,如下:
@Entity(foreignKeys = @ForeignKey(entity = User.class,
parentColumns = "id",
childColumns = "user_id"))
class Book{
@PrimaryKey
public int bookId;
public String title;
@ColumnInfo(name = "user_id")
public int userId;
}
外鍵是十分強大的,它允許你指定引用實體發生更新是發生的行為,比如,當需要刪除一個用戶的時候刪除其下所有的圖書,只需要為Book的@ForeignKey的屬性onDelete設置為CASCADE。
注意:SQLite在處理@Insert(onConflict=REPLACE)的時候,其實是進行了REMOVE和REPLACE兩個操作,而不是單單的UPDATE。此時這里的REMOVE操作可能會影響到對應的外鍵,
嵌套對象
有時你需要在數據庫邏輯中表達一個實體或者Java類,你可以使用@Embedded注解來實現。具體看例子。
例如上面的User實體有一個Address類型的字段,Address包含了street,city,state和postCode這幾個字段。當生成表格時,Address中的字段將被分別定義為User表中的列名。如下:
class Address{
public String street;
public String state;
public String city;
@ColumnInfo(name = "post_code")
public int postCode;
}
@Entity
class User{
@PrimaryKey
public int id;
public String firstName;
@Embedded
public Address address;
}
這是User表包含以下字段:id, firstName, street, state, city和post_code。
注意:以上是可以多重嵌套的。
如果User中嵌套的A和B中存在相同字段,可以使用@Embedded的prefix屬性,Room會在生成table的時候將prefix的值加在列名前。
Data Access Objects (DAOs)
Room中的主要組件就是Dao,DAO以簡潔的方式抽象訪問數據庫。
Intert
當你創建了一個DAO的方法并加上@Insert注解,Room就會生成一個這個方法是實現,用于完成此次插入操作:
@Dao
public interface MyDao{
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertUsers(User... users);
@Insert
public void insertBothUsers(User user1, User user2);
@Insert
public void insertUsersAndFriends(User user, List friends);
}
如果插入方法只接受一個參數的話,表示僅僅插入一條數據,這是這個方法可以返回一個long型值,為新行的id。如果參數為數組或集合,則需要返回對應的long[]或者List。
Update
Update是一個用于更新批量數據的實用方法,它通過主鍵來匹配需要更改數據庫數據:
@Dao
public interface MyDao{
@Update
public void updateUsers(User... users);
}
此方法可以返回一個int型數據,表示此次修改影響到的行數。
DELETE
Delete用于批量刪除數據庫中的數據,它也是通過主鍵來匹配需要刪除的數據:
@Dao
public interface MyDao{
@Delete
public void deleteUsers(User... users);
}
此方法可以返回一個int型數據,表示此次刪除的行數。
QUERY
@Query是DAO中的一個重要注解,它允許你對數據庫進行讀寫操作。每一個@Query方法都會在編譯期做校驗,所以如果query存在問題的話,你的App編譯將無法通過。
Room同時也會校驗query的返回值,如果返回結果和查詢語句中的結果不匹配,Room將會以一下兩種方式提醒你:
如果有部分字段匹配的話會給出警告。
如果沒有字段匹配,則給出錯誤提示。
簡單的查詢
@Dao
public interface MyDao{
@Query("SELECT * FROM user")
public User[] loadAllUsers();
}
這是一個加載所有用戶的查詢,寫法比較簡單。在編譯期,Room知道需要查詢User的所有列的值。如果查詢語句包含語法錯誤或者沒有user這個表,則Room會在編譯時期報錯并給出錯誤信息。
查詢的參數傳遞
大部分情況,你需要給查詢語句傳遞特定的參數,比如查詢特定年齡段的User,如下:
@Dao
public interface MyDao{
@Query("SELECT * FROM user WHERE age > :minAge")
public User[] loadAllUsersOlderThan(int minAge);
}
在編譯器處理這個查詢操作的時候,Room會將參數minAge與:minAge進行綁定。如果此時無法匹配,則會出現編譯錯誤。
總結
以上是生活随笔為你收集整理的android官方架构room,Android 官方架构组件介绍之 Room(翻译)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android+studio+选择+苹方
- 下一篇: android布局的属性大全,Andro