Either way will add an entity to a PersistenceContext, the difference is in what you do with the entity afterwards.
Persist takes an entity instance, adds it to the context and makes that instance managed (ie future updates to the entity will be tracked).
Merge returns the managed instance that the state was merged to. It does return something what exists in PersistenceContext or creates a new instance of your entity. In any case, it will copy the state from the supplied entity, and return managed copy. The instance you pass in will not be managed (any changes you make will not be part of the transaction - unless you call merge again). Though you can use the returned instance (managed one).
Maybe a code example will help.
MyEntity e = new MyEntity();
// scenario 1
// tran starts
em.persist(e);
e.setSomeField(someValue);
// tran ends, and the row for someField is updated in the database
// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue);
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)
// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue);
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)
Scenario 1 and 3 are roughly equivalent, but there are some situations where you'd want to use Scenario 2.
To understand this, you must take a step back. In OO, the customer owns the orders (orders are a list in the customer object). There can't be an order without a customer. So the customer seems to be the owner of the orders.
But in the SQL world, one item will actually contain a pointer to the other. Since there is 1 customer for N orders, each order contains a foreign key to the customer it belongs to. This is the "connection" and this means the order "owns" (or literally contains) the connection (information). This is exactly the opposite from the OO/model world.
This may help to understand:
public class Customer {
// This field doesn't exist in the database
// It is simulated with a SQL query
// "OO speak": Customer owns the orders
private List<Order> orders;
}
public class Order {
// This field actually exists in the DB
// In a purely OO model, we could omit it
// "DB speak": Order contains a foreign key to customer
private Customer customer;
}
The inverse side is the OO "owner" of the object, in this case the customer. The customer has no columns in the table to store the orders, so you must tell it where in the order table it can save this data (which happens via mappedBy
).
Another common example are trees with nodes which can be both parents and children. In this case, the two fields are used in one class:
public class Node {
// Again, this is managed by Hibernate.
// There is no matching column in the database.
@OneToMany(cascade = CascadeType.ALL) // mappedBy is only necessary when there are two fields with the type "Node"
private List<Node> children;
// This field exists in the database.
// For the OO model, it's not really necessary and in fact
// some XML implementations omit it to save memory.
// Of course, that limits your options to navigate the tree.
@ManyToOne
private Node parent;
}
This explains for the "foreign key" many-to-one design works. There is a second approach which uses another table to maintain the relations. That means, for our first example, you have three tables: The one with customers, the one with orders and a two-column table with pairs of primary keys (customerPK, orderPK).
This approach is more flexible than the one above (it can easily handle one-to-one, many-to-one, one-to-many and even many-to-many). The price is that
- it's a bit slower (having to maintain another table and joins uses three tables instead of just two),
- the join syntax is more complex (which can be tedious if you have to manually write many queries, for example when you try to debug something)
- it's more error prone because you can suddenly get too many or too few results when something goes wrong in the code which manages the connection table.
That's why I rarely recommend this approach.
Best Solution
JPA's behaviour is correct (meaning as per the specification): objects aren't deleted simply because you've removed them from a OneToMany collection. There are vendor-specific extensions that do that but native JPA doesn't cater for it.
In part this is because JPA doesn't actually know if it should delete something removed from the collection. In object modeling terms, this is the difference between composition and "aggregation*.
In composition, the child entity has no existence without the parent. A classic example is between House and Room. Delete the House and the Rooms go too.
Aggregation is a looser kind of association and is typified by Course and Student. Delete the Course and the Student still exists (probably in other Courses).
So you need to either use vendor-specific extensions to force this behaviour (if available) or explicitly delete the child AND remove it from the parent's collection.
I'm aware of: