-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: Optional ManyToOne
PostPosted: Wed Dec 17, 2008 7:01 am 
Newbie

Joined: Wed Dec 17, 2008 6:38 am
Posts: 4
Hi everyone,

I have a problem with ManyToOne relations with optional parameter set to true. I have a Project entity that contains a ManyToOne optional relationship to a ProjectEVA entity. Also I have another entity called ProjectBaseline that has a many to one relationship with Project.
Now, I have the following query:
SELECT t FROM ProjectBaseline AS t INNER JOIN FETCH t.project k2 LEFT JOIN FETCH t.project.projectEVA k5 [... other left join fetch here] WHERE t.project.companyId = ?1 AND t.isLast = ?2 ORDER BY t.name ASC
in my dao.
The problem is that when i do baseline.getProject().getProjectEVA() for a project that does not have a projectEVA I get the proxy for the project eva object and when I want to use this object in the jsp page a lazy initialization exception appears. I tried in my code to check for the projects that do not have a projecteva by testing if baseline.getProject().getProjectEVA() != null, but since the above call returns the projecteva proxy this is always true. So the enxt thing I tried was to try to initialize the proxy using Hibernate.initialize(baseline.getProject().getProjectEVA()), but now I get an EntityNotFoundException saying that it cannot find the entity.
I read http://www.hibernate.org/315.html and I was expecting that since I was using the LEFT JOIN FETCH for the projecteva entity in the query, the projectEVA field would be set to null, if no entity was found for the query.
Thanks for your help.
The code looks like this:

Code:
//this code generates the above query
PaginatedListWrapper<T> wrappedBaselines = projectDAO
                .findAllBaselines(wrapper, company, engineeringArea, projectState, costCenter, includeCloseProjects,
                        search,
                        workerLogin, pmoArea, loads);

        List<ProjectBaseline> baselines = (List<ProjectBaseline>) wrappedBaselines.getList();
       
        for (ProjectBaseline baseline : baselines) {
           ProjectEVA projectEVA = null;
           Hibernate.initialize(baseline.getProject().getProjectEVA());
               //initially i expected here null for projects without eva, but got the proxy
           if(baseline.getProject().getProjectEVA() != null){
              projectEVA = baseline.getProject().getProjectEVA();
           } else {
              /*
               * If there is no projectEVA defined for the baseline(this is possible because the the relation between projects and projectEVA is optional)
               * then use a projectEVA that has only 0 or null values.
               */
              log.info(MessageFormat.format("There is no projectEVA defined for baseline {0}. Creating an empty WISEProjectEVA for the baseline's project eva decorator.", baseline.getId()));
              projectEVA = new ProjectEVA();
           }
            baseline.setProjectEVADecorator(new WISEProjectEVA(projectEVA, null));
        }


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 17, 2008 11:19 am 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
From your description it seems like the problem lies with what you have in the database. You have not posted any mappings but I assume that in the table that stores project you have a column which contains the ID for a ProjectEVA object. The proxy feature can only differentiate between null/not-null values. Eg. if the value is null Hibernate will not create a proxy. If there is a real value Hibernate will create a proxy. Hibernate doesn't check that the reference actually exists. It would kind of make the proxy feature useless, since the point with a proxy is to delay loading until the information is requested.

In other words, I think your table/column Project.ProjectEVA has a value that doesn't exists in the ProjectEVA table. That is why you get an EntityNotFoundException when trying to initialize the proxy. In order to use the proxy feature of Hibernate you should make sure that all rows that references non-existing rows in other tables has a null value.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 17, 2008 11:42 am 
Newbie

Joined: Wed Dec 17, 2008 6:38 am
Posts: 4
Nordborg, thanks for your reply. What I have in the database is a project entry in the Project table, with no projecteva in the projecteva table that would reference the project from the Project table. The projecteva table contains a foreign key referring to the project table. The mapping is as below:
Code:
@Entity
@org.hibernate.annotations.Entity(dynamicInsert = true, dynamicUpdate = true)
public class Project extends AuditableEntity implements Serializable {
//....
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "ID", unique = true, nullable = false, updatable = false, insertable = false)
    private ProjectEVA projectEVA;
//...
}

@Entity
@Table(name = "PROJECTEVA")
public class ProjectEVA {
//...
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "ID", unique = true, insertable = false, updatable = false)
    private Project project;
//...
}

