EclipseLink - bidirectional OneToMany relation - java

Let's say I have two entities:
#Entity
public class Customer implements Serializable {
...
#OneToMany(cascade=ALL, mappedBy="customer")
public Set<Order> getOrders() {
return orders;
}
...
}
#Entity
public class Order implements Serializable {
...
#ManyToOne
#JoinColumn(name="CUST_ID", nullable=false)
public Customer getCustomer() {
return customer;
}
...
}
Then, I'm persisting Customer entity, and after that, Order entity with reference to previously added Customer.
When I retrieve this customer from database, and call getOrders, it returns empty set.
Is it normal behaviour? If it is, what can I do to automatically refresh this set when I add new Order entity?

Jpa does not maintain relationships for you, the application is required to set both sides of bidirectional relationships to keep them in synch with the database. Add order to the orders list when you set the order->customer relation and if customer is detached, merge it to have the changes to the collection picked up.
Otherwise you will need to explicitely refresh using em.refresh or a query with a refresh query hint after the transaction, or evict the customer from the caches. Either way, it requires a database hit that is easily avoided by just maintaining both sides of relationships.

If you retrieve the customer from the same transaction, then yes, it's expected behavior. The reason is that the EntityManager returns the order it has in its first-level cache, and that you created yourself, without adding any order to its set of orders. It's your responsibility to maintain the coherence of the object graph by maintaining the two sides of the association:
order.setCustomer(customer);
customer.addOrder(order);

Related

JPA OneToOne Lazy relation

I have 2 entities: User and UserProfile that have a bidirectional #OneToOne relationship between them.
Due to some old DB Design the UserProfile is the owner (i have the column user_id in users_profiles table)
The relationship is Lazy as I have fetchType Lazy and optional = false.
Everything works as expected, I mean when I load an UserProfile it does not automatically loads the User also. I guess this is perfectly normal as I load from the owner side.
My problem is that if I load a User (owned side) it loads automatically the UserProfile although the relationship is lazy.
I mean: Is this normal that when I load an entity from the owned side to load the owner entity also ?
#Entity
#Table(name = "users")
public class User extends BaseEntity implements Serializable {
#OneToOne(mappedBy = "user", optional=false, fetch = FetchType.LAZY)
private UserProfile profile;
// .................rest of entity
}
#Entity
#Table(name="users_profiles")
public class UserProfile extends BaseEntity implements Serializable {
#OneToOne(optional=false, fetch = FetchType.LAZY)
#JoinColumn(name="user_id")
private User user;
// ... rest of entity here
}
The way that I test this is by loading the User entity with EntityManager method find(id).
I have noticed that when the relation is not lazy I have only one query with a join inside. If I put the current setup I have two individual queries: 1 for user and the other one for profile.
It is important to realize, that lazy loading behavior is not enforced by JPA specification, it is only recommended. It is up to hibernate as the implementation to choose if it is supported and under which conditions.
Hibernate usually does its best to load the data lazily when requested, but in case of one-to-one mapping, when the relationship is stored in another table (the primary key is in table users_profiles), it needs to query also the second table to retrieve the primary key to create a proxy object. In fact, it does not retrieve only id, but full row and creates the UserProfile in eager way, because it costs almost nothing to fetch additinal data when the table needs to be joined in any case.
The answer for Hibernate: one-to-one lazy loading, optional = false suggests that making relationship non-optional should make it lazy, but I doubt it is true. Hibernate would need to create a proxy without an ID, which is doubtfully correct in all cases. One case where that would fail is when you remove the proxy object from the collection in parent entity, and then try to read its data - as the proxy itself does not carry enough information to retrieve data lazily, it is not possible without connection to parent entity.
This is normal, by default, hibernate creates run-time proxies. It loads the objects as a proxy unless a fetch mode is specified or set to false.
for example, load() always retrieves proxy objects. If called more than once within same session, it reads data from persistent context cache. That's because once the object is loaded in cache, the next subsequent calls perform repeatable read.

Many to many bidirectional mapping in JPA

