masterdany88
masterdany88

Reputation: 5331

jpa/hibernate @ManyToOne putting null error: org.hibernate.TransientPropertyValueException:

I have form where I have decision select field like OrganizationalCell: When I choose one option f.e: OrganizationalCellUnit, I have to atach another input with entity OrganizationalCellUnit, which is mandatory only in that configuration.

When I choose other value, this one isnt (OrganizationalCellUnit) mandatory anymore.

I have my entity that I am trying to persist:

@ManyToOne(cascade = CascadeType.MERGE)
public OrganizationalCell organizationalCell;

@ManyToOne(cascade = CascadeType.MERGE)
@Nullable
public OutPatientClinic outPatientClinic;

@ManyToOne(cascade = CascadeType.MERGE)
@Nullable
public Workshop workshop;

@ManyToOne(cascade = CascadeType.MERGE)
public OrganizationalCellUnit organizationalCellUnit = null;

/** The clinic. */
@ManyToOne(cascade = CascadeType.MERGE)
public models.Clinic clinic;

/** The unit. */
@ManyToOne(cascade = CascadeType.MERGE)
public models.Unit unit;

And I am getting an error:

Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing: models.MedicalIncident.organizationalCellUnit -> models.OrganizationalCellUnit

In db these fields allow nullabe value.

How to make this field not mandatory, and make hibernate put null value in db

---Update---- After updating CascadeType to all on that Many to one relation I have this error:

play.api.Application$$anon$1: Execution exception[[ConstraintViolationException: Validation failed for classes [models.OrganizationalCellUnit] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
        ConstraintViolationImpl{interpolatedMessage='error.required', propertyPath=updateUser, rootBeanClass=class models.OrganizationalCellUnit, messageTemplate='error.required'}
        ConstraintViolationImpl{interpolatedMessage='error.required', propertyPath=name, rootBeanClass=class models.OrganizationalCellUnit, messageTemplate='error.required'}
        ConstraintViolationImpl{interpolatedMessage='error.required', propertyPath=creationUser, rootBeanClass=class models.OrganizationalCellUnit, messageTemplate='error.required'}
]]]
        at play.api.Application$class.handleError(Application.scala:293) ~[play_2.10-2.2.4.jar:2.2.4]
        at play.api.DefaultApplication.handleError(Application.scala:399) [play_2.10-2.2.4.jar:2.2.4]
        at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$3.apply(PlayDefaultUpstreamHandler.scala:264) [play_2.10-2.2.4.jar:2.2.4]
        at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$3.apply(PlayDefaultUpstreamHandler.scala:264) [play_2.10-2.2.4.jar:2.2.4]
        at scala.Option.map(Option.scala:145) [scala-library.jar:na]
        at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3.applyOrElse(PlayDefaultUpstreamHandler.scala:264) [play_2.10-2.2.4.jar:2.2.4]
Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [models.OrganizationalCellUnit] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
        ConstraintViolationImpl{interpolatedMessage='error.required', propertyPath=updateUser, rootBeanClass=class models.OrganizationalCellUnit, messageTemplate='error.required'}
        ConstraintViolationImpl{interpolatedMessage='error.required', propertyPath=name, rootBeanClass=class models.OrganizationalCellUnit, messageTemplate='error.required'}
        ConstraintViolationImpl{interpolatedMessage='error.required', propertyPath=creationUser, rootBeanClass=class models.OrganizationalCellUnit, messageTemplate='error.required'}
]
        at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:159) ~[hibernate-core-4.2.0.CR1.jar:4.2.0.CR1]
        at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:94) ~[hibernate-core-4.2.0.CR1.jar:4.2.0.CR1]
        at org.hibernate.action.internal.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:178) ~[hibernate-core-4.2.0.CR1.jar:4.2.0.CR1]
        at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:75) ~[hibernate-core-4.2.0.CR1.jar:4.2.0.CR1]
        at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362) ~[hibernate-core-4.2.0.CR1.jar:4.2.0.CR1]
        at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:203) ~[hibernate-core-4.2.0.CR1.jar:4.2.0.CR1]

--- Edit Added

public class OrganizationalCellUnit extends GenericDictionary<OrganizationalCellUnit> {}

@MappedSuperclass
public abstract class GenericDictionary<T extends Generic<T>> extends Generic<T> {

    @Required
    public String name;
    @Required
    public boolean active = true;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean getActive() {
        return active;
    }

    public void setActive(boolean stat) {
        this.active = stat;
    }
    /**
     * Options.
     *
     * @return the map
     */
    public Map<String, String> getOptions() {
        return getOptions(getList());
    }
    public Map<String, String> getOptions(List<T> list) {
        LinkedHashMap<String, String> options = new LinkedHashMap<String, String>();
        for (T e : list) {
            GenericDictionary<?> entity = (GenericDictionary<?>) e;
            if(entity.active==true) {
                options.put(e.id.toString(), entity.name);
            }
        }
        return options;
    }

    public void on() {
        this.active = true;
        update();       
    }

    public void off() {
        this.active = false;
        update();       
    }
    public abstract String getFieldDescription(String name);
    @Override
    public boolean equals(Object other) {
        if (other == null) return false;
        if (other == this) return true;
        if (!(other instanceof GenericDictionary))return false;
        GenericDictionary<?> otherMyClass = (GenericDictionary<?>) other;
        return name.equals(otherMyClass.name);
    }

@MappedSuperclass
public abstract class Generic<T extends Generic> {
    @Transient
    public Class<T> entityClass;
    Generic() {
        entityClass = ((Class) ((Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]));
    }
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    public Long id;

    @ManyToOne(cascade = CascadeType.MERGE)
    @Constraints.Required
    public User creationUser;
    @ManyToOne(cascade = CascadeType.MERGE)
    @Constraints.Required
    public User updateUser;
    @Constraints.Required
    public String creationDate = Index.getDate(null);
    @Constraints.Required
    public String updateDate = Index.getDate(null);

    public User getCreationUser() {return creationUser;}
    public void setCreationUser(User user) {this.creationUser = user;}
    public void setCreationUser() {this.creationUser = User.getCurrentUser();}
    public User getUpdateUser() {return updateUser;}
    public void setUpdateUser(User user) {this.updateUser = user;}
    public void setUpdateUser() {this.updateUser = User.getCurrentUser();}
    public String getCreationDate() {return creationDate;}
    public void setCreationDate(String creationDate) {this.creationDate = creationDate;}
    public String getUpdateDate() {return updateDate;}
    public void setUpdateDate(String updateDate) {this.updateDate = updateDate;}
    public T getBy(Long id) {
        return JPA.em().find(entityClass, id);
    }
    public List<T> getList() {
        return (List<T>) JPA.em().createQuery("FROM " + entityClass.getSimpleName()).getResultList();
    }

    public List<T> getByUser_id(Long id) {
        List<T> entities = new ArrayList<T>();
        TypedQuery<T> query = JPA.em().createQuery("SELECT r FROM " + entityClass.getSimpleName() + " r WHERE r.user.id != :user_id", entityClass).setParameter("user_id", id);
        try {
            entities = query.getResultList();
        } catch (NoResultException e) {
            entities = null;
        }
        return entities;
    }

    public List<T> getByParameterAndValue(String parameter, String value) {
        List<T> entities = new ArrayList<T>();
        String sqlString = "SELECT e FROM " + entityClass.getSimpleName() + " e WHERE e."+ parameter +" != :value";
        TypedQuery<T> query = JPA.em().createQuery(sqlString, entityClass).setParameter("value", value);
        try {
            entities = query.getResultList();
        } catch (NoResultException e1) {
            entities = null;
        } catch (Exception e) {
            Index.toLog("error","Unsupported error in Generic model class in " + entityClass);
        }
        return entities;
    }



    @PrePersist
    public void prePersist() {
        Logger.warn(this.toString());
        setCreationDate(Index.getDate(null));
        preUpdate();
    }

    @PreUpdate
    public void preUpdate() {
        Logger.debug(this.toString());
        setUpdateDate(Index.getDate(null));
    }
    public void toDataBase() {
        System.out.println("------------------Trying to persist------------------------");
        JPA.em().persist(this);
    }
    public void update() {
        JPA.em().merge(this);
    }
    public void delete() {
        JPA.em().remove(this);
    }

    /**
     *  A Generic toString method that can be used in any class.
     *  uses reflection to dynamically print java class field
     *  values one line at a time.
     *  requires the Apache Commons ToStringBuilder class. 
     */
    public String toString() {
      return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
    }
//  @Override
//  public int hashCode() {
//      return id.hashCode();
//  }


}

Upvotes: 1

Views: 1553

Answers (2)

Patrik Beck
Patrik Beck

Reputation: 2505

I had the same problem, and it turned out (at least in my case) that this was Play's fault - I can see you using Play as well.

ModelObject object = form.get()

Even if organizationalCell field is not passed from the form, Play actually creates a dummy model object (of type OrganizationalCellUnit in your case). So object.organizationalCell is not null. JPA then rightfully shouts if you try to call

object.save()

What makes this incredibly difficult to spot is, that

object.organizationalCell.toString()

will actually write out "null".

The solution:

ModelObject object = form.get();
if(object.organizationalCell.id == null) object.organizationalCell = null;

object.save();

Upvotes: 1

Pracede
Pracede

Reputation: 4361

Instead of

@ManyToOne(cascade = CascadeType.MERGE)
public OrganizationalCellUnit organizationalCellUnit = null;

Use this

@ManyToOne(cascade = CascadeType.ALL)
public OrganizationalCellUnit organizationalCellUnit;

Upvotes: 0

Related Questions