首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在JPA中处理循环关系

在JPA中处理循环关系
EN

Stack Overflow用户
提问于 2021-04-26 07:34:05
回答 1查看 31关注 0票数 0

我正在用JavaEE为一个管理房间预订的应用程序开发REST API。我有以下问题:

我的应用程序有3个实体类:

代码语言:javascript
复制
@Entity
public class Reservation {

    @GeneratedValue
    @Id
    private Integer id;
    @ManyToOne
    @JoinColumn(name="room_id")
    private Room room;
    private LocalDateTime reservationStart;
    private LocalDateTime reservationEnd;
    @ManyToOne
    @JoinColumn(name="user_id")
    private User user;
代码语言:javascript
复制
@Entity
public class Room {
    @GeneratedValue
    @Id
    private Integer id;
    private String description;
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "room")
    private List<Reservation> reservations;
}
代码语言:javascript
复制
@Entity
public class User {
    @Id
    @GeneratedValue
    private Integer id;
    private String firstName;
    private String surname;
    private UserRole role;
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
    private List<Reservation> reservations;
}

在使用JPA的DAO中的一个查询中,我希望检索属于用户的所有数据:个人数据、用户的预订和每个预订的房间详细信息,而不创建无限数量的嵌套对象:

代码语言:javascript
复制
{
  "user": {
    "id": "id",
    "firstname": "name",
    "lastname": "lastname",
    "email": "email",
    "Reservations": [
      {
      "id" : "1234",
      "startdate": "S",
      "stopdate": "date",
      "room": {
        "id": "id",
        "description": "somedescription"
      }
    },
    {
      "id" : "2134",
      "startdate": "S",
      "stopdate": "date",
      "room": {
        "id": "id",
        "description": "description"
      }
    }
    ]
  }
}

天真的方法:

代码语言:javascript
复制
    public User read(Integer id) {
        return entityManager.find(User.class, id);
    }

显然返回LazyInitializationError,预留以PersistentBag而不是List的形式返回,因为它们不是初始化的。

代码语言:javascript
复制
    public User read(Integer id) {
        User user = entityManager.find(User.class, id);
        user.getReservations();
        return user;
    }

返回相同的值。

也是如此:

代码语言:javascript
复制
    public User read(Integer id) {
        Query query = entityManager.createQuery("select u from User u " +
                "join fetch u.reservations r " +
                "where u.id = :id");
        query.setParameter("id", id);
        Object singleResult = query.getSingleResult();
        return (User) singleResult;
    }

切换到FetchType.Eager以RESTEASY008205: JSON绑定序列化错误java.lang.StackOverflowError结束,我认为这是因为yasson试图做无限深的嵌套JSON。

我不想在@ManyToOne关系上使用@JsonbTransient注释,因为我还希望能够检索属于某个房间的所有预订,例如按日期过滤,并在这些预订中包含预订它们的人的信息,因此我需要这些字段。

我可以使用一个简单的SQL查询来检索数据(在控制台):

代码语言:javascript
复制
select * from User
         LEFT JOIN Reservation as reservation on user_id = reservation.user_id
         LEFT JOIN Room as room on reservation.room_id = room.id
WHERE User_id = 1

但是entityManager.createNativeQuery返回了

代码语言:javascript
复制
NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias [id] during auto-discovery of a native-sql query

JAX-RS端点被标记为事务性。它有一个直接注入的DAO对象,而DAO对象又有一个注入的EntityManager。我使用的是JBOSS 20.0.1FINAL,Hibernate 5.4.9,JAVAEE 8.0.1,Java 1.8。

也许这只是一个好的JPQL查询的问题,但到目前为止,我已经花了两天多的时间在它上面,仍然不能让它工作(但学到了很多关于调试Hibernate的知识),所以任何帮助都将不胜感激。

EN

回答 1

Stack Overflow用户

发布于 2021-04-28 16:30:00

通常这是通过引入DTO来解决的,我认为这是Blaze-Persistence Entity Views的一个完美用例。

我创建了这个库,以便在JPA模型和自定义接口或抽象类定义的模型之间轻松映射,就像Spring数据在类固醇上的投影一样。其思想是以您喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(Getter)映射到实体模型。

使用Blaze-Persistence实体视图,您的用例的DTO模型可能如下所示:

代码语言:javascript
复制
@EntityView(User.class)
public interface UserDto {
    @IdMapping
    Integer getId();
    String getFirstname();
    String getLastname();
    String getEmail();
    Set<ReservationDto> getReservations();

    @EntityView(Reservation.class)
    interface ReservationDto {
        @IdMapping
        Integer getId();
        @Mapping("reservationStart")
        LocalDateTime getStartdate();
        @Mapping("reservationEnd")
        LocalDateTime getEnddate();
        RoomDto getRoom();
    }
    @EntityView(Room.class)
    interface RoomDto {
        @IdMapping
        Integer getId();
        String getDescription();
    }
}

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

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

Spring数据集成允许您像使用Spring数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

代码语言:javascript
复制
Page<UserDto> findAll(Pageable pageable);

最好的部分是,它将只获取实际需要的状态!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67259199

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档