2014-05-01 3 views
0

У меня есть класс с несколькими лениво инициализированными коллекциями, как OneToMany, так и ManyToMany. Отношение ManyToMany является однонаправленным, поэтому класс 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 

Edit 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 

Edit 2: Видимо ManyToMany коллекции в родительском объекте projectForm.getProject() инициализирован и пуст. Как это может быть? Я ожидаю, что он будет унифицирован или если он по какой-то причине инициализирован, не пуст?

+0

Не могли бы вы показать метод 'add' класса' Project'? Это полиморфный метод, взяв либо аргумент 'Logging', либо' Expert', так? и 'projectService.save()' тоже. –

+0

Я отредактировал мое сообщение соответственно. – user3170702

+0

Измените FetchType.LAZY на FetchType.EAGER и убедитесь, что это хотя бы дает значения внутри getProject(). Если вы объявите этот объект лениво загруженным, Hibernate инициализирует пустую коллекцию и не будет заполнять ее до тех пор, пока вы не попытаетесь ее использовать. – JamesENL

ответ

0

Думаю, у меня может быть преступник.

Вы инициализации коллекции ManyToMany в

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

Хорошо, я посмотрю позже. Кстати, зачем вам 'CascadeType.ALL' в ваших отношениях ManyToMany? –

+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' никогда не создаются, в базе данных имеется более или менее фиксированный набор. Ваше предложение работает, но только если я явно получаю проект из базы данных, получаю его коллекцию «экспертов», добавляю «эксперт» и присоединяю коллекцию к новому объекту «project» с контроллера. В контроллере я вызываю службу 'save', и все хорошо. Это нормально? Благодаря! – user3170702

+0

Я также заметил, что коллекция удаляется, когда я просто делаю сохранение, поэтому пришлось изменить службу 'save', чтобы сначала получить коллекцию из базы данных. Что я могу с этим поделать? – user3170702

+0

Поскольку ваши коллекции лениво загружены, Hibernate не вытаскивает их из базы данных, если вы специально не зададите ее. Вы можете установить свою коллекцию как 'FetchType.EAGER', чтобы коллекция была инициализирована, когда вы вывели свой объект из базы данных. – JamesENL

Смежные вопросы