Spring2.5+Hibernate3.3项目:让LocalSessionFactoryBean强制数据库连接只读
嘿,针对你的Spring 2.5 + Hibernate 3.3遗留项目要加只读报表库的需求,我给你整理了一套完全适配你技术栈的可行方案,一步步来:
实现步骤
1. 配置报表库的只读ComboPooledDataSource
首先给报表复制库单独配置一个数据源,核心是把连接全局设为只读——C3P0的ComboPooledDataSource可以通过setReadOnly(true)实现,这样从这个池里拿的所有连接默认都是只读状态,从根源上避免误写操作。
XML配置示例:
<bean id="reportDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- 报表库JDBC基础配置 --> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://report-db-host:3306/your_db?useUnicode=true&characterEncoding=utf8" /> <property name="user" value="report_user" /> <property name="password" value="report_pass" /> <!-- 核心:强制连接只读 --> <property name="readOnly" value="true" /> <!-- C3P0连接池参数按需调整 --> <property name="initialPoolSize" value="5" /> <property name="minPoolSize" value="5" /> <property name="maxPoolSize" value="20" /> <property name="acquireIncrement" value="5" /> <property name="maxIdleTime" value="1800" /> </bean>
2. 配置第二个LocalSessionFactoryBean
为报表库单独创建SessionFactory,关联上面的只读数据源,同时可以针对报表查询做些优化,比如关闭不必要的二级缓存、设置会话默认只读等。
XML配置示例:
<bean id="reportSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="reportDataSource" /> <!-- 只加载报表相关的实体映射,避免全量加载浪费资源 --> <property name="mappingResources"> <list> <value>com/yourcompany/model/SalesReport.hbm.xml</value> <value>com/yourcompany/model/UserStats.hbm.xml</value> </list> </property> <!-- Hibernate配置属性 --> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.show_sql">false</prop> <!-- 双重保障:设置Hibernate会话默认只读 --> <prop key="hibernate.connection.read_only">true</prop> <!-- 报表查询不需要缓存的话直接关闭,提升性能 --> <prop key="hibernate.cache.use_second_level_cache">false</prop> <prop key="hibernate.cache.use_query_cache">false</prop> </props> </property> </bean>
3. 配置报表库的专属事务管理器
因为是只读库,事务管理器要绑定报表SessionFactory,同时全局设置事务属性为只读,避免不必要的事务开销。
XML配置示例:
<!-- 报表库的Hibernate事务管理器 --> <bean id="reportTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="reportSessionFactory" /> </bean> <!-- 报表事务拦截器:所有报表方法默认只读事务 --> <bean id="reportTransactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="reportTransactionManager" /> <property name="transactionAttributes"> <props> <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <!-- 特殊方法可单独配置 --> </props> </property> </bean> <!-- 自动代理报表相关DAO,应用事务拦截器 --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*ReportDao</value> <!-- 匹配所有报表DAO的Bean名称 --> </list> </property> <property name="interceptorNames"> <list> <value>reportTransactionInterceptor</value> </list> </property> </bean>
4. 实现报表专用DAO
创建报表相关的DAO时,注入reportSessionFactory而非原主库的SessionFactory,确保操作的是只读库。
Java代码示例:
public class SalesReportDao extends HibernateDaoSupport { // 注入报表专属SessionFactory @Override @Autowired public void setSessionFactory(SessionFactory sessionFactory) { super.setSessionFactory(sessionFactory); } public List<SalesReport> queryMonthlySales(int year, int month) { String hql = "from SalesReport where year = :year and month = :month"; return getHibernateTemplate().findByNamedParam(hql, new String[]{"year", "month"}, new Object[]{year, month}); } }
对应的XML配置:
<bean id="salesReportDao" class="com.yourcompany.dao.SalesReportDao"> <property name="sessionFactory" ref="reportSessionFactory" /> </bean>
关键注意事项
- 双重只读保障:数据源层面+Hibernate会话层面+事务拦截器三重限制,最大程度杜绝误写操作。
- 映射隔离:报表SessionFactory只加载必要的实体映射,减少内存占用和初始化时间。
- 连接池隔离:两个数据源完全独立,避免主库和报表库的连接池互相影响,按需调整各自参数。
- 避免跨库事务:Spring2.5对跨库事务支持有限,尽量保证报表操作和主库操作完全隔离。
内容的提问来源于stack exchange,提问作者offbyone
相关产品推荐
相关产品推荐

