2017-06-19 169 views
6

我試圖在谷歌在I/O 2017中引入的Android Room Persistence(ORM)的幫助下,在Android SQLite中設計和實現文件夾樹結構。 在我的設計中,可以包含其他文件夾和文件。 這裏是我的文件夾和文件代碼:Android房間FOREIGN KEY約束失敗

文件型號:

@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) 

D任何人都知道什麼是錯的或者爲我的項目表提出了另一個設計?

+0

「我做一個文件夾對象和嘗試插入它「 - 請編輯您的問題併發布此代碼以及完整的Java堆棧跟蹤。 – CommonsWare

+0

@CommonsWare編輯。 –

+0

你還沒有指定父文件夾,我的猜測是觸發這個錯誤的原因。當然,你不能指定第一個文件夾的父文件夾,因爲沒有父指向。 [自引用外鍵在SQLite中工作](https://stackoverflow.com/q/15967146/115145),儘管它看起來像[[缺少時需要顯式使用'NULL'值](https: //stackoverflow.com/a/6518551/115145)。我不知道Room爲'INSERT'語句生成了什麼。 – CommonsWare

回答

3

我得到這個工作,但沒有使用int主鍵。僅僅因爲這些問題,我不是那種對這種ORM場景的狂熱粉絲。

所以,這裏是一個使用UUID其主鍵的自我指涉Category類:

/*** 
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", 
    [email protected](
    entity=Category.class, 
    parentColumns="id", 
    childColumns="parentId", 
    onDelete=CASCADE), 
    [email protected](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); 
+0

這很好,我用你的代碼來實現文件夾結構,現在它像一個魅力一樣工作。但是你知道我的代碼有什麼問題嗎? –

+1

@HamidrezaSahraei:唯一的區別是你使用自動生成的'int'鍵,而我不是。你可以嘗試切換到'Integer'並查看'null'默認值是否工作得更好。我的猜測是'0'默認'int'打破了外鍵約束,因爲Room不能說「0」表示「無關係」。但是,這只是一個猜測。 – CommonsWare

+0

你可以讓你的外鍵類型爲Integer,它是可以爲空的。並保持你的id類型int – Yushi