Spring Boot / JPA:为什么 war 转换中断功能 and/or JPA 搜索?

Spring Boot / JPA: Why would war conversion break functionality and/or JPA searches?

我是运行一个基于社区版Broadleaf Commerce演示站点(辣酱)的应用。

为了试验 CI 和部署选项,我们已按照 broadleaf and spring 文档将库存 spring 引导包(jar)转换为 war。

转换后,应用程序将正常启动,但似乎某些数据库 (JPA) 交互已中断(搜索和数据库更新时出错)。

我有几个相关的问题:

  1. 当从 jar 转换为 war 时,initialize/access JPA(通过 Hibernate)资源是否需要额外的配置或注意事项?spring 文档中指定的内容以上?
  2. 运行 通过 Maven 目标 mvn spring-boot:run(成功)与 运行 java -jar <war-file> 之间有什么区别 (失败)?
  3. 特别是与我看到的关于 JPA 标准路径的错误(复制在下面)相关,我发现这个 SO post 表明它可能是一个实现问题。但回到问题 #2,如果是这样的话,如果是代码问题,为什么我 运行 应用程序的方式很重要?

可能相关的信息:

错误信息:

2018-11-08 17:46:22.578  INFO 17053 --- [nio-8443-exec-7] o.h.cache.internal.StandardQueryCache    : HHH000248: Starting query cache at region: query.Order

2018-11-08 17:46:22.820 ERROR 17053 --- [nio-8443-exec-7] .BroadleafSimpleMappingExceptionResolver : Error caught and handled.:05d420e6-69fc-4098-b085-2f8393a9ad7f

org.springframework.dao.InvalidDataAccessApiUsageException: Unable to resolve attribute [archiveStatus] against path; nested exception is java.lang.IllegalArgumentException: Unable to resolve attribute [archiveStatus] against path
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:384) ~[spring-orm-4.3.18.RELEASE.jar!/:4.3.18.RELEASE]
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246) ~[spring-orm-4.3.18.RELEASE.jar!/:4.3.18.RELEASE]
    ...
Caused by: java.lang.IllegalArgumentException: Unable to resolve attribute [archiveStatus] against path
    at org.hibernate.ejb.criteria.path.AbstractPathImpl.unknownAttribute(AbstractPathImpl.java:120) ~[hibernate-entitymanager-4.1.11.Final.jar!/:4.1.11.Final]
    at org.hibernate.ejb.criteria.path.AbstractPathImpl.locateAttribute(AbstractPathImpl.java:229) ~[hibernate-entitymanager-4.1.11.Final.jar!/:4.1.11.Final]
    at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:200) ~[hibernate-entitymanager-4.1.11.Final.jar!/:4.1.11.Final]
    at org.broadleafcommerce.core.search.dao.SearchFacetDaoImpl.readAllSearchFacets(SearchFacetDaoImpl.java:65) ~[broadleaf-framework-5.2.6-GA.jar!/:na]
    at org.broadleafcommerce.core.search.dao.SearchFacetDaoImpl$$FastClassBySpringCGLIB$73d5b3.invoke(<generated>) ~[broadleaf-framework-5.2.6-GA.jar!/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.18.RELEASE.jar!/:4.3.18.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:736) ~[spring-aop-4.3.18.RELEASE.jar!/:4.3.18.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.18.RELEASE.jar!/:4.3.18.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.18.RELEASE.jar!/:4.3.18.RELEASE]
    ... 150 common frames omitted

Spring 引导应用程序配置文件:

  @SpringBootApplication
  @EnableAutoConfiguration
  public class SiteApplication extends SpringBootServletInitializer {

    @Configuration
    @EnableBroadleafSiteAutoConfiguration
    public static class BroadleafFrameworkConfiguration {}

      @Override
      protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SiteApplication.class);
      }

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

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <groupId>org.broadleafcommerce</groupId>
    <artifactId>broadleaf-boot-starter-parent</artifactId>
    <version>5.2.6-GA</version>
  </parent>

  <artifactId>our-site</artifactId>
  <packaging>war</packaging>
  ... 
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <scope>provided</scope>
    </dependency>
    ...
  </dependencies>
  ...
</project>

存档状态class(根据错误消息)

@Embeddable
public class ArchiveStatus implements Serializable, SandBoxNonProductionSkip {

    @Column(name = "ARCHIVED")
    @AdminPresentation(friendlyName = "archived", visibility = VisibilityEnum.HIDDEN_ALL, group = "ArchiveStatus")
    protected Character archived = 'N';

    public Character getArchived() {
        return archived;
    }

    public void setArchived(Character archived) {
        this.archived = archived;
    }
}

产品Class(嵌入存档状态)

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@javax.persistence.Table(name = "BLC_PRODUCT")
//multi-column indexes don't appear to get exported correctly when declared at the field level, so declaring here as a workaround
@org.hibernate.annotations.Table(appliesTo = "BLC_PRODUCT", indexes = {
        @Index(name = "PRODUCT_URL_INDEX",
                columnNames = {"URL", "URL_KEY"}
        )
})
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "blProducts")
... (BLC-specific overrides ommitted) ...
public class ProductImpl implements Product, ProductAdminPresentation, Status, AdminMainEntity, Locatable, TemplatePathContainer {
    ...
    @Embedded
    protected ArchiveStatus archiveStatus = new ArchiveStatus();
    ...
}

DAO 方法:

    public List<SearchFacet> readAllSearchFacets(FieldEntity entityType) {
        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<SearchFacet> criteria = builder.createQuery(SearchFacet.class);

        Root<SearchFacetImpl> facet = criteria.from(SearchFacetImpl.class);

        criteria.select(facet);

        Path<Character> archived = facet.get("archiveStatus").get("archived");

        criteria.where(
                builder.equal(facet.get("showOnSearch").as(Boolean.class), true),
                builder.or(builder.isNull(archived.as(String.class)),
                           builder.notEqual(archived.as(Character.class), 'Y')),
                facet.join("fieldType")
                        .join("indexField")
                        .join("field")
                        .get("entityType")
                        .as(String.class)
                        .in(entityType.getAllLookupTypes())
        );

        TypedQuery<SearchFacet> query = em.createQuery(criteria);
        query.setHint(QueryHints.HINT_CACHEABLE, true);
        query.setHint(QueryHints.HINT_CACHE_REGION, "query.Search");

        return query.getResultList();
    }

我在 运行 DemoSite 通过编译的 jarfile 时发现了同样的错误。

看看这个话题:https://github.com/BroadleafCommerce/DemoSite/issues/51

在我的例子中,我必须在站点的 pom.xml 中包含 spring-instrument 依赖项,并使用 -javaagent 参数指定其 jar 位置:

$ java -Xmx1024m -javaagent:./path/to/spring-instrument.jar -jar site.jar