java Spring数据JPA:使用单独的存储库更新嵌入集合时如何更新实体?

wkyowqbh  于 2023-03-16  发布在  Java
关注(0)|答案(2)|浏览(186)

概述

我有一个Customer实体,其中嵌入了Order集合。CustomerOrder使用单独的Spring Data JPA存储库(CustomerRepositoryOrderRepository)进行更新。
以下操作在事务性方法中执行:
1.使用OrderRepositorysave方法为客户添加订单
1.使用CustomerRepositoryfindById方法读取客户
预期结果:客户包含订单
实际结果:客户 * 不 * 包含订单

示例项目

详情

客户:

@Entity
public class Customer {

    @Id
    String id;

    @OneToMany(mappedBy = "customer")
    List<Order> orders;

}

订单:

@Entity
@Table(name = "orders")
public class Order {

    @Id
    String id;

    @ManyToOne
    @JoinColumn(name = "customer_id", nullable = false)
    Customer customer;

}

我使用Spring Data存储库进行读/写操作:
一个二个一个一个
使用OrderRepository对嵌入订单集合的更新不会反映在客户实体上:

Customer customer = new Customer();
customer.id = "customer-0";

customer = customerRepository.save(customer);

Order order = new Order();
order.id = "order-0";
order.customer = customer;

orderRepository.save(order);

System.out.println(customerRepository.findById("customer-0"));

预期输出:
x1米11米1x
实际产量:
Optional[Customer{id='customer-0', orders=null}]
我知道以下解决方案/变通方法:

  • save替换为saveAndFlush,并使用EntityManager刷新客户对象:这将生成预期输出。
  • 将订单分配给客户。保存订单和客户。
  • 在新交易中重新读取客户。

不幸的是,我正在处理一个现有的代码库,其中数据库访问是分散的。实现这些替代方案中的任何一个都不是微不足道的。
有没有一种方法可以告诉JPA在通过OrderRepository更新嵌入的Order集合时“自动地”更新Customer实体?

uurity8g

uurity8g1#

您不需要指定使用的是哪个JPA实现,也没有JPA标准方法来实现这一点。但是Hibernate提供了一种保持双向关系双方同步的方法。我从未使用过它,所以我只引用article by Thorben Janssen
您只需向构建流程添加Maven或Gradle插件,即可将所需代码添加到实体的类文件中。
下面的配置将hibernate-enhance-maven插件添加到maven构建中,并生成管理双向关联所需的代码。

<project>
    ...
     
    <build>
        <plugins>
            <plugin>
                <groupId>org.hibernate.orm.tooling</groupId>
                <artifactId>hibernate-enhance-maven-plugin</artifactId>
                <version>${hibernate.version}</version>
                <executions>
                    <execution>
                        <configuration>
                            <failOnError>true</failOnError>
                            <enableAssociationManagement>true</enableAssociationManagement>
                        </configuration>
                        <goals>
                            <goal>enhance</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

这就是你所需要做的。在你的下一个Maven构建中,插件将添加额外的代码到你的双向关联的到一端的setter方法中。这段代码更新了关联的另一端,这样你就不需要自己做了。

disbfnqx

disbfnqx2#

据我所知,一个实体的两个具有相同主键的持久化对象不能共存于一个持久化上下文中(驻留在一级缓存中)。

customer = customerRepository.save(customer);

并且由于tx尚未提交或会话尚未刷新,因此以下调用将不会命中DB

customerRepository.findById("customer-0")

此外,到目前为止还没有记录被刷新到DB,所有内容都在persistentcontext中,因此查询将从一级缓存/持久化上下文中获取实体
即使您已经从数据库中检索了现有的客户记录,并再次触发了一个单独的查询来检索同一个客户,它也会返回先前查询中检索到的持久化上下文中的现有对象。
ORM框架将首先查看具有特定ID的实体是否已经在持久性上下文中,如果是,则它将仅仅从持久性上下文或第一级高速缓存返回对象/实体。
但是,如果您使用的是上面“Jens Schauder”建议的类插装,插装代码将负责填充关系/关联链接。
几年前还没有Hibernate插装,我们过去常常显式地设置关系,即使现在我更喜欢做以下事情(不要同时使用插装和显式设置关系):
首先,我建议初始化列表,即List<Order> orders = new ArrayList<>(),并在Customer类中为订单添加一个“getter”,然后执行以下操作;

customer.getOrders().add(order);

order.setCustomer(customer);

如果你做了上面的操作或者使用了hib工具,你的查询将会给予你想要的结果。
P.S.对您的项目进行上述更改后,我得到以下输出:
可选[客户{id =“客户-0”,订单=[订单{id =“订单-0”,客户=客户-0}]}]可选[客户{id =“客户-0”,订单=[订单{id =“订单-0”,客户=客户-0}]}]

相关问题