繁体   English   中英

Spring 启动 Rest API, Z9CE3D1BD8890F16A0C448093595087CZ 是什么方法?

[英]Spring Boot Rest API, JPA Entities, DTOs, what is the best approach?

我接到了这个任务,只是为了练习,它变得非常漫长和具有挑战性,但它教会了我很多东西,主要是关于 lambdas 和 JPA。

它是一个基本的 Rest API,用于创建酒店、房间、客人、预订、客人类型、房间类型等。

我最初的问题是学习 JPA 关系、OneToOne、OneToMany 等,单向、双向等等。

我也在使用 PostgreSQL,使用"sping.jpa.hibernate.ddl-auto=create-drop(or update)" ,根据需要更改。

所以我很高兴和兴奋使用我的新@Annotations 来关联我的实体,并获取我需要的任何信息的列表,遇到了多个问题,在这里阅读了很多问题,解决了我的问题,但现在我遇到了新问题,但随后,开始质疑我的方法,也许我不应该把一切都留给 JPA。

让我告诉你我的意思。 我将保持我的课程简短,只显示相关信息。

我有我的预订实体。

    @Data
    @Entity
    @Table(name = "reservation")
    public class Reservation {
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      @OneToOne(cascade = CascadeType.ALL)
      @JoinColumn(name = "guest", referencedColumnName = "id")
      @JsonManagedReference
      @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
      private Guest guest;
      @OneToOne(cascade = CascadeType.ALL)
      @JoinColumn(name = "room", referencedColumnName = "id")
      private Room room;
      @ManyToMany(fetch = FetchType.LAZY,
          cascade = CascadeType.ALL)
      @JoinTable(name = "reservation_rooms",
          joinColumns = { @JoinColumn(name = "reservation_id" )},
          inverseJoinColumns = { @JoinColumn(name = "room_id") }
      )
      @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
      private List<ReservationRoom> roomList = new ArrayList<>();
    
      private LocalDate start_date;
      private LocalDate end_date;
      private Boolean check_in;
      private Boolean check_out;
    
      public void addRoom(Room room) {
        this.roomList.add(room);
      }
    
      public void removeRoom(Long id) {
        Room room = this.roomList.stream().filter(g -> g.getId() == id).findFirst().orElse(null);
        if (room != null) {
          this.roomList.remove(room);
        }
      }
    
    }

这是我的房间实体。

    @Data
    @Entity
    @Table(name = "room")
    public class Room {
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      private String name;
      private String description;
      private Integer floor;
      @JsonProperty("max_guests")
      private Integer maxGuests;
      @ManyToOne(fetch = FetchType.LAZY)
      @JsonBackReference
      private Hotel hotel;
      @ManyToOne(fetch = FetchType.LAZY)
      @JsonProperty("type")
      @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
      private RoomType roomType;
    
      @Override
      public boolean equals(Object o) {
        if (this == o) {
          return true;
        }
        if (!(o instanceof Room)) {
          return false;
        }
        return id != null && id.equals(((Room) o).getId());
      }
    
      @Override
      public int hashCode() {
        return getClass().hashCode();
      }
    }

这是我的来宾实体。


    @Data
    @Entity
    @Table(name = "guest")
    public class Guest {
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      private String first_name;
      private String last_name;
      private String email;
      @ManyToOne(fetch = FetchType.LAZY)
      @JsonProperty("type")
      @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
      private GuestType guest_type;
      @ManyToMany(fetch = FetchType.LAZY,
          cascade =  {
              CascadeType.PERSIST,
              CascadeType.MERGE
          },
          mappedBy = "guestList"
      )
      @JsonBackReference
      @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
      private List<Reservation> reservationList = new ArrayList<>();
    
      public Guest(){}
    
      public Guest(Long id) {
        this.id = id;
      }
    
      public List<Reservation> getReservationList() {
        return reservationList;
      }
    
      public void setReservationList(List<Reservation> reservationList) {
        this.reservationList = reservationList;
      }
    }

一开始只能订1间房,后来需求变了,现在可以订多间房了。 所以现在,客人名单需要链接到与预订相关的房间,而不是直接链接到预订。 (我知道我有一个客人和一个房间,还有两者的列表,这是因为我使用单身客人作为预订的名称,而单人房作为“主要”房间,但不要请不要介意)。

把 JPA 放在一边,因为我面临的每一个挑战我都会问自己“JPAish 怎么做?”,然后研究如何用 JPA 来做(这就是我了解@ManyToMany 等注释的方法)。

我要做的只是创建一个新表,将预订与房间相关联(这已经在我的实体中使用 JPA 完成),然后还添加 de guest id。

因此,这个新表将有一个带有 reservation_id、room_id 和 guest_id 的 PK。 很简单,然后创建我的 Reservation model,它有一个房间列表,而这个房间 model 将有一个客人列表。 简单的。

