Hibernate/JPA однонаправленный OneToMany с условием соединения с постоянным значением в исходной таблице

Я хочу использовать аннотации Hibernate для представления однонаправленного отношения "один ко многим" с использованием соединения. Я хочу добавленное условие для соединения, поэтому это происходит только тогда, когда столбец в исходной таблице ( "один" ) равен постоянному значению. Например.

SELECT *
FROM buildings b
LEFT JOIN building_floors bf on bf.building_id = b.id AND b.type = 'OFFICE'

Я хочу представить часть b.type = 'OFFICE' этого запроса.

Мой вопрос очень похож на этот, за исключением того, что у меня есть условие в исходной таблице. JPA/Hibernate присоединяются к постоянному значению

Объекты Java выглядят следующим образом:

@Entity
@Table(name = "buildings")
public class Building {

    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "type")
    private String type;

    @OneToMany(mappedBy = "buildingId",
            fetch = FetchType.EAGER,
            cascade = {CascadeType.ALL},
            orphanRemoval = true)
    @Fetch(FetchMode.JOIN)
    // buildings.type = 'OFFICE'   ????
    private Set<BuildingFloors> buildingFloors;

    // getters/setters
}

@Entity
@Table(name = "building_floors")
public class BuildingFloor {

    @Id
    @Column(name = "building_id")
    private int buildingId;

    @Id
    @Column(name = "floor_id")
    private int floorId;

    @Column(name = "description")
    private String description;

    // getters/setters
}

Я пробовал несколько вещей, где у меня есть этот комментарий к заполнителю:

@Where аннотация

Это не работает, поскольку это относится к целевому объекту.

аннотация @JoinColumns

@JoinColumns({
        @JoinColumn(name = "building_id", referencedColumnName = "id"),
        @JoinColumn(name = "'OFFICE'", referencedColumnName = "type")
})

Это не работает, потому что я получаю следующую ошибку (упрощенную для ясности): Syntax error in SQL statement "SELECT * FROM buildings b JOIN building_floors bf on bf.building_id = b.id AND bf.'OFFICE' = b.type"

Разная аннотация @JoinColumns

@JoinColumns({
        @JoinColumn(name = "building_id", referencedColumnName = "id"),
        @JoinColumn(name = "buildings.type", referencedColumnName = "'OFFICE'")
})

Это не работает, потому что при использовании однонаправленного отношения OneToMany ссылочное имяColumnName происходит из исходной таблицы. Поэтому я получаю сообщение об ошибке: org.hibernate.MappingException: Unable to find column with logical name: 'OFFICE' in buildings

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

Ответы

Ответ 1

Почему бы не использовать наследование? (Я использую его с JPA, я никогда не использую hibernate напрямую)

@Entity
@Inheritance
@Table(name = "buildings")
@DiscriminatorColumn(name="type")
public class Building {

    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "type")
    private String type;
}

И:

@Entity
@DiscriminatorValue("OFFICE")
public class Office extends Building {
    @OneToMany(mappedBy = "buildingId",
        fetch = FetchType.EAGER,
        cascade = {CascadeType.ALL},
        orphanRemoval = true)
    private Set<BuildingFloors> buildingFloors;
}

Ответ 2

Создать базу данных Открыть со следующим выбором:

SELECT bf.* FROM building_floors bf JOIN buildings b on bf.building_id = b.id AND b.type = 'OFFICE'

Сопоставьте его с классом OfficeBuildingFloors как обычный объект, а затем используйте @OneToMany для него в классе Building.

Конечно, вы не сможете модифицировать такую ​​коллекцию и избегать любых исключений, которые вы можете использовать @Immutable на OfficeBuildingFloors.

Ответ 3

По-моему, вы должны создать конкретный запрос для достижения своих целей, вместо того, чтобы ставить конкретные аннотации с постоянным параметром. Я не вижу, что вы упоминаете другие рамки, кроме Hibernate, поэтому я приведу пример с Hibernate. В вашем классе Building ваши сопоставления unidirectional выглядят следующим образом:

@OneToMany(fetch = FetchType.Lazy, cascade = {CascadeType.ALL}, orphanRemoval = true)
@JoinTable(name = "building_floors", joinColumns = @JoinColumn(name = "id"), inverseJoinColumns = @JoinColumn(name = "building_id")
private Set<BuildingFloor> buildingFloors;

Затем вы можете извлечь свои данные, используя TypedQuery, как это.

TypedQuery<Customer> query = getEntityManager().createNamedQuery("select b from building b inner join fetch b.buildingFloors where b.type = 'OFFICE'", Building.class);
List<Building> result = query.getResultList();

Мои решения не относятся к Hibernate, на самом деле вы можете выполнить это с помощью простой JPA. Надеюсь, это поможет вам достичь ваших целей.

Ответ 4

Как вы хотите использовать таблицу источника фильтра, вы можете использовать аннотацию @Loader

@Entity
@Table(name = "buildings")
@Loader(namedQuery = "building")
@NamedNativeQuery(name="building", 
    query="SELECT * FROM buildings b"
        + " LEFT JOIN building_floors bf on bf.building_id = b.id"
        + " WHERE b.type = 'OFFICE' AND b.id = ?",
    resultClass = Building.class)
class Building

Подход с представлением в БД будет лучше и понятнее, если он также может быть использован внутри БД. В противном случае переименуйте Building в нечто, явно отображающее фильтрацию.

Другие подходы к упоминанию: @Filter, @FilterDef.