在@ManyToMany 关系中更新后休眠不需要的/意外的删除语句
Hibernate unwanted / unexpected delete statement after update in @ManyToMany relationship
我将提供一些关于我的 webapp 的一般信息(我现在只提及有问题的 tables)。所以,我有 3 tables Trainer、Event 和 Event-Trainer。
CREATE TABLE `Event` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Title` varchar(1000) DEFAULT NULL,
`Date` varchar(45) DEFAULT NULL,
`Time` varchar(45) DEFAULT NULL,
`Duration` varchar(20) DEFAULT NULL,
`Location` varchar(45) DEFAULT NULL,
`Days` int(11) DEFAULT '1',
`Price` double DEFAULT NULL,
`PlaceLimit` int(11) DEFAULT NULL,
`CategoryID` int(11) DEFAULT NULL,
`Notes` varchar(1000) DEFAULT '(No notes)',
PRIMARY KEY (`ID`),
KEY `CategoryID_idx` (`CategoryID`),
CONSTRAINT `CategoryID` FOREIGN KEY (`CategoryID`) REFERENCES `Category` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
CREATE TABLE `Trainer` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`FirstName` varchar(45) DEFAULT NULL,
`LastName` varchar(45) DEFAULT NULL,
`Email` varchar(45) DEFAULT NULL,
`Phone` varchar(45) DEFAULT NULL,
`City` varchar(45) DEFAULT NULL,
`Picture` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`)
)
CREATE TABLE `Event-Trainer` (
`EventID` int(11) NOT NULL,
`TrainerID` int(11) NOT NULL,
PRIMARY KEY (`EventID`,`TrainerID`),
KEY `FK_TrainerID` (`TrainerID`),
CONSTRAINT `FK_EventID_2` FOREIGN KEY (`EventID`) REFERENCES `Event` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_TrainerID_2` FOREIGN KEY (`TrainerID`) REFERENCES `Trainer` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
这些是实体 类
//Event.java
@Entity
@Table(name = "Event", schema = "business-club")
public class Event {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private int id;
...
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "CategoryID")
private Category category;
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}
, fetch = FetchType.EAGER)
@JoinTable(
name = "`Event-Trainer`",
joinColumns = @JoinColumn(name = "EventID"),
inverseJoinColumns = @JoinColumn(name = "TrainerID")
)
private List<Trainer> trainers;
...
}
//Trainer.java
@Entity
@Table(name = "Trainer", uniqueConstraints = {@UniqueConstraint(columnNames = {"FirstName","LastName","Email"})})
public class Trainer {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="ID")
private int id;
...
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable(
name="`Event-Trainer`",
joinColumns = @JoinColumn(name = "TrainerID"),
inverseJoinColumns = @JoinColumn (name="EventID")
)
private List<Event> events;
现在让我们开始讨论这个问题,我在使用 Hibernate 时遇到了一个奇怪的行为,即在我更新一个 Trainer 之后它 "Unexpectedly" 删除了 Event-Trainer 中与该培训师相关的记录 table 这是我更新训练器后休眠的日志
Hibernate: update Trainer set City=?, Email=?, FirstName=?, LastName=?, Phone=?, Picture=? where ID=?
Hibernate: delete from `Event-Trainer` where TrainerID=?
我尝试更改两个实体中的级联类型但它没有用,我将获取类型更改为 LAZY 并且在更新时出现异常(如果有人向我解释这个我将不胜感激,尽管我不不认为这有什么大不了的,至少现在是这样)
如果需要,我会post更多信息
我认为 CascadeType.DETACH 超过 table 'Event-Trainer' 导致了这个问题。
您能在 'Trainer' table 中看到更新后的值吗?
多对多关系的映射不正确。您应该使用 @ManyToMany 中的 mappedBy 属性使一侧为主,另一侧为从。
感谢 Mohamed Gara,这是我的问题的解决方案
我改变了
//Trainer Entity
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable(
name="`Event-Trainer`",
joinColumns = @JoinColumn(name = "TrainerID"),
inverseJoinColumns = @JoinColumn (name="EventID")
)
private List<Event> events;
到
@ManyToMany(mappedBy = "trainers",fetch = FetchType.EAGER)
private List<Event> events;
和
//Event Entity
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}
, fetch = FetchType.EAGER)
@JoinTable(
name = "`Event-Trainer`",
joinColumns = @JoinColumn(name = "EventID"),
inverseJoinColumns = @JoinColumn(name = "TrainerID")
)
private List<Trainer> trainers;
到
@ManyToMany(fetch = FetchType.EAGER)
private List<Trainer> trainers;
我还必须将 table Event-Trainer 的名称更改为 Event_Trainer
并将主键的名称从 EventID、TrainerID 更改为 events_ID、trainers_ID 分别。 (我在抛出一些异常后这样做了)
编辑 1
我在删除分配给事件的培训师时遇到异常,因此我将两个外键的删除和更新时从 NO ACTION 更改为 CASCADE,并在两个实体的 @ManyToMany 注释中指定了级联类型
@ManyToMany( ... cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
我将提供一些关于我的 webapp 的一般信息(我现在只提及有问题的 tables)。所以,我有 3 tables Trainer、Event 和 Event-Trainer。
CREATE TABLE `Event` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Title` varchar(1000) DEFAULT NULL,
`Date` varchar(45) DEFAULT NULL,
`Time` varchar(45) DEFAULT NULL,
`Duration` varchar(20) DEFAULT NULL,
`Location` varchar(45) DEFAULT NULL,
`Days` int(11) DEFAULT '1',
`Price` double DEFAULT NULL,
`PlaceLimit` int(11) DEFAULT NULL,
`CategoryID` int(11) DEFAULT NULL,
`Notes` varchar(1000) DEFAULT '(No notes)',
PRIMARY KEY (`ID`),
KEY `CategoryID_idx` (`CategoryID`),
CONSTRAINT `CategoryID` FOREIGN KEY (`CategoryID`) REFERENCES `Category` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
CREATE TABLE `Trainer` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`FirstName` varchar(45) DEFAULT NULL,
`LastName` varchar(45) DEFAULT NULL,
`Email` varchar(45) DEFAULT NULL,
`Phone` varchar(45) DEFAULT NULL,
`City` varchar(45) DEFAULT NULL,
`Picture` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`)
)
CREATE TABLE `Event-Trainer` (
`EventID` int(11) NOT NULL,
`TrainerID` int(11) NOT NULL,
PRIMARY KEY (`EventID`,`TrainerID`),
KEY `FK_TrainerID` (`TrainerID`),
CONSTRAINT `FK_EventID_2` FOREIGN KEY (`EventID`) REFERENCES `Event` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_TrainerID_2` FOREIGN KEY (`TrainerID`) REFERENCES `Trainer` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
这些是实体 类
//Event.java
@Entity
@Table(name = "Event", schema = "business-club")
public class Event {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private int id;
...
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "CategoryID")
private Category category;
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}
, fetch = FetchType.EAGER)
@JoinTable(
name = "`Event-Trainer`",
joinColumns = @JoinColumn(name = "EventID"),
inverseJoinColumns = @JoinColumn(name = "TrainerID")
)
private List<Trainer> trainers;
...
}
//Trainer.java
@Entity
@Table(name = "Trainer", uniqueConstraints = {@UniqueConstraint(columnNames = {"FirstName","LastName","Email"})})
public class Trainer {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="ID")
private int id;
...
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable(
name="`Event-Trainer`",
joinColumns = @JoinColumn(name = "TrainerID"),
inverseJoinColumns = @JoinColumn (name="EventID")
)
private List<Event> events;
现在让我们开始讨论这个问题,我在使用 Hibernate 时遇到了一个奇怪的行为,即在我更新一个 Trainer 之后它 "Unexpectedly" 删除了 Event-Trainer 中与该培训师相关的记录 table 这是我更新训练器后休眠的日志
Hibernate: update Trainer set City=?, Email=?, FirstName=?, LastName=?, Phone=?, Picture=? where ID=?
Hibernate: delete from `Event-Trainer` where TrainerID=?
我尝试更改两个实体中的级联类型但它没有用,我将获取类型更改为 LAZY 并且在更新时出现异常(如果有人向我解释这个我将不胜感激,尽管我不不认为这有什么大不了的,至少现在是这样)
如果需要,我会post更多信息
我认为 CascadeType.DETACH 超过 table 'Event-Trainer' 导致了这个问题。 您能在 'Trainer' table 中看到更新后的值吗?
多对多关系的映射不正确。您应该使用 @ManyToMany 中的 mappedBy 属性使一侧为主,另一侧为从。
感谢 Mohamed Gara,这是我的问题的解决方案 我改变了
//Trainer Entity
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable(
name="`Event-Trainer`",
joinColumns = @JoinColumn(name = "TrainerID"),
inverseJoinColumns = @JoinColumn (name="EventID")
)
private List<Event> events;
到
@ManyToMany(mappedBy = "trainers",fetch = FetchType.EAGER)
private List<Event> events;
和
//Event Entity
@ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}
, fetch = FetchType.EAGER)
@JoinTable(
name = "`Event-Trainer`",
joinColumns = @JoinColumn(name = "EventID"),
inverseJoinColumns = @JoinColumn(name = "TrainerID")
)
private List<Trainer> trainers;
到
@ManyToMany(fetch = FetchType.EAGER)
private List<Trainer> trainers;
我还必须将 table Event-Trainer 的名称更改为 Event_Trainer
并将主键的名称从 EventID、TrainerID 更改为 events_ID、trainers_ID 分别。 (我在抛出一些异常后这样做了)
编辑 1
我在删除分配给事件的培训师时遇到异常,因此我将两个外键的删除和更新时从 NO ACTION 更改为 CASCADE,并在两个实体的 @ManyToMany 注释中指定了级联类型
@ManyToMany( ... cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})