Wie verwende ich die Room Persistence Library mit einer vorab ausgefüllten Datenbank?

69

Ich möchte Room mit einer vorab ausgefüllten Datenbank verwenden, kann jedoch nicht verstehen, wie Room mitgeteilt werden soll, wo sich meine Datenbank befindet.

Ich habe es jetzt eingefügt src/main/assets/databasesund wenn ich die Instanz für die Raumdatenbank erstelle, erstelle ich es folgendermaßen:

Room.databaseBuilder(
    getApplicationContext(),
    AppDatabase.class,
    "justintrain.db"
)
.allowMainThreadQueries()
.build();

Auf diese Weise wird meiner Meinung nach jedes Mal eine neue Datenbank erstellt, oder es wird sowieso nicht die vorab ausgefüllte Datenbank verwendet.

Wie kann ich meine Datenbank finden?

Alberto Giunta
quelle
6
Ich habe keine gute Lösung dafür gesehen. Ich habe eine Feature-Anfrage dafür eingereicht .
CommonsWare
@CommonsWare, also wird es eine Weile dauern, bis es implementiert wird, denke ich. Kennen Sie eine Problemumgehung, die bis dahin verwendet werden könnte? (Trotzdem vielen Dank für die Meldung!)
Alberto Giunta
2
Nun, Sie können davon ausgehen, dass die Datenbank getDatabasePath()für den von Ihnen gewählten Datenbankdateinamen gespeichert wird . Vereinbaren Sie dann, dass das Asset in diesen Pfad kopiert wird, bevor Sie das erstellen RoomDatabase, falls diese Datei noch nicht vorhanden ist. Dies ist die erste Option, die ich in dieser Ausgabe vorschlage. Im Idealfall haben wir mehr Sicherheit, dass " getDatabasePath()für den von Ihnen gewählten Datenbankdateinamen" die richtige Antwort ist.
CommonsWare
@AlbertoGiunta funktioniert das für Sie medium.com/google-developers/… ?
Gregriggins36
1
CommonsWare hat hier eine gute Lösung: github.com/commonsguy/cw-androidarch/tree/v0.6/General/…
Live-Love

Antworten:

40

So habe ich es gelöst und wie Sie Ihre Anwendung mit einer vorab ausgefüllten Datenbank versenden können (bis zu Room v. Alpha5)

  • Legen Sie Ihre SQLite-Datenbank database_name.dbin den assets/databasesOrdner

  • Nehmen Sie die Dateien aus diesem Repo und legen Sie sie in ein Paket namens dhsqlAsset

  • AppDatabaseÄndern Sie in Ihrer Klasse den DB-Erstellungscode Ihres Raums entsprechend:

    Room.databaseBuilder(context.getApplicationContext(), 
                         AppDatabase.class, 
                         "database_name.db")
    .openHelperFactory(new AssetSQLiteOpenHelperFactory())
    .allowMainThreadQueries()
    .build();
    

Beachten Sie, dass Sie "database_name.db"und nicht getDatabasePath()oder andere Methoden verwenden müssen: Es wird nur der Name der Datei benötigt.

