在FUSE EAP环境中使用Springboot自动配置连接到SQL Server时出现问题

时间:2019-06-25 15:55:29

标签: spring-boot sql-server-2012 redhat jbossfuse jboss-eap-7

在FUSE EAP环境中使用SpringBoot时,这可能对我来说是一个误解。我一直在尝试部署服务,该服务是根据RedHat文档以及我在网上找到的混合了Camel和SpringBoot的原型/示例而开发的,但无济于事。

据我了解,当创建到EAP Fuse服务器中已配置和测试的JNDI数据源的连接时,我可以使用application.properties或application.yml来让spring应用程序自动配置连接。就我而言,由于扩展JpaRepository的CRUD操作并不能真正满足需求,因此需要使用@PersistenceContext来调用EntityManager。

根据RedHat的文档,FUSE 7.2已安装在EAP 7.1中,并且POM使用的是org.jboss.redhat-fuse.fuse-springboot-bom版本7.2.0.fuse-720020-redhat-00001。

我尝试使用spring的自动配置,声明@Configuration类的手动配置,通过camel-context.xml文件声明数据库连接的手动配置以及其他一些次要测试。

根据我是否尝试通过使spring-boot-maven-plugin具有重新打包执行目标而生成的.jar或.jar.original,错误会有所不同,到目前为止,获得的错误是:

  • NullPointer,因为EntityManager em为null(.jar.original)
  • java.lang.NoClassDefFoundError:org / springframework / boot / orm / jpa / EntityManagerFactoryBuilder(.jar.original,当对数据源进行手动配置时,可以在带有@Configuration注释的Java类中,也可以在骆驼中使用Spring DSL的context.xml)
  • java.lang.ClassNotFoundException:com.example.dao.genericDAOImpl(带有所有依赖项的.jar包)

这是我的程序的片段,其中包括POM,Application.java和试图获取EntityManager的组件,如果不够/不清楚,将乐意提供更多片段。

POM.xml

...
    <properties>
        <fuse.version>7.2.0.fuse-720020-redhat-00001</fuse.version>
        ...
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    </dependencies>
...
<build>
        <defaultGoal>spring-boot:run</defaultGoal>
        <plugins>
            ...
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.5.16.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            ...
        </plugins>
    </build>
...

application.properties

spring.datasource.jndi-name=jdbc:sqlserver://ip:1433;DatabaseName=dbname
spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.generate-ddl=false

Application.java

@ImportResource({"classpath:spring/camel-context.xml"})
@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

camel-context.xml

<beans ...>
...
    <camelContext id="identidades_financieras" xmlns="http://camel.apache.org/schema/spring">
        <onException>
            <exception>java.lang.Exception</exception>
            <handled>
                <constant>true</constant>
            </handled>
            <setHeader headerName="Exchange.HTTP_RESPONSE_CODE">
                <constant>500</constant>
            </setHeader>
            <setBody>
                <simple>${exception.message}</simple>
            </setBody>
        </onException>
        <restConfiguration apiContextPath="/openapi.json"
            bindingMode="json" component="undertow"
            contextPath="/restservice/api_v1" enableCORS="true">
            <dataFormatProperty key="prettyPrint" value="true"/>
        </restConfiguration>
        <rest enableCORS="true" id="rest-for-openapi-document" path="/openapi">
            <get id="openapi.json" produces="application/json" uri="openapi.json">
                <description>Gets the OpenAPI document for this service</description>
                <route id="route-for-openapi-document">
                    <setHeader headerName="Exchange.CONTENT_TYPE" id="setHeader-for-openapi-document">
                        <constant>application/vnd.oai.openapi+json</constant>
                    </setHeader>
                    <setBody id="setBody-for-openapi-document">
                        <constant>resource:classpath:openapi.json</constant>
                    </setBody>
                </route>
            </get>
        </rest>
        <rest bindingMode="auto" enableCORS="true"
            id="rest-b5d099c1-1996-458b-b5db-34aadc57a548" path="/">
            <get id="customPaginatexxxVO" produces="application/json" uri="/xxx">
                <to uri="direct:customPaginatexxxVO"/>
            </get>
...

        <route id="route-28f4489d-b354-401b-b774-6425bec1c120">
            <from id="from-17c4205f-8d28-4d3d-a265-cb1c38c9bc32" uri="direct:customPaginatexxxVO"/>
            <log id="customPaginatexxxVO-log-1" message="headers ====> pageSize: ${header.pageSize} - pageNumber: ${header.pageNumber}"/>
            <bean id="to-ee6565efaf-de46-4941-b119-be7aaa07d892"
                method="paginate" ref="genericService"/>
            <log id="customPaginatexxxVO-log-2" message="${body}"/>
        </route>
<beans/>

genericService.java

@Service
public class genericServiceImpl implements genericService {

    @Autowired
    private genericDAO dao;
    ...
    @Override
    public xxxVO paginate(Map<String, Object> reqHeaders) {
        ... pageProps are defined using reqHeaders ...
        xxxVO paginated = dao.customPagination(pageProps);
        return paginated;
    }
    ...
}

genericDAOImpl.java,当调用有关em的任何内容时都会出错。

@Repository
public class genericDAOImpl implements genericDAO {

    @PersistenceContext //when manually configured, I've added the (unitName="") in reference to the persistence unit, from my understanding, since only one datasource was created, this should pick up by default
    private EntityManager em;
...
    @Override
    public xxxVO customPagination(paginateProps pageProps) {
        xxxVO result = null;
        try {
            CriteriaBuilder paginationBuilder = em.getCriteriaBuilder();
            CriteriaQuery<T> paginationQuery = paginationBuilder.createQuery(entity.class);
            Root<T> entityClass = paginationQuery.from(entity.class);
            paginationQuery.select(entityClass);
            ... some settings with pageProps ...
            TypedQuery<T> query = em.createQuery(paginationQuery);
            entityList = query.getResultList();
            ... entityList is transformed to xxxVO ...
        } catch (Exception e) {
            LOG.error("caught something");
            e.printStackTrace();
        }
        return result;
    }   
...

如前所述,根据我尝试过的选项,我遇到了许多不同的错误,并且大多数错误显然归因于配置错误或未正确部署,在谈到SpringBoot时,我还是没有经验和骆驼(Camel),以及我在互联网上阅读的不同内容引起了一些混乱。为了确保,分页方法虽然很简单,但如果它的EntityManager没有为空,则应该可以使用。

以下是一些日志:

根据我所做的测试,在解析.jar(具有所有依赖关系的胖jar)时,可以使用java -jar进行正确部署,但不能在保险丝eap服务中进行部署

09:16:01,937 WARN  [org.springframework.context.support.GenericApplicationContext] (MSC service thread 1-3) Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.example.dao.genericDAOImpl] for bean with name 'genericDAO' defined in URL [vfs:/content/identidades_financieras-1.0-SNAPSHOT.jar/BOOT-INF/classes/spring/camel-context.xml]; nested exception is java.lang.ClassNotFoundException: com.example.dao.genericDAOImpl from [Module "deployment.identidades_financieras-1.0-SNAPSHOT.jar" from Service Module Loader]
09:16:01,940 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-3) MSC000001: Failed to start service jboss.deployment.unit."identidades_financieras-1.0-SNAPSHOT.jar".CamelContextActivationService."identidades_financieras-1.0-SNAPSHOT.jar": org.jboss.msc.service.StartException in service jboss.deployment.unit."identidades_financieras-1.0-SNAPSHOT.jar".CamelContextActivationService."identidades_financieras-1.0-SNAPSHOT.jar": Cannot create camel context: identidades_financieras-1.0-SNAPSHOT.jar
    at org.wildfly.extension.camel.service.CamelContextActivationService.start(CamelContextActivationService.java:71)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:2032)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1955)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.example.dao.genericDAOImpl] for bean with name 'genericDAO' defined in URL [vfs:/content/identidades_financieras-1.0-SNAPSHOT.jar/BOOT-INF/classes/spring/camel-context.xml]; nested exception is java.lang.ClassNotFoundException: com.example.dao.genericDAO from [Module "deployment.identidades_financieras-1.0-SNAPSHOT.jar" from Service Module Loader]
...

使用手动配置的DataSource和EntityManagerFactory部署.jar.original(基本上只是Java)时。据我了解,该服务期望org.springframework.boot依赖项存在于服务器上。检查模块后,保险丝层中没有org.springframework.boot模块。这是故意的吗?