但我不想在我当前的 Room 实体中添加访客列表,因为我有一个端点,可能还有几个其他函数可以检索我的 Room 实体,而且我不想添加访客列表,因为随着时间的推移,这个列表会越来越大,而且它是你不需要传递的信息。

所以我做了一些研究,发现我可以用@Inheritance 或@MappedSuperclass 扩展我的实体,我可以创建一个Reservation_Room model,其中包括一个客人列表并在我的预订中添加一个Reservation_Room 列表而不是房间列表实体,我真的不知道它是否可能。

话虽如此,在我继续研究并开始修改我的代码之前,我想知道这是否是正确的方法? 或者,如果我在这方面强迫 JPA 太多? 最好的方法是什么? 可以在 JPA 上轻松实现/映射 3 id 关系表吗?

主要目标是让我的房间实体按原样公开,但是当房间被添加到预订时,这个房间也会有一个客人列表。 我可以做这个 JPAish 吗? 或者我应该创建一个新的 model 并根据需要填写信息? 这不会免除我创建我的 3 ids 表。

根据您在此处所写的内容,我认为您可能已经意识到持久性 model 并不总是与您在 Z293C9EA246FF9985DC6F62A650F78986 端点中使用的演示 model 匹配。 这通常是人们发现 DTO 的地方,您似乎也听说过。

DTO 应根据端点表示的需要进行调整/创建。 如果您不想公开某些 state,那么就不要在 DTO 中为该数据声明 getter/field。 持久化 model 应该简单地设计成某种方式,以便您可以按需要的方式持久化和查询数据。 DTO 和实体之间的转换是另一回事,对此我只能建议您尝试Blaze-Persistence Entity Views

我创建了该库以允许在 JPA 模型和自定义接口或抽象 class 定义的模型之间轻松映射,例如 Spring 类固醇上的数据投影。 这个想法是您以您喜欢的方式定义您的目标结构(域模型),并通过 JPQL 表达式将 map 属性(吸气剂)定义为实体 model。

使用 Blaze-Persistence Entity-Views 的 DTO model 可能如下所示:

@EntityView(Reservation.class)
public interface ReservationDto {
    @IdMapping
    Long getId();
    GuestDto getGuest();
    List<RoomDto> getRooms();
}
@EntityView(Guest.class)
public interface GuestDto {
    @IdMapping
    Long getId();
    String getName();
}
@EntityView(Room.class)
public interface RoomDto {
    @IdMapping
    Long getId();
    String getName();
}

查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。

ReservationDto a = entityViewManager.find(entityManager, ReservationDto.class, id);

Spring 数据集成允许您几乎像 Spring 数据投影一样使用它: https://persistence.blazebit.com/documentation/entity-view/manual-html/

Page<ReservationDto> findAll(Pageable pageable);

最好的部分是,它只会获取实际需要的 state!

我会说您需要在持久性和端点之间添加一个层。 因此,您将拥有控制器/服务/存储库(在 Spring 世界中)。 您应该使用实体作为存储库的返回类型(因此也在服务中使用它们),但将 DTO 返回给控制器。 通过这种方式,您将解耦您在它们之间所做的任何修改(例如,您可能对返回存储在实体中的字段失去兴趣,或者您可能希望从其他来源向 dto 添加更多信息)。

在这种特殊情况下,我将创建 4 个表:Reservations、Guests、Rooms、GuestsForReservation。

  • 客人将包含 id +客人数据(姓名、电话号码等)
  • 房间将包含 id + 房间数据
  • GuestForReservation 将包含 id + reservationId + guestId(因此您可以获得每个预订的客人列表)。 FK 用于 reservationId 和 guestId,PK 用于提到的合成 id。
  • 预订将包含 id(合成)、房间 id、日期从、日期到,可能是主要客人 id(如果对您有意义,它可能是支付账单的人)。 没有指向 GuestForReservation 表的链接,或者如果需要,您可以获取 GuestForReservation 列表。

当您要预订房间时,您有一个 ReservationRequest object,它将 go 到 ReservationService,这里您将通过 roomId 和日期查询 ReservationRepository。 如果没有返回任何内容,则创建各种实体并将它们保存在 ReservationRepository 和 GuestForReservation 存储库中。

通过使用该服务和各种存储库的组合,您应该能够获得所需的所有信息(每个房间的客人列表、每个日期的客人列表等)。 在服务级别,您然后 map 将您需要的数据传递给 DTO 并将其传递给 controller(以您需要的格式),甚至传递给其他服务(取决于您的需要)。

对于实体和 DTO 之间的映射,有不同的选择,您可以简单地创建一个名为 ReservationMapper 的组件(例如)并自己做(获取一个实体并根据您的需要构建一个 DTO); 从 Springframework 实现 Converter; 使用 MapStruct(在我看来很麻烦); 等等

如果你想在 JPA 中表示一个由多列组成的 id,通常使用@Embeddable类(使用它们时应该将它们标记为EmbeddedId ),你可以谷歌它们以获取更多信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM