2010-03-15 45 views
6

me gustaría hacer algo así:JPA/Hibernate Identificación Embedded

  • Un ReportingFile objeto que puede ser un LogRequest o un archivo LogReport. (ambos obtuvieron la misma estructura)
  • Un objeto Informes que contienen para un logRequest, una lista de logReport con una fecha.

Intenté establecer un EmbededId, que sería un atributo de logRequest. Y ese es el problema que tengo. No llego a mannage id integrado. (http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-identifier)

Si usted tiene una pista sobre la forma que debería hacerlo :)

Un ejemplo (no de trabajo) sería:

@Entity 
@AssociationOverride(name="logRequest.fileName", joinColumns = { @JoinColumn(name="log_request_file_name") }) 
public class Reporting { 

    @EmbeddedId 
    private ReportingFile logRequest; 

    @CollectionOfElements(fetch = FetchType.EAGER) 
    @JoinTable(name = "t_reports", schema="", joinColumns = {@JoinColumn(name = "log_report")}) 
    @Fetch(FetchMode.SELECT) 
    private List<ReportingFile> reports; 

    @Column(name="generated_date",nullable=true) 
    private Date generatedDate; 

    [...] 
} 

@Embeddable 
public class ReportingFile { 

    @Column(name="file_name",length=255) 
    private String fileName; 

    @Column(name="xml_content") 
    private Clob xmlContent; 

    [...] 
} 

En esta muestra, tengo una el siguiente error :

15.03.2010 16:37:59 [ERROR] org.springframework.web.context.ContextLoader  Context initialization failed 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0' defined in class path resource [config/persistenceContext.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [config/persistenceContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: test] Unable to configure EntityManagerFactory 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:480) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380) 
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264) 
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164) 
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:881) 
    at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:597) 
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:366) 
    at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255) 
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199) 
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45) 
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843) 
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4350) 
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) 
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:719) 
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) 
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443) 
    at org.apache.catalina.core.StandardService.start(StandardService.java:516) 
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:710) 
    at org.apache.catalina.startup.Catalina.start(Catalina.java:578) 
    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.catalina.startup.Bootstrap.start(Bootstrap.java:288) 
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413) 
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [config/persistenceContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: test] Unable to configure EntityManagerFactory 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1337) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380) 
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264) 
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:308) 
    at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:270) 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:122) 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.<init>(PersistenceExceptionTranslationInterceptor.java:78) 
    at org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisor.<init>(PersistenceExceptionTranslationAdvisor.java:70) 
    at org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor.setBeanFactory(PersistenceExceptionTranslationPostProcessor.java:97) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1325) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473) 
    ... 29 more 
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: test] Unable to configure EntityManagerFactory 
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:265) 
    at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:125) 
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83) 
    at org.springframework.orm.jpa.LocalEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalEntityManagerFactoryBean.java:91) 
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:291) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1368) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1334) 
    ... 46 more 
Caused by: org.hibernate.AnnotationException: A Foreign key refering Reporting from Reporting has the wrong number of column. should be 2 
    at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:272) 
    at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1319) 
    at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1158) 
    at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:600) 
    at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:541) 
    at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:43) 
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1140) 
    at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:319) 
    at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1125) 
    at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1226) 
    at org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:159) 
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:854) 
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:191) 
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:253) 
    ... 52 more 

Respuesta

12

Bueno, vamos a ver

Usted tiene una una @AssociationOverride notación cual su API dice:

This annotation is used to override a many-to-one or one-to-one mapping of property or field for an entity relationship.

Y

It may be applied to an entity that extends a mapped superclass to override a many-to-one or one-to-one mapping defined by the mapped superclass

no he visto ni @ManyToOne ni @OnoToOne en su pregunta. Entonces, hay algo mal con tu pregunta.

pero he visto el siguiente registro en su StackTrace

A Foreign key refering Reporting from Reporting has the wrong number of column. should be 2

Tiene una clave primaria compuesta definido por una anotación @EmbeddedId que tiene dos propiedades (nombre de archivo y xmlcontent)

Ahora, vamos a ver sus @CollectionOfElements

@CollectionOfElements(fetch=FetchType.EAGER) 
@JoinTable(name="t_reports", 
    // Your mistake goes here 
    [email protected](name="log_report")) 
@Fetch(FetchMode.SELECT) 
private List<ReportingFile> reports; 

su clave externa solo contiene una propiedad mientras que la clave principal compuesta contiene dos propiedades. Ambos deben coincidir con. Esto explica por qué aparece este mensaje

A Foreign key refering Reporting from Reporting has the wrong number of column. should be 2

Su asignación correcta debería parecerse este

@CollectionOfElements(fetch=FetchType.EAGER) 
@JoinTable(name="t_reports", 
    joinColumns={ 
     // The number of columns defined by our Compound primary key (2) 
     // must match the number of columns defined by its Foreign key (2) 
     @JoinColumn(name="file_name", referencedColumnName="file_name"), 
     @JoinColumn(name="xml_content", referencedColumnName="xml_content")}) 
@Fetch(FetchMode.SELECT) 
private List<ReportingFile> reports; 

Y más: no sé si su base de datos de destino admite un CLOB como clave primaria. Así que tenlo en cuenta.

añadido a la respuesta original de acuerdo con su respuesta

primer escenario (sin @CollectionOfElements)

@Entity 
public class Reporting { 

    // You said you just want a single fileName as primary key 
    @Id 
    @Column(name="file_name", nullable=false) 
    private String fileName; 

    // You said you still want to have the whole ReportingFile object 
    @Embedded 
    private ReportingFile reportingFile; 

    @Embeddable 
    public static class ReportingFile implements Serializable { 

     // You must set up insertable=false, updatable=false 
     @Column(name="file_name", insertable=false, updatable=false) 
     private String fileName; 

     @Column(name="xml_content") 
     private Clob xmlContent; 

    } 

} 

tener en cuenta lo siguiente: hibernación no permite definir dos propiedades donde ambas comparten la misma columna. Por eso, tengo que definir insertable = false, updatable = false en la propiedad fileName.

y cuando lo haga la siguiente

Reporting reporting = new Reporting(); 
reporting.setFileName("home.pdf"); 

ReportingFile reportingFile = new ReportingFile(); 
// It will not persisted because of insertable=false, updatable=false 
reportingFile.setFileName("home.pdf"); 
reportingFile.setXmlContent(someClobObject); 

session.save(reporting); // It works 

primer escenario (con @CollectionOfElements)

@Entity 
public class Reporting { 

    // You said you just want a single fileName as primary key 
    @Id 
    @Column(name="file_name", nullable=false) 
    private String fileName; 

    // You said you still want to have the whole ReportingFile object 
    @Embedded 
    private ReportingFile reportingFile; 

    @CollectionOfElements(fetch=FetchType.EAGER) 
    @JoinTable(
     name="t_reports", 
     [email protected](name="file_name", referencedColumnName="file_name", insertable=false, updatable=false)) 
    private List<ReportingFile> reports; 

    @Embeddable 
    public static class ReportingFile implements Serializable { 

     // You must set up insertable=false, updatable=false 
     @Column(name="file_name", insertable=false, updatable=false) 
     private String fileName; 

     @Column(name="xml_content") 
     private Clob xmlContent; 

    } 

}  

y cuando lo haga la siguiente

Reporting reporting = new Reporting(); 
reporting.setFileName("home.pdf"); 

ReportingFile reportingFile = new ReportingFile(); 
// It will not persisted because of insertable=false, updatable=false 
reportingFile.setFileName("home.pdf"); 
reportingFile.setXmlContent(someClobObject); 

reporting.getReports().add(reportingFile); 

session.save(reporting); // It does not work 

Hibernate se quejan

repeated column in mapping for collection: reports column: file_name

Por lo tanto, cuando se tiene un @CollectionOfElements que comparte una clave externa, no funcionará como se espera. Pero se puede hacer una solución

@Entity 
public class Reporting { 

    // You said you just want a single fileName as primary key 
    @Id 
    @Column(name="file_name", nullable=false) 
    private String fileName; 

    // You said you still want to have the whole ReportingFile object 
    @Embedded 
    private ReportingFile reportingFile; 

    // Your workaround goes here 
    @CollectionOfElements(fetch=FetchType.EAGER) 
    @JoinTable(
     name="t_reports", 
     [email protected](name="file_name", referencedColumnName="file_name")) 
    private List<Clob> xmlContents; 

    @Transient 
    public List<ReportingFile> getReports() { 
     List<ReportingFile> reports = new ArrayList<ReportingFile>(); 

     for(Clob xmlContent: getXmlContents()) 
      reports.add(new ReportingFile(getFileName(), xmlContent)); 

     return reports; 
    } 

    @Embeddable 
    public static class ReportingFile implements Serializable { 

     // required no-arg constructor 
     public ReportingFile() {} 

     public ReportingFile(String fileName, Clob xmlContent) { 
      this.fileName = fileName; 
      this.xmlContent = xmlContent; 
     } 

     // You must set up insertable=false, updatable=false 
     @Column(name="file_name", insertable=false, updatable=false) 
     private String fileName; 

     @Column(name="xml_content") 
     private Clob xmlContent; 

    } 

}  

aviso Puede definir una clase @Embeddable como estática cuando se utiliza dentro de su clase propietaria.

+0

Ok gracias! El hecho es que quería tener solo fileName como clave principal, como dijiste, para evitar usar un Clob como clave principal. Pero no sé cómo especificar al EmbeddedId (y si es posible) que debería tomar solo el atributo fileName de ReportingFile. Eso es lo que traté de hacer con @AssociationOverride. (con el error que resaltó) Por eso también en @CollectionOfElements, quería tener solo un param. – RoD

+0

lo tanto, he cambiado que (@OneToOne adición): \t @OneToOne \t @Column (name = "nombre_archivo", longitud = 255) \t nomArchivo cadena privada; para respetar la API. Tengo el siguiente error: Causado por: org.hibernate.AnnotationException: Intento no válido para definir un @JoinColumn con una asociación mappedBy: logRequest.fileName Parece que no es la manera de elegir un único parámetro para el PK . – RoD

+0

Lo siento, tal vez no estaba muy claro;) Solo quiero el nombre de archivo (logRequest.fileName) como PK de los informes, pero aún quiero tener todo el objeto ReportingFile (más los informes privados List ). En lo que me sugiera, perderé el "contenido" de mi archivo. Si agregué @Column (name = "xml_content") privado Clob xmlContent; a la clase de Informes que proporcionó, estaría bien, pero no usaría el objeto "ReportingFile" para esto (que es exactamente un nombre de archivo + contenido). – RoD