Include the list of entities associated with the database within the annotation.
Contain an abstract method that has 0 arguments and returns the class that is annotated with @Dao.
Entity
Represents a table within the database.
DAO
Contains the methods used for accessing the database.
Note: You should follow the singleton design pattern when instantiating an AppDatabase object, as each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances.
// Top-level build file where you can add configuration options common to all sub-projects/modules.buildscript{repositories{google()jcenter()}dependencies{classpath'com.android.tools.build:gradle:3.0.1'// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files}}allprojects{repositories{google()jcenter()}}taskclean(type:Delete){deleterootProject.buildDir}
packagecom.wispeedio.dbtesting;importandroid.arch.persistence.room.Dao;importandroid.arch.persistence.room.Insert;importandroid.arch.persistence.room.Query;importjava.util.List;/**
*
*/@DaopublicinterfaceCommandDao{@Insertlong[]insertAll(Command...commands);@Query("SELECT * FROM "+Command.TABLE_NAME)Command[]loadAll();@Query("SELECT COUNT(*) FROM "+Command.TABLE_NAME)intcount();}
packagecom.wispeedio.dbtesting;importandroid.arch.persistence.db.SupportSQLiteOpenHelper;importandroid.arch.persistence.room.Database;importandroid.arch.persistence.room.DatabaseConfiguration;importandroid.arch.persistence.room.InvalidationTracker;importandroid.arch.persistence.room.Room;importandroid.arch.persistence.room.RoomDatabase;importandroid.arch.persistence.room.TypeConverters;importandroid.content.Context;/**
*
*/@Database(entities={Command.class},version=1)@TypeConverters({Converters.class})publicabstractclassAppDatabaseextendsRoomDatabase{/** The only instance */privatestaticAppDatabasesInstance;/**
* Gets the singleton instance of Database
* @param context
* @return The singleton instance of AppDatabase.
*/publicstaticsynchronizedAppDatabasegetInstance(Contextcontext){if(sInstance==null){sInstance=Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class,"appdb").build();}returnsInstance;}/**
* @return The DAO for the Command table.
*/publicabstractCommandDaocommand();}
dependencies{// ViewModel and LiveDataimplementation"android.arch.lifecycle:extensions:1.1.0"// alternatively, just ViewModelimplementation"android.arch.lifecycle:viewmodel:1.1.0"// alternatively, just LiveDataimplementation"android.arch.lifecycle:livedata:1.1.0"annotationProcessor"android.arch.lifecycle:compiler:1.1.0"// Room (use 1.1.0-alpha1 for latest alpha)implementation"android.arch.persistence.room:runtime:1.0.0"annotationProcessor"android.arch.persistence.room:compiler:1.0.0"// Pagingimplementation"android.arch.paging:runtime:1.0.0-alpha5"// Test helpers for LiveDatatestImplementation"android.arch.core:core-testing:1.1.0"// Test helpers for RoomtestImplementation"android.arch.persistence.room:testing:1.0.0"}
简单例子,1个 entity ,1个 DAO
User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@EntitypublicclassUser{@PrimaryKeyprivateintuid;@ColumnInfo(name="first_name")privateStringfirstName;@ColumnInfo(name="last_name")privateStringlastName;// Getters and setters are ignored for brevity,// but they're required for Room to work.}
UserDao.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@DaopublicinterfaceUserDao{@Query("SELECT * FROM user")List<User>getAll();@Query("SELECT * FROM user WHERE uid IN (:userIds)")List<User>loadAllByIds(int[]userIds);@Query("SELECT * FROM user WHERE first_name LIKE :first AND "+"last_name LIKE :last LIMIT 1")UserfindByName(Stringfirst,Stringlast);@InsertvoidinsertAll(User...users);@Deletevoiddelete(Useruser);}
you can tell SQLite to delete all books for a user if the corresponding instance of User is deleted by including onDelete = CASCADE in the @ForeignKey annotation.
SQLite handles @Insert(onConflict = REPLACE) as a set of REMOVE and REPLACE operations instead of a single UPDATE operation.
classAddress{publicStringstreet;publicStringstate;publicStringcity;@ColumnInfo(name="post_code")publicintpostCode;}// The table representing a User object then contains columns with the following names: id, firstName, street, state, city, and post_code.@EntityclassUser{@PrimaryKeypublicintid;publicStringfirstName;@EmbeddedpublicAddressaddress;}
Accessing data using Room DAOs
Insert
When you create a DAO method and annotate it with @Insert, Room generates an implementation that inserts all parameters into the database in a single transaction.
If the @Insert method receives only 1 parameter, it can return a long, which is the new rowId for the inserted item
If the parameter is an array or a collection, it should return long[] or List instead.
Each @Query method is verified at compile time, so if there is a problem with the query, a compilation error occurs instead of a runtime failure.
It gives a warning if only some field names match.
It gives an error if no field names match
1
2
3
4
5
@DaopublicinterfaceMyDao{@Query("SELECT * FROM user")publicUser[]loadAllUsers();}
Passing parameters into the query
1
2
3
4
5
@DaopublicinterfaceMyDao{@Query("SELECT * FROM user WHERE age > :minAge")publicUser[]loadAllUsersOlderThan(intminAge);}
1
2
3
4
5
6
7
8
9
@DaopublicinterfaceMyDao{@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")publicUser[]loadAllUsersBetweenAges(intminAge,intmaxAge);@Query("SELECT * FROM user WHERE first_name LIKE :search "+"OR last_name LIKE :search")publicList<User>findUserWithName(Stringsearch);}
Returning subsets of columns
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// create the following plain old Java-based object (POJO) to fetch the user's first name and last name:publicclassNameTuple{@ColumnInfo(name="first_name")publicStringfirstName;@ColumnInfo(name="last_name")publicStringlastName;}// use this POJO in your query method:@DaopublicinterfaceMyDao{@Query("SELECT first_name, last_name FROM user")publicList<NameTuple>loadFullName();}
Passing a collection of arguments
1
2
3
4
5
@DaopublicinterfaceMyDao{@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")publicList<NameTuple>loadUsersFromRegions(List<String>regions);}
Observable queries
Room generates all necessary code to update the LiveData when the database is updated.
Reactive queries with RxJava
Room can also return RxJava2 Publisher and Flowable objects from the queries you define. To use this functionality, add the android.arch.persistence.room:rxjava2 artifact from the Room group into your build Gradle dependencies. You can then return objects of types defined in RxJava2, as shown in the following code snippet:
1
2
3
4
5
@DaopublicinterfaceMyDao{@Query("SELECT * from user where id = :id LIMIT 1")publicFlowable<User>loadUserById(intid);}
For more details, see the Google Developers Room and RxJava article.
Direct cursor access
1
2
3
4
5
@DaopublicinterfaceMyDao{@Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")publicCursorloadRawUsersOlderThan(intminAge);}
Querying multiple tables
1
2
3
4
5
6
7
8
@DaopublicinterfaceMyDao{@Query("SELECT * FROM book "+"INNER JOIN loan ON loan.book_id = book.id "+"INNER JOIN user ON user.id = loan.user_id "+"WHERE user.name LIKE :userName")publicList<Book>findBooksBorrowedByNameSync(StringuserName);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// You can also return POJOs from these queries. For example, you can write a query that loads a user and their pet's name as follows:@DaopublicinterfaceMyDao{@Query("SELECT user.name AS userName, pet.name AS petName "+"FROM user, pet "+"WHERE user.id = pet.user_id")publicLiveData<List<UserPet>>loadUserAndPetNames();// You can also define this class in a separate file, as long as you add the// "public" access modifier.staticclassUserPet{publicStringuserName;publicStringpetName;}}
Migrating Room databases
Each Migration class specifies a startVersion and endVersion.
At runtime, Room runs each Migration class’s migrate() method, using the correct order to migrate the database to a later version.
Caution: If you don’t provide the necessary migrations, Room rebuilds the database instead, which means you’ll lose all of your data in the database.
Upon compilation, Room exports your database’s schema information into a JSON file.
To export the schema, set the room.schemaLocation annotation processor property in your build.gradle file, as shown in the following code snippet:
To test these migrations, add the android.arch.persistence.room:testing Maven artifact from Room into your test dependencies, and add the schema location as an asset folder, as shown in the following code snippet:
@RunWith(AndroidJUnit4.class)publicclassMigrationTest{privatestaticfinalStringTEST_DB="migration-test";@RulepublicMigrationTestHelperhelper;publicMigrationTest(){helper=newMigrationTestHelper(InstrumentationRegistry.getInstrumentation(),MigrationDb.class.getCanonicalName(),newFrameworkSQLiteOpenHelperFactory());}@Testpublicvoidmigrate1To2()throwsIOException{SupportSQLiteDatabasedb=helper.createDatabase(TEST_DB,1);// db has schema version 1. insert some data using SQL queries.// You cannot use DAO classes because they expect the latest schema.db.execSQL(...);// Prepare for the next version.db.close();// Re-open the database with version 2 and provide// MIGRATION_1_2 as the migration process.db=helper.runMigrationsAndValidate(TEST_DB,2,true,MIGRATION_1_2);// MigrationTestHelper automatically verifies the schema changes,// but you need to validate that the data was migrated properly.}}
Test on an Android device
The recommended approach for testing your database implementation is writing a JUnit test that runs on an Android device. Because these tests don’t require creating an activity, they should be faster to execute than your UI tests.
When setting up your tests, you should create an in-memory version of your database to make your tests more hermetic, as shown in the following example:
Next, you add the @TypeConverters annotation to the AppDatabase class so that Room can use the converter that you’ve defined for each entity and DAO in that AppDatabase:
Using these converters, you can then use your custom types in other queries, just as you would use primitive types
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// User.java@EntitypublicclassUser{...privateDatebirthday;}// UserDao.java@DaopublicinterfaceUserDao{...@Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")List<User>findUsersBornBetweenDates(Datefrom,Dateto);}