使用列表视图检索数据的SQLite外键语法存在问题

时间:2019-04-03 23:03:43

标签: java android sqlite foreign-keys

这是上下文。我有一个databasehelper,可在一个数据库中创建3个表。这些表中的两个具有外键。一切正常,但一切都很好,但是鉴于我想做的事,我需要使用列表视图而不是文本视图来显示我的数据,并能够进行编辑,删除等操作。出于某种原因,列表视图在Android上与其他编程语言完全不同。我想出了适配器和所有的废话。我的新问题是我的应用程序无法显示数据,当我检查logcat时收到此消息

 Caused by: java.lang.IllegalArgumentException: column '_id' does not exist. Available columns: [id, termName, termStart, termEnd]

该表用于一个名为“ term_table”的表。因此,我将id从'id'更改为'_id',并且成功了。

所以一切都好吧?否,因为其他两个表都有自己的ID和外键。当我对另一个名为“ assess_table”的表进行相同的查询时,遇到了相同的错误,因此我将“ id”更改为“ _id”。问题是“ courses_table”(第三个表)使用“ _id”作为外键来引用称为“ term_table”的表。所以我收到一条错误消息,告诉我我正在创建重复的列-主键'_id'和外键'_id'以引用term_table。

我将“ courses_table”的ID从“ _id”重命名为“ courses_id”。现在我回到第一个方框,得到相同的错误:

 Caused by: java.lang.IllegalArgumentException: column '_id' does not exist. Available columns: [assess_id, assessName, assessDueDate, assessType, courses_id]

因此,基本上,如果必须将所有三个表都命名为_id,并且如果外键的名称与所引用表的主键的名称相同,那么在创建外键时应该使用什么语法,因此我将始终获得重复列错误。我读到,此_id命名方案仅与游标适配器有关。什么语法适合使用?

这是我的数据库帮助器中的3个表

//terms table
   public static final String TERM_TABLE_NAME = "term_table";
   public static final String COL_TERM1 = "_id";
   public static final String COL_TERM2 = "termName";
   public static final String COL_TERM3 = "termStart";
   public static final String COL_TERM4 = "termEnd";
   public static final String TERM_DATABASE_NAME = "term.db";
   //courses table
   public static final String COURSES_TABLE_NAME = "courses_table";
   public static final String COL_COURSE1 = "courses_id";
   public static final String COL_COURSE2 = "courseTitle";
   public static final String COL_COURSE3 = "courseStartDate";
   public static final String COL_COURSE4 = "courseEndDate";
   public static final String COL_COURSE5 = "courseStatus";
   public static final String COL_COURSE6 = "optionalNote";
   public static final String COL_COURSE7 = "mentorName";
   public static final String COL_COURSE8 = "mentorPhone";
   public static final String COL_COURSE9 = "mentorEmail";
   public static final String COL_COURSE_TERMID = "_id";
   //assess table
   public static final String ASSESS_TABLE_NAME = "assess_table";
   public static final String COL_ASSESS1 = "assess_id";
   public static final String COL_ASSESS2 = "assessName";
   public static final String COL_ASSESS3 = "assessDueDate";
   public static final String COL_ASSESS4 = "assessType";
   public static final String COL_ASSESS_COURSEID = "courses_id";

这是创建语法

public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TERM_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, termName TEXT, termStart TEXT, termEnd TEXT)");
        db.execSQL("CREATE TABLE " + COURSES_TABLE_NAME + " (courses_id INTEGER PRIMARY KEY AUTOINCREMENT, courseTitle TEXT, courseStartDate TEXT, courseEndDate TEXT, courseStatus INTEGER, optionalNote TEXT, mentorName TEXT, mentorPhone TEXT, mentorEmail TEXT, _id INTEGER, FOREIGN KEY (_id) REFERENCES term_table(_id))");
        db.execSQL("CREATE TABLE " + ASSESS_TABLE_NAME + " (assess_id INTEGER PRIMARY KEY AUTOINCREMENT, assessName TEXT, assessDueDate TEXT, assessType INTEGER, courses_id INTEGER, FOREIGN KEY (courses_id) REFERENCES courses_table(courses_id))");
    }

2 个答案:

答案 0 :(得分:1)

像这样重命名主键和外键:

