在一个最近的项目中,我遇到了一个有趣的问题:采用被赋予单个对象的服务方法,并在一个夜间过程的上下文中重用相同的代码,该过程独立于应用程序而在数千个相同类型的对象上运行。
试图在两个地方维护如此大小和复杂性的相同过程真是荒谬的,所以我需要找到一种共享代码的方法。 一种可能的解决方案是使用DeltaSpike (Apache的CDI扩展集合)提供一种从主要Java / JPA / Hibernate应用程序访问实体和类的简单方法。
似乎有足够的文档来使DeltaSpike在网络上的类似情况下运行,但是人们对此应用程序的可行性持怀疑态度。 我的团队无法使它在项目的其他部分工作,因此继续使用Spring Batch。
最大的障碍是无法创建功能性的EntityManager,无论他们如何紧密地遵循可以找到的文档和示例。 该项目的较小部分为实现DeltaSpike提供了另一个很好的候选人,但是在对以下教程非常熟悉之后,找到了操作方法,并阅读了正式的实现说明,我被困在同一个地方:EntityManager将无法工作,至少不能与我面前的技术和版本组合使用。
幸运的是,我能够将弗兰肯斯坦的一些教程和示例中的部分放在一起,以针对我的情况获得可行的DeltaSpike实现,因此我想我会分享我发现的内容,因此下次其他人可能会更轻松一些。
基础
首先,我正在使用Maven,因此这里是您需要添加的依赖项:
<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>
如您所见,我们正在考虑在一个也需要JBoss / Weld的项目中实现DeltaSpike。 我所依赖的其他依赖项包括javax.enterprise,Hibernate和JPA。 根据最终的实现,您可能不需要所有这些DeltaSpike依赖项,因此请确保在完成后清理pom.xml。
应用程式
对于我的示例,我将使用一个基本的Application类,该类遵循您在其他DeltaSpike示例中可能看到的模式:
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 herecdiContainer.shutdown();}
}
特别注意这一行:
contextControl.startContext(ApplicationScoped.class);
该行查找带有@ApplicationScoped批注的类,这些类需要包含在上下文中。
EntityManagerProducer
这是棘手的地方。 遵循典型的DeltaSpike教程,将为您提供一个类似于以下内容的EntityManagerProducer:
@ApplicationScoped
public class EntityManagerProducer
{@PersistenceUnitprivate EntityManagerFactory entityManagerFactory;@Produces@Default@RequestScopedpublic EntityManager create(){return this.entityManagerFactory.createEntityManager();}public void dispose(@Disposes @Default EntityManager entityManager){if (entityManager.isOpen()){entityManager.close();}}
}
考虑到我要处理的各种因素,我唯一遇到的问题是根本不起作用。 无论我尝试了什么,EntityManager始终为null。 我怀疑EntityManagerFactory不能正常工作,所以我做了一些挖掘,发现这种获取EntityManagerFactory的方法:
private EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("PERSISTENCE_UNIT", setProperties());
使用此方法,我可以基于在persistence.xml中定义的持久性单元(它具有我在参数中提供的名称)来获得一个有效的EntityManagerFactory。 在这里,我定义了应用程序需要成为EntityManagerFactory一部分的实体,以便能够执行诸如运行之类的小事情。
这是新的和改进的EntityManagerProducer的外观:
@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();}}@Producesprotected 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中,包括从服务器参数中的系统属性中获取它们,如果环境变量可能会更改应用程序的功能,这将非常有用。
最后的想法
希望这为如何设置和实现DeltaSpike提供了一个简单有效的示例。
旁注:我最终无法使用这种方法来解决问题,因为要处理的对象数量比预期的要大得多(几百万个),但是我认为分享发现的内容仍然有用。
翻译自: https://www.javacodegeeks.com/2016/01/working-deltaspike-tutorial.html