Spring Data JPA同时保存两个实体,列“ user_id”中为空值

时间:2019-02-26 08:20:27

标签: java spring-boot spring-data-jpa

我的应用程序中有用户,用户中有工作细节,我使用一种方法来设置用户/工作细节数据,然后添加新用户或修改现有用户。

这是设置用户/工作数据的方法:

public WorkDetail setWorkerData(PatchWorkerRequest request, User user, WorkDetail workDetail) {

        if (request.getName() != null) {
            user.setName(request.getName());
        }

        if (request.getIdCode() != null) {
            user.setIdCode(request.getIdCode());
        }

        if (request.getEmail() != null) {
            user.setEmail(request.getEmail());
        }

        if (request.getPhone() != null) {
            user.setPhone(request.getPhone());
        }

        if (request.getAddress() != null) {
            user.setAddress(request.getAddress());
        }

        if (request.getSignatureLevel() != null) {
            user.setSignatureLevel(request.getSignatureLevel());
        }

        if (request.getAltContactRelation() != null) {
            user.setAltContactRelation(request.getAltContactRelation());
        }

        if (request.getAltContactPhone() != null) {
            user.setAltContactPhone(request.getAltContactPhone());
        }

        if (request.getRoles() != null) {
            user.setRoles(request.getRoles());
        }

        if (request.getStatus() != null) {
            user.setStatus(request.getStatus());
        }

        // Work details

        if (request.getJobRelation() != null) {
            workDetail.setJobRelation(request.getJobRelation());
        }

        if (request.getSalary() != null) {
            workDetail.setSalary(request.getSalary());
        }

        if (request.getDistricts() != null) {
            workDetail.setDistricts(request.getDistricts());
        }

        if (request.getCompetences() != null) {
            workDetail.setCompetences(request.getCompetences());
        }

        workDetail.setUser(user);
        user.setWorkDetail(workDetail);

        return workDetailRepository.save(workDetail);
    }

现在,使用以下代码可以修改现有的工作程序:

public WorkDetail modifyWorker(Long workerId, PatchWorkerRequest request) {
        WorkDetail workDetail = this.getWorkDetailById(workerId);
        User user = userService.getUserById(workDetail.getUser().getId());
        return setWorkerData(request, user, workDetail);
    }

但是,当我尝试创建新用户/工人时,出现了"null value in column "user_id" violates not-null constraint"错误。我认为这是因为workDetail和用户未正确连接。

这是创建新工作程序的方法:

public WorkDetail createWorker(PatchWorkerRequest request) {
        WorkDetail workDetail = new WorkDetail();
        User user = new User();
        String generatedPassword = userService.generatePassword(8);
        user.setPassword(passwordEncoder.encode(generatedPassword));
        emailService.sendMail("SYDA", new String[]{request.getEmail()},
                "Project SYDA",
                "New password: " + generatedPassword + ".);
        return setWorkerData(request, user, workDetail);
    }

还有,在保存用户之后,我有什么办法可以发送电子邮件,以防万一出现错误,它不会发送电子邮件?

实体:

用户:

@Entity
@Data
@NoArgsConstructor
@TypeDefs({
        @TypeDef(name = "pgsql_enum", typeClass = PostgreSQLEnumType.class)
})
@Table(name = "user_acc")
public class User implements Serializable {

    @Id
    @Column(unique = true, nullable = false, columnDefinition = "serial")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;

    @Column(name = "id_code")
    private BigInteger idCode;

    @Column(name = "email", nullable = false)
    private String email;

    @Column(name = "address")
    private String address;

    @Column(name = "phone")
    private BigInteger phone;

    @Column(name = "alt_contact_relation")
    private String altContactRelation;

    @Column(name = "alt_contact_phone")
    private BigInteger altContactPhone;

    @Column(name = "password", nullable = false)
    @JsonIgnore
    private String password;

    @JsonIgnore
    @Column(name = "create_time", nullable = false, updatable = false)
    private Date createTime = new Date();

    @JsonIgnore
    @Column(name = "update_time", nullable = false)
    private Date updateTime = new Date();

    @Column(name = "status", nullable = false, columnDefinition = "active_status")
    @Enumerated(EnumType.STRING)
    @Type(type = "pgsql_enum")
    private UserStatus status;

    @OneToOne
    @PrimaryKeyJoinColumn
    @JsonIgnore
    private WorkDetail workDetail;

    @OneToMany(mappedBy = "user")
    private List<UserFile> userFiles = new ArrayList<>();

    @ManyToOne
    @JoinColumn(name = "signature_level_id")
    private SignatureLevel signatureLevel;

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = UserRole.class)
    @JoinTable(name = "user_has_user_role",
            joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "user_role_id", referencedColumnName = "id")}
    )
    private Set<UserRole> roles = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = Mechanism.class)
    @JoinTable(name = "user_has_mechanism",
            joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "mechanism_id", referencedColumnName = "id")}
    )
    private Set<Mechanism> mechanisms = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = Service.class)
    @JoinTable(name = "user_has_service",
            joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "service_id", referencedColumnName = "id")}
    )
    private Set<Service> services = new HashSet<>();

    @PreUpdate
    @PrePersist
    public void onCreateOnUpdate() {
        updateTime = new Date();
    }

    public enum UserStatus {
        active, temporarily_inactive, inactive
    }
}

WorkDetail:

@Entity
@Data
@Table(name = "work_detail")
public class WorkDetail {

