【问题标题】:Multiple Spring Data JPA repositories in single transaction单个事务中的多个 Spring Data JPA 存储库
【发布时间】:2015-04-07 04:04:08
【问题描述】:

我正在尝试映射以下代码: * 订单包含许多 OrderItems * 文章可以被(包含)多个 OrderItems 引用

在一个事务中,我需要创建订单、几篇文章和带有新文章的 OrderItems。当有直线路径时一切都很清楚 - 订单包含 OrderItems 等等 - 然后我可以调用

orderRepository.save(order);
orderRepository.flush();

保存带有 OrderItems 实体的新订单实体。问题也始于尝试保存 Article 实体。显然需要做更多的工作,因为我收到了错误消息;

org.springframework.dao.InvalidDataAccessApiUsageException: 
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing

如何在一个事务中组织这样的工作?多个存储库是否可以在单个事务下工作,如果是这样 - 其 flush() 完成事务。是否有必要的多个刷新(例如,一个用于文章实体,另一个用于订单),如果是 - 如果需要,如何回滚所有这些?

显然不可能将存储库与简单的 EntityManager.getTransaction().{begin(), commit()} 代码混合,因为错误消息是:

java.lang.IllegalStateException: Cannot obtain local EntityTransaction from a transaction-synchronized EntityManager

如果一个实体(OrderItem)由两个实体(Order 和 Article)拥有并且所有三个实体都应该在一个事务中创建,那么最佳做法是什么?

添加了部分代码。 completeOrder 是测试过程,其中为新订单创建了 5 个项目和 5 个新文章。此过程给出异常“引用和未保存的瞬态实例”。我正在为 Article 实体使用复合键,但这应该不是问题,我现在正在尝试使用具有通常单字段键的 Article 实体的相同场景。

@Entity
public class Order extends FrameworkEntity {
    ...
    @OneToMany(mappedBy="order", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<OrderItem>();
}


@Entity
public class OrderItem extends FrameworkEntity {
    ...
    @ManyToOne
    @JoinColumn(name="fk_order")
    private Order order;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(name="fk_date"),
        @JoinColumn(name="fk_number")
    })
    private Article article;
}

@Entity
public class Article extends FrameworkEntity {
    ...
    @Id
    protected PkArticle pkArticle;

    @OneToMany(mappedBy="article", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<OrderItem>();
}

@Embeddable
public class PkArticle extends FrameworkCompositeKey {

    @Column(name = "article_number")
    private Long articleNumber;

    @Temporal(TemporalType.DATE) 
    @Column(name = "article_date")
    private Date articleDate;
    ...
}


public class OrderService {

    public void completeOrder() {

        Order entity = new Order();
        for (int i=0; i<5; i++) {
            Date articleDate;
            Long articleNumber;

            Calendar cal = Calendar.getInstance();
            cal.setTimeInMillis(0);
            cal.set(2015, 1, 31, 0, 0, 0);
            articleDate = cal.getTime(); 
            articleNumber = (new Random()).nextLong();

            Article article = new Article(articleNumber, articleDate);

            //entityManager.getTransaction().begin();
            //entityManager.persist(space);
            //entityManager.getTransaction().commit();

            OrderItem item = new OrderItem();

            item.setArticle(article);
            article.getOrderItems().add(item);

            item.setOrder(entity);
            entity.getOrderItems().add(item);
        }
        entity=(Order) repository.save(entity);
        repository.flush();
    }
]

【问题讨论】:

  • 您能否发布一个带有OrderOrderItemsArticle 类的小项目及其各自的存储库来演示问题?您所描述的内容似乎很简单,但尚不清楚您在哪里面临问题,因此实际代码会有所帮助。
  • 由于您正在保存(和刷新)Order,因此保存将级联到OrderItems,因为从OrderOrderItem@OneToMany 具有CascadeType.ALL。但是,级联将在此处停止,因为从OrderItemArticle@ManyToOne 没有设置级联类型,并且@ManyToOne 的默认值是CascadeType.NONE。请参阅下面我的回答,了解如何使您的代码正常工作。

标签: spring jpa orm jpa-2.0 spring-data


【解决方案1】:

你有这样的代码吗?

@Entity
public class OrderItem {
  @ManyToOne
  private Article article;
}

如果是,请将其更改为:

@Entity
public class OrderItem {
  @ManyToOne(cascade = CascadeType.PERSIST)
  private Article article;
}

这将确保当一个新的OrderItem 和一个尚未保存的Article 被持久化时,Article 也将被保存。

【讨论】:

    猜你喜欢
    • 2013-05-30
    • 2020-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-30
    • 1970-01-01
    • 2017-02-11
    相关资源
    最近更新 更多