Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate onetomany mapping

Tags:

java

hibernate

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

like image 375
Sven Avatar asked Sep 07 '10 08:09

Sven


2 Answers

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

  • Hibernate Core Documentation
    • 1.2.6. Working bi-directional links
  • JPA wiki book
    • Example of a OneToMany relationship and inverse ManyToOne annotations

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"
like image 147
Pascal Thivent Avatar answered Sep 30 '22 15:09

Pascal Thivent


@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.

like image 42
Adeel Ansari Avatar answered Sep 30 '22 15:09

Adeel Ansari