我正在尝试使用Spring Boot,Spring Data JPA,Hibernate,Gradle和Spring Data Rest在Spring MVC中实现@ManyToMany双向映射。当我实现我的两(2)个实体,事件和标签之间的映射。事件可以是零到多个标签,标签可以与零到多个事件相关联。事实上,我能够通过映射实体的setter成功地将POJO保存到hibernate会话中,包括HashSet<>()。但是,只要我尝试通过诸如.findAll()
之类的CrudRepository方法查询POJO列表,它就会立即爆炸。看一下控制台输出,看起来Hibernate试图将其他对象追加100次。当我使用@RestController进行列表操作(.findAll()
)时,当我导航RESTful端点时,它不是仅显示两个JSON对象,而是显示大量未关闭的JSON片段。控制台输出也表明jackson数据绑定可能在序列化Hibernate正在输出的.findAll()
结果时遇到问题。
非常感谢任何帮助。对我来说最大的困惑是为什么Hibernate会尝试多次追加结果字符串。这可能是一个问题:
fetch = FetchType.EAGER
?的build.gradle
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-rest")
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.hibernate:hibernate-validator")
compile("org.apache.tomcat.embed:tomcat-embed-el")
compile("com.h2database:h2")
}
Event.java
package event.model;
import org.hibernate.validator.constraints.Email;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*;
import javax.validation.constraints.Future;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.*;
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"title", "date"})})
public class Event {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotNull
@Size(min = 2, max = 55)
private String title;
// .. some additional private variables
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "events_tags",
joinColumns=@JoinColumn(name = "event_id"),
inverseJoinColumns = @JoinColumn(name="tag_id")
)
private Set<Tag> tags = new HashSet<>();
public Event() {
}
public Event(String title, String description, Date date, String organizer, String email, String location) {
this.title = title;
this.description = description;
this.date = date;
this.organizer = organizer;
this.email = email;
this.location = location;
}
public long getId() {
return id;
}
// .. GETTERS and SETTERS for other properties
public Set<Tag> getTags(){
return tags;
}
public void setTags(Set<Tag> tags){
this.tags = tags;
}
// @Overide toString() method
}
Tag.java
package event.model;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.*;
@Entity
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotNull
@Size(min = 3, max = 255)
private String title;
@ManyToMany(mappedBy = "tags", fetch = FetchType.EAGER)
private Set<Event> events = new HashSet<>();
public Tag() {
}
public Tag(String title) {
this.title = title;
}
public long getId() {
return id;
}
// .. GETTERS AND SETTERS
public Set<Event> getEvents() {
return events;
}
public void setEvents(Set<Event> events){
this.events = events;
}
// @Override toString() method
}
Application.java
@SpringBootApplication
public class Application implements CommandLineRunner {
@Autowired
EventRepository eventRepository;
@Autowired
TagRepository tagRepository;
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... strings) throws Exception {
SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy HH:mm a");
String[] dateStrings = {"07-25-2015 12:00 PM", "08-25-2015 12:00 PM"};
Date date1 = formatter.parse(dateStrings[0]);
Date date2 = formatter.parse(dateStrings[1]);
Event event1 = eventRepository.save(new Event("Bacon Day", "This day is about bacon. I just need so much bacon all the time", date1, "John Smith", "john.smith@test.com", "W523"));
Event event2 = eventRepository.save(new Event("Salad Day", "This day is about salad. I just need so much salad all the time", date2, "Jane Smith", "jane.smith@test.com", "N423"));
Tag tag1 = tagRepository.save(new Tag("Savory"));
Tag tag2 = tagRepository.save(new Tag("Vegetarian"));
event1.getTags().add(tag1);
event1.getTags().add(tag2);
event1 = eventRepository.save(event1);
System.out.println(event1);
System.out.println(eventRepository.findAll());
}
}
控制台错误输出
Hibernate: select events0_.tag_id as tag_id2_2_0_, events0_.event_id as event_id1_1_0_, event1_.id as id1_0_1_, event1_.date as date2_0_1_, event1_.description as descript3_0_1_, event1_.email as email4_0_1_, event1_.location as location5_0_1_, event1_.organizer as organize6_0_1_, event1_.title as title7_0_1_ from events_tags events0_ inner join event event1_ on events0_.event_id=event1_.id where events0_.tag_id=?
2015-07-13 18:35:28.084 ERROR 4088 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler processing failed; nested exception is java.lang.StackOverflowError] with root cause
java.lang.StackOverflowError: null
at java.util.Date.getYear(Date.java:651)
at java.sql.Timestamp.toString(Timestamp.java:279)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at event.model.Event.toString(Event.java:128)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at java.util.AbstractCollection.toString(AbstractCollection.java:462)
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:317)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at event.model.Tag.toString(Tag.java:51)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at java.util.AbstractCollection.toString(AbstractCollection.java:462)
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:317)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at event.model.Event.toString(Event.java:128)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at java.util.AbstractCollection.toString(AbstractCollection.java:462)
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:317)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at event.model.Tag.toString(Tag.java:51)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at java.util.AbstractCollection.toString(AbstractCollection.java:462)
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:317)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
继续增加数百行。
答案 0 :(得分:3)
尝试在父级集合的子类中使用@JsonIgnore
。它试图递归地序列化。
Follow这个链接用于处理此问题的其他方法。
答案 1 :(得分:1)
我遇到了类似的问题并使用 @JsonBackReference 解决了这个问题:
@ManyToMany(mappedBy = "tags")
@JsonBackReference
private Set<Event> events = new HashSet<>();