Room is part of the database framework in Google’s JetPack component library
Analysis of the
- Realm
- High performance, 10 times faster than SQLite
- Support RxJava/Kotlin
- But without nested classes and requiring fields to specify default values, I find nested data classes indispensable
- Custom database engine, so it will require the import of JNI library, resulting in apK volume explosion (multiple architecture platform integration more than 5MB)
- Function design is complicated
- The official graphics tools are relatively crude but up-to-date
- Cross-platform, Android/iOS/Mac/Windows
- Support for listening databases
- DBFlow
- The main use of functions to operate the database, high learning costs
- Native support for database encryption
- Support for listening databases
- Support coroutine /Kotlin/RxJava
- Out of favor, especially domestically
- GreenDao
- Relatively outdated, complex configuration
- Features such as listening tables /Kotlin/ coroutines are not supported
- No longer actively maintain, the official is currently actively maintain its other open source database ObjectBox
- ObjectBox 2. The world’s fastest embedded database 2. No support for nested objects 3. Small size (minimum compression to increase volume 1MB) 4. Simple and elegant function design 5. Support DSL 6. Support to listen to the database 7. 9. JSON file automatically generated according to the configuration migration cross-platform, Android/iOS/Mac/Windows/Go
- ROOM
- The mainstream
- SQL statement support
- Manipulating a database requires writing abstract functions
- Official maintenance of the database framework in JetPack components
- Listening database
- Support for nested objects
- Support for Kotlin coroutines /RxJava
- SQL statement highlighting and compile-time checking (with AndroidStudio support)
- Using SQLite facilitates the transfer of database files across multiple platforms (for example, some contact information is a SQLite file)
- Because SQLite can encrypt database through third-party framework (ROOM native does not support)
- This works with AndroidStudio’s built-in database view window
Conclusion:
I recommend ROOM for its size and ease of use
I usually project development necessary framework
- Network Request Net
- List (including StateLayout) BRV
- The default page StateLayout
- JSON and long text log printing tool LogCat
- Supports asynchronous and global customization of the toast tool Tooltip
- Develop the debugging window tool DebugKit
- One line of code creates the transparent StatusBar StatusBar
features
- SQL statements are highlighted
- Simple introduction
- powerful
- Database listening
- Support Kotlin coroutine /RxJava/Guava
Rely on
dependencies {
def room_version = "2.2.0 - rc01"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// Kotlin uses kapt instead of annotationProcessor
// Optional - Kotlin extension and coroutine support
implementation "androidx.room:room-ktx:$room_version"
// Optional - RxJava support
implementation "androidx.room:room-rxjava2:$room_version"
// Optional - Guava support, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
// Test help
testImplementation "androidx.room:room-testing:$room_version"
}
Copy the code
Gradle configuration
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [
"room.schemaLocation":"$projectDir/schemas".toString(),
"room.incremental":"true"."room.expandProjection":"true"]}}}}Copy the code
room.expandProjection
: Rewrites SQL queries based on function return types when using star projectionsroom.schemaLocation
: Displays the database summary. You can view the field information, version number, and database creation statementroom.incremental
: Enables the Gradle incremental comment processor
use
ROOM creates all the registered table structures as soon as the database object is created
- Creating a database
- Creating an Operation Interface
- Create a data class: typically a data class derived from a JSON reverse sequence
- use
Creating a database
@Database(entities = [Book::class], version = 1)
abstract class SQLDatabase : RoomDatabase() {
abstract fun book(a): BookDao
}
Copy the code
Creating an Operation Interface
@Dao
interface BookDao {
@Query("select * from Book where")
fun qeuryAll(a): List<Book>
@Insert
fun insert(vararg book: Book): List<Long>
@Delete
fun delete(book: Book): Int
@Update
fun update(book: Book): Int
}
Copy the code
Creating a data class
@Entity
data class Book(
@PrimaryKey(autoGenerate = true)
var number: Long = 0.var title:String
)
Copy the code
use
val db = Room.databaseBuilder(this, SQLDatabase::class.java."drake").build(a)// drake is the database file name
val book = Book("Alive")
db.book().insert(book)
val books = db.user().qeuryAll()
Copy the code
annotations
Entity
@Entity
The modifier class acts as a data table whose name is case insensitive
public @interface Entity {
/** * table name, default class name table name */
String tableName(a) default "";
/** * Index example:@Entity(indices = {@Index("name"), @Index("last_name", "address")})
*/
Index[] indices() default {};
/** * whether to inherit the parent index */
boolean inheritSuperIndices(a) default false;
/** * joins the primary key */
String[] primaryKeys() default {};
/** * foreign key array */
ForeignKey[] foreignKeys() default {};
/** * ignores the field array */
String[] ignoredColumns() default {};
}
Copy the code
ROOM requires public access for each database serialized field
Index
@Index
public @interface Index {
/** * specifies the field name of the index */
String[] value();
** * index fieldName * index_${tableName}_${fieldName} example: index_Foo_bar_baz */
String name(a) default "";
/** ** only */
boolean unique(a) default false;
}
Copy the code
Ignore
@Ignore
Fields decorated by this annotation are not counted in the table structure
Database
public @interface Database {
/** * specifies that the data table is created when the database is initializedClass<? >[] entities();/** * specifies which views the database contains */Class<? >[] views()default {};
/** * The current database version */
int version(a);
/** * Whether database profiles are allowed anywhere. Default is true. Gradle 'room. SchemaLocation' is required to be configured */
boolean exportSchema(a) default true;
}
Copy the code
PrimaryKey
@PrimaryKey
Each database requires at least one primary key field, even if there is only one field in the data table
boolean autoGenerate() default false; // The primary key grows automaticallyCopy the code
If the primary key is automatically generated, it must be of type Long or Int.
ForeignKey
@ForeignKey
public @interface ForeignKey {
// The table entity that references the foreign key
Class entity(a);
// The foreign key column to reference
String[] parentColumns();
// The column to be associated
String[] childColumns();
// The action that is performed when the parent entity (the associated foreign key table) is deleted from the database
@Action int onDelete(a) default NO_ACTION;
// The operation performed when the superclass entity (the associated foreign key table) is updated
@Action int onUpdate(a) default NO_ACTION;
// Whether the foreign key constraint should be deferred until the transaction completes
boolean deferred(a) default false;
// Operations defined for onDelete, onUpdate
int NO_ACTION = 1; / / no action
int RESTRICT = 2; // Do not delete the parent key if there are child keys
int SET_NULL = 3; // Child table deletion causes the parent key to be set to NULL
int SET_DEFAULT = 4; // Child table deletion causes the parent key to be set to the default value
int CASCADE = 5; // Delete all key entries in the child table
@IntDef({NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE})
@interface Action {
}
}
Copy the code
The sample
@Entity
@ForeignKey(entity = Person::class, parentColumns = ["personId"],childColumns = ["bookId"], onDelete = ForeignKey.RESTRICT )
data class Book(
@PrimaryKey(autoGenerate = true)
var bookId: Int = 0.@ColumnInfo(defaultValue = "12") var title: String = "A Song of Ice and Fire"
)
Copy the code
ColumnInfo
Modify fields as columns (fields) in the database
public @interface ColumnInfo {
/** * the column name, which defaults to the currently decorated field name */
String name(a) default INHERIT_FIELD_NAME;
/** * specifies that the current field is of type Affinity. */ is generally not used
@SQLiteTypeAffinity int typeAffinity(a) default UNDEFINED;
// The following types
int UNDEFINED = 1;
int TEXT = 2;
int INTEGER = 3;
int REAL = 4; //
int BLOB = 5;
/** * This field is the index */
boolean index(a) default false;
/** * specifies the order in which the columns are arranged when the table is built */
@Collate int collate(a) default UNSPECIFIED;
int UNSPECIFIED = 1; // Default value, similar to BINARY
int BINARY = 2; // Case sensitive
int NOCASE = 3; // case insensitive
int RTRIM = 4; // Case sensitive arrangement, omit trailing Spaces
@RequiresApi(21)
int LOCALIZED = 5; // Use the current system default sequence
@RequiresApi(21)
int UNICODE = 6; / / unicode order
/** * The default value of the current column, if the default value changes to require processing database migration, this parameter supports SQL statement function */
String defaultValue(a) default VALUE_UNSPECIFIED;
}
Copy the code
- The main parameters used are only
index/name
- Not only fields of the Entity class can be modified by this annotation, but non-entity classes can also be modified by this annotation (for example, POJO classes used to expand projections; type projections are covered later).
Index
@Index
Increase query speed
// The name of the field to be indexed
String[] value();
// Index name
String name(a) default "";
// indicates that the field is unique in the table and cannot be repeated
boolean unique(a) default false;
Copy the code
RawQuery
@RawQuery
This annotation is used to modify Dao functions with the SupportSQLiteQuery parameter for raw queries (the compiler does not validate SQL statements), typically using @Query
interface RawDao {
@RawQuery
fun queryBook(query:SupportSQLiteQuery): Book
}
va; book = rawDao.queryBook(SimpleSQLiteQuery("SELECT * FROM song ORDER BY name DESC"));
Copy the code
To return observable objects such as Flow, specify the annotation parameter observedEntities
@RawQuery(observedEntities = [Book::class])
fun query(query:SupportSQLiteQuery): Flow<MutableList<Book>>
Copy the code
Embedded
@Embedded
If a data table entity has a field that belongs to another object, that field can be modified with this annotation to make the external class contain all of the fields of that class (in the data table).
This external class should not be considered an Entity modified table structure
String prefix(a) default "";
// prefix, which is added to all field names in the data table
Copy the code
Relation
@Relation
Earlier, this annotation could only modify collection fields, but now it can modify any type.
The following demonstrates creating a one-to-many
Create a book
@Entity
data class Book(
@PrimaryKey(autoGenerate = true)
var id: Long = 0.var title:String = "drake"
)
Copy the code
Create a person
@Entity
data class Person(
@PrimaryKey(autoGenerate = true) var id: Int = 0.var name: String
)
Copy the code
Create a user
data class User(
@Embedded var person: Person,
@Relation(entity = Book::class, parentColumn = "id", entityColumn = "id")
var book: Book List
= List
)
Copy the code
- Entity parameter Generally, this parameter is not required. It can be inferred from the return value type. You can use this parameter if you want to define the target entity
- User is not an Entity
- User must contain all the fields of Person, so Embedded annotations are recommended
parentColumn
Corresponding to the field in User,entityColumn
Corresponding to fields in Book (i.e., the “many” data table in one-to-many)
You can then freely insert Person or Book and return User when querying
@Dao
interface UserDao {
@Query("select * from person")
fun find(a): List<User>
}
Copy the code
You can see that the query SQL statement queries the Person table, but the function return type is List
. The User contains both Peron and the Book corresponding to Person.
The so-called corresponding principle namely parentColumn/entityColumn these two properties, parentColumn said
Returns the specified column directly, by default by type
data class User(
@Embedded var person: Person,
@Relation(entity = Book::class, parentColumn = "id", entityColumn = "id")
var book: Book List
= List
)
Copy the code
Bridge table
Define a separate data table to represent the relationship between two data tables
Create a bridge table
@Entity(primaryKeys = ["personId"."bookId"])
data class LibraryRelation(
var personId: Int.var bookId: Int
)
Copy the code
- Bridging tables require the same primary key
The bridge table is specified by the parameter associateBy
data class User(
@Embedded var person: Person,
@Relation(
entity = Book::class,
parentColumn = "personId",
entityColumn = "bookId",
associateBy = Junction(BookCaseRef::class, parentColumn = "pId", entityColumn = "bId")
)
var book: List<Book>
)
Copy the code
- In the Junction
parentColumn/entityColumn
The default value is the argument of the same name in Relation. The meaning is the name of the field in the bridge table
Complete many-to-many queries
data class User(
@Embedded var person: Person,
@Relation(
entity = Book::class,
parentColumn = "personId",
entityColumn = "bookId",
associateBy = Junction(BookCaseRef::class, parentColumn = "pId", entityColumn = "bId")
)
var book: List<Book>
)
data class BookCase(
@Embedded var book: Book,
@Relation(
entity = Person::class,
parentColumn = "bookId",
entityColumn = "personId",
associateBy = Junction(BookCaseRef::class, parentColumn = "bId", entityColumn = "pId")
)
var person: List<Person>
)
Copy the code
Transaction
The Dao abstract class allows you to create a function annotated with @Transaction in which the database operations are in a Transaction
Typically, the function runTransaction is used
@Dao
abstract class UserDao {
@Insert
abstract fun insertPerson(person: Person): Long
@Query("select * from Person")
abstract fun findUser(a): List<User>
@Delete
abstract fun delete(p: Person)
@Transaction
open fun multiOperation(deleteId: Int) {
insertPerson(Person(deleteId, "nathan"))
insertPerson(Person(deleteId, "nathan")) // The transaction failed due to a duplicate primary key conflict}}Copy the code
- Insert/Delete/Update modified functions are themselves in transactions
- ROOM allows only one transaction to run, with other transactions queued
- @tranAction requires that the decorated function cannot be
final/private/abstract
, but can be abstract if the function also contains @query - Query If the Query containing the Relation annotation has more than one Query, this parameter is used
@Transaction
Multiple queries will be placed in a transaction to avoid other transactions
DML
- Add, delete, modify, and search are all subject to the primary key, that is, other attributes of the data can not correspond to the records in the data table or can be deleted according to the primary key
- DML in ROOM is all performed by annotated abstract functions
- DML functions can return Long for Insert and Int for other Update/Delete functions. Or all return to Unit.
- When the argument is mutable, the return value should also be of mutable type; otherwise, only the value of the first record is returned
- Mutable types include List/MutableList/Array
- When performing DML operations on multiple data bodies (such as inserting multiple users), any one that does not match will discard the commit altogether
- All DML annotations except Transaction are required to be abstract/public/rewritable
All DML operations require the definition of abstract functions in an interface decorated with @DAO
@Dao
interface BookDao {
@Query("select * from Book")
fun find(a): Flow<Book>
@Insert
fun insert(vararg book: Book): List<Long>
@Delete
fun delete(book: Book): Int
@Update
fun update(book: Book): Int
}
Copy the code
- Daos can be abstract classes or interfaces
Insert
@Insert
fun insert(book: Book): Long
@Insert
fun insert(vararg book: Book): List<Long>
Copy the code
@Insert
/** * how to handle conflicts when inserting columns * Use {@linkOnConflictStrategy#ABORT} (default) Rollback transaction * Use {@linkOnConflictStrategy#REPLACE} REPLACE existing columns * Use {@linkOnConflictStrategy#IGNORE} preserves existing columns */
@OnConflictStrategy
int onConflict() default OnConflictStrategy.ABORT;
Copy the code
- The return value of the modified function must be of type Long: the primary key of the inserted record
- Automatically generate the primary key for the primary key is Long or Int type, value must be 0 will automatically generated at the same time, if the specified primary key and repeat sell SQLiteConstraintException manually
Delete
@Delete
The return type of the modified function must be Int: to remove the row index, starting at 1
Update
@Update
Update rows based on primary key matches
The return value can be Int to update the column index
Query
@Query
This annotation takes a single string argument, which is part of an SQL query and is highlighted by the compiler’s validation rules and code and (very powerful here) auto-complete.
The compiler verifies that the return value matches the query column
To reference function parameters use :{parameter name}
@Dao
public interface MyDao {
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
public User[] loadAllUsersBetweenAges(int minAge, int maxAge);
@Query("SELECT * FROM user WHERE first_name LIKE :search "
+ "OR last_name LIKE :search")
public List<User> findUserWithName(String search);
}
Copy the code
- When the SQL statement is written, the data table is case-sensitive against the class name. Otherwise, the table name in the SQL statement cannot be taken into account when the class name is renamed
Field mapping
You may only need a few fields of the table, so you can create a new object to receive partial field results of the query
The user data table contains many fields
public class NameTuple {
@ColumnInfo(name="first_name")
public String firstName;
@ColumnInfo(name="last_name")
public String lastName;
}
@Dao
public interface MyDao {
@Query("SELECT first_name, last_name FROM user")
public List<NameTuple> loadFullName(a);
}
Copy the code
- NameTuple objects can be decorated with non-entity annotations
- Name The name of the field in the data table (or annotated with ColumnInfo)
Query parameters
Query parameters can use collections
@Dao
public interface MyDao {
@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
public List<NameTuple> loadUsersFromRegions(List<String> regions);
}
Copy the code
The query results
- Entity object: The first object in the query collection
- Array: An empty array with no result
- Set: An empty set with no results
Returns NULL if the entity object is not queried, and returns empty List if the collection is not queried.
Can be viewed
Query functions can register observers with the following return types
-
LiveData
- Initialize the
- delete
- update
- insert
-
Flowable
- insert
- update
Observers are notified when rows in the data table in the query are updated.
Execute SQL statement
The @Query annotation can execute virtually any SQL statement
@Query("delete from book where bookId = :bookId")
fun deleteFromId(bookId: Int): Int // Delete rows by Id, return Int to affect the number of rows in the table
Copy the code
# # # view
@DatabaseView
The database view represents the creation of a virtual table whose table structure may be partial columns of other tables. The main purpose is to reuse data tables
@DatabaseView("SELECT user.id, user.name, user.departmentId," +
"department.name AS departmentName FROM user " +
"INNER JOIN department ON user.departmentId = department.id")
data class UserDetail(
var id: Long.varname: String? .var departmentId: Long.var departmentName: String?
)
Copy the code
The field name is automatically mapped to the field name of the entity class
Register the view to the database before the view can be created, and then query the view like a table; The View array is all the view bytecode
@Database(entities = [User::class], views = [MovieView::class], version = 1)
abstract class SQLDatabase : RoomDatabase() {
abstract fun user(a): UserDao
}
Copy the code
Annotation parameters
public @interface DatabaseView {
/** * query statement */
String value(a) default "";
/** * View name, default is class name */
String viewName(a) default "";
}
Copy the code
ROOM
Create database access objects
The singleton pattern should be followed and multiple database instance objects should not be accessed
static <T extends RoomDatabase> Builder<T> databaseBuilder(Context context, Class
klass, String name)
// Create a serialized database
static <T extends RoomDatabase> Builder<T> inMemoryDatabaseBuilder(Context context, Class<T> klass)
// Create a database in memory that will be cleared after application destruction
Copy the code
ROOM does not allow access to the database from the main thread by default, unless the function allowMainThreadQueries is used, but locking the UI is not recommended, especially when querying database contents in list swiping.
RoomDatabase
public abstract void clearAllTables(a)
// Clear all rows in the table
public boolean isOpen(a)
// Return false if the database connection is already initialized
public void close(a)
// Close the database (if already open)
public InvalidationTracker getInvalidationTracker(a)
public Returns the invalidation tracker for this database.
public SupportSQLiteOpenHelper getOpenHelper(a)
// Return the SQLiteOpenHelper object that uses the database
public Executor getQueryExecutor(a)
public Executor getTransactionExecutor(a)
public boolean inTransaction(a)
Return true if the current thread is in a transaction
public Cursor query(String query, Object[] args)
// A shortcut function to query the database with parameters
Copy the code
The transaction
All database operations in interface callbacks are transactions and are rolled back if they fail
public void runInTransaction(@NonNull Runnable body)
public <V> V runInTransaction(@NonNull Callable<V> body)
Copy the code
RoomDatabase constructor
Roomdatabase.builder This constructor is responsible for building a database instance object
public Builder<T> addMigrations(Migration... migrations)
// Add migration
public Builder<T> allowMainThreadQueries(a)
// allow mainthread queries
public Builder<T> createFromAsset(String databaseFilePath)
Create and open a pre-packaged database in the 'assets/' directory
public Builder<T> createFromFile(File databaseFile)
// Configure room to create and open a pre-packaged database
public Builder<T> fallbackToDestructiveMigration(a)
// If no migration is found, a destructive database rebuild is allowed
public Builder<T> fallbackToDestructiveMigrationFrom(int. startVersions)
// Only the specified start version is allowed to destructively rebuild the database
public Builder<T> fallbackToDestructiveMigrationOnDowngrade(a)
// Destructive migrations are allowed if migrations are not available when you degrade an older version
public T build(a)
// Create database
Copy the code
Functions that are not commonly used
public Builder<T> enableMultiInstanceInvalidation(a)
// Set the notification and synchronization of invalid tables in one database instance. Both database instances must be enabled to be valid.
// This does not apply to in-memory databases, just database instances for different database files
// It is disabled by default
public Builder<T> openHelperFactory(SupportSQLiteOpenHelper.Factory factory)
// Set up the database factory
public Builder<T> setQueryExecutor(Executor executor)
// Set the thread executor for asynchronous queries. Use coroutines instead of this function
public Builder<T> setTransactionExecutor(Executor executor)
// Sets the thread executor for asynchronous transactions
Copy the code
The log
Set the logging mode of SQLite
Builder<T> setJournalMode(roomDatabase.journalMode JournalMode) // Sets the JournalModeCopy the code
JournalMode
- No log TRUNCATE
- WRITE_AHEAD_LOGGING Outputs logs
- AUTOMATIC default behavior, low RAM or lower than API16 without logging
The life cycle
Builder<T> addCallback(RoomDatabase.Callback callback)
Copy the code
RoomDatabase.Callback
public void onCreate(SupportSQLiteDatabase db)
// When the database is first created
public void onDestructiveMigration(SupportSQLiteDatabase db)
// After a destructive migration
public void onOpen(SupportSQLiteDatabase db)
// Open the database
Copy the code
Type conversion
By default, only basic types and their boxing classes are allowed in queries. If we want to use other types as query criteria and field types, we need to define type converters.
You can convert content between custom objects and database serialization using @Typeconverter
class DateConvert {
@TypeConverter
fun fromDate(date: Date): Long {
return date.time
}
@TypeConverter
fun toDate(date: Long): Date {
return Date(date)
}
}
Copy the code
TypeConverters can be decorated
- Database (@database modifier)
- Data body (@entity modifier)
- Field properties of the data body, (parameters are not supported)
- The official documentation says you can decorate the Dao but I’ll get an error when I try
The scope varies depending on the modifier
In a modified database, Date goes through the converter throughout the database operation
@Database(entities = {User.java}, version = 1)
@TypeConverters({DateConvert.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao(a);
}
Copy the code
DQL
ROOM supports four types of query function returns
-
Single/Mabye/Completable/observables/Flowable RxJava observed
-
LiveData: Active observer in the JetPack library
-
Flow: a Flow in a Kotlin coroutine
-
Cursor: The primitive query result set of SQLite in Android. This return object cannot listen for database changes
I no longer recommend using RxJava in projects because concurrency is not easy and callback regions are easy to create. I recommend coroutines, or my open source project, Net, if I don’t think I can completely replace RxJava
All of the return types I’ve listed except Cursor support listening for changes in database query results in callbacks. The observer is triggered when the table you queried changes (even if the change does not match the result of the query) and then queries again. And all tables involved in your query will be notified when changes are made.
@Query("select * from Book")
fun find(a): Flow<Array<Book>>
@Query("select * from Book")
fun find(a): Observable<Array<Book>>
@Query("select * from Book")
fun find(a): LiveData<Array<Book>>
@Query("select * from Book")
fun find(a): LiveData<List<Book>> // List and Array are both acceptable
@Query("select * from Book")
fun find(a): Flow<Array<Book>>
@Query("select * from Book")
fun find(a): Cursor
Copy the code
Sample query Flow
val result = db.book().find()
GlobalScope.launch {
result.collect { Log.d("Log"."result = $it")}}Copy the code
-
We can use the function distinctUntilChanged to filter out duplicate data behavior (== indicates whether the data is the same or not, which is supported by the data class by default. For other member attributes, re-equals is required.)
GlobalScope.launch { bookFlow.distinctUntilChanged().collect { Log.d("Log"."result = $it")}}Copy the code
-
It is recommended to use with Net to automatically follow the life cycle and handle exceptions
features
I will be updating articles to introduce new features that follow the release
Pre-packaged databases
The database DB files can be placed in a path (File) or asset directory, and ROOM can rebuild by copying the pre-packaged database if the migration conditions are met.
Current application database version | Pre-packaged database versions | Update the application database version | Migration strategy | describe |
---|---|---|---|---|
2 | 3 | 4 | Destructive migration | Delete the application database reconstruction version 4 |
2 | 4 | 4 | Destructive migration | Copy the prepackaged database files |
2 | 3 | 4 | Manual migration | Copy the pre-packaged database files and run manual migration 3->4 |
- The destructive migration mentioned here is invoked when ROOM is created
fallbackToDestructiveMigration
Function, manual migration means no call - It is recommended to useDataGripTo create the SQLite file, suffix
.sqlite
or.db
Essentially no difference, but Android generally uses DB - Destructive migration must be enabled at the same time the pre-packaged database is enabled, otherwise it will be thrown
The pre-packaged database is only a rollback method, but still cannot retain user data completely. You need to migrate the database manually
A projection
When the queried table contains many fields and I only need two of them, I can create a POJO that contains only two fields instead of the previous Entity class.
ROOM queries do not necessarily return tables decorated with Entity names, as long as the corresponding field name can be projected to
Since expanded projections are introduced, it is emphasized that pojos used for expanded projections can also use certain annotations, such as ColumnInfo,PrimaryKey, Index
Data tables and POJO classes for simplification
@Entity
data class Book(
@PrimaryKey(autoGenerate = true)
var bookId: Int = 0.var title: String = "drake"
)
// Suppose I just focus on the title and don't want to get extra ids
data class YellowBook(
@ColumnInfo(name = "title")
var virtual: String = "drake"
)
Copy the code
The original query and the query after using the expanded projection
// This is the original
@Query("select * from Book")
abstract fun findBook(a): List<Book>
// This is the expanded projection
@Query("select * from Book")
abstract fun findBook(a): List<YellowBook>
Copy the code
The target entity
The DAO annotations @INSERT, @update, and @Delete now have a new property entity
Similar to the expanded projection described above, use YellowBook for this tutorial
/ / the original
@Insert
fun insert(vararg book: Book): List<Long>
// Use the target entity
@Insert(entity = Book::class)
fun insert(vararg book: YellowBook): List<Long>
Copy the code
- The default values for the missing fields in YellowBook (ColumnInfo) are used
defaultValues
) to insert,bookId
The automatically generated primary key ID is used. - The default values mentioned here are not the Kotlin parameter or field default values, but the default values in SQLite