JPA 자동 구성 CRUD 저장소, 최대 절전 모드 및 MySQL에서 스프링 부트를 사용하려고합니다. 내가 기대하는 방식으로 작동하는 조회를 얻는 데 어려움을 겪고 있습니다.스프링 부트 JPA 하이버 네이트 CRUD 저장소의 찾아보기 테이블
User
엔티티는 현재 enabled
또는 disabled
인 속성이 status
입니다. 그러나 이러한 값은 다시 컴파일하지 않고 변경 가능해야하기 때문에이 값을 하드 코딩 할 수 없습니다. 그래서 나는 룩업 테이블이 모델에서 many-to-one 관계로 표현 된 status
의 가능한 값을 포함하고 있다고 생각합니다. status
테이블에는 해당 상태의 자동 생성 기본 키를 참조하는 외래 키 열이있을 수 있습니다. 나는 이것이 ORM이 아닌 SQL 코딩에서 상당히 표준적인 것이라고 생각한다.
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
:사용자 모델 클래스, 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...
}
및 중첩 모델 Status.java 여기 JPA 함께 할 내 시도이다
다음은 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;
일부 테스트 행을 데이터베이스에 삽입하여 CRUD 저장소의 읽기 기능을 확인했습니다. 조회 테이블이 제대로 참조되고 있음을 알 수 있습니다. 우리는 새로운 사용자를 생성 POST JSON을 할 때
{
"users": [
{
"guid": "rick",
"status": {
"name": "enabled"
},
"description": null
},
{
"guid": "morty",
"status": {
"name": "disabled"
},
"description": null
}
],
}
문제는 온다 :
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 캐릭터 라인 출력합니다. 이 작품
{
"guid": "jerry",
"status": {
"id": 2,
"name": "disabled"
}
}
하지만 결함이있다 : 나는 다음과 같은 JSON 본체를 사용할 수 있습니다. 구체적으로 상태의 ID를 전달합니다. 이 값은 시스템 내부에 있습니다. Google API 사용자가이 키를 추적하지 않아도되고 시스템에서 결과를 출력하지 못하도록합니다. 정말 조회 테이블, imho의 목적을 패배. 나는 사용자가 간단하게 전달할 수 있도록하는 것을 선호 : 그들은 단지 대신 "status":"disable"
을 통과하고 자동으로 조회 테이블로 해결 할 수있는 경우
{
"guid": "jerry",
"status": {
"name": "disabled"
}
}
나는 더 행복 할 것입니다.
{
"guid": "jerry",
"status": "disabled"
}
하지만, 내 현재의 구성으로, JPA는 기본 키가 명시 적으로 전달되지 않은 이름 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]
는이 문제를 해결 나는 (CrudRepository를 확장)를 StatusRepository을 작성하고 명시 적으로 조회를 할 수 있지만, 속도가 느린이 모두 하나의 저장소를 호출하는 것보다 덜 우아 될 수 있습니다.
여러 저장소 호출없이 새 사용자를 만들 수 있고 사용자가 명시 적으로 ID를 전달하지 않아도되는 특수 효과 및/또는 기타 변경 사항은 무엇입니까?
공간을 절약하기 위해 일부 수업을 생략했지만 entire example project은 GitHub에서 찾을 수 있습니다.
'Status.name' 열 ID를 만들 수 있다면 가능합니다. – 11thdimension
@ 11thdension, 듣기 좋은 곳입니다. 이것이 어떻게 효과가 있을지에 대한 예를 지적 해 주시겠습니까? –
'Status' 클래스의'name' 컬럼이 유일하다면 이것을'@ Id'로 표시하고'Long id' 필드를 제거 할 수 있습니다. 이렇게하면'{ "name": "disabled"}'를 보내면 JPA가 자체적으로 가져올 수 있습니다. – 11thdimension