1、搭建一个简单的springboot项目,引入相关的依赖及配置。
1.1 application.properties
#pgsql
spring.datasource.url=jdbc:postgresql://localhost:5432/db_name?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver
#如果是mysql,可以换成下面的配置
#spring.datasource.url=jdbc:mysql://localhost:3306/socks?useSSL=false
#spring.datasource.username=username
#spring.datasource.password=password
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#mybatis的配置
#mapper文件的位置
mybatis-plus.mapper-locations=classpath*:/mapper/**Mapper.xml
mybatis-plus.type-aliases-package=com.hehe.pojo
mybatis-plus.global-config.id-type=3
mybatis-plus.global-config.field-strategy=2
mybatis-plus.db-column-underline=true
mybatis-plus.refresh-mapper=true
mybatis-plus.logic-delete-value=0
mybatis-plus.logic-not-delete-value=1
#开启驼峰映射
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.cache-enabled=false
#\u5237\u65B0mapper \u8C03\u8BD5\u795E\u5668
mybatis-plus.global-config.refresh-mapper=true
1.2 依赖pom.xml
下面主要的依赖是关于mybatis的和数据源,以及数据库驱动的依赖,具体选择psql还是mysql,自己可以选择。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--基本信息 -->
<groupId>com.hehe</groupId>
<artifactId>springboot-mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-mybatis</name>
<description>SpringBoot快速整合Mybatis案例</description>
<!--继承信息 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M4</version>
<relativePath/>
</parent>
<properties>
<mybatis-plus-boot-starter.version></mybatis-plus-boot-starter.version>
<druid.version>1.1.2</druid.version>
<postgresql.version>42.1.4</postgresql.version>
<mybatis-plus-boot-starter.version>2.2.0</mybatis-plus-boot-starter.version>
<pagehelper-spring-boot-starter.version>1.2.3</pagehelper-spring-boot-starter.version>
<slf4j.version>1.7.25</slf4j.version>
<logback.version>1.2.2</logback.version>
</properties>
<!--依赖管理 -->
<dependencies>
<dependency> <!--添加Web依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>-->
</dependency>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>-->
<!--<dependency><!–添加MySQL驱动依赖 –>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>-->
<!--Db begin-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper-spring-boot-starter.version}</version>
</dependency>
<!--Db end-->
<!--Log begin-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<!--Log end-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.40</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.15.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.14.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency><!--添加Test依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--指定远程仓库(含插件)-->
<repositories>
<repository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/snapshot</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/snapshot</url>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
<!--构建插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
3、创建事务的配置类(非必须,可以直接应用springboot自带的注解@Transaction)
本文加了一个事务的配置类,如下
package com.hehe.config;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* <p>全局事务配置</p>
*
* @author anna 2019/3/12 18:45
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2019/3/12
* @modify by reason:TransactionConfig:{原因}
*/
@Aspect
@Configuration
public class TransactionConfig {
//事务超时时间
private static final int TX_METHOD_TIMEOUT=60;
//com.hehe.service包或其下面的所有子包的任意类的任意方法
private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.hehe.service..*.*(..))";
//注入事务管理器
@Autowired
private PlatformTransactionManager transactionManager;
//事务的切面
@Bean
public TransactionInterceptor txAdvice() {
/**
* 只读事务,不做更新操作
*/
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
readOnlyTx.setReadOnly(true);
//事务的传播行为required-->[如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务]
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
/**
* 必须带事务
* 当前存在事务就使用当前事务,当前不存在事务,就开启一个新的事务
*/
RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
//检查型异常也回滚--异常回滚
requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
//传播行为
requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//写事务超时
requiredTx.setTimeout(TX_METHOD_TIMEOUT);
/***
* 无事务地执行,挂起任何存在的事务
*/
RuleBasedTransactionAttribute noTx = new RuleBasedTransactionAttribute();
noTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
/**
* 方法前缀与事务类型的定义
*/
Map<String, TransactionAttribute> txMap = new HashMap<>();
//写事务(一般添加、修改、删除为写事务)
txMap.put("add*", requiredTx);
txMap.put("save*", requiredTx);
txMap.put("insert*", requiredTx);
txMap.put("update*", requiredTx);
txMap.put("delete*", requiredTx);
//只读事务(查询)
txMap.put("get*", readOnlyTx);
txMap.put("query*", readOnlyTx);
txMap.put("find*", readOnlyTx);
txMap.put("list*", readOnlyTx);
txMap.put("count*", readOnlyTx);
txMap.put("exist*", readOnlyTx);
txMap.put("search*", readOnlyTx);
//无事务,要求不能在事务中执行的方法
txMap.put("noTx*", noTx);
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
source.setNameMap(txMap);
TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
return txAdvice;
}
@Bean
public Advisor txAdviceAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, txAdvice());
}
}
3、service
@Service
public class PassengerHourServiceImpl implements IPassengerHourService {
private static final Logger log = LoggerFactory.getLogger(PassengerHourServiceImpl.class);
@Autowired
private PassengerHourMapper passengerHourMapper;
@Override
public void insertPassengerHourList(List<PassengerHour> passengerHourList){
passengerHourMapper.insertBatch(passengerHourList);
}
}
package com.hehe.service.impl;
import com.hehe.pojo.PassengerHour;
import com.hehe.service.IPassengerHourService;
import com.hehe.service.ITestTransactionService;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
/**
* <p></p>
*
* @author anna 2019/3/12 19:28
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2019/3/12
* @modify by reason:TestTransactionServiceImpl:{原因}
*/
@Service
public class TestTransactionServiceImpl implements ITestTransactionService {
private static final Logger log = LoggerFactory.getLogger(TestTransactionServiceImpl.class);
@Autowired
private IPassengerHourService passengerHourService;
@Override
public void insertPassengerHour(PassengerHour passengerHour,boolean isException) {
int a=1;
passengerHourService.insertPassengerHourList(Arrays.asList(passengerHour));
log.info("has insert,isException=",isException);
//制造一个异常来导致插入回滚
if (isException) {
log.info("has insert,next a/0");
a=a/0;
}
}
}
public interface PassengerHourMapper {
void insertBatch(@Param("list") List<PassengerHour> passengerHourList);
}
PassengerHourMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hehe.mapper.PassengerHourMapper">
<sql id="base_columns">
statistics_time,group_id,group_name,flow_in_num,flow_out_num,flow_net_num,area_size,create_time,update_time
</sql>
<!--批量插入-->
<insert id="insertBatch" parameterType="com.hehe.pojo.PassengerHour">
insert into tb_passenger_hour_temp (<include refid="base_columns"/>)
values
<foreach collection="list" item="item" separator=",">
(
#{item.statisticsTime},
#{item.groupId},
#{item.groupName},
#{item.flowInNum},
#{item.flowOutNum},
#{item.flowNetNum},
#{item.areaSize},
#{item.createTime},
#{item.updateTime}
)
</foreach>
</insert>
</mapper>
controller
package com.hehe.controller;
import com.hehe.pojo.PassengerHour;
import com.hehe.service.ITestTransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
/**
* <p></p>
*
* @author anna 2019/3/12 19:34
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2019/3/12
* @modify by reason:TestTransactionController:{原因}
*/
@RestController
@RequestMapping("/transaction")
public class TestTransactionController {
@Autowired
private ITestTransactionService testTransactionService;
@GetMapping("/insert")
public String testInsert(String groupId,boolean isException){
PassengerHour ph = new PassengerHour();
ph.setGroupName("test");
ph.setAreaSize(10);
ph.setStatisticsTime(LocalDateTime.of(LocalDate.now(), LocalTime.MIN));
ph.setGroupId(groupId);
ph.setFlowInNum(1);
ph.setFlowOutNum(1);
ph.setFlowNetNum(1);
ph.setCreateTime(LocalDateTime.now());
testTransactionService.insertPassengerHour(ph,isException);
return "success";
}
}
4、启动并测试
4.1 先测试异常回滚的情况
显然从日志可以看出执行了插入语句,那么查询数据库看看是否插入成功
从查询结果来看,插入失败了,说明事务生效了,异常回滚了。
说明一下,具体的数据库表及结构什么的读者可以自己定义一个,这里只是偷懒,应用了之前的一个定义好的表来测试的。
4.2 接下来测试一下正常情况的插入,也就是不抛异常时看看是否能插入成功,主要为了对比更有说服力
显然插入成功了。