在使用spring进行数据库事务管理时,不管是采用JDBC还是hibernate都必须处理好如何获取链接的问题,不能过简单使用getSession(),和 ds.getConnect()来获取数据连接,否则链接一次请求访问就会耗用多个数据连接.
使用HibernateTransactionManager正确的方法是:
xml配置(省略,网上有很多相关的资料)
基础DAO
public class GenericDao {
protected SessionFactory sessionFactory;
//设置sessionFacotry
@Resource(name = "sessionFactory")
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
//获得session
protected Session getSession() {
//使用getCurrentSession(),确保在同一非嵌套型的事务中只消耗一个连接
//如果在此处直接调用getSession()那么在实际操作中,调用该方法多少次,便会消耗多少个连接
return sessionFactory.getCurrentSession();
}
......
}
使用Jdbc Spring事务处理的正确使用方法:
spring Bean 配置文件片段:
org.springframework.jdbc.datasource.DataSourceTransactionManager
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost/tech_data"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
基础的数据服务层片段:
public abstract class AbstractServiceBase {
private DataSource ds ;
//此处一定要注意,采用本地线程变量来保存当前线程使用的数据库连接;
//不要直接使用ds.getConnection()去获得connnection;
//否则一个请求会耗用多个连接,在高并发的情况下连接池有可能被耗尽
private static ThreadLocal<Connection> conLocal = new ThreadLocal<Connection>();
//设置数据源,beanName = dataSource
@Resource(name="dataSource")
public void setDataSource(DataSource ds){
this.ds = ds;
}
/**
* 获取一个默认的数据连接
* @return
* @throws SQLException
*/
public Connection getConnection(){
try{
//直接从本地线程中获取,避免重复的调用ds.getConnection();
Connection con = conLocal.get();
if(con != null)
return con;
// 本地线程不存在连接,则直接获取,并把该连接放到本地变量中
con = ds.getConnection();
conLocal.set(con);
return con;
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}
}
为什么会发生这样的事情?,那么这个跟spring的事务处理机制 有关系:
spring事务处理基本原理,主要是通过代理类来完成,以org.springframework.jdbc.datasource.DataSourceTransactionManager.class为例用伪代码进行说明:
1.采用线程本地变量存储要进行事务控制的Connection
class TransactionContext{
private static ThreadLocal(List<Connection>) cons = new ThreadLocal(new ArrayList<Connection>);
//增加一个连接
public void addConnection(conn){
cons.get().add(conn);
}
public void commint(){..}//关闭线程内的所有连接
public void rollBack(){ }回滚所有连接
}
2.生成DataSource的代理类
class DataSourceProxy implements DataSource{
private DataSource target; //被代理的实际的数据源 DataSource
//重写getConnection()方法
//每次调用getConnection()都会把该connection放进ThreadLocal中
Connection getConnection(){
Connect conn = target.getConnection();
TransactionContext.addConnection(conn);
return conn;
}
}
3.对具体的服务类访问使用代理类进行访问
calss ServiceProxy implemens service{
private service target;
private Object handleer(Invocation context){
try{
Method.invoke(target , params[]);
//反射调用具体的类方法,该方法会调用dataSourceProxy.getConnection()
TransactionContext.commit();//统一提交本地线程中的数据库连接
}catch(Exception ex){
TransactionContext.rollback();//统一进行回滚
}
}
}
所以,使用spring 事务管理,必须自己去处理数据连接的唯一性。
原创文章,转载请注明出处,个人原创文章:http://www.chlusoft.com/tech/349.jhtml
使用HibernateTransactionManager正确的方法是:
xml配置(省略,网上有很多相关的资料)
基础DAO
public class GenericDao {
protected SessionFactory sessionFactory;
//设置sessionFacotry
@Resource(name = "sessionFactory")
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
//获得session
protected Session getSession() {
//使用getCurrentSession(),确保在同一非嵌套型的事务中只消耗一个连接
//如果在此处直接调用getSession()那么在实际操作中,调用该方法多少次,便会消耗多少个连接
return sessionFactory.getCurrentSession();
}
......
}
使用Jdbc Spring事务处理的正确使用方法:
spring Bean 配置文件片段:
org.springframework.jdbc.datasource.DataSourceTransactionManager
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost/tech_data"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
基础的数据服务层片段:
public abstract class AbstractServiceBase {
private DataSource ds ;
//此处一定要注意,采用本地线程变量来保存当前线程使用的数据库连接;
//不要直接使用ds.getConnection()去获得connnection;
//否则一个请求会耗用多个连接,在高并发的情况下连接池有可能被耗尽
private static ThreadLocal<Connection> conLocal = new ThreadLocal<Connection>();
//设置数据源,beanName = dataSource
@Resource(name="dataSource")
public void setDataSource(DataSource ds){
this.ds = ds;
}
/**
* 获取一个默认的数据连接
* @return
* @throws SQLException
*/
public Connection getConnection(){
try{
//直接从本地线程中获取,避免重复的调用ds.getConnection();
Connection con = conLocal.get();
if(con != null)
return con;
// 本地线程不存在连接,则直接获取,并把该连接放到本地变量中
con = ds.getConnection();
conLocal.set(con);
return con;
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}
}
为什么会发生这样的事情?,那么这个跟spring的事务处理机制 有关系:
spring事务处理基本原理,主要是通过代理类来完成,以org.springframework.jdbc.datasource.DataSourceTransactionManager.class为例用伪代码进行说明:
1.采用线程本地变量存储要进行事务控制的Connection
class TransactionContext{
private static ThreadLocal(List<Connection>) cons = new ThreadLocal(new ArrayList<Connection>);
//增加一个连接
public void addConnection(conn){
cons.get().add(conn);
}
public void commint(){..}//关闭线程内的所有连接
public void rollBack(){ }回滚所有连接
}
2.生成DataSource的代理类
class DataSourceProxy implements DataSource{
private DataSource target; //被代理的实际的数据源 DataSource
//重写getConnection()方法
//每次调用getConnection()都会把该connection放进ThreadLocal中
Connection getConnection(){
Connect conn = target.getConnection();
TransactionContext.addConnection(conn);
return conn;
}
}
3.对具体的服务类访问使用代理类进行访问
calss ServiceProxy implemens service{
private service target;
private Object handleer(Invocation context){
try{
Method.invoke(target , params[]);
//反射调用具体的类方法,该方法会调用dataSourceProxy.getConnection()
TransactionContext.commit();//统一提交本地线程中的数据库连接
}catch(Exception ex){
TransactionContext.rollback();//统一进行回滚
}
}
}
所以,使用spring 事务管理,必须自己去处理数据连接的唯一性。
原创文章,转载请注明出处,个人原创文章:http://www.chlusoft.com/tech/349.jhtml