最近重新学习了数据库事务管理 纠正了好多错误的观念,下面从数据库事务的概念,JDBC事务,Spring+Hibernate事务管理三部分来介绍。
一.数据库事务的概念
数据库事务是指作为单个逻辑工作单元执行的一系列操作。设想网上购物的一次交易,其付款过程至少包括以下几步数据库操作:
1更新客户所购商品的库存信息 2保存客户付款信息--可能包括与银行系统的交互
3生成订单并且保存到数据库中 4更新用户相关信息,例如购物数量等等
正常的情况下,这些操作将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果在这一系列过程中任何一个环节出了差错,例如在更新商品库存信息时发生异常、该顾客银行帐户存款不足等,都将导致交易失败。一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,比如最后一步更新用户信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态--库存信息没有被更新、用户也没有付款,订单也没有生成。否则,数据库的信息将会一片混乱而不可预测。
二.JDBC事务
这里要注意以下几个问题
1.使用con.setAutoCommit(false);更改JDBC事务的默认提交方式,默认为true,一旦关闭了自动提交,除非通过调用 commit() 显式地告诉它提交语句,否则无法提交 SQL 语句(即,数据库将不会被持久地更新)。在提交之前的任何时间,我们都可以调用 rollback() 回滚事务,并恢复最近的提交值(在尝试更新之前)。
2.使用try--catch--finally处理异常,并关闭连接
3.使用commit显式的提交事务,提交之后就无法更改。
使用上述方法可以在事务建立至事务提交之间添加多个SQL,即构成了事务。
public int delete(int sID) { dbc = new DataBaseConnection(); Connection con = dbc.getConnection(); try { con.setAutoCommit(false);// 更改JDBC事务的默认提交方式,默认为true, dbc.executeUpdate("delete from bylaw where ID=" + sID); dbc.executeUpdate("delete from bylaw _content where ID=" + sID); dbc.executeUpdate("delete from bylaw _affix where bylawid=" + sID); con.commit();//提交JDBC事务,commit之后就无法从数据库中抹去 con.setAutoCommit(true);// 恢复JDBC事务的默认提交方式 dbc.close(); return 1; } catch (Exception exc) { con.rollBack();//回滚JDBC事务 exc.printStackTrace(); dbc.close(); return -1; } }
三 spring hibernate整合事务
一个较为典型的Spring 托管hibernate的applicationContext的配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- property configure 获取property配置文件的路径 --> <context:property-placeholder location="classpath:conf/constant.properties, classpath:conf/db.properties,classpath:conf/ctx.properties" /> <!-- 自动搜索 base-package 的注解配置--> <context:component-scan base-package="com"/> <!-- DataSource 配置数据源--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="maxActive" value="200"></property> <property name="maxIdle" value="30"></property> <property name="maxWait" value="500"></property> <property name="defaultAutoCommit" value="true"></property> </bean> <!-- Hibernate Properties --> <bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="properties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="jdbc.fetch_size">50</prop> <prop key="jdbc.batch_size">25</prop> <prop key="hibernate.connection.autocommit">false</prop> <!-- <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <prop key="hibernate.cache.use_query_cache">true</prop> <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> --> <prop key="hibernate.query.substitutions">true 1,false 0</prop> </props> </property> </bean> <!-- SessionFactory 配置 SessionFactory 以便注入--> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="hibernateProperties"> <ref local="hibernateProperties"/> </property> <property name="mappingResources"> <list> <value>com/amazon/model/User.hbm.xml</value> </list> </property> <!-- <property name="mappingDirectoryLocations"> <list> <value>com/amazon/model/User.hbm.xml</value> </list> </property>--> </bean> <!-- Transaction Manager 事务管理--> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local="sessionFactory"/> </property> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- Base Transaction Proxy --> <!-- <bean id="baseTxProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true"> <property name="transactionManager"> <ref local="transactionManager"/> </property> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> </props> </property> </bean> --> <!-- views configuration 配置前端视图,可以完成多视图配置--> <bean id="freeMarkerConfigurer" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/" /> <property name="freemarkerSettings"> <props> <prop key="locale">zh_cn</prop> <prop key="template_update_delay">0</prop> <prop key="number_format">0.##########</prop> <prop key="defaultEncoding">UTF-8</prop> <prop key="auto_include"></prop> </props> </property> <property name="freemarkerVariables"> <map> <entry key="xml_escape" value-ref="fmXmlEscape" /> </map> </property> </bean> <bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape" /> <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="contentType"> <value>text/html; charset=UTF-8</value> </property> <property name="cache" value="true" /> <property name="prefix" value="/WEB-INF/tmpl/" /> <property name="suffix" value=".ftl" /> <property name="exposeSpringMacroHelpers" value="true"></property> <property name="exposeRequestAttributes" value="true"></property> </bean> </beans>
Dao的实现
import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import com.amazon.dao.UserDao; import com.amazon.model.User; @Repository("userDao") public class UserDaoImpl implements UserDao{ @Autowired private SessionFactory sessionFactory; public void getAll() { // TODO Auto-generated method stub } @Override public void getById(long id) { // TODO Auto-generated method stub } //添加Transactional 才能使用getCurrentSession @Transactional @Override public void insert(User user) { // TODO Auto-generated method stub Session session=sessionFactory.getCurrentSession();//openSession(); session.save(user); } public void setSesionFactory(SessionFactory sesionFactory) { this.sessionFactory = sesionFactory; } public SessionFactory getSesionFactory() { return sessionFactory; } }
测试文件
import javax.annotation.Resource; import org.hibernate.SessionFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import com.amazon.dao.UserDao; import com.amazon.model.User; @Component public class BaseTest { private static SessionFactory sessionFactory; @Resource public void setSessionFactory(SessionFactory sessionFactory) { BaseTest.sessionFactory = sessionFactory; } public SessionFactory getSessionFactory() { return sessionFactory; } public static void main(String args[]){ ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println(sessionFactory); UserDao userDao=(UserDao)ctx.getBean("userDao"); User user=new User(); user.setUserName("aaa"); user.setUserStatus("Admin"); userDao.insert(user); } }
在此要注意openSession和getCurrentSession的区别
1 openSession
a.永远创建session
b.需要关闭 session 2.getCurrentSession
a.如果当前上下文中存在可用的session会获取,如果不存在则会创建 b.不需要关闭session
getCurrentSession()
1.用途:界定事务边界 2.事务提交自动关闭
2.只有在抛出RuntimeException的时候才能回滚,其他的Exception不能够回滚
3.在事务的设置上,最好在Service上设置事务