事务的本质
- Spring 的声明式事务本质上是通过 AOP 来增强了类的功能
- Spring 的 AOP 本质上就是为类做了一个代理
看似在调用自己写的类,实际用的是增强后的代理类
我们在之前写个一个程序。是关于事务的,当时在invokeInsertThenRollback()方法中,没有事务回滚,现在我们修改一下这个方法,让它有事务回滚。
@Component
public class FooServiceImpl implements FooService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private FooServiceImpl fooService;
@Override
@Transactional
public void insertRecord() {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('AAA')");
}
@Override
@Transactional(rollbackFor = RollbackException.class)
public void insertThenRollback() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
throw new RollbackException();
}
@Override
public void invokeInsertThenRollback() throws RollbackException {
fooService.insertThenRollback();
}
}
结果如下:
我们可以看到,BBB回滚了。用这种方式,让我们一个不带事务的方法,调用带事务的方法。
REQUIRES_NEW 与 NESTED 事务传播特性的说明
REQUIRES_NEW,始终启动一个新事务
- 两个事务没有关联
NESTED,在原事务内启动一个内嵌事务
- 两个事务有关联
- 外部事务回滚,内嵌事务也会回滚
我们以一个例子为例:
public class FooServiceImpl implements FooService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private FooService fooService;
@Override
@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRES_NEW)
public void insertThenRollback() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
// throw new RollbackException();
}
@Override
@Transactional(rollbackFor = RuntimeException.class)
public void invokeInsertThenRollback() {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('AAA')");
try {
fooService.insertThenRollback();
} catch (RollbackException e) {
log.error("RollbackException", e);
}
throw new RuntimeException();
}
}
REQUIRES_NEW情况下
两个事务互相不影响
NESTED情况下
外部事务回滚,内嵌事务也会回滚
Alibaba Druid 的一些展开说明
慢 SQL 日志
系统属性配置
- druid.stat.logSlowSql=true
- druid.stat.slowSqlMillis=3000
在Java启动的时候,输入这两个命令
Spring Boot
-
spring.datasource.druid.filter.stat.enabled=true
通过springbootstart,就是的Druid的springboot-start会默认开启统计的filter -
spring.datasource.druid.filter.stat.log-slow-sql=true
开启慢SQL查询 -
spring.datasource.druid.filter.stat.slow-sql-millis=3000
超过3000ms的sol语句会被我们抓出来
以一个例子为例:
@SpringBootApplication
@Slf4j
@EnableTransactionManagement(proxyTargetClass = true)
public class DruidDemoApplication implements CommandLineRunner {
@Autowired
private DataSource dataSource;
@Autowired
private FooService fooService;
public static void main(String[] args) {
SpringApplication.run(DruidDemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
log.info(dataSource.toString());
new Thread(() -> fooService.selectForUpdate()).start();
new Thread(() -> fooService.selectForUpdate()).start();
}
}
@Repository
public class FooService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void selectForUpdate() {
jdbcTemplate.queryForObject("select id from foo where id = 1 for update", Long.class);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
}
结果如下:
我们可以看到,这条慢SQL被查出来。
此外,我们还可以看到,连接池的状态和两个连接的详细信息。