【问题标题】:Why I'm getting an error when I'm trying to insert a new object in db?为什么当我尝试在 db 中插入新对象时出现错误?
【发布时间】:2020-01-18 19:10:34
【问题描述】:

我的场景如下

一个User可以有一个Track列表,与之对应,Track实体包含一个用户id。(@OneToMany)

每当创建新曲目时,曲目列表都会更新。

上述实体如下:

追踪实体

@Entity
@Table(name ="track")
public class Track {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long trackId;

@ManyToOne
@JoinColumn(name = "userId", nullable = false)
private User user;

@OneToOne(mappedBy = "track")
private Share share;

private String trackName;

@OneToMany(mappedBy = "pointId")
private List<Point> point;

@OneToOne(mappedBy = "track")
private TrackStatistic trackStatistic;

用户实体

@Entity
@Table(name = "user")
public class User {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name = "USER_ID")
private Long id;

private String firstName;

private String lastName;

@Column(unique = true)
private String username;

private String password;

@Column(unique = true)
private String email;

@Column(unique = true)
private String phoneNumber;

private int age;

private Role role;

@OneToMany(mappedBy = "shareId")
private List<Share> shares;

@OneToMany(mappedBy = "trackId")
private List<Track> tracks;

}

createTrack方法如下

public Track createTrack(String username, TrackDTO trackDTO) {
    //Find user
    User user = userRepository.findByUsername(username);

    //Convert Dto to Entity
    Track track = modelMapper.map(trackDTO, Track.class);

    //Update user track list
    user.getTracks().add(track);

    //Update track
    track.setUser(user);

    //save user
    userRepository.save(user);

    //save track
    return trackRepository.save(track);
}

注意TrackDTO是Track实体对应的Dto类


当我运行createTrack 时,我遇到了以下错误:

2020-01-18 20:48:23.315 ERROR 14392 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper:
 Cannot add or update a child row: a foreign key constraint fails (`thdb`.`track`, CONSTRAINT `FK5cftk3hw8vfnaigtj063skvxs` FOREIGN KEY (`track_id`) REFERENCES `user` (`user_id`))

2020-01-18 20:48:23.338 ERROR 14392 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]:
 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails 
(`thdb`.`track`, CONSTRAINT `FK5cftk3hw8vfnaigtj063skvxs` FOREIGN KEY (`track_id`) REFERENCES `user` (`user_id`))

【问题讨论】:

  • "a foreign key constraint fails (`thdb`.`track`, CONSTRAINT `FK5cftk3hw8vfnaigtj063skvxs` FOREIGN KEY (`track_id`) REFERENCES `user` (`user_id`))"
  • @Turing85 是的,这就是错误,为什么我得到这个?
  • 错误告诉你原因:违反了外键约束。
  • 是的,我知道错误告诉我什么,我不知道违反了什么约束键,也不知道为什么有时有效,有时无效。
  • 尝试将private List&lt;Track&gt; tracks; 的级联类型设置为PERSIST 或尝试持久化track 而不是useruser 已经存在,你“只”需要坚持track 和关系)

标签: java mysql hibernate spring-boot jpa


【解决方案1】:

已编辑


通过主动级联,当您保存User时,无需再次保存Track。所以我对您的代码进行了一些更改,如下所示,希望对您有所帮助

1- 将cascade = CascadeType.ALL 添加到用户实体

2- 将targetEntity = Track .class, mappedBy = "user" 添加到用户实体

3- 将@ManyToOne(targetEntity = User.class) @JoinColumn(name = "USER_FK") 添加到跟踪实体

4-删除trackRepository.save(track);

5- 只需保存用户 (userRepository.save(user))。通过级联,它也保存了轨道。

6- 返回用户列表中的最后一个曲目。(最新保存的曲目)


我将上述编辑编码如下

用户实体

@Entity
@Table(name = "user")
public class User {

  //other properties

   @OneToMany(targetEntity = Track.class , mappedBy = "user" ,cascade = CascadeType.ALL)
   private List<Track> tracks;

   //getters and setters   
  
  }

追踪实体

@Entity
@Table(name ="track")
public class Track {
 
//other properties

@ManyToOne(targetEntity = User.class)
@JoinColumn(name = "USER_FK",nullable = false)
private User user;

//getters and setters

}

createTrack 方法

