I can't understand difference between those annotations. In my use case i want create one-to-many relation between tables. And found two options: one with @ForeignKey and another with @Relation
Also i found that if i update the row (e.g. with OnCoflictStrategy.Replace) i will lost foreign key for this row is it true?
A @ForeignKey defines a constraint (aka rule) that requires that the child column(s) exist in the parent column(s). If an attempt is made to break that rule then a conflict occurs (which may be handled various ways by the onDelete/onUpdate definition).
An @Relationship is used to define a relationship where a number of child (perhaps Foreign Key children) objects are returned in the parent object.
Underneath it all @Relation automatically (effectively) joins the tables and generates the number of child objects. Whilst a @ForeignKey just affects the schema (with the exception of onDelete/onUpdate handling), it does not result in the respective tables being joined.
Perhaps Consider the following :-
Service Entity
@Entity(
tableName = "services"
)
class Services {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "services_id")
var id: Long = 0
var service_date: String = ""
var user_mobile_no: String = ""
}
and
ServiceDetail Entity :-
@Entity(
tableName = "service_detail",
foreignKeys = [
ForeignKey(
entity = Services::class,
parentColumns = ["services_id"],
childColumns = ["services_id"],onDelete = ForeignKey.SET_DEFAULT
)
]
)
class ServiceDetail {
@PrimaryKey
var id: Long? = null;
var services_id: Long = 0;
@ColumnInfo(defaultValue = "1")
var service_type_id: Long = 0;
constructor()
@Ignore
constructor(services_id: Long, service_type_id: Long) {
this.services_id = services_id
this.service_type_id = service_type_id
}
}
Now consider this normal class (POJO), which is NOT an entity (aka table) :-
class ServiceWithDetail {
@Embedded
var services: Services? = null
@Relation(entity = ServiceDetail::class,parentColumn = "services_id",entityColumn = "services_id")
var serviceDetail: List<ServiceDetail>? = null
}
This is roughly saying when you ask for a ServiceWithDetail object then get a services object along with a list of the related service_detail objects
You would have a Dao such as :-
@Query("SELECT * FROM services")
fun getAllServices() :List<ServiceWithDetail>
So it will get all the services from the services table along with the related (i.e. where the services_id in the services_detail is the same as the services_id of the current services row being processed).
onConflictStrategy
REPLACE does the following :-
When a UNIQUE or PRIMARY KEY constraint violation occurs, the REPLACE algorithm deletes pre-existing rows that are causing the constraint violation prior to inserting or updating the current row and the command continues executing normally.
If a NOT NULL constraint violation occurs, the REPLACE conflict resolution replaces the NULL value with the default value for that column, or if the column has no default value, then the ABORT algorithm is used. If a CHECK constraint or foreign key constraint violation occurs, the REPLACE conflict resolution algorithm works like ABORT.
When the REPLACE conflict resolution strategy deletes rows in order to satisfy a constraint, delete triggers fire if and only if recursive triggers are enabled.
The update hook is not invoked for rows that are deleted by the REPLACE conflict resolution strategy. Nor does REPLACE increment the change counter. The exceptional behaviors defined in this paragraph might change in a future release.REPLACE
Hence, the potential for the behaviour that you have experienced. However, it depends upon what the update is doing. If the value for the ForeignKey(s) differ then they should, assuming there is no Foreign Key conflict replace the foreign key value with the new valid value. If the Foreign Key value(s) is(are) unchanged then replacement row will have the same Foreign Keys.