苏州网站建设公司有哪些,中国建设教育协会证书查询网站,公司简历模板免费,淘宝网页设计网站文章目录 引入EntitiesPrimary Key主键索引和唯一性对象之间的关系外键获取关联的Entity对象嵌套对象Data Access Objects#xff08;DAOs#xff09;使用Query注解的方法简单的查询带参数查询返回列的子集可被观察的查询 数据库迁移用法 引入
原始的SQLite有以下两个缺点: … 文章目录 引入EntitiesPrimary Key主键索引和唯一性对象之间的关系外键获取关联的Entity对象嵌套对象Data Access ObjectsDAOs使用Query注解的方法简单的查询带参数查询返回列的子集可被观察的查询 数据库迁移用法 引入
原始的SQLite有以下两个缺点:
没有编译时SQL语句的检查。尤其是当你的数据库表发生变化时需要手动的更新相关代码这会花费相当多的时间并且容易出错。编写大量SQL语句和Java对象之间相互转化的代码。
针对以上的缺点Google提供了Room来解决这些问题。Room包含以下三个重要组成部分: Database:使用注解申明一个类注解中包含若干个Entity类这个Database类主要负责创建数据库以及获取数据对象的。 Entities:表示每个数据库的总的一个表结构同样也是使用注解表示类中的每个字段都对应表中的一列。 DAO:Data Access Object的缩写表示从从代码中直接访问数据库屏蔽sql语句。 和传统写数据库创建访问的代码大概形式差不多。以存储User信息为例
Entity
public class User {PrimaryKeyprivate int uid;ColumnInfo(name first_name)private String firstName;ColumnInfo(name last_name)private String lastName;// Getters and setters are ignored for brevity, // but theyre required for Room to work.//Getters和setters为了简单起见就省略了但是对Room来说是必须的
}Dao
public interface UserDao {Query(SELECT * FROM user)ListUser getAll();Query(SELECT * FROM user WHERE uid IN (:userIds))ListUser 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);Insertvoid insertAll(User... users);Deletevoid delete(User user);
}Database(entities {User.class}, version 1)
public abstract class AppDatabase extends RoomDatabase {public abstract UserDao userDao();
}在创建了上面三个文件后就可以通过如下代码创建数据库了:
AppDatabase db Room.databaseBuilder(getApplicationContext(),AppDatabase.class, database-name).build();下面详细介绍提到的各个部分:
Entities
Entity 如果上面的User类中包含一个字段是不希望存放到数据库中的那么可以用Ignore注解这个字段:
Entity
class User {PrimaryKeypublic int id;public String firstName;public String lastName;//不需要被存放到数据库中IgnoreBitmap picture;
}Room持久化一个类的field必须要求这个field是可以访问的。可以把这个field设为public或者设置setter和getter。
Entity
public class User {PrimaryKey(autoGenerate true)NonNullpublic int id;ColumnInfo(name user_name, defaultValue )public String userName;ColumnInfo(name user_age)public int userAge;ColumnInfo(name nick_name)public String nickName;ColumnInfo(name address)public String address;
}Entity就是表示数据库中的表所映射的实体类 PrimaryKey表示主键这里是id autoGenerate true 是自增 NonNull表示不为空。 ColumnInfo表示表中的列名name user_name表示列名的值 //可以不用写这个ColumnInfo注解写它主要是为了设置列名不写则使用变量名做为列名。 Primary Key主键
每个Entity都必须定义一个field为主键即使是这个Entity只有一个field。如果想要Room生成自动的primary key可以使用PrimaryKey的autoGenerate属性。如果Entity的primary key是多个Field的复合Key可以向下面这样设置:
Entity(primaryKeys {firstName, lastName})//括号内设置主键
class User {public String firstName;public String lastName;IgnoreBitmap picture;
}在默认情况下Room使用类名作为数据库表的名称。如果想要设置不同的名称可以参考下面的代码设置表名tableName为users:
Entity(tableName users)
class User {...
}和设置tableName相似Room默认使用field的名称作为表的列名。如果想要使用不同的名称可以通过ColumnInfo(name first_name)设置代码如下:
Entity(tableName users)
class User {PrimaryKeypublic int id;ColumnInfo(name first_name)//自定义列名public String firstName;ColumnInfo(name last_name)public String lastName;IgnoreBitmap picture;
}索引和唯一性
根据访问数据库的方式你可能想对特定的field建立索引来加速你的访问。下面这段代码展示了如何在Entity中添加索引或者复合索引:
Entity(indices {Index(name),Index(value {last_name, address})})下面的代码展示了对数据库中特定field设置唯一性(这个表中的firstName和lastName不能同时相同):
Entity(indices {Index(value {first_name, last_name},unique true)})对象之间的关系
SQLite是关系型数据库那么就可以在两个对象之间建立联系。大多数ORM库允许Entity对象互相引用但Room明确禁止了这样做。
既然不允许建立直接的关系Room提供以外键的方式在两个Entity之间建立联系。
外键
例如有一个Pet类需要和User类建立关系可以通过ForeignKey来达到这个目的代码如下:
Entity(foreignKeys ForeignKey(entity User.class,parentColumns id,childColumns user_id))
class Pet {PrimaryKeypublic int petId;public String name;ColumnInfo(name user_id)public int userId;
}外键可以允许你定义被引用的Entity更新时发生的行为。例如你可以定义当删除User时对应的Pet类也被删除。可以在ForeignKey中添加onDelete CASCADE实现。
获取关联的Entity
Entity之间可能也有一对多之间的关系。比如一个User有多个Pet通过一次查询获取多个关联的Pet。
public class UserAndAllPets {Embedded//嵌入...中public User user;Relation(parentColumn id, entityColumn user_id)public ListPet pets;
}Dao
public interface UserPetDao {Query(SELECT * from User)public ListUserAndAllPets loadUserAndPets();
}使用 Relation 注解的field必须是一个List或者一个Set。通常情况下 Entity 的类型是从返回类型中推断出来的可以通过定义 entity()来定义特定的返回类型。 用 Relation 注解的field必须是public或者有public的setter。这是因为加载数据是分为两步的① 父Entity被查询 ②触发用 Relation 注解的entity的查询。所以在上面UserAndAllPets例子中首先User所在的数据库被查询然后触发查询Pets的查询。即Room首先出创建一个空的对象然后设置父Entity和一个空的list。在第二次查询后Room将会填充这个list。
对象嵌套对象
有时候需要在类里面把另一个类作为field这时就需要使用Embedded。这样就可以像查询其他列一样查询这个field。 例如User类可以包含一个field Address代表User的地址包括所在街道、城市、州和邮编。代码如下:
class Address {public String street;public String state;public String city;ColumnInfo(name post_code)public int postCode;
}Entity
class User {PrimaryKeypublic int id;public String firstName;Embeddedpublic Address address;
}在存放User的表中包含的列名如下:id,firstName,street,state,city,post_code。 Embedded的field中也可以包含其他Embedded的field。 如果多个Embedded的field是类型相同的可以通过设置prefix来保证列的唯一性。
Data Access ObjectsDAOs
DAOs是数据库访问的抽象层。 Dao可以是一个接口也可以是一个抽象类。如果是抽象类那么它可以接受一个RoomDatabase作为构造器的唯一参数。 Room不允许在主线程中防伪数据库除非在builder里面调用allowMainThreadQueries()。因为访问数据库是耗时的可能阻塞主线程引起UI卡顿。
添加方便使用的方法:
Insert:使用Insert注解的方法Room将会生成插入的代码。
Dao
public interface MyDao {Insert(onConflict OnConflictStrategy.REPLACE)public void insertUsers(User... users);Insertpublic void insertBothUsers(User user1, User user2);Insertpublic void insertUsersAndFriends(User user, ListUser friends);
}如果Insert方法只接受一个参数那么将返回一个long对应着插入的rowId。如果接受多个参数或者数组或者集合那么就会返回一个long的数组或者list。
我们看到直接中有个参数onConflict表示的是当插入的数据已经存在时候的处理逻辑有三种操作逻辑REPLACE、ABORT和IGNORE。如果不指定则默认为ABORT终止插入数据。这里我们将其指定为REPLACE替换原有数据。
Update
Dao
public interface MyDao {Updatepublic void updateUsers(User... users);
}也可以让update方法返回一个int型的整数代表被update的行号。根据主键搜索
Delete
Dao
public interface MyDao {Deletepublic void deleteUsers(User... users);
}和update方法一样也可以返回一个int型的整数代表被delete的行号。
使用Query注解的方法 Query注解的方法在编译时就会被检查如果有任何查询的问题都会抛出编译异常而不是等到运行以后才会发现异常。 Room也会检查查询返回值的类型如果返回类型的字段和数据路列名存在不一致会收到警告。如果两者完全不一致就会产生错误。 简单的查询
Dao
public interface MyDao {Query(SELECT * FROM user)public User[] loadAllUsers();
}带参数查询
下面的代码显示了如何根据年龄条件查询User信息:
Dao
public interface MyDao {Query(SELECT * FROM user WHERE age :minAge)public User[] loadAllUsersOlderThan(int minAge);
}同理这里也会在编译时做类型检查如果表中没有age这个列那么就会抛出错误。 也可以穿入多个参数或一个参数作为多个约束条件查询用户:
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 ListUser findUserWithName(String search);
}返回列的子集
有时可能只需要Entity的几个field例如只需要获取User的姓名就行了。通过只获取这两列的数据不仅能够节省宝贵的资源还能加快查询速度:
public class NameTuple {ColumnInfo(namefirst_name)public String firstName;ColumnInfo(namelast_name)public String lastName;
}Dao
public interface MyDao {Query(SELECT first_name, last_name FROM user)public ListNameTuple loadFullName();
}可被观察的查询
通过和LiveData的配合使用就可以实现当数据库内容发生变化时自动收到变化后的数据的功能。
Dao
public interface MyDao {Query(SELECT first_name, last_name FROM user WHERE region IN (:regions))public LiveDataListUser loadUsersFromRegionsSync(ListString regions);
}数据库迁移
随着业务的扩展有时候需要对数据库调整一些字段。当数据库升级时需要保存已有的数据。 Room使用Migration来实现数据库的迁移。每个Migration都指定了startVersion和endVersion。在运行的时候Room运行每个Migration的migrate()方法按正确的顺序来迁移数据库到下个版本。如果没有提供足够的迁移信息Room会重新创建数据库这意味着将会失去原来保存的信息。
用法
Room.databaseBuilder(getApplicationContext(), MyDb.class, database-name).addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();static final Migration MIGRATION_1_2 new Migration(1, 2) {Overridepublic void migrate(SupportSQLiteDatabase database) {database.execSQL(CREATE TABLE Fruit (id INTEGER, name TEXT, PRIMARY KEY(id)));}
};static final Migration MIGRATION_2_3 new Migration(2, 3) {Overridepublic void migrate(SupportSQLiteDatabase database) {database.execSQL(ALTER TABLE Book ADD COLUMN pub_year INTEGER);}
};private void initDB() {//本地持久化数据库db Room.databaseBuilder(getApplicationContext(), MyDatabase.class, DemoDB)//是否允许在主线程上操作数据库默认false。.allowMainThreadQueries()//数据库创建和打开的事件会回调到这里可以再次操作数据库.addCallback(new CallBack()).build();
}