首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> DB2 資料庫 >> DB2 教學

Hibernate + DB2 實戰:零 SQL 實現 Java 對象持久化全流程

     編輯:DB2 教學

技術棧與版本鎖定

組件版本說明
DB211.5 / 10.5+支持 JDBC 4.2+, 行級安全、JSON、時間旅行查詢
Hibernate5.4.x / 6.1+JPA 2.2 / 3.0 實現,模塊化、Java 17+ 兼容
Hibernate Tools (Eclipse 插件)5.4+實體/映射文件雙向生成、反向工程
DB2 JDBC 驅動db2jcc4.jar (JCC 4.x)Type 4 純 Java,com.ibm.db2.jcc.DB2Driver
WebSphere Studio / Eclipse2023-06+IDE 環境

項目初始化與依賴管理

Maven 依賴 (推薦)


<dependencies>
  <!-- Hibernate Core -->
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.6.15.Final</version>
  </dependency>
  
  <!-- DB2 JDBC (需從 IBM 下載安裝到本地倉庫或私服) -->
  <dependency>
    <groupId>com.ibm.db2</groupId>
    <artifactId>db2jcc4</artifactId>
    <version>11.5.8.0</version>
  </dependency>
  
  <!-- 連接池: HikariCP (生產必備) -->
  <dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version>
  </dependency>
  
  <!-- JPA API (可選,純 Hibernate API 可不加) -->
  <dependency>
    <groupId>jakarta.persistence</groupId>
    <artifactId>jakarta.persistence-api</artifactId>
    <version>3.1.0</version>
  </dependency>
</dependencies>

核心配置:hibernate.cfg.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  "https://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <!-- JDBC 連接配置 -->
    <property name="hibernate.connection.driver_class">com.ibm.db2.jcc.DB2Driver</property>
    <property name="hibernate.connection.url">jdbc:db2://localhost:50000/SAMPLE:retrieveMessagesFromServerOnGetMessage=true;sendDataAsIs=true;progresssiveStreaming=2;</property>
    <property name="hibernate.connection.username">db2inst1</property>
    <property name="hibernate.connection.password">password</property>
    
    <!-- 連接池配置 (HikariCP) -->
    <property name="hibernate.hikari.dataSourceClassName">com.ibm.db2.jcc.DB2SimpleDataSource</property>
    <property name="hibernate.hikari.maximumPoolSize">20</property>
    <property name="hibernate.hikari.minimumIdle">5</property>
    <property name="hibernate.hikari.idleTimeout">300000</property>
    <property name="hibernate.hikari.connectionTimeout">20000</property>
    
    <!-- 方言:關鍵!DB2 專用優化 -->
    <property name="hibernate.dialect">org.hibernate.dialect.DB2Dialect</property>
    <!-- V10.5+ 推薦: org.hibernate.dialect.DB2105Dialect -->
    
    <!-- Schema 管理 -->
    <property name="hibernate.hbm2ddl.auto">validate</property> 
    <!-- 生產環境: validate / none; 開發: update / create-drop -->
    
    <!-- SQL 輸出與格式化 -->
    <property name="hibernate.show_sql">false</property>
    <property name="hibernate.format_sql">true</property>
    <property name="hibernate.use_sql_comments">true</property>
    
    <!-- 批處理優化 -->
    <property name="hibernate.jdbc.batch_size">50</property>
    <property name="hibernate.order_inserts">true</property>
    <property name="hibernate.order_updates">true</property>
    <property name="hibernate.jdbc.batch_versioned_data">true</property>
    
    <!-- 二級緩存 (可選,推薦 Ehcache/Infinispan) -->
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
    
    <!-- 實體映射 -->
    <mapping class="com.company.model.Employee"/>
    <mapping class="com.company.model.Department"/>
  </session-factory>
</hibernate-configuration>

實體類設計:Employee / Department


@Entity
@Table(name = "EMPLOYEE", schema = "HR")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // 二級緩存
public class Employee implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq_gen")
    @SequenceGenerator(name = "emp_seq_gen", sequenceName = "HR.EMP_SEQ", allocationSize = 50)
    @Column(name = "EMPNO", nullable = false, length = 6)
    private String empno;  // DB2 CHAR(6) 建議用 String
    
    @Column(name = "FIRSTNME", length = 12)
    private String firstName;
    
    @Column(name = "LASTNAME", length = 15)
    private String lastName;
    
    @Column(name = "WORKDEPT", length = 3)
    private String workDept;
    
    @Column(name = "HIREDATE")
    @Temporal(TemporalType.DATE)
    private Date hireDate;
    
    @Column(name = "SALARY", precision = 9, scale = 2)
    private BigDecimal salary;
    
    // 多對一:部門 (延遲加載默認)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "WORKDEPT", referencedColumnName = "DEPTNO", 
                insertable = false, updatable = false) // 共享列,不可寫
    private Department department;
    
    // 業務鍵 equals/hashCode (基於 empno)
    // Getter/Setter/toString 省略...
}

@Entity
@Table(name = "DEPARTMENT", schema = "HR")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Department implements Serializable {
    
    @Id
    @Column(name = "DEPTNO", length = 3)
    private String deptNo;
    
    @Column(name = "DEPTNAME", length = 36)
    private String deptName;
    
    @Column(name = "MGRNO", length = 6)
    private String mgrNo;
    
    @Column(name = "ADMRDEPT", length = 3)
    private String admrDept;
    
    // 一對多:員工 (雙向關聯)
    @OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
    @OrderBy("lastName ASC")
    private Set<Employee> employees = new HashSet<>();
    
    // Getter/Setter...
}

DB2 專用優化映射技巧

1. 序列生成器優化


@SequenceGenerator(
    name = "emp_seq_gen", 
    sequenceName = "HR.EMP_SEQ", 
    allocationSize = 50,  // 關鍵:匹配 DB2 SEQUENCE CACHE 50,減少往返
    initialValue = 1000
)

原則:Hibernate allocationSize 必須等於 DB2 CREATE SEQUENCE ... CACHE N 的 N 值,否則會出現主鍵跳號或重複。

2. CHAR 定長字符串處理


// DB2 CHAR(6) 不自動填充空格,Hibernate 需手動處理或使用 @Column(length=6)
// 方案 A: 實體層自動填充
@PrePersist @PreUpdate
void padEmpno() { 
    if (empno != null) empno = String.format("%-6s", empno); 
}

// 方案 B: 使用 VARCHAR 類型 (推薦新表)
// ALTER TABLE HR.EMPLOYEE ALTER COLUMN EMPNO SET DATA TYPE VARCHAR(6);

3. LOB 大對象映射


@Lob
@Column(name = "RESUME", columnDefinition = "CLOB(1M)")
private String resume;  // 或 byte[] 對應 BLOB

@Lob
@Basic(fetch = FetchType.LAZY) // 關鍵:大對象必須延遲加載
@Column(name = "PHOTO", columnDefinition = "BLOB(10M)")
private byte[] photo;

4. 悲觀鎖與 DB2 隔離級別


// 會話級設置 (連接池 DataSource 屬性)
// db2.jcc.overrideIsolationLevel=4 (RR) / 2 (RS) / 1 (CS)

// 代碼級悲觀鎖
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = session.get(Employee.class, "000010", LockMode.PESSIMISTIC_WRITE);
// 生成: SELECT ... FROM HR.EMPLOYEE WHERE EMPNO=? FOR UPDATE WITH RS USE AND KEEP EXCLUSIVE LOCKS
tx.commit();

DAO 層封裝與事務管理


@Repository
@Transactional(readOnly = true)
public class EmployeeDao {
    
    @PersistenceContext
    private EntityManager em; // JPA 標準
    // 或 @Autowired SessionFactory sessionFactory; // Hibernate 原生
    
    public Employee findById(String empno) {
        return em.find(Employee.class, empno);
    }
    
