【问题标题】:JPA/Hibernate Embedded idJPA/Hibernate 嵌入式 id
【发布时间】:2010-03-15 15:45:34
【问题描述】:

我想做这样的事情:

  • 可以是 LogRequest 或 LogReport 文件的对象 ReportingFile。 (两者的结构相同)
  • 一个对象报告包含一个 logRequest,一个带有日期的 logReport 列表。

我尝试设置一个 EmbededId,这将是 logRequest 的一个属性。这就是我遇到的问题。我不是来管理嵌入式 id 的。 (http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-identifier)

如果你知道我应该怎么做:)

一个例子(不工作)是:

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

    [...]
}

在这个示例中,我有以下错误:

    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

【问题讨论】:

    标签: java hibernate jpa jakarta-ee


    【解决方案1】:

    好吧,让我们看看

    你有一个@AssociationOverride 注释,它的API 说:

    此注释用于覆盖实体关系的属性或字段的多对一或一对一映射。

    它可以应用于扩展映射超类的实体以覆盖由映射超类定义的多对一或一对一映射

    在您的问题中,我既没有看到@ManyToOne 也没有看到@OnoToOne。所以,你的问题有问题。

    但我在您的 StackTrace 中看到了以下日志

    从 Reporting 引用 Reporting 的外键列数错误。应该是 2

    您有一个复合主键,由 @EmbeddedId 注释定义,该注释具有两个属性(fileName 和 xmlContent)

    现在,让我们看看你的@CollectionOfElements

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

    它的外键只包含一个属性,而您的复合主键包含两个属性两者必须匹配。它解释了为什么您会看到这条好消息

    从 Reporting 引用 Reporting 的外键列数错误。应该是 2

    你的正确映射应该是这样的

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

    还有更多:我不知道您的目标数据库是否支持 Clob 作为主键。所以请记住。

    根据您的回复添加到原始答案中

    第一个场景(没有 @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;
    
       }
    
    }
    

    请记住以下几点:Hibernate 不允许您定义两个共享同一列的属性。因此,我必须在 fileName 属性中定义 insertable=false, updatable=false。

    当我执行以下操作时

    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
    

    第一个场景(with @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",
            joinColumns=@JoinColumn(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;
    
       }
    
    }    
    

    当我执行以下操作时

    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 会抱怨

    集合映射中的重复列:报告列:文件名

    因此,当您有一个共享外键的@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;
    
        // Your workaround goes here
        @CollectionOfElements(fetch=FetchType.EAGER)
        @JoinTable(
            name="t_reports",
            joinColumns=@JoinColumn(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;
    
       }
    
    }    
    

    请注意,在您拥有的类中使用时,您可以将 @Embeddable 类定义为静态。

    【讨论】:

    • 好的,谢谢!事实是,我只想将 fileName 作为主键,就像你说的那样,以避免使用 Clob 作为主键。但我不知道如何向 EmbeddedId(如果可能的话)指定它应该只采用 ReportingFile 的 fileName 属性。这就是我试图用@AssociationOverride 做的事情。 (您突出显示的错误)这也是为什么在我的@CollectionOfElements 中,我只想有一个参数。
    • 所以,我改变了(添加@OneToOne):@OneToOne @Column(name="file_name",length=255) private String fileName;为了尊重 API。我有以下错误: 原因:org.hibernate.AnnotationException:非法尝试使用 mappedBy 关联定义 @JoinColumn:logRequest.fileName 似乎不是只为 PK 选择一个参数的方法。
    • 对不起,也许我不是很清楚;)我只希望文件名(logRequest.fileName)作为报告的 PK,但我仍然想要整个 ReportingFile 对象(更多私有列表报告;)。根据您的建议,我将丢失文件的“内容”。如果我添加了 @Column(name="xml_content") private Clob xmlContent;到您提供的报告类,没关系,但我不会为此使用“ReportingFile”对象(这正是一个文件名+内容)。
    • 非常感谢!它比预期的要复杂:D
    猜你喜欢
    • 2011-09-29
    • 2010-11-10
    • 2014-05-04
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多