问题
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
- //shows up as 1 in the log is incorrect or
- The Trip does not exist when the attempt is made to add the location.
I'd suggest adding the following for debugging purposes :-
- 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();
- 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