簡體   English   中英

使用復合鍵 Hibernate 自定義連接

[英]Custom Join with composite key Hibernate

謝謝你的時間! 我有一個復雜的場景,我試圖在春天用 JPA/Hibernate 映射。

首先,我有一個動態生成日期的函數,它輸出以下內容:

id                                  |starts_at              |ends_at                |frequency|separation|count|until|timezone_name|is_full_day|location_id                         |created_at             |updated_at             |version|name                         |description      |picture|
------------------------------------+-----------------------+-----------------------+---------+----------+-----+-----+-------------+-----------+------------------------------------+-----------------------+-----------------------+-------+-----------------------------+-----------------+-------+
d255afb6-3730-4845-8e5e-18baa909dcfe|2021-12-21 18:00:00.000|2021-12-22 02:00:00.000|ONCE     |         1|     |     |UTC          |false      |fe88fa63-360e-4281-a383-35826a759e59|2021-10-29 00:22:46.109|2021-10-29 00:22:46.109|      0|                             |                 |       |
e7193453-4c1b-433d-8127-5eedf2efd011|2022-02-20 20:00:00.000|2022-02-20 23:00:00.000|WEEKLY   |         1|     |     |UTC          |false      |fe88fa63-360e-4281-a383-35826a759e59|2021-11-02 19:23:31.029|2021-11-02 19:23:31.029|      0|Festa da minha graaande amiga|A Fofinha nao vai|       |
e7193453-4c1b-433d-8127-5eedf2efd011|2022-02-21 20:00:00.000|2022-02-21 23:00:00.000|WEEKLY   |         1|     |     |UTC          |false      |fe88fa63-360e-4281-a383-35826a759e59|2021-11-02 19:23:31.029|2021-11-02 19:23:31.029|      0|Muito massa essa fita        |Mudanca drastica |       |
e7193453-4c1b-433d-8127-5eedf2efd011|2022-02-22 20:00:00.000|2022-02-22 23:00:00.000|WEEKLY   |         1|     |     |UTC          |false      |fe88fa63-360e-4281-a383-35826a759e59|2021-11-02 19:23:31.029|2021-11-02 19:23:31.029|      0|Festa da minha graaande amiga|A Fofinha nao vai|       |
e7193453-4c1b-433d-8127-5eedf2efd011|2022-02-24 20:00:00.000|2022-02-24 23:00:00.000|WEEKLY   |         1|     |     |UTC          |false      |fe88fa63-360e-4281-a383-35826a759e59|2021-11-02 19:23:31.029|2021-11-02 19:23:31.029|      0|Festa da minha graaande amiga|A Fofinha nao vai|       |
e7193453-4c1b-433d-8127-5eedf2efd011|2022-02-25 20:00:00.000|2022-02-25 23:00:00.000|WEEKLY   |         1|     |     |UTC          |false      |fe88fa63-360e-4281-a383-35826a759e59|2021-11-02 19:23:31.029|2021-11-02 19:23:31.029|      0|Festa da minha graaande amiga|A Fofinha nao vai|       |

重要的部分是:id(它是相同的,因為它是數據庫中的同一個實體;starts_at:事件的初始日期和元數據部分(它是與另一個表的左連接);

這是輸出上述示例的查詢:

    select
    e.*,
    coalesce (em.name,
    em2.name) as name,
    coalesce (em.description ,
    em2.description) as description,
    coalesce (em.picture ,
    em2.picture) as picture
from
    recurring_events_for('2021-12-03T10:15:30',
    '2022-12-03T10:15:30',
    'UTC',
    5,
    false) e
left join events_metadata em on
    em.event_id = e.id
    and em.starts_at = e.starts_at
left join events_metadata em2
on
    em2.event_id = e.id
    and em2.starts_at = (
    select
        MIN(starts_at)
    from
        events_metadata
    where
        event_id = e.id
    limit 1)

我是如何使用 Hibernate 映射的:

事件(recurring_events_for 的輸出):

@Entity
@Table(name = "events")
public class Event extends BaseEntity {

    @Column
    private LocalDateTime startsAt;

    @Column
    private LocalDateTime endsAt;

    @Column
    @Enumerated(EnumType.STRING)
    private EventFrequency frequency;

    @Column(nullable = false)
    private Integer separation;

    @Column
    private Integer count;

    @Column
    private LocalDate until;

    @Column(nullable = false)
    private String timezoneName;

    @Column(nullable = false)
    private boolean isFullDay;

    @ManyToMany
    @JoinTable(
            name = "event_organizers",
            joinColumns = @JoinColumn(name = "event_id"),
            inverseJoinColumns = @JoinColumn(name = "user_id"))
    private Set<User> organizers;

    @OneToMany(mappedBy = "product")
    private Set<EventProduct> products;

    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    @JoinColumn(name = "location_id")
    private Location location;

    @OneToMany(mappedBy = "event",
            orphanRemoval = true,
            cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    private Set<EventRecurrence> recurrences;

    @OneToOne(mappedBy = "id.event", optional = false, orphanRemoval = true, fetch = FetchType.LAZY)
    private EventMetadata metadata;

event_metadata 表:

@Table(name = "events_metadata")
@Entity
public class EventMetadata extends BaseEntityNoId {

    @EmbeddedId
    private EventMetadataPK id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String description;

    @Column
    private String picture;

    @Column(nullable = false)
    private boolean isPrivate;

    @Column(nullable = false)
    private boolean showGuestList;

    @Column(nullable = false)
    private boolean friendCanInviteFriends;

    @OneToMany(mappedBy = "event", orphanRemoval = true)
    private Set<EventAttendance> eventAttendances;

嵌入式PK:

@Embeddable
public class EventMetadataPK implements Serializable {

    @OneToOne(orphanRemoval = true)
    @JoinColumn(name = "event_id", updatable = false, insertable = false)
    private Event event;

    @Column(nullable = false)
    private LocalDateTime startsAt;

最后,我如何在 JPA 世界中調用它:

@Query(nativeQuery = true,
            value = "select e.*, coalesce (em.*, em2.*) as metadata " +
                    "from recurring_events_for(:range_start, :range_end, :time_zone, :events_limit, false) e " +
                    "left join events_metadata em on em.event_id = e.id and em.starts_at = e.starts_at " +
                    "left join events_metadata em2 " +
                    "on em2.event_id = e.id " +
                    "and em2.starts_at = (select MIN(starts_at) from events_metadata where event_id = e.id limit 1)")
    List<Event> getEvents(@Param("range_start") LocalDateTime rangeStart,
                          @Param("range_end") LocalDateTime rangeEnd,
                          @Param("time_zone") String timezone,
                          @Param("events_limit") Integer limit);

問題是,嵌入 PK 中映射的 starts_at 列與 recurring_events_for 的結果虛擬連接,因此(我認為)不可能使用 Hibernate 映射它。 我該如何處理這種情況??

謝謝!

您可以嘗試使用構造函數結果集映射

  1. 注釋Event實體類似於:
@Entity
@Table(name = "events")
@SqlResultSetMapping(
        name = "recurring_events_mapping",
        classes = {
                @ConstructorResult(
                        targetClass = Event.class,
                        columns = {
                                @ColumnResult(name = "ID", type = String.class),
                                @ColumnResult(name = "STARTS_AT", type = LocalDateTime.class),
                                @ColumnResult(name = "ENDS_AT", type = LocalDateTime.class),
                                @ColumnResult(name = "FREQUENCY", type = String.class),
                                @ColumnResult(name = "SEPARATION", type = Integer.class),
                                @ColumnResult(name = "COUNT", type = Integer.class),
                                @ColumnResult(name = "UNTIL", type = LocalDate.class),
                                @ColumnResult(name = "TIMEZONE_NAME", type = String.class),
                                @ColumnResult(name = "IS_FULL_DAY", type = Boolean.class),
                                @ColumnResult(name = "LOCATION_ID", type = String.class),
                                @ColumnResult(name = "CREATED_AT", type = LocalDateTime.class),
                                @ColumnResult(name = "UPDATED_AT", type = LocalDateTime.class),
                                @ColumnResult(name = "VERSION", type = Integer.class),
                                // metadata
                                @ColumnResult(name = "NAME", type = String.class),
                                @ColumnResult(name = "DESCRIPTION", type = String.class),
                                @ColumnResult(name = "PICTURE", type = String.class)
                        }
                )
        }
)
@NamedNativeQuery(
        name = "Event.RecurringEventsQuery",
        query = "select  e.*, " +
                "    coalesce (em.name, em2.name) as name, " +
                "    coalesce (em.description, em2.description) as description, " +
                "    coalesce (em.picture , em2.picture) as picture " +
                "from recurring_events_mock e " + // just for test
                "left join events_metadata em on " +
                "    em.event_id = e.id " +
                "    and em.starts_at = e.starts_at " +
                "left join events_metadata em2 on " +
                "    em2.event_id = e.id " +
                "    and em2.starts_at = ( " +
                "    select MIN(starts_at) " +
                "    from events_metadata " +
                "    where event_id = e.id " +
                "    limit 1)" +
                "where :time_zone IS NOT NULL " + // just for test
                "    and :events_limit > 0 " + // just for test
                "    and :range_start IS NOT NULL " + // just for test
                "    and :range_end IS NOT NULL ", // just for test
        resultSetMapping = "recurring_events_mapping")
  1. 然后在您的 EventRepository 中像這樣使用它
    @Query(name = "Event.RecurringEventsQuery")
    List<Event> getEvents(
            @Param("range_start") LocalDateTime rangeStart,
            @Param("range_end") LocalDateTime rangeEnd,
            @Param("time_zone") String timezone,
            @Param("events_limit") Integer limit
    );
    List<Event> findAll();
  1. 您還必須在 Event 和相關類中創建適當的構造函數,例如:
    public Event() {
    }

    public Event(
            String id,
            LocalDateTime startsAt,
            LocalDateTime endsAt,
            String frequency,
            Integer separation,
            Integer count,
            LocalDate until,
            String timezoneName,
            Boolean isFullDay,
            String locationId,
            LocalDateTime createdAt,
            LocalDateTime updatedAt,
            Integer version,
            // metadata
            String name,
            String description,
            String picture) {
        this.id = id;
        this.startsAt = startsAt;
        this.endsAt = endsAt;
        this.frequency = EventFrequency.valueOf(frequency);
        this.separation = separation;
        this.count = count;
        this.until = until;
        this.timezoneName = timezoneName;
        this.isFullDay = isFullDay;
        this.location = new Location(locationId);
        this.metadata = new EventMetadata(id, startsAt, name, description, picture);
    }

在這里你可以找到丑陋的 POC 存儲庫https://bitbucket.org/kasptom/stackoverflow-69887977

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM