2010-03-15 43 views
6

Tôi muốn làm điều gì đó như thế:JPA/Hibernate nhúng id

  • Một ReportingFile đối tượng đó có thể là một LogRequest hoặc một tập tin LogReport. (cả hai đều có cấu trúc tương tự)
  • Một báo cáo đối tượng có chứa một logRequest, một danh sách logReport với một ngày.

Tôi đã cố gắng đặt một EmbededId, đó sẽ là một thuộc tính của logRequest. Và đó là vấn đề tôi nhận được. Tôi không đến mannage id nhúng. (http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-identifier)

Nếu bạn có một đầu mối về làm thế nào tôi nên làm điều đó :)

Một ví dụ (không hoạt động) sẽ là:

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

    [...] 
} 

Trong ví dụ này, tôi có một lỗi sau :

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 

Trả lời

12

Vâng, chúng ta hãy xem

Bạn có một @AssociationOverride ký hiệu mà API nó nói:

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

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

Tôi đã không nhìn thấy không phải @ManyToOne cũng không @OnoToOne trong câu hỏi của bạn. Vì vậy, có gì đó sai với câu hỏi của bạn.

Nhưng tôi đã thấy các bản ghi sau trong StackTrace bạn

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

Bạn có một Compound chính chủ chốt xác định bởi một chú thích @EmbeddedId trong đó có hai thuộc tính (fileName và xmlContent)

Bây giờ, hãy xem @CollectionOfElements của bạn

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

Khóa ngoại của nó chỉ chứa một thuộc tính trong khi khóa chính Hợp chất của bạn chứa hai thuộc tính. Cả hai phải khớp với. Nó giải thích tại sao bạn thấy thông báo này đẹp

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

lập bản đồ đúng của bạn nên trông như thế này

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

Và hơn thế nữa: tôi không biết liệu cơ sở dữ liệu mục tiêu của bạn hỗ trợ một CLOB như khóa chính. Vì vậy, hãy ghi nhớ điều đó.

Đã thêm vào câu trả lời ban đầu theo trả lời của bạn

kịch bản đầu tiên (mà không @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; 

    } 

} 

Hãy ghi nhớ những điều sau đây: Hibernate không cho phép bạn xác định hai thuộc tính trong đó cả hai đều có cùng một cột. Do đó, tôi phải định nghĩa insertable = false, updatable = false trong thuộc tính fileName.

Và khi tôi làm như sau

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 

kịch bản đầu tiên (với @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; 

    } 

}  

Và khi tôi làm như sau

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 sẽ phàn nàn

repeated column in mapping for collection: reports column: file_name

Vì vậy, khi bạn có một @CollectionOfElements mà chia sẻ một chìa khóa nước ngoài, nó sẽ không hoạt động như mong đợi. Nhưng bạn có thể thực hiện một cách giải quyết

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

    } 

}  

Lưu ý rằng bạn có thể xác định lớp @Embeddable tĩnh khi sử dụng trong lớp của bạn.

+0

Ok cảm ơn bạn! Thực tế là, tôi muốn chỉ có tên tệp là khóa chính, như bạn đã nói, để tránh sử dụng Clob làm khóa chính. Nhưng tôi không biết cách chỉ định cho EmbeddedId (và nếu có thể), nó sẽ chỉ lấy thuộc tính tệpName của ReportingFile. Đó là những gì tôi đã cố gắng thực hiện với @AssociationOverride. (với sai lầm bạn đã đánh dấu) Đó cũng là lý do tại sao trong @CollectionOfElements của tôi, tôi chỉ muốn có một thông số. – RoD

+0

Vì vậy, tôi đã thay đổi điều đó (thêm @OneToOne): \t @OneToOne \t @Column (name = "file_name", length = 255) \t riêng String fileName; để tôn trọng API. Tôi có lỗi sau: Gây ra bởi: org.hibernate.AnnotationException: Cố gắng bất hợp pháp để xác định @JoinColumn với liên kết được ánh xạBảng: logRequest.fileName Có vẻ như đó không phải là cách để chọn chỉ một tham số cho PK . – RoD

+0

Xin lỗi, có lẽ tôi không thực sự rõ ràng;) Tôi chỉ muốn fileName (logRequest.fileName) là PK của Báo cáo, nhưng tôi vẫn muốn có toàn bộ đối tượng ReportingFile (nhiều báo cáo Danh sách riêng ); Trong những gì bạn đề xuất cho tôi, tôi sẽ mất "nội dung" của tệp của tôi. Nếu tôi đã thêm @Column (name = "xml_content") riêng tư Clob xmlContent; vào lớp Báo cáo bạn đã cung cấp, nó sẽ là ok, nhưng tôi sẽ không sử dụng đối tượng "ReportingFile" cho điều này (chính xác là một nội dung fileName +). – RoD