一、背景
1.前面讲解了很多的基础理论知识,现在我们开始几个简单点的小栗子,来巩固下我们前面学习的基础知识,接下来我们来具体来讲解下例子吧。
2.接着我们第一篇的小案例继续讲解,然后我们开始来讲解我们今天的小案例,好了,废话不多说了,直接开始来讲解吧。
二、项目图
1.项目结构
三、代码组成
1.pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.application.properties
#配置项目端口
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/threadtest?serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
3.ThreadPoolTaskExecutorConfig.java
@Configuration
public class ThreadPoolTaskExecutorConfig {
private static int CORE_POOL_SIZE = 5;
private static int MAX_POOL_SIZE = 1000;
@Bean(name="threadPoolTaskExecutor")
public ThreadPoolTaskExecutor serviceJobTaskExecutor(){
ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
//线程池维护线程的最少数量
poolTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
//线程池维护线程的最大数量
poolTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
//线程池所使用的缓冲队列
poolTaskExecutor.setQueueCapacity(200);
//线程池维护线程所允许的空闲时间
poolTaskExecutor.setKeepAliveSeconds(30000);
poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
System.out.println(poolTaskExecutor);
return poolTaskExecutor;
}
}
4.DataSourceConfig.java
@Component
@Configuration
@MapperScan(basePackages = "com.thread.threadtest.dao", sqlSessionTemplateRef = "systemSqlSessionTemplate")
public class DataSourceConfig {
@Bean(name = "systemDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary") // application.properteis中对应属性的前缀
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "systemSqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("systemDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
bean.setTypeAliasesPackage("com.thread.threadtest.pojo");
return bean.getObject();
}
@Bean(name = "systemTransactionManager")
@Primary
public DataSourceTransactionManager testTransactionManager(@Qualifier("systemDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "systemSqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("systemSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
5.UserVoController.java
@RestController
@RequestMapping("/user")
public class UserVoController {
@Autowired
private UserVoService userVoService;
@RequestMapping("/submit")
public String submit(String param){
param = userVoService.submit(param);
return param;
}
@RequestMapping("/execute")
public String execute(String param){
param = userVoService.execute(param);
return param;
}
}
6.UserVoService.java
public interface UserVoService {
String submit(String param);
String execute(String param);
}
7.UserVoServiceImpl.java
@Service
public class UserVoServiceImpl implements UserVoService {
@Autowired
private ThreadPoolTaskExecutorConfig threadPoolTaskExecutorConfig;
@Autowired
private UserVoDao userVoDao;
@Override
public String submit(String param) {
Future<String> future = threadPoolTaskExecutorConfig.serviceJobTaskExecutor().submit(new UserVoCallableTask(param));
try {
param = future.get();
} catch (Exception e) {
e.printStackTrace();
return "error";
}
UserVo user = new UserVo();
user.setName(param);
userVoDao.insertUserVo(user);
return param;
}
@Override
public String execute(String param) {
threadPoolTaskExecutorConfig.serviceJobTaskExecutor().execute(new UserVoRunnableTask(param, userVoDao));
return "success";
}
}
8.UserVoDao.java
public interface UserVoDao {
void insertUserVo(UserVo user);
}
9.UserVo.java
public class UserVo {
private int id;
private String ucode;
private String name;
private String sex;
private String pwd;
private int sid;
private String school;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUcode() {
return ucode;
}
public void setUcode(String ucode) {
this.ucode = ucode;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
10.UserVoCallableTask.java
public class UserVoCallableTask implements Callable<String> {
private String param;
public UserVoCallableTask(String param){
this.param = param;
}
@Override
public String call() throws Exception {
param +="UserVoCallableTask";
int a = 1/0;
return param;
}
}
11.UserVoRunnableTask.java
public class UserVoRunnableTask implements Runnable {
private String param;
private UserVoDao userVoDao;
public UserVoRunnableTask(String param, UserVoDao userVoDao) {
this.param = param;
this.userVoDao = userVoDao;
}
@Override
public void run() {
param += "UserVoRunnableTask";
UserVo user = new UserVo();
user.setName(param);
int a = 1/0;
userVoDao.insertUserVo(user);
}
}
12.ThreadtestApplication
@SpringBootApplication
@ComponentScan(basePackages = {"com.thread.threadtest.*"})
public class ThreadtestApplication {
public static void main(String[] args) {
SpringApplication.run(ThreadtestApplication.class, args);
}
}
13.请求submit接口:
14.请求execute接口:
15.总结
1.Callable执行call时遇到异常会抛出,而Runnable执行run时遇到异常并不会抛出。
四、多线程与事务回滚:
上述,如果在事务中调用了多线程,submit遇到异常会抛出且必须被捕获,不会触发回滚,execute遇到异常主线程无法感知,也不会触发回滚。那如果需要在多线程调用时实现事务回滚该怎么做呢?这就需要加入其它的操作了:
1、submit方法与事务回滚:我们知道sumbit方法提交线程在获取返回结果时是需要捕获异常的,那么我们就可以在捕获到异常时手动回滚当前事务。
(1)主线程正常,子线程发生异常,只回滚主线程:这种情况比较简单,主线程捕获异常后直接TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();回滚主线程就可以了:
1.1.UserServiceImpl.java
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
private UserMapper userMapper;
@Override
@Transactional
public String submit(String param) {
Future<String> future = threadPoolTaskExecutor.submit(new UserCallableTask(param,userMapper));
UserDTO user = new UserDTO();
user.setName("我是主线程");
userMapper.insert(user);
try {
param = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return "error";
} catch (ExecutionException e) {
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return "error";
}
return param;
}
@Override
public String execute(String param) {
threadPoolTaskExecutor.execute(new UserRunnableTask(param,userMapper));
return "success";
}
}
1.2.UserCallableTask.java
public class UserCallableTask implements Callable<String> {
private String param;
private UserMapper userMapper;
public UserCallableTask(String param, UserMapper userMapper){
this.param = param;
this.userMapper = userMapper;
}
@Override
public String call() throws Exception {
param += "UserCallableTask";
UserDTO user = new UserDTO();
user.setName("我是子线程");
userMapper.insert(user);
int a = 1/0;
return param;
}
}
3.结果
4.数据库没有主线程的数据插入:
说明主线程回滚成功。
(2)、主线程或子线程异常,主线程、子线程全部回滚:同时回滚主线程和子线程,就需要把主线程和子线程放到同一个事务中。说明主线程、子线程全部回滚成功。
参考博客:https://blog.csdn.net/w_t_y_y/article/details/102817576
五、结束
Always keep the faith!!!