Room database - Foreign Key Constraint Failed Error code 787

走远了吗. 提交于 2020-04-17 22:53:07

问题


I am getting the above problem, and am unable to solve even while looking at other similar questions and their solutions, as well as the sqlite document on foreign keys.

I understand that a foreign key must exist in the parent table first before it can be created in the child table. However, even though that was done first the problem is still there.

This is how my program flows until the point where it crashes: MainActivity -> Create Trip -> Shows up as a RecyclerView -> Click on it to enter another activity (passes its trip_id) -> Create a Location -> crashes when save is selected (trip_id, locationName, latLng)

In this case, Trip has a PK: trip_id, while Location takes it as a 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)

This is the line to create the Location object in the 2nd activity in the onActivityResult method

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

I fail to see how this is adding a foreign key before it is added in the parent table...


回答1:


There appears to be nothing wrong with the code shown, that is using your code (slightly modified for convenience (just a long for latLng and run on main thread).

Then using :-

    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());
    }

results in (as expected) :-

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

i.e TripId 1 exists and 2 locations have been added referencing that Trip.

As such either

  1. //shows up as 1 in the log is incorrect or
  2. The Trip does not exist when the attempt is made to add the location.

I'd suggest adding the following for debugging purposes :-

  1. add the following to the appropriate Dao (the following uses TripDao and that abstract TripDao tripDao(); is coded in the @Database class (AppDatabase was used below))

:-

@Query("SELECT * FROM trip_table")
List<Trip> getAllTrips();
  1. after Line Location location = new Location(tripId, locationName, locationLatLng); and before the insert add

:-

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());

This will the list the current Trips and the location that is to be added. Assuming a failure then would be a discrepancy between the Trip id's and the REFERENCES trip value. If the Trip doesn't exist as expected then for some reason it is not being added otherwise the incorrect TripId is being passed. The above should help to establish which.



来源:https://stackoverflow.com/questions/58627377/room-database-foreign-key-constraint-failed-error-code-787

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!