简体   繁体   中英

JPQL/HQL: select with OR fails

I have some problems with my query, but don't know where exactly.
This query return, for example, 3 Change objects:

select c from Change c where c.entityOne.serial = 'TEST3'

This query return, for example, 2 Change objects:

select c from Change c where c.entityTwo.serial = 'TEST3'

But this query return 0, when I expect something between 3 and 5.

select c from Change c where (c.entityOne.serial = 'TEST3' or c.entityTwo.serial = 'TEST3')

Where am I wrong? If you need some more code, please, ask.

TEST TO REPRODUCE:
Sorry for a lot of code, but it is required to reproduce this issue.

Change.java

@Entity
public class Change {
    private long id;
    private EntityOne entityOne;
    private EntityTwo entityTwo;

    @Id
    @GeneratedValue
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @ManyToOne
    public EntityOne getEntityOne() {
        return entityOne;
    }

    public void setEntityOne(EntityOne entityOne) {
        this.entityOne = entityOne;
    }

    @ManyToOne
    public EntityTwo getEntityTwo() {
        return entityTwo;
    }

    public void setEntityTwo(EntityTwo entityTwo) {
        this.entityTwo = entityTwo;
    }
}

EntityOne.java and EntityTwo.java (simply rename the class)

@Entity
public class EntityOne {
    private long id;
    private String serial;

    @Id
    @GeneratedValue
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getSerial() {
        return serial;
    }

    public void setSerial(String serial) {
        this.serial = serial;
    }
}

Test.java - run it.

public class Test {
    private static EntityManagerFactory buildFactory() {
        try {
            return Persistence.createEntityManagerFactory("migration-tool");
        } catch (Throwable ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        EntityManagerFactory emf = buildFactory();
        EntityManager em = emf.createEntityManager();
        Query q = em.createQuery("select c from Change c where (c.entityOne.serial = 'TEST3' or c.entityTwo.serial = 'TEST3')");
        List<Change> changeRecords =  q.getResultList();
        System.out.println(changeRecords.size());
        emf.close();
    }
}

persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
    <persistence-unit name="migration-tool" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>test.Change</class>
        <class>test.EntityOne</class>
        <class>test.EntityTwo</class>
        <properties>
            <property name="hibernate.search.autoregister_listeners" value="false"/>
            <property name="hibernate.cache.region.factory_class"
                      value="net.sf.ehcache.hibernate.EhCacheRegionFactory"/>
            <property name="hibernate.cache.use_query_cache" value="true"/>
            <property name="hibernate.connection.url" value="jdbc:postgresql://127.0.0.1:5434/test"/>
            <property name="hibernate.connection.username" value="postgres"/>
            <property name="hibernate.connection.password" value="123456"/>
            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
        </properties>
    </persistence-unit>
</persistence>

Dependencies in pom.xml

<dependency>
    <groupId>postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.1-901.jdbc4</version>
</dependency>
<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>persistence-api</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-annotations</artifactId>
    <version>3.4.0.GA</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>3.4.0.GA</version>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.0</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.4.2</version>
</dependency>

And SQL script to fill in the data.

CREATE TABLE entityone
(
  id bigint NOT NULL,
  serial character varying(255),
  PRIMARY KEY (id)
);

CREATE TABLE entitytwo
(
  id bigint NOT NULL,
  serial character varying(255),
  PRIMARY KEY (id)
);

CREATE TABLE change
(
  id bigint NOT NULL,
  entityone_id bigint,
  entitytwo_id bigint,
  PRIMARY KEY (id),
  FOREIGN KEY (entityone_id) REFERENCES entityone(id),
  FOREIGN KEY (entitytwo_id) REFERENCES entitytwo(id)
);

insert into entityone values (1, 'TEST3');
insert into entityone values (2, 'TEST1');
insert into entityone values (3, 'TEST2');
insert into entityone values (4, 'TEST3');

insert into entitytwo values (1, 'TEST3');
insert into entitytwo values (2, 'TEST3');
insert into entitytwo values (3, 'TEST4');
insert into entitytwo values (4, 'TEST3');

insert into change values (1, 1, NULL);
insert into change values (2, 2, NULL);
insert into change values (3, 3, NULL);
insert into change values (4, 4, NULL);
insert into change values (5, NULL, 1);
insert into change values (6, NULL, 2);
insert into change values (7, NULL, 3);
insert into change values (8, NULL, 4);

Writing up an answer from comments:

The problem are the NULL values. Chapter 16.4 of Hibernate documentation (v 4.3) state:

16.4. Forms of join syntax

HQL supports two forms of association joining: implicit and explicit.

The queries shown in the previous section all use the explicit form, that is, where the join keyword is explicitly used in the from clause. This is the recommended form.

The implicit form does not use the join keyword. Instead, the associations are "dereferenced" using dot-notation. implicit joins can appear in any of the HQL clauses. implicit join result in inner joins in the resulting SQL statement.

from Cat as cat where cat.mate.name like '%s%'

I have bolded out the most relevant part. What this says is that your dot notation will be unrolled into an inner join, and since the key is null - your entities will not appear in the join.
The SQL that Hibernate executed also shows this (the below is beautified for the sake of readability):

select change  
from Change change, EntityOne entityone, EntityTwo entitytwo
where 
change.entityOne_id=entityone.id and
change.entityTwo_id=entitytwo.id and  
(
    ((change.entityOne_id is not null) and entityone.serial='TEST3') or 
    ((change.entityTwo_id is not null) and entitytwo.serial='TEST3')
)

Unfortunately, the dot notation will not allow you to get the desired result. You would have to create the joins yourself, as in the above docs, chapter 16.3. Example:

SELECT c
FROM Change AS c
    LEFT JOIN Change.entityOne as entityOne
    LEFT JOIN Change.entityTwo as entityTwo
WHERE
    entityOne.serial = "TEST3" OR entityTwo.serial = "TEST3"

NOTE: I do not have a working setup with Hibernate, you may have to add extra null checks to the above

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