복붙노트

[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. ==============================

    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. ==============================

    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. ==============================

    3.아무도 이것을 언급하지 않았기 때문에.

    아무도 이것을 언급하지 않았기 때문에.

    나에게 이것은 버전 관리와 비슷하게 들린다.

    실제로 코드를 작성하지 않고도 원하는 작업을 정확히 수행 할 수있는 주석이 있습니다.

    출처 : blogs.oracle.com (강조 광산)

    예제를 조금 수정했습니다. 데이터베이스 필드에 Integer, Long 등의 상자 유형을 사용하는 것이 좋습니다. 엔터티를 처음 저장하기 전에 NULL이 아닌 id도 null이됩니다. 상자 유형을 사용하면이 필드가 null 일 수 있고 명시 적이라는 것을 알게됩니다.

  4. from https://stackoverflow.com/questions/31362100/explain-behaviors-in-mapping-auto-incremented-composite-id-sequence-with-hiberna by cc-by-sa and MIT license