一、缘由
上一篇文章Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法介绍到了怎么样在Sping、MyBatis、Hibernate整合的应用中动态切换DataSource数据源的方法,但最终遗留下一个问题:不能切换数据库方言。数据库方言可能在当前应用的架构中意义不是很大,但是如果单纯用MyBatis或Hibernate做数据库持久化操作,还是要处理这一问题。
那么下面将介绍怎么样动态切换SessionFactory,为什么要切换SessionFactory?
因 为这里切换SessionFactory就可以实现多数据源和多个SessionFactory,每个SessionFactory有自己独立的数据库配 置和SessionFactory的相关配置。我们的数据库方言就配置在SessionFactory这里,所以实现了切换SessionFactory 也就实现了切换数据库方言的问题。这个主要是针对Hibernate来操作的,而MyBatis则需要动态切换SqlSessionFactory才行。
二、实现代码
1、定义全局切换SessionFactory的工具
package com.hoo.framework.spring.support;
/**
* <b>function:</b> 多数据源
* @author hoojo
* @createDate 2013-9-27 上午11:36:57
* @file CustomerContextHolder.java
* @package com.hoo.framework.spring.support
* @project SHMB
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public abstract class CustomerContextHolder {public final static String SESSION_FACTORY_MYSQL = "mysql";public final static String SESSION_FACTORY_ORACLE = "oracle";private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();public static void setCustomerType(String customerType) {contextHolder.set(customerType);}public static String getCustomerType() {return contextHolder.get();
}public static void clearCustomerType() {contextHolder.remove();}}同样上面的静态变量和前一文章中介绍的一致,它需要和下面配置文件中的SessionFactory的key对应。
2、实现自己的SessionFactory
定义好接口
package com.hoo.framework.spring.support.core;
import org.hibernate.SessionFactory;
/**
* <b>function:</b> 动态SessionFactory接口
* @author hoojo
* @createDate 2013-10-12 下午03:29:52
* @file DynamicSessionFactory.java
* @package com.hoo.framework.spring.support.core
* @project SHMB
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public interface DynamicSessionFactory extends SessionFactory {public SessionFactory getHibernateSessionFactory();
}
实现接口
package com.hoo.framework.spring.support.core;
import java.io.Serializable;
import java.sql.Connection;
import java.util.Map;
import java.util.Set;
import javax.naming.NamingException;
import javax.naming.Reference;
import org.hibernate.Cache;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.TypeHelper;
import org.hibernate.classic.Session;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.stat.Statistics;
import com.hoo.framework.spring.support.CustomerContextHolder;
/**
* <b>function:</b> 动态数据源实现
* @author hoojo
* @createDate 2013-10-12 下午03:31:31
* @file DynamicSessionFactoryImpl.java
* @package com.hoo.framework.spring.support.core
* @project SHMB
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
@SuppressWarnings({ "unchecked", "deprecation" })public class DynamicSessionFactoryImpl implements DynamicSessionFactory {private static final long serialVersionUID = 5384069312247414885L;private Map<Object, SessionFactory> targetSessionFactorys;
private SessionFactory defaultTargetSessionFactory;
/**
* @see com.hoo.framework.spring.support.core.DynamicSessionFactory#getHibernateSessionFactory()
* <b>function:</b> 重写这个方法,这里最关键
* @author hoojo
* @createDate 2013-10-18 上午10:45:25
*/
@Overridepublic SessionFactory getHibernateSessionFactory() {
SessionFactory targetSessionFactory = targetSessionFactorys.get(CustomerContextHolder.getCustomerType());if (targetSessionFactory != null) {
return targetSessionFactory;
} else if (defaultTargetSessionFactory != null) {return defaultTargetSessionFactory;
}return null;
}@Overridepublic void close() throws HibernateException {this.getHibernateSessionFactory().close();
}@Overridepublic boolean containsFetchProfileDefinition(String s) {return this.getHibernateSessionFactory().containsFetchProfileDefinition(s);}@Overridepublic void evict(Class clazz) throws HibernateException {this.getHibernateSessionFactory().evict(clazz);
}@Overridepublic void evict(Class clazz, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evict(clazz, serializable);
}@Overridepublic void evictCollection(String s) throws HibernateException {this.getHibernateSessionFactory().evictCollection(s);
}@Overridepublic void evictCollection(String s, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evictCollection(s, serializable);
}@Overridepublic void evictEntity(String entity) throws HibernateException {this.getHibernateSessionFactory().evictEntity(entity);
}@Overridepublic void evictEntity(String entity, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evictEntity(entity, serializable);
}@Overridepublic void evictQueries() throws HibernateException {this.getHibernateSessionFactory().evictQueries();
}@Overridepublic void evictQueries(String queries) throws HibernateException {this.getHibernateSessionFactory().evictQueries(queries);
}@Overridepublic Map<String, ClassMetadata> getAllClassMetadata() {
return this.getHibernateSessionFactory().getAllClassMetadata();}@Overridepublic Map getAllCollectionMetadata() {
return this.getHibernateSessionFactory().getAllClassMetadata();}@Overridepublic Cache getCache() {
return this.getHibernateSessionFactory().getCache();}@Overridepublic ClassMetadata getClassMetadata(Class clazz) {
return this.getHibernateSessionFactory().getClassMetadata(clazz);}@Overridepublic ClassMetadata getClassMetadata(String classMetadata) {
return this.getHibernateSessionFactory().getClassMetadata(classMetadata);}@Overridepublic CollectionMetadata getCollectionMetadata(String collectionMetadata) {
return this.getHibernateSessionFactory().getCollectionMetadata(collectionMetadata);}@Overridepublic Session getCurrentSession() throws HibernateException {return this.getHibernateSessionFactory().getCurrentSession();}@Overridepublic Set getDefinedFilterNames() {
return this.getHibernateSessionFactory().getDefinedFilterNames();}@Overridepublic FilterDefinition getFilterDefinition(String definition) throws HibernateException {return this.getHibernateSessionFactory().getFilterDefinition(definition);}@Overridepublic Statistics getStatistics() {
return this.getHibernateSessionFactory().getStatistics();}@Overridepublic TypeHelper getTypeHelper() {
return this.getHibernateSessionFactory().getTypeHelper();}@Overridepublic boolean isClosed() {return this.getHibernateSessionFactory().isClosed();}@Overridepublic Session openSession() throws HibernateException {return this.getHibernateSessionFactory().openSession();}@Overridepublic Session openSession(Interceptor interceptor) throws HibernateException {return this.getHibernateSessionFactory().openSession(interceptor);}@Overridepublic Session openSession(Connection connection) {
return this.getHibernateSessionFactory().openSession(connection);}@Overridepublic Session openSession(Connection connection, Interceptor interceptor) {
return this.getHibernateSessionFactory().openSession(connection, interceptor);}@Overridepublic StatelessSession openStatelessSession() {
return this.getHibernateSessionFactory().openStatelessSession();}@Overridepublic StatelessSession openStatelessSession(Connection connection) {
return this.getHibernateSessionFactory().openStatelessSession(connection);}@Overridepublic Reference getReference() throws NamingException {return this.getHibernateSessionFactory().getReference();}public void setTargetSessionFactorys(Map<Object, SessionFactory> targetSessionFactorys) {this.targetSessionFactorys = targetSessionFactorys;
}public void setDefaultTargetSessionFactory(SessionFactory defaultTargetSessionFactory) {this.defaultTargetSessionFactory = defaultTargetSessionFactory;
}}上面最重要的就是getHibernateSessionFactory重写这个方法,其他方法和原来实现的无异。重写这个方法后利用CustomerContextHolder动态设置SessionFactory类型就可以动态的切换SessionFactory。
3、动态的事务管理器,因为我们这里是动态切换SessionFactory,所以事务这块也需要动态切换SessionFactory来完成事务的操作。
package com.hoo.framework.spring.support.tx;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import com.hoo.framework.spring.support.core.DynamicSessionFactory;
/**
* <b>function:</b> 重写HibernateTransactionManager事务管理器,实现自己的动态的事务管理器
* @author hoojo
* @createDate 2013-10-12 下午03:54:02
* @file DynamicTransactionManager.java
* @package com.hoo.framework.spring.support.tx
* @project SHMB
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public class DynamicTransactionManager extends HibernateTransactionManager {private static final long serialVersionUID = -4655721479296819154L;/**
* @see org.springframework.orm.hibernate4.HibernateTransactionManager#getDataSource()
* <b>function:</b> 重写
* @author hoojo
* @createDate 2013-10-12 下午03:55:24
*/
@Overridepublic DataSource getDataSource() {
return SessionFactoryUtils.getDataSource(getSessionFactory());
}/**
* @see org.springframework.orm.hibernate4.HibernateTransactionManager#getSessionFactory()
* <b>function:</b> 重写
* @author hoojo
* @createDate 2013-10-12 下午03:55:24
*/
@Overridepublic SessionFactory getSessionFactory() {
DynamicSessionFactory dynamicSessionFactory = (DynamicSessionFactory) super.getSessionFactory();
SessionFactory hibernateSessionFactory = dynamicSessionFactory.getHibernateSessionFactory();return hibernateSessionFactory;
}}这里主要重写getDataSource()/getSessionFactory()这两个方法,getSessionFactory方法是利用 我们上面定义的接口来动态获取我们在上下文(CustomerContextHolder)中定义切换的SessionFactory对象。而 getDataSource则是获得动态SessionFactory的DataSource,这里也不难理解。
4、至此,重写的接口和实现都完成,下面开始配置相关的代码
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 配置c3p0数据源 -->
<bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass" value="${datasource.driver}"/><property name="jdbcUrl" value="${datasource.url}"/><property name="user" value="${datasource.username}"/><property name="password" value="${datasource.password}"/><property name="acquireIncrement" value="${c3p0.acquireIncrement}"/><property name="initialPoolSize" value="${c3p0.initialPoolSize}"/><property name="minPoolSize" value="${c3p0.minPoolSize}"/><property name="maxPoolSize" value="${c3p0.maxPoolSize}"/><property name="maxIdleTime" value="${c3p0.maxIdleTime}"/><property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/><property name="maxStatements" value="${c3p0.maxStatements}"/><property name="numHelperThreads" value="${c3p0.numHelperThreads}"/><property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/><property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/></bean><bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/><property name="user" value="root"/><property name="password" value="jp2011"/><property name="acquireIncrement" value="${c3p0.acquireIncrement}"/><property name="initialPoolSize" value="${c3p0.initialPoolSize}"/><property name="minPoolSize" value="${c3p0.minPoolSize}"/><property name="maxPoolSize" value="${c3p0.maxPoolSize}"/><property name="maxIdleTime" value="${c3p0.maxIdleTime}"/><property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/><property name="maxStatements" value="${c3p0.maxStatements}"/><property name="numHelperThreads" value="${c3p0.numHelperThreads}"/><property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/><property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/></bean><!-- Annotation 配置sessionFactory,配置数据库连接,注入hibernate数据库配置 -->
<bean id="mySQLSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"><property name="dataSource" ref="dataSourceMySQL"/><property name="packagesToScan" value="com.hoo.**.mysqlentity"/><property name="annotatedClasses"><array><value>com.hoo.common.entity.IDGenerator</value></array></property><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop><!-- 链接释放策略 on_close | after_transaction | after_statement | auto -->
<prop key="hibernate.connection.release_mode">after_transaction</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.format_sql">true</prop></props></property></bean><!-- Annotation 配置sessionFactory,配置数据库连接,注入hibernate数据库配置 -->
<bean id="oracleSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"><property name="dataSource" ref="dataSourceOracle"/><property name="packagesToScan" value="com.hoo.**.entity"/><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop><prop key="hibernate.connection.release_mode">after_transaction</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.format_sql">true</prop><!--prop key="hibernate.hbm2ddl.auto">update</prop-->
</props></property></bean><!-- 动态SessionFactory -->
<bean id="sessionFactory" class="com.hoo.framework.spring.support.core.DynamicSessionFactoryImpl"><property name="defaultTargetSessionFactory" ref="oracleSessionFactory"/><property name="targetSessionFactorys"><map><entry value-ref="oracleSessionFactory" key="oracle"/><entry value-ref="mySQLSessionFactory" key="mysql"/></map></property></bean><!-- 自定义动态切换SessionFactory事务管理器,注入sessionFactory -->
<bean id="transactionManager" class="com.hoo.framework.spring.support.tx.DynamicTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean><!-- 配置事务的传播特性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="edit*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="execute*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="*" read-only="true" /></tx:attributes></tx:advice><!-- 配置那些类、方法纳入到事务的管理 -->
<aop:config><aop:pointcut expression="execution(* com.hoo.**.service.impl.*.*(..))" id="transactionManagerMethod"/><aop:advisor advice-ref="txAdvice" pointcut-ref="transactionManagerMethod" /></aop:config></beans>配置也和我们之前的配置差不多,就是事务管理器部分注入的SessionFactory是我们自己定义的。
5、简单测试
@Testpublic void testAdd() {// 主要就是这行代码 它才是完成SessionFactory的切换
CustomerContextHolder.setCustomerType(CustomerContextHolder.SESSION_FACTORY_MYSQL);DeviceInfo entity = new DeviceInfo();
entity.setSbbh(System.currentTimeMillis() + "");
entity.setIpdz("my ip address2");
entity.setJd(1234);try {
service.add(entity);} catch (Exception e) {
e.printStackTrace();}}经过测试发现可以查询数据,利用hibernate查询分页也可以生成对应数据库的分页语句。同样在服务层的方法中添加完操作后,特意抛出一个异常,数据也能正常回滚操作,所以DynamicTransactionManager也是起到了该有的作用!
这里我的测试用例是手动切换CustomerContextHolder.setCustomerType的,但实际开发中我们还是得用Spring的Aop中的Interceptor进行切面编程,完成动态切换SessionFactory。上一篇文章Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法已经提到了(读者可以参考该篇博文中的第二节的3、7小节DataSourceMethodInterceptor MultipleDataSourceInterceptor),这里就不再赘述!
三、引发的问题
上述的实现如果在使用不当的情况下,在实际开发中很可能存在一些问题!
问题1是这样的,通常事务是在Service这层完成,这个应该是没有异议。倘若是这样的话,问题便出现了。而通常我们在MVC中的视图层中仅仅会调用Service中一个方法来完成所有当前业务的处理。如果这个Service中同时操作dbA、dbB两个数据库,事务提交的时候是用哪个数据库的事务呢?所以我们把不同数据库的操作放在一个方法,就会出现事务的问题的,除非两个数据库的事务能够同时回滚!
大致情景是:Service中的add4Oracle操作Oracle数据库,Service中的 add4MySQL操作MySQL数据库,最后把Service中的add4Oracle、add4MySQL方法放到一个operation方法 中,MVC视图层的业务控制调用Service中的operation。像这样的情况,如果add4Oracle或add4MySQL方法中的某一个方法 出现异常,就需要把两个数据库事务都回滚。
解决办法就是在Service或Dao中的一个方法中同时操作两个数据库,手动完成事务控制。出现异常就全部回滚,没有异常就全部提交,只能这样牺牲下。
问题2是利用拦截器不能同时切换一个方法操作两个数据库的可能,例如一个service中的 query方法即需要用query MySQL,也需要query Oracle。那么解决办法就是在query方法中调用当前service的query4Oracle和query4Oracle,那样绕过去还是能利用 拦截器进行动态切换的。
四、总结
要完成数据库方言的切换,我们就需要配置多个SessionFactory利用自己实现的 DynamicSessionFactory和CustomerContextHolder完成SessionFactory的切换。利用 DynamicTransactionManager完成当前Session的事务操作。通过对这些对象的操作和配置,最终可以完成 SessionFactory的动态切换。实际使用中虽然有些问题出现,但是最终还是有解决方案,尽管有些不完美。所谓的完美的东西总有它不完美的地方, 这样才算完美,比如断臂的维纳斯女神~!
相关推荐
2. **配置 Hibernate**:创建 Hibernate 的配置文件 `hibernate.cfg.xml`,指定数据库连接、方言、实体扫描路径等。 3. **配置数据源**:在 Spring 配置文件中配置数据源,可以使用 Druid 或其他第三方库。 4. **...
本文将详细讲解如何在Spring 3.2.3版本中配置SessionFactory,以便整合Hibernate 4.2.2,实现对数据库操作的高效管理。SessionFactory是Hibernate的核心组件,它负责创建Session对象,而Session则是与数据库交互的...
通过在 `ApplicationContext.xml` 配置文件中定义一个 bean,我们可以将数据库连接信息(如数据源 `dataSource`)和 Hibernate 相关属性(如方言 `dialect`)注入到 SessionFactory 中。以下是一个示例配置: ```...
标题 "Spring 简单整合 Hibernate 例子" 暗示了我们将探讨如何在Java Web开发中结合两个流行框架——Spring和Hibernate。Spring是一个全面的开源应用框架,它提供了依赖注入(DI)和面向切面编程(AOP)等功能,而...
6. **整合Hibernate和Spring**:使用Spring的Hibernate支持,配置SessionFactory,可以通过`<bean>`标签创建SessionFactory并注入到需要的地方。 7. **测试**:编写单元测试和集成测试,验证SSH整合是否成功,确保...
1. **配置Hibernate**:在Spring的配置文件中,我们需要配置Hibernate的属性,如数据库连接信息、方言、实体扫描路径等,以及SessionFactory bean。 2. **配置事务管理器**:创建PlatformTransactionManager类型的...
【Spring3 Hibernate4 Ehcache整合实例详解】 在Java企业级应用开发中,Spring、Hibernate和Ehcache是三个非常重要的框架和技术。Spring作为轻量级的IoC(Inversion of Control)和AOP(Aspect Oriented ...
总的来说,Spring和Hibernate的整合旨在提供一个统一的、可扩展的开发环境,使得开发者可以更专注于业务逻辑,而不是底层的数据库操作和事务管理。通过合理配置和设计,这种整合可以提高开发效率,增强系统的可维护...
Spring4可以管理Hibernate的SessionFactory,并通过AOP实现事务管理,使得数据库操作更加简洁和高效。 SSH整合的关键步骤包括: 1. **配置环境**:确保所有必要的JAR包(包括Struts2、Spring4、Hibernate5及其依赖...
- Hibernate的本地SessionFactoryBean:配置Hibernate的相关属性,如hibernate.dialect(数据库方言)、hibernate.show_sql(显示SQL语句)等。 - 实体映射(Entity Mapping):定义实体类与数据库表之间的映射,...
- **Hibernate配置**:编写`hibernate.cfg.xml`文件,定义数据库连接参数、实体扫描路径、方言、缓存策略等。 - **实体类和映射文件**:为数据库表创建对应的Java实体类,并编写对应的Hibernate映射文件(.hbm.xml...
在Spring 2.5中,我们通常会看到一个`hibernate.cfg.xml`文件,用于配置Hibernate的数据库连接、方言、缓存等设置。此外,每个数据模型对应的实体类还会有一个对应的映射文件,如`*.hbm.xml`,这些文件描述了对象与...
3. **配置Hibernate**:在Hibernate配置文件(如`hibernate.cfg.xml`)中,设定数据库连接参数、实体类扫描路径、方言、缓存等。实体类是映射到数据库表的对象,需使用`@Entity`注解标识。 4. **映射实体**:创建...
3. 配置Hibernate:配置Hibernate的属性,如数据库连接信息、方言、缓存策略等,这通常在`hibernate.cfg.xml`文件中完成。 4. 使用注解:在实体类上使用Hibernate注解,定义数据库表映射和字段映射。 5. 声明事务...
3. **Hibernate配置**:在Hibernate的配置文件(如`hibernate.cfg.xml`)中,我们需要指定数据库连接信息、方言、缓存策略等。此外,还需要映射实体类到数据库表,这通常是通过`hibernate-mapping`元素完成的。 4. ...
3. **整合 Spring MVC 和 Hibernate** - 使用 Spring 的 `LocalSessionFactoryBean` 创建 SessionFactory 实例。 - 创建一个 DAO(数据访问对象)接口,实现数据操作的方法,如查询、添加、删除和更新。 - 使用 ...
5. **配置Hibernate**:在applicationContext.xml中配置Hibernate的SessionFactory,包括实体管理工厂、实体类扫描路径、数据库连接信息、方言等。还可以配置Hibernate的缓存策略。 6. **编写实体类**:根据数据库...
在这个“Spring与Hibernate整合实例(博客用户登录增删改查)”中,我们将探讨如何将这两个强大的框架——Spring和Hibernate——结合起来,以实现一个博客系统的用户管理功能,包括用户登录、注册、信息修改以及数据...
SessionFactory中 加入为了能得到同一个Session”表明本文要讨论的是在使用Spring框架整合Hibernate时,如何实现Spring管理Hibernate的SessionFactory对象,以便能够共享同一个数据库连接会话。 SSH框架指的是...
2. **配置 Hibernate**:设置 Hibernate 的配置文件(如 `hibernate.cfg.xml`),包括数据库连接信息、方言、实体类等。 3. **创建 SessionFactory**:通过 Spring 注入方式获取 SessionFactory 实例。 4. **事务...