Spring MVC JPA @ManyToMany双向

时间:2015-07-13 23:48:28

标签: java spring-mvc jpa

希望事情进展顺利。

我正在尝试使用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会尝试多次追加结果字符串。这可能是一个问题:

  1. @ManyToMany映射配置?标签实体需要@JoinColumns吗?
  2. fetch = FetchType.EAGER
  3. 缺乏/过度的GETTER / SETTER?从事件或标记实体中删除/添加GETTER / SETTER?
  4. Spring Boot和/或Gradle?我可以在互联网上找到的任何其他例子都是使用Maven。
  5. 依赖关系? spring-starter-boot-starter-data-jpa vs spring-data-jpa?
  6. 构造? (Event.java没有标签的参数构造函数)
  7. HashSet<>()事件和/或代码的属性类型?
  8. 的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)
    

    继续增加数百行。

2 个答案:

答案 0 :(得分:3)

尝试在父级集合的子类中使用@JsonIgnore。它试图递归地序列化。

Follow这个链接用于处理此问题的其他方法。

答案 1 :(得分:1)

我遇到了类似的问题并使用 @JsonBackReference 解决了这个问题:

@ManyToMany(mappedBy = "tags")
@JsonBackReference
private Set<Event> events = new HashSet<>();