目录

  • 一、Spring对持久层技术支持

    • 1、Spring支持的持久层技术
    • 2、Spring JDBC
      • 2.1、 JDBCTemplate类
      • 2.2、Spring JDBC CRUD操作
      • 2.3、Spring提供的JdbcDaoSupport
  • 二、Spring中的事务控制
    • 1、引出事务
    • 2、事务回顾
    • 3、Spring对事务的管理API
    • 4、事务传播规则

Spring系列

  1. Spring — Spring简介、入门、配置 , IoC和DI思想
  2. Spring — IoC核心(基于XML)、DI核心(基于XML)
  3. Spring — 使用IoC和DI模拟注册案例、注解配置IoC和DI
  4. Spring — 静态代理、动态代理、拦截器思想
  5. Spring — AOP思想、AOP开发、Pointcut语法、注解配置AOP
  6. Spring — DAO层、Spring JDBC、Spring事务控制
  7. Spring — XML配置事务、注解+XML、纯注解的配置方式
  8. Spring整合MyBatis
  9. Spring Java Config — 组件注册相关注解
  10. Spring Java Config — 常用注解

一、Spring对持久层技术支持

跳转到目录
很多持久层技术,单独使用的话,操作API会很麻烦, 有了Spring的支持,操作起来就会很简单; 更强大的是: Spring提供了对事务的支持

1、Spring支持的持久层技术

跳转到目录
Spring自身并没有提供持久层框架,但是提供了和持久层技术无缝整合的API;

2、Spring JDBC

跳转到目录

2.1、 JDBCTemplate类

跳转到目录

  • 使用方法和QueryRunner基本一致
  • 构造方法传递数据源DataSource对象
  • API方法
    • update(String sql, Object…obj)执行insert,update,delete语句
    • queryForObject(String sql,RowMapper mapper,Object…obj)查询返回单个对象
    • queryForObject(String sql,Class cla,Object…obj)查询返回单个对象,基本类型及其包装类和字符串
    • query(String sql,RowMapper mapper,Object…obj)查询返回集合对象
  • RowMapper接口实现类BeanPropertyRowMapper,查询的结果集封装,适用单个对象或者集合
2.2、Spring JDBC CRUD操作

跳转到目录
数据库

CREATE TABLE `employee` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(40) NOT NULL,`age` int(11) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

Java代码

// Dao包
public interface EmployeeDao1 {void save(Employee1 emp);void update(Employee1 emp);void delete(Long id);Employee1 get(Long id);List<Employee1> listAll();
}public class EmployeeDaoImpl1 implements EmployeeDao1 {// =================核心===================private JdbcTemplate jdbcTemplate;// 属性: dataSource (通过setter注入ds的值)public void setDataSourse(DataSource ds) {this.jdbcTemplate = new JdbcTemplate(ds);}// =================核心===================@SuppressWarnings("unchecked")public void save(Employee1 emp) {String sql = "INSERT INTO employee (name, age) VALUES (?, ?)";jdbcTemplate.update(sql, emp.getName(), emp.getAge());/*// 包含了通过名称占位符的模板方法,简化开发;适合?比较多的情况NamedParameterJdbcTemplate namedParameterJdbcTemplate = null;namedParameterJdbcTemplate.update("INSERT INTO employee (name,age) VALUES (:ename,:eage)", new HashMap(){{this.put("ename", emp.getName());this.put("eage", emp.getAge());}});*/}public void update(Employee1 emp) {String sql = "UPDATE employee SET name = ?, age = ? WHERE id = ?";jdbcTemplate.update(sql, emp.getName(), emp.getAge(), emp.getId());}public void delete(Long id) {String sql = "DELETE from employee WHERE id = ?";jdbcTemplate.update(sql, id);}public Employee1 get(Long id) {List<Employee1> list = jdbcTemplate.query("SELECT id, name, age FROM employee WHERE id = ?", new Object[]{id},new RowMapper<Employee1>() {public Employee1 mapRow(ResultSet resultSet, int i) throws SQLException {Employee1 emp = new Employee1();emp.setId(resultSet.getLong("id"));emp.setName(resultSet.getString("name"));emp.setAge(resultSet.getInt("age"));return emp;}});return list.size() == 1 ? list.get(0) : null;/*Employee1 employee1 = jdbcTemplate.queryForObject("SELECT id, name, age FROM employee WHERE id = ?",new Object[]{id},new RowMapper<Employee1>() {public Employee1 mapRow(ResultSet rs, int i) throws SQLException {Employee1 emp = new Employee1();emp.setId(rs.getLong("id"));emp.setName(rs.getString("name"));emp.setAge(rs.getInt("age"));return emp;}});return employee1;*/}public List<Employee1> listAll() {return jdbcTemplate.query("SELECT id, name, age FROM employee", new RowMapper<Employee1>() {// 把每一行的结果集映射成一个Employee对象public Employee1 mapRow(ResultSet resultSet, int i) throws SQLException {Employee1 emp = new Employee1();emp.setId(resultSet.getLong("id"));emp.setName(resultSet.getString("name"));emp.setAge(resultSet.getInt("age"));return emp;}});}
}

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 https://www.springframework.org/schema/context/spring-context.xsd"><!--从classpath的根路径去加载db.properties文件--><context:property-placeholder location="classpath:db.properties"/><!--配置一个druid的连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"init-method="init" 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="${jdbc.initialSize}"/></bean><!--配置DAO--><bean id="employeeDaoImpl1" class="com.sunny.dao.impl.EmployeeDaoImpl1"><!--属性是setDataSourse方法--><property name="dataSourse" ref="dataSource"/></bean></beans>

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringJdbcTest {@Autowired //表示将xml中创建的dao对象,注入到下面的变量中private EmployeeDao1 dao;@Testpublic void testSave(){Employee1 emp = new Employee1();emp.setName("文");emp.setAge(21);dao.save(emp);}@Testpublic void testUpdate(){Employee1 emp = new Employee1();emp.setName("西门吹风");emp.setAge(43);emp.setId(10L);dao.update(emp);}@Testpublic void testDelete(){dao.delete(11L);}@Testpublic void testGet(){Employee1 employee1 = dao.get(10L);System.out.println(employee1);}@Testpublic void testListAll(){List<Employee1> employee1s = dao.listAll();for (Employee1 employee1 : employee1s) {System.out.println(employee1);}}
}

通过查询操作,在代码中没有迭代结果集的操作;那么是谁在迭代结果集并且设置属性后,又是谁把创建出来的每一个Employee对象放到List集合中去的呢?

2.3、Spring提供的JdbcDaoSupport

跳转到目录


JdbcDaoSupport类中定义了JdbcTemplate类并提供了get方法,我们dao层类直接继承即可,自己无需在声明JdbcTemplate对象;

public class EmployeeDaoImpl1ByJdbcTemplate extends JdbcDaoSupport implements EmployeeDao1 {public void save(Employee1 emp) {String sql = "INSERT INTO employee (name, age) VALUES (?, ?)";super.getJdbcTemplate().update(sql, emp.getName(), emp.getAge());}

xml

 <bean id="employeeDaoImpl1ByJdbcTemplate" class="com.sunny.dao.impl.EmployeeDaoImpl1ByJdbcTemplate"><property name="dataSource" ref="dataSource"/></bean>

Dao的bean元素依然要配置dataSource属性

二、Spring中的事务控制

跳转到目录

看这篇: https://www.yuque.com/baiyunhecanggou/qnvllg/gw00gg#6a6a86f6

1、引出事务

跳转到目录
银行转账案例: 需求: 从id为10086账户给id为10010账户转1000元


  • 数据库表设计略;
  • Java代码
// domain包
@Data
public class Account {private Long id;private int balance;
}// Dao包
public interface AccountDao {/*** 从指定账户转出多少钱* @param outId* @param money*/void transOut(Long outId, int money);/*** 从指定账户转入多少钱* @param inId* @param money*/void transIn(Long inId, int money);
}
public class AccountDaoImpl implements AccountDao {private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource ds){this.jdbcTemplate = new JdbcTemplate(ds);}public void transOut(Long outId, int money) {String sql = "UPDATE account SET balance = balance - ? WHERE id = ?";jdbcTemplate.update(sql, money, outId);}public void transIn(Long inId, int money) {String sql = "UPDATE account SET balance = balance + ? WHERE id = ?";jdbcTemplate.update(sql, money, inId);}
}
// Service包
public interface AccountService {/*** 从指定账户转出指定金额给另一个账户* @param outId* @param inId* @param money*/void trans(Long outId, Long inId, int money);
}
public class AccountServiceImpl implements AccountService {private AccountDao dao;public void setDao(AccountDao dao) {this.dao = dao;}public void trans(Long outId, Long inId, int money) {dao.transOut(outId, money);int a = 1 / 0; // 抛出异常dao.transIn(inId, money);}
}

xml文件

    <context:property-placeholder location="classpath:db.properties"/><!--配置一个druid的连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"init-method="init" 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="${jdbc.initialSize}"/></bean><!--配置dao--><bean id = "accountDao" class="com.sunny.dao.impl.AccountDaoImpl"><property name="dataSource" ref="dataSource"/></bean><!--配置service--><bean id="accountService" class="com.sunny.service.impl.AccountServiceImpl"><property name="dao" ref="accountDao"/></bean>

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTxTest {@Autowiredprivate AccountService service;@Testpublic void test(){service.trans(10086L, 10010L, 1000);}
}

结果


分析原因:

出现问题的原因:
转入和转出的操作不在同一个事务之中;

2、事务回顾

跳转到目录
Spring的事务机制是Spring给我们提供的一套事务管理的方式,项目中我们就不需要手动去控制事务了

编程式事务:我们的事务控制逻辑(增强逻辑)和业务逻辑混合在一起,比如我们之前的tcf模式控制转账事务,这种方式就叫做编程式事务

声明式事务:通过配置,在不侵犯原有业务逻辑代码的基础上就添加了事务控制功能,这种方式叫做声明式事务(我们这里的Spring声明式事务控制就是通过AOP达到这个目的的)

关于事务

事务(Transaction,简写为tx):在数据库中,事务是指一组逻辑操作,不论成功与失败都作为一个整体进行工作,要么全部执行成功,要么全部不执行(执行失败)。

  • 事务基本特性(ACID,是针对单个事务的一个完美状态)

    • 原子性:一个事务内的操作,要么都成功,要么都失败。很经典的例子:转账,汇款和收款要成功都成功,要失败都失败。
    • 一致性:指的是数据的一致性,和原子性其实是一件事情,只不过描述的角度不一样,原子性是从事务的操作的角度,一致性是从数据的角度来描述的,比如转账之前(1000,1000),如果转账100,那么数据状态应该是(900、1100),不应该出现中间状态(900,1000)或者(1000,1100)
    • 隔离性:事务并发的时候,比如事务1做的动作给员工涨工资2000块,但是此时事务还没有提交,事务2去查询工资发现工资多了2000块,这就是脏读。解决方法就是建立事务之间的隔离机制。
    • 持久性:事务一旦提交,事务提交,变化即生效。即使数据库服务器宕机,那么恢复之后,数据也应该是事务提交之后的状态,不应该回滚到以前了。
  • 事务并发问题

    • 脏读

      财务人员今天心情不好,状态不好,误操作发起事务1给员工张三本月涨了1w块钱工资,但是还没有提交事务

      张三发起事务2,查询当月工资,发现多了1W块钱,涨工资了,财务人员发现不对劲,把操作撤回,把涨工资的事务1给回滚了

    • 幻读(幻读出现在增加insert和删除delete的时候)

      • 比如事务1查询工资表中工资为1w的员工的个数(10个员工),此时事务1还没有结束
      • 正在这个时候,事务2,人力部门有两个新员工入职,他们的工资也是1w,人力部门通过事务2向工资表插入了两条记录,并且提交事务了
      • 这个时候,事务1又去查询工资为1w的员工个数,发现多了两个员工(12个人),见鬼了,这种情况就叫做幻读
    • 不可重复读(出现在修改update的时候)

