【问题标题】:Can an @Embeddable class be private?@Embeddable 类可以是私有的吗?
【发布时间】:2011-08-24 20:37:12
【问题描述】:

JPA 规范中是否有任何内容描述了有效的@Embeddable 类可以是什么?我已经看过但找不到任何东西。

我正在使用带有 Hibernate (3.6.4.Final) 和 Spring (3.0.5) 的 EclipseLink (2.3.0-M7 - 完整构建字符串 2.3.0.v20110429-r9282) 并按照文档中的说明设置了我的应用程序EclipseLink/Examples/MOXy/Spring/JAXBAnnotations。其他一切目前都有效,并且已经持续了几个月。我正在添加一个 @Embeddable 类并开始获取 NPE。

这里有一些示例代码,如果是 EclipseLink 的错误,我希望有人可以使用它来重现此代码,因为我删除了 EclipseLink 库和配置(并恢复为 Hibernate 的实现),然后我不再获得 NPE。

测试类Broken.java

package x.y.z.model;

import javax.persistence.*;
import javax.xml.bind.annotation.*;
import java.io.Serializable;

@Entity
public class Broken {
    @EmbeddedId
    private Pk pk = new Pk();

    @Embeddable 
    private static class Pk implements Serializable {
        @ManyToOne
        private String foo;

        public String getFoo() {
            return this.foo;
        }
        public void setFoo(String foo) {
            this.foo = foo;
        }
    }
}

我的Spring bean配置是:

<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
  <property name="contextPath" value="x.y.z.model"/>
</bean>
<bean id="xmlHelper" class="x.y.xml.XMLHelper">
  <property name="marshaller" ref="jaxbMarshaller"/>
</bean>

(请参阅上面的XMLHelper 类链接)

最后我将以下内容添加到 jaxb.in​​dex

Broken

启动 Spring 应用程序时,出现以下异常:

ERROR org.springframework.test.context.TestContextManager 324 - Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@9b601d] to prepare test instance [x.y.z.service.AnyTest@198dc19]
java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:308)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:220)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:301)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:303)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
    at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:35)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:115)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:97)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.maven.surefire.booter.ProviderFactory$ClassLoaderProxy.invoke(ProviderFactory.java:103)
    at $Proxy0.invoke(Unknown Source)
    at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:150)
    at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcess(SurefireStarter.java:91)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:69)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jaxbMarshaller' defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is org.springframework.oxm.UncategorizedMappingException: Unknown JAXB exception; nested exception is
javax.xml.bind.JAXBException
 - with linked exception:
[java.lang.NullPointerException]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:84)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:280)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:304)
    ... 31 more
Caused by: org.springframework.oxm.UncategorizedMappingException: Unknown JAXB exception; nested exception is javax.xml.bind.JAXBException
 - with linked exception:
[java.lang.NullPointerException]
    at org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:668)
    at org.springframework.oxm.jaxb.Jaxb2Marshaller.getJaxbContext(Jaxb2Marshaller.java:335)
    at org.springframework.oxm.jaxb.Jaxb2Marshaller.afterPropertiesSet(Jaxb2Marshaller.java:317)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
    ... 44 more
Caused by: javax.xml.bind.JAXBException
 - with linked exception:
[java.lang.NullPointerException]
    at org.eclipse.persistence.jaxb.JAXBContext$ContextPathInput.createContextState(JAXBContext.java:661)
    at org.eclipse.persistence.jaxb.JAXBContext$ContextPathInput.createContextState(JAXBContext.java:621)
    at org.eclipse.persistence.jaxb.JAXBContext.<init>(JAXBContext.java:134)
    at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:108)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:128)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:249)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:372)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:337)
    at org.springframework.oxm.jaxb.Jaxb2Marshaller.createJaxbContextFromContextPath(Jaxb2Marshaller.java:355)
    at org.springframework.oxm.jaxb.Jaxb2Marshaller.getJaxbContext(Jaxb2Marshaller.java:328)
    ... 47 more
Caused by: java.lang.NullPointerException
    at org.eclipse.persistence.jaxb.javamodel.Helper.isBuiltInJavaType(Helper.java:261)
    at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.shouldGenerateTypeInfo(AnnotationsProcessor.java:1526)
    at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.processClass(AnnotationsProcessor.java:1029)
    at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.processAdditionalClasses(AnnotationsProcessor.java:994)
    at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.postBuildTypeInfo(AnnotationsProcessor.java:576)
    at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.processClassesAndProperties(AnnotationsProcessor.java:230)
    at org.eclipse.persistence.jaxb.compiler.Generator.<init>(Generator.java:104)
    at org.eclipse.persistence.jaxb.JAXBContext$ContextPathInput.createContextState(JAXBContext.java:658)
    ... 60 more

如果我将 @Embeddable 类从 private 更改为 public(并使用 EclipseLink),NPE 将不再发生。所以我有一个可行的解决方案,但想了解原因。

【问题讨论】:

    标签: java hibernate spring jpa eclipselink


    【解决方案1】:

    JPA 2.0 Specification 声明可嵌入类必须遵守为实体规定的相同要求。此类要求在规范的第 2.1 节中进行了说明,并声明:

    • 实体类必须有一个无参数的构造函数。它也可能有其他构造函数。无参数构造函数必须是公共的或受保护的。
    • 实体类必须是顶级类。枚举或接口不得 指定为一个实体。
    • 实体类不能是最终的。实体类的任何方法或持久实例变量都不能是最终的。

    因此,我认为影响您的可能是可嵌入类不是顶级类,而且它是私有的。

    您不妨试一试。即使不是这个原因,您也可以考虑遵循该标准的建议,否则,您无法保证您的代码符合该标准。

    【讨论】:

    • +1 谢谢!我认为您可能对 top-level class 要求有答案,因为 top-level classes 不能是 private。我将尝试使用一些不同的 JPA 2.0 供应商,看看他们如何处理这个问题。我绝对同意遵守标准。我实际上不确定为什么我首先声明了 private 类!
    【解决方案2】:

    这可能更依赖于底层 impl 如何加载 Embeddable 类而不是其他任何东西。查看 EclipseLink 2.0.0 的代码(找不到您的版本的 src),它表明 NPE 来自有关该类的元数据。

    如果我必须做出有根据的猜测,我会说 EclipseLink 默默地失败了类加载(后来导致 NPE),而 Hibernate 使用另一种机制来加载类(通过字节码编织或其他方式)。

    【讨论】:

    • +1 谢谢!那么,理论上你同意这是 EclipseLink 中的一个错误吗?
    • 根据edarlozo的回答,要求类必须是顶级的,所以唯一的“bug”就是EclipseLink的报错。
    猜你喜欢
    • 2011-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-12
    • 2016-01-02
    • 1970-01-01
    • 2016-05-22
    相关资源
    最近更新 更多