我如何保存一个包含实体的新学生,但不创建这些实体,因为它们已经存在于数据库中?

how do i save a new student that contains entities, but don't create these entities because they already exist in the database?

我想保存一名学生,一名学生有一个 TargetAudience 对象作为属性。这些目标受众已经硬编码在我的数据库中。 (目标受众 = 校园 + 专业)。现在当我 post 像这样时:

{
    "user": {
        "userName": "jan",
        "password": "tibo123",
        "role": "ROLE_STUDENT"
    },
    "targetAudience": {
        "majorCode": "IW E-ICT",
        "campus": {
            "name": "GroepT",
            "street": "Andreas Vesaliusstraat",
            "postalCode": "3000",
            "streetNr": "13"
        }
    }
}

它不起作用,因为它每次都会为校园创建一个新对象,并且因为我使用名称作为主键,它会引发异常。 spring data jpa 不应该查看实体是否已经存在然后使用它吗?或者我怎样才能做到这一点?

抱歉,如果不清楚,这是我第一次 posting

student.java:


package com.bachproject.demo.student;

import com.bachproject.demo.onderwerp.Onderwerp;
import com.bachproject.demo.targetAudience.TargetAudience;
import com.bachproject.demo.user.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.util.List;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    @Id
    @SequenceGenerator(
            name = "student_sequence",
            sequenceName = "student_sequence",
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.SEQUENCE,
            generator = "student_sequence"
    )
    private Long studentId;

    @OneToOne(
            cascade = CascadeType.ALL,
            fetch = FetchType.EAGER,
            optional = true
    )
    @JoinColumn(
            name = "user_id",
            referencedColumnName = "userId"
    )
    private User user;

    @OneToOne(
            cascade = CascadeType.ALL,
            fetch = FetchType.EAGER,
            optional = true
    )
    @JoinColumn(
            name = "target_audience",
            referencedColumnName = "TargetAudienceId"
    )
    private TargetAudience targetAudience;

    //private List<Onderwerp> preferences;

}

StudentController.java

package com.bachproject.demo.student;

import com.bachproject.demo.onderwerp.Onderwerp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/students")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping
    @CrossOrigin(origins = "*")
    public List<Student> getStudents(){
        return studentService.getStudents();
    }

    @PostMapping("/register")
    @CrossOrigin(origins = "*")
    public Student registerStudent(@RequestBody Student student) {
        System.out.println(student);
        return studentService.registerStudent(student);
    }
}

StudentService.java:

package com.bachproject.demo.student;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentService {

    @Autowired
    private StudentRepository studentRepository;

    public List<Student> getStudents() {
        return studentRepository.findAll();
    }

    public Student registerStudent(Student student) {
        return studentRepository.save(student);
    }
}

TargetAudience.java:

package com.bachproject.demo.targetAudience;

import com.bachproject.demo.campus.Campus;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TargetAudience {

    @Id
    @SequenceGenerator(
            name = "targetAudience_sequence",
            sequenceName = "targetAudience_sequence",
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.SEQUENCE,
            generator = "targetAudience_sequence"
    )
    private Long TargetAudienceId;

    // for example IW E-ICT-> industriele wetenschappen Elektronica ICT
    private String majorCode;

    @OneToOne(
            cascade = CascadeType.ALL,
            fetch = FetchType.EAGER,
            optional = true
    )
    @JoinColumn(
            name = "campus",
            referencedColumnName = "name"
    )
    private Campus campus;

}

Campus.java:

package com.bachproject.demo.campus;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Campus {

    @Id
    private String name;

    private String street;
    private String postalCode;
    private String streetNr;
}

您在尝试保存时遇到了什么异常?堆栈跟踪会更有帮助。但是,您可以通过两种方式解决此问题

或者, 如果你想完全避免保存 TargetAudience,你可以在 @Transient 注解中注解 TargetAudience 属性 这样,它就不会被考虑用于数据库保存。

或者, JPA 正在尝试使用新的主键 every-time 保存对象,因为该对象处于分离状态(如果您的 JPA 提供程序是休眠的,这是默认的 JPA 提供程序)。您应该修改 StudentService 中的 registerStudent() 方法,例如 -

public Student registerStudent(Student student) {
    TargetAudience targetAudience = targetAudienceRepository.findByMajorCode(student.getTargetAudience().getMajorCode());
    student.setTargetAudience(targetAudience);
    return student;
}

因此,您明确地从数据库中获取 TargetAudience 并将其设置为您正在获取的学生对象。这样,获取的 targetAudience 将处于持久状态(未分离),并且不会再次尝试分配新的主键。