会议室数据库-外键约束失败,错误代码787

时间:2019-10-30 14:16:38

标签: java android android-sqlite android-room

我遇到了上述问题,即使在查看其他类似问题及其解决方案以及外键上的sqlite文档时也无法解决。

我知道外键必须先存在于父表中,然后才能在子表中创建。但是,即使先完成了,问题仍然存在。

这是我的程序流向崩溃点的方式: MainActivity->创建行程->显示为RecyclerView->单击以输入另一个活动(通过其trip_id)->创建位置->选择保存(trip_id,locationName,latLng)时崩溃

在这种情况下,Trip拥有一个PK:trip_id,而Location则将其视为FK。

public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ViewHolder> {

    public static final int UNCOMPLETED = 0;
    public static final int COMPLETED = 1;
    public static final int HIGHTLIGHT = 2;
    public static final int HIGHTLIGHT_COMPETED = 3;
    Cursor cursor;
    Context context;

    public NoteAdapter(Context context, Cursor cursor) {
        this.context = context;
        this.cursor = cursor;
    }

    public Cursor getCursor() {
        return this.cursor;
    }

    public void setCursor(Cursor cursor) {
        this.cursor = cursor;
    }

    public Context getContext() {
        return context;
    }

    public void setContext(Context context) {
        this.context = context;
    }

    @Override
    public int getItemViewType(int position) {
        if (cursor.moveToPosition(position)) {
            cursor.move(position);
            String content = cursor.getString(cursor.getColumnIndex(DatabaseHandler.KEY_CONTENT));
            int completed = cursor.getInt(cursor.getColumnIndex(DatabaseHandler.KEY_COMPLETED));
            int hightlight = cursor.getInt(cursor.getColumnIndex(DatabaseHandler.KEY_HIGHTLIGHT));


            if (completed == 1) {
                if (hightlight == 1) {
                    return HIGHTLIGHT_COMPETED;
                }
                return COMPLETED;
            }

            if (hightlight == 1 && completed == 0) {
                return HIGHTLIGHT;
            }

            return UNCOMPLETED;
        }
        return -1;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.item_row, parent, false);

        switch (viewType) {
            case UNCOMPLETED:
                view = inflater.inflate(R.layout.item_row, parent, false);
                break;
            case COMPLETED:
                view = inflater.inflate(R.layout.item_row_completed, parent, false);
                break;
            case HIGHTLIGHT:
                view = inflater.inflate(R.layout.item_row_hightlight, parent, false);
                break;
            case HIGHTLIGHT_COMPETED:
                view = inflater.inflate(R.layout.item_row_hightlight_completed, parent, false);
                break;
        }

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
        if (!cursor.moveToPosition(position)) {
            return;
        }
        String content = cursor.getString(cursor.getColumnIndex(DatabaseHandler.KEY_CONTENT));
        Date date = Utilities.stringToDate(cursor.getString(cursor.getColumnIndex(DatabaseHandler.KEY_DEADLINE)));

        int completed = cursor.getInt(cursor.getColumnIndex(DatabaseHandler.KEY_COMPLETED));

        holder.tvContent.setText(content);
        holder.tvDate.setText(Utilities.dateToString(date));

        if(completed == 1){
            holder.imgIcon.performClick();
        }
    }

    @Override
    public int getItemCount() {
        return (cursor.getCount());
    }



    public class ViewHolder extends RecyclerView.ViewHolder {
        TextView tvContent;
        TextView tvDate;
        ImageButton imgIcon;

        public ViewHolder(@NonNull final View itemView) {
            super(itemView);

            tvContent = itemView.findViewById(R.id.tvContent);
            tvDate = itemView.findViewById(R.id.tvDate);
            imgIcon = itemView.findViewById(R.id.imgCheck);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int position = getAdapterPosition();
                    cursor.move(position);

                    Note note = Utilities.cursorToNote(cursor);
                    Intent intent = new Intent(itemView.getContext(), AddNoteActivity.class);
                    intent.putExtras(note.sendNoteBundle());
                    intent.putExtra("type", true);
                    itemView.getContext().startActivity(intent);

                }
            });
        }
    }
}

这是在onActivityResult方法的第二个活动中创建Location对象的行

    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.class

 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

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

在将其添加到父表之前,我看不到它是如何添加外键的。

1 个答案:

答案 0 :(得分:0)

显示的代码似乎没有问题,那就是正在使用您的代码(为方便起见进行了少许修改(对于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个引用该Trip的位置。

就这样

  1. //在日志中显示为1 不正确或
  2. 尝试添加位置时,旅程不存在。

我建议添加以下内容以进行调试:-

  1. 将以下内容添加到适当的Dao中(以下内容使用TripDao,并且abstract TripDao tripDao();编码在@Database类中(以下使用AppDatabase))

:-

@Query("SELECT * FROM trip_table")
List<Trip> getAllTrips();
  1. 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 ID和REFERENCES跳闸值之间的差异。如果Trip不如预期的那样存在,则由于某种原因,它不会被添加,否则将传递不正确的TripId。以上应该有助于确定哪个。