2011-05-13 22 views
5

¿Hay algo en la especificación JPA que describa qué clase de @Embeddable válida puede ser? He buscado pero no puedo encontrar nada.¿Puede una clase @Embeddable ser privada?

Estoy usando EclipseLink (2.3.0-M7 - cadena completa de compilación 2.3.0.v20110429-r9282) con Hibernate (3.6.4.Final) y Spring (3.0.5) y he configurado mi aplicación como documentada en EclipseLink/Examples/MOXy/Spring/JAXBAnnotations. Todo lo demás funciona actualmente y lo ha sido durante meses. Estaba agregando una clase @Embeddable y comencé a obtener un NPE.

Aquí hay un código de ejemplo que espero que alguien pueda usar para reproducir esto si es un error con EclipseLink porque elimino la biblioteca y la configuración de EclipseLink (y vuelvo a la implementación de Hibernate) y ya no obtengo el NPE.

clase de prueba 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; 
     } 
    } 
} 

La configuración de frijol de primavera que tengo es:

<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> 

(ver arriba enlace para XMLHelper clase)

Finalmente añadir el siguiente a jaxb.index:

Broken 

Al iniciar la aplicación de primavera, me sale el siguiente excepción:

ERROR org.springframework.test.context.TestContextManager 324 - Caught exception while allowing TestExecutionListener [org.springframewor[email protected]9b601d] to prepare test instance [[email protected]] 
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 

Si cambio de la clase @Embeddable del público (y utilizar EclipseLink), la NPE ya no privada a ocurre. Entonces tengo una solución que funciona pero quiero entender por qué.

+0

Un buen hallazgo de hecho. +1 –

Respuesta

3

El JPA 2.0 Specification establece que las clases incrustables deben cumplir los mismos requisitos establecidos para las entidades. Tales requisitos se indican en el apartado 2.1 de la especificación, y afirmar que:

  • clase La entidad debe tener un constructor sin argumentos. Puede tener otros constructores también. El constructor no-arg debe ser público o estar protegido.
  • La clase de entidad debe ser una clase de nivel superior. Una enumeración o interfaz no debe ser designada como una entidad.
  • La clase de entidad no debe ser definitiva. Ningún método o variable de instancia persistente de la clase de entidad puede ser final.

Por lo tanto, yo creo que lo que está afectando a usted podría ser que la clase incrustada no es una clase de nivel superior, más el hecho de que es privado.

Puede que le guste probarlo. Incluso si esta no es la razón, le recomendamos que siga las recomendaciones de la norma; de lo contrario, no puede garantizar que su código cumpla con ella.

+0

+1 ¡Gracias! Creo que es posible que tengas la respuesta con el requisito _ de nivel superior ya que las clases de nivel superior no pueden ser _privadas_. Voy a probar con algunos proveedores diferentes de JPA 2.0 y ver cómo lo manejan. Definitivamente estoy de acuerdo con mantener el estándar. ¡No estoy seguro de por qué declaro la clase _private_ en primer lugar! – andyb

1

Esto es probablemente más dependiente de cómo la impl subyacente carga la clase Embeddable que cualquier otra cosa. Buscando el código para EclipseLink 2.0.0 (no se pudo encontrar el src para su versión), sugiere que el NPE es de metadatos sobre la clase.

Si tuviera que adivinar, diría que EclipseLink está fallando silenciosamente en la carga de clases (que luego causa el NPE), mientras que Hibernate usa otro mecanismo para cargar la clase (a través de bytecode weaving o lo que sea).

+0

+1 ¡Gracias! Entonces, en teoría, ¿estás de acuerdo en que es un error en EclipseLink? – andyb

+0

Según la respuesta de edarlozo, es un requisito que la clase debe ser de nivel superior, por lo que el único "error" es el informe de error de EclipseLink. –

Cuestiones relacionadas