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

Hibernate и @JoinFormula: org.hibernate.mapping.Formula не может быть добавлен в org.hibernate.mapping.Column

Я пытаюсь написать адаптер hibernate для старой схемы базы данных. Эта схема не имеет выделенного столбца идентификатора, но использует три других столбца для объединения данных.

На некоторых таблицах мне нужно использовать coalesce. Это то, к чему я придумал:

Об определении:

  • Автомобиль может иметь элементы, назначенные пользователем автомобиля или группой пользователей автомобилей.
  • Если FORIGN_ELEMENT содержит имя пользователя, определение будет "u"
  • Если FORIGN_ELEMENT содержит имя группы, определение будет "g"
  • Это также означает, что одна таблица (CAR_TO_ELEMENT) используется неправильно для сопоставления автомобилей с элементами и группами cargroups. Я определил класс CarElement и подклассы CarUserElement и CarGroupElement. Состояние
  • является либо "активной", либо неинтересной строкой
  • Я установил определенность и положение в другом месте, нам не нужно беспокоиться об этом.
  • Используйте DEP_NR в таблице соединений. Если он равен нулю, используйте USR_DEP_NR. Я сделал это с COALESCE(NULLIF()) успешно в родном SQL и хочу добиться того же в Hibernate с Pojos.

Хорошо, вот мы идем с кодом:

@Entity
@Table(name="CAR")
public class Car extends TableEntry implements Serializable {
    @Id
    @Column(name="DEP_NR")
    private int depnr;

    @Id
    @Column(name="USER_NAME")
    @Type(type="TrimmedString")
    private String username;

    @ManyToOne(fetch = FetchType.EAGER, targetEntity=CarGroup.class)
    @JoinColumns(value={ 
            @JoinColumn(name="GROUP_NAME"),
            @JoinColumn(name="DEP_NR"),
            @JoinColumn(name="state"),
    })
    private CarGroup group;

    @OneToMany(fetch=FetchType.EAGER, targetEntity=CarUserElement.class, mappedBy="car")
    private Set<CarUserElement> elements;
}
@Entity
@Table(name="CAR_GROUP")
public class CarGroup extends TableEntry implements Serializable {
    @Id
    @Column(name="DEP_NR")
    private int depnr;

    @Id
    @Column(name="GROUP_NAME")
    @Type(type="TrimmedString")
    private String group;

    @ManyToOne(fetch = FetchType.EAGER, targetEntity=Car.class)
    @JoinColumns(value={ 
            @JoinColumn(name="GROUP_NAME"),
            @JoinColumn(name="DEP_NR"),
            @JoinColumn(name="state"),
    })
    private Set<Car> cars;

    @OneToMany(fetch=FetchType.EAGER, targetEntity=CarGroupElement.class, mappedBy="car")
    private Set<CarGroupElement> elements;
}
@MappedSuperclass
public class CarElement extends TableEntry {
    @Id
    @ManyToOne(fetch = FetchType.EAGER, targetEntity=Element.class)
    @JoinColumns(value={ 
            @JoinColumn(name="ELEMENT_NAME"),
            @JoinColumn(name="state"),
    })
    private Element element;
}
@Entity
@Table(name="CAR_TO_ELEMENT")
public class CarUserElement extends CarElement {
    @Id
    @Column(name="DEFINITION")
    private char definition;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumnsOrFormulas(value = {
        @JoinColumnOrFormula([email protected](value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
        @JoinColumnOrFormula([email protected](name="FORIGN_ELEMENT", referencedColumnName="USER_NAME")),
        @JoinColumnOrFormula([email protected](name="STATE", referencedColumnName="STATE"))
    })
    private Car car;

}
@Entity
@Table(name="CAR_TO_ELEMENT")
public class CarGroupElement extends CarElement {
    @Id
    @Column(name="DEFINITION")
    private char definition;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumnsOrFormulas(value = {
        @JoinColumnOrFormula([email protected](value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
        @JoinColumnOrFormula([email protected](name="FORIGN_ELEMENT", referencedColumnName="GROUP_NAME")),
        @JoinColumnOrFormula([email protected](name="STATE", referencedColumnName="STATE"))
    })
    private Car car;

}

Я пробовал все доступные версии hibernate (от 3.5.1 [первая версия с @JoinColumnsOrFormulas] до 4.x.x), но я всегда получаю эту ошибку:

Exception in thread "main" java.lang.ClassCastException: org.hibernate.mapping.Formula cannot be cast to org.hibernate.mapping.Column
    at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:351)
    at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1338)
    at org.hibernate.cfg.annotations.CollectionBinder.bindOneToManySecondPass(CollectionBinder.java:791)
    at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:719)
    at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:668)
    at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:66)
    at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1597)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1355)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1737)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1788)

У других пользователей спящего режима, похоже, есть одна и та же проблема: они не могут заставить его работать с какой-либо версией, см. этот поток и другие вопросы, касающиеся stackoverflow: https://forum.hibernate.org/viewtopic.php?f=1&t=1010559

Чтобы быть более полным, вот мой класс TrimmedString: https://forum.hibernate.org/viewtopic.php?p=2191674&sid=049b85950db50a8bd145f9dac49a5f6e#p2191674

Спасибо заранее!

PS: Он работает с объединением только этих трех столбцов только с одним DEP-NR-столбцом (т.е. DEP_NR OR USR_DEP_NR, используя только @JoinColumns). Но мне нужно это COALESCE(NULLIF()).

4b9b3361

Ответ 1

Присоединение формул в Hibernate очень хрупко; У меня всегда было трудное время, чтобы заставить их работать должным образом.

Обходной путь, который часто помогал мне, заключался в создании представлений базы данных, которые отображали правильные столбцы (включая внешние ключи, которых нет в исходных таблицах). Затем я сопоставил объекты с представлениями, используя сопоставления Hibernate/JPA.

Иногда в сгенерированном SQL используются избыточные соединения при использовании таких объектов, но база данных оптимизирует такие запросы в большинстве случаев, чтобы план выполнения был оптимальным в любом случае.

Другим подходом может быть использование @Subselect s, которые являются своего рода представлениями Hibernate, но я ожидаю, что они будут менее эффективными, чем классические представления базы данных.

Ответ 2

У меня возникла аналогичная проблема, и кажется, что проблема заключается в том, что вы используете @Formula внутри @Id. Hibernate хочет, чтобы идентификаторы были вставлены, а формулы - только для чтения.

В моем случае я смог обойти проблему, создав собственные свойства Id столбцов самостоятельно и сделав объединенный объект отдельным свойством. Я не знаю, будет ли это работать в вашем случае, так как вы используете в своей формуле два разных столбца, но если ваш код может выглядеть примерно так:

@Entity
@Table(name="CAR_TO_ELEMENT")
public class CarUserElement extends CarElement {
    @Id
    @Column(name="DEFINITION")
    private char definition;

    @Id
    @Column(name="DEP_NR")
    private Integer depNr;

    @Id
    @Column(name="USR_DEP_NR")
    private Integer usrDepNr;

    @Id
    @Column(name="FORIGN_ELEMENT")
    private String userName;

    @Id
    @Column(name="STATE")
    private String state;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumnsOrFormulas(value = {
        @JoinColumnOrFormula([email protected](value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
        @JoinColumnOrFormula([email protected](name="FORIGN_ELEMENT", referencedColumnName="USER_NAME", insertable = false, updatable = false)),
        @JoinColumnOrFormula([email protected](name="STATE", referencedColumnName="STATE", insertable = false, updatable = false))
    })
    private Car car;

}