[JAVAEE]实验05:Spring事务管理
2019-04-04 本文已影响0人
Topus
1.Spring JDBC
1.1applicationContext.xml内配置好数据库相关信息
<!-- 指定需要扫描的包(包括子包),使注解生效 -->
<context:component-scan base-package="com.ch5"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- MySQL数据库驱动 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 连接数据库的URL -->
<property name="url" value="jdbc:mysql://localhost:3306/springtest?characterEncoding=utf8"/>
<!-- 连接数据库的用户名 -->
<property name="username" value="root"/>
<!-- 连接数据库的密码 -->
<property name="password" value="363316495"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
1.2创建实体类MyUser
public class MyUser {
private Integer uid;
private String uname;
private String usex;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUsex() {
return usex;
}
public void setUsex(String usex) {
this.usex = usex;
}
public String toString() {
return "myUser [uid=" + uid +", uname=" + uname + ", usex=" + usex + "]";
}
}
1.3创建数据库访问层TestDao
接口类:
package ch5;
import java.util.List;
public interface TestDao {
public int update(String sql, Object[] param);
public List<MyUser> query(String sql, Object[] param);
}
实现类:
@Repository("testDao")
public class TestDaoImpl implements TestDao{
//自动装配
@Autowired
//使用配置文件中的JDBC(相当于依赖注入了)
private JdbcTemplate jdbcTemplate;
//更新方法
@Override
public int update(String sql, Object[] param) {
return jdbcTemplate.update(sql, param);
}
//查询方法
@Override
public List<MyUser> query(String sql, Object[] param) {
RowMapper<MyUser> rowMapper = new BeanPropertyRowMapper<MyUser>(MyUser.class);
return jdbcTemplate.query(sql, rowMapper, param);
}
}
1.4测试:
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取增强后的目标对象
TestDao td = (TestDao)appCon.getBean("testDao");
//插入的sql
//暂时没有在这里发现问题:
//UID为主键,这里设置为空,不会出现问题,原因是:mysql数据库里面可以勾选主键自动增长
String insertSql = "insert into user values(null,?,?)";
//数组param的值与insertSql语句一一对应
//此处我的mysql不支持中文字符,改成了英文字符,不影响
Object param1[] = {"chenheng1", "nan"};
Object param2[] = {"chenheng2", "nv"};
Object param3[] = {"chenheng3", "nan"};
Object param4[] = {"chenheng4", "nv"};
//添加用户
td.update(insertSql, param1);
td.update(insertSql, param2);
td.update(insertSql, param3);
td.update(insertSql, param4);
//查询用户的sql
String selectSql ="select * from user";
List<MyUser> list = td.query(selectSql, null);
for(MyUser mu : list) {
System.out.println(mu);
}
}
}
1
2.编程式事务管理
(1)基于底层API的编程式事务管理
2.1.1 applicationContext.xml内配置事务管理器
<!-- 为数据源添加事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager" <property name="dataSource" ref="dataSource" />
</bean>
2.1.2 创建数据访问类
//数据访问类
@Repository("codeTransaction")
public class CodeTransaction {
@Autowired
// 使用配置文件中的JDBC模板
private JdbcTemplate jdbcTemplate;
//DataSourceTransactionManager是PlatformTransactionManager接口的实现类
@Autowired
private DataSourceTransactionManager txManager;
//定义test方法
public String test() {
// 默认事务定义,例如隔离级别、传播行为
TransactionDefinition tf = new DefaultTransactionDefinition();
//开启事务ts
TransactionStatus ts = txManager.getTransaction(tf);
String message = "ִ执行成功,没有事务回滚";
try {
// 删除表中数据
String sql = " delete from user ";
//添加表中数据
String sql1 = " insert into user values(?,?,?) ";
Object param[] = { 1, "topus", "nan" };
// 先删除数据
jdbcTemplate.update(sql);
//添加一条数据
jdbcTemplate.update(sql1, param);
// 添加相同的一条数据
jdbcTemplate.update(sql1, param);
//提交事务
txManager.commit(ts);
} catch (Exception e) {
// 出现异常,事务回滚
txManager.rollback(ts);
message = "主键重复,事务回滚";
e.printStackTrace();
}
//返回的是执行成功与否的消息
return message;
}
}
2.2.3测试
public class TestCodeTransaction {
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
CodeTransaction ct = (CodeTransaction)appCon.getBean("codeTransaction");
String result = ct.test();
System.out.println(result);
}
}
不重复添加
success
数据表
重复添加
回滚
(2)基于TranesactionTemplate的编程式事务管理
2.2.1为事务管理器添加事务模板
<!-- 为事务管理器txManager创建transactionTemplate -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"/>
</bean>
2.2.2 创建数据访问类
@Repository("transactionTemplateDao")
public class TransactionTemplateDao {
@Autowired
//使用配置文件中的jdbc
private JdbcTemplate jdbcTemplate;
@Autowired
private TransactionTemplate transactionTemplate;
String message = "";
public String test() {
//匿名内部类
transactionTemplate.execute(new TransactionCallback<Object>(){
@Override
public Object doInTransaction(TransactionStatus arg0) {
//删除
String sql = " delete from user ";
//添加
String sql1 = " insert into user values(?,?,?) ";
Object param[] = {
1,
"zyz",
"nan"
};
try{
//先删除数据
jdbcTemplate.update(sql);
//添加一条数据
jdbcTemplate.update(sql1, param);
//添加一条重复数据
jdbcTemplate.update(sql1, param);
message = "执行成功没有事务回滚ִ";
}catch(Exception e){
message = "主键重复,事务回滚";
e.printStackTrace();
}
return message;
}
});
return message;
}
}
2.2.3 创建测试类
public class TransactionTemplateTest {
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
TransactionTemplateDao ct = (TransactionTemplateDao)appCon.getBean("transactionTemplateDao");
String result = ct.test();
System.out.println(result);
}
}
数据库
不重复
成功
重复
回滚
3.声明式事务管理
(基于AOP技术实现的事务管理,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务)
(1)xml
3.1.1创建Dao层
public interface TestDao {
public int save(String sql, Object param[]);
public int delete(String sql, Object param[]);
}
@Repository("TestDao")
public class TestDaoImpl implements TestDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int save(String sql, Object[] param) {
return jdbcTemplate.update(sql,param);
}
@Override
public int delete(String sql, Object[] param) {
return jdbcTemplate.update(sql,param);
}
}
3.1.2创建service层
public class TestServiceImpl implements TestService{
@Autowired
private TestDao testDao;
@Override
// 这里调用Dao层的save和delete方法,参数为sql和param,等控制层在传入sql语句
public int save(String sql, Object[] param) {
return testDao.save(sql, param);
}
@Override
public int delete(String sql, Object[] param) {
return testDao.delete(sql, param);
}
}
3.1.3创建controller层
private TestService testService;
public String test() {
// 此处在加入sql语句
String message = "";
String deleteSql ="delete from user";
String saveSql = "insert into user values(?,?,?)";
Object param[] = {1,"topus","nan"};
try{
testService.delete(deleteSql, null);
testService.save(saveSql, param);
//重复插入相同主键的数据
testService.save(saveSql, param);
}catch(Exception e){
message = "主键重复,回滚";
e.printStackTrace();
}
return message;
}
}
3.1.4创建配置文件
在xml里面使用<tx:advice>编写通知声明事务
使用<aop:config>编写AOP让spring自动对目标对象生成代理
<!-- 指定需要扫描的包(包括子包),使注解生效 -->
<context:component-scan base-package="com.statement"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- MySQL数据库驱动 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 连接数据库的URL -->
<property name="url" value="jdbc:mysql://localhost:3306/springtest?characterEncoding=utf8"/>
<!-- 连接数据库的用户名 -->
<property name="username" value="root"/>
<!-- 连接数据库的密码 -->
<property name="password" value="root"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 为数据源添加事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 编写通知声明事务 -->
<tx:advice id="myAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- *表示任意方法 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 编写AOP,让Spring自动对目标对象生成代理,需要使用AspectJ的表达式 -->
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* com.statement.service.*.*())" id="txPointCut"/>
<!-- 切面:将切入点与通知关联 -->
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
3.1.6测试
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext appCon = new ClassPathXmlApplicationContext("/com/statement/xml/XMLstatementapplicationContext.xml");
StatementController ct = (StatementController)appCon.getBean("statementController");
String result = ct.test();
System.out.println(result);
}
(2)基于transactional注解的声明式事务管理
3.2.1创建配置文件
<!-- 指定需要扫描的包(包括子包),使注解生效 -->
<context:component-scan base-package="com.statement"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- MySQL数据库驱动 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 连接数据库的URL -->
<property name="url" value="jdbc:mysql://localhost:3306/springtest?characterEncoding=utf8"/>
<!-- 连接数据库的用户名 -->
<property name="username" value="root"/>
<!-- 连接数据库的密码 -->
<property name="password" value="root"/>
</bean>
<!-- 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 为数据源添加事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 为事务管理器注册注解驱动 -->
<tx:annotation-driven transaction-manager="txManager" />
</beans>
3.2.2在service层添加注解@Transactional
//加上注解@Transactional,就可以指定这个类需要受Spring的事务管理
//注意@Transactional智能针对pulic属性范围内的方法添加
@Transactional
public class TestServiceImpl implements TestService{
@Autowired
private TestDao testDao;
@Override
// 这里调用Dao层的save和delete方法,参数为sql和param,等控制层在传入sql语句
public int save(String sql, Object[] param) {
return testDao.save(sql, param);
}
@Override
public int delete(String sql, Object[] param) {
return testDao.delete(sql, param);
}
}
3.2.3在事务处理中捕获异常
修改注解
@Transactional(rollbackFor = (Exception.class))
//指定回滚生效的异常类
1
2
其实本质上问题在于书内的try catch的异常捕获写在的service层,但是在默认情况下,Spring只在(发生未被捕获的RuntimeException时)才回滚事务,此时如果想在事务处理中捕获异常就必须指定回滚生效的异常类.