谷歌提供的数据库封装库,不建议直接用Sqlite
这边简单起见字段都用public,如果是private需要提供getter和setter方法,它们的名称要遵循Room中JavaBeans规范
此外,Sqlite是关系型数据库,Room可以在表之间添加关联
一对一关联 场景举例:每个用户至多只有一个收藏列表,想要查询出哪些用户有收藏列表 使用: @Entity public class User{ @PrimaryKey public long uid; } @Entity public class Collection{ @PrimaryKey public long cid; //子实体需包含父实体的主键的引用 public long uidInCol; } //为它们创建一对一关联类 public class UserAndCollection{ //引入其他实体需要@Embedded注解 @Embedded public User user; //子实体需要使用@Relation注解,其中parentColumn为父实体主键列名称,entiryColumn为引用父主键的名称 @Relation(parentColumn = "uid", entiryColumn = "uidInCol") public Collection collection; } //最后在dao中使用查询 @Transaction //原子操作 @Query("SELECT * FROM user") public List<UserAndCollection> getUsersAndCollections(); .一对多关联 场景举例:每个用户可以有多个播放列表 使用:使用基本同一对一只是在创建关联的时候稍有不同,子实体需要用List public class UserAndCollection{ @Embedded public User user; //这边使用集合类 @Relation(parentColumn = "uid", entiryColumn = "uidInCol") public List<Collection> collections; } 多对多关联 场景举例:每个播放列表对应多首歌曲,每个歌曲可以在多个播放列表中 使用: //创建表 @Entity public class PlayList{ //播放列表 @PrimaryKey public long playListId; } @Entity public class Song{ //歌曲 @PrimaryKey public long songId; } @Entity(primaryKeys = {"playListId", "songId"}) public class PlayListsAndSongs{ //保存列表和歌曲对应关系 public long playListId; public long songId; } //添加多对多关联 public class PlayListWithSongs{ @Embedded public PlayList playList; //使用associateBy字段来标识PlayList和Song如何对应 @Relation(parentColumn = "playListId", entiryColumn = "songId", associateBy = @Junction(PlayListsAndSongs.class)) public List<Songs> songs; } //最后在dao中使用查询 @Transaction //原子操作 @Query("SELECT * FROM PlayList") public List<PlayListsAndSongs> getPlayListsAndSongs(); 嵌套 场景举例:每个用户有多个播放列表,每个列表有多首歌曲 使用:表的声明都没变 //添加关联 public class UserWithPlayListsAndSongs{ @Embedded public User user; @Relation(entity = PlayList.class, parentColumn = "uid", entiryColumn = "uidInPlayList") public List<PlayListWithSongs> playListWithSongs; } //在dao中查询所有用户下播放列表,和每个播放列表下的歌曲 @Transaction @Query("SELECT * FROM user") public List<UserWithPlayListsAndSongs> getUserWithPlayListsAndSongs(); 写关于表的操作 @Dao public interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(User... users); @Insert void insertUser(User user1, User user2); @Insert void insertUserAndCollection(User user, List<Collection> collections); //按主键匹配更新user,一般返回一个int值,标识更新了多少行 @Update int update(User... users); @Update int update(List<User> users); //按主键匹配删除user,一般返回一个int值,标识删除了多少行 @Delete int delete(User... users); @Query("SELECT * FROM user") LiveData<List<User>> getAll(); } @Inset @Insert(onConflict = OnConflictStrategy.REPLACE) onConflict标识当发生冲突时处理办法 默认是ABORT,即终止,常用的还有IGNORE 忽略 和 REPLACE替换 @Insert(entity = User.class) 指定使用哪个实体 比如: @Entity publicclass User{ @PrimaryKey(autoGenerate = true) long uid; String name; String age; @ColumnInfo(defaultValue = "火星") String address; } public class SimpleUser{ String name; String age; } //这时在dao中使用时就需要指定实体 @Insert(entity = User.class) public void insert(SimpleUser simpleUser); @Update 和Insert一样,可以通过onConflict来指定冲突发送时处理方式,entity来指定实体@Delete 和Insert一样,可以通过entity来指定实体@Query @Query("SELECT * FROM user")里面的语句会在编译时进行验证,因此如果查询出现问题,如表名错误、字段名错误、查询规则没写完等,就会发生编译错误,而不是运行时失败. 如果这样写:
@Query("SELECT * FROM user") List<SimpleUser> getAll();Room 会验证查询的返回值,如果SimpleUser和User有部分字段匹配,则会发出警告。如果没有字段匹配,则会发出错误。
这样写法用的较多,因为页面不会同时需要user的所有数据,这时就可以为该页面创建一个新类作为查询返回值,Room会从user匹配相应的列到返回值中,但是注意SimpleUser中属性名需要和User表生成的列匹配。从而加快查询效率,节省资源。还有一种情况就是多表查询,查询出来的结果一般不是需要这些表所有列,这时也是需要新写一个类,比如:
@Query("SELECT user.name AS userName, pet.name AS petName " + "FROM user, pet " + "WHERE user.id = pet.user_id") public LiveData<List<UserPet>> loadUserAndPetNames();设置查询条件:参数在sql中按 :参数名 方式使用,Room 通过参数名称进行匹配。如果有不匹配的情况,也会在编译时报错。
@Query("SELECT * FROM user WHERE first_name LIKE :firstName") List<User> getAll(String firstName);这边实验了一下,如果想传对象,比如user,它是否会自动找user的first_name然后匹配,即
@Query("SELECT * FROM user WHERE first_name LIKE :user") List<User> getAll(User user);结果编译时报错
Query method parameters should either be a type that can be converted into a database column or a List / Array that contains such type. You can consider adding a Type Adapter for this.想法错误,这错误大致意思是参数名必须是列名称,或者包含列名称的列表、数组,即
@Query("SELECT * FROM user WHERE first_name LIKE :firstNames") List<User> getAll(List<String> firstNames);返回值处理是数据对象外还可以是游标,但是谷歌不建议这样写,官方描述是Cursor无法保证行是否存在或者行包含哪些值。只有当您已具有需要光标且无法轻松重构的代码时,才使用此功能。
其实一般返回值不直接写数据对象,因为这样Room就是一个简单的数据库操作,体现不出架构组件的优势,而实际往往需要当数据变化时界面自动更新这时就要使用 LiveData 类型的返回值。当数据库更新时,Room 会生成更新 LiveData 所必需的所有代码
@Query("SELECT * FROM user") LiveData<List<User>> getAll(); 配置数据库 //使用@Database注解生成数据库,由它注释的类必须是一个抽象类并且继承自RoomDatabase @Database(entities = {User.class, Message.class}, version = 1, exportSchema = false) public abstract class CalendarDataBase extends RoomDatabase { public abstract UserDao userDao(); public abstract MessageDao messageDao(); public abstract MessageAndUserDao mAuDao(); }数据库对其内部的Entity 和 Dao 没有数量限制,但是不可重复。exportSchema标识Room将数据库架构导出到文件夹中,还有一个参数是views 和 entities 标识保存的实体view
使用 //先封装一个类方便外部调用 public class CalendarDbManager { public static final String DB_NAME = "calendar_db"; private Context mContext; public static CalendarDbManager getInstance(){ return InnerCalendarDbManager.instance; } private static class InnerCalendarDbManager{ private static CalendarDbManager instance = new CalendarDbManager(); } public void initCalendarDb(Context context) { if (context instanceof Application){ mContext = context; }else{ // throw new Exception(""); } } public void insert(){ //需在子线程中 new Thread(){ @Override public void run() { CalendarDataBase dataBase = Room.databaseBuilder(mContext, CalendarDataBase.class, DB_NAME).build(); User user = new User(); user.uid = 1; user.firstName = "a"; user.lastName = "b"; dataBase.userDao().insertAll(user); } }.start(); } public LiveData<List<User>> queryAll(){ CalendarDataBase dataBase = Room.databaseBuilder(mContext, CalendarDataBase.class, DB_NAME).build(); return dataBase.userDao().getAll(); } } //在Activity或者Fragment中使用 CalendarDbManager.getInstance().initCalendarDb(getApplicationContext()); CalendarDbManager.getInstance().insert(); CalendarDbManager.getInstance().queryAll().observe(this, new Observer<List<User>>() { @Override public void onChanged(List<User> users) { mBinding.setUser(users.get(0)); } });