为什么 Flutter Floor DAO findAll returns 实体设置为空 ID?
Why Flutter Floor DAO findAll returns entity set with null ids?
这是一个使用 Floor(相当于 Jetpack 的 Room 的包)的 Flutter 项目。
我有一个带有自动递增 ID 的实体(请注意,这是预空安全代码,在我现在从 post 空安全代码段开始的答案中):
const String ACTIVITIES_TABLE_NAME = 'activities';
@Entity(
tableName: ACTIVITIES_TABLE_NAME,
indices: [
Index(value: ['start'])
],
)
class Activity {
@PrimaryKey(autoGenerate: true)
int id;
@ColumnInfo(name: 'device_name')
final String deviceName;
@ColumnInfo(name: 'device_id')
final String deviceId;
final int start;
Activity({
this.deviceName,
this.deviceId,
this.start,
});
}
我有这个 DAO:
@dao
abstract class ActivityDao {
@Query('SELECT * FROM $ACTIVITIES_TABLE_NAME ORDER BY start DESC')
Future<List<Activity>> findAllActivities();
@insert
Future<int> insertActivity(Activity activity);
}
到目前为止,我在我的应用程序中记录了五项活动,hunky-dory。
_database =
await $FloorAppDatabase.databaseBuilder('app_database.db').build();
...
_activity =
Activity(deviceName: device.name, deviceId: device.id.id, start: _lastRecord.millisecondsSinceEpoch);
final id = await _database.activityDao.insertActivity(_activity);
_activity.id = id;
然后在另一个视图中列出它们,但是有一个潜伏的问题。
final data = await _database.activityDao.findAllActivities();
当我使用这种非常简单的方法查询实体时,我的数据库中所有大约五个活动都返回了一个 id
字段,其中填充了 null
。这使得该实体完全无用,因为我想对其执行的任何其他操作都因缺少实际 ID 而失败。我做错了什么吗?
我主要使用 MySQL、Postgres 和非 SQLite RDBMS。据我了解,在 SQLite 中,每一行都有一个唯一的 rowid
,并且通过声明我的自动增量 id
主键字段,基本上我为该 rowid 取了别名?不管是什么,我都需要身份证。不能为空。
我正在调试 Floor 的内核。
Future<List<T>> queryList<T>(
final String sql, {
final List<dynamic> arguments,
@required final T Function(Map<String, dynamic>) mapper,
}) async {
final rows = await _database.rawQuery(sql, arguments);
return rows.map((row) => mapper(row)).toList();
}
此时 rows
仍然正确填写了 ID,尽管行中的实体只是动态值列表。 rows.map
应该将它们映射到实体对象,并且由于某种原因不能携带 id?这可能是地板错误吗?
好的,现在我看到在生成的 database.g.dart
映射器中没有 id:
static final _activitiesMapper = (Map<String, dynamic> row) => Activity(
deviceName: row['device_name'] as String,
deviceId: row['device_id'] as String,
start: row['start'] as int);
这就解释了为什么 id 是 null
。但这是生成的代码,我如何告诉 Floor 我需要 id?或者我为什么要告诉它,它必须默认存在,谁不想知道一个对象的主键?
好的,这是因为我没有将 id
作为构造函数参数。我没有那个,因为它是一个自动递增字段,我不是决定它的人。但是,如果没有将它作为构造函数参数,生成的代码就无法将它作为参数与映射器一起传递,因此它会将它从映射器中移除。所以我添加了 id 作为构造函数参数。
Post null-safety 版本:
class Activity {
@PrimaryKey(autoGenerate: true)
int? id;
@ColumnInfo(name: 'device_name')
final String deviceName;
@ColumnInfo(name: 'device_id')
final String deviceId;
final int start;
final int end;
Activity({
this.id,
required this.deviceName,
required this.deviceId,
required this.start,
this.end: 0,
});
}
这个Floor GitHub问题我也加在这里,看来以后可能会有注解:https://github.com/vitusortner/floor/issues/527
前 null-safety 版本:
import 'package:meta/meta.dart';
class Activity {
@PrimaryKey(autoGenerate: true)
int id;
@ColumnInfo(name: 'device_name')
@required
final String deviceName;
@ColumnInfo(name: 'device_id')
@required
final String deviceId;
final int start;
Activity({
this.id: null,
this.deviceName,
this.deviceId,
this.start,
});
}
编译器对 null 有点不确定,但在 flutter packages pub run build_runner build
之后 database.g.dart
的映射器也有 id
。
这是一个使用 Floor(相当于 Jetpack 的 Room 的包)的 Flutter 项目。 我有一个带有自动递增 ID 的实体(请注意,这是预空安全代码,在我现在从 post 空安全代码段开始的答案中):
const String ACTIVITIES_TABLE_NAME = 'activities';
@Entity(
tableName: ACTIVITIES_TABLE_NAME,
indices: [
Index(value: ['start'])
],
)
class Activity {
@PrimaryKey(autoGenerate: true)
int id;
@ColumnInfo(name: 'device_name')
final String deviceName;
@ColumnInfo(name: 'device_id')
final String deviceId;
final int start;
Activity({
this.deviceName,
this.deviceId,
this.start,
});
}
我有这个 DAO:
@dao
abstract class ActivityDao {
@Query('SELECT * FROM $ACTIVITIES_TABLE_NAME ORDER BY start DESC')
Future<List<Activity>> findAllActivities();
@insert
Future<int> insertActivity(Activity activity);
}
到目前为止,我在我的应用程序中记录了五项活动,hunky-dory。
_database =
await $FloorAppDatabase.databaseBuilder('app_database.db').build();
...
_activity =
Activity(deviceName: device.name, deviceId: device.id.id, start: _lastRecord.millisecondsSinceEpoch);
final id = await _database.activityDao.insertActivity(_activity);
_activity.id = id;
然后在另一个视图中列出它们,但是有一个潜伏的问题。
final data = await _database.activityDao.findAllActivities();
当我使用这种非常简单的方法查询实体时,我的数据库中所有大约五个活动都返回了一个 id
字段,其中填充了 null
。这使得该实体完全无用,因为我想对其执行的任何其他操作都因缺少实际 ID 而失败。我做错了什么吗?
我主要使用 MySQL、Postgres 和非 SQLite RDBMS。据我了解,在 SQLite 中,每一行都有一个唯一的 rowid
,并且通过声明我的自动增量 id
主键字段,基本上我为该 rowid 取了别名?不管是什么,我都需要身份证。不能为空。
我正在调试 Floor 的内核。
Future<List<T>> queryList<T>(
final String sql, {
final List<dynamic> arguments,
@required final T Function(Map<String, dynamic>) mapper,
}) async {
final rows = await _database.rawQuery(sql, arguments);
return rows.map((row) => mapper(row)).toList();
}
此时 rows
仍然正确填写了 ID,尽管行中的实体只是动态值列表。 rows.map
应该将它们映射到实体对象,并且由于某种原因不能携带 id?这可能是地板错误吗?
好的,现在我看到在生成的 database.g.dart
映射器中没有 id:
static final _activitiesMapper = (Map<String, dynamic> row) => Activity(
deviceName: row['device_name'] as String,
deviceId: row['device_id'] as String,
start: row['start'] as int);
这就解释了为什么 id 是 null
。但这是生成的代码,我如何告诉 Floor 我需要 id?或者我为什么要告诉它,它必须默认存在,谁不想知道一个对象的主键?
好的,这是因为我没有将 id
作为构造函数参数。我没有那个,因为它是一个自动递增字段,我不是决定它的人。但是,如果没有将它作为构造函数参数,生成的代码就无法将它作为参数与映射器一起传递,因此它会将它从映射器中移除。所以我添加了 id 作为构造函数参数。
Post null-safety 版本:
class Activity {
@PrimaryKey(autoGenerate: true)
int? id;
@ColumnInfo(name: 'device_name')
final String deviceName;
@ColumnInfo(name: 'device_id')
final String deviceId;
final int start;
final int end;
Activity({
this.id,
required this.deviceName,
required this.deviceId,
required this.start,
this.end: 0,
});
}
这个Floor GitHub问题我也加在这里,看来以后可能会有注解:https://github.com/vitusortner/floor/issues/527
前 null-safety 版本:
import 'package:meta/meta.dart';
class Activity {
@PrimaryKey(autoGenerate: true)
int id;
@ColumnInfo(name: 'device_name')
@required
final String deviceName;
@ColumnInfo(name: 'device_id')
@required
final String deviceId;
final int start;
Activity({
this.id: null,
this.deviceName,
this.deviceId,
this.start,
});
}
编译器对 null 有点不确定,但在 flutter packages pub run build_runner build
之后 database.g.dart
的映射器也有 id
。