База данных помещения - ошибка ограничения внешнего ключа, код ошибки 787 - PullRequest
0 голосов
/ 30 октября 2019

Я получаю вышеуказанную проблему и не могу решить даже при рассмотрении других подобных вопросов и их решений, а также документа sqlite по внешним ключам.

Я понимаю, что внешний ключ должен существоватьсначала в родительской таблице, прежде чем ее можно будет создать в дочерней таблице. Однако, хотя это было сделано в первую очередь, проблема все еще существует.

Так проходит моя программа до точки, в которой происходит сбой: MainActivity -> Create Trip -> отображается как RecyclerView -> щелкните по нему, чтобы ввести другое действие (передает его trip_id) -> создать местоположение-> вылетает при выборе сохранения (trip_id, locationName, latLng)

В этом случае Trip имеет PK: trip_id, а Location принимает его как FK.

    Process: com.example.TravelPlanner, PID: 4701
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:354)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY[787])
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:995)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
        at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.java:51)
        at androidx.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:64)
        at com.example.travelplanner.LocationDao_Impl.insert(LocationDao_Impl.java:110)
        at com.example.travelplanner.LocationRepository$InsertLocationAsyncTask.doInBackground(LocationRepository.java:48)
        at com.example.travelplanner.LocationRepository$InsertLocationAsyncTask.doInBackground(LocationRepository.java:39)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)

Этострока для создания объекта Location во 2-м действии в методе onActivityResult

 String locationName = data.getStringExtra("locationName");
            String latLng = data.getStringExtra("latLng");

            Intent intent = getIntent();
            Bundle bundle = intent.getExtras();
            int tripId = (int)bundle.get("tripId"); // D/TRIPID: Trip id is 1

            Log.d("TRIPID", "Trip id is " + tripId);

            String[] latlong = latLng.split(",");
            double latitude = Double.parseDouble(latlong[0]);
            double longitude = Double.parseDouble(latlong[1]);
            LatLng locationLatLng = new LatLng(latitude,longitude);

 Location location = new Location(tripId, locationName, locationLatLng); 
 Log.d("LOCATIONID", "Trip id is " + location.getTripId()); // D/LOCATIONID: Trip id is 1

locationViewModel.insert(location); //crashes here

Location.class

@Entity(tableName = "location_table",foreignKeys = @ForeignKey(entity = Trip.class, parentColumns = "location_Id", childColumns = "trip_Id"),
        indices = {@Index(value = {"trip_Id"})})

public class Location {

    @PrimaryKey(autoGenerate = true)
    private int locationId;

    @ColumnInfo (name = "trip_Id")
    private int tripId;

    private String locationName;

    @TypeConverters(LatLngConverter.class)
    private LatLng latLng;


    public Location(int tripId, String locationName, LatLng latLng) {
        this.tripId = tripId;
        this.locationName = locationName;
        this.latLng = latLng;
    } 

Trip.class

@Entity(tableName = "trip_table",
        indices = {@Index(value = {"location_Id"},
                unique = true)})

public class Trip {

    @PrimaryKey(autoGenerate = true) 
    @ColumnInfo (name = "location_Id")
    private int id;

    private String title;

    private String description;

    private int priority;

    //id will be auto generated so it need not be inside constructor
    public Trip(String title, String description, int priority) {
        this.title = title;
        this.description = description;
        this.priority = priority;
    } 

Не удаетсячтобы увидеть, как происходит добавление внешнего ключа перед его добавлением в родительскую таблицу ...

1 Ответ

0 голосов
/ 30 октября 2019

Кажется, нет ничего плохого в показанном коде, который использует ваш код (слегка измененный для удобства (просто long для latLng и запуска в основном потоке).

Затем используйте: -

    appDatabase = Room.databaseBuilder(this,AppDatabase.class,"travel_planner")
            .allowMainThreadQueries()
            .build();
    Trip trip1 = new Trip("A Trip","My first trip",1);
    int tripID = (int) appDatabase.tripDao().insertTrip(trip1);
    appDatabase.locationDao().insertLocation(new Location(tripID,"Here",100));
    appDatabase.locationDao().insertLocation(new Location(tripID,"Here again",200));
    List<Trip> tripList = appDatabase.tripDao().getAllTrips();
    for (Trip t: tripList) {
        Log.d("TRIPINFO","Trip is " + t.getDescription() + " ID is " + t.getId());
    }
    List<Location> locationList = appDatabase.locationDao().getAllLocations();
    for (Location l: locationList) {
        Log.d("LOCINFO","Location is " + l.getLocationName() + " ID is " + l.getLocationId() + " REFERENCES Trip " + l.getTripId());
    }

приводит к (как и ожидалось): -

2019-10-31 06:28:09.591 27335-27335/? D/TRIPINFO: Trip is My first trip ID is 1 
2019- 10-31 06:28:09.593 27335-27335/? D/LOCINFO: Location is Here ID is 1 REFERENCES Trip 1 
2019-10-31 06:28:09.593 27335-27335/? D/LOCINFO: Location is Here again ID is 2 REFERENCES Trip 1

т.е. TripId 1 существует, и были добавлены 2 местоположения, ссылающиеся на это отключение.

Таким образом, либо

  1. // отображается как 1 в журнале неверно или
  2. Поездка делаетне существует, когда делается попытка добавить местоположение.

Я бы предложил добавить следующее для целей отладки: -

  1. добавить следующее в соответствующий Dao (следующее использует TripDao и код abstract TripDao tripDao(); закодирован в классе @Database (AppDatabase использовался ниже))

: -

@Query("SELECT * FROM trip_table")
List<Trip> getAllTrips();
после строки Location location = new Location(tripId, locationName, locationLatLng); и перед вставкой добавить

: -

List<Trip> tripList = appDatabase.tripDao().getAllTrips();
for (Trip t: tripList) {
    Log.d("TRIPINFO","Trip is " + t.getDescription() + " ID is " + t.getId());
}
Log.d("LOCINFO","Location is " + location.getLocationName() + " ID is " + location.getLocationId() + " REFERENCES Trip " + location.getTripId());

Это список текущих поездок и местоположения, которое должно быть добавлено. Если предположить, что сбой будет несоответствием между идентификаторами отключений и значением срабатывания ССЫЛКИ. Если Trip не существует, как ожидалось, то по какой-то причине он не добавляется, в противном случае передается неверный TripId. Вышесказанное должно помочь установить, какие.

...