    @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED)
    public Employee save(Employee emp) {
        if (emp.getEmpno() == null) {
            em.persist(emp);
            return emp;
        } else {
            return em.merge(emp);
        }
    }
    
    @Transactional
    public void batchInsert(List<Employee> employees) {
        int batchSize = 50;
        for (int i = 0; i < employees.size(); i++) {
            em.persist(employees.get(i));
            if (i % batchSize == 0) {
                em.flush();
                em.clear(); // 清理一級緩存,防止 OOM
            }
        }
    }
    
    // 原生 SQL 查詢 (複雜報表)
    public List<Object[]> findSalaryStatsByDept() {
        String sql = "SELECT WORKDEPT, AVG(SALARY), MAX(SALARY), COUNT(*) " +
                     "FROM HR.EMPLOYEE GROUP BY WORKDEPT";
        return em.createNativeQuery(sql).getResultList();
    }
}

反向工程:從現有 DB2 數據庫生成實體

Maven 插件配置


<plugin>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-tools-maven-plugin</artifactId>
  <version>5.6.1.Final</version>
  <configuration>
    <hibernateConfigFile>src/main/resources/hibernate.cfg.xml</hibernateConfigFile>
    <outputDirectory>src/main/java</outputDirectory>
    <packageName>com.company.model</packageName>
    <generateAnnotations>true</generateAnnotations>
    <generateInterfaces>false</generateInterfaces>
    <schemaSelection>HR</schemaSelection>
    <tableNames>EMPLOYEE,DEPARTMENT</tableNames>
    <customTemplates>false</customTemplates>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>com.ibm.db2</groupId>
      <artifactId>db2jcc4</artifactId>
      <version>11.5.8.0</version>
    </dependency>
  </dependencies>
</plugin>

# 執行生成
mvn hibernate-tools:hbm2java

性能調優檢查清單

優化點配置/代碼效果
批量寫入jdbc.batch_size=50 + order_inserts/updates=trueINSERT/UPDATE 吞吐提升 5-10x
序列預分配allocationSize = DB2 SEQUENCE CACHE主鍵生成零網絡往返
延遲加載優化@ManyToOne(fetch=LAZY) + @BatchSize(size=25)解決 N+1 查詢問題
二級緩存Ehcache/Infinispan + READ_WRITE 策略熱點數據 (部門/字典) 訪問零 SQL
查詢計劃緩存Hibernate 自動準備語句 + DB2 包緩存重複查詢編譯開銷歸零
只讀查詢優化@Transactional(readOnly=true) + scrollable results大結果集流式處理,內存常數
統計信息同步定期 RUNSTATS + REORG優化器選擇最優計劃

常見坑與解決方案

🚫 常見錯誤

  • DB2Dialect 版本不匹配:使用 DB2Dialect 連接 V10.5+ 導致語法錯誤 → 改用 DB2105Dialect 或更高
  • CHAR 字段空格截斷:JPA 規範默認 trim CHAR → 設置 hibernate.use_identifier_rollback=false 或手動填充
  • LOB 流關閉異常:stream closed 錯誤 → 確保 @Basic(fetch=LAZY) 且在事務內訪問,或啟用 hibernate.jdbc.lob.non_contextual_creation=true
  • 死鎖頻發:DB2 默認 RR 隔離級別 + 索引缺失 → 降級至 CS + 補充索引 + 統一訪問順序
  • 序列跳號:allocationSize ≠ CACHE → 嚴格對齊,或改用 GenerationType.IDENTITY (DB2 IDENTITY 列)

總結

Hibernate + DB2 組合通過 方言適配、序列對齊、批處理優化、緩存策略 四大支柱,實現了 Java 對象到關系表的高性能、零 SQL 映射。關鍵成功因素:

  1. 選對 Dialect 版本 (DB2 版本對齊)
  2. 序列 allocationSize = DB2 CACHE
  3. 啟用 批處理重排序 (order_inserts/updates)
  4. 熱點數據上 二級緩存
  5. 定期 RUNSTATS/REORG 維護優化器統計信息

遵循此指南,可構建出可維護、高性能、可擴展的 DB2 持久層架構。

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