1、回顾事务
2、spring事务管理
3、事务案例--转账
4、手动管理事务(了解)
5、工厂bean 生成代理:半自动(了解)
6、AOP配置事务--基于xml【掌握】
7、AOP配置事务--基于注解【掌握】
8、整合web--在web.xml中配置spring容器
9、从Servlet中获取spring容器
10、SSH整合--jar包
11、spring整合hibernate:没有hibernate.cfg.xml 【重点】
12、spring整合struts:struts创建action 【重点】
1、回顾事务 <--返回目录
* 事务:一组业务操作ABCD,要么全部成功,要么全部不成功。
* 特性:ACID
- 原子性:整体
- 一致性:完成
- 隔离性:并发
- 持久性:结果
* 隔离问题:
- 脏读:一个事务读到另一个事务没有提交的数据
- 不可重复读:一个事务读到另一个事务已提交的数据(update)
- 虚读(幻读):一个事务读到另一个事务已提交的数据(insert)
* 隔离级别:
- read uncommitted:读未提交。存在3个问题
- read committed:读已提交。解决脏读,存在2个问题
- repeatable read:可重复读。解决:脏读、不可重复读,存在1个问题。
- serializable :串行化。都解决,单事务。
* mysql 事务操作
ABCD 一个事务 Connection conn = null; try{ //1 获得连接 conn = ...; //2 开启事务 conn.setAutoCommit(false); A B C D //3 提交事务 conn.commit(); } catche(){ //4 回滚事务 conn.rollback(); }
* mysql 事务操作--Savepoint
需求:AB(必须),CD(可选) Connection conn = null; Savepoint savepoint = null; //保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分) try{ //1 获得连接 conn = ...; //2 开启事务 conn.setAutoCommit(false); A B savepoint = conn.setSavepoint(); C D //3 提交事务 conn.commit(); } catche(){ if(savepoint != null){ //CD异常 // 回滚到CD之前 conn.rollback(savepoint); // 提交AB conn.commit(); } else{ //AB异常 // 回滚AB conn.rollback(); } }
2、spring事务管理 <--返回目录
* 涉及事务,首先需要jar包:spring-tx-3.2.0.RELEASE.jar
* 三个顶级接口
- PlatformTransactionManager 平台事务管理器,spring要管理事务,必须使用事务管理器
进行事务配置时,必须配置事务管理器。
- TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情,
例如:隔离级别、是否只读、超时时间 等
进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
- TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。
spring底层根据状态进行相应操作。
* 以后,我们需要做的:配置事务管理器,配置事务详情,事务状态我们不管
* 常见的事务管理器
- DataSourceTransactionManager,jdbc开发时事务管理器,采用JdbcTemplate
** 需要导spring-jdbc-3.2.0.RELEASE.jar
- HibernateTransactionManager,hibernate开发时事务管理器,整合hibernate
** 需要导spring-orm-3.2.0.RELEASE.jar
* 事务管理器的api详解
TransactionStatus getTransaction(TransactionDefinition definition):事务管理器 通过"事务详情",
获得"事务状态",从而管理事务。
void commit(TransactionStatus status) 根据状态提交
void rollback(TransactionStatus status) 根据状态回滚
* 事务状态TransactionStatus的方法
isNewTranction():是否是新的事务
hasSavepoint():是否有保存点
setRollbackOnly():设置回滚
isRollbackOnly():是否回滚
flush():刷新
isCompleted():是否完成
* 事务详情TransactionDefinition
- getName()方法:配置事务详情名称,一般取方法名称,例如save、add*等
- isReadOnly():是否只读,一般增删改:读写,查询:只读
- getTimeout():获得超时时间
- getIsolationLevel():隔离级别
- getPropagationBehavior():传播行为
- TIMEOUT_DEFAULT:默认超时时间,默认值-1,使用数据库底层的超时时间
- ISOLATION_DEFAULT:隔离级别 0 1 2 4 8
- ISOLATION_READ_UNCOMMITTED
- ISOLATION_READ_COMMITTED
- ISOLATION_REPEATABLE_READ
- ISOLATION_SERIALIZABLE
- 传播行为:在两个业务之间如何共享事务 A调用B,设置B的传播行为
** PROPAGATION_REQUIRED【默认值】 required 必须:支持当前事务,A有事务,B将使用该事务;
如果A没有事务,B将创建一个新的事务
** PROPAGATION_SUPPORTS supports 支持:支持当前事务,A有事务,B将使用该事务;
如果A没有事务,B将以非事务执行
** PROPAGATION_MANDATORY mandatory 强制:支持当前事务,A有事务,B将使用该事务;
如果A没有事务,B将抛异常
** PROPAGATION_REQUIRES_NEW【以后常用】 必须新的:如果A有事务,将A的事务挂起,
B创建一个新的事务;如果A没有事务,B创建新的事务
** PROPAGATION_NOT_SUPPORTED 不支持:如果A有事务,将A的事务挂起,B将以非事务执行;
如果A没有事务,B将以非事务执行
** PROPAGATION_NEVER 不支持事务,也不支持当前事务:如果A有事务,B将抛异常;
如果A没有事务,B以非事务执行
** PROPAGATION_NESTED【也要掌握】 嵌套:A和B底层采用保存点机制,执行嵌套事务
3、事务案例--转账 <--返回目录
创建表
create database db_test1 default character set utf8; use db_test1; create table t_account( id int primary key auto_increment, username varchar(50), money int )engine innodb; insert into t_account(username,money) values('jack','1000'); insert into t_account(username,money) values('rose','1000');
导入jar包
** 核心:4+1 ** aop :4 (aop联盟、spring aop、aspectjweaver规范、spring aspect) ** jdbc和事务相关:2(jdbc、tx) ** 驱动:mysql ** 连接池:c3p0
dao层:接口和实现类
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void out(String outer, Integer money) { this.getJdbcTemplate().update("update account set money = money - ? where username = ?", money,outer); } @Override public void in(String inner, Integer money) { this.getJdbcTemplate().update("update account set money = money + ? where username = ?", money,inner); } }
service层:接口和实现类
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String outer, String inner, Integer money) { accountDao.out(outer, money); //outer减钱 //断电,这样就会导致:outer减钱,但是inter不加钱 //int i = 1/0; accountDao.in(inner, money); //inter加钱 } }
spring配置
<!-- 1 数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ee19_spring_day03"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property> </bean> <!-- 2 dao 依赖JdbcTemplate,继承JdbcDaoSupport,需要注入数据源 --> <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 3 service 依赖dao层,需要注入accountDao--> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean>
测试
@Test public void demo01(){ String xmlPath = "applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); AccountService accountService = (AccountService) applicationContext.getBean ("accountService"); accountService.transfer("jack", "rose", 100); }
4、手动管理事务(了解) <--返回目录
* spring底层使用TransactionTemplate事务模板进行操作
* 操作
- service需要获得TransactionTemplate
- 所以,在spring配置TransactionTemplate,并注入到service
- 模板TransactionTemplate需要注入事务管理器
- 所以,需要配置事务管理器DataSourceTransactionManager
- 事务管理器需要注入DataSource
1) 配置数据源
2) 配置事务管理器(依赖数据源)
3) 配置模板TransactionTemplate(依赖事务管理器)
4) 配置service(依赖模板TransactionTemplate)
5、工厂bean 生成代理:半自动(了解) <--返回目录
spring提供了管理事务的代理工厂bean TransactionProxyFactoryBean
1) spring 配置一个代理<bean id="" class="TransactionProxyFactoryBean"/>
2) getBean(TransactionProxyFactoryBean的id值)获得代理对象
spring配置文件:
<!-- 4 service 代理对象 4.1 proxyInterfaces 接口 4.2 target 目标类 4.3 transactionManager 事务管理器 4.4 transactionAttributes 事务属性(事务详情) prop.key :确定哪些方法使用当前事务配置 prop.text:用于配置事务详情 格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception 传播行为 隔离级别 是否只读 异常回滚 异常提交 注意:如果这里配置readOnly只读,若执行增删改,会报错 +Exception:表示有异常,仍提交。可能导致,事务只执行一部分业务 例如: <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默认传播行为, 和隔离级别 <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> 只读 <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang. ArithmeticException</prop> 有异常仍提交 --> <bean id="proxyAccountService" class="org.springframework.transaction.interceptor. TransactionProxyFactoryBean"> <property name="proxyInterfaces" value="com.itheima.service.AccountService"></property>指定接口 <property name="target" ref="accountService"></property> 【指定一个目标类】 <property name="transactionManager" ref="txManager"></property> 指定事务管理器 <property name="transactionAttributes"> 指定事务详情 <props> <!-- 指定目标类的transfer()方法使用当前事务配置 --> <prop key="【目标类方法】transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> </props> </property> </bean> <!-- 5 事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
6、AOP配置事务--基于xml【掌握】 <--返回目录
在spring xml 配置aop 自动生成代理,进行事务的管理,三步
1)配置管理器
2)配置事务详情
3)配置aop
<!-- 4 事务管理 --> <!-- 4.1 事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 4.2 事务详情(事务通知) , 在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等 <tx:attributes> 用于配置事务详情(属性属性) <tx:method name=""/> 详情具体配置 propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的 isolation 隔离级别 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/> </tx:attributes> </tx:advice> <!-- 4.3 AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接点,从而获得切入点:ABC --> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/> </aop:config>
7、AOP配置事务--基于注解【掌握】 <--返回目录
步骤:
1)配置事务管理器,将并事务管理器交予spring
2)在目标类或目标方法添加注解即可
** 在目标类上配,则作用整个类;在目标方法上配,则作用于该方法
** @Transactional
** @Transactional(propagation=Propagation.REQUIRED , isolation = Isolation.DEFAULT)
在applicationContext.xml中配置
<!-- 4 事务管理 --> <!-- 4.1 事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 4.2 将管理器交予spring * transaction-manager 配置事务管理器 * proxy-target-class true:底层强制使用cglib 代理 --> <tx:annotation-driven transaction-manager="txManager"/>
8、整合web--在web.xml中配置spring容器 <--返回目录
* 以前测试都是new出spring容器,例如:
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
accountService.transfer("jack", "rose", 1000);
* 但是spring容器创建一次就够了
* 导入jar包 spring-web
* 所以,为了让spring配置文件在服务器第一次启动就加载,在web.xml中配置
<!-- 确定spring配置文件位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 配置spring监听器,加载xml配置文件 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
9、从Servlet中获取spring容器 <--返回目录
* 在Servlet中获取spring容器,从而可以使用
- Spring把WebApplicationContext(XmlWebApplicationContext是默认实现类)放在了ServletContext中,
ServletContext也是一个“容器”,也是一个类似Map的结构,而WebApplicationContext在ServletContext中
的KEY就是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
我们如果要使用WebApplicationContext则需要从ServletContext取出,Spring提供了一个
WebApplicationContextUtils类,可以方便的取出WebApplicationContext,
只要把ServletContext传入就可以了。
---------------------
方法一:
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
ac.getBean("beanId");
说明:这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring的情况。
方法二:通过Spring提供的工具类获取ApplicationContext对象
ApplicationContext ac1 = WebApplicationContextUtils
.getRequiredWebApplicationContext(ServletContext sc);
ApplicationContext ac2 = WebApplicationContextUtils
.getWebApplicationContext(ServletContext sc);
ac1.getBean("beanId");
ac2.getBean("beanId");
说明:这种方式适合于采用Spring框架的B/S系统,通过ServletContext对象获取ApplicationContext对象,
然后在通过它获取需要的类实例。
上面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。
其中 servletContext sc 可以具体 换成 servlet.getServletContext()或者 this.getServletContext()
或者 request.getSession().getServletContext(); 另外,由于spring是注入的对象放在
ServletContext中的,所以可以直接在ServletContext取出 WebApplicationContext 对象:
WebApplicationContext webApplicationContext = (WebApplicationContext)servletContext
.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
---------------------
【总结:通过ServletContext对象获取Spring容器】
//方式1
ApplicationContext applicationContext=(ApplicationContext)this.getServletContext()
.getAtt·ribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//方式2
ApplicationContext applicationContext=WebApplicationContextUtils
.getWebApplicationContext(this.getServletContext());
//然后在Servlet中可以使用spring容器--applicationContext了
10、SSH整合--jar包 <--返回目录
最好新建一个文件夹,然后往里面添加jar包,然后添加到项目里
struts2:2.3.15.3
hibernate : 3.6.10
spring: 3.2.0
* jar包整合
- struts2:共13个
- spring
** 核心 4+1:beans、core、context、expression,commons-logging (struts已经导入)
** aop相关 4个:aop联盟、spring-aop 、aspect规范、spring-aspect
** 数据库db 2个:spring-jdbc、spring-tx
** 整合jUnit:spring-test
** 整合web:spring-web
** 整合hibernate:spring-orm
** 驱动:mysql
** 连接池:c3p0
共4+4+2+5=15
- hibernate
** 核心 1个:hibernate3.jar
** 必须:6个
** jpa规范 1个
** 整合log4j
导入 log4j...jar (struts已经导入)
整合(过渡):slf4j-log4j12-1.7.5.jar
** 二级缓存
核心:ehcache-1.5.0.jar
依赖:backport-util-concurrent-2.1.jar和commons-logging(存在)
共8+1+2=11
- spring整合hibernate: spring-orm前面写了
struts整合spring:struts2-spring-plugin-2.3.15.3.jar
- 所以,整合ssh共:13+15+11+1=40个 删除一个重复的javassist,最终39个
11、spring整合hibernate:没有hibernate.cfg.xml 【重点】 <--返回目录
* 与使用jdbcTemplate类似
* dao层,继承HibernateDaoSupport
// 底层需要SessionFactory,自动创建HibernateTemplate模板
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
@Override
public void save(User user) {
this.getHibernateTemplate().save(user);
}
}
* 使用c3p0数据源,数据源4大参数存放到properties文件中
新建jdbcinfo.properties文件,添加一些内容
jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=UTF-8 jdbc.user=root jdbc.password=
* applicationContext.xml文件配置
<!-- 1.1 加载properties文件 --> <context:property-placeholder location="classpath:com/itheima/f_properties/jdbcInfo.properties"/> <!-- 1.2 配置数据源 --> <bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 1.3 配置 LocalSessionFactoryBean,获得 -> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.current_session_context_class">thread</prop> </props> </property> <property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property> </bean> <!-- 2 dao,需要注入sessionFactory --> <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 3 service --> <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean>
12、spring整合struts:struts创建action 【重点】 <--返回目录
配置web.xml
<!-- 1 确定applicationContext.xml位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 2 spring监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 3 struts 前端控制器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
要求:Action类中,必须提供service名称与spring配置文件中<bean id="">的id一致。(如果名称一样,将自动注入)