2016-04-23 29 views
8

Ich habe folgt ManyToMany Beziehung zwischen WorkDay (hat Anmerkung ManyToMany) und EventJPA - Hibernate ManyToMany tun viele Insert in Join-Tabelle

WORKDAY Einheit

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

Ereignis Einheit

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

Befestigt UI Form für Klarheit

enter image description here

Wenn ich Uhr mit Lauf Symbol ersten Mal drücken, bedeutet das „Ereignis erstellen und Arbeitstag starten“ in Bohne, die folgenden Methoden aufrufen:

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

In log ich sehe:

  1. Einsatz in Ereignistabelle
  2. Update work_day Tabelle
  3. Einsatz in work_day_event Tabelle

auf UI aktualisiert nur angebracht Rahmen. Immer sieht gut aus .. aktuelles WorkDay Objekt hat ein Element in der events Sammlung, auch alle Daten in DB eingeführt werden .. aber wenn diese Zeit bearbeitet Ereignis Reihe

enter image description here

Ereignis Reihe Zuhörer:

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("Невозможно сохранить изменения", "Пересечение временых интервалов у событий"); 
    } 
} 

in die einfügen neue Zeile mit den gleichen work_day_id und event_id Daten. Und wenn edit row sonst noch einen einfügen und etc .. Im Ergebnis habe ich mehrere gleich Zeilen in work_day_event Tabelle. Warum passiert das?

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

enter image description here

+0

Sie haben 'CascadeType.ALL' für die Ereignisse in den Arbeitstag. Das bedeutet, dass das Ereignis nicht separat gespeichert werden muss. Das Speichern des Arbeitstages sollte den Job erledigen: 'em.merge (workDay);'. –

+0

@XtremeBiker, ja, aber es ist nicht mein Problem resoled – HAYMbl4

+0

Haben Sie versucht, die separate Mapper-Entity-Klasse für das work_day_event verwenden? –

Antwort

3

ändern CascadeType.ALL-CascadeType.MERGE für events in WokrDay entity

Verwenden Sie diesen Code

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

statt

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

Verwenden Sie nicht ArrayList, verwenden Sie HashSet. Weil ArrayList Duplikate erlaubt.

Für weitere Informationen über CasecadeType, folgen Sie der Anleitung:

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

ty für Ihre Antwort. Ich habe versucht, 'CascadeType.MERGE' statt' CascadeType.ALL' zu verwenden, auch habe ich versucht, 'Set' zu verwenden, aber es hat mein Problem nicht gelöst. – HAYMbl4

+0

@ HAYMbl4 Wenn möglich, können Sie Ihr Projekt über GitHub teilen. Ich werde versuchen, Ihr Problem zu beheben. – SkyWalker

+0

@ HAYMbl4 Sie können http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/ zum besseren Verständnis gehen. – SkyWalker