Alberto Giunta
quelle
Hey Alberto, welches Programm würden Sie für die Generierung von .db-Dateien empfehlen (ich meine, Datenbankeditor)? Ich benötige einen Editor, der eine Datenbank generiert, die mit dem Raumschema kompatibel ist.
Pedro Guerra
7
Hei @PedroGuerra! Ich habe ein wirklich einfaches Programm namens "DB Browser for SQLite" verwendet. Ich denke, es ist plattformübergreifend, so dass es gut für Ihre Bedürfnisse funktionieren sollte!
Alberto Giunta
1
Funktioniert auch mit Alpha9 (muss nur bei Bedarf implementiert werden AutoClosable), kann dies jedoch nicht mit Beta1 tun ( SupportSQLiteOpenHelper.Configurationeinige AssetSQLiteOpenHelper
Dinge
@AlbertoGiunta Hey Alberto, entschuldige die Anfrage, aber könntest du bitte ein neues Tag auf github erstellen, damit ich es mit jitpack in mein Projekt importieren kann?
Pedro Guerra
12
Danke für @Alberto Giunta Das spart mir Zeit! Ich benutze deinen Code und baue auf Abhängigkeit, um hier einfach zu verwenden! github.com/daolq3012/AssetSQLiteOpenHelper Jeder kann dies auf einfache Weise verwenden.
DaoLQ
28

UPDATE (7. November 2019)

Room unterstützt jetzt die Verwendung einer vorgefertigten Datenbank ab Version 2.2.0

https://developer.android.com/jetpack/androidx/releases/room#2.2.0

Lösung vor Version 2.2.0: Einfache Lösung ohne andere externe Bibliotheken.

Room verwendet den vorhandenen Android-Framework-Code, um eine Datenbank zu erstellen oder zu öffnen. Wenn Sie sich den Quellcode von FrameworkSQLiteOpenHelper(Room's Version von SQLiteOpenHelper) ansehen, werden intern interne SQLiteOpenHelper.getReadableDatabase()Methoden und andere Methoden aufgerufen, wo immer dies erforderlich ist.

Die einfachste Lösung besteht also darin, die DB-Datei einfach aus dem Assets-Verzeichnis zu kopieren, mContext.getDatabasePath("my-database.sqlite")bevor Sie die DB mit Room erstellen.

In Ihrem Fall sieht der Code ungefähr so ​​aus -

private final String DB_NAME = "my-database.sqlite";

private MyDatabase buildDatabase(Context context) {
    final File dbFile = context.getDatabasePath(DB_NAME);

    if(!dbFile.exists()) {
        copyDatabaseFile(dbFile.getAbsolutePath());
    }

    return Room.databaseBuilder(context.getApplicationContext(),
        MyDatabase.class, DB_NAME)
        .build();
}

private void copyDatabaseFile(String destinationPath) {
    // code to copy the file from assets/database directory to destinationPath
}

Dieser Link enthält den Code, der zum Kopieren des DB- Links mit Code benötigt wird

Nishanth
quelle
4
Ich danke dir sehr. Dies sollte die akzeptierte Antwort sein. Ich habe einen ganzen Tag damit verbracht, das herauszufinden. Ich wusste, dass wir keine weitere Bibliothek benötigen und dass das Kopieren der Datenbankdatei in den Datenbankspeicherort funktionieren sollte.
Bugs passieren am
Leider ist diese Antwort nur der 3. Platz und wird wahrscheinlich von vielen übersehen.
Big_Chair
Wie würden Sie einen Test dafür schreiben?
user1927033
1
Wie weisen Sie Room an, die neu kopierte Datenbank zu verwenden? Ich kann über einen Dateibrowser bestätigen, dass die Datenbank korrekt übertragen wurde, aber Room.databasebuilder generiert immer noch eine leere Datenbank.
DinosauRuss
16

Ich hatte das gleiche Problem und habe eine Bibliothek erstellt, die genau das tut. Die akzeptierte Antwort funktioniert, aber ich denke, es ist einfacher, eine Bibliothek zu benutzen.

AppDatabase db = RoomAsset
    .databaseBuilder(context.getApplicationContext(), AppDatabase.class, "database_name.db")
    .build(); 

Fügen Sie es am Ende der Repositorys zu Ihrem root build.gradle hinzu:

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

Fügen Sie die Abhängigkeit hinzu

dependencies {
    // ... other dependencies
    implementation 'com.github.humazed:RoomAsset:v1.0'
}

Sie finden die Bibliothek hier: https://github.com/humazed/RoomAsset

humazed
quelle
3
Das ist riskant. Die Migration ist möglicherweise nicht erfolgreich.
Sourav Bagchi
Können Sie näher erläutern, was riskant bedeutet? Gibt es bestimmte Bedenken?
Humazed
Ich persönlich verwende Context.deleteDatabase()in der Anwendung, wenn die Anwendung aktualisiert wurde.
Shkschneider
Es funktioniert nicht: Das Schemaexportverzeichnis wird dem Anmerkungsprozessor nicht zur Verfügung gestellt, sodass wir das Schema nicht exportieren können. Sie können entweder ein room.schemaLocationAnnotation Processor-Argument angeben oder exportSchema auf false setzen.
Sandeep Yohans
13

Funktionierende 2019-Lösung ohne Hacks oder Abhängigkeiten (Kotlin)

  1. Platzieren Sie Ihre .dbDatei in assets/databases(oder wirklich in einem beliebigen Ordner, solange es sich darunter befindet assets).

  2. Verwenden Sie die vorhandene createFromAsset()Funktion von Raum 2.2 und übergeben Sie den Pfad zur Datenbank. Wenn Ihre Datenbankdatei beispielsweise benannt ist my_data.dbund sich im databasesVerzeichnis des assetsOrdners befindet, würden Sie dies tun createFromAsset("databases/my_data.db").

Angenommen, Ihr Datenbankname (z. B. my_data) ist in einer konstanten Variablen mit dem Namen gespeichert DATABASE_NAME, können Sie diesen Beispielcode verwenden:

Room.databaseBuilder(
                    context.applicationContext,
                    MyDatabase::class.java,
                    DATABASE_NAME
                )
                    .createFromAsset("databases/$DATABASE_NAME.db")
                    .build()

Wichtig : Stellen Sie sicher, dass das Schema Ihrer Datenklasse / Entität genau mit dem Schema Ihrer .dbDatei übereinstimmt . Wenn eine Spalte beispielsweise nicht explizit als NOT NULLin der .dbDatei markiert ist, bedeutet dies, dass die Spalte Nullwerte enthalten kann. In Kotlin müssten Sie dies mit val colName: dataType? = nullin Ihrer Datenklasse abgleichen. Wenn Sie dies nur tun val colName: dataType, kompiliert Kotlin dies zu einer NOT NULLSpalte und löst eine Ausnahme aus, wenn Sie versuchen, Ihre App auszuführen.

Hinweis : Wenn Sie stattdessen eine Raumdatenbank aus einer Datenbankdatei erstellen möchten, die Sie auf das Android-Gerät selbst herunterladen, können Sie die createFromFile()Funktion alternativ verwenden . Lesen Sie dazu die offizielle Dokumentation .

AleksandrH
quelle
6

Room unterstützt jetzt Prepopulated Databases. Bereiten Sie Ihre Datenbank einfach mit Programmen wie SQLite Browser oder einem anderen Programm vor . Dann legen Sie es Assets Folderwahrscheinlich in einem Unterordner namens databasethen call:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.build()

Wenn Sie Ihre Datenbank nicht als Asset bereitgestellt, sondern heruntergeladen oder im Dateisystem gespeichert haben, lautet die Methode wie folgt:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromFile(File("mypath"))
.build()

Weitere Beschreibungen oder Datenbankmigrationen zu dieser Funktion finden Sie im Dokumentationstraining .

Xenolion
quelle
1
Funktioniert perfekt. Ein großes Dankeschön an @Xenolion Du hast meine vielen Tage gerettet.
GreenROBO
1
Ich habe gerade bemerkt, dass meine Datenbank immer von der Datenbank überschrieben wird (jedes Mal, wenn ich die App starte). Hatte sonst niemand dieses Problem? Mit Zimmer 2.2.5
Rimes
Ich weiß es, klicken Sie hier, um zu sehen, ob ich Ihnen helfen kann, schreiben
Xenolion
1

Ähnliche Lösung mit Raum ohne Verwendung externer Bibliotheken: 1. Kopieren Sie Ihre Datenbank in den Assets-Ordner. 2. Kopieren Sie Ihre Datenbank aus dem Assets-Ordner

public class MainActivity extends AppCompatActivity {

public static AppDatabase db;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    copyDatabase(getApplicationContext(), "yourdatabase.db");

    db = Room.databaseBuilder(getApplicationContext(), .class, "yourdatabase.db").allowMainThreadQueries().build();
}

private void copyDatabase(Context context, String databaseName) {
    final File dbPath = context.getDatabasePath(databaseName);

    // If the database already exists, return
    if (dbPath.exists()) {
        Log.d("Activity", "db Path Exists");
        return;
    }

    // Make sure we have a path to the file
    dbPath.getParentFile().mkdirs();

    // Try to copy database file
    try {
        final InputStream inputStream = context.getAssets().open(databaseName);
        final OutputStream output = new FileOutputStream(dbPath);

        byte[] buffer = new byte[8192];
        int length;

        while ((length = inputStream.read(buffer, 0, 8192)) > 0) {
            output.write(buffer, 0, length);
        }

        output.flush();
        output.close();
        inputStream.close();
    }
    catch (IOException e) {
        Log.d("Activity", "Failed to open file", e);
        e.printStackTrace();
    }
}

}}

graben
quelle
1

Ab Raum 2.2 können Sie Ihre Datenbank mit dem folgenden Befehl vorab füllen:

Room.databaseBuilder(appContext, TestDatabase.class, “Sample.db”)
    .createFromAsset(“database/myapp.db”)
    .build()
Tuan Dao
quelle
0

Sie kopieren Sie einfach assets/databasesauf app/databases
und als Add addMigrations()in databaseBuilder
sie Ihre Daten halten

user6269562
quelle