programing

하이버네이트가 MySQL의 "ON DUFFICED KEY UPDATE" 구문과 함께 작동할 수 있습니까?

sourcejob 2023. 10. 1. 19:25
반응형

하이버네이트가 MySQL의 "ON DUFFICED KEY UPDATE" 구문과 함께 작동할 수 있습니까?

MySQL "를합니다.INSERT ... ON DUPLICATE KEY UPDATE ..." 데이터베이스에 blindly 삽입하고 기존 레코드가 있는 경우 업데이트로 되돌릴 수 있는 구문입니다.

이것은 빠른 트랜잭션 격리를 원하는 경우에 유용하며 데이터베이스에 이미 있는 값에 따라 업데이트할 값이 달라집니다.

작위적인 예로, 블로그에서 이야기를 보는 횟수를 세고자 합니다.이 구문을 사용하는 한 가지 방법은 다음과 같습니다.

 INSERT INTO story_count (id, view_count) VALUES (12345, 1)
 ON DUPLICATE KEY UPDATE set view_count = view_count + 1

이것은 거래를 시작하고 새로운 이야기가 첫 페이지를 장식할 때 발생하는 불가피한 예외를 처리하는 것보다 더 효율적이고 효과적일 것입니다.

동면 상태에서 같은 작업을 수행하거나 같은 목표를 달성하려면 어떻게 해야 합니까?

첫째, Hibernate의 HQL 구문 분석기는 데이터베이스별 키워드를 이해하지 못하기 때문에 예외를 발생시킵니다.실 HQL은"다가 않습니다.INSERT ... SELECT ....".

둘째, Hibernate는 SQL을 선택만 제한합니다.합니다를 최대 절전 합니다.session.createSQLQuery("sql").executeUpdate().

,saveOrUpdate이 경우 청구서에 맞지 않습니다.테스트는 통과되겠지만, 1초에 방문자가 1명 이상인 경우에는 생산 실패가 발생합니다.

내가 정말로 동면을 제압해야 합니까?

하이버네이트 @SQ 를 본 적이 있습니까?주석 삽입?

@Entity
@Table(name="story_count")
@SQLInsert(sql="INSERT INTO story_count(id, view_count) VALUES (?, ?)
 ON DUPLICATE KEY UPDATE view_count = view_count + 1" )
public class StoryCount

이것은 오래된 질문이지만, 저는 비슷한 문제가 있어서 이 주제에 추가해야겠다고 생각했습니다.기존 상태 비저장 세션 감사 로그 작성기에 로그를 추가해야 했습니다.기존 구현에서는 표준 세션 구현의 캐싱 동작이 불필요한 오버헤드가 발생하고 최대 절전 모드 청취자가 감사 로그 작성을 위해 해고되는 것을 원치 않았기 때문에 상태 비저장 세션을 사용하고 있었습니다.이 구현은 상호 작용 없이 가능한 한 높은 쓰기 성능을 달성하는 것이었습니다.

그러나 새 로그 유형은 insert-else-update 유형의 동작을 사용해야 했습니다. 여기서 기존 로그 항목을 트랜잭션 시간을 "플래깅" 유형의 동작으로 업데이트하려고 합니다.상태 비저장 세션에서는 saveOrUpdate()가 제공되지 않으므로 insert-else-update를 수동으로 구현해야 했습니다.

다음과 같은 요구사항을 고려할 때:

mysql "삽입..."을 사용할 수 있습니다.사용자 지정 sql-insert를 통해 최대 절전 모드 지속 개체에 대한 중복 키 업데이트" 동작을 수행합니다.사용자 지정 sql-insert 절은 주석(위 답변에서와 같이) 또는 sql-insert 엔티티를 통해 최대 절전 모드 xml 매핑을 정의할 수 있습니다.

<class name="SearchAuditLog" table="search_audit_log" persister="com.marin.msdb.vo.SearchAuditLog$UpsertEntityPersister">

    <composite-id name="LogKey" class="SearchAuditLog$LogKey">
        <key-property
            name="clientId"
            column="client_id"
            type="long"
        />
        <key-property
            name="objectType"
            column="object_type"
            type="int"
        />
        <key-property
            name="objectId"
            column="object_id"
        />
    </composite-id>
    <property
        name="transactionTime"
        column="transaction_time"
        type="timestamp"
        not-null="true"
    />
    <!--  the ordering of the properties is intentional and explicit in the upsert sql below  -->
    <sql-insert><![CDATA[
        insert into search_audit_log (transaction_time, client_id, object_type, object_id)
        values (?,?,?,?) ON DUPLICATE KEY UPDATE transaction_time=now()
        ]]>
    </sql-insert>

원래 포스터는 MySQL에 대해 구체적으로 물어봅니다.mysql로 insert-else-update 동작을 구현했을 때 sql의 'update path'가 실행되었을 때 예외가 발생하고 있었습니다.구체적으로, mysql은 1개의 행만 업데이트될 때 2개의 행이 변경되었다고 보고했습니다(기존 행이 삭제되고 새 행이 삽입된 것으로 보아).해당 기능에 대한 자세한 내용은 이 를 참조하십시오.

업데이트 결과 동면에 영향을 받는 행의 2배가 반환되었을 때 동면 상태는 배치된 상태가 되었습니다.영향을 받는 행이 너무 많음예외는 트랜잭션을 롤백하고 예외를 촉진합니다.예외를 잡고 처리한다 해도 이미 그 시점에서 거래는 철회된 상태였습니다.

약간의 조사 끝에 저는 이것이 동면 상태에서 사용하고 있는 엔티티 지속자의 문제라는 것을 알게 되었습니다.제 경우 hibernate는 SingleTableEntityPersister를 사용하고 있었는데, 이는 업데이트된 행의 수가 배치 작업에서 정의된 행의 수와 일치해야 한다는 예상을 정의합니다.

이 동작을 실행하는 데 필요한 마지막 수정은 (위의 xml 매핑에 표시된 것처럼) 사용자 지정 퍼시스터를 정의하는 것이었습니다.이 경우 SingleTableEntityPersister를 확장하고 삽입 기대를 '오버라이드'하는 작업만 수행할 수 있었습니다.예를 들어, 이 정적 클래스를 지속성 개체에 부착하고 최대 절전 모드 매핑에서 사용자 정의 퍼시스터로 정의했습니다.

public static class UpsertEntityPersister extends SingleTableEntityPersister {

    public UpsertEntityPersister(PersistentClass arg0, EntityRegionAccessStrategy arg1, SessionFactoryImplementor arg2, Mapping arg3) throws HibernateException {
        super(arg0, arg1, arg2, arg3);
        this.insertResultCheckStyles[0] = ExecuteUpdateResultCheckStyle.NONE;
    }

}

이것을 찾기 위해 동면 코드를 뒤지는 데 꽤 오랜 시간이 걸렸습니다. 저는 이것에 대한 해결책이 있는 어떤 주제도 인터넷에서 찾을 수 없었습니다.

Grails를 사용하는 경우 도메인 클래스를 JAVA 월드로 이동하고 @SQ를 사용할 필요가 없는 솔루션을 찾았습니다.주석 삽입:

  1. 사용자 지정 최대 절전 모드 구성 생성
  2. 영구 클래스 맵 재정의
  3. ON DUFFIC 키를 사용하여 원하는 Persistent Class에 사용자 지정 INSERT sql을 추가합니다.

예를 들어, 사용자라는 도메인 개체가 있는데 중복 키 업데이트 시 삽입할 도메인 개체를 삽입하려면 다음과 같은 구성을 만듭니다.

public class MyCustomConfiguration extends GrailsAnnotationConfiguration {

    public MyCustomConfiguration() {
        super();

        classes = new HashMap<String, PersistentClass>() {
            @Override
            public PersistentClass put(String key, PersistentClass value) {
                if (Person.class.getName().equalsIgnoreCase(key)) {
                    value.setCustomSQLInsert("insert into person (version, created_by_id, date_created, last_updated, name) values (?, ?, ?, ?, ?) on duplicate key update id=LAST_INSERT_ID(id)", true, ExecuteUpdateResultCheckStyle.COUNT);
                }
                return super.put(key, value);
            }
        };
    }

DataSource.groovy의 최대 절전 모드 구성으로 추가합니다.

dataSource {
    pooled = true
    driverClassName = "com.mysql.jdbc.Driver"
    configClass = 'MyCustomConfiguration'
}

LAST_INSERT_를 사용하여 주의할 점만 알려드립니다.ID, 문에 명시적으로 설정하지 않으면 INSERT 대신 UPDATE를 실행하면 올바르게 설정되지 않으므로(예: id=LAST_INSERT_)아이디(id).GORM이 어디서 아이디를 가져오는지 확인하지 못했지만 LAST_INSERT_를 사용하는 곳이 있을 것으로 추측합니다.신분증.

도움이 되길 바랍니다.

언급URL : https://stackoverflow.com/questions/913341/can-hibernate-work-with-mysqls-on-duplicate-key-update-syntax

반응형