2014-05-01 27 views
0

我有一个类,有几个懒惰的初始化收藏,OneToManyManyToManyManyToMany关系是单向的,因此Expert类没有Project集合。ManyToMany收藏不断被删除

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "project") 
private List<Logging> loggings = new ArrayList<Logging>(); 

@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
@JoinTable(name = "project_expert", joinColumns = { @JoinColumn(name = "project") }, inverseJoinColumns = { @JoinColumn(name = "expert") }) 
private List<Expert> experts = new ArrayList<Expert>(); 

public void add(Logging logging) { 
    logging.setProject(this); 
    this.loggings.add(logging); 
} 

public void add(Expert expert) { 
    this.experts.add(expert); 
} 

对象添加到OneToMany集合,我这样做:

Project project = projectService.save(projectForm.getProject()); 
Logging logging = new Logging(); 
project.add(logging); 
logging = loggingService.save(logging); 

这工作得很好。然而,当我尝试的元素添加到ManyToMany,那么这个集合似乎在保存父对象被删除了,所以我不能添加多个子对象:

@RequestMapping(value="/newexpert", method=RequestMethod.POST) 
public String newExpert(@ModelAttribute("projectForm") ProjectForm projectForm, BindingResult result, ModelMap model) { 

    Project project = projectService.save(projectForm.getProject()); 
    Expert expert = projectForm.getExpert(); 
    if (expert != null) { 
     project.add(expert); 
    } 
    project = projectService.save(project); 

    return "redirect:/project/" + project.getId(); 
} 

这是什么服务方法看起来像(projectRepository扩展PagingAndSortingRepository):

@Override 
@Transactional 
public Project save(Project project) { 
    return projectRepository.save(project); 
} 

在日志中,我发现这一点:

2014-05-01 17:01:49 DEBUG AbstractCollectionPersister:1174 - Deleting collection: [test.model.Project.experts#1] 
2014-05-01 17:01:49 DEBUG SQL:109 - delete from project_expert where project=? 
2014-05-01 17:01:49 DEBUG AbstractCollectionPersister:1232 - Done deleting collection 

编辑1:这是在save(projectForm.getProject())会发生什么:

2014-05-01 18:30:04 DEBUG Loader:2136 - Loading entity: [test.model.Project#1] 
2014-05-01 18:30:04 DEBUG SQL:109 - select project0_.id as id1_4_1_, ... from Project project0_ left outer join project_expert experts1_ on project0_.id=experts1_.project left outer join Expert expert2_ on experts1_.expert=expert2_.id where project0_.id=? 
2014-05-01 18:30:04 DEBUG Loader:951 - Result set row: 0 
2014-05-01 18:30:04 DEBUG Loader:1485 - Result row: EntityKey[test.model.Expert#2], EntityKey[test.model.Project#1] 
2014-05-01 18:30:04 DEBUG Loader:1305 - Found row of collection: [test.model.Project.experts#1] 
2014-05-01 18:30:04 DEBUG TwoPhaseLoad:160 - Resolving associations for [test.model.Project#1] 
2014-05-01 18:30:04 DEBUG TwoPhaseLoad:286 - Done materializing entity [test.model.Project#1] 
2014-05-01 18:30:04 DEBUG CollectionLoadContext:232 - 1 collections were found in result set for role: test.model.Project.experts 
2014-05-01 18:30:04 DEBUG CollectionLoadContext:280 - Collection fully initialized: [test.model.Project.experts#1] 
2014-05-01 18:30:04 DEBUG CollectionLoadContext:240 - 1 collections initialized for role: test.model.Project.experts 
2014-05-01 18:30:04 DEBUG Loader:2160 - Done entity load 
2014-05-01 18:30:04 DEBUG AbstractTransactionImpl:175 - committing 
2014-05-01 18:30:04 DEBUG AbstractFlushingEventListener:149 - Processing flush-time cascades 
2014-05-01 18:30:04 DEBUG AbstractFlushingEventListener:189 - Dirty checking collections 
2014-05-01 18:30:04 DEBUG CollectionEntry:202 - Collection dirty: [test.model.Project.loggings#1] 
2014-05-01 18:30:04 DEBUG CollectionEntry:202 - Collection dirty: [test.model.Project.parties#1] 
2014-05-01 18:30:04 DEBUG CollectionEntry:202 - Collection dirty: [test.model.Project.experts#1] 
2014-05-01 18:30:04 DEBUG Collections:194 - Collection found: [test.model.Project.experts#1], was: [test.model.Project.experts#1] (initialized) 
2014-05-01 18:30:04 DEBUG Collections:201 - Collection found: [test.model.Project.loggings#1], was: [test.model.Project.loggings#1] (uninitialized) 
2014-05-01 18:30:04 DEBUG Collections:201 - Collection found: [test.model.Project.parties#1], was: [test.model.Project.parties#1] (uninitialized) 
2014-05-01 18:30:04 DEBUG AbstractFlushingEventListener:123 - Flushed: 0 insertions, 0 updates, 0 deletions to 3 objects 
2014-05-01 18:30:04 DEBUG AbstractFlushingEventListener:130 - Flushed: 0 (re)creations, 3 updates, 0 removals to 3 collections 
2014-05-01 18:30:04 DEBUG EntityPrinter:114 - Listing entities: 
2014-05-01 18:30:04 DEBUG EntityPrinter:121 - test.model.Expert{id=2, ...} 
2014-05-01 18:30:04 DEBUG EntityPrinter:121 - test.model.Project{..., id=1, ...} 
2014-05-01 18:30:04 DEBUG EntityPrinter:121 - test.model.Expert{id=1, ...} 
2014-05-01 18:30:04 DEBUG SQL:109 - select count(id) from Logging where project_id =? 
2014-05-01 18:30:04 DEBUG SQL:109 - select count(id) from Party where project_id =? 
2014-05-01 18:30:04 DEBUG AbstractCollectionPersister:1174 - Deleting collection: [test.model.Project.experts#1] 
2014-05-01 18:30:04 DEBUG SQL:109 - delete from project_expert where project=? 
2014-05-01 18:30:04 DEBUG AbstractCollectionPersister:1232 - Done deleting collection 
2014-05-01 18:30:04 DEBUG JdbcTransaction:113 - committed JDBC Connection 
2014-05-01 18:30:04 DEBUG JdbcTransaction:126 - re-enabling autocommit 
... 
2014-05-01 18:30:04 DEBUG AbstractLoadPlanBasedCollectionInitializer:88 - Loading collection: [test.model.Project.loggings#1] 
2014-05-01 18:30:04 DEBUG SQL:109 - select loggings0_.project_id as project_5_4_0_, loggings0_.id as id1_2_0_, ... from Logging loggings0_ where loggings0_.project_id=? 
2014-05-01 18:30:04 DEBUG ResultSetProcessorImpl:168 - Preparing collection intializer : [test.model.Project.loggings#1] 
2014-05-01 18:30:04 DEBUG ResultSetProcessorImpl:127 - Starting ResultSet row #0 
2014-05-01 18:30:04 DEBUG CollectionReferenceInitializerImpl:77 - Found row of collection: [test.model.Project.loggings#1] 
2014-05-01 18:30:04 DEBUG TwoPhaseLoad:160 - Resolving associations for [test.model.Logging#1] 
2014-05-01 18:30:04 DEBUG TwoPhaseLoad:286 - Done materializing entity [test.model.Logging#1] 
2014-05-01 18:30:04 DEBUG CollectionLoadContext:232 - 1 collections were found in result set for role: test.model.Project.loggings 
2014-05-01 18:30:04 DEBUG CollectionLoadContext:280 - Collection fully initialized: [test.model.Project.loggings#1] 
2014-05-01 18:30:04 DEBUG CollectionLoadContext:240 - 1 collections initialized for role: test.model.Project.loggings 
2014-05-01 18:30:04 DEBUG AbstractLoadPlanBasedCollectionInitializer:118 - Done loading collection 

编辑2:显然,ManyToMany集合中的父对象projectForm.getProject()初始化而空。怎么可能?我期望它是单元化的,或者如果它由于某种原因被初始化,不是空的?

+0

您能否显示'Project'类的'add'方法?这是一种多态的方法,可以采用Logging或Expert这个参数,对吧?和'projectService.save()'。 –

+0

我已经编辑了我的帖子。 – user3170702

+0

将FetchType.LAZY更改为FetchType.EAGER,并查看它是否至少在getProject()中提供了值。如果将该对象声明为lazily加载,Hibernate将初始化一个空集合,直到尝试使用它时才会填充它。 – JamesENL

回答

0

我想我可能有罪魁祸首。

你初始化多对多集合作为

private List<Expert> experts = new ArrayList<Expert>(); 

,然后你add()方法访问集合为this.experts.add(..)。然而ManyToMany协会是LAZY。那么JPA提供程序如何知道您不打算用新的集合重写集合呢?

尝试改变add()与吸气的集合,如

public void add(Expert expert) { 
    this.getExperts().add(expert); 
} 
+0

不幸的是,这并没有奏效。我添加了更多日志,因为当我保存从窗体返回的对象时,可以看到集合被删除。该对象没有任何初始化的集合,但这不会导致'OneToMany'集合被覆盖。我认为它也适用于'ManyToMany'集合。 – user3170702

+1

好的,我稍后再看看。顺便说一句,为什么你需要在你的ManyToMany关系中使用'CascadeType.ALL'? –

+0

正确,我不需要'CascadeType.ALL',会改变它。 – user3170702

1

在服务这​​样的创建方法:

@Transactional 
public Project addExperts(Project project, List<Expert> experts){ 
    List<Expert> projectExperts = project.getExperts(); 
    for(Expert expert: experts){ 
     projectExperts.add(expert); 
    } 

    project.setExperts(projectExperts); 
    return project; 
} 

这将封装在一个事务中Project.experts的取这样可以正确地取得并初始化延迟加载的集合。通过添加您在控制器中确定的列表,然后可以正确保存项目并使级联创建新的专家对象。我很确定这应该解决您的问题。

如果您没有看到INSERT声明创建您的Expert对象,那多数民众赞成你的问题。如果它们不存在于数据库中,Hibernate无法将对Expert对象的引用插入到JoinTable中,因此它会删除您的集合对象。

+0

事实上,没有'Expert'对象被创建,数据库中有一个或多或少的固定集合。你的建议是有效的,但是只有当我明确地从数据库中获取项目,获得其'expert'集合,添加'expert',并将集合附加到来自控制器的新'project'对象时。然后在控制器中调用'save'服务,一切正常。这是正常的吗?谢谢! – user3170702

+0

我也注意到当我只做一次保存时,集合被删除,所以必须将'save'服务更改为首先从数据库中获取集合。我能做些什么呢? – user3170702

+0

因为你的集合是懒惰加载的,所以Hibernate不会将它们从数据库中拉出来,除非你特别问它。您可以将您的集合设置为“FetchType.EAGER”,以便在将对象从数据库中取出时集合被初始化。 – JamesENL