【问题标题】:JPA + Spring: Mapping a foreign key column to a ID based on the value received before persisting the entityJPA + Spring:根据在持久化实体之前收到的值将外键列映射到 ID
【发布时间】:2019-04-09 12:43:16
【问题描述】:

如果这是一个愚蠢的问题,请原谅,但我对 JPA 和 Spring 非常陌生。 我想知道是否有人可以帮助解决以下问题,因为我在 StackOverflow 和 google 上进行了大量搜索,但找不到任何适用于我的用例的示例。可能是我遗漏了关键字,因为我不知道这种映射在 JPA 中叫什么。

所以基本上,我正在尝试开发一个 REST API 来从/在数据库中检索和插入记录,为此我有两个表,即 CLIENT_MASTERTITLE

CLIENT_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)
);

同样,TITLE 表的方案是,

CREATE TABLE TITLE (
   ID INT GENERATED BY DEFAULT AS IDENTITY,
   TITLE VARCHAR(10),
   PRIMARY KEY (ID)
);

CLIENT_MASTER 表具有以下外键约束,

ALTER TABLE CLIENT_MASTER ADD CONSTRAINT TITLE_ID FOREIGN KEY (TITLE_ID) REFERENCES TITLE;

两个表的实体定义如下,

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;
}

在我的休息控制器中,我在下面插入新客户端

@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);
}

cmrepository 是的实例,

@Repository
public class CMRepository {

@PersistenceContext
private EntityManager entityManager;

@Override
public void create(ClientMaster cm) {
    entityManager.persist(cm);

 }
}

我在 post 请求中发送以下 json,我想要将标题值映射到 TITLE 表中的相应 id 并插入具有正确标题 id 的记录。

{
    "title": {
        "title": "Mr"
    },
    "firstName": "XXX",
    "middleName": "YYY",
    "lastName": "ZZZ",
}

根据上述情况,当前的行为是,对于CLIENT_TABLE 中的每条记录插入,TITLE 表中都会创建一条新记录,这不是我想要的。

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

非常感谢。

【问题讨论】:

  • 而不是 CascadeType.ALL 尝试仅将其设置为 MERGE
  • @vc73 不幸的是,将CascadeType 更改为MERGE 无效。

标签: java spring hibernate rest jpa


【解决方案1】:

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

你必须:

  1. 首先通过title 获取Title 条目。
  2. title 设置为ClientMaster(如果已获取)。否则不执行任何操作并允许级联保留该值。
  3. ClientMaster 对象上使用merge 而不是persist

    cmrepository.merge(cm);

【讨论】:

  • 为什么 mergepersist 即使使用现有的引用对象也能正常工作。
  • 如果您获取 Title 实体.. 在持久化之前它将处于分离状态。如果你尝试持久化一个分离的实体(通过这里的级联)你会得到一个异常
  • 除非在控制器级别设置了 Transactional。但我们不知道
  • 非常感谢大家,我已经在下面发布了我的答案。抱歉,我无法将此答案标记为有用,因为我还没有足够的声誉。
【解决方案2】:

谢谢大家

我听从了 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);
}

【讨论】:

    【解决方案3】:

    我认为不插入它不是一个好主意,因为您已经定义了一个伪造的键关系,对于标题表存储所有 ID 总是更好。

    【讨论】:

    • 不,只有预定义的标题集在创建表后初始化。如果请求中的标题与任何现有标题都不匹配,则最终用户将无法在 client_master 表中插入任何记录。这基本上是一个要求。
    猜你喜欢
    • 1970-01-01
    • 2013-04-17
    • 2018-12-08
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    • 2020-05-19
    • 1970-01-01
    • 2021-09-25
    相关资源
    最近更新 更多