文章目录

一、今日内容

1、jdbcTemplate的使用
2、spring的事务控制
3、了解spring5的新特性

二、jdbcTemplate的使用

1、jdbcTemplate的介绍

jdbc — dbutils – jdbcTemplate(spring 提供) – mybatis(主流) – spring data jpa(趋势)

2、数据源配置

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring331
jdbc.username=root
jdbc.password=root

applicationContext.xml (引入spring-context才有提示)

<!--引入外部属性文件 引入spring-context才有提示-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>

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="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="user" value="${jdbc.username}"></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="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

c. spring jdbc 自带数据源,包含了JdbcTemplate对象

依赖

<!--spring自带数据源 "spring的jar都引入同一个版本 不能混用"-->
<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.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

3、jdbcTemplate的CRUD

可以发现,jdbcTemplate和dbutils没有太大区别,一类jdbcAPI,同类:配置、写法和作业功能几乎完全雷同

queryRunner(datasource)---->提供API操作数据
jdbcTemplate.set(datasource)---->提供API操作数据库

spring_day04_jdbcTemplate、事务控制、5新特性_sql

Account.java

public class Account {
private Integer id;
private String name;
private Float money;
//省略get/set...
}

AccountRowMapper.java

jdbcTemplate.query(sql, new AccountRowMapper());
jdbcTemplate.queryForObject(sql, new AccountRowMapper(), 1);
模板查询集合或者单个对象时指定返回值类型为确定javaBean对象时需要实现接口的类

public class AccountRowMapper implements RowMapper<Account> {

/**
* @param rs 结果集对象,只包含一行数据
* @param rowNum
* @return
* @throws SQLException
*/
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getFloat("money"));
return account;
}
}

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">
<!--引入外部属性文件 引入spring-context才有提示-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!--配置c3p0数据源-->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

<!--dbcp数据源-->
<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.username}"></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.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

<!--各数据源就名字上有点区别 用起来没有任何区别-->

<!--创建jdbcTemplate模板对象 和dbutils的queryRunner一样 一种jdbc工具类-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="springDataSource"></property>
</bean>
<!--可以直接用jdbcTemplate操作数据库 test类相当于daoimpl类-->


</beans>

TestJdbcTemplate.java

package cn.ahpu;

import cn.ahpu.domain.Account;
import cn.ahpu.mapper.AccountRowMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
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;

/**
* 查询:query
* 增删改:update
*
* queryForList:查询返回list<map>集合
* query(sql,属性与列的映射对象,参数):返回值:list<pojo>
* queryForObject :针对于返回一个对象
* update(sql ,参数):执行增删改操作
*
* @author 寒面银枪
* @create 2020-02-19 0:36
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {

@Autowired
JdbcTemplate jdbcTemplate;

@Test
public void testFindAll(){
String sql="select * from account";
/*List<Map<String, Object>> accountList = jdbcTemplate.queryForList(sql);
for (Map<String, Object> map : accountList) {
System.out.println(map);
}*/
/*{id=1, name=aaa, money=800.0}
{id=2, name=bbb, money=1100.0}
{id=3, name=ccc, money=1000.0}
{id=4, name=hhh, money=999.99}*/

//需要自己写个类AccountRowMapper实现RowMapper接口
List<Account> accountList = jdbcTemplate.query(sql, new AccountRowMapper());
for (Account account : accountList) {
System.out.println(account);
}
}

@Test
public void testFindById(){
String sql="select * from account where id=?";
/*List<Account> accountList = jdbcTemplate.query(sql, new AccountRowMapper(), 1);
System.out.println(accountList.size()==1?accountList.get(0):"结果为空");*/

//直接查询单个对象 查询为空会报错 掌握自己写映射这种方式 ★★★★★★★★★★★★★★★★★重点掌握此法
Account account = jdbcTemplate.queryForObject(sql, new AccountRowMapper(), 1);
System.out.println(account);

//查询单个对象方式2:参数"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,"zangsan",10000);
}

@Test
public void testUpdate(){
String sql="update account set name=?,money=? where id=?";
jdbcTemplate.update(sql,"lisi",5000,5);
}

@Test
public void testDelete(){
String sql="delete from account where id=?";
jdbcTemplate.update(sql,5);
}

@Test
public void testGetTotalCount(){
String sql="select count(*) from account";
//参数(sql语句,返回值类型)
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(count);
}
}

4、在dao中使用jdbcTemplate方法一 (好用★)

a、applicationContext.xml中创建模板

<!--创建jdbcTemplate模板对象 和dbutils的queryRunner一样 一种jdbc工具类-->
<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;

