一、今日目标
1、jdbcTemplate的使用
2、spring的事务控制
3、了解spring5的新特性
持久层中程序:
package com.itheima.dao.impl;
import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.utils.ConnectionUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;
/**
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
ConnectionUtil connectionUtil;
@Autowired
QueryRunner queryRunner;
@Override
public Account findByName(String name) {
String sql = "select * from account where name = ?";
try { //含数据库配置
return queryRunner.query(connectionUtil.getThreadConnection(),sql ,new BeanHandler<>(Account.class),name);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void update(Account account) {
String sql = "update account set money = ? where name = ?";
try {
queryRunner.update(connectionUtil.getThreadConnection(),sql ,account.getMoney() ,account.getName());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
=======================================================================================
二、jdbcTemplate的使用
1、jdbcTemplate的介绍
jdbc --- dbutils -- jdbcTemplate(spring 提供) -- mybatis(主流) -- spring data jpa(趋势)
2、数据源配置
a. c3p0数据源
<!--c3p0数据源--> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency>
<!--配置c3p0数据源-->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
b. dbcp 数据源
<!--dbcp数据源--> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency>
<!--dbcp数据源-->
<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
c. spring jdbc 自带数据源,包含了JdbcTemplate对象,包含事务管理类
<!--spring自带数据源--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency>
<!--spring自带的数据源-->
<bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
创建JdbcTemplate模板对象
<!--创建jdbcTemplate模板对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="springDataSource"></property> </bean>
3、jdbcTemplate的CRUD
/**
* 查询使用query
* 增删改:update
*
* queryForList :查询返回一个List集合,Map集合
* query(sql ,属性与列的映射对象,参数):返回值:List<pojo>
* queryForObject :针对于返回一个对象
* update(sql ,参数):执行增删改操作
*
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
package com.itheima;
import com.itheima.domain.Account;
import com.itheima.mapper.AccountRowMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
import java.util.Map;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {
@Autowired
JdbcTemplate jdbcTemplate;
@Test
public void testFindAll(){
String sql = "select * from account";
// 方法返回值是List集合,list中是map、集合
List<Map<String, Object>> accountList = jdbcTemplate.queryForList(sql);
for (Map<String, Object> map : accountList) {
System.out.println(map);
}
List<Account> accountList1 = jdbcTemplate.query(sql, new AccountRowMapper());
for (Account account : accountList1) {
System.out.println(account);
}
}
@Test
public void testFindById(){
String sql = "select * from account where id = ?";
// List<Account> accountList = jdbcTemplate.query(sql, new AccountRowMapper(), 5);
// System.out.println(accountList.size() == 1 ?accountList.get(0):"结果为null");
// queryForObject(SQL语句,列与属性的映射, 参数):掌握
Account account = jdbcTemplate.queryForObject(sql, new AccountRowMapper(), 3);
System.out.println(account);
// queryForObject(sql语句,参数列表(必须是数组类型), jdbcTemplate 自带映射:必须保证属性和列名一致)
// Account account = jdbcTemplate.queryForObject(sql, new Object[]{1}, new BeanPropertyRowMapper<>(Account.class));
// System.out.println(account);
}
@Test
public void testSave(){
String sql = "insert into account values(null , ? ,?)";
jdbcTemplate.update(sql ,"zhangsan", 10000);
}
@Test
public void testUpdate(){
String sql = "update account set money = ?, name = ? where id = ?";
jdbcTemplate.update(sql ,1000,"lisi",4);
}
@Test
public void testDel(){
String sql = "delete from account where id = ?";
jdbcTemplate.update(sql , 4);
}
@Test
public void testGetTotalCount(){
String sql = "select count(*) from account";
// queryForObject(sql语句, 返回值类型)
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(count);
}
}
4、在dao中使用jdbcTemplate方法一
a. applicationContext.xml
<!--创建jdbcTemplate模板对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="springDataSource"></property> </bean>
b.在dao层中使用模板对象
@Repository public class AccountDaoImpl implements AccountDao { @Autowired JdbcTemplate jdbcTemplate;
在dao中使用jdbcTemplate方法
5、在dao中使用jdbcTemplate方法二
a. 在dao实现类中继承接口JdbcDaoSupport类
public class AccountDaoImpl2 extends JdbcDaoSupport implements AccountDao {
b.所有的dao对象需要在xml中创建,需要通过set方法注入数据源对象
<!--创建dao层对象--> <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl2"> <!--通过set方法注入数据源--> <property name="dataSource" ref="springDataSource"></property> </bean>
spring的事务控制
什么是事务:事务是逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
参考:什么是事务
https://www.runoob.com/mysql/mysql-transaction.html
1、spring事务控制的api介绍--事务管理类
org.springframework.orm.hibernate5.HibernateTransactionManager: 在hibernate环境下使用
org.springframework.jdbc.datasource.DataSourceTransactionManager: 在jdbcTemplate,mybatis(ibatis)环境下使用
2、事务的特性
a、事务的四个特性
原子性: 不可再分割
隔离性: 事务之间的隔离级别
一致性: 要么全部完成,要么全部不完成
持久性: 一旦提交持久化到数据中
说一下 ACID 是什么?
- Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
- Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
- Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
b、隔离级别
SQL 标准定义的四个隔离级别为:
Read Uncommitted(读未提交)、Read Committed(读已提交)、 Repeatable Read(可重复读)、 Serializable(可串行化)
读未提交:read uncommited
产生的问题:脏读,不可重复读,幻读
脏读:读到了未提交的数据
不可重复读:在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据(数据内容不同)
一种更易理解的说法是:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据并修改数据。那么,在第一个事务的两次读数据之间。由于另一个事务的修改,那么第一个事务两次读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
幻读(虚读):数据条数不同
脏读:事务A读到了事务B未提交的数据。
不可重复读:事务A第一次查询得到一行记录row1,事务B提交修改后,事务A第二次查询得到row1,但列内容发生了变化。
幻读:事务A第一次查询得到一行记录row1,事务B提交修改后,事务A第二次查询得到两行记录row1和row2。
参考:幻读和不可重复读的区别
读已提交:read commited
产生的问题:不可重复读,幻读
解决的问题:脏读
重复读: repeatable read
产生的问题:幻读
解决的问题:脏读,不可重复读
串行化(序列化): serializable
产生的问题:null
解决的问题: 所有的问题
隔离级别最高,效率最低
c、数据库的支持的隔离级别 -- 一般情况下选择都是默认的隔离级别
mysql:支持:read uncommited read commited repeatable read serializable 支持三个隔离级别
默认的隔离级别:repeatable read
Oracle支持:read commited serializable read only(只读)
默认的隔离级别:read commited
3、事务的传播
a. 掌握
REQUIRED: 必要的: 如果没有事务,则新建一个事务,如果有事务,加入这个事务当中, spring指定为默认值
增删改:(需要事务,没有的话需要新建)
SUPPORTS: 支持的: 如果没有事务,非事务执行,如果有事务,加入这个事务当中
查询(查询不需要事务不是必须的)
b. 了解
MANDATORY: 可以使用当前的事务,如果没有事务,抛出异常
REQUERS_NEW: 新建一个事务,如果存在事务,则挂起事务
NOT_SUPPORTED: 必须非事务执行,如果有事务,则挂起事务
NEVER: 非事务执行,如果存在事务,抛出异常
NESTED: 有事务,嵌套执行,没有事务,执行REQUIRED
4、是否为只读的事务
a.如果是查询,则为只读的事务 readOnly=true
b.如果是增删改,则为非只读的事务,readOnly=false
=============================================================================
5、基于xml声明式事务管理(配置文件)(重点)(推荐)
a. 编程式事务管理:在业务层写了事务技术代码(不好)
b. 声明式事务管理:在配置文件声明事务对象,管理事务,业务层中没有任何事务代码
1) 引入依赖(aspectjweaver(aop)、spring-tx(事务管理)):
<!--spring自带数据源--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!--aop的切面配置需要的jar包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <!--spring 的事务管理jar包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency>
2) 编写配置文件
<!--注入数据源属性:事务存在连接中- 连接存在连接池(数据源)中--> 把dataSource 注入 txManager
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置c3p0数据源-->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--dbcp数据源-->
<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--spring自带的数据源-->
<bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--创建jdbcTemplate模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="springDataSource"></property>
</bean>
<!--开启注解, 扫描包-->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!--创建事务管理器对象-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源属性:事务存在连接中- 连接存在连接池(数据源)中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
<!--事务的增强: 过滤方法是否需要拦截
id:唯一的标识
transaction-manager:指定事务管理器
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--增强:方法的过滤-->
<tx:attributes>
<!--指定需要拦截的方法
isolation:隔离级别, 一般选择默认的
propagation:传播的行为,
read-only: 是否为只读的事务,增删改:非只读事务 read-only=false
查询:只读的事务:read-only=true
find* :通配符配置,只要以 find开头即可
-->
<!--配置方式一:-->
<!--增删改-->
<!--<tx:method name="insert*" />-->
<!--<tx:method name="add*" />-->
<!--<tx:method name="update*" />-->
<!--<tx:method name="del*" />-->
<!--<tx:method name="delete*" />-->
<!--<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>-->
<!--查询-->
<!--<tx:method name="find*" propagation="SUPPORTS" read-only="true" ></tx:method>-->
<!--<tx:method name="select*" propagation="SUPPORTS" read-only="true" ></tx:method>-->
<!--<tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>-->
<!--<tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"></tx:method>-->
<!--配置方式二-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>
<!--其他方法的配置方法 增删改不能只读-->
<tx:method name="*" propagation="REQUIRED" read-only="false"></tx:method>
</tx:attributes>
</tx:advice>
<!--aop的配置:
切面= 切入点 +通知(增强)
-->
<aop:config>
<!--切面配置
advice-ref: 通知关联对象
pointcut: 切入点:
-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:advisor>
</aop:config>
</beans>
其中设置了事务的隔离级别、事务的传播,事务是否为可读。
3) 业务层
业务层不需要任何事务管理,只需要提供业务代码即可
===========================================================================================
6、基于注解的配置(重点)
a. 引入依赖
与xml完全一致
b. 配置文件
1)配置事务管理类
<!--创建事务管理器对象-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源属性:事务存在连接中- 连接存在连接池(数据源)中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
2) 开启事务的注解管理
<!--transaction-manager: 关联事务管理器对象-->
<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven><!--开启aop的注解:自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--spring自带的数据源-->
<bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--创建jdbcTemplate模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="springDataSource"></property>
</bean>
<!--开启注解,扫描包-->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!--创建事务管理器对象-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源属性:事务存在连接中- 连接存在连接池(数据源)中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
<!--transaction-manager: 关联事务管理器对象-->
<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>
<!--开启aop的注解:自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
c. 业务层
在类上标记注解:@Transactional, 类中所有的方法都会使用事务管理
在方法上标记注解:@Transactional:只有该方法按照事务执行
d.属性介绍
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false,timeout = -1)