@Transactional注解可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有public方法将都具有该类型的事务属性,同时,也可以在方法级别使用该注解来覆盖类级别的定义。虽然@Transactional注解可以作用于接口、接口方法、类以及类方法上,但是Spring小组建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。
如何在事务处理中捕获异常 声明式事务处理的流程是:1)Spring根据配置完成事务定义,设置事务属性。2)执行开发者的代码逻辑。3)如果开发者的代码产生异常(如主键重复)并且满足事务回滚的配置条件,则事务回滚;否则,事务提交。4)事务资源释放。 现在的问题是,如果开发者在代码逻辑中加入了try...catch...语句,Spring还能不能在声明式事务处理中正常得到事务回滚的异常信息?答案是不能。
1)修改@Transactional注解 我们需要将TestServiceImpl类中的@Transactional注解修改为: @Transactional(rollbackFor= {Exception.class}) //rollbackFor指定回滚生效的异常类,多个异常类逗号分隔; //noRollbackFor指定回滚失效的异常类 2)在catch语句中添加“throw new RuntimeException();”语句。 注意:在实际工程应用中,经常在catch语句中添加“TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();”语句即可。也就是说,不需要在@Transaction注解中添加rollbackFor属性。
package entity; 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 + "]"; } }
package service; public interface TestService { public void testJDBC(); }
package service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; import dao.TestDao; import entity.MyUser; @Service @Transactional //@Transactional(rollbackFor= {Exception.class}) public class TestServiceImpl implements TestService { @Autowired public TestDao testDao; @Override public void testJDBC() { String insertSql = "insert into user values(null,?,?)"; // 数组param的值与insertSql语句中?一一对应 Object param1[] = { "chenheng1", "男" }; Object param2[] = { "chenheng2", "女" }; Object param3[] = { "chenheng3", "男" }; Object param4[] = { "chenheng4", "女" }; String insertSql1 = "insert into user values(?,?,?)"; Object param5[] = { 1, "chenheng5", "女" }; Object param6[] = { 1, "chenheng6", "女" }; try { // 添加用户 testDao.update(insertSql, param1); testDao.update(insertSql, param2); testDao.update(insertSql, param3); testDao.update(insertSql, param4); // 添加两个ID相同的用户,出现唯一性约束异常,使事物回滚。 testDao.update(insertSql1, param5); testDao.update(insertSql1, param6); // 查询用户 String selectSql = "select * from user"; List<MyUser> list = testDao.query(selectSql, null); for (MyUser mu : list) { System.out.println(mu); } } catch(Exception e) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); System.out.println("主键重复,事务回滚。"); throw new RuntimeException(); } } }
package dao; import java.util.List; import entity.MyUser; public interface TestDao { public int update(String sql, Object[] param); public List<MyUser> query(String sql, Object[] param); }
package dao; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import entity.MyUser; @Repository public class TestDaoImpl implements TestDao { @Autowired // 使用配置类中的JDBC模板 private JdbcTemplate jdbcTemplate; /** * 更新方法,包括添加、修改、删除 param为sql中的参数,如通配符? */ @Override public int update(String sql, Object[] param) { return jdbcTemplate.update(sql, param); } /** * 查询方法 param为sql中的参数,如通配符? */ @Override public List<MyUser> query(String sql, Object[] param) { RowMapper<MyUser> rowMapper = new BeanPropertyRowMapper<MyUser>(MyUser.class); return jdbcTemplate.query(sql, rowMapper); } }
package config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration // 通过该注解来表明该类是一个Spring的配置,相当于一个xml文件 @ComponentScan(basePackages = { "dao", "service" }) // 配置扫描包 @PropertySource(value = { "classpath:jdbc.properties" }, ignoreResourceNotFound = true) //配置多个配置文件 value={"classpath:jdbc.properties","xx","xxx"} @EnableTransactionManagement // 开启声明式事务的支持 public class SpringJDBCConfig { @Value("${jdbc.url}") // 注入属性文件jdbc.properties中的jdbc.url private String jdbcUrl; @Value("${jdbc.driverClassName}") private String jdbcDriverClassName; @Value("${jdbc.username}") private String jdbcUsername; @Value("${jdbc.password}") private String jdbcPassword; /** * 配置数据源 */ @Bean public DriverManagerDataSource dataSource() { DriverManagerDataSource myDataSource = new DriverManagerDataSource(); // 数据库驱动 myDataSource.setDriverClassName(jdbcDriverClassName); ; // 相应驱动的jdbcUrl myDataSource.setUrl(jdbcUrl); // 数据库的用户名 myDataSource.setUsername(jdbcUsername); // 数据库的密码 myDataSource.setPassword(jdbcPassword); return myDataSource; } /** * 配置JdbcTemplate */ @Bean(value = "jdbcTemplate") public JdbcTemplate getJdbcTemplate() { return new JdbcTemplate(dataSource()); } /** * 为数据源添加事务管理器 */ @Bean public DataSourceTransactionManager transactionManager() { DataSourceTransactionManager dt = new DataSourceTransactionManager(); dt.setDataSource(dataSource()); return dt; } }
package config; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import service.TestService; public class TestJDBC { public static void main(String[] args) { // 初始化Spring容器ApplicationContext AnnotationConfigApplicationContext appCon = new AnnotationConfigApplicationContext(SpringJDBCConfig.class); TestService ts = appCon.getBean(TestService.class); ts.testJDBC(); appCon.close(); } }