spring_day04_jdbcTemplate、事务控制、5新特性

一、今日内容

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操作数据库

在这里插入图片描述

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*" />&lt;!&ndash;全部都是默认配置 不需要写 下面留着方便看&ndash;&gt;-->
            <!--<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>-->
            <!--&lt;!&ndash;查询 name用通配符 和模糊查询一样 以find开头的都匹配&ndash;&gt;-->
            <!--<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. 业务层
    业务层不需要任何事务管理,只需要提供业务代码即可

在这里插入图片描述
在这里插入图片描述

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个配置,万事大吉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值