简体   繁体   中英

Hibernate @OneToOne self join with OR in join clause

I have a @OneToOne annotation which I want to join on 2 possible columns. I know how to do it with a plain SQL query, but I have no idea how this could work with hibernate annotations.

Following entity:

@Entity
public class Foo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @OneToOne
    @JoinColumn(name = "cancelRecord")
    private Foo cancelRecord;

    private String externalId;
    private String externalCancellationId;
}

and the corresponding database table:

create table Foo(
    id int identity(1,1) not null primary key, --primary key
    cancelRecord int,
    externalId varchar(100),
    externalCancellationId varchar(100)
)

So far cancelRecord is only joined on that column. Now I want it to join on either cancelRecord or externalCancellationId . A valid SQL query for that would be:

SELECT f.* 
FROM Foo f 
        INNER JOIN Foo fo 
        ON (f.cancelRecord = fo.id 
        OR f.externalCancellationId = fo.externalId)

The crucial part about this query is the OR in the join clause (f.cancelRecord = fo.id **OR** f.externalCancellationId = fo.externalId) .

I assume that this is not possible with @JoinColumn and I'd need to rely on @JoinFormula in some way.

Is that assumption right? If so, would I simply need to copy the above query as @JoinFormula ?


I tried to use this @JoinFoluma :

@OneToOne
@JoinFormula(value = "SELECT f.* 
        FROM Foo f 
        INNER JOIN Foo fo 
        ON (f.cancelRecord = fo.id 
        OR f.externalCancellationId = fo.externalId)")
private Foo cancelRecord;

This causes an NullPointerException when loading the spring context:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [spring/test-database.xml]: Invocation of init method failed; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1589)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:554)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
    ... 43 common frames omitted
Caused by: java.lang.NullPointerException: null
    at org.hibernate.cfg.AnnotationBinder.bindOneToOne(AnnotationBinder.java:3185)
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1798)
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:961)
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:788)
    at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:250)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:231)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:274)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:84)
    at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:474)
    at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:85)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:724)
    at org.springframework.orm.hibernate5.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:511)
    at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:495)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1648)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1585)
    ... 50 common frames omitted

EDIT:

Exception resolved with this question , but sadly this again ends with an AND instead of an OR .

That will not work since the @JoinColumn or @JoinFormula are used to determine Foreign Key column value, so they don't work with multiple columns.

If you define multiple @JoinColumn or @JoinFormula , then the ON relationship will use an AND not an OR .

In your case, since you probably only want to read this association, as opposed to setting it using Hibernate, then you are better off using an SQL query instead.

You can use more than one @JoinColumn annotation. See an example below:

   @OneToOne
   @JoinColumns(
     {
       @JoinColumn(updatable=false,insertable=false, name="cancelRecord", referencedColumnName="cancelRecord"),
       @JoinColumn(updatable=false,insertable=false, name="other", referencedColumnName="other")
     }
   )
   private Foo cancelRecord;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM