When you use Update method in a TObjectManager object, there should be no managed object with same Id in the object manager, otherwise an exception is raised. You can avoid such exception using the Merge or Replicate methods. These methods behave almost exactly the same, and will take a transient instance and merge it into the persistent instance. In other words, all the content of the transient object will be copied to the persistent object. Note that the transient object will continue to be transient.
If there is no persistent object in the object manager with the same id, the object manager will load an object from the database with the same id of the transient object being merged.
If the object has an id and no object is found in the database with that id, the behavior depends on the method called (and that is the only difference between Merge and Replicate methods):
- if Merge method was called, an exception will be raised;
- if Replicate method was called, a new object with the specified id will be saved (inserted).
Customer2 := TCustomer.Create;
Customer2.Id := Customer2Id;
Customer2.Name := 'Mary';
Customer2.Sex := tsFemale;
MergedCustomer := Manager2.Merge<TCustomer>(Customer2);
In the example above, a TCustomer object was created and assigned an existing id. When calling Merge method, all data in Customer2 will be copied to the persistent object with same id in Manager2. If no persistent object exists in memory, it will be loaded from the database. Customer2 variable will still reference a transient object. The result value of Merge/Replicate method is a reference to the persistent object in the object manager.
If the transient object passed to Merge/Replicate has no id, then a Save operation takes place. Merge/Replicate will create a new internal instance of object, copy all the contents from the passed object to the internal one, and Save (insert) the newly created object. Again, the object returned by Merge/Replicate is different from the one passed. Take a look at the following example:
NewCustomer := TCustomer.Create;
NewCustomer.Name := 'John';
MergedCustomer := Manager2.Replicate<TCustomer>(NewCustomer);
// MergedCustomer <> NewCustomer! NewCustomer must be destroyed
In the example above, NewCustomer doesn't have an id. In this case, Merge/Replicate will create a new customer in database, and return the newly created object. MergedCustomer points to a different instance than NewCustomer. MergedCustomer is the persistent one that is tracked by the object manager (and will be destroyed by it when manager is destroyed). NewCustomer continues to be a transient instance and must be manually destroyed.
Note that Merge/Replicate does nothing in the database in update operations - it just updates the persistent object in memory. To effectively update the object in the database you should then call Flush method. The only exception is the one described above when the object has no id, or when Replicate saves a new object with existing id. In those cases, a Save (insert) operation is performed immediately in the database.
The cascades defined in Association and ManyValuedAssociation attributes in your class are applied here. Any associated object or collection item that has TCascadeType.Merge defined will also be merged/replicated into the manager and the reference will be changed. For example, if Customer has a Country property pointing to a transient TCountry object. The TCountry object will me merged, a new instance will be returned from the merging process, and Customer.Country property will be changed to reference the new instanced returned by the merging process.