Работа с EntityManager

EntityManager – это стандартный интерфейс JPA для загрузки и сохранения сущностей. Он также может выполнять собственные SQL-запросы.

Мы рекомендуем использовать DataManager для работы с сущностями и обращаться к EntityManager только если он вам действительно нужен.

Для получения информации о методах EntityManager обратитесь к документации API javax.persistence.EntityManager.

EntityManager можно использовать в приложениях Jmix для работы с сущностями JPA в основном таким же образом, как и в любом приложении Spring. Однако Jmix предоставляет собственную реализацию стандартного интерфейса, которая имеет некоторые особенности.

Получение EntityManager

Экземпляр EntityManager можно инжектировать в любой бин Spring с помощью аннотации @PersistenceContext. Для использования экземпляра EntityManager у вас должна быть открытая транзакция. Например:

@PersistenceContext
private EntityManager entityManager;

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

Если вам нужно работать с сущностями из дополнительного хранилища данных, получите экземпляр EntityManager для этого хранилища, указав имя хранилища данных в параметре unitName аннотации @PersistenceContext, например:

@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.

Использование фетч-планов

По умолчанию при использовании EntityManager для загрузки сущностей по id или запросу JPQL, он возвращает все локальные (непосредственные) атрибуты и жадно извлеченные ссылки по правилам спецификации JPA. Другие ссылки загружаются лениво в той же транзакции.

Вы можете использовать фетч-планы для оптимизации загрузки необходимого графа объектов независимо от инструкции извлечения FetchType.LAZY в аннотациях атрибутов сущности. Фетч-план должен быть указан в свойствах. Например:

@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, даже если они не включены в план выборки явно. Если вы хотите загрузить только часть локальных атрибутов, создайте «частичный» фетч-план. Например:

@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() для сущности с чертой Soft Delete, он обновляет атрибуты @DeletedDate и @DeletedBy вместо удаления записи из базы данных.

Вы можете отключить мягкое удаление для конкретной транзакции, чтобы полностью удалить объекты с чертой Soft Delete и иметь возможность загружать экземпляры, помеченные как удаленные, указав свойство PersistenceHints.`SOFT_DELETION. Например:

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

Ограничения EntityManager

  1. События EntitySavingEvent и EntityLoadingEvent не отправляются при сохранении и загрузке сущностей с EntityManager.

  2. Ленивая загрузка ссылок вне транзакции, загружающей корневую сущность, не работает. Вы получите исключение "java.lang.IllegalStateException: Cannot get unfetched attribute …​'’, если получите доступ к такой ссылке.

  3. Ссылки между хранилищами данных не поддерживаются.

  4. Проверки доступа к данным пропускаются.

  5. Не поддерживаются следующие функции JPA: именованные запросы, CriteriaBuilder, EntityGraph, EntityTransaction.