我的数据库 Brand
和 Product
中有两个表,具有下一个简单结构:
|品牌 |身份证PK |
|产品 |身份证PK | brand_id FK |
和该表的实体:
@Entity
@Table(name = "Brand")
public class Brand {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "brand")
private String brand;
/* getters and setters */
}
@Entity
@Table(name = "Product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "brand_id")
private Brand brand;
/* getters and setters */
}
当我使用 Spring-Data 时,我有用于品牌实现的存储库和服务:
@Repository
public interface BrandRepository extends JpaRepository<Brand, Long> {
Brand findByBrand(String brand);
}
public interface BrandService {
Brand findByBrand(String brand);
}
@Service
public class BrandServiceImpl implements BrandService {
@Autowired
private BrandRepository brandRepository;
@Override
public Brand findByBrand(String brand) {
return brandRepository.findByBrand(brand);
}
}
对于产品:
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}
public interface ProductService {
Product save(Product product);
}
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductRepository productRepository;
@Override
public Product save(Product product) {
return productRepository.save(product);
}
}
目标是保存 Product 对象。如果数据库中不存在品牌对象,则应自动保存品牌对象,否则应将其设置为产品:
Brand brand = brandService.findByBrand(brandName);
if (brand == null) {
brand = new Brand();
brand.setBrand("Some name");
}
product.setBrand(brand);
productService.save(product);
如果具有指定 brandName 的 Brand 对象不在我的数据库中,它工作正常。但如果是,我会得到:
PersistentObjectException: detached entity passed to persist
品牌。
我可以将级联类型更改为 MERGE,它会正常工作。但是,如果我使用 MERGE 级联类型运行代码,并且具有指定品牌名称的品牌对象不在我的数据库中,我会得到
IllegalStateException:
org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance - save the transient instance before flushing
对于 Brand(这真的不足为奇)。
应该是什么级联类型?或者我做错了什么?
最佳答案
简答:
你的级联注解没有问题。您不应依赖自动级联并在服务层内手动实现此逻辑。
长答案:
你有两种情况:
- 场景 1 - CascadeType.ALL + 现有品牌 = 独立实体 传递到坚持
- 场景 2 - CascadeType.MERGE + 新品牌 = 保存 flushin 之前的 transient 实例
场景 1 发生是因为 JPA 试图在持久化 PRODUCT (CascadeType.ALL) 之后持久化 BRAND。一旦 BRAND 已经存在,就会出现错误。
场景 2 发生是因为 JPA 没有尝试保留 BRAND (CascadeType.MERGE) 并且之前没有保留 BRAND。
很难找出解决方案,因为有太多的抽象层。 Spring data抽象JPA,JPA抽象Hibernate,Hibernate抽象JDBC等等。
一个可能的解决方案是使用 EntityManager.merge 而不是 EntityManager.persist,以便 CascadeType.MERGE 可以工作。我相信您可以重新实现 Spring Data 保存方法。这里有一些引用:Spring Data: Override save method
另一个解决方案是简短的回答。
例子:
@Override
public Product save(Product product, String brandName) {
Brand brand = brandService.findByBrand(brandName);
if (brand == null) {
brand = brandService.save(brandName);
}
return productRepository.save(product);
}
关于java - 传递的分离实体在 Spring-Data 中持久存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40247030/