非空属性引用了一个瞬时值 - 当前操作之前必须保存瞬时实例。

61

我有两个领域模型和一个像下面这样的Spring REST控制器:

@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
@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
}

我正尝试使用下面的JSON作为请求体调用上面的REST服务:

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

在国家表中已经有国家代码1了。问题是当我调用这个服务时,会得到以下错误:

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

非常感谢您的帮助!

6个回答

60

尝试使用CascadeType.ALL

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

private Country country;

12
好的,那么请这样做。您的客户对象具有ID为1的国家对象,但该国家对象尚未持久化。首先通过发送国家ID获取国家对象。将从数据库中检索到的国家对象设置到客户对象中。然后保存客户对象。我希望这一次不会出现任何错误。 - Raju Rudru
奇怪的是,当这个不起作用时,CascadeType.ALL 对我有用:@ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) - Sam Barnum

16
我有一个类似的问题。有两个实体: 文档状态文档状态 之间有一个 OneToMany 的关系,表示 文档状态 历史。
因此,在 状态 中有一个 @NotNull @ManyToOne 引用到 文档
同时,我还需要知道 文档 的当前 状态。因此,我需要另一种关系,这次是 @OneToOne,同样是 @NotNull 的,在 文档 内部。
问题是:如果两个实体都有对彼此的 @NotNull 引用,第一次如何持久化这两个实体?
解决方案是:从 actualStatus 引用中移除 @NotNull 引用。这样,就能够持久化这两个实体。

这并没有真正回答问题。如果您有不同的问题,可以通过点击提问来提出。一旦您拥有足够的声望,您还可以添加赏金以吸引更多关注此问题。 - skolima
23
我提出了一个与我的问题类似的解决方案。与新问题无关。 - djejaquino

1

我想补充一个导致我遇到同样错误的情况:

确保任何可能存在的反向引用不为null。

具体来说,在我的情况下,我正在使用Mapstruct更新实体的一些字段,例如:

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

我的MyClassMapper实现不够完善,导致dbInstance字段的反向引用被设置为null,而它们应该指向dbInstance


0

我有完全相同的问题。解决方法似乎是像这样发送 JSON:

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

我猜测@RequestBody试图映射一个实体而不是单个字段,因为Customer实例引用了一个Country实例。
(我也有两个类似的实体,它们是关联的。在数据库中,被引用实体(在你的情况下是Country)的记录已经创建,但使用json创建实体(在你的情况下是Customer)时,出现了相同的错误消息。对我来说,CascadeType.ALL没有起到作用,但上述JSON中的更改解决了问题。当然,在进一步的配置中可以考虑CascadeType。)

0

我遇到了同样的错误,以下是我解决问题的方法:

第一实体:

    @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;

第二个实体:

@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;

我的控制器:

@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;
    }

当我添加了 "person.setCustomer(customer);" 时,问题得到了解决。 由于两个POJO类都有对方的引用,所以在使用JPA repository方法(customerRepo.save(customer))之前,我们必须"set"彼此的引用。

-3

你应该改成:

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

至:

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

只需删除可为空的设置。


请问您能否解释一下在这种情况下可空类型为什么容易出现问题? - Asma Rahim Ali Jafri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接