1 事物
事务:指的是逻辑上一组操作,组成这个事务的各个执行单元,要么一起成功,要么一起失败!
1.1 事务的特性(ACID)
原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。
一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作
1.2 事物并发可能的问题
脏读:一个事务读取到另一个事务未提交的数据。
不可重复读:一个事务因读取到另一个事务已提交的数据。导致对同一条记录读取两次以上的结果不一致。update操作
幻读:一个事务因读取到另一个事务已提交的数据。导致对同一张表读取两次以上的结果不一致。insert、delete操作
1.3 事物隔离级别
① Read uncommitted (读未提交):最低级别,任何情况都无法保证。
② Read committed (读已提交):可避免脏读的发生。Oracle、DB2默认隔离级别
③ Repeatable read (可重复读):可避免脏读、不可重复读的发生。mysql默认隔离级别
④ Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
2 spring事物
Spring事务管理器的接口是PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器
- PlatformTransactionManager接口 – 平台事务管理器.(真正管理事务的类)。该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类!
- TransactionDefinition接口 – 事务定义信息.(事务的隔离级别,传播行为,超时,只读)
- TransactionStatus接口 – 事务的状态(是否新事务、是否已提交、是否有保存点、是否回滚)
- 总结:上述对象之间的关系:平台事务管理器真正管理事务对象.根据事务定义的信息TransactionDefinition 进行事务管理,在管理事务中产生一些状态.将状态记录到TransactionStatus中
底层采用AOP的技术
3 spring管理Jdbc Template
3.1 导入相关依赖
<!-- 省略spring基本依赖,core,beans.context,
<!-- spring 事物 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-version}</version>
</dependency>
3.2 纯注解形式
零xml配置
3.2.1 db.properties和ProertiesConfig
db.properties
db.driver=com.mysql.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
db.username = root
db.password=root
@Configuration
@PropertySource("classpath:db.properties")
public class ProertiesConfig {
@Value("${db.driver}")
private String DRIVER;
@Value("${db.url}")
private String URL;
@Value("${db.username}")
private String USERNAME;
@Value("${db.password}")
private String PASSWORD;
@Bean("dataSource")
public DataSource dataSource(){
// spring内置连接池
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(DRIVER);
dataSource.setUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
return dataSource;
}
}
3.2.2 DBConnectConfig
@Configuration
// 开启事物管理器
@EnableTransactionManagement
// 导入ProertiesConfig 配置
@Import(ProertiesConfig.class)
public class DBConnectConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}
3.2.3 SpringConfiguration
// 全局配置文件
@Configuration
@ComponentScan(basePackages = "com.cc")
@Import(DBConnectConfig.class)
@EnableAspectJAutoProxy
public class SpringConfiguration {
//spring容器初始化时,会调用配置类的无参构造函数
public SpringConfiguration(){
System.out.println("容器启动初始化");
}
}
3.2.4 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
// 开启事物
@Transactional
// 如果不加@Rollback(false)则insert update 会一直失败
@Rollback(false)
public class JdbcTempleTest {
@Autowired
JdbcTemplate jdbcTemplate;
private int threadNum;
@Test
public void testInsert(){
System.out.println("第"+ threadNum +" thread");
String sql ="INSERT INTO user(username,password,email) VALUES(?,?,?)";
try {
int count = jdbcTemplate.update(sql, "ceshi Temple"+threadNum, "1111", "test@com");
System.out.println(count);
}catch (Throwable t){
t.printStackTrace();
}
}
@Test
public void testQuery(){
System.out.println("第"+ threadNum +" thread");
String sql ="SELECT * FROM user";
List<Map<String, Object>> users = jdbcTemplate.queryForList(sql);
users.stream().forEach((x)->System.out.println(threadNum +" "+x));
}
@Test
public void testThread(){
for (int i =0;i<5;i++){
threadNum =i+1;
Runnable r1 = ()->testInsert();
r1.run();
Runnable r2 = ()->testQuery();
r2.run();
}
}
}
3.3 xml配置连接池+注解 形式
上面的demo中采用spring内置连接池管理datasource,这次使用c3p0连接池,导入相关依赖
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
3.3.1 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.cc"/>
<context:property-placeholder location="classpath:db.properties"/>
<!-- 创建连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${db.driver}"/>
<property name="jdbcUrl" value="${db.url}"/>
<property name="user" value="${db.username}"/>
<property name="password" value="${db.username}"/>
</bean>
<!-- transactionManager 事物管理器管理连接池 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事物 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
3.3.2 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
@Transactional
public class JdbcTempleXmlTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testInsert(){
String sql ="INSERT INTO user(username,password,email) VALUES(?,?,?)";
try {
int count = jdbcTemplate.update(sql, "ceshi XML", "1111", "test@com");
System.out.println(count);
}catch (Throwable t){
t.printStackTrace();
}
}
@Test
public void testQuery(){
String sql ="SELECT * FROM user";
List<Map<String, Object>> users = jdbcTemplate.queryForList(sql);
users.stream().forEach((x)->System.out.println(x));
}
}