我试图重写Android实体对象的创建。
当从数据库实例化对象时,我希望初始化其他属性。我跟踪了Android实体文档
我使用的是房间版本:room-runtime:2.1.0
我试图在setter中和构造函数中记录消息,但是没有一个消息出现在我的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创建对象时,可以添加特定的代码吗?比如在AzMobile设置器中?
那么房间是如何实例化对象的呢?属性是私有的,访问它的唯一方法是通过LogCat中似乎不被调用的setter。
答案后编辑
有空间的棘手之处在于理解我们实体的实现是如何工作的。
我的实体是客户端对象,但当我从DB执行查询时,我使用ViewModel (类似于ImportantInformationsClientViewModel)执行查询,我认为,由于Room只知道客户端实体,它会将我的ViewModel封装在实体中,并从该实体神奇地构建它(这并不愚蠢)。对我来说很有意义..。)
在检查了我的android生成的DAO实现(ScheduleDao_Impl)之后,我看到这个房间实际上是直接构建ViewModel对象的。我只是在ViewModel中移动了我的属性和函数,一切都成功了。
如果我必须列出要知道的重要事情:
android-room只使用@Entity在SQLite数据库中构建对象模型,而不使用@实体构建查询对象android-room将生成一个YourDao_Impl.java对象,您可以使用CTRL + MAJ + F访问它。android-room将需要ctor或setter或两者都需要(它只需要访问所有属性)ApplicationDatabase_Impl文件将帮助你理解安卓房间是如何工作的,以及所有的东西是如何包装在一起的。发布于 2019-07-19 13:07:37
最新情况(澄清后)
我很惊讶你没看到那些电话。因为通过检查生成的DAO,我可以看到房间在做什么。它通过SQLite进行查询,并使用游标在结果中移动,调用CTOR。
DAO是在编译时生成的,所以在构建项目之后,如果您在macOS (或者安卓工作室使用的任何快捷方式查找),点击“ctrl F”或"cmd“,然后尝试查找DAO的名称。您将看到YourDao和YourDao_Impl() -> --这是自动生成的。)打开它。
这是来自我的DAOs实现之一的一个简化的副本/粘贴:
“模型”是RealTimeData。Dao的方法是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;
}
}所以你的意思是,当房间实例化这个,你的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将接受它的插入或类似。
这就是你想做的吗?
发布于 2019-07-19 12:51:33
每个实体必须有一个no-arg构造函数或一个参数匹配字段(基于类型和名称)的构造函数。构造函数不必以参数的形式接收所有字段,但如果字段未传递给构造函数,则它应该是公共的或具有公共设置器的。如果匹配的构造函数可用,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;
}
}https://stackoverflow.com/questions/57112805
复制相似问题