我正在尝试覆盖Android Room实体对象的创建。
从数据库实例化对象时,我想初始化其他属性。我关注了Android Room实体Documentation
我正在使用会议室版本:room-runtime:2.1.0
我试图在设置器和构造函数中记录消息,但是消息没有出现在LogCat中。
@Entity
// EDIT AFTER SOLUTION. You have to do this in the class that room is using to query the data, in my case it was a viewModel.
public class Client /* or ImportantInformationsClientViewModel */ {
@SerializedName("azEMail")
private String azEMail;
@SerializedName("azFirstName")
private String azFirstName;
@SerializedName("azMobile")
private String azMobile;
@Ignore
private MyMobileObject;
public Client(String azEMail, String azFirstName, String azMobile) {
Log.d("CLIENT_LOG", "Constructor is instanciated"); // Never logged
this.azEMail = azEMail;
this.azFirstName = azFirstName;
this.azMobile = azMobile;
// I would like to instantiate MyMobileObject each time this constructor (or the setter) is called
}
public String getAzEMail() {
return azEMail;
}
public void setAzEMail(String azEMail) {
Log.d("CLIENT_LOG", "Setter is called"); // Never Logged
this.azEMail = azEMail;
}
public String getAzFirstName() {
return azFirstName;
}
public void setAzFirstName(String azFirstName) {
this.azFirstName = azFirstName;
}
public String getAzMobile() {
return azMobile;
}
public void setAzMobile(String azMobile) {
this.azMobile = azMobile;
// I would like to instantiate MyMobileObject each time this setter (or the constructor) is called
}
public String getAzName() {
return azName;
}
public void setAzName(String azName) {
this.azName = azName;
}
public void setupObject() {
// One ugly way to fix the problem is to call this method when my object is created. I want to avoid this.
}
}
我发现解决该问题的一种方法是在对象中创建一个setupObject
方法,并在查询中找到对象后调用此方法。它确实可以工作,但是有点丑陋,而且它增加了更多的代码和复杂性。我正在努力避免这种情况。
是否可以添加将在android-room创建对象时调用的特定代码?例如在AzMobile设置器中吗?
房间如何实例化对象呢?属性是私有的,访问它的唯一方法是通过在LogCat中似乎未调用的设置器。
有空间的棘手的事情是了解我们实体的实现方式。
我的实体是一个客户端对象,但是当我从数据库进行查询时,我正在使用ViewModel(类似于ImportantInformationsClientViewModel之类)进行查询。 我以为,由于Room仅了解客户实体,因此它将ViewModel包裹在实体中,然后从Entity神奇地构建了它(这不是那么愚蠢。对我来说这很有意义。。)
检查了我的android-room生成的DAO实现(ScheduleDao_Impl
)后,我看到该房间实际上是在直接构建ViewModel对象。我只是在ViewModel中移动了属性和函数,一切正常。
如果我必须列出要知道的重要事项:
android-room
仅使用@Entity来构建SQLite数据库中的对象模型,而不使用@Entity来构建查询对象
android-room
将在构建应用程序时生成YourDao_Impl.java对象,您可以使用CTRL + MAJ + F
android-room
将需要ctor或setter或两者(仅需要访问所有属性)
花几个小时检查所有ApplicationDatabase_Impl
文件,这将帮助您了解android-room的工作原理以及所有内容如何包装在一起。
答案 0 :(得分:1)
很奇怪您没有看到这些电话。由于通过检查生成的DAO,我可以看到ROOM在做什么。它通过SQLite查询,并使用游标在结果之间移动,从而调用CTOR。
DAO是在编译时生成的,因此,在构建项目后,如果您使用的是macOS(或Android Studio用于查找的快捷方式),请按“ ctrl-shift-F”或“ cmd”,然后尝试找到您的DAO的名称。您将看到YourDao
和YourDao_Impl()
->这是自动生成的。 :)
打开那个。
这是我的DAO实现之一的简化复制/粘贴:
“模型”为RealTimeData
。道的方法是loadAll()
,很明显它返回了List<RealTimeData>
。
这里是方法(删除了最不相关的内容):我在行中添加了注释。
@Override
public List<RealTimeData> loadAll() {
/// PREPARE THE QUERY, SQL, AND CURSOR.
final String _sql = "SELECT * FROM realtime_data";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
final Cursor _cursor = __db.query(_statement);
try {
// OBTAIN THE COLUMN NAMES FROM THE TABLE DEFINITION
final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("id");
final int _cursorIndexOfJsonData = _cursor.getColumnIndexOrThrow("json_data");
final int _cursorIndexOfIsSent = _cursor.getColumnIndexOrThrow("is_sent");
final int _cursorIndexOfDeviceId = _cursor.getColumnIndexOrThrow("device_id");
final int _cursorIndexOfDateCreated = _cursor.getColumnIndexOrThrow("date_created");
// THIS WILL STORE THE RESULTS
final List<RealTimeData> _result = new ArrayList<RealTimeData>(_cursor.getCount());
// ITERATE IT, CREATE A "RealTimeData" AND POPULATE IT.
while(_cursor.moveToNext()) {
final RealTimeData _item;
final String _tmpId;
_tmpId = _cursor.getString(_cursorIndexOfId);
final String _tmpJsonData;
_tmpJsonData = _cursor.getString(_cursorIndexOfJsonData);
final Date _tmpDateCreated;
final Long _tmp;
// SOME THINGS NEED EXTRA CHECKS, THIS IS A DATE FIELD, STORED AS "long", SO NULL MUST BE CHECKED OR THE DATE CONVERTER WOULD THROW NPE
if (_cursor.isNull(_cursorIndexOfDateCreated)) {
_tmp = null;
} else {
_tmp = _cursor.getLong(_cursorIndexOfDateCreated);
}
// IT'S A DATE, SO CALL THE DATE CONVERTER (supplied via the @TypeConverter() annotation)
_tmpDateCreated = DateConverter.toDate(_tmp);
// BAM: INVOKE THE CTOR
_item = new RealTimeData(_tmpId,_tmpJsonData,_tmpDateCreated);
// NOW USE SETTERS FOR THE "OTHERS"
final boolean _tmpIsSent;
final int _tmp_1;
_tmp_1 = _cursor.getInt(_cursorIndexOfIsSent);
_tmpIsSent = _tmp_1 != 0;
_item.setSent(_tmpIsSent);
final String _tmpDeviceId;
_tmpDeviceId = _cursor.getString(_cursorIndexOfDeviceId);
_item.setDeviceId(_tmpDeviceId);
// AND ADD IT TO THE RESULTS...
_result.add(_item);
}
// YOU GET THIS ONE :p
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
“本例中的模型”看起来完全像这样:
Entity(tableName = "realtime_data")
public class RealTimeData {
@PrimaryKey
@NonNull
private String id;
@ColumnInfo(name = "json_data")
private String jsonData;
@ColumnInfo(name = "is_sent")
private boolean isSent;
@ColumnInfo(name = "device_id")
private String deviceId;
@ColumnInfo(name = "date_created")
private Date dateCreated;
@Ignore
RealTimeData(@NonNull final String jsonData, @NonNull final Date dateCreated) {
id = UUID.randomUUID().toString();
this.jsonData = jsonData;
this.dateCreated = dateCreated;
}
RealTimeData(@Nonnull final String id, final String jsonData, final Date dateCreated) {
this.id = id;
this.jsonData = jsonData;
this.dateCreated = dateCreated;
}
String getJsonData() {
return jsonData;
}
@Nonnull
public String getId() {
return id;
}
public boolean isSent() {
return isSent;
}
public String getDeviceId() {
return deviceId;
}
public Date getDateCreated() {
return dateCreated;
}
public void setSent(final boolean sent) {
isSent = sent;
}
public void setDeviceId(final String deviceId) {
this.deviceId = deviceId;
}
}
所以您要说的是,当ROOM实例化此对象时,不会调用您的ctor吗?
对于有价值的东西,您可以有其他构造函数(前提是它们不会遮盖空的公共对象和/或使用所有字段的对象)。添加@Ignore
属性。
例如:(出于一致性考虑,我从哈迪克的回答中窃取了哈迪克的样本)
@Entity
public class User {
@PrimaryKey
private final int uid;
private String name;
@ColumnInfo(name = "last_name")
private String lastName;
public User(int uid) {
this.uid = uid;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Ignore
User(String firstName, String lastName) {
this.lastName = lastname;
this.name = firstName;
}
}
这将起作用,但是请记住,如果不使用“自动生成的”主键,则需要在Room接受其插入或类似操作之前将一个分配给该字段。
这是您要做什么吗?
答案 1 :(得分:0)
每个实体必须具有无参数构造函数,或者其参数匹配字段(基于类型和名称)的构造函数。构造函数不必将所有字段都作为参数来接收,但是如果没有将字段传递给构造函数,则它应该是公共的或具有公共的setter。如果有匹配的构造函数可用,Room将始终使用它。
例如
@Entity
public class User {
@PrimaryKey
private final int uid;
private String name;
@ColumnInfo(name = "last_name")
private String lastName;
public User(int uid) {
this.uid = uid;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}