Hibernate 5.x 引入了全新的啟動引導 API(BootstrapServiceRegistry 等),與 Spring 整合時需注意 LocalSessionFactoryBean 的包路徑變更(從 org.springframework.orm.hibernate3/4 遷移至 org.springframework.orm.hibernate5)。本文基於 Maven 多模項目,詳細記錄從依賴管理、容器配置、實體定義到測試驗證的完整流程。
核心策略:使用 <properties> 統一版本號,spring-orm 模塊提供整合支持,排除傳遞依賴衝突。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hibernate.version>5.1.0.Final</hibernate.version>
<spring.version>4.3.25.RELEASE</spring.version> <!-- 建議用 4.3.x 維護版 -->
<mysql.version>5.1.47</mysql.version>
<dbcp.version>1.4</dbcp.version>
</properties>
<dependencies>
<!-- 測試 -->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
<!-- 日誌門面與實現 -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency>
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.25</version></dependency>
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>
<!-- 數據庫驅動與連接池 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency>
<dependency><groupId>commons-dbcp</groupId><artifactId>commons-dbcp</artifactId><version>${dbcp.version}</version></dependency>
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.10</version></dependency> <!-- Hibernate 5 需要 -->
<!-- Hibernate 5 核心 -->
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>${hibernate.version}</version></dependency>
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>5.2.4.Final</version></dependency> <!-- 版本獨立 -->
<!-- Spring 核心模塊 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>${spring.version}</version></dependency> <!-- 關鍵:整合包 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version><scope>test</scope></dependency>
<!-- Spring MVC (可選,後續擴展用) -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency>
</dependencies>
applicationContext.xml)核心 Bean:DataSource (DBCP)、LocalSessionFactoryBean (Hibernate 5)、HibernateTransactionManager (事務)、tx:annotation-driven (註解驅動)。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:context="https://www.springframework.org/schema/context"
xmlns:tx="https://www.springframework.org/schema/tx"
xsi:schemaLocation="... (省略 schemaLocation)">
<!-- 1. 屬性佔位符 (建議外置 jdbc.properties) -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 2. 數據源 (DBCP) -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 連接池調優參數 -->
<property name="initialSize" value="5" />
<property name="maxActive" value="20" />
<property name="maxIdle" value="10" />
<property name="minIdle" value="2" />
<property name="maxWait" value="10000" />
<property name="defaultAutoCommit" value="true" />
</bean>
<!-- 3. SessionFactory (Hibernate 5 關鍵類) -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.example.model" /> <!-- 掃描實體包 -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop> <!-- 自動建表 -->
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</prop> <!-- 關鍵:Spring 管理 CurrentSession -->
<prop key="javax.persistence.validation.mode">none</prop> <!-- 關閉 JSR303 驗證(可選) -->
</props>
</property>
</bean>
<!-- 4. 事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 5. 註解驅動事務 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- 6. 掃描 DAO/Service 組件 -->
<context:component-scan base-package="com.example.dao, com.example.service" />
</beans>
package com.example.model;
import javax.persistence.*;
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // MySQL 自增
private Integer id;
@Column(name = "user_name", length = 50, nullable = false)
private String name;
@Column(name = "email", unique = true)
private String email;
// Getter/Setter/toString 省略
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired private SessionFactory sessionFactory;
// 獲取當前線程綁定的 Session (Spring 管理)
private Session getSession() {
return sessionFactory.getCurrentSession();
}
@Override
public void save(User user) {
getSession().save(user);
}
@Override
public User getById(Integer id) {
return getSession().get(User.class, id);
}
}
@Service
@Transactional // 類級別事務,public 方法自動開啟事務
public class UserServiceImpl implements UserService {
@Autowired private UserDao userDao;
@Override
@Transactional(readOnly = true) // 查詢只讀優化
public User findById(Integer id) {
return userDao.getById(id);
}
@Override
public void register(User user) {
// 業務邏輯...
userDao.save(user);
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringHibernateIntegrationTest {
@Autowired private UserService userService;
@Autowired private SessionFactory sessionFactory;
@Test
public void testSaveAndQuery() {
User user = new User();
user.setName("Spring+Hibernate5");
user.setEmail("test@integration.com");
userService.register(user); // 事務提交
// 驗證
User found = userService.findById(user.getId());
assertNotNull(found);
assertEquals(user.getName(), found.getName());
System.out.println("整合測試通過: " + found);
}
}
| 問題現象 | 原因分析 | 解決方案 |
|---|---|---|
NoSuchMethodError: LocalSessionFactoryBean | Spring 版本與 Hibernate 版本不匹配,或引用了舊包路徑 | 確保用 org.springframework.orm.hibernate5.LocalSessionFactoryBean,Spring 4.3+ 支持 Hibernate 5 |
No CurrentSessionContext configured | 缺少 hibernate.current_session_context_class 配置 | 設置為 org.springframework.orm.hibernate5.SpringSessionContext |
| 事務不生效,數據未提交 | 未掃描 Service 包、未開啟 tx:annotation-driven、方法非 public | 檢查 component-scan、tx 配置、方法修飾符 |
hbm2ddl.auto 配置錯誤或權限不足 | 設置為 update,確保數據庫用戶有 DDL 權限 |
Spring 4 整合 Hibernate 5.1 的核心在於:正確的 Maven 依賴版本組合、Spring 專用的 Hibernate 5 SessionFactory Bean、SpringSessionContext 綁定當前 Session、聲明式事務管理。掌握此套配置,可快速搭建穩健的持久層基礎設施,後續擴展 Spring Data JPA 或 MyBatis 亦同理可證。