我有以下类(class):
产品类别:
package com.springtraining.hibernate.invoice;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
@NoArgsConstructor
@Getter
@Entity
@Table(name="PRODUCTS")
public class Product {
@Id
@GeneratedValue
@NotNull
@Column(name = "ID")
private int id;
@Column(name = "NAME")
private String name;
@OneToMany(
targetEntity = Item.class,
mappedBy = "product",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY
)
private List<Item> items = new ArrayList<>();
public Product(String name) {
this.name = name;
}
}
package com.springtraining.hibernate.invoice.dao;
import com.springtraining.hibernate.invoice.Product;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
@Repository
@Transactional
public interface ProductDao extends CrudRepository<Product, Integer> {
}
现在,一个 Item 类:
package com.springtraining.hibernate.invoice;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@NoArgsConstructor
@Getter
@Entity
@Table(name="ITEMS")
public class Item {
@Id
@GeneratedValue
@NotNull
@Column(name = "ID")
private int id;
@JoinColumn(name = "PRODUCT_ID", referencedColumnName = "id")
@ManyToOne
private Product product;
@NotNull
@Column(name = "PRICE")
private BigDecimal price;
@NotNull
@Column(name = "QUANTITY")
private int quantity;
@NotNull
@Column(name = "VALUE")
private BigDecimal value;
@JoinColumn(name="INVOICE_ID", referencedColumnName = "id")
@ManyToOne
private Invoice invoice;
public Item(Product product, String price, int quantity) {
this.product = product;
this.product.getItems().add(this);
this.price = new BigDecimal(price);
this.quantity = quantity;
this.value = this.price.multiply(new BigDecimal(quantity));
}
public void setInvoice(Invoice invoice) {
this.invoice = invoice;
invoice.getItems().add(this);
}
}
package com.springtraining.hibernate.invoice.dao;
import com.springtraining.hibernate.invoice.Item;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
@Repository
@Transactional
public interface ItemDao extends CrudRepository<Item, Integer> {
}
还有一个 Invoice 类:
package com.springtraining.hibernate.invoice;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
@NoArgsConstructor
@Getter
@Entity
@Table(name="INVOICES")
public class Invoice {
@Id
@GeneratedValue
@NotNull
@Column(name = "ID")
private int id;
@NotNull
@Column(name = "NUMBER")
private String number;
@OneToMany(
targetEntity = Item.class,
mappedBy = "invoice",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY
)
private List<Item> items = new ArrayList<>();
public Invoice(String number) {
this.number = number;
}
}
package com.springtraining.hibernate.invoice.dao;
import com.springtraining.hibernate.invoice.Invoice;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
@Repository
@Transactional
public interface InvoiceDao extends CrudRepository<Invoice, Integer> {
}
现在,当我使用这些类运行单元测试时,出现以下错误:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.springtraining.hibernate.invoice.Item.product -> com.springtraining.hibernate.invoice.Product; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.springtraining.hibernate.invoice.Item.product -> com.springtraining.hibernate.invoice.Product
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.springtraining.hibernate.invoice.Item.product -> com.springtraining.hibernate.invoice.Product; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.springtraining.hibernate.invoice.Item.product -> com.springtraining.hibernate.invoice.Product
单元测试代码如下所示:
package com.springtraining.hibernate.invoice.dao;
import com.springtraining.hibernate.invoice.Invoice;
import com.springtraining.hibernate.invoice.Item;
import com.springtraining.hibernate.invoice.Product;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
@RunWith(SpringRunner.class)
@SpringBootTest
public class InvoiceDaoTestSuite {
@Autowired
private InvoiceDao invoiceDao;
@Test
public void testInvoiceDaoSave() {
// Given
Product product1 = new Product("prod1");
Product product2 = new Product("prod2");
Item item1 = new Item(product1, "100", 10);
Item item2 = new Item(product1, "200", 10);
Item item3 = new Item(product2, "50", 2);
Item item4 = new Item(product2, "250", 25);
Invoice invoice1 = new Invoice("HK-47");
item2.setInvoice(invoice1);
item3.setInvoice(invoice1);
Invoice invoice2 = new Invoice("HK-48");
item1.setInvoice(invoice2);
item4.setInvoice(invoice2);
// When
invoiceDao.save(invoice1);
invoiceDao.save(invoice2);
int invoice1_id = invoice1.getId();
int invoice2_id = invoice2.getId();
// Then
Assert.assertNotEquals(0, invoice1_id);
Assert.assertNotEquals(0, invoice2_id);
Assert.assertTrue(invoice1.getItems().containsAll(Arrays.asList(item2, item3)));
Assert.assertTrue(invoice2.getItems().containsAll(Arrays.asList(item1, item4)));
Assert.assertTrue(product1.getItems().containsAll(Arrays.asList(item1, item2)));
Assert.assertTrue(product1.getItems().containsAll(Arrays.asList(item3, item4)));
// Clean-up
try {
invoiceDao.deleteById(invoice1_id);
invoiceDao.deleteById(invoice2_id);
} catch (Exception e) {
// Do nothing
}
}
}
我已经看了这个代码几个小时了,但我仍然不明白,我错过了一些东西。保存发票实体时,应该自动实例化与其关联的 Item 和 Product 对象。
有人吗?
最佳答案
在public class Item
中,添加@ManyToOne
cascade = CascadeType.ALL属性,如下所示:
@JoinColumn(name = "PRODUCT_ID", referencedColumnName = "id")
@ManyToOne(cascade = CascadeType.ALL)
private Product product;
@JoinColumn(name = "INVOICE_ID", referencedColumnName = "id")
@ManyToOne(cascade = CascadeType.ALL)
private Invoice invoice;
当您使用关键字new
创建新实体时,它处于Transient
状态。要将其持久化/保存到数据库,您首先需要将其添加到持久化上下文
。 CascadeType.ALL
包含 CascadeType.PERSIST
,它将指示 Hibernate
持久产品 和发票实体。
此外,删除实体字段上由 @Id
注释的 @NotNull
。这不是必需的,因为您的字段是主键,而不是这个
invoiceDao.save(invoice1);
invoiceDao.save(invoice2);
int Invoice1_id = Invoice1.getId();
int Invoice2_id = Invoice2.getId();
你可以这样做:
int invoice1_id = invoiceDao.save(invoice1).getId();
int invoice2_id = invoiceDao.save(invoice2).getId();
关于Java Spring TransientPropertyValueException 与 @OneToMany,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62724462/