[SPRING] 자동 증가 된 복합 id 시퀀스를 Hibernate로 매핑 할 때의 행동을 설명하십시오.
SPRING자동 증가 된 복합 id 시퀀스를 Hibernate로 매핑 할 때의 행동을 설명하십시오.
나는 테이블을 가지고있다.
CREATE TABLE `SomeEntity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`,`subid`),
it.I 자동 증분 필드를 가진 엔티티 클래스가 지속되면 가져온 자동 증분 ID를 읽고 싶습니다.
getter에 대한 주석은 다음과 같습니다.
private long id;
private int subid;
@Id
@GeneratedValue **//How do i correct this to have multiple rows with same id and different subid**
@Column(name = "id")
public long getId() {
return id;
}
@Id
@Column(name = "subid")
public int getSubid() {
return subid;
}
엔티티를 다음과 같이 갖고 싶습니다.
id 1 subid 0
id 1 subid 1
id 1 subid 2
id 2 subid 0
subid는 데이터베이스에서 기본값 0이며 해당 행에 대한 업데이트를 프로그래밍 방식으로 증가시킵니다. 나는이 SO 게시물에서와 같은 해결책을 시도했다. JPA - persist () 후 자동으로 생성 된 ID 반환
@Transactional
@Override
public void daoSaveEntity(SomeEntity entity) {
entityManager.persist(entity);
}
이제이 트랜잭션 밖에서 할당 된 자동 증가 ID를 얻으려고합니다.
@Override
public long serviceSaveEntity(SomeEntity entity) {
dao.daoSaveEntity(entity);
return entity.getId();
}
나는 이것을 웹 서비스에서 호출하고있다.
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response createEntity(SomeEntity entity) {
업데이트 방법은 다음과 같습니다.
@Transactional
public void updateReportJob(SomeEntity someEntity) {
Query query =
entityManager
.createQuery("UPDATE SomeEntity SET state=:newState WHERE id = :id");
query.setParameter("newState","PASSIVE");
query.setParameter("id", id);
query.executeUpdate();
double rand = Math.random();
int i = (int) (rand * 300);
try {
Thread.sleep(i); //only to simulate concurrency issues
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Integer> resList =
entityManager.createQuery("select max(subid) from SomeEntity WHERE id = :id")
.setParameter("id", jobId).getResultList();
// Increment old subid by 1
int subid = resList.get(0);
SomeEntity.setsubid(subid + 1);
SomeEntity.setState("ACTIVE");
// entityManager.merge(SomeEntity);
entityManager.persist(SomeEntity);
}
Id 1을 가진 엔티티에 대해 N 개의 스레드로부터 N 개의 동시 업데이트를 보내고 아래에 다른 몇 가지 등록 정보를 보냅니다.
SomeEnity 엔티티 = new SomeEntity ();
entity.setId(1);
long num = Thread.currentThread().getId();
entity.setFieldOne("FieldOne" + num);
entity.setFieldTwo("" + i);
entity.setFieldThree("" + j);
i++;
j++;
@id에 ID를 붙이고 Subid에 @Id 주석을 넣고 update에 'entityManager.persist'를 사용하는 경우 1 내가 300 개의 스레드로 실행했을 때 "너무 많은 연결"이라는 연결 예외로 인해 실패했습니다. 데이터베이스 상태는 다음과 같습니다.
id 1 subid 0
id 1 subid 1
id 1 subid 2
.. ....
id 1 subid 150
subid는 항상 증분이며 경쟁 상태는 경쟁 상태로 인해 정의되지 않습니다.
케이스 2는 id가 @Id이고 subid가 @Id 어노테이션이고, update가 'entityManager.merge'이다.
id 1 subid 0 id 1 subid 0 id 2 subid 0 .. .... id 151 subid 0 (아마도 사건 1보다 하나 이상의 스레드가 성공적이었던 것일까?)
사례 3 id에 @GeneratedValue 및 @Id를 사용하고 ** 업데이트에 subid 및 entityManager.persist에 @Id 주석을 추가하지 않음 ** 예외 - 지속하기 위해 전달 된 분리 된 엔터티
사례 3 id에 @GeneratedValue 및 @Id가 있고 ** 업데이트에 subid 및 entityManager.merge에 @Id 주석이 없습니다 ** 업데이트가 순차적으로 실행되면 데이터베이스 상태는 다음과 같습니다.
id 1 subid 0
다음 업데이트 이후
id 1 subid 1
각 업데이트 후에 동일한 행이 업데이트됩니다 (한 번에 하나의 행으로 이어짐)
id 1 subid 2
사례 4는 동시 업데이트가있는 사례 3과 동일합니다. 동시에 (300 스레드) 동시에 실행할 경우 아래 예외가 발생합니다.
org.hibernate.HibernateException: More than one row with the given identifier was found: 1
데이터베이스 상태 id 1 subid 2 (하나의 스레드 만 성공적 이었지만 경쟁 조건이 0에서 2로 subid를 업데이트했기 때문에)
사례 5 id에 @GeneratedValue 및 @Id를 사용하고 subid에 @Id 주석을 넣음 subid로도 생성 실패 org.hibernate.PropertyAccessException : SomeEntity.id의 setter를 호출하는 중에 IllegalArgumentException이 발생했습니다.
원인을 설명해주세요. 메소드의 javadoc에서 알 수 있습니다.
persist - 인스턴스를 관리하고 지속적으로 만듭니다.
병합 - 주어진 엔티티의 상태를 현재의 영속 컨텍스트에 병합합니다.
제 질문은 최대 절전 모드로 어노테이션을 관리하는 방법에 대한 것입니다. 세션이 아직 닫히지 않은 경우 3의 경우 분리 된 엔티티 예외가있는 이유는 무엇입니까? case 5에서 IllegalArgumentException이 발생하는 이유는 무엇입니까?
나는 최대 절전 모드 3.6 mysql 5와 봄 4를 사용하고있다. 또한 이러한 증분 ID 및 subid를 달성하는 방법을 제안하십시오. (사용자 정의 SelectGenerator 사용, 데모 구현 또는 다른 방법으로 열 연결을 수행하지 않고)
해결법
-
==============================
1.id 필드는 이미 고유하고 자동 증가되므로이 경우 복합 ID가 필요하지 않으므로 엔티티는 다음과 같이 보일 수 있습니다.
id 필드는 이미 고유하고 자동 증가되므로이 경우 복합 ID가 필요하지 않으므로 엔티티는 다음과 같이 보일 수 있습니다.
@Id @Column(name = "id") public long getId() { return id; } @Column(name = "subid") public int getSubid() { return subid; }
엔티티 관리자를 사용하여 엔티티를 가져올 수 있습니다.
entityManager.find(MyEntity.class, entityId);
또는 id와 subid를 모두 사용하는 쿼리를 사용하여 엔티티를 가져올 수 있습니다.
MyEntity myEntity = entityManager.createTypeQuery("select me from MyEntity where id = :id and subid = :subid", MyEntity.class) .setParameter("id", entityId) .setParameter("subid", entitySubId) .getSingleResult();
또한 Hibernate는 데이터베이스 컬럼에서 id를 가져올 수있는 SelectGenerator를 가지고 있는데, 이것은 데이터베이스가 트리거를 사용하여 id를 생성 할 때 유용하다.
불행하게도 복합 id에서는 작동하지 않으므로 확장 된 SelectGenerator를 작성하거나 id 및 sub-id를 단일 VARCHAR 열로 결합하는 단일 문자열 id_sub_id 열을 사용합니다.
'1-0' '1-1' '2-0' '2-1'
데이터베이스 트리거를 작성하여 데이터베이스 특정 저장 프로 시저를 사용하여 두 개의 열을 갱신하고 두 개의 열을 VARCHAR로 집계해야합니다. 그런 다음 표준 SelectGenerator를 사용하여 집계 된 열을 String 필드에 매핑합니다.
@Id @Column(name = "id_sub_id") @GeneratedValue( strategy = "trigger" ) @GenericGenerator( name="trigger", strategy="org.hibernate.id.SelectGenerator", parameters = { @Parameter( name="keys", value="id_sub_id" ) } ) public String getId() { return id; }
-
==============================
2.매핑 방법을 이해하려면 아래의 디자인 개요를 참조하십시오.
매핑 방법을 이해하려면 아래의 디자인 개요를 참조하십시오.
저축 도서 :
@Transactional public void saveBook(Book book) { em.persist(book); }
책 버전 저장 중 :
@Transactional public void saveBookVersion(BookVersion bookVersion) { Book book = em.find(Book.class, bookVersion.getBook().getId()); bookVersion.setBook(book); book.setLatestBookVersion(bookVersion); em.persist(bookVersion); }
최신 도서 버전 검색 :
@Transactional(readOnly=true) public Book getLatestBookVersion(Long bookId) { // it is enough if lastestBookVersion is loaded eagerly return em.find(Book.class, bookId); }
위의 논리 모델의 테이블 스키마 매핑 :
-
==============================
3.아무도 이것을 언급하지 않았기 때문에.
아무도 이것을 언급하지 않았기 때문에.
나에게 이것은 버전 관리와 비슷하게 들린다.
실제로 코드를 작성하지 않고도 원하는 작업을 정확히 수행 할 수있는 주석이 있습니다.
출처 : blogs.oracle.com (강조 광산)
예제를 조금 수정했습니다. 데이터베이스 필드에 Integer, Long 등의 상자 유형을 사용하는 것이 좋습니다. 엔터티를 처음 저장하기 전에 NULL이 아닌 id도 null이됩니다. 상자 유형을 사용하면이 필드가 null 일 수 있고 명시 적이라는 것을 알게됩니다.
from https://stackoverflow.com/questions/31362100/explain-behaviors-in-mapping-auto-incremented-composite-id-sequence-with-hiberna by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Spring AOP CGLIB 프록시의 필드가 null입니다. (0) | 2019.02.23 |
---|---|
[SPRING] JPA Hibernate Persistence 예외 [PersistenceUnit : default] Hibernate SessionFactory를 빌드 할 수 없다. (0) | 2019.02.23 |
[SPRING] ClassNotFoundException - FreeMarkerConfigurationFactory (0) | 2019.02.23 |
[SPRING] 스프링 부트 용 외부 라이브러리 폴더 (0) | 2019.02.23 |
[SPRING] Spring Rest Controller의 JSON 반환 값으로 사용되는 간단한 문자열 (0) | 2019.02.23 |