Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate is using wrong table name for order by expression with three level inheritance

In our project we have different user types presented by different classes. And we have a BaseEntity class as @MappedSuperclass. When we try to use user classes with InheritanceType.JOINED hibernate creates an sql that we think it is wrong.

Base Entity :

@MappedSuperclass
public abstract class BaseEntity implements java.io.Serializable {


private Integer id;

private Date createdDate = new Date();


public Integer getId() {
    return id;
}

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

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CREATED_DATE", nullable = true)
public Date getCreatedDate() {
    return createdDate;
}

public void setCreatedDate(Date createdDate) {
    this.createdDate = createdDate;
}

}

Base User

@Entity
@Table(name = "BASE_USER")
@Inheritance(strategy = InheritanceType.JOINED)
@AttributeOverride(name = "id", column = @Column(name = "ID", nullable = false, insertable = false, updatable = false))
public abstract class BaseUser extends BaseEntity{


@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
@SequenceGenerator(name = "seq", sequenceName = "USER_SEQ", allocationSize = 1)
public Integer getId() {
    return super.getId();
}

}

User

@Entity
@Table(name = "FIRM_USER")
public class FirmUser extends BaseUser {

private String name;

@Column(name = "name")
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

}

Sample Code

public class HibernateUtil {

private static final SessionFactory sessionFactory;

static {
    try {
        sessionFactory = new AnnotationConfiguration()
                .addAnnotatedClass(FirmUser.class)
                .addAnnotatedClass(BaseUser.class)
                .addAnnotatedClass(BaseEntity.class)
                .configure()
                .buildSessionFactory();
    } catch (Throwable ex) {
        throw new ExceptionInInitializerError(ex);
    }
}

public static Session getSession() throws HibernateException {
    return sessionFactory.openSession();
}

public static void main(String[] args) {
    getSession().save(new FirmUser());

    Query query = getSession().createQuery("select distinct a from FirmUser a ORDER BY a.id");
    query.list();

}
}

For this hql

select distinct a from FirmUser a ORDER BY a.id

hibernate creates this sql

select distinct firmuser0_.ID as ID1_0_,
       firmuser0_1_.CREATED_DATE as CREATED_2_0_,
       firmuser0_.name as name1_1_
from   FIRM_USER firmuser0_ 
       inner join BASE_USER firmuser0_1_ on firmuser0_.ID=firmuser0_1_.ID
order by firmuser0_1_.ID

"order by firmuser0_1_.ID" causes

 HSQLDB  : ORDER BY item should be in the SELECT DISTINCT list:

or

 ORACLE : ORA-01791: not a SELECTed expression

But firmuser0_.ID is in select clause and we actually try to order by ID of FirmUser (firmuser0_) not BaseUser (firmuser0_1_)

If we do not use BaseEntity it works as expected.

Why does hibernate use joined class for ordering in case of it also inherits from another class?

like image 724
Ekrem Avatar asked Apr 22 '15 11:04

Ekrem


1 Answers

I replicated your test case, you can find it on GitHub.

There must be a Hibernate bug, because when you use the alias, the select clause uses the subclass ID, while the ORDER BY uses the base class id, which since it's not in the select clause, throws an exception:

SELECT inheritanc0_.id             AS ID1_0_,
       inheritanc0_1_.created_date AS CREATED_2_0_,
       inheritanc0_.NAME           AS name1_1_
FROM   firm_user inheritanc0_
       INNER JOIN base_user inheritanc0_1_
               ON inheritanc0_.id = inheritanc0_1_.id
ORDER  BY inheritanc0_1_.id 

Notice the ORDER BY inheritanc0_1_.id, it should have been ORDER BY inheritanc0_.id instead.

Workaround 1:

Rewrite the query without alias:

List<FirmUser> result1 = (List<FirmUser>) session.createQuery("from FirmUser order by id").list(); 

The SQL being properly generated:

SELECT inheritanc0_.id             AS ID1_0_,
       inheritanc0_1_.created_date AS CREATED_2_0_,
       inheritanc0_.NAME           AS name1_1_
FROM   firm_user inheritanc0_
       INNER JOIN base_user inheritanc0_1_
               ON inheritanc0_.id = inheritanc0_1_.id
ORDER  BY inheritanc0_1_.id 

Workaround 2:

or specifying the subclass.id as well, but that results in an array of subclass and subclass entity tuples:

List<Object[]> result2 = (List<Object[]>) session.createQuery("select distinct a, a.id from FirmUser a order by id").list();

Giving the following SQL:

SELECT DISTINCT inheritanc0_1_.id           AS col_0_0_,
                inheritanc0_1_.id           AS col_1_0_,
                inheritanc0_.id             AS ID1_0_,
                inheritanc0_1_.created_date AS CREATED_2_0_,
                inheritanc0_.NAME           AS name1_1_
FROM   firm_user inheritanc0_
       INNER JOIN base_user inheritanc0_1_
               ON inheritanc0_.id = inheritanc0_1_.id
ORDER  BY inheritanc0_1_.id 

Workaround 3:

Like always, a native query gives you the ultimate control over any tuple association:

List<FirmUser> result3 = (List<FirmUser>) session.createSQLQuery(
        "select * " +
        "from FIRM_USER a " +
        "LEFT JOIN BASE_USER b ON a.id = b.id " +
        "order by a.id"
)
.addEntity("a", FirmUser.class)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();

You should fill a Hibernate issue for this problem, as it's not behaving as it should be.

like image 75
Vlad Mihalcea Avatar answered Oct 19 '22 10:10

Vlad Mihalcea