Detailed use of Room for Android local data storage

Room has an ORM package based on SQLite, which is similar to JPA in use and does not need to write too much sql.

Prepare, import dependencies

//room
def room_version="2.4.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
//implementation "androidx.room:room-rxjava2:$room_version"
//implementation "androidx.room:room-rxjava3:$room_version"
//implementation "androidx.room:room-guava:$room_version"
//testImplementation "androidx.room:room-testing:$room_version"
//implementation "androidx.room:room-paging:2.5.0-alpha01"

Key Notes

1. @Database: Room database object. This class needs to inherit from RoomDatabase, and complete the creation of the database through Room.databaseBuilder() combined with the singleton design pattern. The Dao object we created is returned here in the form of an abstract method, and only one line of code is required.

  • entities: specify which tables the database has
  • version: Specifies the database version number, and subsequent database upgrades are judged based on the version number

2. @Entity: This class is associated with the table in Room. The tableName attribute can set the name for the table, if not set, the table name is the same as the class name.

3. @PrimaryKey: Used to specify the field as the primary key of the table.

4. @ColumnInfo: Set the name of the field stored in the database table and specify the type of the field; the default field name is the same as the attribute name

5. @Ignore: Ignore this field

1. Steps to use

1. Create an entity class, corresponding to a table in the database, using the annotation @Entity
2. Create a Dao interface class for manipulating data, using the annotation @Dao; no implementation is required, and the framework will automatically generate an implementation class when compiling
3 . Create a database object Database, inherit RoomDatabase, and use the singleton mode to return an instance
4. When used in an Activity, Room data operations must be executed in an asynchronous thread, so use the thread pool to execute in the Activity, or use RxJava to switch threads

Use the code example

1. Create an entity class, corresponding to a table in the database, using the annotation @Entity

@Entity
public class Person {
    
    

    // 主键,自增长
    @PrimaryKey(autoGenerate = true)
    private int id;
    private String name;
    private String sex;
    private int age;
}

2. Create a Dao interface class for manipulating data, using the annotation @Dao; no implementation is required, and the framework will automatically generate an implementation class when compiling

@Dao
public interface PersonDao {
    
    
    // 插入
    @Insert
    void insertPersons(Person... persons);
    // 修改
    @Update
    void updatePersons(Person... persons);
    // 删除所有
    @Query("delete from Person")
    void deleteAllPersons();
    // 删除指定实体
    @Delete
    void deletePersons(Person... persons);
    // 根据id删除
    @Query("delete from Person where id in (:ids)")
    void deleteByIds(int ...ids);
    // 根据id查询
    @Query("select * from Person where id in (:ids)")
    List<Person> selectByIds(int ...ids);
    // 查询所有
    @Query("select * from Person order by id desc")
    List<Person> selectAllPersons();
}

3. Create a database object Database, inherit RoomDatabase, and use the singleton mode to return an instance

@Database(entities = {
    
    Person.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    
    
    public abstract PersonDao personDao();

    private volatile static AppDatabase instance;

    public static AppDatabase getInstance(Context context){
    
    
        if (instance == null) {
    
    
            synchronized (DBHelper.class) {
    
    
                if (instance == null) {
    
    
                    instance = Room.databaseBuilder(context, AppDatabase.class, "person.db").build();
                }
            }
        }
        return instance;
    }
}

4. Use in Activity

Room data operations must be performed in an asynchronous thread, so use the thread pool to perform in the Activity

ExecutorService pool = Executors.newCachedThreadPool();

// 插入数据
public void insertRoom(View view) {
    
    
    AppDatabase db = AppDatabase.getInstance(getApplicationContext());

    pool.execute(() -> {
    
    
        PersonDao dao = db.personDao();
        Person p1 = new Person("用户1", "男", 18);
        Person p2 = new Person("用户2", "男", 28);
        Person p3 = new Person("用户3", "男", 38);
        dao.insertPersons(p1, p2, p3);
    });

}

// 查询数据
public void queryRoom(View view) {
    
    

    AppDatabase db = AppDatabase.getInstance(getApplicationContext());

    pool.execute(() -> {
    
    

        PersonDao dao = db.personDao();
        List<Person> list = dao.selectAllPersons();
        list.forEach(p-> Log.d("test", p.toString()));

    });
}

// 根据id查询
public void queryRoomById(View view) {
    
    

    AppDatabase db = AppDatabase.getInstance(getApplicationContext());

    pool.execute(() -> {
    
    

        PersonDao dao = db.personDao();
        List<Person> list = dao.selectByIds(3,4);
        list.forEach(p-> Log.d("test", p.toString()));

    });
}

// 删除
public void deleteRoom(View view) {
    
    

    AppDatabase db = AppDatabase.getInstance(getApplicationContext());

    pool.execute(() -> {
    
    

        PersonDao dao = db.personDao();
        dao.deleteByIds(1,2);
    });
}

2. Type Converter

SQLite supports five data types: null, integer, real, text, and blob. In fact, SQLite also accepts data types such as varchar, char, and decimal, but it will be converted into the corresponding five data types during operation or storage. Therefore, Various types of data can be saved into any field.

In addition to the above basic types, how to store other objects such as Date, BigDecimal, or Json?

Room provides us with a very convenient type converter function.

  • @TypeConverter, define type conversion static method
  • @TypeConverters, defines a class class that contains a set of conversion methods

1. Create a type conversion type, such as Date and Long conversion

Use the annotation @TypeConverter to declare specific conversion methods, each method must contain a parameter, and must have a return value.

public class DateConverter {
    
    

    @TypeConverter
    public static Date toDate(Long dateLong) {
    
    
        return dateLong == null ? null : new Date(dateLong);
    }

    @TypeConverter
    public static Long fromDate(Date date) {
    
    
        return date == null ? null : date.getTime();
    }
}

2. Create a good converter class and use it on the entity

Use the annotation @TypeConverters({DateConverter.class}), then all Date attributes in the entity class will be converted to Long storage, and will be automatically converted from Long to Date display when the query is retrieved.

Note: @TypeConverters are placed on element attributes, Class, Dao, Database

  • Put it in the element attribute, it is only valid for changing the attribute
  • Put it on the entity Class, valid for all elements in the class
  • Put it on Dao, it is valid for all methods of Dao
  • Put it in Database, it is valid for all entities of Database and all Dao

To avoid confusion it is generally recommended to only define converters on Entity or properties

@Entity
@TypeConverters({
    
    DateConverter.class})
public class BsGoods {
    
    
    private static final long serialVersionUID = 1122172437556010779L;
    // 主键
    @PrimaryKey
    private Long id;
    private Date createdDate;
    private Date updatedDate;
    
    ...
}

Other type conversion examples, BigDecimal to String.

If it is a complex object such as JavaBean, it can be converted into a Json string for storage.

public class BigDecimalConverter {
    
    

    @TypeConverter
    public static String toStr(BigDecimal decimal) {
    
    
        return decimal == null ? null : decimal.toString();
    }

    @TypeConverter
    public static BigDecimal toDecimal(String str) {
    
    
        return str == null ? null : new BigDecimal(str);
    }
}

3. Combine RxJava, use it in Activity, and update the UI elements of the interface

The update of UI elements on the Android interface must be executed in the main thread, but the data query of Room can only be processed by the exception thread. So how to update the queried data to the page control?

Here you can combine RxJava to realize streaming operation and switch offline!

The sample code queries all product data and displays it on the page control. The control uses a custom TableView, which will not be expanded for now. Only data query and display are displayed here.

1. Define the query method in the Database class and pass in the callback function

public void selectAll(Consumer<List<BsGoods>> fun) {
    
    
    BsGoodsDao dao = bsGoodsDao();

    Observable.just("select")
            .map(s -> dao.selectAll())
            .subscribeOn(Schedulers.io())// 给上面的操作分配异步线程
            .observeOn(AndroidSchedulers.mainThread())// 给终点分配安卓主线程
            .subscribe(new Observer<List<BsGoods>>() {
    
    
                @Override
                public void onSubscribe(@NonNull Disposable d) {
    
    

                }

                @Override
                public void onNext(@NonNull List<BsGoods> bsGoods) {
    
    
                    fun.accept(bsGoods);
                }

                @Override
                public void onError(@NonNull Throwable e) {
    
    

                }

                @Override
                public void onComplete() {
    
    

                }
            });
}

2. Used in the Activity, pass in the callback function to update the interface UI

private void initializeTableViewLocal() {
    
    
    BsGoodsDatabase db = BsGoodsDatabase.getInstance(getContext());
    db.selectAll(list -> {
    
    
        GoodsTableViewModel tableViewModel = new GoodsTableViewModel(list);
        TableViewAdapter tableViewAdapter = new TableViewAdapter(tableViewModel);
        mTableView.setAdapter(tableViewAdapter);
        mTableView.setTableViewListener(new TableViewListener(mTableView));

        tableViewAdapter.setAllItems(tableViewModel.getColumnHeaderList(), tableViewModel
                .getRowHeaderList(), tableViewModel.getCellList());

    });

}

Guess you like

Origin blog.csdn.net/wlddhj/article/details/128286193