Spring Boot JPA Hibernate CRUD 存储库中的查找表
将 Spring Boot 与 JPA 自动配置的 CRUD 存储库、Hibernate 和 MySQL 一起使用。我在按预期的方式获取查找表时遇到了一些问题。
用户实体有一个名为 status 的属性,该属性当前为 enabled 或 disabled。但是,我不能硬编码这些值,因为它们必须在不重新编译的情况下可以更改。所以我认为一个查找表包含可能的状态值,表示为用户模型上的多对一关系。状态表可以有一个外键列,该列引用有关状态的自动生成主键。我觉得这在非 ORM SQL 编码中是相当标准的。以下是我对 JPA 的尝试:
用户模型类 user.java:
package com.example.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonIgnore
private Long id;
@Column(nullable = false, updatable = false)
private String guid;
@ManyToOne
@JoinColumn(name = "status", nullable = false, updatable = false)
private Status status;
private String description;
public User() {
}
public User(final String guid) {
this.guid = guid;
}
@Override
public String toString() {
return String.format("User[id='%d', guid='%s', description='%s']", id, guid, description);
}
@Override
public boolean equals(final Object obj) {
if (obj == null || !(obj instanceof User)) { return false; }
final User rhs = (User) obj;
return new EqualsBuilder().append(guid, rhs.getGuid()).build();
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(guid).build();
}
...getters and setters...
}
model Status.java:
package com.example.model;
import javax.persistence.Entity;
import javax.persistence.Id;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
public class Status {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonIgnore
private Long id;
private String name;
public Status() {
}
public Status(final String name) {
this.name = name;
}
@Override
public String toString() {
return String.format("Status[id='%d', name='%s', description='%s']", id, name);
}
@Override
public boolean equals(final Object obj) {
if (obj == null || !(obj instanceof Status)) { return false; }
final Status rhs = (Status) obj;
return new EqualsBuilder().append(name, rhs.getName()).build();
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(name).build();
}
...getters and setters...
}
UserRepository.java
package com.example.repository;
import org.springframework.data.repository.CrudRepository;
import com.example.model.User;
public interface UserRepository extends CrudRepository<User, Long> {
boolean existsByGuid(String guid);
User findByGuid(String guid);
boolean deleteByGuid(String guid);
}
SQL 表:
CREATE TABLE `status` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`description` varchar(255) DEFAULT NULL,
`guid` varchar(255) NOT NULL,
`status` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `status_id` (`status`),
FOREIGN KEY (`status`) REFERENCES `status` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I have inserted some test rows into the database to check the read function of the CRUD repository. I can see that the lookup table is being referenced properly.
INSERT INTO `status` (`name`) VALUES
('enabled'),
('disabled');
INSERT INTO `user` (`guid`, `status`)
SELECT 'rick', `status`.`id` FROM `status` WHERE `status`.`name` = 'enabled';
INSERT INTO `user` (`guid`, `status`)
(SELECT 'morty', `status`.`id` FROM `status` WHERE `status`.`name` = 'disabled');
JSON stringified 输出:
{
"users": [
{
"guid": "rick",
"status": {
"name": "enabled"
},
"description": null
},
{
"guid": "morty",
"status": {
"name": "disabled"
},
"description": null
}
],
}
当我们想要发布 JSON 来创建一个新用户时,问题就来了。我可以使用如下 JSON 主体:
{
"guid": "jerry",
"status": {
"id": 2,
"name": "disabled"
}
}
异常信息:
2017-11-26 22:21:57.174 WARN 3748 --- [nio-8080-exec-7] o.h.a.i.UnresolvedEntityInsertActions : HHH000437: Attempting to save one or more entities that have a non-nullable association with an unsaved transient entity. The unsaved transient entity must be saved in an operation prior to saving these dependent entities.
Unsaved transient entity: ([com.example.model.Status#<null>])
Dependent entities: ([[com.example.model.User#<null>]])
Non-nullable association(s): ([com.example.model.User.status])
2017-11-26 22:21:57.213 ERROR 3748 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : com.example.model.User.status -> com.example.model.Status; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : com.example.model.User.status -> com.example.model.Status] with root cause
org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : com.example.model.User.status -> com.example.model.Status
at org.hibernate.action.internal.UnresolvedEntityInsertActions.checkNoUnresolvedActionsAfterOperation(UnresolvedEntityInsertActions.java:123) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.engine.spi.ActionQueue.checkNoUnresolvedActionsAfterOperation(ActionQueue.java:414) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.internal.SessionImpl.checkNoUnresolvedActionsAfterOperation(SessionImpl.java:619) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:777) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
As a workaround, I can create a StatusRepository (which extends CrudRepository) and do an explicit lookup, but this would be slower and less elegant than doing this all in one repository call.
解决办法
有两种选择。
使用 spring data jpa getOne 方法。或者它在 hibernate 中的对应项:
使用“String name”作为 @Id 或 @naturaid。
Hibernate 支持子实体的持久性,即使它是一个全新的实体,但具有 id,即 user.setStatus(new Status(1,“disabled”))。因此,您可以自己缓存这些值。在你自己的缓存中查找它们。
本文内容由互联网用户自发贡献,版权归作者所有,本社区不拥有所有权,也不承担相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:jaagool@sina.cn 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。