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
There are several things to fix in your mapping.
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).@JoinColumn
annotation should be defined on the owning side only, not both sides.@OrderColumn
annotation of the Hibernate specific @IndexColumn
@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);
}
...
}
@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
.
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