Статьи

Рабочий учебник DeltaSpike

В недавнем проекте я столкнулся с интересной проблемой: взять метод службы, которому был предоставлен один объект, и повторно использовать тот же код в контексте ночного процесса, который выполнялся независимо от приложения на тысячах объектов того же типа. ,

Было бы смешно пытаться поддерживать идентичный процесс такого размера и сложности в двух местах, поэтому мне нужно было найти способ поделиться кодом. Одним из возможных решений было использование DeltaSpike , коллекции расширений CDI для Apache, чтобы обеспечить простой способ доступа к сущностям и классам из основного приложения Java / JPA / Hibernate.

Казалось, что было достаточно документации для запуска DeltaSpike в подобных ситуациях в Интернете, но был некоторый скептицизм по поводу жизнеспособности его использования в этом приложении; моя команда не смогла заставить его работать для другой части проекта и перешла к использованию Spring Batch для этой части.

Самым большим препятствием была неспособность создать функциональный EntityManager, независимо от того, насколько внимательно они следили за документацией и примерами, которые могли найти. Этот меньший компонент проекта представлял другого хорошего кандидата для реализации DeltaSpike, но после очень знакомого танца следующих руководств, поиска практических рекомендаций и чтения официальных инструкций по реализации я застрял в том же месте: EntityManager просто не будет работать, по крайней мере, с сочетанием технологий и версий, которые у меня были передо мной.

К счастью, мне удалось собрать воедино несколько уроков и примеров по Франкенштейну, чтобы получить работающую реализацию DeltaSpike для моей ситуации, поэтому я решил поделиться тем, что нашел, так что в следующий раз кому-то будет немного легче.

Основы

Для начала я использую Maven, поэтому вот зависимости, которые вам нужно добавить:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<dependency>
    <groupId>org.apache.deltaspike.cdictrl</groupId>
    <artifactId>deltaspike-cdictrl-api</artifactId>
    <version>1.2.1</version>
    <scope>compile</scope>
</dependency>
 
<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se</artifactId>
    <version>1.1.16.Final</version>
    <scope>runtime</scope>
</dependency>
 
<dependency>
    <groupId>org.apache.deltaspike.cdictrl</groupId>
    <artifactId>deltaspike-cdictrl-weld</artifactId>
    <version>1.2.1</version>
    <scope>runtime</scope>
</dependency>
 
<dependency>
    <groupId>org.apache.deltaspike.core</groupId>
    <artifactId>deltaspike-core-api</artifactId>
    <version>1.5.0</version>
</dependency>
 
<dependency>
    <groupId>org.apache.deltaspike.modules</groupId>
    <artifactId>deltaspike-jpa-module-api</artifactId>
    <version>1.4.0</version>
</dependency>
 
<dependency>
    <groupId>org.apache.deltaspike.modules</groupId>
    <artifactId>deltaspike-jpa-module-impl</artifactId>
    <version>1.4.0</version>
</dependency>

Как видите, мы рассматриваем реализацию DeltaSpike в проекте, который также нуждается в JBoss / Weld. Другие зависимости, которые я включил, включали javax.enterprise, Hibernate и JPA. В зависимости от вашей окончательной реализации вам могут не понадобиться все эти зависимости DeltaSpike, поэтому обязательно очистите ваш pom.xml, когда вы закончите.

Приложение

Для моего примера я буду использовать базовый класс Application, который следует шаблону, который вы, возможно, видели в других примерах DeltaSpike:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
import javax.enterprise.context.ApplicationScoped;
import org.apache.deltaspike.cdise.api.CdiContainer;
import org.apache.deltaspike.cdise.api.CdiContainerLoader;
import org.apache.deltaspike.cdise.api.ContextControl;
import org.apache.deltaspike.core.api.config.ConfigResolver;
 
public class Application {
     
    public static void main(String[] args) {
        CdiContainer cdiContainer = CdiContainerLoader.getCdiContainer();
        cdiContainer.boot();
         
        ContextControl contextControl = cdiContainer.getContextControl();
        contextControl.startContext(ApplicationScoped.class);
         
        //Your code here
         
        cdiContainer.shutdown();
    }
}

