我有一个包含几个懒惰初始化集合的类,OneToMany
和ManyToMany
。 ManyToMany
关系是单向的,因此Expert
类没有Project
集合。
@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 :(得分: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;
}
这将封装在Transaction中获取Project.experts,以便可以正确地获取和初始化延迟加载的集合。通过添加您在控制器中确定的列表,您可以正确保存项目并让级联创建新的专家对象。我很确定这可以解决你的问题。
如果您没有看到创建INSERT
个对象的Expert
个语句,那就是您的问题。如果数据库中不存在Expert
对象,则Hibernate无法将JoinTable
对象的引用插入{{1}},因此它会删除您的集合对象。
答案 1 :(得分:0)
我想我可能有罪魁祸首。
您正在将ManyToMany集合初始化为
private List<Expert> experts = new ArrayList<Expert>();
然后您的add()
方法以this.experts.add(..)
的形式访问该集合。然而,ManyToMany协会是LAZY
。那么JPA提供商怎么会知道你不打算用新的覆盖集合覆盖集合呢?
尝试使用集合的getter更改add()
,如
public void add(Expert expert) {
this.getExperts().add(expert);
}