public Track createTrack(String username, TrackDTO trackDTO) {

    //Find user
    User user = userRepository.findByUsername(username);

    //Convert Dto to Entity
    Track track = modelMapper.map(trackDTO, Track.class);

    //Update User and Track
    user.getTracks().add(track);
    track.setUser(user);

    //save user
    User result = userRepository.save(user);
    
    //find saved track (you can fetch track by other ways too)
    Track savedTrack = result.getTracks().get(result.getTracks().size()-1);

    return savedTrack;
}

【讨论】:

  • 我现在明白了:java.sql.SQLIntegrityConstraintViolationException: Column 'user_fk' cannot be null
  • @dragos.pavel 很抱歉造成误解,我编辑了解决方案,请运行新代码,我测试了两次,一切正常。 (我更改了 createTrack 方法并在工作流程中添加了一个新部分,.....)
【解决方案2】:

关系是循环的,用户包含轨道,轨道包含用户。两个方向都有 fk 约束,所以这个问题。您可以通过保持用户独立而不参考跟踪并保持跟踪参考用户来修复它。固定关系还有其他方法,主要是要避免循环关系

【讨论】:

  • 在数据库级别,这无关紧要。只要两个实体(或更准确地说,关系)都写在同一个事务中,就可以了。
  • 我不能 100% 确定这是不是真的,它可能是特定于数据库的。 Oracle 在提交前给出了 uk 和 fk 的错误。
【解决方案3】:

您应该在用户之前保存曲目。

public Track createTrack(String username, TrackDTO trackDTO) {
    User user = userRepository.findByUsername(username);
    Track track = modelMapper.map(trackDTO, Track.class);
    user.getTracks().add(track);
    track.setUser(user);
    userRepository.save(user); // 1
    return trackRepository.save(track); // 2
}

如果是普通的旧 SQL 查询,你的代码就会出错:

  • (1) 查询会执行 insert into USER_TRACK (&lt;USER&gt;, &lt;TRACK&gt;),但在保存用户时,&lt;TRACK&gt; 尚未保存,因此您无法分配到用户/跟踪关系,因为您缺少 ID。
  • (2) 查询将执行 insert into TRACK (&lt;TRACK&gt;) 并生成一个 id,但它发生在之后。

您可能缺少一些 JPA 映射,因为我认为 JPA 通常会为您处理这些问题。

【讨论】:

  • 先保存曲目,然后再保存用户,同样的错误,当我第一次创建新曲目时,我在数据库中插入了同样的错误。
【解决方案4】:

重构了实体之间的关系,问题依然存在。跟踪和用户之间关系的小预览如下所示: 跟踪实体

@Entity
@Table(name ="TRACK")
public class Track {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "TRACK_ID")
    private Long trackId;

    private String trackName;

    @OneToMany(cascade = CascadeType.ALL,
            orphanRemoval = true)
    @JoinColumn(name = "POINT_ID")
    private List<Point> point;

    @OneToOne
    @JoinColumn(name="TRACK_STATISTIC_ID")
    private TrackStatistic trackStatistic;

    private long numberOfLikes;

    private Date creationTime;

用户实体

@Entity
@Table(name = "USER")
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "USER_ID")
    private Long userId;

    private String firstName;

    private String lastName;

    @Column(unique = true)
    private String username;

    private String password;

    @Column(unique = true)
    private String email;

    @Column(unique = true)
    private String phoneNumber;

    private int age;

    private Role role;

    private boolean locked;

    private long numberOfReports;

    @JsonIgnore
    @ManyToMany()
    @JoinTable(name="FOLLOWER",
            joinColumns={@JoinColumn(name="USER_ID")},
            inverseJoinColumns={@JoinColumn(name="FOLLOWER_ID")})
    private Set<User> followed = new HashSet<User>();

    @JsonIgnore
    @ManyToMany(mappedBy="followed")
    private Set<User> follower = new HashSet<User>();

   //How it was before
    @OneToMany
    @JoinColumn(name = "TRACK_ID")
    private List<Track> tracks;

   //How is it now
    @OneToMany
    @JoinColumn(name = "USER_ID")
    private List<Track> tracks;


    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "SHARE_ID")
    private List<Share> shares;

创建轨道时调用的方法是:

public Track createTrack(String username, TrackDTO trackDTO) {
        Track track = modelMapper.map(trackDTO, Track.class);
        Track newTrack = trackRepository.save(track);
        return newTrack;
    }

所以问题出在曲目列表上的@JoinColumn 注释上,我将名称 TRACK_ID 而不是 USER_ID

【讨论】:

    猜你喜欢
    • 2021-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-01
    相关资源
    最近更新 更多