Java JPA&x2B;Spring:根据在持久化实体之前接收到的值将外键列映射到ID

Java JPA&x2B;Spring:根据在持久化实体之前接收到的值将外键列映射到ID,java,spring,hibernate,rest,jpa,Java,Spring,Hibernate,Rest,Jpa,请原谅,如果这是一个愚蠢的问题,但我是非常新的JPA和春天。 我想知道是否有人可以帮助解决以下问题,因为我在StackOverflow和google上做了很多搜索,但找不到我的用例的任何示例。这可能是因为我缺少关键字,因为我不知道这种映射在JPA中被称为什么 因此,基本上,我正在尝试开发一个RESTAPI来从数据库中检索和插入一条记录,我有两个表,即CLIENT\u MASTER和TITLE CLIENT\u MASTER的架构如下所示 CREATE TABLE CLIENT_MASTER (

请原谅,如果这是一个愚蠢的问题,但我是非常新的JPA和春天。 我想知道是否有人可以帮助解决以下问题,因为我在StackOverflow和google上做了很多搜索,但找不到我的用例的任何示例。这可能是因为我缺少关键字,因为我不知道这种映射在JPA中被称为什么

因此,基本上,我正在尝试开发一个RESTAPI来从数据库中检索和插入一条记录,我有两个表,即
CLIENT\u MASTER
TITLE

CLIENT\u MASTER
的架构如下所示

CREATE TABLE CLIENT_MASTER (
   ID INT GENERATED BY DEFAULT AS IDENTITY(START WITH 1000),
   TITLE_ID INT,
   FIRST_NAME VARCHAR(255),
   MIDDLE_NAME VARCHAR(255),
   LAST_NAME VARCHAR(255),
   PRIMARY KEY (ID)
);
import java.io.Serializable;
...
import lombok.Setter;

@Entity
@Table(name = "CLIENT_MASTER")
@Getter
@Setter
public class ClientMaster implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID", updatable = false, nullable = false)
    private Integer id;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "TITLE_ID")
    private Title title;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "MIDDLE_NAME")
    private String middleName;

    @Column(name = "LAST_NAME")
    private String lastName;
}

import java.io.Serializable;
....
import lombok.Setter;

@Entity
@Table(name = "TITLE")
@Getter
@Setter
public class Title implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID", updatable = false, nullable = false)
    private Integer id;

    @Column(name = "TITLE")
    private String title;
}
类似地,
标题
表的方案为

CREATE TABLE TITLE (
   ID INT GENERATED BY DEFAULT AS IDENTITY,
   TITLE VARCHAR(10),
   PRIMARY KEY (ID)
);
CLIENT\u MASTER
表具有以下外键约束

ALTER TABLE CLIENT_MASTER ADD CONSTRAINT TITLE_ID FOREIGN KEY (TITLE_ID) REFERENCES TITLE;
两个表的实体定义如下:

CREATE TABLE CLIENT_MASTER (
   ID INT GENERATED BY DEFAULT AS IDENTITY(START WITH 1000),
   TITLE_ID INT,
   FIRST_NAME VARCHAR(255),
   MIDDLE_NAME VARCHAR(255),
   LAST_NAME VARCHAR(255),
   PRIMARY KEY (ID)
);
import java.io.Serializable;
...
import lombok.Setter;

@Entity
@Table(name = "CLIENT_MASTER")
@Getter
@Setter
public class ClientMaster implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID", updatable = false, nullable = false)
    private Integer id;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "TITLE_ID")
    private Title title;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "MIDDLE_NAME")
    private String middleName;

    @Column(name = "LAST_NAME")
    private String lastName;
}

import java.io.Serializable;
....
import lombok.Setter;

@Entity
@Table(name = "TITLE")
@Getter
@Setter
public class Title implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID", updatable = false, nullable = false)
    private Integer id;

    @Column(name = "TITLE")
    private String title;
}
在我的rest控制器中,我必须在下面插入新的客户端

@RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(value = HttpStatus.OK)
public ResponseEntity<?> create(@RequestBody ClientMaster cm) {
    cmrepository.create(cm);
    HttpHeaders headers = new HttpHeaders();
    ControllerLinkBuilder linkBuilder = linkTo(methodOn(CompanyController.class).get(cm.getId()));
    headers.setLocation(linkBuilder.toUri());
    return new ResponseEntity<>(headers, HttpStatus.CREATED);
}
我在post请求中发送了下面的json,我想要的是将标题值映射到
title
表中相应的id,并插入具有正确标题id的记录

{
    "title": {
        "title": "Mr"
    },
    "firstName": "XXX",
    "middleName": "YYY",
    "lastName": "ZZZ",
}
如上所述,当前的行为是,对于在
CLIENT_表
中插入的每个记录,在
TITLE
表中创建一个新记录,这不是我想要的

有谁能为我指出正确的方向,如何实现这一目标


非常感谢。

持久性提供程序无法通过字段值本身自动假定id

你必须:

  • 首先通过
    标题
    获取
    标题
  • 如果提取了
    ClientMaster
    ,则在
    ClientMaster
    上设置
    title
    。。不执行其他操作,并允许级联保持该值
  • ClientMaster
    对象上使用
    merge
    而不是
    persist

    cmrepository.merge(cm)


  • 我不认为不插入它是个好主意,因为您已经定义了forgien键关系,所以标题表最好存储所有ID。

    谢谢大家

    我听从了Maciej的建议,现在我的控制器如下所示

    @RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(value = HttpStatus.OK)
    public ResponseEntity<?> create(@RequestBody ClientMaster company) {
        //fetch and set title. 
        company.setTitle(titleService.get(company.getTitle().getTitle()));
        companyService.create(company);
        HttpHeaders headers = new HttpHeaders();
        ControllerLinkBuilder linkBuilder = linkTo(methodOn(CompanyController.class).get(company.getId()));
        headers.setLocation(linkBuilder.toUri());
        return new ResponseEntity<>(headers, HttpStatus.CREATED);
    }
    
    @RequestMapping(method=RequestMethod.POST,products=MediaType.APPLICATION\u JSON\u值)
    @ResponseStatus(值=HttpStatus.OK)
    公共响应创建(@RequestBody ClientMaster company){
    //获取并设置标题。
    company.setTitle(titleService.get(company.getTitle().getTitle());
    创建(公司);
    HttpHeaders=新的HttpHeaders();
    ControllerLinkBuilder linkBuilder=linkTo(methodOn(CompanyController.class).get(company.getId());
    headers.setLocation(linkBuilder.toUri());
    返回新的ResponseEntity(标题,HttpStatus.CREATED);
    }
    
    而不是CascadeType。请尝试将其仅设置为MERGE@vc73不幸的是,将
    CascadeType
    更改为
    MERGE
    无效。为什么
    MERGE
    a
    persist
    即使使用现有的引用对象也可以正常工作。如果获取标题实体。。在持久化之前,它将处于分离状态。如果您试图持久化一个分离的实体(通过这里的级联),您将得到一个异常,除非在控制器级别设置了事务。但是我们不知道,谢谢大家,我已经在下面发布了我的答案。很抱歉,我无法将此答案标记为有用,因为我还没有足够的声誉。不,只有预定义的标题集在创建表后初始化。如果请求中的标题与任何现有标题不匹配,则最终用户将无法在客户机主表中插入任何记录。这基本上是一项要求。