12
votes

Apple's documentation of Relationship Delete Rules is simple and clear. But it only talks about One-to-Many relationship(Delete Rules for One-to-One relationship are easy to infer). It's not clear what these rules mean for Many-to-One relationship. So let's clarify them here.

We use the Employees-Department example used in Apple's documentation. Though the real-life implications may be ridiculous of these rules applying to Employees-Department relationship, we, as programmers, are only talking about their logic implications here.

  • Deny
    If there is an object at the relationship destination, then the source object cannot be deleted.

    For example, if you want to deleted an employee, no matter whether there are still other employees in his department, you must ensure that the department is first deleted otherwise the employee cannot be deleted.

  • Nullify
    Remove the source object from the inverse relationship of the object at the destination. (See @bshirley's concise explanation)

    For example, if you delete an employee, remove him from employees relationship of his department. This only makes sense if the number of remaining employees of the department is larger than the minimum count required, or if you ensure that you add new employees for the department before the next save operation.

    [Question: If it is the last employee, will the department's employees relationship become empty set or null?]
    (Answered by @TechZen: A to-many relationship always returns a set object. It is never nil. If there are not objects on the other side of the relationship, the set is empty.)

  • Cascade Delete the object at the destination of the relationship.

    For example, if you delete an employee, delete his department at the same time, even if there are still other employees in the department.

    (Usage Caveat: It usually causes a "a daisy chain of deletions across the entire object graph", as described by @TechZen in his example.)

  • No Action
    Do nothing to the object at the destination of the relationship.

    For example, if you delete an employee, leave his department as it is, even if it still believes it owns that employee.

The meanings of Delete Rules for Many-to-Many relationship can be inferred from here.

2
why is this example thinking from and employee perspective....if you delete an employee, delete his department at the same time, even if there are still other employees in the department..should be reverse.....if you delete an department, delete all the employee at the same time, even if there are still employees in the departmentAnish Parajuli 웃

2 Answers

8
votes

These are the delete rules for all relationships (not attributes). They are applicable for to-One or to-Many relationships.

  • Nullify - if you delete an employee, the reverse relationship is set to nil, if it was 1-to-1 then literally, in this case department's employees is reduced by one

  • Cascade - if you delete an employee, it's department will be deleted. The Department will follow the delete rule on all it's properties, 1) if employees delete rule was Cascade, all employees would be deleted by this action; 2) if employees delete rule was Nullify, all the employees would be "stranded" without a department

1
votes

You seem to be assuming that there is some change in the behavior of delete rules between one-to-many and many-to-one but there isn't. Everything works exactly the same way. If you think about it it has to be that way because a one-to-many is just the reciprocal relationship of a many-to-one.

I think it is the idea of a reciprocal relationship that is tripping you up here. Each side of the relationship is defined separately and has its own delete rule that can be different from the delete rule on the other side.

Let's take the common standard Department and Employee for an example.

Department{
  name:string
  employees<--(required,cascade)-->>Employee.department
}

Employee{
  name:string
  department<<--(required, nullify)-->Department.employee
  projects<--(optional,cascade)-->>Project.owner
}

Project{
  name:
  owner<<--(required,nullify)-->Employee.projects
}

Note that every relationship, every arrow line in the graphical model, has two descriptions attached to it in the reciprocal relationship (which is the standard form.) Each description describes the relationship from the "perspective" of one of the related entities. So, every one-to-many relationship is just the flipside of a matching many-to-one relationship.

Also, not the optional/required/mincount of a to-many relationship on side can block deletions of objects on the other side.

Take the Department<-->>Employee relationship. From the perspective of the Department, a Department object must have at least one employee. If it only has one employee, then that employee cannot be deleted. If the Department object itself is deleted then all it's employees are deleted as well. From the perspective of the employee, each employee must have a single department. Any attempt to save nil value for an employee object's department value will throw a validation error. However, if the employee object is deleted, then the nothing happens to the Department object at all except that it looses one of its employee objects. However, if the employee object is the sole employee object in the relationship, the Department object will block the deletion.

A cascade deletion, as the name implies, can set off a daisy chain of deletions across the entire object graph. When you delete a Department object it deletes all its related Employee objects each of which in turn delete all their Project objects.

But what if you have a cascade delete rule set on both sides of a many-to-many relationship like this:

Alpha{
  betas<<--(cascade)-->>Beta.alphas
}

Beta{
  alphas<<--(cascade)-->>Alpha.betas
}

In that case, deleting any one object in the graph would delete all other objects related by either keypath. Deleting one Beta object would delete all its Alpha objects which would in turn delete all their Beta objects which would in turn delete all their Alpha objects... and so on until all the related objects reachable were deleted.

Obviously a two sided to-many, cascade relationship is a good way to shoot yourself in the foot.

In summation:

  • Relationships are defined on both sides of the relationships by each entity independently.
  • Delete rules can be overridden by optionality or mincounts.
  • Understanding the runtime behavior of a relationship requires combining the effects of delete rules, optionality and mincounts.

One reason for the creation of the data model editor was to impose some restrictive logic on relationship rules to keep/warn coders from creating unexpected and cross purpose rules.