“This is the fourth day of my participation in the First Challenge 2022. For details: First Challenge 2022”
An overview,
1.1 describe
The SQLite Api was introduced earlier, but today let’s take a look at Room, a key member of Android Jetpack.
The Room persistence library provides an abstraction layer on top of SQLite to allow smooth database access while leveraging the full capabilities of SQLite. In particular, Room offers the following benefits:
- Compile-time validation of SQL queries.
- Convenient comments to minimize repetitive and error-prone boilerplate code.
- Simplified database migration path.
Because of these considerations, we strongly recommend that you use Room instead of using the SQLite API directly.
1.2 Main Components
Room consists of three main components:
- The database class that holds the database and serves as the primary access point for the underlying connection to persistent data with the application.
- Data entities representing tables in an application database.
- Data Access Object (DAO), which provides methods that your application can use to query, update, insert, and delete data in a database.
The database class provides your application with a DAO instance associated with that database. In turn, applications can use daOs to retrieve data from the database as instances of associated data entity objects. The application can also use defined data entities to update rows in the corresponding tables or create new rows for insertion. The following diagram illustrates the relationship between the different components of Room.
2. Use Room
2.1 Adding a Dependency
Add dependencies to app/build.gradle:
dependencies {
//Room
def room_version = "Against 2.4.1."
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
}
Copy the code
2.2 Creating Data Entities
Define each Room Entity as a class with the @Entity annotation. The Room entity consists of the fields in the database that correspond to each column in the table, including one or more columns that make up the primary key. Therefore, entity classes tend to be small model classes that do not contain any logic. Our User class represents the model of the data in the database and tells Room that it should create a table based on this class:
@Entity
public class User {
@PrimaryKey
public int id;
public String mName;
public int mAge;
}
Copy the code
Set the PrimaryKey (such as id) by adding the @primarykey annotation to the correct field.
Note: To keep a field, Room must have access to it. You either set it to public or provide getters and setters to ensure Room can access the field.
By default:
-
Database table name: Room uses the class name as the database table name. Or set the name of the table via the tableName property of the @entity annotation.
-
Column names: Room uses field names as column names in the database by default. Or change the column name by adding it to the field with the @columninfo annotation and setting the name attribute.
2.2.1 Setting the tableName or Name attribute
@Entity(tableName = "users")
public class User {
@PrimaryKey
public int id;
@ColumnInfo(name = "name")
public String mName;
@ColumnInfo(name = "age")
public int mAge;
}
Copy the code
At this point we set the table name to Users. Change the column names of mName and mAge in the table to name and age respectively.
Note: Table and column names in SQLite are case insensitive.
2.2.2 Setting the Primary Key
1. Define a single primary key
Each Room entity must define a primary key that uniquely identifies each row in the corresponding database table. The most straightforward approach is to annotate a single column, such as the ID attribute in the User class above, with @primaryKey.
Note: If you need Room to assign an automatic ID to an entity instance, set the autoGenerate property of @primaryKey to true
@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
public int id;
}
Copy the code
2. Define compound primary keys
If you need to uniquely identify an instance of an Entity by a combination of columns, you can define compound primaryKeys by listing these columns in the @entity primaryKeys property:
@Entity(tableName = "users",primaryKeys = {"mName","mAge"})
public class User {
public String mName;
public int mAge;
}
Copy the code
2.2.3 Ignoring fields
By default, Room creates a column for each field defined in the entity. But some fields we don’t need to save to the database, we can annotate them with @ignore.
@Entity(tableName = "users")
public class User {
@Ignore // @ignore This property is not in the database production column
public String nickname;
}
Copy the code
2.3 Creating a DAO for Data Access Objects
The DAO is responsible for defining the methods to access the database. With Room, we don’t need all the code associated with Cursor, just define our query using comments in the UserDao class. Each DAO contains methods that provide abstract access to the application database. At compile time, Room automatically generates the implementation of the DAO you define.
You can define daOs as interfaces or abstract classes. For basic use cases, you should usually use interfaces. In either case, you must annotate your Dao using @dao. Daos have no attributes, but they do define one or more ways to interact with data in an application database.
@Dao // This is a must
public interface UserDao {
// A convenient way to insert, update, and delete rows in a database without writing any SQL code.
// Add a single entity
@Insert
void insertUser(User user);
// Add multiple entities
@Insert
void insertUsers(List<User> users);
// Update data
@Update
void updateUser(User user);
// Delete data
@Delete
void deleteUser(User user);
Write your own SQL query methods
// Query the users table
@Query("SELECT * FROM users")
List<User> getAll(a);
// Query the Users table based on name, passing the set of parameters to the query
@Query("SELECT * FROM users WHERE name IN (:usernames)")
List<User> loadAllByNames(int[] usernames);
// Pass simple parameters to the query
@Query("SELECT * FROM users WHERE age > :minAge")
public User[] loadAllUsersOlderThan(int minAge);
}
Copy the code
2.4 Creating a Database
Define an AppDatabase class to hold the database. AppDatabase defines the database configuration and acts as the application’s primary point of access to persistent data. The database class must meet the following conditions:
- The class must be annotated with the @DATABASE annotation, which includes an array of entities that list all the data entities associated with the Database.
- This class must be an abstract class that extends RoomDatabase.
- For each DAO class associated with the database, the database class must define an abstract method that takes zero parameters and returns an instance of the DAO class.
import androidx.room.Database;
@Database(entities = {User.class},version = 1)
public abstract class AppDatabase {
public abstract UserDao userDao(a);
}
Copy the code
Now that the three main components of Room have been created, let’s get started.
2.5 call
Note: database operations must be placed in child threads, do not operate on the main thread (otherwise an error will be reported), although you can force this to be enabled, do not operate on the main thread to avoid ANR problems.
2.5.1 Creating a Database
Database to store path: / data/data/com. SCC. Datastorage/files/room_db
// Create database
String dir = getFilesDir()+"/room_db";
AppDatabase db = Room.databaseBuilder(AppGlobalUtils.getApplication(),
AppDatabase.class, dir).build();
Copy the code
2.5.2 Adding Data
List<User> list = new ArrayList<>();
list.add(new User(10."Handsome every time.".20."GMT"));
list.add(new User(12."Zhu Yuanzhang".30."11"));
list.add(new User(15."Zhao Kuangyin".40."13:00"));
list.add(new User(18."Li Shimin".50."15:00"));
new Thread(() -> {
userDao.insertUsers(list);
Log.e("Room"."Insert successful:" + db.userDao().queryAll().size());
}).start();
Copy the code
2.5.3 Searching for Data
new Thread(() -> {
StringBuilder sql = new StringBuilder();
List<User> list = userDao.queryAll();
sql.append(list.size());
if (list.size() > 0) {
for (User bean : list) {
sql.append("\n").append(bean.toString());
}
}
Log.e("Room", sql.toString());
}).start();
Copy the code
2.5.4 Modifying Data
new Thread(() -> {
StringBuilder sql = new StringBuilder();
List<Integer> id = new ArrayList<>();
id.add(18);
List<User> list = db.userDao().queryAllById(id);
if (list.size() > 0) {
sql.append("\n Before modifying data:").append(list.get(0).toString());
}
User user = new User(18."The Empress of China".32."Twelve");
userDao.updateUser(user);
list = db.userDao().queryAllById(id);
if (list.size() > 0) {
sql.append("\n After modification:").append(list.get(0).toString());
}
Log.e("Room", String.valueOf(sql));
}).start();
Copy the code
2.5.5 Deleting Data
new Thread(() -> {
StringBuilder sql = new StringBuilder();
List<User> list = userDao.queryAll();
if (list.size() > 0) {
for (User bean : list) {
sql.append("\n").append(bean.toString());
}
}
User user = new User();
user.id = 12;
db.userDao().deleteUser(user);
sql.append("\n After deletion");
List<User> listDelete = db.userDao().queryAll();
Log.e(getClass().getName(), "list:" + list.size());
if (listDelete.size() > 0) {
for (User bean : listDelete) {
sql.append("\n").append(bean.toString());
}
}
Log.e("Room", sql.toString());
}).start());
Copy the code
Then you’ll notice that Room and SQLite are basically the same. Easy to use, just try it.
4. Related links
Android data processing scheme
Android Data Store (1)- File storage
Android Data Storage (2)-Preferences or MMKV
Android Data Store (3)-SQLite database instance