JPA - EntityManager查找方法返回重复项

时间:2016-05-04 10:20:08

标签: java spring hibernate jpa

我有两个子实体列表的实体。调用EntityManager.find()时,它会根据两个列表的乘法返回重复项。

我使用Spring,Jackson和JPA与Hibernate和SQL Server。

在使用第一个 N 元素和第二个子实体 M 的父级进行测试时,它始终返回两个实体的 N * M 元素

例如,下面有 3 任务 5 评论,JPA返回 15 对于两个列表。 (任务列表5份,评论列表3份)

控制器的输出是:

Comments 15
Tasks 15

其余代码如下。

controller.java

@RequestMapping(method = RequestMethod.GET)
public String listAll(Model model) {

    Goal goal = new Goal();

    goal = service.getGoalById(25);

    System.out.println("Comments " + goal.getComments().size());
    System.out.println("Tasks " + goal.getTasks().size());

    return "home";
}

service.java

@Transactional
public Goal getGoalById(int goalId) {
    Goal goal = em.find(Goal.class, goalId);

    return goal;
}

goal.java

@Entity
@Table(name = "goal")
public class Goal {

@Id
@GeneratedValue
private int id;

@JsonManagedReference
@OneToMany(mappedBy = "tasksGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private List<Task> projectTasks = new ArrayList<Task>();

@JsonManagedReference
@OneToMany(mappedBy = "commentsGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private List<Comment> goalComments = new ArrayList<Comment>();

...

}

task.java

@Entity
@Table(name="projectTask")
public class Task {

@Id
@GeneratedValue
private int id;

@JsonBackReference
@ManyToOne (fetch = FetchType.LAZY)
@JoinColumn(name = "task_goal_id")
private Goal tasksGoal;

...

}

comment.java

@Entity
@Table (name="goalComment")
public class Comment {

@Id
@GeneratedValue
private int id;

@JsonBackReference
@ManyToOne (fetch = FetchType.LAZY)
@JoinColumn(name = "goal_id")
private Goal commentsGoal;

...

}

的persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit"
    transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServer2008Dialect" />
        <property name="hibernate.hbm2ddl.auto" value="update" />
        <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
        <property name="hibernate.connection.charSet" value="UTF-8" />

        <!-- Hibernate prints SQL -->
        <property name="hibernate.show_sql" value="true" />

    </properties>
</persistence-unit>

2 个答案:

答案 0 :(得分:2)

这是因为你的fetch = FetchType.EAGER。 Hibernate尝试一次性获取所有内容,即对链接到&#34; master&#34;的每个元素条目使用一个单独的查询。宾语。     Hibernate创建JOIN并尝试通过一个查询获取所有集合。

如果将@Fetch(FetchMode.SELECT)注释添加到集合中,则可以以N + 1查询为代价成功解决此问题。

如果你真的需要FetchType.EAGER并且不想用Set替换你的集合,你可以为你的集合使用@Fetch(FetchMode.SELECT)注释:     @JsonManagedReference     @OneToMany(mappedBy =&#34; tasksGoal&#34;,cascade = CascadeType.PERSIST,fetch = FetchType.EAGER)     @Fetch(FetchMode.SELECT)     private List projectTasks = new ArrayList();

@JsonManagedReference
@OneToMany(mappedBy = "commentsGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@Fetch (FetchMode.SELECT)
private List<Comment> goalComments = new ArrayList<Comment>();

答案 1 :(得分:0)

@Fetch (FetchMode.SELECT) 

可以帮到你。它是默认的提取策略。它启用了所有相关集合的延迟加载。