I have the following JPA entities.
A profile have many users and a user have many profiles:
#Entity
public class Profile implements Serializable {
#Id
private Long id;
#ManyToMany(cascade = CascadeType.ALL)
private List<User> users;
...
}
#Entity
public class User implements Serializable {
#Id
private Long id;
#ManyToMany(mappedBy = "users")
private List<Profile> profiles;
...
}
On my application, when a user is merged, the profiles are updated on database.
However, when a profile is merged, the users are not updated.
Is possible to map my entities in order to make both sides merge their lists?
I am using JPA 2.1 and Hibernate.
Your Profile entity is ownind side or relationship. It's up to it, to manage relationship, so in order to update User you'll have to update Profile too or make manual SQL calls.
Java Specification for JPA 2.1 says that:
• For many-to-many bidirectional relationships either side may be the owning side
So if you'd like to make both entities editable from both side, remove mappedBy element and assigne necessacy cascade. But I'm not sure it works in Hibernate (didn't try actually), see this docs on mapping, there's no information about m:m without owning side: http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch07.html#collections-bidirectional
Otherwise, you may need to iterate through collection in Profile entity and then change them. For example:
for( User user : profile.getUsers() ) {
user.setSomething(.....);
}
session.merge(profile);
Changing List to Set might be needed in order to avoid Hibernate's delete and reinsert, described here: http://assarconsulting.blogspot.fr/2009/08/why-hibernate-does-delete-all-then-re.html
Also, don't forget about equals() and hashCode() methods override

How to get the primary key of new entity added to JPA relationship before tx commit?

Lets say I have a JPA entities that look like the following, full annotations details omitted for simplicity
#Entity
public class Order {
#Id Integer id;
#OneToMany List<Parts> parts;
}
#Entity
public class Part {
#Id Integer id;
#ManyToOne Order order;
}
Lets say that I am adding a new part to an existing order
Order order = entityManager.find(Order.class,1);
Part newPart = new Part();
newPart.setOrder(order);
order.getParts().add(part);
// set other part fields
Integer newPartId = newPart.getId(); // newPartId is null
Is there a way to get the id of the newly added item without explicitly calling persist() on the entity manager or waiting till after the current tx commits?
Once you've fixed your mapping as explained by Andrei's answer, persist your entity and then call flush() on the EntityManager, and then get the ID from the entity.
Hibernate won't generate an ID for a non-persistent entity it doesn't even know about.
As I understand you have there a bidirectional relationship, in which you do not specify which is the owner of the relationship:
For example, in order to mark the Part entity as the owner of the relationship, you add in the Order entity the following:
#Entity
public class Order {
#Id Integer id;
#OneToMany(mappedBy="parts") List<Parts> parts;
}
And now to your question: it is not possible, as long your entity does not reach the DB. In order to force a synchronization to DB, you must call entityManager.flush(). On the other side, you could also call entityManager.merge() instead of persist(), and after that a flush().
No, it's not possible. The id is generated when the entity is persisted for the first time in the database. There's nothing you can do about it.

How to add one-to-many association in Hibernate to an embedded type?

I have an Entity that holds the last instance of a Component:
#Entity
public class Customer {
...
#Embedded
public ServiceCall getLastServiceCall() {...}
...
}
#Embeddable
public class ServiceCall() {
public Date getDate();
public Status getStatus();
}
The ServiceCall is embedded in order to call customer.getLastServiceCall().getDate() without requiring another table join.
I want to make that association one-to-many, and start saving all ServiceCalls, while holding one embedded in the customer.
Hibernate's Docs (v3.6, which I'm using) states:
You can also use association annotations in an embeddable object (ie #OneToOne, #ManyToOne, #OneToMany or #ManyToMany). To override the association columns you can use #AssociationOverride.
and it seem that all I should do is add #OneToMany to the LastServiceCall association.
Will that work for me? If not, what are my alternatives? If yes, how will that affect 2nd level cache, and is there a limitation on updating that embedded instance (I can live with an immutable objects)?
#Embeded types are not supposed to have their own identity in the database, so I don't think you can add #OneToMany to the Customer class on the ServiceCall.
#OneToMany
#Embedded
public ServiceCall getLastServiceCall() {...}
However you can add an association to the #Embeded Service call element like so.
#Entity
pubic class HistoricalServiceCall extends ServiceCall
{
#Id
private String id;
}
#Embeddable
public class ServiceCall {
#OneToMany(fetch=FetchType.LAZY)
#JoinColumn(name="join_column_defined_on_customer_table")
List<HistoricalServiceCall> getServiceCallHistory();
}
Update: putting FetchType.LAZY on the getServiceCallHistory() is a hint to the JPA provider to wait until you call getServiceCallHistory before it does another select to pull in that association.
So with the setup I am describing if you do customer.getLastServiceCall().getDate() it will not pull
in the ServiceCallHistory before the relationship is lazy.
What you need is the following:
A Customer entity
An embeddable ServiceCall
A HistoricalServiceCall entity.
The Customer should contain an embedded ServiceCall field (the last service call).
The HistoricalServiceCall entity should have an ID, an embedded ServiceCall field (the data of the HistoricalServiceCall), and, potentially, a ManyToOne association to Customer if you want the OneToMany to be bidirectional.
The Customer should have a OneToMany association to HistoricalServiceCall.

How to load the id, but not the object if it doesn't exist in a ManyToOne mapping

I have two objects Customer and Transaction. They look something like this:
#Entity
class Transaction {
#Id
long id;
#Column("CUSTOMER_ID")
long customerId;
#ManyToOne
#JoinColumn(name="CUSTOMER_ID", insertable=false, updatable=false, nullable=false)
Customer customer
...
}
#Entity
class Customer {
#ID
long id;
...
}
The problem is we use fake customer numbers to identify some types of transactions. It's a legacy practice that I can do nothing to change and various other processes outside my control rely on it.
What I'd like to be able to do is get the customer number back for the dummy customer transactions and just have a null customer on my transaction object. For transactions that aren't to dummy customers I'd like to have the customer object set. Ideally I'd rather not have to test for the dummy customer numbers and issue separate queries.
How do I write a query and map my object to do that? Right now everything I try, I end up with an EntityNotFound exception for the dummy policy IDs.
using UNION you could select the proper customer record, unioned with a default 'NO SUCH CUSTOMER' record, make sure the default value comes second by ordering apropriatly and fetching only the first result.
In JPA if the ManyToOne does not reference anything you should just get null. Not sure how you are getting EntityNotFound. It may be a specific issue to your JPA provider.

Categories

Resources