Hibernate @Version annotation and object references an unsaved transient instance [duplicate]
Asked Answered
L

1

0

My New Project is in Hibernate 4.2.5.Final and Spring. After Login, I am storing the user object in the session. Now after successful login, I need to insert one record in the application log. Here are the classes:

Class BaseEntity

 @MappedSuperclass
public abstract class BaseEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long ID;
    @Version
    private Long version;
    private Long createdBy;
    @Temporal(value = TemporalType.TIMESTAMP)
    private Date createdDate;
    private Long updatedBy;
    @Temporal(value = TemporalType.TIMESTAMP)
    private Date updatedDate;
    private String deactivatedReason;
    private String activatedReason;
    private Integer active;

    public long getID() {
        return ID;
    }

    public void setID(long iD) {
        ID = iD;
    }

    public Long getVersion() {
        return version;
    }

    public void setVersion(Long version) {
        this.version = version;
    }

    public Long getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(Long createdBy) {
        this.createdBy = createdBy;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public Long getUpdatedBy() {
        return updatedBy;
    }

    public void setUpdatedBy(Long updatedBy) {
        this.updatedBy = updatedBy;
    }

    public Date getUpdatedDate() {
        return updatedDate;
    }

    public void setUpdatedDate(Date updatedDate) {
        this.updatedDate = updatedDate;
    }

    public String getDeactivatedReason() {
        return deactivatedReason;
    }

    public void setDeactivatedReason(String deactivatedReason) {
        this.deactivatedReason = deactivatedReason;
    }

    public String getActivatedReason() {
        return activatedReason;
    }

    public void setActivatedReason(String activatedReason) {
        this.activatedReason = activatedReason;
    }

    public Integer getActive() {
        return active;
    }

    public void setActive(Integer active) {
        this.active = active;
    }

}

Class Users:

package com.product.domain;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.Pattern;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;

import com.product.audit.IAuditLog;
import com.product.domain.base.BaseEntity;

@Entity
@Table(name = "users")
@NamedQueries({ @NamedQuery(name = "Users.findUserByUserID", query = "SELECT usr  FROM Users  as usr WHERE usr.userName = ? and usr.practice.code = ?") })
public class Users extends BaseEntity implements IAuditLog {


    private String userName;
    private String password;

    // bi-directional many-to-one association to Practice
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "practiceID")
    private Practice practice;
    //getters and setters
}

Class ApplicationEvents:

package com.product.domain;

import java.util.Date;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "applicationevents")
public class ApplicationEvents {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long ID;

    @Temporal(value = TemporalType.TIMESTAMP)
    private Date eventDate;
    private String comments;
    private Integer eventType;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "practiceID")
    private Practice practice;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userID")
    private Users userID;

    //getters and setters

}

ApplicationEvents appEvent = new ApplicationEvents();
        appEvent.setEventType(eventType);
        appEvent.setPractice(practice);
        appEvent.setUserID(userID);
        appEvent.setComments(comments);
        appEvent.setEventDate(new Timestamp(new Date().getTime()));
        CRUDService.Save(appEvent);

When I try to save ApplicationEvent, I am getting the following error:

18:34:34.540 [http-bio-8080-exec-8] INFO  c.p.a.MyUserDetailsService - Logged User authentication sucess. UserName admin and practice Name Physicians Choice Laboratory
Hibernate: 
    /* insert com.product.domain.ApplicationEvents
        */ insert 
        into
            applicationevents
            (comments, eventDate, eventType, practiceID, userID) 
        values
            (?, ?, ?, ?, ?)
19 Sep, 2013 6:34:34 PM org.zkoss.bind.impl.ParamCall call:117
SEVERE: 
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: com.product.domain.Users; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.product.domain.Users
    at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:166)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:680)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:562)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:755)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475)

Very Important Note: When I remove the @Version in the base entity, everything works fine.

Lory answered 19/9, 2013 at 13:11 Comment(9)
Where does the Users object come from exactly? If you are pulling the User from the session, at that moment in time it is detached from the persistence context and considered transient. See Persistence Contexts. You would need to do a merge or saveOrUpdate on the Users object to re-attach it to the persistence context as @Prabhakaran suggests.Jaffa
Let me re-state that... the Users object is transient if it is being pulled from the session, not detached. Detached objects are slightly different than transient objects Working with objects.Jaffa
Yes User Object is coming from the session and as you said it is in transient state. But i am just wonder, how the problem does not occur when i remove @version in baseentity ? And also i am afraid of saving user object using saveupdate or merge, because we have audit log for each tableLory
I am really afraid of my detail tables. since my one of the detail tables contain related to more than 10 master tables. So all those as referred as foreign key to the detail table, so if this is the case, do i need to do merge for all the 10 master first and then save detail table ?Lory
The @Version annotation is meant to prevent exactly what you are worried about. If there was a concurrent modification of the Users object it will throw an exception during flush Optimistic concurrency control. As to why you don't get an exception if you remove @Version... it's probably because hibernate is not doing a version check in that case and therefore goes back to 'last-commit-wins' mode.Jaffa
Ok what is the alternate method by keeping @version and not saving the user object, but insert one record into detail table application events ?Lory
let us continue this discussion in chatLory
Please help to resolve my problemLory
It is my mistake. In the users table, the particular record version column has null value. See hereLory
R
0

First you need to save the user.

        CRUDService.Save(userID);

Then you associate user to the ApplicationEvents

        ApplicationEvents appEvent = new ApplicationEvents();
        appEvent.setEventType(eventType);
        appEvent.setPractice(practice);
        appEvent.setUserID(userID);
        appEvent.setComments(comments);
        appEvent.setEventDate(new Timestamp(new Date().getTime()));
        CRUDService.Save(appEvent);
Renaissance answered 19/9, 2013 at 13:16 Comment(4)
I dont want to save the user, because there is no change. Once again, userID is a object of users class which is obtained from the login controller.Lory
In the application event table, userid is just a foreign keyLory
Very Important Note: When I remove the @Version in the base entity, everything works fine.Lory
I tried the following parasjain.net/2008/01/14/…Lory

© 2022 - 2024 — McMap. All rights reserved.