Android 房间 FOREIGN KEY 约束失败
Android Room FOREIGN KEY constraint failed
我正在尝试在 Android SQLite 中设计和实现文件夹树结构,借助 Android Room Persistence(一种 ORM)Google 在 [=34= 中引入] 2017年。
在我的设计中,一个文件夹可以包含另一个文件夹和文件。
这是我的文件夹和文件代码:
文件模型:
@Entity(tableName = "files", foreignKeys = @ForeignKey(entity = Folder.class,
parentColumns = "id",
childColumns = "parent_id",
onDelete = CASCADE))
public class File {
@PrimaryKey(autoGenerate = true)
private int id;
private String title;
private Date creationDate;
@ColumnInfo(name = "parent_id")
public int parentId;
//here setters and getters skipped but exist in original code
}
这是文件夹代码:
@Entity(tableName = "folders", foreignKeys = @ForeignKey(entity = Folder.class,
parentColumns = "id",
childColumns = "parent_id",
onDelete = CASCADE,onUpdate = SET_NULL))
public class Folder {
@PrimaryKey(autoGenerate = true)
private int id;
private String name;
@ColumnInfo(name = "parent_id")
private int parentId;
}
它的父 ID 列上有一个外键。
这里是 FolderDAO:
@Dao
public interface FolderDAO {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertFolder(Folder... folders);
@Update
public void updateFolder(Folder... folders);
@Delete
public void deleteFolders(Folder... folders);
@Query("SELECT * FROM folders")
List<Folder> getAll();
@Query("SELECT * FROM folders WHERE id IN (:folderIds)")
List<Folder> loadAllByIds(int[] folderIds);
}
但是当我创建文件夹对象并尝试插入它时:
AsyncTask.execute(new Runnable() {
@Override
public void run() {
Folder folder = new Folder();
folder.setName("All");
DatabaseInstance.getInstance(getApplicationContext()).folderDAO().insertFolder(folder);
}
});
得到这个错误:
FOREIGN KEY 约束失败
--------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: sahraei.hamidreza.com.note, PID: 1835
android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787)
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:782)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at android.arch.persistence.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.java:80)
at android.arch.persistence.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:80)
at sahraei.hamidreza.com.note.DAO.FolderDAO_Impl.insertFolder(FolderDAO_Impl.java:80)
at sahraei.hamidreza.com.note.ItemListActivity.run(ItemListActivity.java:62)
at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
有谁知道我的项目出了什么问题或建议另一种设计 table?
我让它工作了,但没有使用 int
主键。我不是那种 ORM 场景的超级粉丝,只是因为这些问题。
因此,这是一个使用 UUID
作为其主键的自引用 Category
class:
/***
Copyright (c) 2017 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
*/
package com.commonsware.android.room.dao;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import java.util.UUID;
import static android.arch.persistence.room.ForeignKey.CASCADE;
@Entity(
tableName="categories",
foreignKeys=@ForeignKey(
entity=Category.class,
parentColumns="id",
childColumns="parentId",
onDelete=CASCADE),
indices=@Index(value="parentId"))
public class Category {
@PrimaryKey
public final String id;
public final String title;
public final String parentId;
@Ignore
public Category(String title) {
this(title, null);
}
@Ignore
public Category(String title, String parentId) {
this(UUID.randomUUID().toString(), title, parentId);
}
public Category(String id, String title, String parentId) {
this.id=id;
this.title=title;
this.parentId=parentId;
}
}
现在您可以拥有像这样的 DAO 方法:
@Query("SELECT * FROM categories WHERE parentId IS NULL")
Category findRootCategory();
@Query("SELECT * FROM categories WHERE parentId=:parentId")
List<Category> findChildCategories(String parentId);
我花了很长时间才弄清楚 parentColumns
元素是自动生成的主键似乎根本不允许,即使它是使用 Kotlin 数据的 uuid class 的默认值如下:
class MyClass {
// Don't do this
@PrimaryKey(autoGenerate = true)
var uuid: String = UUID.randomUUID()
}
始终确保父列由您直接指定。
我正在尝试在 Android SQLite 中设计和实现文件夹树结构,借助 Android Room Persistence(一种 ORM)Google 在 [=34= 中引入] 2017年。 在我的设计中,一个文件夹可以包含另一个文件夹和文件。 这是我的文件夹和文件代码:
文件模型:
@Entity(tableName = "files", foreignKeys = @ForeignKey(entity = Folder.class,
parentColumns = "id",
childColumns = "parent_id",
onDelete = CASCADE))
public class File {
@PrimaryKey(autoGenerate = true)
private int id;
private String title;
private Date creationDate;
@ColumnInfo(name = "parent_id")
public int parentId;
//here setters and getters skipped but exist in original code
}
这是文件夹代码:
@Entity(tableName = "folders", foreignKeys = @ForeignKey(entity = Folder.class,
parentColumns = "id",
childColumns = "parent_id",
onDelete = CASCADE,onUpdate = SET_NULL))
public class Folder {
@PrimaryKey(autoGenerate = true)
private int id;
private String name;
@ColumnInfo(name = "parent_id")
private int parentId;
}
它的父 ID 列上有一个外键。
这里是 FolderDAO:
@Dao
public interface FolderDAO {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertFolder(Folder... folders);
@Update
public void updateFolder(Folder... folders);
@Delete
public void deleteFolders(Folder... folders);
@Query("SELECT * FROM folders")
List<Folder> getAll();
@Query("SELECT * FROM folders WHERE id IN (:folderIds)")
List<Folder> loadAllByIds(int[] folderIds);
}
但是当我创建文件夹对象并尝试插入它时:
AsyncTask.execute(new Runnable() {
@Override
public void run() {
Folder folder = new Folder();
folder.setName("All");
DatabaseInstance.getInstance(getApplicationContext()).folderDAO().insertFolder(folder);
}
});
得到这个错误:
FOREIGN KEY 约束失败
--------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: sahraei.hamidreza.com.note, PID: 1835
android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787)
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:782)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at android.arch.persistence.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.java:80)
at android.arch.persistence.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:80)
at sahraei.hamidreza.com.note.DAO.FolderDAO_Impl.insertFolder(FolderDAO_Impl.java:80)
at sahraei.hamidreza.com.note.ItemListActivity.run(ItemListActivity.java:62)
at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
有谁知道我的项目出了什么问题或建议另一种设计 table?
我让它工作了,但没有使用 int
主键。我不是那种 ORM 场景的超级粉丝,只是因为这些问题。
因此,这是一个使用 UUID
作为其主键的自引用 Category
class:
/***
Copyright (c) 2017 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
*/
package com.commonsware.android.room.dao;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import java.util.UUID;
import static android.arch.persistence.room.ForeignKey.CASCADE;
@Entity(
tableName="categories",
foreignKeys=@ForeignKey(
entity=Category.class,
parentColumns="id",
childColumns="parentId",
onDelete=CASCADE),
indices=@Index(value="parentId"))
public class Category {
@PrimaryKey
public final String id;
public final String title;
public final String parentId;
@Ignore
public Category(String title) {
this(title, null);
}
@Ignore
public Category(String title, String parentId) {
this(UUID.randomUUID().toString(), title, parentId);
}
public Category(String id, String title, String parentId) {
this.id=id;
this.title=title;
this.parentId=parentId;
}
}
现在您可以拥有像这样的 DAO 方法:
@Query("SELECT * FROM categories WHERE parentId IS NULL")
Category findRootCategory();
@Query("SELECT * FROM categories WHERE parentId=:parentId")
List<Category> findChildCategories(String parentId);
我花了很长时间才弄清楚 parentColumns
元素是自动生成的主键似乎根本不允许,即使它是使用 Kotlin 数据的 uuid class 的默认值如下:
class MyClass {
// Don't do this
@PrimaryKey(autoGenerate = true)
var uuid: String = UUID.randomUUID()
}
始终确保父列由您直接指定。