Может ли класс @Embeddable быть закрытым? - PullRequest
5 голосов
/ 13 мая 2011

Есть ли в спецификации JPA что-либо, что описывает, каким может быть действительный класс @Embeddable?Я посмотрел, но ничего не могу найти.

Я использую EclipseLink (2.3.0-M7 - полная сборка строка 2.3.0.v20110429-r9282) с Hibernate (3.6.4.Final) и Spring (3.0.5) и настроил мое приложение, как описано в 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;
        }
    }
}

У меня есть конфигурация bean-компонента Spring:

<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.index :

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 больше не будетпроисходит.Итак, у меня есть решение, которое работает, но я хочу понять, почему.

Ответы [ 2 ]

3 голосов
/ 13 мая 2011

В спецификации JPA 2.0 указано, что встраиваемые классы должны соответствовать тем же требованиям, что и для сущностей. Такие требования изложены в разделе 2.1 спецификации, и они утверждают, что:

  • Класс сущности должен иметь конструктор без аргументов. У него могут быть и другие конструкторы. Конструктор без аргументов должен быть открытым или защищенным.
  • Класс сущности должен быть классом верхнего уровня . Перечисление или интерфейс не должны быть обозначен как субъект.
  • Класс сущности не должен быть окончательным. Никакие методы или постоянные переменные экземпляра класса сущностей не могут быть окончательными.

Таким образом, я считаю, что на вас может повлиять то, что встраиваемый класс не является классом верхнего уровня плюс тот факт, что он является закрытым.

Вы можете попробовать. Даже если это не является причиной, вам может потребоваться следовать рекомендациям стандарта, в противном случае вы не можете гарантировать, что ваш код соответствует ему.

1 голос
/ 13 мая 2011

Это, вероятно, больше зависит от того, как базовый impl загружает класс Embeddable, чем что-либо еще. Просматривая код EclipseLink 2.0.0 (не удалось найти src для вашей версии), можно предположить, что NPE взят из метаданных о классе.

Если бы мне пришлось сделать обоснованное предположение, я бы сказал, что EclipseLink молча прерывает загрузку класса (что позже вызывает NPE), в то время как Hibernate использует другой механизм для загрузки класса (через переплетение байт-кода или что-то еще).

...