事务特性:
原子性:在整个事务中,所有执行的Sql要么全部执行,要么全部不执行。
一致性:数据库必须要从一个一致性状态转变到另一个一致性状态。是从业务角度来分析的,比如购买十件商品,操作数据库的时候只减少了一件商品,那么就不行了。这边卖十件商品,仓库就必须要减少十件商品。
Mysql事务处理: Mysql处理事务的语法结构
事务并发问题:
脏读:不合理或者错误的数据。可以将内存空间相互独立,不给相互读取或者只给读取永久数据。——会话独立
不可重复读:可以将记录锁住,锁行
幻读:可以锁表
Mysql事务隔离级别:
隔离级别越高,解决的事务并发问题越来越多,但是并发事务执行的时候效率是越来越低的。
JDBC事务处理:
JDBC是通过Connection对象进行事务管理的。其事务必须基于同一个Connection对象的多个SQL或者语句操作才能封装成一个事务,如果是不同的Connection对象无法将其封装成一个事务。
案例:
JDBC事务隔离级别的设置:
Spring事务处理的各个接口API:
各个接口的详解:
事务传播行为的设置:
案例:
假如有a和b两个方法,在a方法中调用了b方法,那么在a方法中调用了b方法,两个事务之间会不会产生什么问题呢?两个事务之间会如何关联 如何传递呢?这就要讲到事务传播行为的问题了。
假设b方法设置了事务传播行为级别为PROPAGATION_REQUIRED
在a方法调用b方法的时候 会进行判断:
如果a方法没有封装成一个事务 ,a方法调用b方法时,判断当前没有事务。所以开始运行b方法时 b方法便新开启一个新的事务。
如果a方法已经封装成了一个事务,b方法在a方法事务中被调用,b就会加入到a的事务当中。
其余事务行为 如此类推。
Spring事务处理-基于底层API 案例:
OrderServiceImpl.java
package com.imooc.os.service.impl;
import com.imooc.os.dao.OrderDao;
import com.imooc.os.dao.ProductDao;
import com.imooc.os.entity.Order;
import com.imooc.os.entity.Product;
import com.imooc.os.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean;
import java.util.Date;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private ProductDao productDao;
//事务管理器对象
@Autowired
private PlatformTransactionManager transactionManager;
//事务定义的对象
@Autowired
private TransactionDefinition transactionDefinition;
public void addOrder(Order order) {
order.setCreateTime(new Date());
order.setStatus("待付款");
//开启事务 获得事务状态对象
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
orderDao.insert(order);
Product product = productDao.select(order.getProductsId());
product.setStock(product.getStock() - order.getNumber());
productDao.update(product);
transactionManager.commit(transactionStatus);
} catch (Exception e) {
e.printStackTrace();
//如果发生错误则进行回滚操作
transactionManager.rollback(transactionStatus);
}
}
}
spring-service.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"
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">
<import resource="spring-dao.xml"/>
<context:component-scan base-package="com.imooc.os.service.impl1"/>
<!--事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务定义的对象-->
<bean id="transactionDefinition" class="org.springframework.transaction.support.DefaultTransactionDefinition">
<!--传播行为名称 定义-->
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property>
</bean>
</beans>
spring-dao.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"
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">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/os?userUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<context:component-scan base-package="com.imooc.os.dao"/>
</beans>
有关增删查改的具体操作就不列出了。 上面的案例可以通过下图加深理解 案例主要展示了 通过往PlatformTransactionManager (事务管理器对象) 传入事务定义的对象TransactionDefinition 来开启事务,执行完持久化操作后使用 transactionManager.commit(transactionStatus); 提交事务。
如果发生异常,将在catch里面通过 transactionManager.rollback(transactionStatus); 进行回滚操作。
其中PlatformTransactionManager 以及TransactionDefinition 对象使用XML方式配置其中的信息,通过SpringIOC实现注入。
Spring事务处理-基于TransactionTemplate 案例:
上面的基于底层API 案例 每次都要设置开启事务和提交事务,较为繁琐。然后Spring对其进行了封装,下面将展示 基于TransactionTemplate的使用。
上图为TransactionTemplate的源码,里面有一个叫execute的方法,其中以及设置了开启事务和提交事务,所以这个就不用手写了。
OrderServiceImpl.java
package com.imooc.os.service.impl2;
import com.imooc.os.dao.OrderDao;
import com.imooc.os.dao.ProductDao;
import com.imooc.os.entity.Order;
import com.imooc.os.entity.Product;
import com.imooc.os.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.Date;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private ProductDao productDao;
@Autowired
private TransactionTemplate transactionTemplate;
public void addOrder(final Order order) {
order.setCreateTime(new Date());
order.setStatus("待付款");
transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus transactionStatus) {
try {
orderDao.insert(order);
Product product = productDao.select(order.getProductsId());
product.setStock(product.getStock() - order.getNumber());
productDao.update(product);
}catch (Exception e){
e.printStackTrace();
transactionStatus.setRollbackOnly();
}
return null;
}
});
}
}
spring-service2.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"
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">
<import resource="spring-dao.xml"/>
<context:component-scan base-package="com.imooc.os.service.impl1"/>
<!--事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
</beans>
spring-dao.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"
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">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/os?userUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<context:component-scan base-package="com.imooc.os.dao"/>
</beans>
Spring声明式事务处理-基于拦截器方式 案例:
OrderServiceImpl.java
package com.imooc.os.service.impl2;
import com.imooc.os.dao.OrderDao;
import com.imooc.os.dao.ProductDao;
import com.imooc.os.entity.Order;
import com.imooc.os.entity.Product;
import com.imooc.os.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.Date;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private ProductDao productDao;
@Autowired
private TransactionTemplate transactionTemplate;
public void addOrder(final Order order) {
order.setCreateTime(new Date());
order.setStatus("待付款");
orderDao.insert(order);
Product product = productDao.select(order.getProductsId());
product.setStock(product.getStock() - order.getNumber());
productDao.update(product);
}
}
spring-service3.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"
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">
<import resource="spring-dao.xml"/>
<!--事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="orderServiceTarget" class="com.imooc.os.service.impl3.OrderServiceImpl"/>
<!--事务拦截器-->
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<!--定义事务的属性、事务传播行为-->
<property name="transactionAttributes">
<props>
<!--key为目标对象的方法名 readOnly:只读-->
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="search*">PROPAGATION_REQUIRED,readOnly</prop>
<!--对于除了查询以外的那些会变动更新的方法,需要取消只读属性-->
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!--创建代理类 将上面定义的两个bean关联起来-->
<bean id="orderService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="orderServiceTarget"/>
<property name="interceptorNames">
<!--list中可以定义多个idref 也就是多个拦截器-->
<list>
<idref bean="transactionInterceptor"/>
</list>
</property>
</bean>
</beans>
spring-dao.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"
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">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/os?userUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<context:component-scan base-package="com.imooc.os.dao"/>
</beans>
上方的xml代码里,要编写transactionInterceptor和orderService这两个bean可能代码有点长了。所以Spring将这两个bean进行了简化。使用TransactionProxyFactoryBean这个类便可以把以上两个bean的代码放进去,所以只需配置一个bean,简化了代码。
<?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"
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">
<import resource="spring-dao.xml"/>
<!--事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="orderServiceTarget" class="com.imooc.os.service.impl3.OrderServiceImpl"/>
<bean id="orderService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<!--key为目标对象的方法名 readOnly:只读-->
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="search*">PROPAGATION_REQUIRED,readOnly</prop>
<!--对于除了查询以外的那些会变动更新的方法,需要取消只读属性-->
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
<property name="target" ref="orderServiceTarget"/>
</bean>
如下图,第一种和第二种方式只是为了让大家了解Spring事务处理的思路。而第三第四种才是实际开发中常用到的事务处理方式。
下面将开始讲解第三和第四种方式。
Spring事务处理-基于tx命名空间:
基于tx/aop配置切面增强事务
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--导包-->
<import resource="spring-dao.xml"/>
<!--自动扫描-->
<context:component-scan base-package="com.imooc.os.service.impl1"/>
<!--事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--指定事务的属性-->
<tx:attributes>
<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
<tx:method name="find*" propagation="REQUIRED" read-only="true"/>
<tx:method name="search*" propagation="REQUIRED" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--AOP增强 配置-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.imooc.os.service.impl.*.*(..))"/>
<!--事务通知-->s
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
spring-dao.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"
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">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/os?userUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<context:component-scan base-package="com.imooc.os.dao"/>
</beans>
Spring事务处理-基于注解方式:
OrderServiceImpl.java
package com.imooc.os.service.impl6;
import com.imooc.os.dao.OrderDao;
import com.imooc.os.dao.ProductDao;
import com.imooc.os.entity.Order;
import com.imooc.os.entity.Product;
import com.imooc.os.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private ProductDao productDao;
@Transactional(propagation = Propagation.REQUIRED)
public void addOrder(Order order) {
order.setCreateTime(new Date());
order.setStatus("待付款");
orderDao.insert(order);
Product product = productDao.select(order.getProductsId());
product.setStock(product.getStock() - order.getNumber());
productDao.update(product);
}
}
spring-service.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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--导包-->
<import resource="spring-dao.xml"/>
<!--自动扫描-->
<context:component-scan base-package="com.imooc.os.service.impl6"/>
<!--事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
spring-dao.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"
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">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/os?userUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<context:component-scan base-package="com.imooc.os.dao"/>
</beans>