2016-04-23 4 views
8

У меня есть следующие ManyToMany отношения между WorkDay (имеет аннотацию ManyToMany) и EventJPA - Hibernate ManyToMany сделать много вставки в присоединиться к таблице

WORKDAY объект

@Entity 
@Table(name = "WORK_DAY", uniqueConstraints = { @UniqueConstraint(columnNames = { "WORKER_ID", "DAY_ID" }) }) 
@NamedQueries({ 
     @NamedQuery(name = WorkDay.GET_WORK_DAYS_BY_MONTH, query = "select wt from WorkDay wt where wt.worker = :worker and to_char(wt.day.day, 'yyyyMM') = :month) order by wt.day"), 
     @NamedQuery(name = WorkDay.GET_WORK_DAY, query = "select wt from WorkDay wt where wt.worker = :worker and wt.day = :day") }) 
public class WorkDay extends SuperClass { 

    private static final long serialVersionUID = 1L; 

    public static final String GET_WORK_DAYS_BY_MONTH = "WorkTimeDAO.getWorkDaysByMonth"; 
    public static final String GET_WORK_DAY = "WorkTimeDAO.getWorkDay"; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "WORKER_ID", nullable = false) 
    private Worker worker; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "DAY_ID", nullable = false) 
    private Day day; 

    @Column(name = "COMING_TIME") 
    @Convert(converter = LocalDateTimeAttributeConverter.class) 
    private LocalDateTime comingTime; 

    @Column(name = "OUT_TIME") 
    @Convert(converter = LocalDateTimeAttributeConverter.class) 
    private LocalDateTime outTime; 

    @Enumerated(EnumType.STRING) 
    @Column(name = "STATE", length = 16, nullable = false) 
    private WorkDayState state = WorkDayState.NO_WORK; 

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
    @JoinTable(name = "WORK_DAY_EVENT", joinColumns = { 
      @JoinColumn(name = "WORK_DAY_ID", nullable = false)}, inverseJoinColumns = { 
      @JoinColumn(name = "EVENT_ID", nullable = false)}) 
    @OrderBy(value = "startTime desc") 
    private List<Event> events = new ArrayList<>(); 

    protected WorkDay() { 
    } 

    public WorkDay(Worker worker, Day day) { 
     this.worker = worker; 
     this.day = day; 
     this.state = WorkDayState.NO_WORK; 
    } 
} 

объект Event

@Entity 
@Table(name = "EVENT") 
public class Event extends SuperClass { 

    @Column(name = "DAY", nullable = false) 
    @Convert(converter = LocalDateAttributeConverter.class) 
    private LocalDate day; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "TYPE_ID", nullable = false) 
    private EventType type; 

    @Column(name = "TITLE", nullable = false, length = 128) 
    private String title; 

    @Column(name = "DESCRIPTION", nullable = true, length = 512) 
    private String description; 

    @Column(name = "START_TIME", nullable = false) 
    @Convert(converter = LocalDateTimeAttributeConverter.class) 
    private LocalDateTime startTime; 

    @Column(name = "END_TIME", nullable = true) 
    @Convert(converter = LocalDateTimeAttributeConverter.class) 
    private LocalDateTime endTime; 

    @Enumerated(EnumType.STRING) 
    @Column(name = "STATE", nullable = false, length = 16) 
    private EventState state; 

    protected Event() { 
    } 
} 

Прикрепленный форма UI для ясности

enter image description here

Когда я нажимаю часы с значок запуска впервые, это означает «создать событие и начать рабочий день» в фасоли, вызывая следующие методы:

public void startEvent() { 
    stopLastActiveEvent(); 
    Event creationEvent = new Event(workDay.getDay().getDay(), selectedEventType, selectedEventType.getTitle(), 
      LocalDateTime.now()); 
    String addEventMessage = workDay.addEvent(creationEvent); 
    if (Objects.equals(addEventMessage, "")) { 
     em.persist(creationEvent); 
     if (workDay.isNoWork() 
       && !creationEvent.getType().getCategory().equals(EventCategory.NOT_INFLUENCE_ON_WORKED_TIME)) { 
      startWork(); 
     } 
     em.merge(workDay); 
    } else { 
     Notification.warn("Невозможно создать событие", addEventMessage); 
    } 
    cleanAfterCreation(); 
} 

public String addEvent(Event additionEvent) { 
    if (!additionEvent.getType().getCategory().equals(NOT_INFLUENCE_ON_WORKED_TIME) 
      && isPossibleTimeBoundaryForEvent(additionEvent.getStartTime(), additionEvent.getEndTime())) { 
     events.add(additionEvent); 
     changeTimeBy(additionEvent); 
    } else { 
     return "Пересечение временых интервалов у событий"; 
    } 
    Collections.sort(events, new EventComparator()); 
    return ""; 
} 

private void startWork() { 
    workDay.setComingTime(workDay.getLastWorkEvent().getStartTime()); 
    workDay.setState(WorkDayState.WORKING); 
} 

В журнале я вижу:

  1. вставки в таблицу событий
  2. обновление work_day таблица
  3. вставки в work_day_event таблицу

на UI обновляется только придает раме. Всегда выглядит хорошо .. текущий WorkDay объект имеет один элемент в events коллекции, также все данные вставлены в DB .. но если это время ряд редактирования событий

enter image description here

ряд событий Слушатель:

public void onRowEdit(RowEditEvent event) { 
    Event editableEvent = (Event) event.getObject(); 
    LocalDateTime startTime = fixDate(editableEvent.getStartTime(), editableEvent.getDay()); 
    LocalDateTime endTime = fixDate(editableEvent.getEndTime(), editableEvent.getDay()); 
    if (editableEvent.getState().equals(END) && startTime.isAfter(endTime)) { 
     Notification.warn("Невозможно сохранить изменения", "Время окончания события больше времени начала"); 
     refreshEvent(editableEvent); 
     return; 
    } 
    if (workDay.isPossibleTimeBoundaryForEvent(startTime, endTime)) { 
     editableEvent.setStartTime(startTime); 
     editableEvent.setEndTime(endTime); 
     workDay.changeTimeBy(editableEvent); 
     em.merge(workDay); 
     em.merge(editableEvent); 
    } else { 
     refreshEvent(editableEvent); 
     Notification.warn("Невозможно сохранить изменения", "Пересечение временых интервалов у событий"); 
    } 
} 

work_day_event Вставить новую строку с такими же work_day_id и event_id данные. И если строка редактирования еще делает еще одну вставку и т. Д. В результате у меня есть несколько строк равных в таблице work_day_event. Почему это происходит?

link to github project repository(look ver-1.1.0-many-to-many-problem branch)

enter image description here

+0

Вы получили 'CascadeType.ALL' для событий в рабочий день. Это означает, что нет необходимости самостоятельно сохранять событие. Сохранение рабочего дня должно выполняться следующим образом: 'em.merge (workDay);'. –

+0

@ XtremeBiker, да, но это не устраняло мою проблему. – HAYMbl4

+0

Вы пытались использовать отдельный класс сущности mapper для work_day_event? –

ответ

0

Я думаю, что простое решение для удаления каскада на многие ко многим, и сделать работу вручную! , Я вижу, вы уже делаете это избыточно в любом случае. Поэтому попробуйте удалить CascadeType.ВСЕ

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 

How to persist @ManyToMany relation - duplicate entry or detached entity

3

Изменение CascadeType.ALL в CascadeType.MERGE для eventsWokrDay entity в

Используйте этот код

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) 

вместо

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 

Не используйте ArrayList, используйте HashSet. Потому что ArrayList позволяет дублировать.

Для получения дополнительной информации о CasecadeType, следуйте учебник:

  1. Hibernate JPA Cascade Types
  2. Cascading best practices
+0

введите ответ. Я попытался использовать 'CascadeType.MERGE' вместо' CascadeType.ALL', также я попытался использовать 'Set', но это не решило мою проблему. – HAYMbl4

+0

@ HAYMbl4 Если возможно, вы можете поделиться своим проектом через github. Я попытаюсь исправить вашу проблему. – SkyWalker

+0

@ HAYMbl4 Вы можете пройти через http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/ для лучшего понимания. – SkyWalker

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