Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Room composite Primary Key link to Foreign Key

I am implementing an android application with Room database and have a small question about relationships in this DB.

I have two tables:

@Entity(tableName = "foods", primaryKeys = {"id", "language_id"},
        indices = {@Index(value = {"id", "language_id"}, unique = true)},
        inheritSuperIndices = true)
public class Food {

    @NonNull
    @ColumnInfo(name = "id")
    private String mId;

    @NonNull
    @ColumnInfo(name = "language_id")
    private String mLanguageId;

}

@Entity(tableName = "ingredients", primaryKeys = {"id", "language_id"},
        indices = {@Index(value = {"id", "language_id"}, unique = true), 
        @Index(value = {"food_id", "food_language_id"}, unique = true)},
        foreignKeys = {@ForeignKey(entity = Food.class, parentColumns ="id", 
        childColumns = "food_id", onUpdate = CASCADE, onDelete = CASCADE),
        @ForeignKey(entity = Food.class, parentColumns = "language_id", 
        childColumns = "food_language_id", onUpdate = CASCADE, onDelete = 
        CASCADE)},
        inheritSuperIndices = true)
public class Ingredient {

    @NonNull
    @ColumnInfo(name = "id")
    private String mId;

    @NonNull
    @ColumnInfo(name = "language_id")
    private String mLanguageId;

    @ColumnInfo(name = "food_id")
    private String mFoodId;

    @ColumnInfo(name = "food_language_id")
    private String mFoodLanguageId;

}

The both tables 'Food' and 'Ingredient' have composite primary keys ('id', 'language_id'). The Food object has to contains a List and of course a @Relationship

public class FoodWithIngredients extends Food{

    @Relation(parentColumn = "id", entityColumn = "food_id", entity = 
    Ingredient.class)
    private List<Ingredient> mIngredients;

}

After I try to run this code received these messages

Warnning:

food_language_id column references a foreign key but it is not part of an index. This may trigger full table scans whenever parent table is modified so you are highly advised to create an index that covers this column.

Error:

Ingredient has a foreign key (food_id) that references Food (id) but Food does not have a unique index on those columns nor the columns are its primary key. SQLite requires having a unique constraint on referenced parent columns so you must add a unique index to Food that has (id) column(s).

Could someone help me with that?

Thanks in advance :)

like image 269
MrVasilev Avatar asked May 09 '18 08:05

MrVasilev


People also ask

Can a composite primary key include a foreign key?

Is it possible to use one of the attributes of a composite primary key as a foreign key? Yes, this is quite common and perfectly valid. The best example comes from the classic many-to-many relationship.

How can you establish a link between primary key and foreign key?

In a foreign key reference, a link is created between two tables when the column or columns that hold the primary key value for one table are referenced by the column or columns in another table. This column becomes a foreign key in the second table. For example, the Sales.

Does a foreign key have to link to a primary key?

From Books Online: A FOREIGN KEY constraint does not have to be linked only to a PRIMARY KEY constraint in another table; it can also be defined to reference the columns of a UNIQUE constraint in another table. So in your case if you make AnotherID unique, it will be allowed.

How do you make a composite primary key a foreign key?

A composite key specifies multiple columns for a primary-key or foreign-key constraint. The next example creates two tables. The first table has a composite key that acts as a primary key, and the second table has a composite key that acts as a foreign key.


1 Answers

Ok, I found where was my mistake. My @ForeignKey was wrong, the right one is this:

@ForeignKey(
             entity = Food.class,
             parentColumns = {"id", "language_id"},
             childColumns = {"food_id", "food_language_id"},
             onUpdate = CASCADE, onDelete = CASCADE)

The difference is that I put multiple columns inside 'parentColumns' and 'childColumns' and it works correct.

@Danail Alexiev The insertion is something like this:

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertFoods(List<Food> foods);

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertIngredients(List<Ingredient> ingredients);

@Transaction
public void insertFoodData(List<Food> foods, RulesOfGolfDatabase database) {
    if (foods != null && database != null) {
        insertFoods(foods);
        for (Food food : foods) {
            insertIngredients(food.getIngrediants());
        }
    }
}

The most important thing here is that you have to insert first the owner of the @Relation (In this example is Food) and after that every data which is in the relationship

like image 121
MrVasilev Avatar answered Sep 23 '22 01:09

MrVasilev