java 传递到 Spring-Data 中持久化的分离实体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40247030/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detached entity passed to persist in Spring-Data
提问by Ilya Orlov
I have two tables in my db Brand
and Product
with the next simple structure:
我的数据库中有两个表,Brand
并且Product
具有下一个简单的结构:
| Brand | id PK |
| 品牌 | 身PK |
| Product | id PK | brand_id FK |
| 产品 | 身PK | 品牌_id FK |
and entities for that tables:
和该表的实体:
@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 */
}
As I use Spring-Data I have repository and service with implementation for Brand:
当我使用 Spring-Data 时,我拥有带有 Brand 实现的存储库和服务:
@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);
}
}
and for Product:
和产品:
@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);
}
}
The goal is to save Product object. Brand object should be saved automatically if it doesn't exist in db or should be set to Product otherwise:
目标是保存 Product 对象。如果 db 中不存在 Brand 对象,则应自动保存它,否则应将其设置为 Product:
Brand brand = brandService.findByBrand(brandName);
if (brand == null) {
brand = new Brand();
brand.setBrand("Some name");
}
product.setBrand(brand);
productService.save(product);
It works fine if Brand object with specified brandName is not in my db. But if it is I get:
如果具有指定品牌名称的品牌对象不在我的数据库中,则它工作正常。但如果是我得到:
PersistentObjectException: detached entity passed to persist
for Brand.
为品牌。
I can change cascade type to MERGE and it will work fine. But if I run the code with MERGE cascade type and Brand object with specified brandName is not in my db I get
我可以将级联类型更改为 MERGE,它会正常工作。但是,如果我使用 MERGE 级联类型运行代码,并且指定品牌名称的 Brand 对象不在我的数据库中,我会得到
IllegalStateException:
org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance - save the transient instance before flushing
for Brand (that's really not surprised).
对于品牌(这真的不奇怪)。
What Cascade Type should be? Ot what I did wrong?
应该是什么级联类型?我做错了什么?
回答by Leonardo Cruz
Short answer:
简答:
There is no problem with your cascade annotation. You should not rely on automatic cascade and implement this logic by hand and inside your service layer.
你的级联注释没有问题。您不应该依赖自动级联并在您的服务层内部手动实现此逻辑。
Long answer:
长答案:
You have two scenarios:
你有两种情况:
- Scenario 1 - CascadeType.ALL + existing brand = detached entity passed to persist
- Scenario 2 - CascadeType.MERGE + new brand = save the transient instance before flushin
- 场景 1 - CascadeType.ALL + 现有品牌 = 传递给持久化的分离实体
- 场景 2 - CascadeType.MERGE + 新品牌 = 在刷新之前保存瞬态实例
Scenario 1 happens because JPA is trying to persist BRAND after persist PRODUCT (CascadeType.ALL). Once BRAND already exists you got an error.
发生场景 1 是因为 JPA 试图在持久化 PRODUCT (CascadeType.ALL) 之后持久化 BRAND。一旦 BRAND 已经存在,你就会得到一个错误。
Scenario 2 happend because JPA is not trying to persist BRAND (CascadeType.MERGE) and BRAND was not persisted before.
发生场景 2 是因为 JPA 没有尝试持久化 BRAND (CascadeType.MERGE) 并且之前没有持久化 BRAND。
It's hard to figure out a solution because there are so many abstraction layers. Spring data abstracts JPA that abstracts Hibernate that abstracts JDBC and so on.
很难找到解决方案,因为有太多的抽象层。Spring数据抽象了JPA,抽象了Hibernate,抽象了JDBC等等。
A possible solution would be use EntityManager.merge instead of EntityManager.persist so that CascadeType.MERGE could work. I belive you can do that re-implementing Spring Data save method. There is some reference about that here : Spring Data: Override save method
一个可能的解决方案是使用 EntityManager.merge 而不是 EntityManager.persist 以便 CascadeType.MERGE 可以工作。我相信您可以重新实现 Spring Data 保存方法。这里有一些参考:Spring Data: Override save method
Another solution would be the short answer.
另一个解决方案是简短的回答。
Example:
例子:
@Override
public Product save(Product product, String brandName) {
Brand brand = brandService.findByBrand(brandName);
if (brand == null) {
brand = brandService.save(brandName);
}
return productRepository.save(product);
}
回答by Gagandeep Kalra
Adding @Transactional to the method brings the entire discussion into one PersistenceContext alongwith optimising queries(fewer hibernate sql queries)
在方法中添加@Transactional 将整个讨论整合到一个 PersistenceContext 中,同时优化查询(更少的 hibernate sql 查询)
@Override
@Transactional
public Product save(Product product, String brandName) {
Brand brand = brandService.findByBrand(brandName);
if (brand == null) {
brand = brandService.save(brandName);
}
return productRepository.save(product);
}