Подтвердить что ты не робот

Hibernate использует неправильное имя таблицы для заказа по выражению с трехуровневым наследованием

В нашем проекте мы имеем разные типы пользователей, представленные разными классами. И у нас есть класс BaseEntity как @MappedSuperclass. Когда мы пытаемся использовать пользовательские классы с InheritanceType.JOINED, hibernate создает sql, который мы считаем неправильным.

Базовый объект:

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

}

Базовый пользователь

@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();
}

}

Пользователь

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

}

Пример кода

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();

}
}

Для этого hql

select distinct a from FirmUser a ORDER BY a.id

hibernate создает этот 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" вызывает

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

или

 ORACLE : ORA-01791: not a SELECTed expression

Но firmuser0_.ID находится в предложении select, и мы фактически пытаемся сделать заказ по идентификатору FirmUser (firmuser0_), а не BaseUser (firmuser0_1 _)

Если мы не используем BaseEntity, он работает как ожидалось.

Почему hibernate использует объединенный класс для упорядочения, в случае его также наследует от другого класса?

4b9b3361

Ответ 1

Я воспроизвел ваш тестовый пример, вы можете найти его на GitHub.

Должна быть ошибка Hibernate, потому что, когда вы используете псевдоним, предложение select использует идентификатор подкласса, в то время как ORDER BY использует идентификатор базового класса, который, поскольку он не находится в предложении select, выдает исключение:

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 

Обратите внимание на ORDER BY inheritanc0_1_.id, вместо этого он должен был ORDER BY inheritanc0_.id.

Обходной путь 1:

Перепишите запрос без псевдонима:

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

SQL правильно сгенерирован:

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 

Обходной путь 2:

или указать также subclass.id, но это приводит к массиву подклассов подкласса и подкласса:

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

Предоставление следующего 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 

Обходной путь 3:

Как всегда, собственный запрос дает вам окончательный контроль над любой ассоциацией кортежей:

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();

Вы должны заполнить проблему спящего режима для этой проблемы, так как она не ведет себя так, как должна быть.