I expected that since I have a query containing a LEFT JOIN FETCH to the ProjectEVA, if there is no projecteva for the project then the projecteva field of the project object would be set to null and no proxy would be created. Isn't this how things should work? When I have a projecteva for the project this seems to be the case, but when there is no projecteva for the project then the situation described in my previous post appears.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 17, 2008 2:35 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
It is a bit strange that you are getting a proxy when you do a JOIN FETCH, but there may be other reasons for that (the proxy may for example already be in the session).

The EntityNotFoundException that you get when you try to initialize the proxy indicates that you have an "invalid" value in the Project.ProjectEVA column. Valid values are any values from the ProjectEVA.ID column or null, but you have declared Project.projectEVA with nullable="false" so you have told Hibernate that this property can't be null. I guess this can make Hibernate a bit confused and maybe that is why you are getting a proxy even when you are doing a FETCH JOIN. I would try:

1. Change the declaration to nullable="true"
2. Make sure that you have null in all rows in Project.projectEVA that doesn't have a ProjectEVA.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 18, 2008 5:15 am 
Newbie

Joined: Wed Dec 17, 2008 6:38 am
Posts: 4
I have tried changing the Project.ProjectEVA to nullable = true, but it didn't work. This made sense to me, since the nullable=true for projectEVA means that the id column in the ProjectEVA table can be null, which is not true, because this is the primary key of the ProjectEVA table.
I could not try the second advice you gave me because there is no column in the Project table referring to the ProjectEVA table. I only have the id column in the ProjectEVA table referring the id column in the Project table. So what I have is basically a OneToOne relationship between the two tables, but done using the ManyToOne because we wanted to load the entities lazy( the mappings were done before I joined the project).
Now, I thought I can bypass the problem by doing a find of the projectEVA, but now if there is no ProjectEVA with the given id then an EntityNotFound exception is thrown. The code is below:
Code:
ProjectEVA projectEVA = projectDAO.find(ProjectEVA.class, baseline.getProject().getId());
           if(projectEVA == null){
              /*
               * If there is no projectEVA defined for the baseline(this is possible because the the relation between projects and projectEVA is optional)
               * then use a projectEVA that has only 0 or null values.
               */
              log.info(MessageFormat.format("There is no projectEVA defined for baseline {0}. Creating an empty WISEProjectEVA for the baseline's project eva decorator.", baseline.getId()));
              projectEVA = new ProjectEVA();
           }

The exception stack trace is:
Code:
Caused by: javax.persistence.EntityNotFoundException: Unable to find com.criticalsoftware.wiccore.wise.entities.projects.ProjectEVA with id 351
   at org.hibernate.ejb.Ejb3Configuration$Ejb3EntityNotFoundDelegate.handleEntityNotFound(Ejb3Configuration.java:109)
   at org.hibernate.event.def.DefaultLoadEventListener.returnNarrowedProxy(DefaultLoadEventListener.java:223)
   at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:187)
   at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:103)
   at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
   at org.hibernate.impl.SessionImpl.get(SessionImpl.java:815)
   at org.hibernate.impl.SessionImpl.get(SessionImpl.java:808)
   at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:175)
   at org.jboss.ejb3.entity.TransactionScopedEntityManager.find(TransactionScopedEntityManager.java:171)
   at com.criticalsoftware.wiccore.core.persistence.GenericSupport.find(GenericSupport.java:54)

Shouldn't the find method of the EntityManager return null in this case? The documentation for the find method says that the method returns "the found entity instance or null if the entity does not exist ".


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 18, 2008 5:53 am 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Yes, I can se that you are doing a one-to-one on the primary key now. Since you only talked about a many-to-one relation I assumed that you were talking about a regular many-to-on relation. Since your relation builds on the primary key you should use a @OneToOne for the mapping. There are examples at http://www.hibernate.org/hib_docs/annot ... ml#d0e1039. The @ManyToOne variant should only be used when you have a specific foreign key column.

I also think that this approach will disable the proxy feature for the relation from Project -> Project EVA since Hibernate has to check if there is a ProjectEVA for a given Project and while the check is made it can just as well load the ProjectEVA also.


Top
 Profile  
 
 Post subject: Re: Optional ManyToOne
PostPosted: Fri Jan 29, 2010 7:39 am 
Beginner
Beginner

Joined: Fri Jan 23, 2009 10:34 am
Posts: 25
Location: Switzerland
See also http://community.jboss.org/message/522967


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.