Not-null property references a transient value - transient instance must be saved before current operation

JavaJsonSpringHibernateRest

Java Problem Overview


I have 2 domain models and one Spring REST Controller like below:

@Entity
public class Customer{

@Id
private Long id;

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;

// other stuff with getters/setters

}

@Entity
public class Country{

@Id
@Column(name="COUNTRY_ID")
private Integer id;

// other stuff with getters/setters

}

Spring REST Controller:

@Controller
@RequestMapping("/shop/services/customers")
public class CustomerRESTController {

   /**
    * Create new customer
    */
    @RequestMapping( method=RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    @ResponseBody
    public com.salesmanager.web.entity.customer.Customer createCustomer(@Valid @RequestBody   Customer customer, Model model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		customerService.saveOrUpdate(customer);
	
		return customer;
	}

    // other stuff
}

I am trying to call above REST service with below JSON as body:

{
    "firstname": "Tapas",
    "lastname": "Jena",
    "city": "Hyderabad",
    "country": "1"
}

Where country code 1 is already there in Country table. The problem is when I am calling this service getting below error:

org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.test.model.Customer.country -> com.test.model.Country; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.test.model.Customer.country -> com.test.model.Country

Any help will be appreciated!

Java Solutions


Solution 1 - Java

Try putting CascadeType.ALL

@OneToOne(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
@JoinColumn(name="COUNTRY_ID", nullable=false) 

private Country country;

Solution 2 - Java

I had a similar problem. Two entities: Document and Status. Document had a relationship OneToMany with Status, that represented the history of Status the Document had.

So, there was a @NotNull @ManyToOne reference of Document inside Status.

Also, I needed to know the actual Status of Document. So, I needed another relationship, this time @OneToOne, also @NotNull, inside Document.

The problem was: how can I persist both entities the first time if both had a @NotNull reference to the other?

The solution was: remove @NotNull reference from actualStatus reference. This way, it was able to persist both entities.

Solution 3 - Java

I had the exact same problem. The solution seems to be to send the JSON like this:

{
  "firstname": "Tapas",
  "lastname": "Jena",
  "city": "Hyderabad",
  "country": {"id":"1"}
}

I guess @RequestBody tries to map an entity not a single field since the Customer instance is referencing a Country instance.

(I have similarly two entities, joined. In the DB, records for the referenced entity (Country in your case) were already created but the entity creation (Customer in your case) with a json, provided the same error message. For me CascadeType.ALL not helped but the above written change in the JSON solved the problem. For further config of course CascadeType can be considered.)

Solution 4 - Java

I got same error and this is how I solved it:

1st Entity:

    @Entity
    public class Person implements Serializable{
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private int personId;
    	private String name;
    	private String email;
    	private long phoneNumber;
    	private String password;
    	private String userType;
        @OneToOne(fetch = FetchType.LAZY, mappedBy = "personCustomer", cascade 
        = CascadeType.ALL)
	    private Customer customer;

2nd Entity:

@Entity
public class Customer implements Serializable{
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int customerId;
	@OneToOne(fetch = FetchType.LAZY, optional = false, cascade = 
    CascadeType.ALL)
    @JoinColumn(name = "person_customer")
	@JsonIgnore
	private Person personCustomer;

My Controller:

@PostMapping("/customer/registration")
	public PersonCustomer addCustomer(@RequestBody Person person)
	{
		Customer customer = new Customer(person);
		person.setCustomer(customer);
		Customer cust = customerRepo.save(customer);
		logger.info("{}", cust);
		Optional<Person> person_Cust = 
        personRepo.findById(cust.getPersonCustomer().getPersonId());
		Person personNew = person_Cust.get();
		PersonCustomer personCust = new PersonCustomer();
		
		if(cust.equals(null))
		{	
			personCust.setStatus("FAIL");
			personCust.setMessage("Registration failed");
			personCust.setTimestamp(personCust.timeStamp());
		}
		personCust.setStatus("OK");
		personCust.setMessage("Registration OK");
		personCust.setTimestamp(personCust.timeStamp());
		personCust.setPerson(personNew);
		
		return personCust;
	}

The problem got solved when I added "person.setCustomer(customer);". As both POJO classes has each others reference, so we have to "set" each others reference before using the JPA repository method(customerRepo.save(customer));

Solution 5 - Java

Just to add an additional scenario that led me to this exact same error:

Make sure that any backward references that may exist are not null.

Specifically in my case, I was using Mapstruct to update some fields of the entity, e.g.

MyClass newInstance = //...
MyClass dbInstance = repository.findByField(someField);
MyClassMapper.MAPPER.update(dbInstance, newInstance);
repository.save(dbInstance);

And my poor implementation of MyClassMapper led the backward references of dbInstance fields to be set to null when they should be pointing back to dbInstance.

Solution 6 - Java

you should change :

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;

to :

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name="COUNTRY_ID")
private Country country;

just delete nullable setting.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTapas JenaView Question on Stackoverflow
Solution 1 - JavaRaju RudruView Answer on Stackoverflow
Solution 2 - JavadjejaquinoView Answer on Stackoverflow
Solution 3 - JavaPéter MárkusView Answer on Stackoverflow
Solution 4 - JavaNishchay GuptaView Answer on Stackoverflow
Solution 5 - JavaJoão MatosView Answer on Stackoverflow
Solution 6 - JavaMiladesnovakainaView Answer on Stackoverflow