【问题标题】:Jpa - Hibernate ManyToMany do many insert into join tableJpa - Hibernate ManyToMany 将许多插入到连接表中
【发布时间】:2016-08-17 04:45:22
【问题描述】:

我遵循WorkDay(有注释ManyToMany)和Event之间的ManyToMany关系

工作日实体

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

事件实体

@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 表单

当我第一次用运行图标推送时钟时,在bean中表示“创建事件并开始工作日”,调用以下方法:

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. 更新工作日表
  3. 插入到 work_day_event 表中

在 UI 上仅更新附加框架。看起来总是很好.. 当前WorkDay 对象在events 集合中有一个元素,所有数据也都插入到DB.. 但如果这次编辑事件行

事件行监听器:

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_idevent_id 数据的新行。如果编辑行,则再进行一次插入等。结果,我在work_day_event 表中有几行相等的行。为什么会这样?

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

【问题讨论】:

  • 您有CascadeType.ALL 用于工作日的活动。这意味着没有必要单独保存事件本身。节省工作日应该可以完成工作:em.merge(workDay);.
  • @XtremeBiker,是的,但这并没有解决我的问题
  • 您是否尝试过为 work_day_event 使用单独的映射器实体类?
  • 提问时请尽量遵循stackoverflow.com/help/mcve规则。

标签: oracle hibernate jpa-2.1


【解决方案1】:

WorkDay entity 中的eventsCascadeType.ALL 更改为CascadeType.MERGE

使用此代码

@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

【讨论】:

  • 为您解答。我尝试使用CascadeType.MERGE 而不是CascadeType.ALL,我也尝试使用Set,但它没有解决我的问题。
  • @HAYMbl4 如果可能,您可以通过 github 分享您的项目。我会尽力解决你的问题。
  • @HAYMbl4 可以通过viralpatel.net/blogs/… 了解更多。
  • 我认为问题可能在于我使用的是EntityManager,而不是ManyToMany
  • @HAYMbl4 发生同样的问题。请检查此链接stackoverflow.com/questions/31738710/… 并给我更新。那我试试你的代码。
【解决方案2】:

我认为简单的解决方案是删除多对多关系的级联并手动完成工作! .无论如何,我看到您已经在做多余的事情了。所以试着删除你的 CascadeType.ALL

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

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

【讨论】:

    猜你喜欢
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 2014-08-09
    相关资源
    最近更新 更多