【发布时间】:2017-06-19 14:39:28
【问题描述】:
我正在尝试借助 Google 在 I/O 2017 中引入的 Android Room Persistence(一种 ORM)在 Android SQLite 中设计和实现文件夹树结构。 在我的设计中,一个文件夹可以包含另一个文件夹和文件。 这是我的文件夹和文件代码:
文件模型:
@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);
}
});
得到这个错误:
外键约束失败
--------- 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$1.run(ItemListActivity.java:62)
at android.os.AsyncTask$SerialExecutor$1.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)
有人知道我的项目表有什么问题或建议另一种设计吗?
【问题讨论】:
-
您没有指定父文件夹,我猜这就是触发此错误的原因。当然,您不能为第一个文件夹指定父文件夹,因为没有可指向的父文件夹。 Self-referential foreign keys work in SQLite,虽然它看起来像 you need to explicitly use
NULLfor the value when it is missing。我不知道 Room 为这里的INSERT语句生成了什么。 -
@CommonsWare 我试过 NULL 值,我也为父列输入了一个整数,例如 0,但结果没有改变,仍然出现这个错误。
-
@HamidrezaSahraei:请看一下这个。 stackoverflow.com/q/44749812/1788806 使用 String 作为数据类型对于您的用例来说是不必要的。您需要使用 Integer,因为这是一个可以为 null 的类(不是原始类型)。
-
您是否在未进行全新安装的情况下更新了数据库模型?您的清单中还启用了自动备份吗??
标签: android sqlite orm android-room android-orm