2023年7月25日发(作者:)
AndroidRoom使⽤介绍Room是Google提供的⼀个ORM库。Room提供了三个主要的组件:@Database:@Database⽤来注解类,并且注解的类必须是继承⾃RoomDatabase的抽象类。该类主要作⽤是创建数据库和创建Daos(data access objects,数据访问对象)。@Entity:@Entity⽤来注解实体类,@Database通过entities属性引⽤被@Entity注解的类,并利⽤该类的所有字段作为表的列名来创建表。@Dao:@Dao⽤来注解⼀个接⼝或者抽象⽅法,该类的作⽤是提供访问数据库的⽅法。在使⽤@Database注解的类中必须定⼀个不带参数的⽅法,这个⽅法返回使⽤@Dao注解的类。使⽤Room添加google maven 仓库allprojects { repositories { jcenter() maven { url '' } }}添加Room依赖compile ":runtime:1.0.0-alpha5"annotationProcessor ":compiler:1.0.0-alpha5"为了⽅便调试,我们添加Android-Debug-Database的依赖。debugCompile 'd:debug-db:1.0.1'下⾯介绍⼀个简单的例⼦:@Entitypublic class User { @PrimaryKey private int uid; @ColumnInfo(name = "first_name") private String firstName; @ColumnInfo(name = "last_name") private String lastName; // Getters and setters are ignored for brevity, // but they're required for Room to work.}@Daopublic interface UserDao { @Query("SELECT * FROM user") List getAll(); @Query("SELECT * FROM user WHERE uid IN (:userIds)") List loadAllByIds(int[] userIds); @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") User findByName(String first, String last); @Insert void users); @Delete void delete(User user);}@Database(entities = {}, version = 1)public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao();}获取AppDatabase实例:AppDatabase db = seBuilder(getApplicationContext(), , "").build();每次创建AppDatabase实例都会产⽣⽐较⼤的开销,所以应该将AppDatabase设计成单例的。@Database(entities = {}, version = 1)public abstract class AppDatabase extends RoomDatabase { private static AppDatabase INSTANCE; private static final Object sLock = new Object(); public abstract UserDao userDao(); public static AppDatabase getInstance(Context context) { synchronized (sLock) { if (INSTANCE == null) { INSTANCE = seBuilder(licationContext(), , "") .build(); } return INSTANCE; } }}Room不允许在主线程中访问数据库,除⾮在buid的时候使⽤allowMainThreadQueries()⽅法 seBuilder(licationContext(), , "") .allowMainThreadQueries() .build();EntityRoom会利⽤@Entity注解的类的所有字段来创建表的列,如果某些字段不希望存储的话,使⽤@Ignore注解该字段即可。@Entityclass User { @PrimaryKey public int id; public String firstName; public String lastName; @Ignore Bitmap picture;}默认情况下,Room使⽤类名作为表名,使⽤字段名作为列名。我们可以通过@Entity的tableName属性定义⾃⼰的表名,通过@ColumnInfo的name属性定义⾃⼰的列名。@Entity(tableName = "users")class User { @PrimaryKey public int id; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture;}主键每⼀个实体⾄少定义⼀个字段作为主键。可以将@PrimaryKey的autoGenerate属性设置为true来设置⾃动id。如果实体有⼀个复合的主键,可以使⽤ @Entity的primaryKeys属性来指定主键。@Entity(primaryKeys = {"firstName", "lastName"})class User { public String firstName; public String lastName; @Ignore Bitmap picture;}索引和唯⼀性为数据库添加索引可以加快数据的查询。在Room中可以通过@Entity的indices属性添加索引。@Entity(indices = {@Index("name"), @Index(value = {"last_name", "address"})})class User { @PrimaryKey public int id; public String firstName; public String address; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture;}有时候,需要确保某个字段或者多个字段形成的组唯⼀。可以通过将@Index的unique属性设置为true,来确保唯⼀性。在下⾯的例⼦中,防⽌first_name和last_name这两列同时具有相同的数据。@Entity(indices = {@Index(value = {"first_name", "last_name"}, unique = true)})class User { @PrimaryKey public int id; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture;}关系SQLite是关系型数据库,你可以指定不同对象之间的关系。尽管⼤多数ORM类库允许对象之间互相引⽤,但Room明确禁⽌这⼀点。尽管不能使⽤直接关系,Room仍然两个实体之间定义外键。例如,有另外⼀个实体Book,你可以使⽤@ForeignKey注解定义和User之间的关系。@Entity(foreignKeys = @ForeignKey(entity = , parentColumns = "id", childColumns = "user_id"))class Book { @PrimaryKey public int bookId; public String title; @ColumnInfo(name = "user_id") public int userId;}外键⾮常有⽤,因为当引⽤的实体发⽣改变时,你可以指定如何处理。例如,如果@ForeignKey的onDelete属性值为CASCADE,如果删除user,所有具有相同userId的book会被全部删除。嵌套对象假设我们的User实体中新加了下⾯这些字段:public class User { @PrimaryKey(autoGenerate = true) public int id; public String firstName; public String lastName; // @Ignore Bitmap picture; public String street; public String state; public String city; @ColumnInfo(name = "post_code") public int postCode;}创建的表这样看起来⼀点也不⾯向对象,我们完全可以将新加的字段封装成⼀个Address对象。class Address { public String street; public String state; public String city; @ColumnInfo(name = "post_code") public int postCode;}Room提供了⼀个注解@Embedded,允许在⼀个实体中嵌⼊另外⼀个实体,创建的表使⽤的是当前实体和嵌⼊实体的所有字段,所以我们可以修改上⾯的User实体@Entityclass User { @PrimaryKey public int id; public String firstName; @Embedded public Address address;}当⼀个类中嵌套多个类,并且这些类具有相同的字段,则需要调⽤@Embedded的属性prefix 添加⼀个前缀,⽣成的列名为前缀+列名public class User { @PrimaryKey(autoGenerate = true) public int id; public String firstName; public String lastName; @Embedded(prefix = "first") Address address; @Embedded(prefix = "second") AddressTwo addressTwo;}该例中将会创建firstStreet,secondStreet,等列。数据访问对象(DAOs)Dao类是Room最重要的组件,该类主要提供操作数据库的⽅法。便利⽅法Room提供了多个便利⽅法来操作数据库。插⼊当我们创建⼀个Dao⽅法,并使⽤@Insert注解,Room将把所有的参数在⼀次事物中插⼊到数据库中。@Daopublic interface MyDao { @Insert(onConflict = E) public void users); @Insert public void insertBothUsers(User user1, User user2); @Insert public void insertUsersAndFriends(User user, List friends);}onConflict⽤来指定当发⽣冲突是的策略。⽐如将@Index的unique属性设置为true,当产⽣冲突时,默认情况下为会导致崩溃,这⾥设置为E,当发⽣冲突时替换⽼数据。关于其他的冲突策略可以阅读SQL As Understood BySQLite进⾏了解。除了可以将@Insert注解的⽅法返回值设置为void外,还可以将⽅法的返回值设置为long, Long, Long[] 或者 List。如果参数是单个实体,返回long或者Long,该值是插⼊新条⽬的rowId。如果参数是集合或者多个参数时,则返回Long[]或者List更新使⽤@Update注解⽅法,可以使⽤参数实体的值更新主键值和参数实体的主键相同的⾏。@Daopublic interface MyDao { @Update public void users);}@Update注解的⽅法还可以返回int,表⽰受影响的⾏数。删除使⽤@Delete注解⽅法,可以删除主键值和参数实体的主键相同的⾏。@Daopublic interface MyDao { @Delete public void users);}@Delete注解的⽅法还可以返回int,表⽰删除的⾏数。使⽤@Query的⽅法@Query的值为SQL语句,可以被SQLite执⾏。@Query⽀持查询语句,删除语句和更新语句,不⽀持插⼊语句。@Daopublic interface MyDao { @Query("SELECT * FROM user") public User[] loadAllUsers();}传⼊参数Room会在编译时进⾏检查,当代码中包含语法错误,或者表不存在,Room将在编译时出现错误信息。如果我们想获取指定id的⽤户,该怎么办。@Query的value中⽀持添加绑定参数,该参数必须找到与之匹配的⽅法参数,并取得该⽅法参数的值。@Daopublic interface MyDao {//传⼊单个参数 @Query("SELECT * FROM user WHERE age > :minAge") public User[] loadAllUsersOlderThan(int minAge);}在这个例⼦中绑定参数:minAge与⽅法参数minAge相匹配。⽽且允许传⼊多个参数,或者多次引⽤相同的参数@Daopublic 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 findUserWithName(String search);}此外,Room还允许传⼊⼀个参数集合@Daopublic interface MyDao { @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)") public List loadUsersFromRegions(List regions);}返回列的⼦集多数情况下,你只需要获取实体的少数⼏个字段。例如,你的ui可能只展⽰⽤户的名和姓,⽽不是每个⽤户的详细信息。通过只获取需要的列,可以节省资源,并且查询速度更快。只要可以将查询的结果映射到返回的对象上,Room允许返回任何java对象。例如,可以创建如下java对象来获取⽤户的名和姓。public class NameTuple { @ColumnInfo(name="first_name") public String firstName; @ColumnInfo(name="last_name") public String lastName;}在查询⽅法中使⽤该对象@Daopublic interface MyDao { @Query("SELECT first_name, last_name FROM user") public List loadFullName();}如果返回的列NameTuple不存在对应的字段,编译时会出现警告。RxJavaRoom也可以返回RxJava2的Publisher和Flowable对象。要使⽤这个功能需要在gradle中添加:rxjava2.@Daopublic interface MyDao { @Query("SELECT * from user where id = :id LIMIT 1") public Flowable loadUserById(int id);}直接返回CursorRoom还可以直接返回Cursor对象。@Daopublic interface MyDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") public Cursor loadRawUsersOlderThan(int minAge);}查询多个表@Daopublic interface MyDao { @Query("SELECT * FROM book " + "INNER JOIN loan ON _id = " + "INNER JOIN user ON = _id " + "WHERE LIKE :userName") public List findBooksBorrowedByNameSync(String userName);}使⽤类型转换器Room⽀持字符串和基本数据类型以及他们的包装类,但是如果不是基本数据类型,该如何存储呢?⽐如我们的User对象有个Date类型的字段birthday,我们该如何存储。Room提供了@TypeConverter可以将不可存储的类型转换为Room可以存储的类型。public class Converters { @TypeConverter public static Date fromTimestamp(Long value) { return value == null ? null : new Date(value); } @TypeConverter public static Long dateToTimestamp(Date date) { return date == null ? null : e(); }}上⾯的例⼦定义了两个⽅法,Room可以调⽤dateToTimestamp⽅法将Date转化为Long类型进⾏存储,也可以在查询的时候将获取的Long转换为Date对象。为了让Room调⽤该转换器,使⽤@TypeConverters注解将转换器添加到AppDatabase上。@Database(entities = {}, version = 1)@TypeConverters({})public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao();}数据库升级在app发布以后,我们可能会新增表或者修改原来表的结构,这就需要升级数据库。Room提供了 Migration 类⽤于迁移数据库,每⼀个Migration 需要在构造函数⾥指定开始版本和结束版本。在运⾏时,Room会按照提供版本的顺序顺序执⾏每个Migration的migrate()⽅法,将数据库升级到最新的版本。seBuilder(getApplicationContext(), , "database-name") .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { L("CREATE TABLE `Fruit` (`id` INTEGER, " + "`name` TEXT, PRIMARY KEY(`id`))"); }};static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override public void migrate(SupportSQLiteDatabase database) { L("ALTER TABLE Book " + " ADD COLUMN pub_year INTEGER"); }};
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1690215993a316108.html
评论列表(0条)