public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TERM_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, termName TEXT, termStart TEXT, termEnd TEXT)");
        db.execSQL("CREATE TABLE " + COURSES_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, courseTitle TEXT, courseStartDate TEXT, courseEndDate TEXT, courseStatus INTEGER, optionalNote TEXT, mentorName TEXT, mentorPhone TEXT, mentorEmail TEXT, for_cors_term INTEGER, FOREIGN KEY (for_cors_term) REFERENCES term_table(_id))");
        db.execSQL("CREATE TABLE " + ASSESS_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, assessName TEXT, assessDueDate TEXT, assessType INTEGER, for_ases_cors INTEGER, FOREIGN KEY (for_ases_cors) REFERENCES courses_table(_id))");
    }

   public static final String TERM_DATABASE_NAME = "term.db";

   //terms table
   public static final String TERM_TABLE_NAME = "term_table";
   public static final String COL_TERM1 = "_id"; //primary key
   public static final String COL_TERM2 = "termName";
   public static final String COL_TERM3 = "termStart";
   public static final String COL_TERM4 = "termEnd";

   //courses table
   public static final String COURSES_TABLE_NAME = "courses_table";
   public static final String COL_COURSE1 = "_id"; //primary key
   public static final String COL_COURSE2 = "courseTitle";
   public static final String COL_COURSE3 = "courseStartDate";
   public static final String COL_COURSE4 = "courseEndDate";
   public static final String COL_COURSE5 = "courseStatus";
   public static final String COL_COURSE6 = "optionalNote";
   public static final String COL_COURSE7 = "mentorName";
   public static final String COL_COURSE8 = "mentorPhone";
   public static final String COL_COURSE9 = "mentorEmail";
   public static final String COL_COURSE_TERMID = "for_cors_term"; //foreign key

   //assess table
   public static final String ASSESS_TABLE_NAME = "assess_table";
   public static final String COL_ASSESS1 = "_id"; //primary key
   public static final String COL_ASSESS2 = "assessName";
   public static final String COL_ASSESS3 = "assessDueDate";
   public static final String COL_ASSESS4 = "assessType";
   public static final String COL_ASSESS_COURSEID = "for_ases_cors"; //foreign key

或者,如果您不想使用_id字段作为主键,则可以使用RecyclerView代替ListView

答案 1 :(得分:1)

  

所以基本上,如果必须将所有三个表都命名为_id,我的语法是什么   如果外键具有   与被引用表的主键名称相同,所以我将   总是会收到创建重复列的错误。

它们不必一定是您可以使用 AS _id 来动态生成带有别名的列(请注意,由于*您具有 id _id 列),例如

SELECT *, id AS _id
  • 您只希望每个CursorAdapter 1个 _id 列。

关于使用

定义外键
INTEGER PRIMARY KEY _id,....., FOREIGN KEY(_id) REFERENCES parent_table(column_in_parent_table)

它可能不会持续/良好/有用地工作。

您实际上是在说 _id 列必须是父表中的值。因此,您将关系限制为1-1关系,因为INTEGER PRIMARY KEY是隐式唯一的,因为它是通常隐藏的 rowid 列的别名,因为该列是特殊的,所以该值也必须是整数。

例如如果课程表的第一行引用值为1的列,则表示正确(例如TERM1),则该表中不能再引用TERM1,因为_id不得为1。

  • 因此,您遇到的唯一约束(重复项)。

简而言之,您应该引用 _id 列作为外键的父表列(在您的情况下),但不要在子表中使用 _id 列作为引用父表的列,然后将父表限制为该值只有一个孩子,即1-1关系(这实际上意味着两个表是多余的)。

相反,子表中的列应该是主要用于存储引用/关系/关联(可能是具有其他用途的值)的列。

因此,您需要类似:-

public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TERM_TABLE_NAME + " (_id INTEGER PRIMARY KEY, termName TEXT, termStart TEXT, termEnd TEXT)");
        db.execSQL("CREATE TABLE " + COURSES_TABLE_NAME + " (_id INTEGER PRIMARY KEY, courseTitle TEXT, courseStartDate TEXT, courseEndDate TEXT, courseStatus INTEGER, optionalNote TEXT, mentorName TEXT, mentorPhone TEXT, mentorEmail TEXT, course_term_reference INTEGER REFERENCES term_table(_id))");
        db.execSQL("CREATE TABLE " + ASSESS_TABLE_NAME + " (_id INTEGER PRIMARY KEY, assessName TEXT, assessDueDate TEXT, assessType INTEGER, assess_course_reference INTEGER REFERENCES courses_table(_id))");
}
  • 请注意,AUTOINCREMENT已被删除,您无需这样做,也不会产生SQLite Autoincrement的开销,如:-
  
      
  • AUTOINCREMENT关键字强加了额外的CPU,内存,磁盘空间和   磁盘I / O开销,如果没有严格要求,则应避免。它是   通常不需要。
  •   
  • 使用了更简单,也许更容易理解的列外键定义(即,外键是在列定义级别而不是表定义级别定义的)