Обратите внимание на эту строку, в частности:

1
contextControl.startContext(ApplicationScoped.class);

Эта строка ищет классы с аннотацией @ApplicationScoped, которые необходимо включить как часть контекста.

EntityManagerProducer

Здесь все становится сложнее. Следуя вашему типичному учебнику по DeltaSpike, вы получите EntityManagerProducer, который выглядит примерно так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
@ApplicationScoped
public class EntityManagerProducer
{
    @PersistenceUnit
    private EntityManagerFactory entityManagerFactory;
 
    @Produces
    @Default
    @RequestScoped
    public EntityManager create()
    {
        return this.entityManagerFactory.createEntityManager();
    }
 
    public void dispose(@Disposes @Default EntityManager entityManager)
    {
        if (entityManager.isOpen())
        {
            entityManager.close();
        }
    }
}

Единственная проблема, с которой я столкнулся, заключалась в том, что это вообще не работало, учитывая совокупность факторов, с которыми я имел дело EntityManager всегда был нулевым, что бы я ни пытался. Я подозревал, что EntityManagerFactory не работал правильно, поэтому я немного покопался и нашел такой подход к получению EntityManagerFactory:

1
2
private EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("PERSISTENCE_UNIT", setProperties());

Используя это, я смог получить работающий EntityManagerFactory на основе модуля постоянства, который был определен в файле persistence.xml, имя которого я указал в параметрах. Там я определил сущности, которые приложение должно было бы быть частью EntityManagerFactory, чтобы иметь возможность выполнять такие мелочи … как запуск.

Вот как выглядит новый улучшенный EntityManagerProducer:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@ApplicationScoped
public class EntityManagerProducer {
 
    private EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT", setProperties());
 
    private EntityManager entityManager;
 
    protected void closeEntityManager(@Disposes EntityManager entityManager) {
        if (entityManager.isOpen()) {
            entityManager.close();
        }
    }
 
    @Produces
    protected EntityManager createEntityManager() {
        if (entityManager == null) {
            entityManager = entityManagerFactory.createEntityManager();
        }
        return entityManager;
    }
     
    protected Properties setProperties() {
        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
        properties.setProperty("hibernate.show_sql", "false");
        properties.setProperty("hibernate.hbm2ddl.auto", "none");
        properties.setProperty("hibernate.enable_lazy_load_no_trans", "true");
        properties.setProperty("hibernate.jdbc.batch_size", "20");
        properties.setProperty("hibernate.connection.driver_class", "oracle.jdbc.driver.OracleDriver");
        properties.setProperty("hibernate.connection.url", "JDBC_URL");
        properties.setProperty("hibernate.default_schema", System.getProperty("SCHEMA_NAME"));
        properties.setProperty("javax.persistence.jdbc.user", System.getProperty("USER"));
        properties.setProperty("javax.persistence.jdbc.password", System.getProperty("PASSWORD"));
        properties.setProperty("org.hibernate.flushMode", "ALWAYS");
        return properties;
    }
     
}

Обратите внимание на тег @ApplicationScoped; это гарантирует, что этот класс будет включен в контекст при запуске контейнера CDI. Также обратите внимание, что вы можете установить свои свойства для передачи в созданную вами EntityManagerFactory, включая получение их из System Properties, являющейся частью аргументов сервера, что полезно, если есть переменные среды, которые могут изменить работу вашего приложения.

Последние мысли

Надеемся, что это простой и эффективный пример того, как настроить и реализовать DeltaSpike.

Примечание: в итоге я не смог использовать этот подход для решения проблемы, потому что количество обрабатываемых объектов было НАМНОГО больше, чем предполагалось (несколько миллионов), но я подумал, что было бы полезно поделиться тем, что я нашел.

Ссылка: Рабочее руководство DeltaSpike от нашего партнера JCG Дэвида Келли в блоге