gpt4 book ai didi

java - 传递的分离实体在 Spring-Data 中持久存在

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:08:29 25 4
gpt4 key购买 nike

我的数据库 BrandProduct 中有两个表,具有下一个简单结构:

|品牌 |身份证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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com