JPA Hibernate удалить дочерний объект объекта, связанный с двойной PK запускает SQL обновление вместо удаления? - PullRequest
0 голосов
/ 21 февраля 2020

У меня следующая структура таблицы:

enter image description here

  • У каждого chat_bot может быть несколько версий.
  • Каждая версия может быть развернутым в среде.
  • Версия имеет двойной первичный ключ, один из которых указан в таблице chat_bot.
  • Таблица среды имеет только первичные ключи. Два из которых упоминаются в таблице версий.

У меня есть следующая структура класса:

@Entity
@AllArgsConstructor
@Table(name = "chat_bot")
@NoArgsConstructor
public class Bot {

    @Id
    private String id;

    @LazyCollection(LazyCollectionOption.FALSE)
    @OneToMany(mappedBy = "botId", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<BotVersion> versions = new LinkedHashSet<>();
}

@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class BotVersionPK implements Serializable {
    private String botId;
    private int version;
}


@Entity
@AllArgsConstructor
@Table(name = "chat_bot_version")
@NoArgsConstructor
@IdClass(BotVersionPK.class)
public class BotVersion {

    @Id
    private String botId;
    @Id
    private int version;

    @Type(type = "text")
    private String json;

    @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true)
    @JoinColumns({
            @JoinColumn(name = "botId", referencedColumnName = "botId"),
            @JoinColumn(name = "version", referencedColumnName = "version")
    })
    private Set<BotEnvironments> environments = new LinkedHashSet<>();
}

@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class BotEnvironmentsPK implements Serializable {
    private String botId;
    private int version;
    private String environmentCode;
}

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "chat_bot_environments")
@IdClass(BotEnvironmentsPK.class)
public class BotEnvironments {
    @Id
    private String botId;
    @Id
    private int version;
    @Id
    private String environmentCode;
}

Я использую JpaRepository для загрузки и сохранения информации.

Вопрос: В случае, если я удаляю элемент из набора «сред» и сохраняю его, Hibernate не запускает операцию DELETE, а выполняет операцию UPDATE:

update chat_bot_environments set bot_id=null, version=null where bot_id=? and version=?

Я получаю следующую ошибку, которая действительна:

Column 'bot_id' cannot be null

Почему триггеры Hibernate JPA обновляются вместо DELETE? Как я могу удалить дочерний элемент, который связан с ненулевым PK?

1 Ответ

4 голосов
/ 25 февраля 2020

В вашем отображении есть некоторые ошибки.

  1. Объединяющиеся столбцы всегда на дочерней стороне :

    @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true)
    @JoinColumns({
            @JoinColumn(name = "botId", referencedColumnName = "botId"),
            @JoinColumn(name = "version", referencedColumnName = "version")
    })
    private Set<BotEnvironments> environments = new LinkedHashSet<>();
    

Это отображение должно быть перенесено на ребенка (BotEnvironments).

mappedBy должен указывать на поле того же типа, что и объект. Вы определили mappedBy = "botId", но botId имеет тип String (это внешний ключ). Это не отобразит отношения между сущностями.

Не бойтесь двунаправленных отношений. Они не приводят к снижению производительности - на самом деле очень часто они могут оптимизировать постоянство производительности.

Была пара других мелких вещей, которые я исправил. Я пропустил аннотации Lombok и выборку типа для краткости. Вот правильное отображение (сгенерированная схема базы данных одинакова):

Bot :

@Entity
@Table(name = "chat_bot")
public class Bot {

    @Id
    private String id;

    @OneToMany(mappedBy = "bot", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<BotVersion> versions = new LinkedHashSet<>();

}

BotVersion :

@Entity
@Table(name = "chat_bot_version")
@IdClass(BotVersionPK.class)
public class BotVersion {

    @Id
    private String botId;
    @Id
    private int version;

    @ManyToOne
    @JoinColumn(name = "botId", insertable=false, updatable=false)
    private Bot bot;

    @Type(type = "text")
    private String json;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "botVersion")
    private Set<BotEnvironments> environments = new LinkedHashSet<>();

}

BotEnvironments :

@Entity
@Table(name = "chat_bot_environments")
@IdClass(BotEnvironmentsPK.class)
public class BotEnvironments {
    @Id
    private String botId;
    @Id
    private int version;
    @Id
    private String environmentCode;

    @ManyToOne
    @JoinColumns({
            @JoinColumn(name = "botId", referencedColumnName = "botId", insertable=false, updatable=false),
            @JoinColumn(name = "version", referencedColumnName = "version", insertable=false, updatable=false)
    })
    private BotVersion botVersion;

}

Идентификационные классы не изменены.

Вот простой тест, который проверяет, работает ли он:

@Test
public void test() {
    // given
    BotEnvironments botEnvironments = new BotEnvironments("bid", 1, "env");
    BotEnvironments botEnvironments1 = new BotEnvironments("bid", 1, "env1");
    BotVersion botVersion = new BotVersion("bid", 1, "json", Set.of(botEnvironments, botEnvironments1));
    Bot bot = new Bot("bid", Set.of(botVersion));
    botRepository.save(bot);

    // when
    Bot savedBot = botRepository.findAll().get(0);
    savedBot.getVersions().iterator().next().getEnvironments().remove(savedBot.getVersions().iterator().next().getEnvironments().iterator().next());
    botRepository.save(savedBot);

    // then
    assertEquals(1, botEnvironmentsRepository.findAll().size());
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...