Hibernate -- Increment 와 Hilo 메 인 키 생 성 전략 원리
package org.hibernate.id;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.jdbc.Batcher;
import org.hibernate.mapping.Table;
import org.hibernate.type.Type;
import org.hibernate.util.StringHelper;
public class IncrementGenerator
implements IdentifierGenerator, Configurable
{
private static final Log log = LogFactory.getLog(IncrementGenerator.class);
private long next;
private String sql;
private Class returnClass;
public synchronized Serializable generate(SessionImplementor session, Object object)
throws HibernateException
{
if (this.sql != null) {
getNext(session);
}
return IdentifierGeneratorFactory.createNumber(this.next++, this.returnClass);
}
public void configure(Type type, Properties params, Dialect dialect)
throws MappingException
{
String tableList = params.getProperty("tables");
if (tableList == null) tableList = params.getProperty("identity_tables");
String[] tables = StringHelper.split(", ", tableList);
String column = params.getProperty("column");
if (column == null) column = params.getProperty("target_column");
String schema = params.getProperty("schema");
String catalog = params.getProperty("catalog");
this.returnClass = type.getReturnedClass();
StringBuffer buf = new StringBuffer();
for (int i = 0; i < tables.length; ++i) {
if (tables.length > 1) {
buf.append("select ").append(column).append(" from ");
}
buf.append(Table.qualify(catalog, schema, tables[i]));
if (i >= tables.length - 1) continue; buf.append(" union ");
}
if (tables.length > 1) {
buf.insert(0, "( ").append(" ) ids_");
column = "ids_." + column;
}
this.sql = "select max(" + column + ") from " + buf.toString();
}
private void getNext(SessionImplementor session)
{
log.debug("fetching initial value: " + this.sql);
try
{
PreparedStatement st = session.getBatcher().prepareSelectStatement(this.sql);
try {
ResultSet rs = st.executeQuery();
try {
if (rs.next()) {
this.next = (rs.getLong(1) + 1L);
if (rs.wasNull()) this.next = 1L;
}
else {
this.next = 1L;
}
this.sql = null;
log.debug("first free id: " + this.next);
}
finally {
rs.close();
}
}
finally {
session.getBatcher().closeStatement(st);
}
}
catch (SQLException sqle)
{
throw JDBCExceptionHelper.convert(session.getFactory().getSQLExceptionConverter(), sqle, "could not fetch initial value for increment generator", this.sql);
}
}
}
/* Location: D:\Workspace\HibernateTest\bin\lib\hibernate3.jar
* Qualified Name: org.hibernate.id.IncrementGenerator
* Java Class Version: 1.4 (48.0)
* JD-Core Version: 0.5.3
*/
generate 방법 을 보십시오. sql 문장 이 Null 과 같 지 않 을 때 다음 버 전 을 가 져 옵 니 다. 즉, 데이터베이스 에서 가장 큰 id 를 가 져 온 다음 에 sql 을 null 로 설정 합 니 다.이렇게 하면 다음 에 id 를 가 져 올 때 데이터베이스 에서 가 져 오지 않 고 메모리 + 에 있 습 니 다.그러면 문제 가 생 길 수 있 습 니 다.예 를 들 어 시스템 이 현재 A 노드 에서 일 하고 있 는데 next 가 10 까지 갔 을 때 갑자기 네트워크 가 끊 겨 서 시스템 이 B 노드 로 전환 되 었 다. B 노드 가 id 를 얻 을 때 데이터 베 이 스 를 조회 하여 최대 치 를 얻 었 고 next 를 순조롭게 실행 하여 20 까지 갔다. 이때 A 노드 의 next 는 10 이 고 sql 문 구 는 null 이다.따라서 A 는 데이터 베 이 스 를 조회 하지 않 고 next 의 값 을 직접 가 져 오 면 10 부터 20 까지 의 id 가 메 인 키 충돌 이 발생 하고 A 노드 를 다시 시작 해 야 문 제 를 해결 할 수 있 습 니 다.
따라서 hibenate 를 사용 하 는 클 러 스 터 와 관련 이 있다 면 increment 를 메 인 키 생 성 전략 으로 사용 할 수 없습니다.위 에서 분석 한 바 와 같이 이 문 제 를 해결 하려 면 두 프로 세 스 간 의 메모리 공 유 를 해결 해 야 한다. 즉, next 변 수 를 공유 하 는 것 이지 만 실현 하기 에는 매우 복잡 하 다.마지막 으로 Hilo 의 메 인 키 생 성 전략 으로 해결 합 니 다.그 부분의 원본 코드 는 다음 과 같다.
public Serializable doWorkInCurrentTransaction(Connection conn, String sql)
throws SQLException
{
int result;
int rows;
do
{
sql = this.query;
SQL.debug(this.query);
PreparedStatement qps = conn.prepareStatement(this.query);
try {
ResultSet rs = qps.executeQuery();
if (!(rs.next())) {
String err = "could not read a hi value - you need to populate the table: " + this.tableName;
log.error(err);
throw new IdentifierGenerationException(err);
}
int result = rs.getInt(1);
rs.close();
}
catch (SQLException sqle)
{
throw sqle;
}
finally {
qps.close(); }
sql = this.update;
SQL.debug(this.update);
PreparedStatement ups = conn.prepareStatement(this.update);
int rows;
try {
ups.setInt(1, result + 1);
ups.setInt(2, result);
rows = ups.executeUpdate();
}
catch (SQLException sqle)
{
throw sqle;
}
finally {
ups.close();
}
}
while (rows == 0);
return new Integer(result);
}
}
위의 코드 는 메 인 키 를 가 져 올 때 hibenate 는 같은 사무 에서 데이터베이스 에서 메 인 키 를 꺼 내 메 인 키 를 업데이트 합 니 다.이렇게 하면 다른 프로 세 스 가 데이터베이스 에서 값 을 추출 할 때 최대 값 을 얻 을 수 있 도록 합 니 다.
관건 은 다음 코드 입 니 다.
public class TableHiLoGenerator extends TableGenerator
{
public static final String MAX_LO = "max_lo";
private long hi;
private int lo;
private int maxLo;
private Class returnClass;
private static final Log log = LogFactory.getLog(TableHiLoGenerator.class);
public void configure(Type type, Properties params, Dialect d) {
super.configure(type, params, d);
this.maxLo = PropertiesHelper.getInt("max_lo", params, 32767);
this.lo = (this.maxLo + 1);
this.returnClass = type.getReturnedClass();
}
public synchronized Serializable generate(SessionImplementor session, Object obj) throws HibernateException
{
if (this.maxLo < 1)
{
int val = ((Integer)super.generate(session, obj)).intValue();
return IdentifierGeneratorFactory.createNumber(val, this.returnClass);
}
if (this.lo > this.maxLo) {
int hival = ((Integer)super.generate(session, obj)).intValue();
this.lo = ((hival == 0) ? 1 : 0);
this.hi = (hival * (this.maxLo + 1));
log.debug("new hi value: " + hival);
}
return IdentifierGeneratorFactory.createNumber(this.hi + this.lo++, this.returnClass);
}
}
우 리 는 볼 수 있다. ,홈 키 를 조회 할 때 데이터베이스 의 홈 키 + 1 을 저장 합 니 다. 만약 에 이 사물 이 성공 하지 못 하면 this. lo 는 this. max Lo 보다 영원히 클 것 입 니 다. 이때 B 로 전환 되 더 라 도 다시 A 로 돌아 갈 때 먼저 데이터 베 이 스 를 조회 하여 this. hi 가 영원히 같 지 않도록 합 니 다.성공 하면 B 에서 A 로 전환 할 때 B 의 hi 값 이 A 의 hi 값 보다 적어도 this. max Lo + 1 크기 때문에 A 가 안에서 낮은 위 치 를 유지 하 더 라 도 B 와 같 지 않 습 니 다. this. lo 가 this. max Lo 보다 클 때 데이터 베 이 스 를 조회 하기 때 문 입 니 다.이 알고리즘 은 너무 아름답다.
그 상세 한 원 리 는 소스 코드 와 아래 의 링크 를 참조 하여 비교 할 수 있다.
http://hi.baidu.com/sai5d/blog/item/88e5f4db09e90277d0164e30.html
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[JPA] 즉시로딩(EAGER)과 지연로딩(LAZY) (왜 LAZY 로딩을 써야할까?) (1)Proxy는 이 글의 주제인 즉시로딩과 지연로딩을 구현하는데 중요한 개념인데, 일단 원리는 미뤄두고 즉시로딩과 지연로딩이 무엇인지에 대해 먼저 알아보자. 눈 여겨 볼 곳은 'fetch = FetchType.EAGER...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.