第五节 事务[注解方式]
什么要使用连接池?
连接数据库5步骤:
1. 加载驱动类
2.获取连接
3.创建执行SQL语句的对象[Statement和PrepareStatement(预处理方式,问号的索引值是从1开始)]
4.获取结果集对象
5.关闭资源[有序的关闭]
连接池就是预先建立好一些连接,放置到一个容器当中,当你使用的时候,从改缓存池当中取,用完之后需要归还
[如何去自己实现连接池?]
都是使用开源的连接池!
----针对于JDBC[驱动类,访问路径,用户名,密码]
当你去使用别人的项目或者你要把项目发布到服务器端时候,需要注意一下几个问题:
版本!
如果有疑问请查看: 博客文档
还有如果是一个web项目,注意修改为自己的Tomcat版本
----------------------------------------
<?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:aop="http://www.springframework.org/schema/aop" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd"> <!-- 启动Spring注解 --> <context:annotation-config/> <!-- 扫描 --> <context:component-scan base-package="com.shxt"/> <!-- 加载外部的属性文件 --> <context:property-placeholder location="classpath:/jdbc.properties" /> <!-- 配置数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" />
<!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="3" /> <!-- 最大空闲时,当经过一个高峰之后,连接池可以将一些用不到的连接释放,一直减少到maxIdle为止 --> <property name="minIdle" value="5" /> <!-- 连接池的最大值 --> <property name="maxActive" value="20" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" />
</bean> <!-- 使用JDBC连接数据库,进行测试 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean>
</beans>
|
测试的项目,通过模拟情况进行了解!
项目经理,进行调研:
1.买书,一次有且只能买一本书,不支持现金,都是通过书店发账号--充值,问买书的过程需要完成那些组成?
{书属性: ISBN-价钱} {库存: ISBN-数量} {用户: 账号--金钱}
A.通过你的账号,查询你的余额是多少?
B.库存 减少
C. 余额需要减去价格
数据访问层:
package com.shxt.dao;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository // --数据访问层,使用注解@Repository ,默认的ID为: bookShopDaoImpl public class BookShopDaoImpl implements IBookShopDao { @Autowired // private JdbcTemplate jdbcTemplate;
/** * 通过书的编号获取书的价格 */ @Override public int findBookPriceByIsbn(String isbn) { String sql = "SELECT price FROM tx_book WHERE isbn = ?"; return jdbcTemplate.queryForObject(sql, Integer.class, isbn); } /** * 更新库存 * @throws RuntimeException */ @Override public void updateBookStock(String isbn) throws RuntimeException { String sql = "UPDATE tx_book_stock SET stock = stock -1 WHERE isbn = ?"; jdbcTemplate.update(sql, isbn); } /** * 更新用户卡里面的余额 * @throws RuntimeException */ @Override public void updateUserAccount(String account, int price) throws RuntimeException { //简单判断 String sql = "UPDATE tx_user SET balance = balance - ? WHERE account = ?"; jdbcTemplate.update(sql, price, account); }
} |
业务逻辑层
@Service // public class BookShopServiceImpl implements IBookShopService { private IBookShopDao bookShopDao; @Autowired public void setBookShopDao(IBookShopDao bookShopDao) { this.bookShopDao = bookShopDao; }
/** * 模拟买书的业务包含3步骤 */ public void purchase(String account, String isbn) throws RuntimeException { // 1.通过ISBN查询价格 int price = this.bookShopDao.findBookPriceByIsbn(isbn); // 2.更新库存 this.bookShopDao.updateBookStock(isbn); // 3.更新余额 this.bookShopDao.updateUserAccount(account, price); }
} |
修改DAO层代码: [加入简单的判断,使用的runtimeException]
package com.shxt.dao; @Repository // public class BookShopDaoImpl implements IBookShopDao { @Autowired // private JdbcTemplate jdbcTemplate;
/** * 通过书的编号获取书的价格 */ @Override public int findBookPriceByIsbn(String isbn) { String sql = "SELECT price FROM tx_book WHERE isbn = ?"; return jdbcTemplate.queryForObject(sql, Integer.class, isbn); } /** * 更新库存 * @throws RuntimeException */ @Override public void updateBookStock(String isbn) throws RuntimeException { String sql = "select stock from tx_book_stock where isbn = ?"; Integer stock = jdbcTemplate.queryForObject(sql, Integer.class, isbn); if(stock==0){ throw new RuntimeException("库存不足,请等待....."); } sql = "UPDATE tx_book_stock SET stock = stock -1 WHERE isbn = ?"; jdbcTemplate.update(sql, isbn); } /** * 更新用户卡里面的余额 * @throws RuntimeException */ @Override public void updateUserAccount(String account, int price) throws RuntimeException { //简单判断 String sql = "select balance from tx_user where account = ?"; Integer balance = jdbcTemplate.queryForObject(sql, Integer.class, account); if(balance<price){ throw new RuntimeException("您卡内余额不足,请充值"); } sql = "UPDATE tx_user SET balance = balance - ? WHERE account = ?"; jdbcTemplate.update(sql, price, account); }
} |
这样的判断我们还是无法解决买书中出现的问题,这样我们就需要告诉我们的程序,这个方法或者这个类下的方法是一个事务方法,它需要使用事务管理器对该方法进行事务管理,确保数据的完整性和一致性
<!-- 1.配置事务管理器 完成数据的完整性和一致性 MyBatis 应使用JDBC的事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
<!-- 启动事务注解,可以标注在类或者方法上 --> <tx:annotation-driven transaction-manager="transactionManager" /> |
package com.shxt.service;
@Service //补充说明,这默认id是什么( ) public class BookShopServiceImpl implements IBookShopService { private IBookShopDao bookShopDao;
@Autowired public void setBookShopDao(IBookShopDao bookShopDao) { this.bookShopDao = bookShopDao; }
/** * 模拟买书的业务包含3步骤 什么是事务? * 事务[买书业务]逻辑由多个动作组成了一个工作单元[买书的业务],3步骤组成,有一个步骤错了,那么应该都不起作用 * ACID概念 必须会 * @throws Exception * * */ @Transactional //告诉上面的事务管理器,我这个方法是事务方法 public void purchase(String account, String isbn) throws RuntimeException { // 1.通过ISBN查询价格 int price = this.bookShopDao.findBookPriceByIsbn(isbn); // 2.更新库存 this.bookShopDao.updateBookStock(isbn); // 3.更新余额 this.bookShopDao.updateUserAccount(account, price); }
}
|
注: 如果使用try…catch进行捕获的话,应该如何处理,请补充答案[ ]
需要掌握的属性有: noRollbackFor RollbackFor readonly等属性,不需要要会
如果买两本书: 模拟情况,我们需要测试事务的传播性
买两本书的业务代码如下:
package com.shxt.service;
@Service public class BookMoreServiceImpl implements IBookMoreService { @Autowired private IBookShopService bookShopService;
@Override @Transactional(propagation=Propagation.REQUIRED)//标注为事务方法,这个传播性为默认值 public void playMore(String account, String... isbns) { for (String isbn : isbns) { bookShopService.purchase(account, isbn); }
}
} |
当一个方法[purchase]被另一个事务方法[playMore]调用{有标注注解哟!}的时候,默认有延续功能,如果所示
|
package com.shxt.service;
@Service // public class BookShopServiceImpl implements IBookShopService { private IBookShopDao bookShopDao;
@Autowired public void setBookShopDao(IBookShopDao bookShopDao) { this.bookShopDao = bookShopDao; }
/** * 注意这里没有标注该方法为事务方法 * */
public void purchase(String account, String isbn) throws RuntimeException { // 1.通过ISBN查询价格 int price = this.bookShopDao.findBookPriceByIsbn(isbn); // 2.更新库存 this.bookShopDao.updateBookStock(isbn); // 3.更新余额 this.bookShopDao.updateUserAccount(account, price); }
}
|
另一种情况描述:
package com.shxt.service;
@Service public class BookMoreServiceImpl implements IBookMoreService { @Autowired private IBookShopService bookShopService;
@Override @Transactional(propagation=Propagation.REQUIRED)//标注为事务方法,这个传播性为默认值 public void playMore(String account, String... isbns) { for (String isbn : isbns) { bookShopService.purchase(account, isbn); }
}
} |
|
package com.shxt.service;
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 com.shxt.dao.IBookShopDao;
@Service // public class BookShopServiceImpl implements IBookShopService { private IBookShopDao bookShopDao;
@Autowired public void setBookShopDao(IBookShopDao bookShopDao) { this.bookShopDao = bookShopDao; }
/** * 模拟买书的业务包含3步骤 什么是事务?业务逻辑 由多个工作的任务,组成了一个工作单元 买书的业务,3步骤组成,有一个步骤错了,不起作用 * ACID概念 * * @throws RuntimeException * * */ @Transactional(propagation=Propagation.REQUIRES_NEW) public void purchase(String account, String isbn) throws RuntimeException { // 1.通过ISBN查询价格 int price = this.bookShopDao.findBookPriceByIsbn(isbn); // 2.更新库存 this.bookShopDao.updateBookStock(isbn); // 3.更新余额 this.bookShopDao.updateUserAccount(account, price); }
}
|
关于这段文字的描述: 请参考PPT的详细内容和Spring的官方文档,很有收获!慢慢看,耐住性子的看!