JPA Двухэтапный OneToMany Hibernate 6

Наша команда решила перейти с Hibernate 5 на Hibernate 6. В нашем проекте задействованы сотни объектов JPA. Некоторые из них больше не работают из-за новой проверки в Hibernate. Я объясню это подробно, используя минимум атрибутов:

Адаптер стола:

  • столбец Целое число ADID (PK) -> идентификатор адаптера
  • Имя строки столбца

Таблица АдаптерПарам:

  • столбец Целое число ADID (PK, FK to Adaptor.ADID)
  • столбец Строка PAN (PK) -> Имя параметра
  • ...

Таблица АдаптерПарамселектионлист:

  • столбец Целое число ADID (от PK, FK до AdapterParam.ADID)
  • строка столбца PAN (от PK, FK до AdapterParam.PAN)
  • Столбец Строковый код (ПК)

Объект JPA адаптера:

@Entity
public class Adapter implements Serializable {

    @Id
    @Column(name = "ADID")
    private Integer adapterId;

    @OneToMany(mappedBy = "adapter", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<AdapterParam> adapterParams = new HashSet<>();


        // Getter and Setter...
}

JPA-объект AdaptorParam:

@Entity
@IdClass(AdapterParamId.class)
public class AdapterParam implements Serializable {

    public static class AdapterParamId implements Serializable {
        
        private static final long serialVersionUID = 1L;
        
        private Adapter adapter;
        private String parameterName;
        @Override
        public int hashCode() {
            return Objects.hash(adapter, parameterName);
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            AdapterParamId other = (AdapterParamId) obj;
            return Objects.equals(adapter, other.adapter) && Objects.equals(parameterName, other.parameterName);
        }
    }

    @Id
    @Column(name = "PAN")
    private String parameterName;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ADID", referencedColumnName = "ADID")
    private Adapter adapter;

    @OneToMany(mappedBy = "adapterParam", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<AdapterParamSelectionList> adapterParamSelectList = new HashSet<>();


        // Getter and Setter...
}

Объект JPA для AdapterParamSelectionList:

@Entity
@IdClass(AdapterParamSelectionListId.class)
public class AdapterParamSelectionList implements Serializable {

    public static class AdapterParamSelectionListId implements Serializable {
        
        private static final long serialVersionUID = 1L;
        
        private AdapterParam adapterParam;
        private String code;
        @Override
        public int hashCode() {
            return Objects.hash(adapterParam, code);
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            AdapterParamSelectionList other = (AdapterParamSelectionList) obj;
            return Objects.equals(adapterParam, other.adapterParam)
                    && Objects.equals(code, other.code);
        }
    }

    @Id
    @Column(name = "VAL")
    private String code;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ADID", referencedColumnName = "ADID") // this turns into error
    @JoinColumn(name = "PAN", referencedColumnName = "PAN")
    private AdapterParam adapterParam;

        // Getter and Setter...
}

Исключением Hibernate является следующее. Это происходит при запуске нашего приложения:

org.hibernate.AnnotationException: Referenced column 'ADID' in '@JoinColumn' for 'de.example.AdapterParamSelectionList.id.adapterParam' is not mapped by any property of the target entity
    at org.hibernate.boot.model.internal.BinderHelper.findPropertiesByColumns(BinderHelper.java:501)
    at org.hibernate.boot.model.internal.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:160)
    at org.hibernate.boot.model.internal.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:114)
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1850)
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1764)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:334)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:129)
    at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:449)
    at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:101)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:966)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1016)

В Hibernate 5.6 связь между AdaptorParamSelectionList и AdaptorParamBO работала должным образом. Я не знаю, что изменилось. Как лучше всего это определить?

Просто чтобы уточнить: мы используем IdClass вместо EmbeddedId, потому что мы сопоставляем объекты с DTO.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
101
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Начиная с Hibernate 5.3, составные идентификаторы с использованием @IdClass должны напрямую сопоставлять столбцы. В вашем случае этого не происходит, потому что вы используете связь ManyToOne.

Одним из решений было бы переключиться с @IdClass на @EmbeddedId и @Embeddable. Это рекомендуемый вариант, и похоже, он идеально соответствует вашим потребностям.

АдаптерParamId изменится с @IdClass на @Embeddable:

@Embeddable
public class AdapterParamId implements Serializable {

    @Column(name = "ADID")
    private Integer adapterId;
    
    @Column(name = "PAN")
    private String parameterName;

    // equals() and hashCode()

}

Другие вопросы по теме