0

Can anybody explain me why I am getting TransientObjectException when I doing merge. Issue is reproducing only when I create RuleTestEntitiy inside constructor of ActivityTestEntity as showed below. It doesn't appears if I do update or create.

Thanks in advance.

Here is a test:

@ContextConfiguration(locations = {
    "classpath:testApplicationContext_db.xml"})
public class TransientObjectExceptionTest extends  AbstractTestNGSpringContextTests{
@Autowired
SessionFactory sessionFactory;
@Test
public void testAddTestActivity(){
    Session session = sessionFactory.openSession();
    Transaction tx1 = session.beginTransaction();
    ActivityTestEntity newActivityEntity = new ActivityTestEntity();
    session.merge(newActivityEntity);
    tx1.commit();
    session.close();
    sessionFactory.close();
}

}

Exception: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.xxx.RuleTestEntity

@Entity
@Table(name = "ACTIVITY_TEST")
public class ActivityTestEntity implements Serializable{
private static final long serialVersionUID = 4190826330152288861L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ACTIVITY_ID", nullable = false)
private long id;
@OneToMany(mappedBy = "activity", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<RuleTestEntity> rules = new HashSet<>();
public ActivityTestEntity() {
    RuleTestEntity rule = new RuleTestEntity();
    rule.setActivity(this);
    this.getRules().add(rule);
}
public long getId() {
    return id;
}
public void setId(long id) {
    this.id = id;
}
public Set<RuleTestEntity> getRules() {
    return rules;
}
public void setRules(Set<RuleTestEntity> rules) {
    this.rules = rules;
}

}

@Entity
@Table(name = "RULE_TEST")
public class RuleTestEntity implements Serializable {
private static final long serialVersionUID = -4208222848601642508L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "RULE_ID", nullable = false)
@XmlElement(name = Identifiable.ID_FIELD_NAME)
private long id;
@ManyToOne
@JoinColumn(name = "ACTIVITY_ID", nullable = true, updatable = false)
@XmlTransient
private ActivityTestEntity activity;
public ActivityTestEntity getActivity() {
    return activity;
}
public void setActivity(ActivityTestEntity activity) {
    this.activity = activity;
}
public long getId() {
    return id;
}
public void setId(long id) {
    this.id = id;
}
@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
        return false;
    }
    RuleTestEntity that = (RuleTestEntity) o;
    if (id != that.id) {
        return false;
    }
    return activity != null ? activity.equals(that.activity) : that.activity == null;
}
@Override
public int hashCode() {
    int result = (int) (id ^ (id >>> 32));
    result = 31 * result + (activity != null ? activity.hashCode() : 0);
    return result;
}

}

roka
  • 1
  • 3
  • Possible duplicate of [object references an unsaved transient instance - save the transient instance before flushing](http://stackoverflow.com/questions/2302802/object-references-an-unsaved-transient-instance-save-the-transient-instance-be) – Seth Jun 08 '16 at 09:02

1 Answers1

0

this exception is generated when you try to save an Entity referenced to an unsaved entity. you can either save the referenced entity first then save the entity or you can add cascade=CascadeType.All to the relations between the two of them.

Remove the code inside your constructor and then try this code:

    Session session = sessionFactory.openSession();
    Transaction tx1 = session.beginTransaction();
    RuleTestEntity rule = new RuleTestEntity();
    session.save(rule);
    session.refresh(rule);
    ActivityTestEntity newActivityEntity = new ActivityTestEntity();
    Set<RuleTestEntity> rules = new HashSet<>();
    rules.add(rule);
    session.merge(newActivityEntity);
    tx1.commit();
    session.close();
    sessionFactory.close();
Mohamed Nabli
  • 1,629
  • 3
  • 17
  • 24
  • I am using cascade=CascadeType.Al as can see, but it doesn't help. – roka Jun 08 '16 at 09:02
  • you r not using it in `RuleTestEntity` – Mohamed Nabli Jun 08 '16 at 09:09
  • can you please advise where exactly should I use it in RuleTestEntity ? – roka Jun 08 '16 at 09:18
  • `@ManyToOne(cascade=CascadeType.ALL) @JoinColumn(name = "ACTIVITY_ID", nullable = true, updatable = false) @XmlTransient private ActivityTestEntity activity;` – Mohamed Nabli Jun 08 '16 at 09:21
  • Added cascade type to RuleTestEntity but it doesn't help. Still the same issue. – roka Jun 08 '16 at 09:28
  • I have Updated My Ansewer. – Mohamed Nabli Jun 08 '16 at 09:42
  • Yes, I know that if I remove the code from constructor it will works. The question is why I am getting exception having dat logic in constructor ? What is the special case here ? cuz with update and create it works fine. – roka Jun 08 '16 at 10:51
  • your getting this error because hibernate tries to save the rule Object which is referenced to an object not saved yet.try this in your constructor `RuleTestEntity rule = new RuleTestEntity(); Set rules = new HashSet<>(); rules.add(rule); this.setRules(rules);` – Mohamed Nabli Jun 08 '16 at 11:55
  • tried. Still the same. Why do we have this issue only with merge ? it is quite weird as with update it works fine. – roka Jun 08 '16 at 12:54
  • update is an operation applied on existing records, while merge is applied on existing and not existing, if the record already exists in you database it wont generate this error ... – Mohamed Nabli Jun 08 '16 at 13:15
  • Tested case when at first I create ActivityTestEntity with one RuleTestEntity, then adding another RuleTestEntitiy and it fails with the same exception. – roka Jun 08 '16 at 14:48