当前位置: 首页 > 知识库问答 >
问题:

如何在Spring Boot中从双向表关系生成DTO

拓拔君博
2023-03-14

早上好,很高兴见到你。

我最近刚接触Spring Boot,我正在使用REST API,它基本上是一个包含歌曲的播放列表,基本上REST API应该具有以下结构。一个播放列表可以有许多歌曲:

{
    "name": "Lista 1",
    "description": "Lista de reproduccion 2020 spotify",
    "songs": [
        {
            "title": "Tan Enamorados",
            "artist": "CNCO",
            "album": "Tan Enamorados",
            "year": 2020,
            "playList": 1
        },
        {
            "title": "Hawai",
            "artist": "Maluma",
            "album": "PAPI JUANCHO",
            "year": 2020,
            "playList": 1
        }
    ]
}

目前,这是我配置实体的方式

实体歌曲

@Entity
@Table(name = "SONGS")
public class Songs{

    @JsonIgnore
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "ID")
    private Long id;

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

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

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

    @Column(name = "year")
    private int year;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "PLAY_LIST_ID")
    private PlayList playList;

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return this.title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtist() {
        return this.artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public String getAlbum() {
        return this.album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    public int getYear() {
        return this.year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public PlayList getPlayList() {
        return this.playList;
    }

    public void setPlayList(PlayList playList) {
        this.playList = playList;
    }
 
}

实体播放列表

@Entity
@Table(name = "PLAY_LIST")
public class PlayList {

    @JsonIgnore
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "ID")
    private Long id;

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

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


    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "playList")
    private List<Songs> songs = new ArrayList<>();


    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {
        this.description = description;
    }


    public List<Songs> getSongs() {
        return this.songs;
    }

    public void setSongs(List<Songs> songs) {
        this.songs = songs;
    }
    
}

控制器

@RestController
@RequestMapping("playlist")
public class PlayListController {

    @Autowired
    private PlayListService playListService;

    //Get playlist by id with songs belongs that playlist
    
    
    @GetMapping("/{id}")
    public Optional<PlayList> getPlayListByID(@PathVariable(value = "id") Long id) {

        Optional<PlayList> playList = playListService.getById(id);
        return playList;
     }

     
     @PostMapping("/create")
     public PlayList createPlayList(@RequestBody PlayList playList) {
         return playListService.savePlayList(playList);
     }

}

我的班级播放列表

@Service
public class PlayListServiceImpl implements PlayListService {

    @Autowired
    private PlayListRepository playListRepository;

    public Optional <PlayList> getById(Long Id) {
        Optional <PlayList> playList= playListRepository.findById(Id);

        return playList;
    }

    @Override
    public PlayList savePlayList(PlayList playList) {
        return playListRepository.save(playList);
    }
    
}

我的资源库

@Repository
public interface PlayListRepository extends JpaRepository<PlayList, Long> {

    Optional<PlayList> findById(Long Id);
    
}

但是,当我尝试使用get方法获取播放列表时,我遇到了无限递归问题:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.example.api.songs.entity.PlayList[\"songs\"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.api.songs.entity.Songs[\"playList\"]->com.example.api.songs.entity.PlayList[\"songs\"]->org.hibernate.

这是由于歌曲实体中的playList字段,当尝试获取包含歌曲的播放列表时,我会得到一个递归数组,而不是播放列表的id,这是我想要得到的,实际情况是,我得到了如下内容:

我可以通过在字段中应用JsonIgnore来解决,但是这会影响我,当我去保存播放列表时,我不能将idPlayList字段传递给每首歌,以确保每首歌都保存了相应的idPlayList

我认为最好的办法是在这里建立一个DTO,帮助我建立两个表之间的关系,但是,我不知道如何才能做到这一点,在执行播放列表的get时,只获取idPlayList,在省电时,将idPlayList传递到将与播放列表一起保存的每首歌曲中,或者,如果有另一种方式,我可以存储一个播放列表及其歌曲,并将其直接存储,因为目前我必须由BD为每首歌曲分配idPlayList

共有1个答案

全彬
2023-03-14

>

使用带有LEFT JOIN FETCH的JPQL查询加载带有歌曲的PlayList以获取带有列表的歌曲。所以歌曲将已经加载,并且歌曲中的playList将是懒惰的。

select list from PlayList list left join fetch list.songs where list.id = :listId

在Jackson中有Hibernate5Moules。它允许省略懒惰的关联。所以杰克逊不会在歌曲中触摸playList。配置Jackson以省略Spring Boot中的延迟加载属性

建立一个真正的系统。您可以在服务级别上添加PlayListContentSong Content类并将实体映射到这些类。在这种情况下,您可能不需要Hibernate5Moules。但是最好也尝试一下这个模块。

使用启用数据库登录application.properties

logging.level.org.springframework.transaction.interceptor=TRACE
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG

logging.level.org.hibernate.SQL=DEBUG
spring.jpa.properties.hibernate.use_sql_comments=true

仔细检查日志中的事务和SQL。不要让Hibernate产生不必要的查询。

如何用列表保存歌曲

将此方法添加到播放列表中

@Transient
public void addSong(Song song) {
    song.setPlayList(this);
    songs.add(song);
}

使用addSong()方法将歌曲添加到列表中。

保存播放列表。Hibernate将使用正确的列表id保存列表中的所有歌曲,因为在@OneToMany中有cascade=CascadeType.All

 类似资料:
  • 我试图在用户和地址之间建立双向关系, 用户1-------->*地址 但是 地址1-------->1个用户 我在上网时得到了这些信息 > 对于一对一双向关系,拥有方对应于包含对应外键的方 双向关系的反向侧必须通过使用OneToOne、 OneToMany或ManyToMany批注的mappedBy元素来引用其 所属侧。mappedBy元素指定作为 关系所有者的实体中的属性或字段。 但如果我按照情

  • 我很难找到如何在Spring中从@manytomany关系中检索数据。我遇到了无限递归问题,并尝试了一些解决方案,比如使用@JSONIgnoreProperties,但都没有成功。 一个用户可以有多个组,一个组可以有多个用户。我可以将用户添加到组中,但是当涉及到检索与用户关联的所有组时,我会从无限递归中得到堆栈溢出。 理想情况下,我希望能够发送带有用户ID的get请求,然后检索与该用户关联的所有G

  • 问题内容: 假设我有两个实体: 然后,我要保留“客户”实体,然后,参照先前添加的“客户”保留“订单”实体。当我从数据库中检索此客户并调用getOrders时,它将返回空集。这是正常行为吗?如果是,当我添加新的Order实体时,我该怎么做以自动刷新此集合? 问题答案: Jpa不会为您维护关系,因此应用程序需要设置双向关系的两端,以使它们与数据库保持同步。当您设置了order-> customer关系

  • 第404页的“Java持久性2.0,最终发布”有以下示例: 示例3:从一个可嵌入类到另一个实体的一对一关联。 我希望员工内部有多个位置详细信息: 如何更改实体ParkingSpot以指向集合表EMP_LOCATION内的可嵌入Location细节。 应该是 替换为@ElementCollection? 非常感谢。

  • 我正在浏览关于双向关系的Hibernate文档,文档中说: 例7.21。双向一对多,多对一为协会所有者 部队通过部队财产与士兵有双向的一对多关系。您不必(不得)在mappedBy side中定义任何物理映射。 要将双向一对多映射为一对多,并将一对多的一端作为拥有端,您必须删除mappedBy元素,并将多对一@JoinColumn设置为可插入且可更新为false。此解决方案未经优化,将生成其他更新语

  • 问题内容: 我有一个带有和对象的简单模型。 一个问题有很多选择。 许多选择有一个问题 有两种使用Hibernate来实现的方法 实施一:所有者是选择 Question.java Choice.java 实施二:所有者方是质询 Question.java Choice.java 两种实现之间有什么区别? 问题答案: 第一个示例是正常且正确的双向一对多/多对一映射。设置为-attribute(“拥有方