@Override
public List<Account> findAll() {
String sql="select * from account";
List<Account> accountList = jdbcTemplate.query(sql, new AccountRowMapper());
return accountList;
}
}

5、在dao中使用jdbcTemplate方法二 (麻烦 少用)

a、在dao实现类中继承接口JdbcDaoSupport类

getJdbcTemplate直接获取jdbc模板

public class AccountDaoImpl2 extends JdbcDaoSupport implements AccountDao {

@Override
public List<Account> findAll() {
String sql="select * from account";
List<Account> accountList = getJdbcTemplate().query(sql, new AccountRowMapper());
return accountList;
}
}

b、所有的dao对象需要在xml中创建,需要通过set方法(属性)注入数据源对象

因为setDataSource方法写在父类中,无法框架本身的代码中加个@Autowired注解(注解方法创建dao的话只能注解注入属性),因此只能在xml中注入属性值

<!--继承jdbcDaoSupport 提供该类数据源即可 由该类直接创建模板类-->
<!--子类继承父类属性 直接往accountDaoImpl2中注入即可-->
<bean id="accountDao" class="cn.ahpu.dao.impl.AccountDaoImpl2">
<property name="dataSource" ref="springDataSource"></property>
</bean>

第一种在 Dao 类中定义 JdbcTemplate 的方式,适用于所有配置方式( xml 和注解都可以)。
第二种让 Dao 继承> JdbcDaoSupport 的方式,只能用于基于 XML 的方式,注解用不了。

三、Spring 中的事务控制

1、spring事务控制的api介绍–事务管理类

真正管理事务的对象
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用 SpringJDBC 或 iBatis 进行持久化数据时使用
org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate 版本进行持久化数据时使用

2、事务的特性

a、事务的四个特性
原子性: 不可再分割
隔离性: 事务之间的隔离级别
一致性: 要么全部完成,要么全部不完成
持久性: 一旦提交持久化到数据中

b、隔离级别
1、读未提交:read uncommited
产生的问题:脏读,幻读,不可重复读
脏读:读到了未提交的数据

不可重复读:
幻读(虚读):
2、读已提交:read commited
产生的问题:幻读,不可重复读
解决的问题:脏读
3、重复读: repeatable read
产生的问题:幻读
解决的问题:脏读,不可重复读
4、串行化(序列化): 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. 引入依赖:
    pom.xml
<!--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>
<!--spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--spring自带数据源 包含事务管理类-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>

2) 编写配置文件
applicationContext.xml

<!--创建事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源:数据存在于某次数据库连接中-连接存在于连接池(数据源)中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>

<!--事务的增强:过滤方法是否需要拦截
advice 增强 注意名称空间是tx不是cache
有些查询类方法没有事务也行 配置一下提高效率
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--增强:方法的过滤-->
<tx:attributes>
<!--指定需要拦截的方法
isolation:隔离级别,一般选择默认的
propagation:传播的行为
read-only:是否为只读事务 查询只读 增删改非只读
timeout="-1" 用不超时
-->
<!--<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"/>-->
<!--<!–查询 name用通配符 和模糊查询一样 以find开头的都匹配–>-->
<!--<tx:method name="find*" propagation="SUPPORTS" read-only="true" />-->
<!--<tx:method name="select*" propagation="SUPPORTS" read-only="true" />-->
<!--<tx:method name="query*" propagation="SUPPORTS" read-only="true" />-->
<!--<tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>-->

<!--配置方式二-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="select*" 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:通知关联对象(本质上是transactionManager txAdvice对其进行了进一步封装加强)
pointcut:切入点
-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.ahpu.service.impl.*.*(..))"></aop:advisor>
</aop:config>
  1. 业务层
    业务层不需要任何事务管理,只需要提供业务代码即可

spring_day04_jdbcTemplate、事务控制、5新特性_数据源_02


spring_day04_jdbcTemplate、事务控制、5新特性_数据源_03

6、基于注解的配置(重点 一样傻瓜式简单)

a. 引入依赖

与xml完全一致

b. 配置文件

<!--创建事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源:数据存在于某次数据库连接中-连接存在于连接池(数据源)中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>

<!--关联事务管理器对象-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

<!--开启aop的注解:自动代理 或许不需要-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

c. 业务层

在类上标记注解:@Transactional, 类中所有的方法都会使用事务管理
在方法上标记注解:@Transactional:只有该方法按照事务执行

d.属性介绍(类似配置文件方式的txAdvice的配置)

@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly =
false,timeout = -1)

事务处理注解方式简单总结:3个配置,一个注解 ,万事大吉。
也有人喜欢xml方式:不需要每次到业务层加@Transactional了
事务处理xml方式简单总结:3个配置,万事大吉