      • 员工发起事务1查询工资,工资为1w,事务1尚未关闭
      • 人力部门发起事务2给你涨了工资,涨工资到1.2W(update你的工资表的字段信息),并且提交了事务了。
      • 此时,事务1又再次查询自己的工资,发现工资为1.2W,原有的1w这个数据已经读不到了,这就叫做不可重复读
  • 事务隔离级别(解决是事务并发问题的)
    由低到高分别为 Read uncommitted > Read committed > Repeatable read > Serializable 。

    • Read_uncommited (读未提交),就好比十字路口没有红绿灯一样,效率高,但是风险也高,此时什么事务控制都没有。不要使用这种模式

    • Read_commited (读已提交),顾名思义,其他事务提交之后,才能读取到这个事务提交的数据,这种模式能解决脏读(因为脏读事务没提交造成的)问题,解决不了幻读和不可重复读(因为这两个问题的产生就是insert delete update的时候提交了事务造成)

    • Repeatable_Read (可重复读),可重复读解决脏读和不可重复读

    • Serializable (可序化):所有的事务一个个来,不争不抢,一个事务处理完了,另外一个事务继续进行,这样不会出现并发问题。比如ATM机

      MySQL数据库默认隔离级别可重复读Repeatable_Read
      Oracle数据库默认级别读已提交Read_commited

  • 设置事务隔离级别

    • 1 read uncommitted 未提交读,脏读,不可重复读,虚读都可能发生.
    • 2 read committed 已提交读,避免脏读,但是不可重复读和虚读有可能发生(Oracle默认)
    • 4 repeatable read 可重复读,避免脏读,不可重复读,但是虚读有可能发生(MySql默认)
    • 8 serializable 串行化的,避免脏读,不可重复读,虚读的发生
    • 查看当前的事务隔离级别:SELECT @@TX_ISOLATION;
    • 更改当前的事务隔离级别:SET TRANSACTION ISOLATION LEVEL 四个级别之一

    隔离级别越高,数据库事务并发执行性能会越差,在项目中为了考虑并发性能一般使用 Read committed(读已提交),它能避免丢失更新和脏读,但是不可重复读和幻读不可避免。更多情况下使用悲观锁或乐观锁来解决。

3、Spring对事务的管理API (底层AOP->动态代理)

跳转到目录
Spring 支持编程式事务和声明式事务管理两种方式,这里学习声明式事务管理。

Spring 声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

1、Spring 事务管理主要有3个接口:

  • TransactionDefinition接口:

    在Spring中,事务是通过TransactionDefinition接口来定义的,该接口包含与事务属性相关的方法,TransactionDefinition定义了五个表示隔离级别的常量,代表传播行为的常量,在TransactionDefinition中以int值表示超时时间。

  • PlatformTransactionManager接口:

    Platform TransactionManager.getInstance()方法返回一个Transaction Status对象,返回的Transaction Status对象可能代表一个新的或已经存在的事务(如果当前调用堆栈中有一个符合条件的事务)。

  • Transaction Status接口:封装了一些控制事务查询和执行的方法。

2、使用Spring管理事务时,首先需要告诉Spring使用哪一个事务管理器
常用的事务管理器:

  • DataSourceTransactionManager:支持JDBC和MyBatis
  • HiberbateTransactionManager:支持Hibernate

3、Spring 事务回滚规则
指示Spring 事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。

默认配置下,spring只有在抛出的异常为运行时异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。

4、事务传播规则

跳转到目录
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播规则的常量:

情况一: 需要/遵从当前事务

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

情况二: 不遵从当前事务

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:不管当前是否存在事务,都会新开启一个事务,必须是一个新的事务;
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

情况三: 寄生事务

  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于- TransactionDefinition.PROPAGATION_REQUIRED。
事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

事务类型

1)本地事务和分布式事务
本地事务:就是普通事务,能保证单台数据库上操作的ACID,被限定在一台数据库上。
分布式事务:涉及多个数据库源的事务,即跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成的),分布式事务旨在保证这些本地事务的所有操作的ACID,是事务可以跨越多台数据库。

2)JDBC事务和JTA事务
JDBC事务:就是数据库事务类型中的本地事务,通过 Connection对象的控制来管理事务。
JTA事务:JTA(Java Transaction API)是Java EE数据库事务规范,JTA只提供了事务管理接口,由应用程序服务器厂商提供实现,JTA事务比JDBC更强大,支持分布式事务。

3)通过编程实现事务可分为编程式事务和声明式事务
编程式事务:通过编写代码来管理事务
声明式事务:通过注解或XML配置来管理事务

Spring——DAO层、Spring JDBC、Spring事务控制相关推荐

  1. spring dao层注解_Spring– DAO和服务层

    spring dao层注解 欢迎来到Spring教程的第三部分. 在这一部分中,我们将继续编写Timesheet应用程序,这次我们将实现DAO层,业务服务并编写一些测试. 在上一部分中,我们定义了Ge ...

  2. php mysql 嫦娥,介绍MySQL和JDBC的事务控制(TCL)

    文章目录一.MySQL的事务控制(Transaction Control Language) (1)事务的特性(ACID) (2)MySQL的事务控制 (3)mysql事务演示 二.JDBC的事务控制 ...

  3. 【Spring】spring基于注解的声明式事务控制

    结构 domin package com.itheima.domain;import java.io.Serializable;public class Account implements Seri ...

  4. 【spring】spring基于xml的声明式事务控制

    结构 domain package com.itheima.domain;import java.io.Serializable;public class Account implements Ser ...

  5. spring基于注解的声明式事务控制

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  6. spring基于XML的声明式事务控制-配置步骤

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  7. Dao层系列-4-Hibernate Spring Annotation

    2019独角兽企业重金招聘Python工程师标准>>> 之前几篇文章主要是介绍 Hibernate.Hibernate Annotation.Hibernate Spring 集成 ...

  8. Spring基于 XML 的声明式事务控制(配置方式)

    一.引入依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http ...

  9. Spring-学习笔记10【Spring事务控制】

    Java后端 学习路线 笔记汇总表[黑马程序员] Spring-学习笔记01[Spring框架简介][day01] Spring-学习笔记02[程序间耦合] Spring-学习笔记03[Spring的 ...

最新文章

  1. CPSR和SPSR(转)
  2. 如何修改Linux主机名
  3. 【小白学习keras教程】三、Kears中常见模型层Padding、Conv2D、MaxPooling2D、Flatten和Dense
  4. 2016年第七届蓝桥杯 - 国赛 - Java大学C组 - I. 路径之谜
  5. java中的Static、final、Static final各种用法
  6. dfs深度优先搜索_图的深度优先搜索(DFS)
  7. 准考证打印系统关闭怎么办_2021国家公务员考试准考证打印系统关闭了怎么办...
  8. newifimini出厂固件_newifi 新路由 mini用哪个Pandora固件
  9. EPLAN中的edz文件的用法
  10. BUG计算机术语,程序员bug什么意思
  11. 347,猜数字大小 II
  12. 润乾报表统计图各属性功能介绍
  13. YiGo学习(一)YiGo介绍
  14. 谈 DevOps 自动化时,也应该考虑到 SOX 等法案
  15. (六)R语言生物序列比对——Needleman-Wunsch全局比对算法
  16. 深度分析:一次Wi-Fi入侵实录(1)
  17. 荣耀note10无缘鸿蒙,赵明确认荣耀NOTE10 真机参数疑似全曝光!
  18. MySQL 亿级数据的迁移、清洗、与审计分析
  19. 想成为小说家?这款儿童键盘让我的创作欲爆棚!
  20. 装mdac2.8 注意 安装如何提示 出现致命错误。不支持此操作系统

热门文章

  1. 数据库课程设计个人总结
  2. 有很多事情要做,究竟怎么确定执行顺序?
  3. linkedList手写
  4. E - Selling Souvenirs(动态规划 + 贪心 + 思维(缩小时间复杂度和空间复杂度))
  5. WebCam snap应用实例
  6. 计算机专业对身体伤害大,电脑对人体的三大伤害
  7. 利用python渲染IPsec配置
  8. 图片翻译成中文怎么弄?分享三个图片翻译小技巧
  9. 百度地图 - 自定义ECharts覆盖物
  10. 工作中如何做好技术积累『转载-保持学习的空杯心态』