使用 EntityManager

EntityManager 是用于读写实体的标准 JPA 接口。也可用于运行原生 SQL 语句。

我们推荐使用 DataManager 处理实体,仅在真正需要时使用 EntityManager

关于 EntityManager 的接口方法信息,请参阅 javax.persistence.EntityManager API 文档。

在 Jmix 应用程序中可以与任何 Spring 应用程序几乎一样的方式使用 EntityManager 来处理 JPA 实体。但是,Jmix 提供了标准接口的自定义实现,增加了一些特殊功能。

获取 EntityManager

可以通过 @PersistenceContext 注解在任何 Spring bean 中注入 EntityManager 的实例。使用 EntityManager 实例时,需要打开一个数据库 事务。示例:

@PersistenceContext
private EntityManager entityManager;

@Transactional
public Customer createCustomer() {
    Customer customer = metadata.create(Customer.class);
    customer.setName("Bob");
    entityManager.persist(customer);
    return customer;
}

如需处理 附加数据存储 中的实体,可以通过在 @PersistenceContext 注解的 unitName 参数指定数据存储的名称获取 EntityManager 实例,示例:

@PersistenceContext(unitName = "db1")
private EntityManager entityManagerForDb1;

@Transactional("db1TransactionManager")
public Foo createFoo() {
    Foo foo = metadata.create(Foo.class);
    foo.setName("foo1");
    entityManagerForDb1.persist(foo);
    return foo;
}
EntityManager 不能在 UI 控制器中注入。

使用 Fetch Plans

默认情况下,当通过 EntityManager 使用 id 或 JPQL 查询加载实体时,会返回实体的所有本地(直接)属性并根据 JPQ 规范的规则预加载关联属性。其他引用属性则在同一个事务中进行懒加载。

可以使用 fetch plans 来优化对象关系图的加载过程,而不考虑实体属性注解中的 FetchType.LAZY 指令。Fetch plan 需要在属性属性映射(properties map)中指定。示例:

@PersistenceContext
private EntityManager entityManager;

@Autowired
private FetchPlans fetchPlans;

@Transactional
public Order findOrder(UUID orderId) {
    FetchPlan fetchPlan = fetchPlans.builder(Order.class)
            .add("customer")
            .build();

    Map<String, Object> properties = PersistenceHints.builder()
            .withFetchPlan(fetchPlan)
            .build();

    return entityManager.find(Order.class, orderId, properties);
}

上面的示例中,会加载 Order 和关联 Customer 实体的所有的本地属性,尽管没有在 fetch plan 中显式地指定需要加载的属性。如果只需加载部分本地属性,可以构建一个 “partial” fetch plan。示例:

@Transactional
public Order loadGraphOfPartialEntities(UUID orderId) {
    FetchPlan fetchPlan = fetchPlans.builder(Order.class)
            .addAll("number", "date", "customer.name")
            .partial()
            .build();

    Map<String, Object> properties = PersistenceHints.builder()
            .withFetchPlan(fetchPlan)
            .build();

    return entityManager.find(Order.class, orderId, properties);
}

软删除

EntityManager 支持 软删除。当对一个具有 软删除特性 的实体进行 remove() 操作时,会更新 @DeletedDate@DeletedBy 属性,而不会直接从数据库删除实体。

针对特定的事务,可以关闭软删除功能,这样可以硬删除带有软删除特性的实体,也可以加载已经标记删除的实体。通过指定 PersistenceHints.SOFT_DELETION 属性,示例:

@Transactional
public void hardDelete(Product product) {
    entityManager.setProperty(PersistenceHints.SOFT_DELETION, false);
    entityManager.remove(product);
}

EntityManager 的局限

  1. EntityManager 保存和加载实体时,不会发送 EntitySavingEvent 和 EntityLoadingEvent

  2. 在加载根实体的事务外部无法 懒加载 引用属性。会在访问这种属性时抛出 “java.lang.IllegalStateException: Cannot get unfetched attribute …​” 异常。

  3. 不会维护 跨数据存储引用

  4. 会绕过 数据访问权限检查

  5. 不支持下列 JPA 功能:命名查询、CriteriaBuilderEntityGraphEntityTransaction