java – JPA / Hibernate Embedded id
I want to do something like that:
>An object, reportingfile, can be a logrequest or logreport file (both have the same structure) > an object report contains a logrequest and a list of logreports with dates
I try to set an embeddedid, which will be a property of logrequest This is my problem I didn't get to mannage embedded ID( http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity -mapping-identifier)
If you have a clue about what I should do:)
An example (not working) would be:
@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; [...] }
In this example, I have the following errors:
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
Solution
OK, we'll see
You have an @ associationoverride annotation. Its API says:
and
In your question, I have neither seen @ manytoone nor @ onotooone So, you have a problem
But I saw the following logs in stacktrace
You have a composite primary key defined by the @ embeddedid annotation, which has two properties (filename and xmlcontent)
Now, let's take a look at your @ 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;
Its foreign key contains only one attribute, while your composite primary key contains two attributes Both must match It explains why you see the good news
Your correct mapping should look like this
@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;
More: I don't know if your target database supports CLOB as the primary key So remember it
Add to the original answer based on your reply
1 º first case (no @ 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; } }
Remember the following: Hibernate does not allow you to define two properties that share the same column Therefore, I must define insertable = false and updatable = false in the filename attribute
When I do the following
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
2 º first case (using @ 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",updatable=false)) private List<ReportingFile> reports; @Embeddable public static class ReportingFile implements Serializable { // You must set up insertable=false,updatable=false) private String fileName; @Column(name="xml_content") private Clob xmlContent; } }
When I do the following
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 will complain
Therefore, when you have @ collectionofelements that share a foreign key, it will not work as expected But you can do a solution
@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",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) private String fileName; @Column(name="xml_content") private Clob xmlContent; } }
Note that when used in classes you own, you can define the @ embeddable class as static