Hibernate self-reference relationship and lazy loading - java

Before asking the question, here is the context of my problem:
I got a specific entity named 'Category', which have a property Category (a parent) and another property of type 'relatedEntity'.
Here is a sample of the code:
#Entity
public class Category {
...
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityID")
private RelatedEntity entity;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="parentID")
private Category parent;
...
}
So, as you can see, I've declared ManyToOne relationships for these properties, and also a lazy loading fetching strategy.
Now, I use this method inside a DAO object to get a list of Category:
public List<Category> getAll() {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(Category.class);
return (crit.list());
}
I got a specific service object which calls the DAO method:
#Transactional(readOnly = true)
public List<Category> getAllCategories() {
return (categoryDAO.getAll());
}
Nothing really extraordinary for the moment... Now, here come my unit tests:
#Test
public void testCategories() {
List<Category> cat = service.getAllCategories();
assertNotNull(cat);
assertFalse(cat.isEmpty());
for (Category c : cat) {
try {
assertNotNull(c.getEntity().getName());
fail("Expected lazy init. on Category.Entity");
}
catch(LazyInitializationException ex) {
//Exception is caught
}
try {
assertNotNull(c.getParent().getName());
//No exception
fail("Expected lazy init. on Category.Parent");
}
catch(LazyInitializationException ex) {}
}
}
After running the test, an exception is catched in the first try/catch block and the second fail is fired, saying that there is no LazyInitException on the parent property of the category.
So I can call
c.getParent().getParent().getName();
or
c.getParent().getParent().getParent().getName()
without any exception being raised.
I've set 'show_sql=true' in my configuration file, I can't see any supplementary request in my logs after calling the getter on the parent property, so there is no new request but the parent property is loaded.
Can somebody explain me why the parent property is not lazy?
EDIT:
I've updated my test case.
#Test(expected=LazyInitializationException.class)
public void lazyCategoriesParentsList() {
List<Category> cat = service.getAllCategories();
assertNotNull(cat);
assertFalse(cat.isEmpty());
for (Category c : cat) {
assertNotNull(c.getParent().getName());
}
}
And now the test is passing... Was it related to the access of the entity property (via c.getEntity()) in my previous test case?

Could you try getting only one Category instead of loading them all ?
Indeed, when you are loading a Category, if hibernate can find its parent in the same Session, it will initialize it, even if parent is marked as lazy. As you are querying for all categories, it will find all the parents in the Session and initialize them.

Related

How to check is there a row with getOne function

Assume that "Project" is POJO. In service layer of my project, I wrote a function that is get a row from table.
#Override
public ProjectDto getAProject(Long id) {
try {
Project project = projectRepository.getOne(id);
if (project==null) {
throw new IllegalArgumentException("Project not found");
} else {
return modelMapper.map(project, ProjectDto.class);
}
} catch (EntityNotFoundException ex) {
throw new IllegalArgumentException("Project not found");
}
}
The function is working fine with already exist id values. But if I give non-exist value, an exception occur like following. Looks like "getOne()" function don't throw "EntityNotFoundException".
ModelMapper mapping errors: Error mapping com.issuemanagement.entity.Project to com.issuemanagement.dto.ProjectDto
that means the exception come from model mapper logic. Because "project" object filled with null values so couldn't map to DTO class. I modified the function as following to fix this.
#Override
public ProjectDto getAProject(Long id) {
boolean isExist = projectRepository.existsById(id);
if (isExist) {
Project project = projectRepository.getOne(id);
return modelMapper.map(project, ProjectDto.class);
} else {
throw new IllegalArgumentException("Project not found");
}
}
but in this way the program goes to DB for two times. I don't like it. How can I do this operation with just one transaction?
BTW, if I try to run "toString()" function of "project", it throw "EntityNotFoundException" but it's looks like not official way. or it is? I hope there should be a boolean variable in somewhere.
getOne() on JpaRepository will call getReference() on EntityManager under the hood which will return an instance whose state is lazily fetch .The EntityNotFoundException will not throw immediately but will only be thrown when its state is accessed at the first time .
It is normally used in the case that when you need to configure a #ManyToOne relationship for an entity (Let say configure a Department for an Employee) but you just have the ID of the related entity.(e.g. DepartmentId) . Using getOne() allows you to get a Department proxy instance such that you do not really need to query the database to get the actual Department instance just for setting up its relationship for an Employee.
In your case , you should use findById() which will return an empty Optional if the instance does not exist:
#Override
public ProjectDto getAProject(Long id) {
Project project = projectRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("Project not found"));
return modelMapper.map(project, ProjectDto.class);
}

JPA query not returning updated results in list

I have an issue with a webapp running in tomcat where I have an abstract DAO class with a method called all() which returns all entities from the database or JPA cache. It seems to correctly return the entities on the initial call but subsequent calls don't reflect updates happening from separate UI calls which will use the entity managers find method to find the specific entity from the list, update the relative fields and commit that. When I view that list via the same all() method later I still see the original values. If I make another update in the logs I can see the value changing from the correct value(not the original value) to the updated value and the logs shows those updates happening correctly each time.
I'm using guice for injection. I've played around with the logging and can see the same hashcode on the entity manager being used throughout a request but different for each request. I've played with the following the persistance.xml file which didn't seem to help either...
<property name="eclipselink.cache.shared.default" value="false" />
<shared-cache-mode>NONE</shared-cache-mode>
I can't see why my all() won't return updated results, I've also tried adding code to find the specific entity I'm updating in the list then replaced it by calling the following...
entity = em.find(Class.class, id)
This seemed to fix the issue on that particular entity so it appears my query is reusing old.
Here's a snippet from my DAO class
private final Provider<EntityManager> emP;
protected EntityManager em(boolean useTransaction) throws DaoException {
return useTransaction ? begin() : emP.get();
}
public List<T> all() throws DaoException {
EntityManager em = em(false);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(eClass);
Root<T> from = cq.from(eClass);
return em.createQuery(cq.select(from)).getResultList();
}
public T save(T t) throws DaoException {
return save(t, em(true));
}
protected T save(T t, EntityManager em) throws DaoException {
if (Objects.isNull(t)) {
throw new DaoException("can't save null object: " + getDaoClass(), new NullPointerException());
}
T saved;
if (t.getId() > 0) {
saved = em.merge(t);
} else {
em.persist(t);
saved = t;
}
autoCommit();
return saved;
}
protected void autoCommit() throws DaoException {
if (autoCommit) {
commit();
}
}
public void commit() throws DaoException {
EntityManager em = emP.get();
if (!em.getTransaction().isActive()) {
throw new DaoException("transaction isn't active, unable to commit");
}
try {
em.getTransaction().commit();
} catch (IllegalStateException e) {
throw new DaoException("transaction not active", e);
} catch (RollbackException e) {
throw new DaoException("commit rolled back", e);
}
}
So I'm wondering if anyone has any insights on why this might be happening or have any suggestions on what else I can try to check?
So I found the cause of the issue I was having. I was using the ElementCollection annotation in my entities when referencing lists. I removed the annotation and replaced it with a JoinTable and OneToMany annotations and things are working correctly.
The issue I had was that the entity would be stored in the database fine and I was updating that entity as expected but JPA had embedded the list of entities where it was referenced.
So I was seeing the embedded list returned each time which was not the actual entity I had updated. My entities are now using proper join tables instead of embedded objects and everything is behaving as expected.

What Dropwizard-Hibernate doc is trying to say?

I have run into LazyInitializationException and then I ran into the following paragraph from the official doc. Unfortunately, it makes absolutely no sense to me. Please help.
(The code block above the paragraph in the doc.)
#GET
#Timed
#UnitOfWork
public Person findPerson(#PathParam("id") LongParam id) {
return dao.findById(id.get());
}
Important
The Hibernate session is closed before your resource method’s return
value (e.g., the Person from the database), which means your resource
method (or DAO) is responsible for initializing all lazily-loaded
collections, etc., before returning. Otherwise, you’ll get a
LazyInitializationException thrown in your template (or null values
produced by Jackson).
First The Hibernate session is closed before your resource method’s return value. How is this possible? This would have been possible had there been a try-finally block around my resource's return statement, but that is not the case here.
My resource should have been invoked by another method, which I am guessing would open the Hibernate session before my resource method is invoked and would then close the session after my resource method returns. How can it close it before my method returns. I don't get it.
The most important part - which means your resource method (or DAO) is responsible for initializing all lazily-loaded collections, etc., before returning. I have no Hibernate experience. I am using it for the first time now. How do I initialize, or rather what is exactly is meant by "initialize" in context of Hibernate? A code example will help a lot.
PS: This question might look odd, and someone at a cursory glance might even suggest to move it to "English Language and Usage", but please read it carefully. This is a technical question, not paragraph dissection.
Edit:
Added the code block from the doc else it won't make sense anyone. Also I removed one paragraph from my question, which became clear to me, immediately after posting the question.
First The Hibernate session is closed before your resource method’s
return value. How is this possible? This would have been possible had
there been a try-finally block around my resource's return statement,
but that is not the case here.
I know nothing about Dropwizard. So let's see the source (I change it a bit).
From UnitOfWorkAwareProxyFactory
class UnitOfWorkAwareProxyFactory {
public <T> T create(Class<T> clazz) {
final ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(clazz);
final Proxy proxy = (Proxy) factory.createClass().newInstance();
proxy.setHandler(new MethodHandler() {
#Override
public Object invoke(Object self, Method overridden,
Method proceed, Object[] args) {
final UnitOfWork unitOfWork = overridden.getAnnotation(UnitOfWork.class);
final UnitOfWorkAspect unitOfWorkAspect = new UnitOfWorkAspect(sessionFactories);
try {
unitOfWorkAspect.beforeStart(unitOfWork);
Object result = proceed.invoke(self, args);
unitOfWorkAspect.afterEnd();
return result;
} catch (Exception e) {
unitOfWorkAspect.onError();
throw e;
}
}
});
return (T) proxy;
}
}
if you have a class
class PersonDao {
#UnitOfWork
public Person findPerson(LongParam id) {
return dao.findById(id.get());
}
}
You can do something like this
UnitOfWorkAwareProxyFactory factory = new UnitOfWorkAwareProxyFactory();
PersonDao proxy = factory.create(PersonDao.class);
when you do
Person person = proxy.findPerson(1L);
that line becomes
unitOfWorkAspect.beforeStart(unitOfWork);
Object result = findPerson.invoke(proxy, 1L);
unitOfWorkAspect.afterEnd();
return result;
Methods unitOfWorkAspect.beforeStart(unitOfWork) and unitOfWorkAspect.afterEnd() from the source UnitOfWorkAspect
class UnitOfWorkAspect {
public void beforeStart(UnitOfWork unitOfWork) {
session = sessionFactory.openSession();
configureSession();
beginTransaction();
}
public void afterEnd() {
try {
commitTransaction();
} catch (Exception e) {
rollbackTransaction();
throw e;
} finally {
session.close();
}
}
}
The most important part - which means your resource method (or DAO) is responsible for initializing all lazily-loaded collections, etc., before returning. I have no Hibernate experience. I am using it for the first time now. How do I initialize, or rather what is exactly is meant by "initialize" in context of Hibernate?
Initialize in this context means the collection data should be loaded from a database. Some methods of an initialization
1.Use an eager loading, for an example
class User {
#ManyToMany(fetch = FetchType.EAGER)
private List<Role> roles;
}
Hibernate will load roles via joins or subselects, when you get a User entity.
Use Hibernate.initialize(user.getRoles())
Use join fetch in HQL — from User user left join fetch user.roles
Use Criteria with setFetchMode()
Use fetch profiles, entity graphs. Don't know can entity graphs be used with a session, it is a JPA feature: http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/fetching/Fetching.html
If you don't need to fetch collections, you can use a partial objects loading with transforming to the root entity: How to transform a flat result set using Hibernate

