连接池、事务管理以及事务的传播行为的笔记。
Java程序操作数据库,必须获得一个连接Connection。
实际操作中,先获得连接,使用完成关闭连接。这两个操作都比较耗时。
一组连接组成的一个池子(集合),称为连接池。
JavaEE规范规定,连接池必须实现 `javax.sql.DataSource`
接口中规定方法:getConnection()
druid,德鲁伊,alibaba提供,稳定,性能好等
DBCP,Apache提供,tomcat内置
C3P0,hibernate框架底层使用。(等效框架MyBatis)
1.导入坐标:
2. 测试类:
package com.czxy;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import org.junit.Test;
import java.sql.SQLException;
/**
* @Author: 小新
* @Date: 2021-12-03 08:49
*/
public class TestDruid {
@Test
public void run() throws SQLException {
//1.获得连接池
//1.1核心类
DruidDataSource dataSource = new DruidDataSource();
//1.2基本四项
//1) 驱动
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
//2)链接url
dataSource.setUrl("jdbc:mysql://localhost:3306/db_20211028");
//3)用户名
dataSource.setUsername("root");
//4)密码
dataSource.setPassword("1234");
//1.3特殊项【可选】
//1)初始化大小
dataSource.setInitialSize(5);
//2)最大值活动数
dataSource.setMaxActive(10);
//3)最小值空闲数
dataSource.setMinIdle(2);
//2.从连接池中获得链接
DruidPooledConnection conn1 = dataSource.getConnection();
System.out.println(conn1);
conn1.close();
DruidPooledConnection conn2 = dataSource.getConnection();
System.out.println(conn2);
conn2.close();
}
}
3.测试结果:
测试一:
代码:
测试二:
代码:
结果:
在数据库开发中,一组业务逻辑,要么全部成功,要么全部失败。
保证一组操作要么全都成功,对数据库进行完整更新。要么在某一个动作失败的时候让数据恢复原状,不会引起不完整的修改。
①原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
②一致性(Consistency)事务前后数据的完整性必须保持一致。
③ 隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
④持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变 就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
①脏读:一个事务读到了另一个没有提交的数据。
②不可重复读:一个事务读到了另一个事务已有提交的数据(update)。
③幻读:一个事务读到了另一个事务已有提交的数据(insert)。
①读未提交(read uncommitted):一个事务读到另一个事务没有提交的数据。存在3个问题,解决0个问题。
②读已提交(read committed):一个事务读到另一个事务已经提交的数据。存在2个问题,解决1个问题(脏读)。
③可重复读(repeatable read):在一个事务中读到的数据重复的。存在1个问题,解决2两个问题(脏读、不可重复读)。
④串行化(Serializable):单事务。存在0个问题,解决3三个问题(脏读、不可重复读、幻读)。
性能:read uncommitted > read committed > repeatable read > Serializable
安全:read uncommitted < read committed < repeatable read < Serializable
mysql:可重复读(repeatable read)
Oracle:读已提交(read committed)
概述:一个事务调用另一个事务,事务共享问题。
传播行为 | 描述 | 举例 |
PROPAGATION_REQUIRED,required | 支持当前事务,如果没有事务,创建一个新的。 | A 有事务,B使用A的事务。(支持当前事务) A没有事务,B创建新的。() |
PROPAGATION_SUPPORTS,supports | 支持当前事务,如果没有事务,以非事务执行。 | A 有事务,B使用A的事务。(支持当前事务) A没有事务,B以非事务执行。 |
PROPAGATION_MANDATORY,mandatory | 支持当前事务,如果没有事务,抛异常 | A 有事务,B使用A的事务。(支持当前事务) A没有事务,B抛异常。 |
PROPAGATION_REQUIRES_NEW,requires_new | 创建一个新事物,如果当前有事务,将挂起 | A 有事务,B创建新事务,同时挂起A事务。 A 没有事务,B创建新事务。 |
PROPAGATION_NOT_SUPPORTED, not_supported | 不支持当前事务,以非事务执行,如果有挂起 | A 有事务,B以非事务执行,同时挂起A事务。 A 没有事务,B以非事务执行。 |
PROPAGATION_NEVER, never | 不支持当前事务,如果有抛异常。 | A 有事务,B抛异常 A 没有事务,B以非事务执行。 |
PROPAGATION_NESTED, nested | 嵌套事务,底层使用savepoint 进行嵌套事务操作。 |
保存点允许回顾部分事务 。 |
@Transactional 注解可以描述事务定义,一般情况默认值最常用。
@Transactional(
readOnly = false,
timeout = -1,
isolation = Isolation.DEFAULT,
ropagation = Propagation.REQUIRED)
4.1 需求 :完成转账功能。
4.2 表结构:
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(40),
money FLOAT
);
insert into account (id,name,money) values('1','jack','10000');
insert into account (id,name,money) values('2','rose','10000');
4.3 项目结构:
4.4 实现:
mybatis配置类:
@Configuration
public class MyBatisConfiguration {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
//1 创建工厂
// 1.通过工厂bean创建对象,最后需要调用 getObject()获得具体的对象
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
//2 设置数据-- SqlMapConfig.xml 配置信息
// 1.1 设置数据源
factoryBean.setDataSource(dataSource);
// 1.2 设置别名包扫描
factoryBean.setTypeAliasesPackage("com.czxy.demo17_tx.domain");
// 1.3 全局配置:驼峰映射
org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
config.setMapUnderscoreToCamelCase(true);
factoryBean.setConfiguration(config);
// 2 插件配置
// 2.1 分页插件
PageHelper pageHelper = new PageHelper();
Properties pageProps = new Properties();
pageProps.setProperty("dialect", "mysql");
pageProps.setProperty("rowBoundsWithCount", "true");
pageHelper.setProperties(pageProps);
factoryBean.setPlugins(new Interceptor[] { pageHelper });
// 返回SqlSessionFactory
return factoryBean.getObject();
}
/**
* 扫描Dao的包,查找各种XxxMapper接口,创建好UserMapper等对象存入到IOC的容器中
* @return
*/
@Bean
public MapperScannerConfigurer mapperScanner() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.czxy.demo17_tx.mapper");
return configurer;
}
}
Spring配置类:
@Configuration
@PropertySource("classpath:db.properties")
@ComponentScan(basePackages = "com.czxy.demo17_tx")
@EnableTransactionManagement //开启事务管理
public class SpringConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 连接池配置
* @return
*/
@Bean
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
/**
* 平台事务管理器
* @param dataSource
* @return
*/
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
domain:
public class Account {
@Id
private Integer id;
private String name;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
}
mapper:
public interface AccountMapper extends Mapper {
}
service接口及方法:
public interface AccountService {
/**
* 转账
* @param outId
* @param inId
* @param money
*/
public void change(int outId, int inId, float money);
}
service实现类:
@Service
@Transactional(timeout = 10,readOnly = false, isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED) //事务定义
public class AccountServiceImpl implements AccountService {
@Resource
private AccountMapper accountMapper;
@Override
public void change(int outId, int inId, float money) {
//汇款 -
Account outAccount = accountMapper.selectByPrimaryKey(outId);
outAccount.setMoney( outAccount.getMoney() - money);
accountMapper.updateByPrimaryKey(outAccount);
//模拟异常
// int i = 1/0;
//收款 +
Account inAccount = accountMapper.selectByPrimaryKey(inId);
inAccount.setMoney( inAccount.getMoney() + money);
accountMapper.updateByPrimaryKey(inAccount);
}
}
Test测试类:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class, MyBatisConfiguration.class})
public class TestDemo17Tx {
@Resource
private AccountService accountService;
@Test
public void testDemo() {
accountService.change(1,2,100);
System.out.println("转账成功");
}
}
测试结果:
ider:
数据库: