2017-06-19 28 views
6

Próbuję zaprojektować i zaimplementować strukturę drzewa folderów w Android SQLite za pomocą Android Room Persistence (ORM), którą Google wprowadził w I/O 2017. W moim projekcie, folder może zawierać inne foldery i pliki. Oto moje kody do folderu i pliku:Ograniczenie FORAIGN KEY FORTE zabronione

Plik Model:

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

I Oto kod Folder:

@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; 
} 

Jakie masz klucza obcego w kolumnie ID nim samo dla rodzica .

i tu jest 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); 
} 

Ale kiedy robię obiekt folderu i spróbuj go wstawić:

AsyncTask.execute(new Runnable() { 
     @Override 
     public void run() { 
      Folder folder = new Folder(); 
      folder.setName("All"); 

      DatabaseInstance.getInstance(getApplicationContext()).folderDAO().insertFolder(folder); 

     } 
    }); 

dostać ten błąd:

FOREIGN KEY ograniczenia udało

  --------- 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 Ktoś wie, co jest nie tak lub zaproponować inny projekt dla mojego stołu projektu?

+0

„Robię obiekt folderu i spróbuj go wstawić "- edytuj swoje pytanie i opublikuj ten kod wraz z kompletnym śledzeniem stosu Java. – CommonsWare

+0

@CommonsWars Edytowane. –

+0

Nie określono folderu nadrzędnego i domyślam się, że właśnie to powoduje ten błąd. Oczywiście * nie możesz * określić folderu nadrzędnego dla pierwszego folderu, ponieważ nie ma rodzica do wskazania. [Self-referencyjne klucze obce działają w SQLite] (https://stackoverflow.com/q/15967146/115145), chociaż wygląda na to [musisz jawnie użyć 'NULL' dla wartości, gdy jej brakuje) (https: //stackoverflow.com/a/6518551/115145). Nie wiem, co generuje pokój dla instrukcji "INSERT" tutaj. – CommonsWare

Odpowiedz

3

Mam to do pracy, ale nie używając kluczy podstawowych int. Nie jestem wielkim fanem tego typu scenariuszy ORM, tylko z powodu tego rodzaju problemów.

Więc oto autoreferencyjna Category klasy, która używa UUID jej klucz podstawowy:

/*** 
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; 
    } 
} 

Teraz możesz mieć metody DAO jak:

@Query("SELECT * FROM categories WHERE parentId IS NULL") 
Category findRootCategory(); 

@Query("SELECT * FROM categories WHERE parentId=:parentId") 
List<Category> findChildCategories(String parentId); 
+0

To jest świetne, użyłem twojego kodu do zaimplementowania struktury folderów i teraz działa jak urok. Ale czy wiesz, jaki był problem z moim kodem? –

+1

@HamidrezaSahraei: Jedyną znaczącą różnicą jest to, że używasz automatycznie generowanych kluczy 'int', a ja nie. Możesz spróbować przełączyć się na 'Integer' i sprawdzić, czy domyślna wartość' null' działa lepiej. Domyślam się, że domyślne '0'' int 'złamało ograniczenie klucza obcego, ponieważ Room nie może powiedzieć, że' 0' oznacza "brak relacji".Ale to tylko domysły. – CommonsWare

+0

Możesz uczynić swój klucz obcy typu Integer, który jest zerowalny. I zachowaj swój identyfikator typu int – Yushi