openjpa, update, error 'PK has non-default value'

I wonder if anyone has come across this error and can explain what's happening:
<openjpa-2.1.1-SNAPSHOT-r422266:1087028 nonfatal user error>
org.apache.openjpa.persistence.InvalidStateException:
Primary key field com.qbe.config.bean.QBEPropertyHistory.id of com.qbe.config.bean.QBEPropertyHistory#1c710ab has non-default value.
The instance life cycle is in PNewProvisionalState state and hence an
existing non-default value for the identity field is not permitted.
You either need to remove the #GeneratedValue annotation or modify the
code to remove the initializer processing.
I have two objects, Property and PropertyHistory. Property has OneToMany List of PropertyHistory:
#OneToMany(fetch = FetchType.LAZY, cascade=CascadeType.MERGE, orphanRemoval=false)
#JoinColumn(name="PROPERTY_NAME")
#OrderBy("updatedTime DESC")
private List<QBEPropertyHistory> history = new ArrayList<QBEPropertyHistory>();
And Property object is loaded and saved like this:
public T find(Object id) {
T t = null;
synchronized(this) {
EntityManager em = getEm();
t = em.find(type, id);
//em.close(); //If this is uncommented, fetch=LAZY doesn't work. And fetch=EAGER is too slow.
}
return t;
}
public T update(T t) {
synchronized(this) {
EntityManager em = getEm();
em.getTransaction().begin();
t = em.merge(t);
em.getTransaction().commit();
em.close();
return t;
}
}
In the service layer I load a property using find(id) method, instantiate a new PropertyHistory, add it into property prop.getHistory().add(propHist) then call update(prop) and get the above error.
The error disappears if I close EntityManager in find() but that breaks lazy loading and prop.getHistory() always returns null. If I set fetch=EAGER it becomes unacceptably slow as there are 10s of 1000s of records and I need to select thousands of property objects at a time and history is not needed 99.99% of the time.
I can't remove the #GeneratedValue as the error text suggests because it is generated (DB2, autoincrement). Now I wonder how would i "modify the code to remove the initializer processing" ?
Thanks!
The problem is that you are trying to share an Entity across persistence contexts(EntityManager). You could change your methods to take an EntityManager instance and use the same EM for the find and update operations.

Hibernate "no session or session was closed" even with Session bound in Transactional method

I am using Spring 3.0.5, Hibernate 3.6.7 and Vaadin.
I have a simple entity that is like this
#Entity
public class Foo {
#OneToMany(fetch = FetchType.LAZY)
private Collection<Bar> bars;
...
}
I have a Dialog Window that I get from the context and its supposed to show the 'bars' from 'foo'.
#Component
#Scope("prototype")
public class FooBarDialogImp extends Window implements FooBarDialog {
#Transactional(readOnly = true)
public void populate(Foo foo) {
...
for (Bar bar : foo.getBars()) {
// populate the dialog with bar information
...
}
}
}
And when user ask to show a foo bars, I do something like this
public class FooController {
...
public void showFooBars(Foo foo) {
FooBarDialog dialog = context.getBean(FooBarDialog.class);
dialog.populate(foo);
showDialog(dialog);
}
}
but the problem is that I get a "no session" exception from hibernate. I changed the code to inject the session factory and see if there was a bound session, and it was. I don't know what I am doing wrong. Anyone have a idea?
You specified your association type for the collection as LAZY, so it does not get loaded until the getter method on the owning object is invoked. The caveat to doing this is that it needs to be in the scope of a session, either the original one that created it or a new one. And the caveat to using a new session to load the lazy list is that your entity is considered detached from it, and first needs to be merged before you can call to get the lazy collection.
So just call:
session.merge(foo);
Before attempting to iterate its bars.

Categories