如何调整我的父类和子类代码并添加适当的注释以正确实现外键和主键?
让assessment_id 始终是主键,即不要在子类中对其进行编码,然后在子类中有一个用于引用/映射/关联的字段。
所以 PerformanceAssessment 可以是:-
@Entity(tableName = "performance_assessment", foreignKeys = {
@ForeignKey(
entity = Assessment.class,
parentColumns = "assessment_id",
childColumns = "assessment_reference",
onUpdate = CASCADE,
onDelete = CASCADE
)
})
public class PerformanceAssessment extends Assessment {
private int assessment_reference; //<<<<<<<<<
private String type;
public PerformanceAssessment(int assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, int courseID, int assessment_reference, String type) {
super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
....
所有编译和基础表都将使用 :-
构建
_db.execSQL("CREATE TABLE IF NOT EXISTS `assessment_table` (`assessment_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `assessmentName` TEXT, `assessmentStart` TEXT, `assessmentEnd` TEXT, `courseID` INTEGER NOT NULL)");
_db.execSQL("CREATE TABLE IF NOT EXISTS `performance_assessment` (`assessment_reference` INTEGER NOT NULL, `type` TEXT, `assessment_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `assessmentName` TEXT, `assessmentStart` TEXT, `assessmentEnd` TEXT, `courseID` INTEGER NOT NULL, FOREIGN KEY(`assessment_reference`) REFERENCES `assessment_table`(`assessment_id`) ON UPDATE CASCADE ON DELETE CASCADE )");
- ObjectiveAssessment 显然类似。
但我还需要利用子主键作为父数据库的外键,这样当我删除绩效/目标评估实例时,我也可以在向下转换时删除评估实例。
如果可以通过 1(评估)- 多(绩效评估),一个评估有多个绩效评估怎么办?删除 1 个 PerformanceAsessment 将删除父评估,然后由于使用 onDelete CASCADE 将删除级联到所有其他 PerformanceAssessments 和 ObjectiveAssessment。删除不会向上传播到父级(这就是使用术语 CASCADE 的原因,因为它暗示向下而不是无论如何)。
例如,您有一个包含 4 个 PerformanceAssessments 的评估,假设有 3 个 ObjectiveAssessments,其中一个 PerformanceAssessments 被错误地添加了。是否必须删除所有内容并重新输入以更正 1 个错误的绩效评估。
您可以引入一个触发器来自动进行这种向上传播,删除任何内容并删除所有内容,如果这是您想要的。
其他建议
我的评估和绩效/目标评估是一对一的关系。一项评估只能具有绩效或目标类型。
这可能是您希望的结果,但是没有什么可以阻止具有多个绩效和/或目标的评估。这可能是也可能不是问题。
另外,我没有向下转换,而是向上转换了我在应用程序中保存评估的位置。我遇到了外键约束失败(代码 787)并意识到这是因为我在向下转换。我现在唯一的困境是,每当我保存评估时,它只会保存在assessment_table 而不是performance_assessment 或objective_assessment tbl。
您要做的是插入评估(父级),然后使用父级的 id 插入相关的绩效/目标,并使用评估参考设置评估的值。
787 是说如果没有assessment_reference 是评估中的assessment_id,您就不能插入孩子(绩效/目标)。
您可以做什么,很可能适合您的情况:a) 有一个 @Insert long insert(Assessment assessment); 和一个 @Insert long (PerformanceAssessment performanceAssessment)(同样适用于 Objective)
然后使用(假设dao是@Dao的一个实例)
并使用
dao.insert(new PerformanceAssessment(0,"the title", etc,dao.insert(Assessment(....),"the type");
因此,评估是作为插入绩效/目标的一部分添加的,并且评估的 id 用作assessment_reference 值。
-
注意 @Insert 将只返回一个long,所以你必须把它转换成一个int。我建议将 id 和引用更改为长的。开销(如果有的话)将产生最小的影响。数据库中的存储不受影响,省去了将 long 转换为 int 的麻烦。
演示
以下是基于代码的演示,它与您的代码非常接近,但有一些细微的变化。
-
id 已更改为 long 而不是 int。
- autogenerate 已被删除,但 id 将像以前一样生成。
2. 从 SQLite 的角度来看,自动生成所做的只是包含 AUTOINCREMENT 关键字。这实际上是低效的,不推荐。 https://sqlite.org/autoinc.html
所以代码是:-
评估
@Entity(tableName = "assessment_table")
public class Assessment {
@PrimaryKey /* no need for autogenerate */
@ColumnInfo(name = "assessment_id")
/* As not autogenerate then use Long and default to null */
/* same as autogenerate but without the overheads/inefficiencies */
private Long assessment_id = null; //Long rather than long so can be null and have id generated.
private String assessmentName;
private String assessmentStart;
private String assessmentEnd;
private long courseID;
public Assessment(long assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, long courseID) {
this.assessment_id = assessment_id;
this.assessmentName = assessmentName;
this.assessmentStart = assessmentStart;
this.assessmentEnd = assessmentEnd;
this.courseID = courseID;
}
@Ignore
/* Alternative constructor no need to provide id for inserting */
public Assessment(String assessmentName, String assessmentStart, String assessmentEnd, long courseID) {
this.assessmentName = assessmentName;
this.assessmentStart = assessmentStart;
this.assessmentEnd = assessmentEnd;
this.courseID = courseID;
}
public Long getAssessment_id() {
return assessment_id;
}
public void setAssessment_id(Long assessment_id) {
this.assessment_id = assessment_id;
}
public String getAssessmentName() {
return assessmentName;
}
public void setAssessmentName(String assessmentName) {
this.assessmentName = assessmentName;
}
public String getAssessmentStart() {
return assessmentStart;
}
public void setAssessmentStart(String assessmentStart) {
this.assessmentStart = assessmentStart;
}
public String getAssessmentEnd() {
return assessmentEnd;
}
public void setAssessmentEnd(String assessmentEnd) {
this.assessmentEnd = assessmentEnd;
}
public long getCourseID() {
return courseID;
}
public void setCourseID(long courseID) {
this.courseID = courseID;
}
}
绩效评估
@Entity(tableName = "performance_assessment", foreignKeys = {
@ForeignKey(
entity = Assessment.class,
parentColumns = "assessment_id",
childColumns = "assessment_reference",
onUpdate = CASCADE,
onDelete = CASCADE
)
})
public class PerformanceAssessment extends Assessment {
private long assessment_reference;
private String type;
public PerformanceAssessment(long assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, long courseID, long assessment_reference, String type) {
super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
@Ignore
public PerformanceAssessment(String assessmentName, String assessmentStart, String assessmentEnd, long courseID, long assessment_reference, String type) {
super(assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
@Override
public Long getAssessment_id() {
return super.getAssessment_id();
}
@Override
public void setAssessment_id(Long assessment_id) {
super.setAssessment_id(assessment_id);
}
@Override
public String getAssessmentName() {
return super.getAssessmentName();
}
@Override
public void setAssessmentName(String assessmentName) {
super.setAssessmentName(assessmentName);
}
@Override
public String getAssessmentStart() {
return super.getAssessmentStart();
}
@Override
public void setAssessmentStart(String assessmentStart) {
super.setAssessmentStart(assessmentStart);
}
@Override
public String getAssessmentEnd() {
return super.getAssessmentEnd();
}
@Override
public void setAssessmentEnd(String assessmentEnd) {
super.setAssessmentEnd(assessmentEnd);
}
@Override
public long getCourseID() {
return super.getCourseID();
}
@Override
public void setCourseID(long courseID) {
super.setCourseID(courseID);
}
public long getAssessment_reference() {
return assessment_reference;
}
public void setAssessment_reference(long assessment_reference) {
this.assessment_reference = assessment_reference;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
客观评估
@Entity(tableName = "objective_assessment", foreignKeys = {
@ForeignKey(
entity = Assessment.class,
parentColumns = "assessment_id",
childColumns = "assessment_reference",
onUpdate = CASCADE,
onDelete = CASCADE
)
})
public class ObjectiveAssessment extends Assessment {
private long assessment_reference;
private String type;
public ObjectiveAssessment(long assessment_id, String assessmentName, String assessmentStart, String assessmentEnd, long courseID, long assessment_reference, String type) {
super(assessment_id, assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
@Ignore
public ObjectiveAssessment(String assessmentName, String assessmentStart, String assessmentEnd, long courseID, long assessment_reference, String type) {
super(assessmentName, assessmentStart, assessmentEnd, courseID);
this.assessment_reference = assessment_reference;
this.type = type;
}
@Override
public Long getAssessment_id() {
return super.getAssessment_id();
}
@Override
public void setAssessment_id(Long assessment_id) {
super.setAssessment_id(assessment_id);
}
@Override
public String getAssessmentName() {
return super.getAssessmentName();
}
@Override
public void setAssessmentName(String assessmentName) {
super.setAssessmentName(assessmentName);
}
@Override
public String getAssessmentStart() {
return super.getAssessmentStart();
}
@Override
public void setAssessmentStart(String assessmentStart) {
super.setAssessmentStart(assessmentStart);
}
@Override
public String getAssessmentEnd() {
return super.getAssessmentEnd();
}
@Override
public void setAssessmentEnd(String assessmentEnd) {
super.setAssessmentEnd(assessmentEnd);
}
@Override
public long getCourseID() {
return super.getCourseID();
}
@Override
public void setCourseID(long courseID) {
super.setCourseID(courseID);
}
public long getAssessment_reference() {
return assessment_reference;
}
public void setAssessment_reference(long assessment_reference) {
this.assessment_reference = assessment_reference;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
POJO 具有嵌入的评估以及通过 @Relation 包含的 Perf 和 Obj(不是理想的,因为只有 1 或非 1 的规则将为空)。所以:-
AssessmentWithPerformanceAssessmentAndObjectiveAssessment
class AssessmentWithPerformanceAssessmentAndObjectiveAssessment {
@Embedded
Assessment assessment;
@Relation(
entity = PerformanceAssessment.class,
parentColumn = "assessment_id",
entityColumn = "assessment_reference"
)
PerformanceAssessment performanceAssessment;
@Relation(
entity = ObjectiveAssessment.class,
parentColumn = "assessment_id",
entityColumn = "assessment_reference"
)
ObjectiveAssessment objectiveAssessment;
}
单个 @Dao 注释类:-
AssessmentDao
@Dao
abstract class AssessmentDao {
@Insert
abstract long insert(Assessment assessment);
@Insert
abstract long insert(PerformanceAssessment performanceAssessment);
@Insert
abstract long insert(ObjectiveAssessment objectiveAssessment);
/*
use to check if an Assessment has any children
*/
@Query("WITH cte_counts(counter) AS (" +
"SELECT count(*) > 0 FROM performance_assessment WHERE assessment_reference=:assessment_id " +
"UNION ALL SELECT count(*) FROM objective_assessment WHERE assessment_reference=:assessment_id" +
")" +
"SELECT sum(counter) > 0 FROM cte_counts")
abstract boolean hasChildAlready(long assessment_id);
@Transaction
@Query("SELECT * FROM assessment_table WHERE assessment_id=:assessment_id")
abstract AssessmentWithPerformanceAssessmentAndObjectiveAssessment getAssessmentWithPerformanceAssessmentOrObjectiveAssessmentById(long assessment_id);
@Transaction
@Query("SELECT * FROM assessment_table")
abstract List<AssessmentWithPerformanceAssessmentAndObjectiveAssessment> getAllAssessmentsWithPerformanceAssessmentOrObjectiveAssessmentById();
}
@Database 注释类
评估数据库
@Database(entities = {Assessment.class,PerformanceAssessment.class,ObjectiveAssessment.class},version =1)
abstract class AssessmentDatabase extends RoomDatabase {
abstract AssessmentDao getAssessmentDao();
private static volatile AssessmentDatabase instance = null;
static AssessmentDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context,AssessmentDatabase.class,"assessment.db")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
终于在一个Activity中实际使用了上面的内容:-
public class MainActivity extends AppCompatActivity {
AssessmentDatabase db;
AssessmentDao dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = AssessmentDatabase.getInstance(this);
dao = db.getAssessmentDao();
/* Various Inserts */
long loneAssessment = dao.insert(
new Assessment(
"Lone Assessment with no children",
"2021-01-15",
"2021-03-15",
0L
)
);
/* id will be 100, subsequent id's if generated will be 101,102......*/
long a1 = dao.insert(new Assessment(100,"A1","2022-01-01","2022,03-01",10));
long p1 = dao.insert(new PerformanceAssessment("P1","2022-01-31","2022-01-31",10,a1,"X"));
/* back to using generated Assessment id */
long a2 = dao.insert(new Assessment("A1","2022-01-01","2022-03-01",11));
long p2 = dao.insert(new ObjectiveAssessment("O1","2022-01-31","2022-01-31",11,a2,"Y"));
/* Build the Assessment and ObjectiveAssessment ready for insert SEE WARNING */
Assessment a10 = new Assessment("A10","2022-01-01","2022-03-01",20);
ObjectiveAssessment o10 = new ObjectiveAssessment("O10","2022-01-31","2022-01-31",a10.getCourseID(),0,"Z");
/* WARNING assessment_reference WILL NOT BE ANY GOOD (will be 0) */
long a10id = dao.insert(a10);
o10.setAssessment_reference(a10id); /*<<<<<<<<<< NOW assessment_reference should be good */
dao.insert(o10);
/* Both together */
dao.insert(
new PerformanceAssessment("P20","2022-05-07","2022-05-07",11,
/* get the Assessment ID from the insert of the Assessment */
dao.insert(
new Assessment("A20","2022-04-01","2022-06-17",21)
),
"ZZ"
)
);
/* Extract all the Assessments with their children (if any)*/
for(AssessmentWithPerformanceAssessmentAndObjectiveAssessment awpoo: dao.getAllAssessmentsWithPerformanceAssessmentOrObjectiveAssessmentById()) {
Log.d("DBINFO","Assessment is " + awpoo.assessment.getAssessmentName() + " id is " + awpoo.assessment.getAssessment_id() + " etc....");
/* need to check if the the performanceassessment is null - it will be if there isn't one */
if (awpoo.performanceAssessment != null) {
Log.d("DBINFO","\t\tPerformanceAssessment = " + awpoo.performanceAssessment.getAssessmentName() +
" id is "+ awpoo.performanceAssessment.getAssessment_id() +
" references Asessment with id " + awpoo.performanceAssessment.getAssessment_reference());
} else {
Log.d("DBINFO","\t\tNo PerformanceAssessment.");
}
/* null check see performance assessment check above */
if (awpoo.objectiveAssessment != null) {
Log.d("DBINFO","\t\tObjectiveAssessment = " + awpoo.objectiveAssessment.getAssessmentName() +
" id is "+ awpoo.objectiveAssessment.getAssessment_id() +
" references Asessment with id " + awpoo.objectiveAssessment.getAssessment_reference());
} else {
Log.d("DBINFO","\t\tNo ObjectiveAssessment.");
}
}
}
}
运行时(第一次,如果运行两次将失败,因为 id 使用 100)日志显示:-
D/DBINFO: Assessment is Lone Assessment with no children id is 1 etc....
D/DBINFO: No PerformanceAssessment.
D/DBINFO: No ObjectiveAssessment.
D/DBINFO: Assessment is A1 id is 100 etc....
D/DBINFO: PerformanceAssessment = P1 id is 1 references Asessment with id 100
D/DBINFO: No ObjectiveAssessment.
D/DBINFO: Assessment is A1 id is 101 etc....
D/DBINFO: No PerformanceAssessment.
D/DBINFO: ObjectiveAssessment = O1 id is 1 references Asessment with id 101
D/DBINFO: Assessment is A10 id is 102 etc....
D/DBINFO: No PerformanceAssessment.
D/DBINFO: ObjectiveAssessment = O10 id is 2 references Asessment with id 102
D/DBINFO: Assessment is A20 id is 103 etc....
D/DBINFO: PerformanceAssessment = P20 id is 2 references Asessment with id 103
D/DBINFO: No ObjectiveAssessment.
使用 App Inspection 然后数据库看起来像:-
- 查看 Assessment_id 为 2(它是唯一的行标识符)的 PerformanceAssessment 的assessment_reference 值为 103。这表示assessment_id 为 103 的评估是父项(或者此 PerformanceAssessment 与具有103 的评估 ID)。