1

I tried to save one of my entity Site containing a User, this User is registered in database and I don't want to save it too.
The problem is when I try to save the site, I get the following error :

org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: com.project.netmg.bo.impl.User; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.project.netmg.bo.impl.User 

I think, it try to save the user too but it's not that I want. I just want to search the good user and assign it to the site.

java code :

public String saveSite(@Valid @ModelAttribute SiteForm siteForm, BindingResult bindingResult, Model uiModel) {

    populateSiteforSave(siteForm);
    _siteService.saveSite(siteForm.getSite());
    return WebConstants.REDIRECT_TO_VIEW_SITE_URL + siteForm.getSite().getSiteName();
}

private void populateSiteforSave(SiteForm siteForm) {
    siteForm.getSite().setCountry((Country) _countryService.getCountryByName(siteForm.getSite().getCountry().getName()));
    siteForm.getSite().setBusiness((Business) _businessService.getBusinessById(siteForm.getSite().getBusiness().getId()));
    siteForm.getSite().setStatus((Status) _statusService.getStatusById(siteForm.getSite().getStatus().getId()));
    if (!siteForm.getLocalItFullName().isEmpty()) {
        siteForm.getSite().setLocalIt(_userService.findUserByUserFullName(siteForm.getLocalItFullName())); // user
    } else {
        siteForm.getSite().setLocalIt(null);
    }
    if (!siteForm.getRifFullName().isEmpty()) {
        siteForm.getSite().setRif(_userService.findUserByUserFullName(siteForm.getRifFullName())); //user
    } else {
        siteForm.getSite().setRif(null);
    }
    if (siteForm.getSite().getLocalContact().getId() != null) {
        siteForm.getSite().setLocalContact((User) _userService.findUserByUsername(siteForm.getSite().getLocalContact().getUsername())); //user
    }
}

Site class:

@Entity
@Table(name = "SITE", uniqueConstraints = { @UniqueConstraint(columnNames = { "SITE_COUNTRY_ID", "SITE_NAME" }) })
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED, withModifiedFlag = true)
public class Site implements ISite {

/** The Constant serialVersionUID. */
private static final long serialVersionUID = -390717603276436784L;

/** The id. */
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "SITE_ID", unique = true, nullable = false)
private long id;

/** The site address. */
@Column(name = "SITE_ADDRESS", length = BusinessConstants.SITE_ADDRESS)
private String address;

/** The site analog phone number. */
@Column(name = "SITE_ANALOG_PHONE_NUMBER", length = BusinessConstants.SITE_ANALOG_PHONE_NUMBER)
private String analogPhoneNumber;

/** The site comment. */
@Column(name = "SITE_COMMENT", length = BusinessConstants.SITE_COMMENT)
private String comment;

/** The site entity code. */
@Digits(integer = 3, fraction = 0, message = "Please enter max 3 digits")
@Column(name = "SITE_ENTITY_CODE", nullable = false)
private long entityCode;

/** The site invoice code. */
@Digits(integer = 10, fraction = 0, message = "Please enter max 10 digits")
@Column(name = "SITE_INVOICE_CODE", nullable = false)
private long invoiceCode;

/** The site local it phone. */
@Column(name = "SITE_LOCAL_IT_PHONE", length = BusinessConstants.SITE_LOCAL_IT_PHONE)
private String localItPhone;

/** The site name. */
@NotBlank
@Column(name = "SITE_NAME", nullable = false, length = BusinessConstants.SITE_NAME)
private String siteName;

/** The site subnet. */
@NotBlank
@Column(name = "SITE_SUBNET", nullable = false, length = BusinessConstants.SITE_SUBNET)
private String subnet;

/** The site user number. */
@Digits(integer = 4, fraction = 0, message = "Please enter max 4 digits")
@Column(name = "SITE_USER_NUMBER")
private Long userNumber;

/** The business. */
@Valid
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SITE_BUSINESS_ID", nullable = false)
private Business business;

/** The country. */
@Valid
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "SITE_COUNTRY_ID")
private Country country;

/** The local contact. */
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SITE_LOCAL_CONTACT", nullable = true)
private User localContact;

/** The local it. */
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SITE_LOCAL_IT", nullable = true)
private User localIt;

