JdbcTemplate
一 概念
1. Spring提供的一个持久化框架(模板),其核心对象JdbcTemplate(类似之前使用的DBUtils)
二 JdbcTemplate使用基本步骤
1. 导入jar包(5[spring核心]+3[jdbcTemplate]+2[德鲁伊+数据库驱动])
2. 配置文件
* 编写db.properties(属性文件)
* applicationContext.xml
- 加载外部属性文件
- 装配德鲁伊数据源
- 装配JdbcTemplate对象
3. 使用JdbcTemplate中的核心方法,进行DRUD操作
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"
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">
<context:property-placeholder location="classpath:db.properties"/>
<!-- 组件扫描-->
<context:component-scan base-package="com.atguigu.spring"/>
<!-- 加载外部属性文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- 装配数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
<!-- 装配JdbcTemplate对象
javaWweb
Databaseutil->getConnection() closeConnection() [数据源]
BaseDao->CRUD通用方法
-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--指定数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
三 JdbcTemplate的持久化方法(CRUD)
1. update()
* 增删改的通用方法,常用构造方法如下:
- update(String sql,Object... args)
2. batchUpdate()
* 批处理增删改的通用方法,常用构造方法如下:
- batchUpdate(String sql,List<Object[]> batchArgs)
3. queryForObject():获取单个数据的通用方法,常用其中两种形式
* 获取单个对象(bean)
- queryForObject(String sql,RowMapper rm):查询单个对象,无参数
RowMapper<T> rm = new BeanPropertyRowMapper<>(T.class);
- queryForObject(String sql,RowMapper rm,Object... args):查询单个对象,有参数
* 获取当个数值(int count,double price)
- queryForObject(String sql,Class type):查询单个数值,无参数
- queryForObject(String sql,Class type,Object... args):查询单个数值,有参数
4. query()
* 查询多个bean对象的通用方法,常用其构造方法如下:
- query(String sql,RowMapper rm):查询多个bean对象,无参数
- query(String sql,RowMapper rm,Object... args):查询多个bean对象,有参数
package com.atguigu.spring.test;
import com.atguigu.spring.bean.Dept;
import com.atguigu.spring.dao.DeptDao;
import com.atguigu.spring.dao.DeptDaoImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.ArrayList;
import java.util.List;
/**
* @author Study hard Java's Tang
* @create 2020-03-11 9:36
*/
public class Demo {
/**
获取jdbcTemplate对象
*/
//获取容器对象
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext_jdbcTemplate.xml");
//从容器对象中,获取jdbcTemplate对象
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
@Test
public void testGetJdbcTemplate(){
System.out.println("jdbcTemplate = " + jdbcTemplate);
}
/**
测试update()方法,添加dept
*/
@Test
public void testUpdate(){
//写sql语句
String sql = "insert into depts(name) value(?) ";
jdbcTemplate.update(sql, "气氛组");
}
/**
测试批量处理,新增
*/
@Test
public void testBatchUpdate(){
String sql = "insert into depts(name) value(?)";
List<Object[]> list = new ArrayList<>();
list.add(new Object[]{"拆迁队"});
list.add(new Object[]{"砸钱队"});
jdbcTemplate.batchUpdate(sql, list);
}
/**
测试queryForObject,查询单个值
*/
@Test
public void testQuerySingleValue(){
String sql = "select count(*) from depts";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("数据库部门数量count = " + count);
}
/**
测试queryForObject,查询单个bean对象
* BeanPropertyRowMapper
* 将数据库查询结果集,与javaBean中的属性关联映射
*/
@Test
public void testQuerySingleBean(){
String sql = "select id,name from depts where id = ?";
RowMapper<Dept> rm = new BeanPropertyRowMapper<>(Dept.class);
Dept detp = jdbcTemplate.queryForObject(sql, rm, 1);
System.out.println(detp);
}
/**
测试query()方法,查询多个bean对象
*/
@Test
public void testQueryObjects(){
String sql = "select id,name deptName from depts";
RowMapper<Dept> rm = new BeanPropertyRowMapper<>(Dept.class);
List<Dept> query = jdbcTemplate.query(sql, rm);
for (Dept detp : query) {
System.out.println(detp);
}
}
/**
测试Dao(jdbcTemplate)
*/
@Test
public void testDao(){
DeptDao deptDao = context.getBean("deptDaoImpl", DeptDaoImpl.class);
Dept deptById = deptDao.getDeptById(1);
System.out.println("deptById = " + deptById);
}
}
四 使用JdbcTemplate实现Dao模式
Spring声明式事务管理
一 回顾事务基础知识
* 事务四大特性(ACID)
- 原子性(atomicity):不可再分
- 一致性(consistency):满足业务规则的一致性状态,要么同时提交,要么通知回滚
- 隔离性(isolation):并发执行过程中不会互相干扰
- 持久性(durability):对数据的更改,永久的保存
二 Spring中支持的事务管理
applicationContext_tx.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.atguigu.spring"/>
<!-- 加载外部属性文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- 装配数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
<!-- 装配JdbcTemplate对象
javaWweb
Databaseutil->getConnection() closeConnection() [数据源]
BaseDao->CRUD通用方法
-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--指定数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 装配数据源事务管理器:DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启用事务注解-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
</beans>
1 javaweb阶段事务管理不够优化 -> 书城项目 -> 去结账 -> (1. 生成订单2.生成订单详情3.减库存)
2 Spring编程式事务管理
①获取数据库连接Connection对象
②取消事务的自动提交
③执行操作
④正常完成操作时手动提交事务
⑤执行失败时回滚事务
⑥关闭相关资源
* 问题:业务代码与事务管理代码相耦合,代码较分散&混乱。
* 解决:使用AOP框架AspectJ,将事务管理代码(非核心业务代码)先提取到切面,再动态通知到核心业务 代码中。
注意:这种管理事务的方式,称之为声明式事务管理。
3 Spring声明式事务管理(推荐使用)
* demo演示问题:当余额不足时,余额修改失败,但库存修改成功(违背了事务的一致性&原子性..)
* 使用spring声明式事务管理(解决)步骤
1. 加载外部属性文件
2. 装配德鲁伊数据源:DruidDataSource
3. 装配数据源事务管理器:DataSourceTransactionManager
4. 启用事务注解
<tx:annotation-driver transaction-manager="" >
5. 在需要事务的业务上,添加注解:@Transactional
4 事务属性
* propagation(事务传播行为),默认值:Propagation.REQUIRED
* 一个事务(a)调用另一个事务(b)时,此时当前事务如何使用事务机制。
* 属性值
Propagation.REQUIRED:如果当前存在事务,就使用当前事务。
如果当前不存在事务,就新建一个事务,去使用。
Propagation.REQUIRES_NEW:无论当前是否存在事务,都必须创建新事物,去使用。
如果当前存在事务,会将当前事务挂起。
之后,等待新建事务执行结束后,再执行挂起事务。
* isolation(事务隔离级别),默认值:-1(没有设置隔离级别)
* 读未提交(1),存在问题:脏读
* 读已提交(2),存在问题:不可重复读(建议使用)
* 可重复读(4),存在问题:幻读(建议使用)
* 串行化(8),存在问题:效率极低。
* readOnly(事务只读)
* true:查询功能时使用
* false:默认值
* timeout(事务超时)
默认值:-1,不设置超时时间
可以设置int毫秒数,在指定毫秒后,强制事务回滚。(释放事务资源)
* rollbackFor| rollBackForClassName| noRollbackFor| noRollbackForClassName(事务异常回滚|事务异常不回滚)
- 默认:事务遇到runtimeException&Error时回滚,遇到编译时异常不回滚。
/**
* 去结账
* @param username
* @param isbns:书编号的集合
*/
@Transactional
@Override
public void checkOut(String username, List<String> isbns) {
/**
* 业务:余额能买几本书,就允许顾客买几本。
*/
for (String isbn : isbns) {
bookService.purchase(username, isbn);
}
}
/**
* 买书->查询book价格->修改库存->修改余额
* @param username
* @param isbn
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void purchase(String username, String isbn) {
//查询book价格
Integer price = bookDao.findBookPriceByIsbn(isbn);
//修改库存
bookDao.updateBookStock(isbn);
//修改余额
bookDao.updateUserAccount(username,price);
}
三 基于xml方式配置声明式事务
<!-- 配置事务切面 -->
<aop:config>
<aop:pointcut expression="execution(* com.atguigu.tx.component.service.BookShopServiceImpl.purchase(..))"
id="txPointCut"/>
<!-- 将切入点表达式和事务属性配置关联到一起 -->
<aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/>
</aop:config>
<!-- 配置基于XML的声明式事务 -->
<tx:advice id="myTx" transaction-manager="transactionManager">
<tx:attributes>
<!-- 设置具体方法的事务属性 -->
<tx:method name="find*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="purchase"
isolation="READ_COMMITTED"
no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"
propagation="REQUIRES_NEW"
read-only="false"
timeout="10"/>
</tx:attributes>
</tx:advice>