I've got a following Java class which is also a Hibernate entity:
@Entity
@Table(name = "category")
public class Category {
@ManyToOne
@JoinColumn(name="parent_id")
private Category parent;
public Category getParent() {
return parent;
}
public void setParent(Category parent) {
this.parent = parent;
}
The category represents a node in a category tree. I'm implementing a webservice which allows to CRUD categories. For instance, the interface has the ability to create a category tree node and it passes the category id as a parameter.
I want just to create a new Category object and persist it into the database without fetching the parent object. My data provider class looks like this:
public void createCategory(int parent_id, String name, CategoryType type) {
Category category = new Category();
category.setName(name);
// category.setParent(?); <- I don't have this object here
// category.setParentId(id); <- but I do have the id
category.setType(type);
this.categoryDao.save(category);
}
My question is: what can I do to create a new Category object with the parent_id set if assuming I won't call hibernate to fetch the parent for me (this would be stupid)? Can I provide a setParentId/getParentId method for Category class? What hibernate annotations would it have?
Hibernate provides a method called (quite confusingly) Session.load()
for this scenario.
Session.load()
returns a lazy proxy with the given identifier without querying the database (if object with the given identifier is already loaded in the current Session
, it returns an object itself).
You can use that proxy to initialize relationships in your entities being saved:
category.setParent(session.load(Category.class, parent_id));
Note that this code doesn't check existence of Category
with the given id. However, if you have a foreign key constraint in your DB schema, you'll get a constraint violation error when invalid id is passed in.
JPA equivalent of this method is called EntityManager.getReference()
.
There is one more solution to problem - using the default constructor of the entity you want a reference for and set id and version (if it is versioned).
Constructor<S> c = entityClass.getDeclaredConstructor();
c.setAccessible(true);
S instance = c.newInstance();
Fields.set(instance, "id", entityId.getId());
Fields.set(instance, "version", entityId.getVersion());
return instance;
We can use such an approach because we don't use lazy loading but instead have 'views' of entities which are used on the GUI. That allows us to get away from all the joins Hibernate uses to fill all eager relations. The views always have id and version of the entity. Hence, we can fill the reference by creating an object which would appear to Hibernate as not transient.
I tried both this approach and the one with session.load(). They both worked fine. I see some advantage in my approach as Hibernate won't leak with its proxies elsewhere in the code. If not properly used, I'll just get the NPE instead of the 'no session bound to thread' exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With