第五天
1 Spring与jdbc结合
jdbc编程不变,主要是Connection对象的维护,即配置并使用数据源
1) <!-- 基于jdk的规范数据源 -->
<bean name="dataSource1"
class="oracle.jdbc.pool.OracleConnectionPoolDataSource">
<property name="networkProtocol">
<value>tcp</value>
</property>
<property name="databaseName">
<value>XE</value>
</property>
<property name="driverType">
<value>thin</value>
</property>
<property name="portNumber">
<value>1521</value>
</property>
<property name="user">
<value>briup</value>
</property>
<property name="serverName">
<value>127.0.0.1</value>
</property>
<property name="password">
<value>briup</value>
</property>
</bean>
注意:别忘了读取配置文件
<!-- 读取这个资源文件 读完之后下面就可以用${key}来去文件中的value值了 -->
<!-- 这种方式是我们第一节学习的那种配置方式方式的简写 -->
<context:property-placeholder location="com/briup/db/jdbc/jdbc.properties"/>
2) <!-- dbcp数据源 -->
<bean id="dataSource2"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<!-- 最大连接数 -->
<property name="maxActive">
<value>80</value>
</property>
<!-- 最大空闲连接数 -->
<property name="maxIdle">
<value>20</value>
</property>
<!-- 最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间 单位:毫秒 -->
<!-- 超过时间则抛出异常,如果设置为-1表示无限等待 -->
<property name="maxWait">
<value>3000</value>
</property>
</bean>
3) <!-- spring提供的一种数据源 -->
<bean id="dataSource3"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
spring在jdbc的使用中还给我们提供了一个模板类:JdbcTemplate
用以简化我们的jdbc操作
例如:
//使用xml进行配置时候的java类中的写法:
public class JdbcTemplateDao implements AccountDao{
private JdbcTemplate jdbcTemplate;
//只要类中有set方法 在xml文件就可以进行配置和注入
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
...
}
//使用注解进行配置时候的java类中的写法:
@Repository
public class JdbcTemplateDao implements AccountDao {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
....
...
}
注意1:JdbcTemplate模板类如何使用:在htmlsingle中搜索即可,其中包含有大量的使用实例
注意2:spring结合jdbc的时候,不论是否使用这个模板,jdbc操作的事务默认是自动提交的(和之前学习jdbc的时候是一致的)
2 Spring与Hibernate结合
使用Spring整合Hibernate时我们【可以不需要】hibernate.cfg.xml文件。
重点在于配置sessionFactory
<!-- 配置sessionFactory -->
<bean class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
name="sessionFactory">
<!-- 注入数据源 -->
<property name="dataSource">
<ref local="dataSource"/>
</property>
<!-- 注入映射文件-->
<property name="mappingResources">
<list>
<value>com/briup/db/hibernate/account.hbm.xml</value>
</list>
</property>
<!-- 注入hibernate的属性信息-->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
或者:
<!-- 配置sessionFactory -->
<bean name="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
spring结合hibernate的配置xml例子:
<!-- 配置sessionFactory -->
<bean class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
name="sessionFactory">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<!-- 配置dao层对象 -->
<bean name="accountDao" class="com.briup.db.hibernate.Spring_Hibernate">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
注意:
如果你的hibernate中的关系映射用的是【xml】的方式,那么在spring中就要使用的上面LocalSessionFactoryBean类进行SessionFactory的配置
如果你的hibernate中的关系映射用的是【注解】的方式,那么在spring中就是使用AnnotationSessionFactoryBean类进行SessionFactory的配置
例如:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
</props>
</property>
<!-- 扫描哪些包下的实体类使用hibernate的注解 -->
<property name="packagesToScan">
<list>
<value>com.briup.core.bean</value>
<value>com.briup.base.bean</value>
<value>com.briup.util.bean</value>
</list>
</property>
</bean>
在spring中的除了上面这个地方之外,其他地方的配置俩者(hibernate的xml形式和注解形式)都是一样的
和jdbc类似的spirng也提供了hibernate的模板类的使用:HibernateTemplate
除此之外spring提供了一个Dao层的父类HibernateDaoSupport类,这个类封装了上面模板HibernateTemplate,但是不推荐使用,因为需要我们的dao层类去继承这个HibernateDaoSupport.(单继承的问题)
HibernateTemplate配置的例子:
配置文件:
<beans>
.....
<bean id="AccountDao" class="com.briup.db.hibernate.HibernateTemplateDao">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
dao层类: 这里只需要set方法即可依赖注入,不需要真的有对应的属性(attribute),spring要的是property
public class HibernateTemplateDao implements AccountDao{
private HibernateTemplate hibernateTemplate;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
....
}
HibernateTemplate模板类里面封装的方法大部分都算是比较方便(Hibernate本身其实已经很方便了),但是如果有些操作觉得用这个模板类不好写代码,那么可以再用回到Hibernate原来的方式
hibernateTemplate.execute(new HibernateCallback() {
//hibernate操作
public Object doInHibernate(Session session) {
//...注意之前应该讲过session也可以用回到jdbc的方式
session.doWork(new Work(){
public void execute(Connection conn) throws SQLException {
//jdbc原生操作
}
});
return null;
}
};
注意:
前提:没有配置spring管理事务的情况下:
如果我们在spring结合Hibernate的时候,如果使用HibernateTemplate模板的时候,执行代码的时候会发生sql语句(例如insert等),但是事务没有提交(数据库中看不到)
如果我们在spring结合Hibernate的时候,如果使用SessionFactory获得session后再进行操作的,那么Hibernate中的事务需要我们【手动】的开启和提交,并且我们是不能使用sessionFactory.getCurrentSession()来获得session,只能使用openSession方法来获得session。
另外:上面所说的事务的各种问题,我们之后要在spring专门进行统一管理的,之后在spring配置完事务之后上面那些问题都将得以解决.
3 Spring事务管理机制
1)编程式事务管理:所谓编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。
Spring框架提供一致的事务抽象,因此无论对于JDBC还是hibernate的事务都是采用相同的API进行编程。
spring中的编程式事务提供了TransactionTemplate模板类,这个模板类在创建(new)的时候需要传一个事务管理器作为参数。将来把这个事务模板类在service层中声明出来,那么就可以使用这个模板类提供的API把service层的操作包裹起来,达到我们需要的把事务加在serivce方法上的效果.
例如:
xml配置文件:
<beans>
....
...
<!-- 事务管理器 -->
<bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<!-- Service层对象 -->
<bean id="myProductService" class="product.ProductServiceImpl">
<property name="transactionManager" ref="myTxManager"/>
<property name="productDao" ref="myProductDao"/>
</bean>
</beans>
service层实现类:
public class ProductServiceImpl implements ProductService {
private TransactionTemplate transactionTemplate;
private ProductDao productDao;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public void serviceMethod1(final String name) {
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
//在事务包裹中完成业务逻辑代码
//且该业务最后【不需要】返回值
}
}
);
}
public void serviceMethod2(final String name) {
this.transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
//在事务包裹中完成业务逻辑代码
//且该业务最后【需要】返回值
return null;
}
});
}
}
注意:这种编程式事务,把事务的代码直接写到了service层(各种坏处多多,你应该能想到一些把),所以这种方式我们使用的并不多.
2)声明式事务管理(declarative transaction management)
Spring提供的对程序事务管理的方式之一,在Spring配置文件中声明式的处理事务来代替代码式的处理事务.
声明式事务管理的配置方式通常以下几种:
注意:配置事务配置的方式都需要用到事务管理器(切面)和事务拦截器(advice)
1使用ProxyFactoryBean给目标对象产生代理对象.
2使用TransactionProxyFactoryBean为目标对象生成事务代理的配置。这是一个专门给目标对象产生管理事务的代理对象的工厂类
3使用BeanNameAutoProxyCreator根据beanName给目标对象自动生成事务代理的代理对象,
4使用DefaultAdvisorAutoProxyCreator自动代理,和BeanNameAutoProxyCreator基本差不多,二者区别参照我们第三天上课的内容.
5使用tx前缀的专门事务标签和aop前缀的标签结合,将切面(事务管理)织入到切入点上
6使用注解进行事务配置
推荐使用第五种:tx标签结合aop标签
下面每种配置都是要一个类子来体现出来:例子都是基于Hibernate进行的事务配置,如果是jdbc在最后例子完了之后有说明
注意:配置的例子里面都用到【事务属性/事务传播行为】的这个配置,这个在接下来的知识点中介绍到.
Spring结合Hibernate,并且【配置了事务管理】后,我们如果在dao层要通过SessionFactory拿到session进行操作的时候,一定要注意【只能使用】getCurrentSession()的方式,因为使用openSession的话,那么这个新的session是spring所拿不到的,那么也就不能帮我管理这个session中的事务了.(这个和上一次提到情况刚好相反,上一次的情况是没有在spring中配置事务管理)
第一种方式:ProxyFactoryBean
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (相当于之前的切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务拦截器 (相当于之前的advice) -->
<bean name="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<!--
PROPAGATION_REQUIRED
事务七种属性中的其实一种。表示如果当前有事务
就拿要执行的操作放到这个事务里面,如果当前没有事务
那么就新建一个事务然后把这个操作放进去执行。
-Exception
表示如果操作期间抛出了Exception异常或者是它的子类型异常,
那么当前的事务就回滚,-号代表回滚,如果是+号,代表依然提交。
-->
<property name="transactionAttributes">
<props>
<!-- *代表所有的方法 -->
<prop key="*">
PROPAGATION_REQUIRED,-Exception
</prop>
</props>
</property>
</bean>
<!-- 配置代理对象 -->
<bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="service"></property>
<!-- 注入目标对象实现的接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.tran.service.AccountService</value>
</list>
</property>
<!-- 注入advice (也就是上面配置的事务拦截器) -->
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
第二种方式:TransactionProxyFactoryBean
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置一个专门产生代理事务的代理对象 (由一个工厂类产生) -->
<!-- 这个工厂类中结合了事务拦截器,所以我们就不需要配事务拦截器 -->
<bean name="proxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="service"></property>
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">
PROPAGATION_REQUIRED,-Exception
</prop>
</props>
</property>
</bean>
第三种方式:BeanNameAutoProxyCreator
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务拦截器 (advice) -->
<bean name="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<!--
PROPAGATION_REQUIRED
事务七种属性中的其实一种。表示如果当前有事务
就拿要执行的操作放到这个事务里面,如果当前没有事务
那么就新建一个事务然后把这个操作放进去执行。
-Exception
表示如果操作期间抛出了Exception异常或者是它的子类型异常,
那么当前的事务就回滚,-号代表回滚,如果是+号,代表依然提交。
-->
<property name="transactionAttributes">
<props>
<!-- *代表所有的方法 -->
<prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
<!-- 配置代理对象 自动代理 byName的方式-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>service</value>
</list>
</property>
<property name="interceptorNames">
<value>transactionInterceptor</value>
</property>
</bean>
第四种方式:DefaultAdvisorAutoProxyCreator
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务拦截器 (advice) -->
<bean name="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
<!-- 配置advisor 增强器-->
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice 表示增强器要在哪一个advice起作用-->
<property name="advice" ref="transactionInterceptor"></property>
<!-- 注入需要被拦截的目标对象中的方法(连接点) -->
<property name="patterns">
<list>
<value>.*save</value>
<value>.*update</value>
<value>.*remove</value>
</list>
</property>
</bean>
<!-- 自动代理-->
<!-- 必须要有advisor-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
第五种方式:tx标签结合aop标签
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务拦截器 -->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<!-- *代表所有的方法 -->
<tx:method name="*" propagation="REQUIRED" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(public * com.briup.tran.service.*.*(..))" id="myPointCut"/>
<!-- 配置事务拦截器在哪一个切入点上起作用 -->
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="myPointCut"/>
</aop:config>
第六种方式:注解方式
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 通知spring我们在目标对象中做了事务的注解,并指明使用哪一个事务管理器 -->
<!-- 加入上这个标签后 去目标对象去加入相应的注解就可以了 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
对应的AccountServiceImpl类中:
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Throwable.class)
public class AccountServiceImpl implements AccountService{
....
....
....
}
注意:这些配置的service事务的例子是在使用的Hibernate的基础上,如果使用的时候jdbc,那么只要把Hibernate的事务管理器HibernateTransactionManager换成jdbc的事务管理器DataSourceTransactionManager就可以了(其他配置都不变)
dao层使用的是jdbc,spring在service层配置事务管理,这里面有几个需要注意的地方:
第一个地方:在xml中使用的事务管理器是jdbc专用的和Hibernate的不一样,其他地方配置都和上面使用Hibernate配置的情况都一样
xml配置文件:
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置jdbc的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务拦截器 (advice) -->
<bean name="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<!-- *代表所有的方法 -->
<prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
<!-- 配置代理对象 -->
<bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="service"></property>
<!-- 注入目标对象实现的接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.tran.service.AccountService</value>
</list>
</property>
<!-- 注入advice (也就是上面配置的事务拦截器) -->
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
第二个地方:1.在dao层注意通过dataSource拿conn的方式 2.不要关闭conn
public class JDBCAccountDaoImpl implements AccountDao{
private DataSource dataSource;
@Override
public void save(Account account) throws DataAccessException {
try {
/*
一定不要这个拿 因为我们要保证service开始事务和提交事务用的conn和dao层用到的conn是同一个对象
Connection conn = dataSource.getConnection();
*/
/*
一定要这样去拿Connection对象
Connection conn = DataSourceUtils.getConnection(dataSource);
*/
Connection conn = DataSourceUtils.getConnection(dataSource);
Statement stmt = conn.createStatement();
int id = account.getId();
String name = account.getName();
double balance = account.getBalance();
String sql = "insert into account(id,name,balance) values("+id+",'"+name+"',"+balance+")";
stmt.execute(sql);
stmt.close();
//不要关闭conn 因为这里一关闭conn,在service中就拿不到这个对象了
} catch (SQLException e) {
e.printStackTrace();
}
}
//其他方法
..
..
}
注意:
事务属性包括:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
----------------------------------------------
了解内容:
<prop key="myMethod">PROPAGATION_REQUIRED,readOnly,-Exception</prop>
这样的配置,其中:
-Exception表示有Exception抛出时,事务回滚. -代表回滚+就代表提交
readonly 就是read only, 设置操作权限为只读,一般用于查询的方法,优化作用.
注意:指定事务属性的取值有较复杂的规则。具体的书写规则如下:
传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]
1)事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。
2)事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
3)事务的只读属性
事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读
4)事务超时
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
5)事务的回滚规则
通常情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。但是,我们可以根据需要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。
-Exception表示有Exception抛出时,事务回滚. -代表回滚+就代表提交
1 Spring与jdbc结合
jdbc编程不变,主要是Connection对象的维护,即配置并使用数据源
1) <!-- 基于jdk的规范数据源 -->
<bean name="dataSource1"
class="oracle.jdbc.pool.OracleConnectionPoolDataSource">
<property name="networkProtocol">
<value>tcp</value>
</property>
<property name="databaseName">
<value>XE</value>
</property>
<property name="driverType">
<value>thin</value>
</property>
<property name="portNumber">
<value>1521</value>
</property>
<property name="user">
<value>briup</value>
</property>
<property name="serverName">
<value>127.0.0.1</value>
</property>
<property name="password">
<value>briup</value>
</property>
</bean>
注意:别忘了读取配置文件
<!-- 读取这个资源文件 读完之后下面就可以用${key}来去文件中的value值了 -->
<!-- 这种方式是我们第一节学习的那种配置方式方式的简写 -->
<context:property-placeholder location="com/briup/db/jdbc/jdbc.properties"/>
2) <!-- dbcp数据源 -->
<bean id="dataSource2"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<!-- 最大连接数 -->
<property name="maxActive">
<value>80</value>
</property>
<!-- 最大空闲连接数 -->
<property name="maxIdle">
<value>20</value>
</property>
<!-- 最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间 单位:毫秒 -->
<!-- 超过时间则抛出异常,如果设置为-1表示无限等待 -->
<property name="maxWait">
<value>3000</value>
</property>
</bean>
3) <!-- spring提供的一种数据源 -->
<bean id="dataSource3"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
spring在jdbc的使用中还给我们提供了一个模板类:JdbcTemplate
用以简化我们的jdbc操作
例如:
//使用xml进行配置时候的java类中的写法:
public class JdbcTemplateDao implements AccountDao{
private JdbcTemplate jdbcTemplate;
//只要类中有set方法 在xml文件就可以进行配置和注入
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
...
}
//使用注解进行配置时候的java类中的写法:
@Repository
public class JdbcTemplateDao implements AccountDao {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
....
...
}
注意1:JdbcTemplate模板类如何使用:在htmlsingle中搜索即可,其中包含有大量的使用实例
注意2:spring结合jdbc的时候,不论是否使用这个模板,jdbc操作的事务默认是自动提交的(和之前学习jdbc的时候是一致的)
2 Spring与Hibernate结合
使用Spring整合Hibernate时我们【可以不需要】hibernate.cfg.xml文件。
重点在于配置sessionFactory
<!-- 配置sessionFactory -->
<bean class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
name="sessionFactory">
<!-- 注入数据源 -->
<property name="dataSource">
<ref local="dataSource"/>
</property>
<!-- 注入映射文件-->
<property name="mappingResources">
<list>
<value>com/briup/db/hibernate/account.hbm.xml</value>
</list>
</property>
<!-- 注入hibernate的属性信息-->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
或者:
<!-- 配置sessionFactory -->
<bean name="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
spring结合hibernate的配置xml例子:
<!-- 配置sessionFactory -->
<bean class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
name="sessionFactory">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<!-- 配置dao层对象 -->
<bean name="accountDao" class="com.briup.db.hibernate.Spring_Hibernate">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
注意:
如果你的hibernate中的关系映射用的是【xml】的方式,那么在spring中就要使用的上面LocalSessionFactoryBean类进行SessionFactory的配置
如果你的hibernate中的关系映射用的是【注解】的方式,那么在spring中就是使用AnnotationSessionFactoryBean类进行SessionFactory的配置
例如:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
</props>
</property>
<!-- 扫描哪些包下的实体类使用hibernate的注解 -->
<property name="packagesToScan">
<list>
<value>com.briup.core.bean</value>
<value>com.briup.base.bean</value>
<value>com.briup.util.bean</value>
</list>
</property>
</bean>
在spring中的除了上面这个地方之外,其他地方的配置俩者(hibernate的xml形式和注解形式)都是一样的
和jdbc类似的spirng也提供了hibernate的模板类的使用:HibernateTemplate
除此之外spring提供了一个Dao层的父类HibernateDaoSupport类,这个类封装了上面模板HibernateTemplate,但是不推荐使用,因为需要我们的dao层类去继承这个HibernateDaoSupport.(单继承的问题)
HibernateTemplate配置的例子:
配置文件:
<beans>
.....
<bean id="AccountDao" class="com.briup.db.hibernate.HibernateTemplateDao">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
dao层类: 这里只需要set方法即可依赖注入,不需要真的有对应的属性(attribute),spring要的是property
public class HibernateTemplateDao implements AccountDao{
private HibernateTemplate hibernateTemplate;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
....
}
HibernateTemplate模板类里面封装的方法大部分都算是比较方便(Hibernate本身其实已经很方便了),但是如果有些操作觉得用这个模板类不好写代码,那么可以再用回到Hibernate原来的方式
hibernateTemplate.execute(new HibernateCallback() {
//hibernate操作
public Object doInHibernate(Session session) {
//...注意之前应该讲过session也可以用回到jdbc的方式
session.doWork(new Work(){
public void execute(Connection conn) throws SQLException {
//jdbc原生操作
}
});
return null;
}
};
注意:
前提:没有配置spring管理事务的情况下:
如果我们在spring结合Hibernate的时候,如果使用HibernateTemplate模板的时候,执行代码的时候会发生sql语句(例如insert等),但是事务没有提交(数据库中看不到)
如果我们在spring结合Hibernate的时候,如果使用SessionFactory获得session后再进行操作的,那么Hibernate中的事务需要我们【手动】的开启和提交,并且我们是不能使用sessionFactory.getCurrentSession()来获得session,只能使用openSession方法来获得session。
另外:上面所说的事务的各种问题,我们之后要在spring专门进行统一管理的,之后在spring配置完事务之后上面那些问题都将得以解决.
3 Spring事务管理机制
1)编程式事务管理:所谓编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。
Spring框架提供一致的事务抽象,因此无论对于JDBC还是hibernate的事务都是采用相同的API进行编程。
spring中的编程式事务提供了TransactionTemplate模板类,这个模板类在创建(new)的时候需要传一个事务管理器作为参数。将来把这个事务模板类在service层中声明出来,那么就可以使用这个模板类提供的API把service层的操作包裹起来,达到我们需要的把事务加在serivce方法上的效果.
例如:
xml配置文件:
<beans>
....
...
<!-- 事务管理器 -->
<bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<!-- Service层对象 -->
<bean id="myProductService" class="product.ProductServiceImpl">
<property name="transactionManager" ref="myTxManager"/>
<property name="productDao" ref="myProductDao"/>
</bean>
</beans>
service层实现类:
public class ProductServiceImpl implements ProductService {
private TransactionTemplate transactionTemplate;
private ProductDao productDao;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public void serviceMethod1(final String name) {
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
//在事务包裹中完成业务逻辑代码
//且该业务最后【不需要】返回值
}
}
);
}
public void serviceMethod2(final String name) {
this.transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
//在事务包裹中完成业务逻辑代码
//且该业务最后【需要】返回值
return null;
}
});
}
}
注意:这种编程式事务,把事务的代码直接写到了service层(各种坏处多多,你应该能想到一些把),所以这种方式我们使用的并不多.
2)声明式事务管理(declarative transaction management)
Spring提供的对程序事务管理的方式之一,在Spring配置文件中声明式的处理事务来代替代码式的处理事务.
声明式事务管理的配置方式通常以下几种:
注意:配置事务配置的方式都需要用到事务管理器(切面)和事务拦截器(advice)
1使用ProxyFactoryBean给目标对象产生代理对象.
2使用TransactionProxyFactoryBean为目标对象生成事务代理的配置。这是一个专门给目标对象产生管理事务的代理对象的工厂类
3使用BeanNameAutoProxyCreator根据beanName给目标对象自动生成事务代理的代理对象,
4使用DefaultAdvisorAutoProxyCreator自动代理,和BeanNameAutoProxyCreator基本差不多,二者区别参照我们第三天上课的内容.
5使用tx前缀的专门事务标签和aop前缀的标签结合,将切面(事务管理)织入到切入点上
6使用注解进行事务配置
推荐使用第五种:tx标签结合aop标签
下面每种配置都是要一个类子来体现出来:例子都是基于Hibernate进行的事务配置,如果是jdbc在最后例子完了之后有说明
注意:配置的例子里面都用到【事务属性/事务传播行为】的这个配置,这个在接下来的知识点中介绍到.
Spring结合Hibernate,并且【配置了事务管理】后,我们如果在dao层要通过SessionFactory拿到session进行操作的时候,一定要注意【只能使用】getCurrentSession()的方式,因为使用openSession的话,那么这个新的session是spring所拿不到的,那么也就不能帮我管理这个session中的事务了.(这个和上一次提到情况刚好相反,上一次的情况是没有在spring中配置事务管理)
第一种方式:ProxyFactoryBean
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (相当于之前的切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务拦截器 (相当于之前的advice) -->
<bean name="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<!--
PROPAGATION_REQUIRED
事务七种属性中的其实一种。表示如果当前有事务
就拿要执行的操作放到这个事务里面,如果当前没有事务
那么就新建一个事务然后把这个操作放进去执行。
-Exception
表示如果操作期间抛出了Exception异常或者是它的子类型异常,
那么当前的事务就回滚,-号代表回滚,如果是+号,代表依然提交。
-->
<property name="transactionAttributes">
<props>
<!-- *代表所有的方法 -->
<prop key="*">
PROPAGATION_REQUIRED,-Exception
</prop>
</props>
</property>
</bean>
<!-- 配置代理对象 -->
<bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="service"></property>
<!-- 注入目标对象实现的接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.tran.service.AccountService</value>
</list>
</property>
<!-- 注入advice (也就是上面配置的事务拦截器) -->
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
第二种方式:TransactionProxyFactoryBean
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置一个专门产生代理事务的代理对象 (由一个工厂类产生) -->
<!-- 这个工厂类中结合了事务拦截器,所以我们就不需要配事务拦截器 -->
<bean name="proxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="service"></property>
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">
PROPAGATION_REQUIRED,-Exception
</prop>
</props>
</property>
</bean>
第三种方式:BeanNameAutoProxyCreator
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务拦截器 (advice) -->
<bean name="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<!--
PROPAGATION_REQUIRED
事务七种属性中的其实一种。表示如果当前有事务
就拿要执行的操作放到这个事务里面,如果当前没有事务
那么就新建一个事务然后把这个操作放进去执行。
-Exception
表示如果操作期间抛出了Exception异常或者是它的子类型异常,
那么当前的事务就回滚,-号代表回滚,如果是+号,代表依然提交。
-->
<property name="transactionAttributes">
<props>
<!-- *代表所有的方法 -->
<prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
<!-- 配置代理对象 自动代理 byName的方式-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>service</value>
</list>
</property>
<property name="interceptorNames">
<value>transactionInterceptor</value>
</property>
</bean>
第四种方式:DefaultAdvisorAutoProxyCreator
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务拦截器 (advice) -->
<bean name="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
<!-- 配置advisor 增强器-->
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice 表示增强器要在哪一个advice起作用-->
<property name="advice" ref="transactionInterceptor"></property>
<!-- 注入需要被拦截的目标对象中的方法(连接点) -->
<property name="patterns">
<list>
<value>.*save</value>
<value>.*update</value>
<value>.*remove</value>
</list>
</property>
</bean>
<!-- 自动代理-->
<!-- 必须要有advisor-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
第五种方式:tx标签结合aop标签
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务拦截器 -->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<!-- *代表所有的方法 -->
<tx:method name="*" propagation="REQUIRED" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(public * com.briup.tran.service.*.*(..))" id="myPointCut"/>
<!-- 配置事务拦截器在哪一个切入点上起作用 -->
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="myPointCut"/>
</aop:config>
第六种方式:注解方式
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置hibernate的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- 注入sessionFactory对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 通知spring我们在目标对象中做了事务的注解,并指明使用哪一个事务管理器 -->
<!-- 加入上这个标签后 去目标对象去加入相应的注解就可以了 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
对应的AccountServiceImpl类中:
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Throwable.class)
public class AccountServiceImpl implements AccountService{
....
....
....
}
注意:这些配置的service事务的例子是在使用的Hibernate的基础上,如果使用的时候jdbc,那么只要把Hibernate的事务管理器HibernateTransactionManager换成jdbc的事务管理器DataSourceTransactionManager就可以了(其他配置都不变)
dao层使用的是jdbc,spring在service层配置事务管理,这里面有几个需要注意的地方:
第一个地方:在xml中使用的事务管理器是jdbc专用的和Hibernate的不一样,其他地方配置都和上面使用Hibernate配置的情况都一样
xml配置文件:
<!-- 配置service层对象 目标对象-->
<bean name="service" class="com.briup.tran.service.AccountServiceImpl">
<!-- 注入dao层对象 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置jdbc的事务管理器 (切面类)-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务拦截器 (advice) -->
<bean name="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<!-- *代表所有的方法 -->
<prop key="*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
<!-- 配置代理对象 -->
<bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目标对象 -->
<property name="target" ref="service"></property>
<!-- 注入目标对象实现的接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.tran.service.AccountService</value>
</list>
</property>
<!-- 注入advice (也就是上面配置的事务拦截器) -->
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
第二个地方:1.在dao层注意通过dataSource拿conn的方式 2.不要关闭conn
public class JDBCAccountDaoImpl implements AccountDao{
private DataSource dataSource;
@Override
public void save(Account account) throws DataAccessException {
try {
/*
一定不要这个拿 因为我们要保证service开始事务和提交事务用的conn和dao层用到的conn是同一个对象
Connection conn = dataSource.getConnection();
*/
/*
一定要这样去拿Connection对象
Connection conn = DataSourceUtils.getConnection(dataSource);
*/
Connection conn = DataSourceUtils.getConnection(dataSource);
Statement stmt = conn.createStatement();
int id = account.getId();
String name = account.getName();
double balance = account.getBalance();
String sql = "insert into account(id,name,balance) values("+id+",'"+name+"',"+balance+")";
stmt.execute(sql);
stmt.close();
//不要关闭conn 因为这里一关闭conn,在service中就拿不到这个对象了
} catch (SQLException e) {
e.printStackTrace();
}
}
//其他方法
..
..
}
注意:
事务属性包括:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
----------------------------------------------
了解内容:
<prop key="myMethod">PROPAGATION_REQUIRED,readOnly,-Exception</prop>
这样的配置,其中:
-Exception表示有Exception抛出时,事务回滚. -代表回滚+就代表提交
readonly 就是read only, 设置操作权限为只读,一般用于查询的方法,优化作用.
注意:指定事务属性的取值有较复杂的规则。具体的书写规则如下:
传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]
1)事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。
2)事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
3)事务的只读属性
事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读
4)事务超时
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
5)事务的回滚规则
通常情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。但是,我们可以根据需要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。
-Exception表示有Exception抛出时,事务回滚. -代表回滚+就代表提交