    @Id
    @Column(unique = true, nullable = false, columnDefinition = "serial")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "salary")
    private float salary;

    @OneToOne(mappedBy = "workDetail",fetch = FetchType.LAZY, optional = true)
    @PrimaryKeyJoinColumn
    private User user;

    @Column(name = "create_time", nullable = false, updatable = false)
    @JsonIgnore
    private Date createTime = new Date();

    @Column(name = "update_time", nullable = false)
    @JsonIgnore
    private Date updateTime = new Date();

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "workDetail")
    private List<UserLeave> userLeaves = new ArrayList<>();

    @ManyToOne
    @JoinColumn(name = "job_relation_id")
    private JobRelation jobRelation;

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = District.class)
    @JoinTable(name = "work_detail_has_district",
            joinColumns = { @JoinColumn(name = "work_detail_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "district_id", referencedColumnName = "id")}
    )
    private Set<District> districts = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = Competence.class)
    @JoinTable(name = "work_detail_has_competence",
            joinColumns = { @JoinColumn(name = "work_detail_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "competence_id", referencedColumnName = "id")}
    )
    private Set<Competence> competences = new HashSet<>();

    @PreUpdate
    @PrePersist
    public void onCreateOnUpdate() {
        updateTime = new Date();
    }
}

Db表:

-----------------------
-- User table
-----------------------

CREATE TABLE IF NOT EXISTS user_acc (
  id SERIAL NOT NULL PRIMARY KEY,
  name text NOT NULL,
  id_code numeric NOT NULL,
  email text NOT NULL UNIQUE,
  address text NULL,
  alt_contact_relation text NULL,
  alt_contact_phone numeric NULL,
  signature_level_id integer NULL,
  username text NOT NULL,
  password text NOT NULL,
  create_time TIMESTAMP without TIME ZONE DEFAULT now() NOT NULL,
  update_time TIMESTAMP without TIME ZONE DEFAULT now() NOT NULL,
  status active_status NOT NULL DEFAULT 'active',
  CONSTRAINT FK_user_signature_level FOREIGN KEY (signature_level_id) REFERENCES signature_level (id)
  ON DELETE NO ACTION ON UPDATE NO ACTION
);

-----------------------
-- User: work detail table
-----------------------

CREATE TABLE IF NOT EXISTS work_detail (
  id SERIAL NOT NULL PRIMARY KEY,
  salary decimal NULL,
  job_relation_id integer NOT NULL,
  user_id integer NOT NULL,
  create_time TIMESTAMP without TIME ZONE DEFAULT now() NOT NULL,
  update_time TIMESTAMP without TIME ZONE DEFAULT now() NOT NULL,
  CONSTRAINT FK_work_detail_user FOREIGN KEY (user_id) REFERENCES user_acc (id)
  ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT fk_work_details_job_relations1 FOREIGN KEY (job_relation_id)
    REFERENCES job_relation (id)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION);

5 个答案:

答案 0 :(得分:1)

您需要先保存user,然后再将其添加到workDetail中。新的User对象没有ID,这就是为什么要获取该异常的原因。 像这样:

public WorkDetail createWorker(PatchWorkerRequest request) {
        WorkDetail workDetail = new WorkDetail();
        User user = new User();
        String generatedPassword = userService.generatePassword(8);
        user.setPassword(passwordEncoder.encode(generatedPassword));
        user = userRepository.save(user);
        emailService.sendMail("SYDA", new String[]{request.getEmail()},
                "Project SYDA",
                "New password: " + generatedPassword + ".);
        return setWorkerData(request, user, workDetail);
    }

或者您可以在saveUser方法内添加一个setWorkerData调用。

...
 if (request.getStatus() != null) {
            user.setStatus(request.getStatus());
        }
user = userRepository.save(user);
...

答案 1 :(得分:1)

在您的if let jsonData = jsonString.data(using: .utf8) { do { let root = try JSONDecoder().decode(RootObject.self, from: jsonData) } catch { print(error) } } 方法中,您尝试为setWorker()对象设置user,但是workDetail对象没有user,因为该用户对象是分离的模式,仍然没有user_id与用户对象相关联。

user_id

因此,要么在设置前保存 //some code in your setWorker() Method. workDetail.setUser(user); //you try to set a detached User Object which doesn't have it's id. user.setWorkDetail(workDetail); return workDetailRepository.save(workDetail); 并确保user对象就保存在数据库中。

在上述代码之前添加此行。

user

还有另一种无需调用user = userRepository.save(user); // add this line.... workDetail.setUser(user); //now your User Object will have it's id. user.setWorkDetail(workDetail); return workDetailRepository.save(workDetail); 方法即可执行此操作的方法,只需使用级联的概念即可了解更多有关此Link Here

的信息。

答案 2 :(得分:0)

您可以使用spring-boot-starter-mail依赖项来完成这项工作

答案 3 :(得分:0)

这表示您尝试保存workdetail对象时未保存用户对象。 因此,没有要放置的user_id,因此没有null值。 可以将您的实体寄给您吗? (特别是它们之间的映射)。 我想,如果要同时保存两个实体,应该使用@OneToOne(cascade = CascadeType.PERSIST)之类的字眼。

答案 4 :(得分:0)

您的映射错误。
这是您应该做的事情:
在WorkDetail中:
 @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "user_id") private User user;

在用户中:
  @OneToOne(mappedBy = "user") @JsonIgnore private WorkDetail workDetail;