/** The rif. */
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SITE_RIF", nullable = true)
private User rif;

/** The status. */
@Valid
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SITE_STATUS_ID", nullable = false)
private Status status;

/** The end date. */
@Column(name = "SITE_END_DATE")
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(iso = ISO.DATE_TIME)
private Date endDate = null;

@ManyToMany(targetEntity = User.class, mappedBy = "userSites", fetch = FetchType.LAZY)
@NotAudited
private Set<IUser> siteUsers;

User class :

@Entity
@Table(name = "USERS")
public class User implements IUser {

/** The Constant serialVersionUID. */
private static final long serialVersionUID = 6741623705511494367L;
private static final String USER_ID = "USER_ID";

/** The user id. */
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = USER_ID)
private Long id;

/** The user first name. */
@Column(name = "FIRSTNAME", length = BusinessConstants.USER_FIRSTNAME, nullable = false)
@NotNull
private String userFirstName;

/** The user last name. */
@Column(name = "LASTNAME", length = BusinessConstants.USER_LASTNAME, nullable = false)
@NotNull
private String userLastName;

/** The user email. */
@Column(name = "EMAIL", length = BusinessConstants.USER_EMAIL)
@NotNull
private String userEmail;

/** The user uid. */
@Column(name = "LOGIN", length = BusinessConstants.USER_LOGIN, nullable = false, unique = true)
@NotNull
private String username;

@Valid
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PROFILE_ID", referencedColumnName = "PROF_ID", nullable = false)
private Profile profile;

@BatchSize(size = 20)
@ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Site.class)
@JoinTable(name = "USER_SITE",
joinColumns = { @JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { @JoinColumn(name = "SITE_ID", nullable = false) })
private Set<ISite> userSites;

@ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Region.class)
@JoinTable(name = "USER_REGION",
joinColumns = { @JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { @JoinColumn(name = "REGION_ID", nullable = false) })
private Set<IRegion> userRegions;

@BatchSize(size = 20)
@ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Zone.class )
@JoinTable(name = "USER_ZONE",
joinColumns = { @JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { @JoinColumn(name = "ZONE_ID", nullable = false) })
private Set<IZone> userZones;

@BatchSize(size = 20)
@ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Country.class )
@JoinTable(name = "USER_COUNTRY",
joinColumns = { @JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { @JoinColumn(name = "COUNTRY_ID", nullable = false) })
private Set<ICountry> userCountries;

@Transient
private Collection<? extends GrantedAuthority> authorities;

@Transient
private String userFullName;
Anders R. Bystrup
  • 15,729
  • 10
  • 59
  • 55
Jerome Campeaux
  • 353
  • 2
  • 9
  • 19
  • @SteveChaloner I tried it but this is for save the child object but I don't want to save the children, I just want to associate it. and when I make cascade.all I get an error saying that my object User contain some missing fields – Jerome Campeaux May 11 '15 at 11:06
  • See this answer to the above post http://stackoverflow.com/a/20286061/1063929 – Steve Chaloner May 11 '15 at 11:07

2 Answers2

1

You can't really have it both ways. If the object graph includes the User, then it will (have to) be persisted if changed in your code. Have you considered what it evens means to fetch a Site (including a User), then change the localContact and persist the Site again?

If you want the localContact to be settable in the object graph, but not persisted, then it can be annotated with @Transient:

/** The local contact. */
@Transient
private User localContact;

Hope that helps.

Cheers,

Anders R. Bystrup
  • 15,729
  • 10
  • 59
  • 55
  • thank you for help, it's working but I don't really understand why the localContact caused the error. Can you explain what you mean please ? – Jerome Campeaux May 11 '15 at 12:21
  • Most likely the `User` you set on `localContact` does not have an ID corresponding to an existing user in the DB, and since you haven't explicitly set the cascading option mentioned in another answer, you will have to explicitly persist the `User` first. – Anders R. Bystrup May 11 '15 at 12:28
  • ok understand thank you – Jerome Campeaux May 11 '15 at 12:38
0

Have you annotate cascade=CascadeType.ALL to your entity object mapping

eg :

     @ManyToOne(cascade = CascadeType.ALL)
      private String entityType;
Thomas
  • 498
  • 4
  • 16