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

标签 java hibernate spring-data

我的数据库 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/

相关文章:

java - JPA/Hibernate - 与 child 一起分离实体

java - 将 EntityGraph 与 Hibernate 搜索 ORM 结合使用

elasticsearch - spring-data-elasticsearch-2.0.4.RELEASE startsWith不适用于非分析字段

java - CustomRepositoryImpl 中的 Autowiring bean 为 null

java - 在 TextView 中单击链接 HTML

java - Log4j 以不同格式记录两次

java - 装饰器模式和哈希码

java - hibernate可以在实体表创建后初始化数据吗?

Java、Spring 数据。列表映射类型的映射实体字段

java - 缓存文件夹中的 nomedia 文件 (Android)