tags:

views:

120

answers:

2

Hi folks,

I try to map a relation for the first time. I have found a lot of ways to go.

There are two classes: - Project (Parent) - Application (Child)

So one project can have several applications, but one application belongs to just one project..

I constructed them as follows:

Project.java

 import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.OneToMany;

    @Entity
    public class Project {

     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private int id;
        @OneToMany(cascade = CascadeType.ALL)
        @JoinColumn(name = "project_id", nullable=false)
        @org.hibernate.annotations.IndexColumn(name = "project_index")
        List<Application> applications = new ArrayList<Application>();

        [get and set methods...]
        }

Application.java

import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;

    @Entity
    public class Application {

         @Id
         @GeneratedValue(strategy = GenerationType.IDENTITY)
         private int id;

            @ManyToOne
            @JoinColumn(name = "project_id", updatable = false, insertable = false, nullable=false)
            private Project project;

        [get and set methods...]
     }

...but, unfortunately I got an exception I can not deal with:

javax.servlet.ServletException: org.hibernate.PropertyValueException: not-null property references a null or transient value: ....common.entities.Application._applicationsBackref
 javax.faces.webapp.FacesServlet.service(FacesServlet.java:321)
 org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:359)
 org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:275)
 org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
 org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
 org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:343)
 org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:272)
 org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:83)

root cause

javax.faces.el.EvaluationException: org.hibernate.PropertyValueException: not-null property references a null or transient value: ....common.entities.Application._applicationsBackref
 javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:98)
 com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:98)
 javax.faces.component.UICommand.broadcast(UICommand.java:311)
 javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:781)
 javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1246)
 com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:77)
 com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
 com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114)
 javax.faces.webapp.FacesServlet.service(FacesServlet.java:308)
 org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:359)
 org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:275)
 org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
 org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
 org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:343)
 org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:272)
 org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:83)

root cause

org.hibernate.PropertyValueException: not-null property references a null or transient value: ....common.entities.Application._applicationsBackref
 org.hibernate.engine.Nullability.checkNullability(Nullability.java:95)
 org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:313)
 org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
 org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
 org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
 org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
 org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
 org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
 org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)

Where is my mistake? I mean there is nothing null. Only on project side


When I remove @JoinColumn on owning side an exception occures:

java.sql.SQLException: Field 'project_id' doesn't have a default value

Thank you Pascal

+2  A: 

@JoinColumn annotation is not needed on both sides. In fact, you should only put this on owning side of the relationship.

Moreover, you have describe the thing as below,

@JoinColumn(name = "project_id", updatable = false, insertable = false, nullable=false)

Its totally making no sense. This, acutally, means that you don't want to update it, neither you like to insert it, nor you want it null.

Furthermore, you are missing @Column annotation upon your Id fields, in case the name of the actual column is not id.

Adeel Ansari
when you say owning side, do you mean parent side? that's not clear to me
Sven
@Sven: Owning-side is the one which owns the foreign key. So, the entity for the table having foreign-key should be the owning-side. In your case it would be `Application`.
Adeel Ansari
Thank's. I updated my post with a new exception -.-
Sven
@Sven: Did I suggest you to remove it from the owning side? I think I said to keep it only there. :)
Adeel Ansari
You made some good points, +1 too.
Pascal Thivent
@Pascal: Thanks.
Adeel Ansari
+3  A: 

There are several things to fix in your mapping.

  • Your association is bidirectional, you need to use the mappedBy attribute on the "One" side (or what you'll get is two unidirectional associations) to define the field that "owns" the relation on the owning side (the side that holds the foreign key i.e. the Many side in a one-to-many).
  • The @JoinColumn annotation should be defined on the owning side only, not both sides.
  • If you are using a JPA 2.0 compliant version of Hibernate (i.e. Hibernate 3.5+), prefer the standard @OrderColumn annotation of the Hibernate specific @IndexColumn
  • I'm not sure to understand why you defined the @JoinColumn as totally read-only with updatable = false, insertable = false, nullable=false, this doesn't sound appropriate.

So your mappings become (assuming you're using JPA 2.0):

@Entity
public class Project {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToMany(cascade = CascadeType.ALL, mappedBy="project")
    @OrderColumn(name = "project_index")
    List<Application> applications = new ArrayList<Application>();

    // getters, setters
}

And

@Entity
public class Application {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne
    @JoinColumn(name = "project_id")
    private Project project;

    // getters, setters
 }

And since your association, is bidirectional, don't forget to set both sides of the link when creating your object graph:

Project p = new Project();
Application a = new Application();
a.setProject(p);
p.getApplications().add(a);
em.persist(p);

But I would actually recommend to create link management methods in your entities to set both sides of the link, e.g.

@Entity
public class Project {
    ...
    public void addToApplications(Application app) {
        this.applications.add(app);
        app.setProject(this);
    }
    ...
}

Resources

References

  • JPA 1.0 specification
    • Section 2.1.8.2 "Bidirectional ManyToOne / OneToMany Relationships"
    • Section 9.1.24 "OneToMany Annotation"
    • Section 9.1.22 "ManyToOne Annotation"
  • JPA 2.0 specification
    • Section 11.1.39 "OrderColumn Annotation"
Pascal Thivent
+1| Perfect. Actually, I was waiting for your post.
Adeel Ansari
Thank you Pascal, you are kind of genius! Like BalusC as well. If I could I would shout a beer you two !
Sven
@Adeel Thank you :) @Sven You're welcome
Pascal Thivent
One thing: List<Application> applications = new ArrayList<Application>();....that list should not be initialized or else the list gonna be empty after query... I had hibernate to initialize it!!!
Sven
@Sven Hmm... No, this is not true. There must be something else.
Pascal Thivent