四? 事务
?
?????????事务的传播行为:
?
?
?代码部分
项目结构:
?Spring基于注解的事务:
spring.tx.dao.BookShopDao
?spring.tx.dao.impl.BookShopDaoImpl
//spring.tx.dao.BookShopDao
public interface BookShopDao {
//根据图书的编号获取图书的价格
Double getBookPrice(String isbn);
//根据图书的编号更新图书的库存,默认一次只能买一本书
void updateBookStock(String isbn);
//根据用户的id和图书的价格更新账户余额
void updateUserAccount(int userId ,Double bookPrice);
}
//spring.tx.dao.impl.BookShopDaoImpl
@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Double getBookPrice(String isbn) {
String sql="select price from book where isbn=?";
Double bookPrice = jdbcTemplate.queryForObject(sql, Double.class, isbn);
return bookPrice;
}
@Override
public void updateBookStock(String isbn) {
String sql="update book set stock=stock-1 where isbn = ?";
jdbcTemplate.update(sql,isbn);
}
@Override
public void updateUserAccount(int userId, Double bookPrice) {
String sql="update user set balance =balance - ? where user_id = ?";
jdbcTemplate.update(sql,bookPrice,userId);
}
}
spring.tx.service.BookShopService
public interface BookShopService {
//购买图书
void purchase(int userId,String isbn);
}
?spring.tx.service.impl.BookShopServiceImpl
//如果这个注解加到类上,那么这个类中的所有方法都添加上事务
//@Transactional
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
/*
@Transactional注解中的属性:
1、propagation属性;用来设置事务的传播行没
事务的传播行为:一个事务方法((开启了事务的方法)运行在另一个事务方法中时,当前方法是
使用原来的事务还是开启一个新的事务。
propagation属性值说明:
propagation = Propagation.REQUIRED:默认值,使用原来的事务
Propagation.REQUIRES NEW:将原来的事务挂起,开启一个新的事务
2、isolation属性:用来设置事务的隔离级别
isolation属性值说明:
READ_UNCOMMITTED:读未提交。会引起脏读,不可重复读,幻读
READ_COMMITTED:读已提交(Oracle默认)。会引起不可重复读,幻读
REPEATABLE_READ:可重复读(MySql默认)。会引起幻读
SERIALIZABLE:串行化
3、设置异常回滚不回滚相关属性
rollbackFor:设置那些异常回滚,值是异常的类型
rollbackForClassName:设置那些异常回滚,值是异常的名字
norollbackFor:设置那些异常不回滚,值是异常的类型
norollbackForClassName:设置那些不异常回滚,值是异常的名字
4.timeout属性:用来设置事务超时的时间,单位是秒,超过改时间将自动回滚
5.readOnly属性:只读属性。只读事务属性可以设置为true代表这个事务只读取数据但不更新数据,这样可以帮助数据库引秀优化事务
*/
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.SERIALIZABLE,
timeout = 3,readOnly = false)
@Override
public void purchase(int userId, String isbn) {
Double bookPrice = bookShopDao.getBookPrice(isbn);
bookShopDao.updateBookStock(isbn);
bookShopDao.updateUserAccount(userId,bookPrice);
}
}
?spring.tx.service.Cashier
spring.tx.service.impl.CashierImpl
//?spring.tx.service.Cashier
public interface Cashier {
//去结账,一次买多本书
void checkout(int userId, List<String> isbn);
}
//spring.tx.service.impl.CashierImpl
@Service("cashier")
public class CashierImpl implements Cashier {
@Autowired
private BookShopService bookShopService;
@Transactional()
@Override
public void checkout(int userId, List<String> isbns) {
for (String isbn : isbns) {
bookShopService.purchase(userId,isbn);
}
}
}
spring.tx.test.TransactionTest
public class TransactionTest {
ApplicationContext ioc=new ClassPathXmlApplicationContext("beans-tx.xml");
/*
测试bookShopDao中的方法
*/
@Test
public void testBookShopDao(){
BookShopDao bookShopDao= (BookShopDao) ioc.getBean("bookShopDao");
//获取图书价格
Double bookPrice = bookShopDao.getBookPrice("1001");
System.out.println(bookPrice);
//测试更新图书的库存方法
bookShopDao.updateBookStock("1001");
//测试更新用户余额的方法
bookShopDao.updateUserAccount(1,bookPrice);
}
/*
测试BookShopService中的方法
*/
@Test
public void testBookShopService(){
//必须是接口类型,因为spring事务是使用aop实现的,代理类实现这个接口,返回的对象也就是这个接口类型,不能使用接口的实现类去接收接口的类型
BookShopService bookShopService= (BookShopService) ioc.getBean("bookShopService");
bookShopService.purchase(1,"1001");
}
/*
测试Cashier中的购买多本图书的方法
*/
@Test
public void testCashier(){
Cashier cashier= (Cashier) ioc.getBean("cashier");
List<String> isbns=new ArrayList<>();
isbns.add("1001");
isbns.add("1002");
cashier.checkout(1,isbns);
}
}
beans-tx.xml
<!-- 配置自动扫描的包-->
<c:component-scan base-package="com.atguigu.spring.tx"/>
<!-- 引入外部属性文件-->
<c:property-placeholder location="classpath:druid.properties"/>
<!-- 配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<!-- 配置JdbcTemplate-->
<bean id="jdbcTemplate" 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>
<!-- 开启事务注解支持
如果事务管理器的id值是transaqrionManager,那么transaction-manager属性可以省略;
如果事务管理器的id值不是transactionManager,那么必须显示指定该属性值
-->
<tx:annotation-driven/>
<!-- <tx:annotation-driven transaction-manager="transactionManager"/>-->
druid.properties
jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/spring_transaction
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.initialSize=5
jdbc.maxActive=10
?MySql
?
?lib资源:
??spring资源直接去spring下lib文件夹找即可。
|