Kramer786
Kramer786

Reputation: 1298

JPA - optional = true still giving PropertyValueException

Using Hibernate 5 as ORM.

I use to think that optional=true meant that the column could hold null values.

I have two entities -:

An Elective may or may not be associated with an ElectivePractical but an ElectivePractical must always be associated with an Elective.

Elective.java

public class Elective extends Theory {

    private static final long serialVersionUID = 1L;

    @Column (name = "ELEC_NO", 
            table = "ELECTIVES", 
            length = 3)
    private String electiveNo;

    @OneToOne (targetEntity = ElectivePractical.class,
            mappedBy = "elective",
            fetch = FetchType.EAGER,
            cascade = CascadeType.ALL,
            optional = true)
    private ElectivePractical practical;

    @ManyToMany(targetEntity = Student.class, 
            fetch = FetchType.EAGER,
            mappedBy = "electives")
    private Collection<Student> students = new ArrayList<>();

ElectivePractical.java

public class ElectivePractical extends Practical {

    private static final long serialVersionUID = 1L;

    @OneToOne (targetEntity = Elective.class,
            fetch = FetchType.EAGER,
            cascade = CascadeType.MERGE)
    @JoinTable (name = "ELECTIVE_PRACTICAL",
            joinColumns = @JoinColumn (name = "ELEC_PRAC_ID",
            referencedColumnName = "ID", nullable = false), 
            inverseJoinColumns = 
            @JoinColumn (name = "ELEC_THEORY_ID",
            referencedColumnName = "ID", nullable = false))
    private Elective elective;

As you can see that ElectivePractical in Elective.java is set to optional=true

Yet when I try to merge() an Elective instance that has no practical I get the following error -:

javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value : com.sts.entity.subjects.Elective.practical
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1171)
    at com.sts.persistence.STSDao.addCourse(STSDao.java:250)
    at com.sts.validators.CourseValidator.commit(CourseValidator.java:558)
    at test.main.CourseTest.main(CourseTest.java:27)
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value : com.sts.entity.subjects.Elective.practical
    at org.hibernate.engine.internal.Nullability.checkNullability(Nullability.java:92)
    at org.hibernate.action.internal.AbstractEntityInsertAction.nullifyTransientReferencesIfNotAlready(AbstractEntityInsertAction.java:115)
    at org.hibernate.action.internal.AbstractEntityInsertAction.makeEntityManaged(AbstractEntityInsertAction.java:124)
    at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:185)
    at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:163)
    at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:150)
    at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:325)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:272)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:178)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
    at org.hibernate.jpa.event.internal.core.JpaMergeEventListener.saveWithGeneratedId(JpaMergeEventListener.java:56)
    at org.hibernate.event.internal.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:255)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:235)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:173)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:839)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:821)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:826)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1161)
    ... 3 more

What am I doing wrong ? Is this a bug in hibernate ?

Upvotes: 0

Views: 2352

Answers (1)

Tobb
Tobb

Reputation: 12225

The optional=true element is only used for generation of the DDL, as well as validation of the schema. If you have created the database manually, the actual database column might well be NOT NULL even if your Hibernate-mapping says the opposite.

By the way, your mappings can be slimmed down, for instance I don't think you need targetEntity with newer versions of Hibernate.

Update:

I see you are using mappedBy on the same side as the optional=true-element. The mappedBy-element tells Hibernate that the other side of the relationship contains the actual mapping. It might be that this includes the optional=true-element, so that it is ignored. Try adding it to the other side of the relationship.

Upvotes: 2

Related Questions