问题描述
我正在为已开发的 Spring Boot Web 应用程序添加功能。具有子实体的主要实体是记录。它有一些列/变量,我现在希望它们位于它自己的独立实体 (CustomerOrder) 中,并与 Record 以一对一的关系存在。总结一下:
记录{
-事情 1
-事情 2
-事情 3
}
现在变成:
客户订单{
-事情 1
-事情 2
-事情 3
}
记录{客户订单}
我的作品有些问题。以下是 CustomerOrder 模型的相关关系数据:
@Entity
@Table(name="customer_orders")
public class CustomerOrder {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
... other columns
@OnetoOne(orphanRemoval = true,cascade = CascadeType.ALL,mappedBy="customerOrder",fetch = FetchType.EAGER)
private Record record;
}
然后这里是 Record 模型的相关数据:
@Entity
@Table(name="records")
public class Record extends Auditable<String> implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
... other columns
@OnetoOne
@JoinColumn(name="customer_order_id",nullable = false,unique = true)
private CustomerOrder customerOrder;
}
当我尝试发布记录时,当用户尝试在 ui 中创建记录时,我的问题存在。这是记录的 POST 方法:
@PostMapping
public ResponseEntity<?> saveRecord(@RequestBody Record recordBody,BindingResult result) {
if(!result.hasErrors()) {
if(recordBody.getHardwareItems().isEmpty()) {
record = recordsService.save(recordBody);
} else {
// Save the record first,recordId is required on hardwareItems
// Todo: investigate Spring Hibernate/JPA rules - is there a way to save parent before children to avoid a null recordId
CustomerOrder customerOrder = recordBody.getCustomerOrder();
recordBody.setCustomerOrder(new CustomerOrder());
customerOrder.setRecord(record);
customerOrder = customerOrdeRSService.save(customerOrder);
record = recordsService.save(recordBody);
}
} else {
return new ResponseEntity<>(result.getAllErrors(),HttpStatus.BAD_REQUEST);
}
// Return the location of the created resource
uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{recordId}").buildAndExpand(record.getId()).toUri();
return new ResponseEntity<>(uri,HttpStatus.CREATED);
}
我收到的错误如下:
2021-02-19 02:35:50.989 WARN 31765 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.sqlExceptionHelper : sql Error: 1364,sqlState: HY000
2021-02-19 02:35:50.989 ERROR 31765 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.sqlExceptionHelper : Field 'record_id' doesn't have a default value
这至少对我来说是有意义的,因为我试图保存依赖于 Record 对象的 CustomerOrder 对象,该对象尚未持久化。那么,我如何更改订单和/或创建并保留一个 Record 对象,以便我可以将 CustomerOrder 对象保存到其中?
另外,我正在使用 MysqL,这里是我已经拥有的迁移脚本。我必须在此处为 customer_orders 表添加一些内容吗?
-- Add a sample user
INSERT IGnorE INTO users (first_name,last_name,email,password,enabled,role)
VALUES ('Sample','User','sample@email.com','sample password',true,'ROLE_ADMIN');
-- Customer Reference Values
INSERT IGnorE INTO customers (name) VALUES ('value1');
INSERT IGnorE INTO customers (name) VALUES ('value2');
INSERT IGnorE INTO customers (name) VALUES ('value3');
INSERT IGnorE INTO customers (name) VALUES ('value4');
INSERT IGnorE INTO customers (name) VALUES ('value5');
INSERT IGnorE INTO customers (name) VALUES ('value6');
INSERT IGnorE INTO customers (name) VALUES ('value7');
INSERT IGnorE INTO customers (name) VALUES ('value8');
这是 Records 表和 CustomerOrders 表的 MysqL 脚本:
-- -----------------------------------------------------
-- Table `myapp`.`records`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `myapp`.`records` (
`id` BIGINT NOT NULL AUTO_INCREMENT,`customer_order_id` BIGINT NOT NULL,`record_id` BIGINT NOT NULL,PRIMARY KEY (`id`),UNIQUE INDEX `UK_7m7wsqy68b7omkufvckoqv2hf` (`customer_order_id` ASC) VISIBLE,INDEX `FKta31a9q1llknlo2n0jw741987` (`customer_id` ASC) VISIBLE,CONSTRAINT `FK3q3clytyrx7s8edp9ok821j3`
FOREIGN KEY (`customer_order_id`)
REFERENCES `myapp`.`customer_orders` (`id`),CONSTRAINT `FKta31a9q1llknlo2n0jw741987`
FOREIGN KEY (`customer_id`)
REFERENCES `myapp`.`customers` (`id`))
ENGINE = InnoDB
AUTO_INCREMENT = 27
DEFAULT CHaraCTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci;
-- -----------------------------------------------------
-- Table `myapp`.`customer_orders`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `myapp`.`customer_orders` (
`id` BIGINT NOT NULL AUTO_INCREMENT,`record_id_test` BIGINT NOT NULL,UNIQUE INDEX `UK_ilew9pg8y4qnyhmjg38k1fev2` (`record_id_test` ASC) VISIBLE,INDEX `FK5rpb3u59bblj7h70wjr5mvb01` (`record_id` ASC) VISIBLE,CONSTRAINT `FK5rpb3u59bblj7h70wjr5mvb01`
FOREIGN KEY (`record_id`)
REFERENCES `myapp`.`records` (`id`),CONSTRAINT `FKk7a0g7djyhymr54ehoftkhyfw`
FOREIGN KEY (`record_id_test`)
REFERENCES `myapp`.`records` (`id`))
ENGINE = InnoDB
DEFAULT CHaraCTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci;
解决方法
您的架构与您的实体不匹配。您的 customer_orders 表不应包含 record_id 列。您指定您的记录表应该有一个列 customer_order_id。您使用注释显式定义它:
@JoinColumn(name="customer_order_id",nullable = false,unique = true)
然而,你在另一边的注释
@OneToOne(orphanRemoval = true,cascade = CascadeType.ALL,mappedBy="customerOrder",fetch = FetchType.EAGER)
表示这只是一个使用另一个表中的外键的反向引用。从您的架构中删除该列。 我假设您的数据库架构中的其他列而不是您的实体与您的用例无关,因为您有评论说您的实体具有其他字段。但是,外键没有意义,它与您的实体不匹配。从您的数据库架构中删除该列。
,如果我的理解是正确的,那么您有一对一关系的双向映射。因此,customer_order
有一个 record_id
列,而 record
有一个 customer_order_id
列。这并不理想,hibernate 不自动支持这种类型的映射。如果你考虑一下,如果两列都不能为空,那么你就不能保存数据 b/c 你不能同时保存两者。你必须先保存一个。
如果你想保留这个,你必须让其中之一不可为空。然后保存一个,然后保存另一个,然后使用您现在生成的另一个的 ID 重新保存第一个。
这里 hibernate 试图首先保存 customer_order
,b/c 你已经声明 customer_order_id
在 records
表中并且不能为空。这是默认设置。我建议保留这一点,并删除 record_id
表中的另一个 customer_orders
列。
更新: 为了澄清起见,这是一种解决方案。由于 record_id 和 record_id_test 不可为空,在代码中设置它们的值,然后保存。它应该可以工作。
如果我遗漏了什么,请提供更多信息,例如基础数据库、模型中 record_id
列的位置等。
删除两个 record_id
列或为其提供默认值。它们没有映射到实体中的任何内容,因此 Hibernate 没有将它们包含在插入语句中,这会导致失败,因为它们都配置为 NOT NULL
。
我猜他们想以某种方式保存 Record
和 CustomerOrder
之间的引用,但是它被映射到 customer_order_id
@JoinColumn(name="customer_order_id",unique = true)
,
在表创建脚本中,您似乎还在 RECORD_ID
表中包含了一个名为 RECORDS
的列。它已经有标准的 ID
列来存储主键。不确定您是否真的需要其中的 RECORD_ID
列。这被标记为 NOT NULL 并且在表创建脚本中没有设置默认值,因此您得到了错误。
查看表创建脚本中的约束,看起来应该命名为 CUSTOMER_ID
而不是 RECORD_ID
。
请检查此作为第一步。
,为 customerOrder 变量上方的注释添加 @OneToOne(cascade = CascadeType.PERSIST)
基本上是我解决此问题所需要做的全部工作。该组中的另一位开发人员帮助了我。然后,我可以在 POST 方法中去掉对 CustomerOrder 值的所有引用,只需要在记录的 PUT 方法中为 CustomerOrder 设置一个 setter。