事务
事务
是操作数据库的最小工作单元,为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行,或都不执行,事务是一组不可在分割的操作集合
一起成功(事务提交),一起失败(事务回滚)
事务的四大特性:(一原持久隔离)
一致性:事务执行后,数据库与其他业务保持一致,无论是成功还是失败。如转账业务,参与转账的两个账号余额之和应该是不变的
原子性:事务操作是不可再分割的原子单位,事务中所有的操作要么执行全部成功,要么全部执行失败。
持久性:一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
隔离性:隔离性指在并发操作中,不同事物之间应该隔离开来,使每个并发中的事务不会相互干扰。
原生jdbc事务处理
public class DBUtil {
private static String url;
private static String username;
private static String password;
static {
Properties p = new Properties();
try {
p.load(DBUtil.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
} catch (IOException e) {
e.printStackTrace();
}
String driverName = p.getProperty("driverName");
url = p.getProperty("url");
username = p.getProperty("username");
password = p.getProperty("password");
try {
//加载驱动
Class.forName(driverName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() throws SQLException{
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
public static void close(Connection connection, Statement statement, ResultSet resultSet){
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
* 知识点:在Java中使用事务
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtil.getConnection();
//开启事务(设置不自动管理提交)
connection.setAutoCommit(false);
String sql1 = "UPDATE bank SET money=money-200 WHERE id=1;";
statement = connection.prepareStatement(sql1);
statement.executeUpdate();
statement = connection.prepareStatement("UPDATE bw1w12ank SET money=money-200 WHERE id=1;");
statement.executeUpdate();
String sql2 = "UPDATE bank SET money=money+200 WHERE id=2;";
statement = connection.prepareStatement(sql2);
statement.executeUpdate();
//提交事务
connection.commit();
} catch (SQLException e) {
System.out.println("SQL异常");
try {
//回滚事务
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
connection.setAutoCommit(true);
DBUtil.close(connection,statement,null);
}
事务的隔离级别情况
脏读
小花读取了小明未提交的新事务,然后小明回滚了,小花就读取了错误的数据(小明的事务在中间回滚了,实际上小花,小明的钱款并未改变)
不可重复读
一个事务范围内多次查询却返回了不同的数据,这是由于查询间隔,被另一个事务修改并提交了。
幻读
也是不可重复读的特性导致
a事务中有查询和修改两个动作,查询只有一条数据和修改以为也只有一条,但是在同一时间b事务插入了一条数据并提交事务,a事务的修改就修改了两条然后才提交事务。所以再次查询就修改了两条数据产生了幻读
第一类事务丢失(回滚事务丢失)
小明这个事务取50,然后中途放弃把50又还回去(回滚),这时如花这个事务放了50进去,跟回滚重合如花这个事务就丢失了。
第二类事务丢失(提交覆盖丢失)
小花开个事务放50进去,这时小明也开了个事务放50进去,导致小明覆盖了小花,小花事务丢失,总金额只有150
事务的隔离级别5种
DEFAULT
默认隔离级别,每种数据库支持的事务隔离级别不一样,MySQL默认采用的REPEATABLE_READ隔离级别,Oracle默认采用的READ_COMMITTED隔离级别。
READ_UNCOMMITTED
读未提交,即能够读取到没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用
READ_COMMITED
读已提交,允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE_READ
重复读取,即在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
SERLALIZABLE
串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了,将严重影响程序的性能
spring事务
spring配置事务
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
舍弃
<!--在spring中默认配置 配置jdbcTemplate 一个模板-->
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
舍弃
<!--在spring中默认配置TransactionTemplate模板 -->
<!--事务管理器模板-->
<bean id="transactionManager" class="org.springframework.transaction.support.TransactionTemplate">
<!--管理的数据源-->
<property name="transactionManager" ref="transactionManager"/>
</bean>
舍弃
<!--基于AspectJ 申明事务xml -->
<!--配置类型的声明式事务-->
<tx:advice id="tx" transaction-manager="transactionManager">
<!--增强事务属性的配置-->
<tx:attributes>
<!--isolation: DEFAULT,事务的隔离级别
propagation:事务的传播行为
read-only:false, 不是只读
timeout:-1 ,解除死锁
no-rollback-for:发现哪些异常不会滚
rollback-for:发现哪些异常回滚事务
-->
<!-- get* => 其中*表示匹配任意字符串,这里表示的是凡是以get开始的方法-->
<tx:method name="get*" propagation="REQUIRED" isolation="READ_COMMITTED"/>-->
</tx:advice>
<aop:config>
<!--定义切点 哪些类和哪些方法应用增强-->
<aop:pointcut id="pc" expression="execution(* com.qf.ssm.service..*(..))"/>
<!--安装事物通知 定义切面-->
<aop:advisor advice-ref="tx" pointcut-ref="pc" />
</aop:config>-->
<tx:annotation-driven />
<context:component-scan base-package="com.qf.ssm.service" />
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public PlatformTransactionManager platformTransactionManager(){
return new DataSourceTransactionManager(dataSource);
}
}
spring事务的7种传播特性
作用在方法上传播特性
保证同一个事务中 PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个(默认) PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务 PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常 保证没有在同一个事务中 PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务 PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务 PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常 PROPAGATION_NESTED 没有事务就新建一个,如果当前事务存在,则嵌套事务执行
Hibenate不支持嵌套事务PROPAGATION_NESTED,mybatis和jdbc支持嵌套事务
持当前事务,如果不存在,抛出异常 保证没有在同一个事务中 PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务 PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务 PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常 PROPAGATION_NESTED 没有事务就新建一个,如果当前事务存在,则嵌套事务执行
Hibenate不支持嵌套事务PROPAGATION_NESTED,mybatis和jdbc支持嵌套事务
|