但是,我建议将标识符(列名,表名等)编码为常量一次,然后始终引用这些常量。

因此,我建议以上所述会更好:-

public static final String TERM_TABLE_NAME = "term_table";
public static final String COL_TERM1 = "_id";
public static final String COL_TERM2 = "termName";
public static final String COL_TERM3 = "termStart";
public static final String COL_TERM4 = "termEnd";
public static final String TERM_DATABASE_NAME = "term.db";
public static final int DBVERSION = 1;
//courses table
public static final String COURSES_TABLE_NAME = "courses_table";
public static final String COL_COURSE1 = "_id";
public static final String COL_COURSE2 = "courseTitle";
public static final String COL_COURSE3 = "courseStartDate";
public static final String COL_COURSE4 = "courseEndDate";
public static final String COL_COURSE5 = "courseStatus";
public static final String COL_COURSE6 = "optionalNote";
public static final String COL_COURSE7 = "mentorName";
public static final String COL_COURSE8 = "mentorPhone";
public static final String COL_COURSE9 = "mentorEmail";
public static final String COL_COURSE_TERM_REFERENCE = "term_reference";
//assess table
public static final String ASSESS_TABLE_NAME = "assess_table";
public static final String COL_ASSESS1 = "_id";
public static final String COL_ASSESS2 = "assessName";
public static final String COL_ASSESS3 = "assessDueDate";
public static final String COL_ASSESS4 = "assessType";
public static final String COL_ASSESS_COURSE_REFERENCE = "course_reference";

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL(
            "CREATE TABLE " + TERM_TABLE_NAME + "(" +
                    COL_TERM1 + " INTEGER PRIMARY KEY," +
                    COL_TERM2 + " TEXT, " +
                    COL_TERM3 + " TEXT, " +
                    COL_TERM4 + " TEXT" +
                    ")"
    );
    db.execSQL(
            "CREATE TABLE " + COURSES_TABLE_NAME + "(" +
                    COL_COURSE1 + " INTEGER PRIMARY KEY, " +
                    COL_COURSE2 + " TEXT, " +
                    COL_COURSE3 + " TEXT, " +
                    COL_COURSE4 + " TEXT, " +
                    COL_COURSE5 + " INTEGER, " +
                    COL_COURSE6 + " TEXT, " +
                    COL_COURSE7 + " TEXT, " +
                    COL_COURSE8 + " TEXT, " +
                    COL_COURSE9 + " TEXT, " +
                    COL_COURSE_TERM_REFERENCE + "INTEGER REFERENCES " + TERM_TABLE_NAME + "(" + COL_TERM1 + ")" +
                    ")"
    );

    db.execSQL(
            "CREATE TABLE " + ASSESS_TABLE_NAME + "(" +
                    COL_ASSESS1 + " INTEGER PRIMARY KEY, " +
                    COL_ASSESS2 + " TEXT, " +
                    COL_ASSESS3 + " TEXT, " +
                    COL_ASSESS4 + " INTEGER, " +
                    COL_ASSESS_COURSE_REFERENCE + " INTEGER REFERENCES " + COURSES_TABLE_NAME + "(" + COL_COURSE1 + ")" +
                    ")"
    );
}

上面的结果导致SQL(对于每个表)为:-

CREATE TABLE term_table(_id INTEGER PRIMARY KEY,termName TEXT, termStart TEXT, termEnd TEXT)
CREATE TABLE courses_table(courses_id INTEGER PRIMARY KEY, courseTitle TEXT, courseStartDate TEXT, courseEndDate TEXT, courseStatus INTEGER, optionalNote TEXT, mentorName TEXT, mentorPhone TEXT, mentorEmail TEXT, term_reference INTEGER REFERENCES term_table(_id))
CREATE TABLE assess_table(assess_id INTEGER PRIMARY KEY, assessName TEXT, assessDueDate TEXT, assessType INTEGER, course_reference INTEGER REFERENCES courses_table(courses_id))

其他

要使用外键支持,您需要将其打开。因此,您可以覆盖数据库助手的 onConfigure 方法以启用支持,例如:-

@Override
public void onConfigure(SQLiteDatabase db) {
    super.onConfigure(db);
    db.setForeignKeyConstraintsEnabled(true);
}