09:50:17,265 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-8) MSC000001: Failed to start service jboss.deployment.unit."identidades_financieras-1.0-SNAPSHOT.jar".CamelContextActivationService."identidades_financieras-1.0-SNAPSHOT.jar": org.jboss.msc.service.StartException in service jboss.deployment.unit."identidades_financieras-1.0-SNAPSHOT.jar".CamelContextActivationService."identidades_financieras-1.0-SNAPSHOT.jar": Failed to start service
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1978)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/orm/jpa/EntityManagerFactoryBuilder
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.getDeclaredMethods(Class.java:1975)
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:613)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:524)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:510)
    at org.springframework.util.ReflectionUtils.getUniqueDeclaredMethods(ReflectionUtils.java:570)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:697)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:640)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:609)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1490)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:425)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:395)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525)
    at org.wildfly.extension.camel.SpringCamelContextBootstrap$1.run(SpringCamelContextBootstrap.java:90)
    at org.wildfly.extension.camel.proxy.ProxyUtils$1.invoke(ProxyUtils.java:51)
    at com.sun.proxy.$Proxy68.run(Unknown Source)
    at org.wildfly.extension.camel.proxy.ProxyUtils.invokeProxied(ProxyUtils.java:55)
    at org.wildfly.extension.camel.SpringCamelContextBootstrap.createSpringCamelContexts(SpringCamelContextBootstrap.java:87)
    at org.wildfly.extension.camel.service.CamelContextActivationService.start(CamelContextActivationService.java:58)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:2032)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1955)
    ... 3 more
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder from [Module "deployment.identidades_financieras-1.0-SNAPSHOT.jar" from Service Module Loader]
    at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:198)
    at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:412)
    at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:400)
    at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
    ... 27 more

最后,当仅使用Spring自动配置上传.jar.original时,EM为空,使用邮递员,当我使用REST时,状态为500,“无响应”

java.lang.NullPointerException
at com.example.dao.genericDAOImpl.customPagination(GenericDAOImpl.java:252)

该行引用了CriteriaBuilder paginationBuilder = em.getCriteriaBuilder()或调用EM方法的任何其他地方。

感谢您的宝贵时间!任何评论表示赞赏...

2 个答案:

答案 0 :(得分:2)

不支持使用Fuse EAP和Camel子系统的Spring Boot。因此,为什么在保险丝模块层中看不到任何org.springframework.boot依赖项。

如果要将Camel Spring Boot应用程序部署到EAP中,最好禁用Camel子系统进行部署,或者避免完全安装子系统。

答案 1 :(得分:0)

这绝不是解决我遇到的问题的方法,我认为这只是我代码上的一个临时补丁,因为7.4版本的Fuse据说将支持SpringBoot 2.1.x或类似的东西,但是这样做以下内容使我能够创建数据库连接并继续我的生活。除非被告知这是唯一的方法,否则我不会将其标记为可接受的答案。

在Application.java中,我直接禁用了SpringBootServletInitializer。完全公开了,我直截了当地不知道这样做会对应用程序产生什么影响,但是在我尝试部署时,依赖性令人不安。

@ImportResource({"classpath:spring/camel-context.xml"})
@SpringBootApplication
public class Application {//extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

我创建了一个persistence.xml文件,在其中配置了持久性单元的名称,并选择了包含实体的包(或列出了它们,两者都可以使用)。

在camel-context.xml中,我在标记之前声明了以下内容

    <bean class="org.apache.camel.component.jpa.JpaComponent" id="jpa">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
        <property name="transactionManager" ref="jpaTxManager"/>
    </bean>
    <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="jpaTxManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <bean
        class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" id="entityManagerFactory">
        <property name="persistenceUnitName" value="PERSISTENCE UNIT NAME IN PERSISTENCE.XML"/>
    </bean>
    <bean class="org.apache.camel.spring.spi.SpringTransactionPolicy" id="requiredPolicy">
        <property name="transactionManager" ref="jpaTxManager"/>
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
    </bean>

我创建了一个负责创建EntityManager的Java类,该类是@Stateless(EJB),并且使与持久性单元的连接保持静态非常重要。

@Stateless
public class persistenceUnitEntityManagerImpl implements IfEntityManager{


    private static EntityManager em;

    static {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("PERSISTENCE UNIT NAME");
        em = entityManagerFactory.createEntityManager();
    }

    public void setEntityManager( EntityManager em ) {
        persistenceUnitEntityManagerImpl.em = em;
    }

    public EntityManager getEntityManager() {
        return persistenceUnitEntityManagerImpl.em;
    }
}

在需要数据库连接的Bean中,以我为例,使用@Component(在@Repository中应该也一样),我添加了以下内容:

    private IfEntityManagerImpl IfEntityManager;

    @PostConstruct
    public void init() {
         this.persistenceUnitEntityManagerImpl = new persistenceUnitEntityManagerImpl();
     }

每当需要调用EntityManager时,我都可以使用persistenceUnitEntityManagerImpl.getEntityManager()

只要确保该组件不会创建新的连接/实体管理器/其他内容,就可以将LOG添加到@PostConstruct初始化中,如果您的bean是单例的(我相信默认情况下应该是)永远不会获得该日志或打印行。