首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Hibernate 自訂主鍵生成器:實現 Long 類型自增支援叢集

     編輯:Java 相關知識

Hibernate 3.2 內建多種主鍵生成策略(increment、identity、sequence、native、UUID 等),但在實務開發中常遇到無法滿足需求的情境:

  • increment:單機可用,叢集環境下會產生重複鍵
  • identity/sequence/native:依賴資料庫特定機制,跨資料庫移植性差
  • 演算法類(UUID、GUID):產生字串型主鍵,不符合數值型長整數需求

若需求為 Long 型別、自動遞增、支援叢集,則必須實作自訂 IdentifierGenerator。

實作程式碼:資料庫 MAX 值 + 1 策略

package hibernate;

import java.io.Serializable;
import java.sql.Connection;
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.SessionImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.type.Type;

public class IncrementGenerator implements IdentifierGenerator, Configurable {

  private static final Log log = LogFactory.getLog(IncrementGenerator.class);
  private Long next;
  private String sql;

  public Serializable generate(SessionImplementor session, Object object)
      throws HibernateException {
    if (sql != null) {
      getNext(session.connection());
    }
    return next;
  }

  public void configure(Type type, Properties params, Dialect d)
      throws MappingException {
    String table = params.getProperty("table");
    if (table == null) table = params.getProperty(PersistentIdentifierGenerator.TABLE);
    
    String column = params.getProperty("column");
    if (column == null) column = params.getProperty(PersistentIdentifierGenerator.PK);
    
    String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA);
    sql = "select max(" + column + ") from " 
        + (schema == null ? table : schema + '.' + table);
    log.info("ID Generator SQL: " + sql);
  }

  private void getNext(Connection conn) throws HibernateException {
    PreparedStatement st = null;
    ResultSet rs = null;
    try {
      st = conn.prepareStatement(sql);
      rs = st.executeQuery();
      if (rs.next()) {
        next = rs.getLong(1) + 1;
      } else {
        next = 1L;
      }
    } catch (SQLException e) {
      throw new HibernateException(e);
    } finally {
      try { if (rs != null) rs.close(); } catch (SQLException ignore) {}
      try { if (st != null) st.close(); } catch (SQLException ignore) {}
      // 注意:此處不應關閉 connection,由 Hibernate 管理
    }
  }
}

關鍵設計說明

  1. 實作雙介面:IdentifierGenerator(核心生成邏輯)+ Configurable(接收映射檔參數)
  2. 參數化配置:透過 params 取得 table、column、schema,動態組裝 SELECT MAX(...) SQL
  3. 執行邏輯:查詢最大值 +1,無資料時從 1 開始
  4. 資源管理:僅關閉 Statement/ResultSet,不可關閉 Connection(交由 Session 管理)

映射檔配置

<id name="id" type="long" column="id">
  <generator class="hibernate.IncrementGenerator" />
</id>

若需指定資料表/欄位,可在 <generator> 內添加 <param name="table">MY_TABLE</param> 等參數。

叢集環境下的進階考量

上述實作為「查詢 MAX + 1」模式,在高併發叢集下仍可能產生競態條件。生產環境建議採用以下任一進階方案:

  • 資料庫序列/自增欄位:最可靠,配合 native 策略
  • Hi/Lo 演算法:Hibernate 內建 hilo/seqhilo,透過「高位從資料庫取號、低位記憶體自增」減少資料庫往返
  • UUID/GUID:犧牲排序性與儲存空間,換取極致的分散式唯一性
  • 分散式 ID 服務:如 Twitter Snowflake、百度 UidGenerator、美團 Leaf

總結

自訂 IdentifierGenerator 讓 Hibernate 能適應特殊業務需求。本文示範的「MAX+1」策略實作簡單、跨資料庫相容,適合中低併發場景;高併發叢集建議改用資料庫原生序列或 Hi/Lo 機制。掌握擴充點,才能在 ORM 框架中遊刃有餘。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved