| 組件 | 版本 | 說明 |
|---|---|---|
| DB2 | 11.5 / 10.5+ | 支持 JDBC 4.2+, 行級安全、JSON、時間旅行查詢 |
| Hibernate | 5.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 / Eclipse | 2023-06+ | IDE 環境 |
<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>
<?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>
@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...
}
@SequenceGenerator(
name = "emp_seq_gen",
sequenceName = "HR.EMP_SEQ",
allocationSize = 50, // 關鍵:匹配 DB2 SEQUENCE CACHE 50,減少往返
initialValue = 1000
)
原則:Hibernate
allocationSize必須等於 DB2CREATE SEQUENCE ... CACHE N的 N 值,否則會出現主鍵跳號或重複。
// 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);
@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;
// 會話級設置 (連接池 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();
@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();
}
}
<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=true | INSERT/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 連接 V10.5+ 導致語法錯誤 → 改用 DB2105Dialect 或更高hibernate.use_identifier_rollback=false 或手動填充stream closed 錯誤 → 確保 @Basic(fetch=LAZY) 且在事務內訪問,或啟用 hibernate.jdbc.lob.non_contextual_creation=trueGenerationType.IDENTITY (DB2 IDENTITY 列)Hibernate + DB2 組合通過 方言適配、序列對齊、批處理優化、緩存策略 四大支柱,實現了 Java 對象到關系表的高性能、零 SQL 映射。關鍵成功因素:
allocationSize = DB2 CACHE遵循此指南,可構建出可維護、高性能、可擴展的 DB2 持久層架構。