【问题标题】:JPA OneToMany , how to ignore field ManyToOne when print in console or webappJPA OneToMany,如何在控制台或 webapp 中打印时忽略字段 ManyToOne
【发布时间】:2018-12-21 20:41:57
【问题描述】:

我有 2 个表:genres(genre_id,genre_name) 和 movies (movie_id,movie_name,movie_score,genre_id)。电影中的genre_id_fk 引用了流派中的genre_id。

@Entity
@Table(name = "genres", schema = "test")
public class Genre {

@Id
@Column(name = "genre_id")
private int id;
@Column(name = "genre_name")
private String name;
@OneToMany(mappedBy = "genre", fetch = FetchType.LAZY)
private List<Movie> movies = new ArrayList<>();
}

电影的第二个实体

@Entity
@Table(name = "movies", schema = "test")
public class Movie {

@Id
@GeneratedValue
@Column(name = "movie_id")
private int id;
@Column(name = "movie_name")
private String name;
@Column(name = "movie_score")
private double score;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "genre_id")
private Genre genre;
}

当我尝试使用此代码在控制台中打印它时:

public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu");
    EntityManager em = emf.createEntityManager();
    Genre genre = em.find(Genre.class, 1);
    System.out.println(genre);
}

接收Exception in thread "main" java.lang.StackOverflowError 只有从电影类字段“流派”中的 toString() 中删除才能修复它。但是有可能避免吗? Spring Boot 应用程序也存在同样的问题

@RestController
public class GenreController {

@Autowired
private GenreService genreService;

@RequestMapping("/test/{id}")
public List<Genre> getGenreInfo(@PathVariable int id){
    return genreService.getGenreFilms(id);
}
}

这里服务

@Service
public class GenreService {

public List<Genre> getGenreFilms(int id){
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu");
    EntityManager em = emf.createEntityManager();
    List<Genre> genres = new ArrayList<>();
    Genre genre = em.find(Genre.class, id);
    genres.add(genre);
    return genres;
}
}

并收到这样的问题: [{"id":1,"name":"Thriller","movies":[{"id":1,"name":"Any, 你还好吗?","score":5.45,"genre" :{"id":1,"name":"Thriller","movies":[{"id":1,"name":"Any, 你还好吗?","score":5.45,"genre" :{"id":1,"name":"Thriller","movies":[{"id":1,"name":"Any, 你还好吗?","score":5.45,"genre" :.... 和无穷大,有一些例外。控制台我可以通过忽略 toString() 方法中的字段来修复。但是如何在 webapplication 中解决这个问题呢?

这里是控制台打印时休眠调试日志

22:15:37.154 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - 
Resolving associations for [com.company.Genre#1]
22:15:37.164 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done 
materializing entity [com.company.Genre#1]
22:15:37.164 [main] DEBUG 
org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl - 
HHH000387: ResultSet's statement was not registered
22:15:37.165 [main] DEBUG 
org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader - Done 
entity load : com.company.Genre#1
22:15:37.165 [main] DEBUG 
org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl - 
Initiating JDBC connection release from afterTransaction
22:15:37.167 [main] DEBUG org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer 
- Loading collection: [com.company.Genre.movies#1]
22:15:37.167 [main] DEBUG org.hibernate.SQL - select movies0_.genre_id as 
genre_id4_1_0_, movies0_.movie_id as movie_id1_1_0_, movies0_.movie_id as 
movie_id1_1_1_, movies0_.genre_id as genre_id4_1_1_, movies0_.movie_name as 
movie_na2_1_1_, movies0_.movie_score as movie_sc3_1_1_ from test.movies 
movies0_ where movies0_.genre_id=?
Hibernate: select movies0_.genre_id as genre_id4_1_0_, movies0_.movie_id as 
movie_id1_1_0_, movies0_.movie_id as movie_id1_1_1_, movies0_.genre_id as 
genre_id4_1_1_, movies0_.movie_name as movie_na2_1_1_, movies0_.movie_score 
as movie_sc3_1_1_ from test.movies movies0_ where movies0_.genre_id=?
22:15:37.168 [main] DEBUG 
org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl - 
Preparing collection intializer : [com.company.Genre.movies#1]
22:15:37.170 [main] DEBUG 
org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl - 
Starting ResultSet row #0
22:15:37.171 [main] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerIm 
pl - Found row of collection: [com.company.Genre.movies#1]
22:15:37.171 [main] DEBUG 
org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl - 
Starting ResultSet row #1
22:15:37.172 [main] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerIm 
pl - Found row of collection: [com.company.Genre.movies#1]
22:15:37.172 [main] DEBUG 
org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl - 
Starting ResultSet row #2
22:15:37.172 [main] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerIm 
pl - Found row of collection: [com.company.Genre.movies#1]
22:15:37.172 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - 
Resolving associations for [com.company.Movie#1]
22:15:37.172 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done 
materializing entity [com.company.Movie#1]
22:15:37.173 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - 
Resolving associations for [com.company.Movie#2]
22:15:37.173 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done 
materializing entity [com.company.Movie#2]
22:15:37.173 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - 
Resolving associations for [com.company.Movie#3]
22:15:37.173 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done 
materializing entity [com.company.Movie#3]
22:15:37.173 [main] DEBUG 
org.hibernate.engine.loading.internal.CollectionLoadContext - 1 collections 
were found in result set for role: com.company.Genre.movies
22:15:37.173 [main] DEBUG 
org.hibernate.engine.loading.internal.CollectionLoadContext - Collection 
fully initialized: [com.company.Genre.movies#1]
22:15:37.173 [main] DEBUG 
org.hibernate.engine.loading.internal.CollectionLoadContext - 1 collections 
initialized for role: com.company.Genre.movies
22:15:37.173 [main] DEBUG 
org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl - 
HHH000387: ResultSet's statement was not registered
22:15:37.173 [main] DEBUG org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer 
- Done loading collection
Exception in thread "main" java.lang.StackOverflowError

告诉我哪里做错了,如何解决或用谷歌搜索这个问题?只是不通过 localhost:8080/genre/id 打印此对象?做一些特别的印刷品还是什么?

【问题讨论】:

    标签: java spring-boot jpa


    【解决方案1】:

    在尝试 toString 您的 Genre 实体时,您似乎有无限递归。您的代码首先通过 id 加载您的 Genre 实体,然后调用 Genre.toString()。因为您与Movies 有@OneToMany 关系,所以它@98​​7654327@ 列表,然后为与该类型相关的每部电影调用Movie.toString()。然后,对于每部电影,您都与 Genre 建立了@ManyToOne 关系。这就是问题所在。它将为列表中的每部电影再次调用Genre.toString()

    可能的解决方案

    1. 如果您只想在控制台中简单打印,请不要在Genre.toString() 中包含电影列表
    2. 如果您使用的是Jackson,请将@JsonBackReference 添加到Movie 中的@ManyToOne 关系中,这样Json 在映射到Json 时会忽略它 Annotation documentation here
    3. 如果使用 DTO,请不要在 DTO 中包含 Movie 属性。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-19
      • 2016-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-24
      • 1970-01-01
      相关资源
      最近更新 更多