我有用户实体。
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.PrimaryKey;
import com.google.gson.annotations.SerializedName;
@Entity(foreignKeys = {
@ForeignKey(entity = Address.class, parentColumns = "addressId", childColumns = "fk_addressId", onDelete = ForeignKey.CASCADE),
@ForeignKey(entity = Company.class, parentColumns = "companyId", childColumns = "fk_companyId", onDelete = ForeignKey.CASCADE)
})
public class User {
@PrimaryKey(autoGenerate = true)
public int userId;
public String name;
public String username;
public String email;
public int fk_addressId;
public String phone;
public String website;
public int fk_companyId;
public User(@NonNull String name, @NonNull String username, @NonNull String email, int fk_addressId,
@NonNull String phone, @NonNull String website, int fk_companyId) {
this.name = name;
this.username = username;
this.email = email;
this.fk_addressId = fk_addressId;
this.phone = phone;
this.website = website;
this.fk_companyId = fk_companyId;
}
}
用户有两个外键,分别指向 Address 和 Company 实体。用户与地址、用户与公司之间是一对一的关系。
地址有一个外键指向 Geo(用于地理定位)。 Address 和 Geo 之间存在一对一的关系。
我想使用 Android 中的 Room 在一次查询中获取用户的地址、地理位置和公司信息。
我应该如何构建关系类来做到这一点?
此链接中有信息: https://developer.android.com/training/data-storage/room/relationships#java
但在这种情况下,用户实体有两个外键,一个指向地址,另一个指向公司。 (地址是指地理)。
我应该如何构建关系类以使用 Android 中的 Room 在一次查询中获取用户的地址、地理位置和公司信息?
这是地址实体:
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.PrimaryKey;
import com.google.gson.annotations.SerializedName;
@Entity(foreignKeys = {
@ForeignKey(entity = Geo.class, parentColumns = "geoId", childColumns = "fk_geoId", onDelete = ForeignKey.CASCADE)
})
public class Address {
@PrimaryKey(autoGenerate = true)
public int addressId;
public String street;
public String suite;
public String city;
public String zipCode;
public int fk_geoId;
public Address(@NonNull String street, @NonNull String suite, @NonNull String city,
@NonNull String zipCode, int fk_geoId) {
this.street = street;
this.suite = suite;
this.city = city;
this.zipCode = zipCode;
this.fk_geoId = fk_geoId;
}
}
这是地理实体:
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity
public class Geo {
@PrimaryKey(autoGenerate = true)
public int geoId;
public double lat;
public double lng;
public Geo(@NonNull double lat, @NonNull double lng) {
this.lat = lat;
this.lng = lng;
}
}
这里是公司实体:
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity
public class Company {
@PrimaryKey(autoGenerate = true)
public int companyId;
public String name;
public String catchPhrase;
public String bs;
public Company(@NonNull String name, @NonNull String catchPhrase, @NonNull String bs) {
this.name = name;
this.catchPhrase = catchPhrase;
this.bs = bs;
}
}
我应该如何构建关系类以使用 Android 中的 Room 在一次查询中获取用户的地址、地理位置和公司信息?
答案 0 :(得分:1)
首先,我建议更改一些列名称以使其独一无二,例如
将 name
类/实体中的 Company
更改为 companyName
name
类/实体中的User
的歧义@Embedded
的 prefix
参数,例如用于组合的 POJO 中的 @Embedded(prefix = "cmpny_")
(请参阅下面的 UserWithCompanyWithAddressWithGeo)所以 Company
可能是 :-
@Entity
public class Company {
@PrimaryKey(autoGenerate = true)
public int companyId;
public String companyName;
public String catchPhrase;
public String bs;
public Company(@NonNull String companyName, @NonNull String catchPhrase, @NonNull String bs) {
this.companyName = companyName;
this.catchPhrase = catchPhrase;
this.bs = bs;
}
}
其次,您并不需要单独的表来建立一对一的关系,您拥有事物的方式可以是一对多的(1 家公司可以拥有多个用户,1 个地址可以拥有多个用户,1 个地理区域可以拥有)许多地址)。请参阅答案末尾的其他内容。
接下来添加一个类 (POJO) 以获取所有组合数据,例如UserWithCompanyWithAddressWithGeo
。使用此方法,JOIN
s 将用于查询中,因此只需按照 :-
public class UserWithCompanyWithAddressWithGeo {
@Embedded
User user;
@Embedded
Company company;
@Embedded
Address address;
@Embedded
Geo geo;
public UserWithCompanyWithAddressWithGeo(){}
}
如前所述,查询中的 JOINS 将用于建立关系,因此在 a/the Dao 中添加以下内容:-
@Query("SELECT * FROM user JOIN company ON user.fk_companyId = companyId JOIN address ON user.fk_addressId = address.addressId JOIN geo ON address.fk_geoId = geo.geoId")
List<UserWithCompanyWithAddressWithGeo> getUserWithCompanyWithAddressWithGeo();
然后您可以使用 getUserWithCompanyWithAddressWithGeo() 获取所有用户的公司、地址和地理位置。
工作示例
使用上述内容,然后使用以下内容:-
public class MainActivity extends AppCompatActivity {
final String TAG = "MYDBINFO";
MyDatabase db;
AllDao dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = Room.databaseBuilder(this,MyDatabase.class,"mydb")
.allowMainThreadQueries()
.build();
dao = db.getAllDao();
int geo1Id = (int) dao.insertGeo(new Geo(52.5,72.3));
int geo2Id = (int) dao.insertGeo(new Geo(33.3,66.6));
int address1Id = (int) dao.insertAddress(new Address("Street1","suite1","city1","1234",geo1Id));
int address2Id = (int) dao.insertAddress(new Address("Street2","Suite2","City2","4321",geo2Id));
int company1Id = (int) dao.insertCompany(new Company("Company1","Catchphrase1","bs1"));
int company2Id = (int) dao.insertCompany(new Company("Company2","Catchphrase2","bs2"));
int user1 = (int) dao.insertUser(new User("Name1","UserName1","Email1",address1Id,"Phone1","Website1",company1Id));
int user2 = (int) dao.insertUser(new User("Name2","UserName2","Email1",address2Id,"Phone2","Website2",company2Id));
List<UserWithCompanyWithAddressWithGeo> userWithCompanyWithAddressWithGeos = dao.getUserWithCompanyWithAddressWithGeo();
for(UserWithCompanyWithAddressWithGeo userWithCompanyWithAddressWithGeo : userWithCompanyWithAddressWithGeos) {
LogUserWithCompanyWithAddress(userWithCompanyWithAddressWithGeo);
}
}
private void LogUserWithCompanyWithAddress(UserWithCompanyWithAddressWithGeo uwcwa) {
Log.d(TAG,
"User-Name = " + uwcwa.user.name +
" User-Email = " + uwcwa.user.email +
"\n\tCompany - Name = " + uwcwa.company.companyName + " Company - Catchphrase = " + uwcwa.company.catchPhrase +
"\n\tAddress - Street = " + uwcwa.address.street + " Address - City = " + uwcwa.address.city +
"\n\t\tGeo - Lat = " + uwcwa.geo.lat + " Geo - Long = " + uwcwa.geo.lng
);
}
}
日志中的结果(第一次运行):-
2021-04-08 06:58:54.635 D/MYDBINFO: User-Name = Name1 User-Email = Email1
Company - Name = Company1 Company - Catchphrase = Catchphrase1
Address - Street = Street1 Address - City = city1
Geo - Lat = 52.5 Geo - Long = 72.3
2021-04-08 06:58:54.636 D/MYDBINFO: User-Name = Name2 User-Email = Email1
Company - Name = Company2 Company - Catchphrase = Catchphrase2
Address - Street = Street2 Address - City = City2
Geo - Lat = 33.3 Geo - Long = 66.6
附加
Re 1-Many 关系考虑以下已添加到用于测试的活动中(在其他插入之后但在提取之前)
int userx = (int) dao.insertUser(new User("NameX","UserX","EmailX",address1Id,"PhoneX","WebsiteX",company2Id));
:-
2021-04-08 07:34:59.851 12295-12295/a.a.so66992840javaroomonetoones D/MYDBINFO: User-Name = Name1 User-Email = Email1
Company - Name = Company1 Company - Catchphrase = Catchphrase1
Address - Street = Street1 Address - City = city1
Geo - Lat = 52.5 Geo - Long = 72.3
2021-04-08 07:34:59.851 12295-12295/a.a.so66992840javaroomonetoones D/MYDBINFO: User-Name = Name2 User-Email = Email1
Company - Name = Company2 Company - Catchphrase = Catchphrase2
Address - Street = Street2 Address - City = City2
Geo - Lat = 33.3 Geo - Long = 66.6
2021-04-08 07:34:59.851 12295-12295/a.a.so66992840javaroomonetoones D/MYDBINFO: User-Name = NameX User-Email = EmailX
Company - Name = Company2 Company - Catchphrase = Catchphrase2
Address - Street = Street1 Address - City = city1
Geo - Lat = 52.5 Geo - Long = 72.3
替代方法 - 使用@Relation
当按照初始方法嵌入实体时,查询需要使用 JOIN。但是,您可以避免使用 JOIN 并通过使用 @Relation 注释让空间有效地构建 JOIN。但是,当使用@Relation 时,会检索嵌入实体的列表/数组。
对于 3 级层次结构(用户 -> 地址 - 地理),您似乎需要 2 个级别的 @Relation,因此需要 2 个 POJO 类。
第一个(层次结构中的最低层)(地址-地理)可以是 AddressWithGeo :-
public class AddressWithGeo {
@Embedded
Address address;
@Relation(entity = Geo.class,entityColumn = "geoId",parentColumn = "fk_geoId")
List<Geo> geos;
public AddressWithGeo(){}
}
第二个(使用-> 地址和用户 -> 公司)可以是 UserWithCompanyWithAddressAndGeo :-
public class UserWithCompanyWithAddressAndGeo {
@Embedded
User user;
@Relation(entity = Company.class,entityColumn = "companyId",parentColumn = "fk_companyId")
List<Company> company;
@Relation(entity = Address.class,entityColumn = "addressId",parentColumn = "fk_addressId")
List<AddressWithGeo> addressWithGeos;
public UserWithCompanyWithAddressAndGeo(){}
}
要使用上述内容,您至少需要一个 Dao @Query 用于更高级别。但是,以下是两者的@Queries(因此允许使用 Geo 提取地址):-
@Transaction
@Query("SELECT * FROM address")
List<AddressWithGeo> getAddressWithGeos();
@Transaction
@Query("SELECT * FROM user")
List<UserWithCompanyWithAddressAndGeo> getUserWithCompanyWithAddressAndGeo();
以下内容已添加到 MainActivity :-
List<AddressWithGeo> addressWithGeos = dao.getAddressWithGeos(); // shows that AddressWithGeo can be used on it's own
List<UserWithCompanyWithAddressAndGeo> userWithCompanyWithAddressAndGeos = dao.getUserWithCompanyWithAddressAndGeo();
for (UserWithCompanyWithAddressAndGeo userWithCompanyWithAddressAndGeo: userWithCompanyWithAddressAndGeos) {
logUser(userWithCompanyWithAddressAndGeo.user);
for(Company cmpny: userWithCompanyWithAddressAndGeo.company) {
logCompany(cmpny);
}
for(AddressWithGeo awg: userWithCompanyWithAddressAndGeo.addressWithGeos) {
logAddress(awg.address);
for(Geo geo: awg.geos) {
logGeo(geo);
}
}
}
连同以下方法(使将输出写入日志更简单):-
private void logUser(User usr) {
Log.d(TAG,"Name = " + usr.name + " Username = " + usr.username + " Email = " + usr.email + " Phone = " + usr.phone + " Website = " + usr.website);
}
private void logCompany(Company cmpny) {
Log.d(TAG,"\tName = " + cmpny.companyName + " CatchPhrase = " + cmpny.catchPhrase + " BS = " + cmpny.bs);
}
private void logAddress(Address addr) {
Log.d(TAG,"\tStreet = " + addr.street + " Suite = " + addr.suite + " City = " + addr.city + " Zipcode = " + addr.zipCode);
}
private void logGeo(Geo geo) {
Log.d(TAG,"\t\t Lat = " + geo.lat + " Long = " + geo.lng);
}
运行时的结果(除了前面的)是:-
2021-04-08 18:26:04.595 D/MYDBINFO: Name = Name1 Username = UserName1 Email = Email1 Phone = Phone1 Website = Website1
2021-04-08 18:26:04.595 D/MYDBINFO: Name = Company1 CatchPhrase = Catchphrase1 BS = bs1
2021-04-08 18:26:04.595 D/MYDBINFO: Street = Street1 Suite = suite1 City = city1 Zipcode = 1234
2021-04-08 18:26:04.595 D/MYDBINFO: Lat = 52.5 Long = 72.3
2021-04-08 18:26:04.595 D/MYDBINFO: Name = Name2 Username = UserName2 Email = Email1 Phone = Phone2 Website = Website2
2021-04-08 18:26:04.595 D/MYDBINFO: Name = Company2 CatchPhrase = Catchphrase2 BS = bs2
2021-04-08 18:26:04.595 D/MYDBINFO: Street = Street2 Suite = Suite2 City = City2 Zipcode = 4321
2021-04-08 18:26:04.596 D/MYDBINFO: Lat = 33.3 Long = 66.6
2021-04-08 18:26:04.596 D/MYDBINFO: Name = NameX Username = UserX Email = EmailX Phone = PhoneX Website = WebsiteX
2021-04-08 18:26:04.596 D/MYDBINFO: Name = Company2 CatchPhrase = Catchphrase2 BS = bs2
2021-04-08 18:26:04.596 D/MYDBINFO: Street = Street1 Suite = suite1 City = city1 Zipcode = 1234
2021-04-08 18:26:04.596 D/MYDBINFO: Lat = 52.5 Long = 72.3