ManyToMany collection keeps getting deleted

给你一囗甜甜゛ 提交于 2020-01-06 10:53:44

问题


I have a class with several lazily initialized collections, both OneToMany and ManyToMany. The ManyToMany relationship is unidirectional, so the Expert class has no Project collection.

@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);
}

To add an object to a OneToMany collection, I would do this:

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

This works fine. However, when I try to add an element to the ManyToMany collection, the collection seems to be deleted upon saving the parent object, so I can never add more than one child object:

@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();
}

This is what the service method looks like (projectRepository extends PagingAndSortingRepository):

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

In the logs I found this:

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

Edit 1: This is what happens upon 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

Edit 2: Apparently the ManyToMany collection in the parent object projectForm.getProject() is initialized and empty. How can that be? I expect it to be unitialized, or if it is initialized for some reason, not empty?


回答1:


Create a method in your service like this:

@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;
}

This will encapsulate the fetching of Project.experts inside a Transaction so that the lazily loaded collection can be fetched and initialised properly. By adding the list you have determined in your controller, you can then save the project properly and have the cascade create the new expert objects. I'm pretty sure this should fix your problem.

If you don't see INSERT statements creating your Expert objects, then thats your problem. Hibernate can't insert a reference to your Expert objects into your JoinTable if they don't exist in the database, hence why it would delete your collection object.




回答2:


I think I might have the culprit.

You're initializing the ManyToMany collection as

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

and then your add() method accesses the collection as this.experts.add(..). Yet the ManyToMany association is LAZY. So how would the JPA provider know that you didn't intend to override the collection with a new one instead?

Try changing add() with the getter for the collection, as

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


来源:https://stackoverflow.com/questions/23410753/manytomany-collection-keeps-getting-deleted

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!