声明式事物:
环境搭建:
1、加入依赖:数据源、数据库驱动、spring-jdbc
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
2、配置类中加入数据源bean和JdbcTemplate的bean
@Configuration
public class TxConfig {
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("120288");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
return new JdbcTemplate(dataSource());
}
}
在配置JdbcTemplate时的入参是dataSource(),那不就会调用dataSource()方法重新创建一个不受Spring管理的数据源对象了吗?其实不会的,Spring对@Bean注解的Scope为singleton的方法只会实际调用一次,之后再调用时其实是会返回容器中的对象,该配置也可以像下面这样:
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) throws Exception{
return new JdbcTemplate(dataSource);
}
入参的参数Spring会优先从容器中查找,另外需要注意如果使用的数据库驱动比较新时在配置jdbcUrl时需要加上数据库服务器所在的时区:serverTimezone=GMT%2B8(东八区),否则会报异常(The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone)。
3、业务Dao
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(String name,int age){
String sql = "insert into user(user_name,age) values(?,?)";
jdbcTemplate.update(sql,name,age);
}
}
sql中可以有占位符,jdbcTemplate的update方法的第一个参数是sql,后面是个可变参数,可以传任何数据来填充占位符
业务Service:
@Service
public class UserService {
@Autowired
public UserDao userDao;
public void insert(String name,int age){
userDao.insert(name, age);
}
}
测试:
public class MainTest {
@Test
public void test1() throws Exception {
// 1、创建容器对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
UserService bean = context.getBean(UserService.class);
bean.insert("哼哼", 23);
context.close();
}
}
此时的service中是没有事物控制的,也就是说在insert成功之后如果出现异常,依然能够插入成功,比如:
@Service
public class UserService {
@Autowired
public UserDao userDao;
public void insert(String name, int age) {
userDao.insert(name, age);
int i = 10 / 0;
System.out.println(i);
}
}
那怎么将service中的方法变为事物方法呢,需要给事物方法加个注解@Transactional,仅此还不够还需要开启基于注解版的事物管理功能——在配置类上加注解@EnableTransactionManagement
@Service
public class UserService {
@Autowired
public UserDao userDao;
@Transactional(rollbackFor = Exception.class)
public void insert(String name, int age) {
userDao.insert(name, age);
int i = 10 / 0;
System.out.println(i);
}
}
配置类:
@Configuration
@ComponentScan("com.bdm.tx")
@EnableTransactionManagement
public class TxConfig {
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("120288");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) throws Exception{
return new JdbcTemplate(dataSource);
}
}
还需要在容器中注册事物管理器:Spring的JdbcTemplate或者mybatis在进行事物整合时使用的事物管理器是DataSourceTransactionManager;JPA或hibernate在进行事物整合时用WebsphereUowTransactionManager,事物管理器管理的是数据源的事物,管理数据源的每一条连接,事物的开启、回滚等
@Configuration
@ComponentScan("com.bdm.tx")
@EnableTransactionManagement
public class TxConfig {
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("120288");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) throws Exception{
return new JdbcTemplate(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
}
如果数据库表的主键是自增的,在出现异常事物回滚后,数据表的主键将不再连续,因为数据回滚前自增的主键值并不会跟着回滚
配置声明式事物的三个步骤:
1、开启注解方式的事物管理功能:配置类加@EnableTransactionManagement
2、配置事物管理器:在配置类中配置PlatformTransactionManager的实现类,注意入参是数据源
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
3、事物方法上加注解:@Transactional