当前位置: 首页 > news >正文

Spring JdbcTemplate 和 事务

JdbcTemplate概述

JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。
在这里插入图片描述
按如下项目结构准备 maven jar项目即可
在这里插入图片描述
导入依赖

 <dependencies><!--spring核心容器包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.5</version></dependency><!--spring切面包--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.5</version></dependency><!--aop联盟包--><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><!--德鲁伊连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.22</version></dependency><!--springJDBC包--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.5</version></dependency><!--spring事务控制包--><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.3.5</version></dependency><!--spring orm 映射依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.3.5</version></dependency><!--Apache Commons日志包--><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><!--Junit单元测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version><scope>test</scope></dependency><!--lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency></dependencies>

准备JDBC.properties

jdbc_username=root
jdbc_password=root
jdbc_driver=com.mysql.cj.jdbc.Driver
jdbc_url=jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true

准备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:p="http://www.springframework.org/schema/p"xmlns:c="http://www.springframework.org/schema/c"xmlns:util="http://www.springframework.org/schema/util"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd
"><!--spring 注解扫描--><context:component-scan base-package="com.msb"/><!--读取jdbc配置文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--配置德鲁伊连接池--><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对象,并向里面注入DataSource--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--通过set方法注入连接池--><property name="dataSource" ref="dataSource"></property></bean>
</beans>

单个数据操作
准备实体类

package com.msb.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;@AllArgsConstructor
@NoArgsConstructor
@Data
public class Emp implements Serializable{private Integer empno;private String ename;private String job;private Integer mgr;private Date hiredate;private Double sal;private Double comm;private Integer deptno;
}

准备service层接口和实现类

package com.msb.service;
import com.msb.pojo.Emp;
import java.util.List;public interface EmpService {int findEmpCount();Emp findByEmpno(int empno);List<Emp> findByDeptno(int deptno);int  addEmp(Emp emp);int updateEmp(Emp emp);int deleteEmp( int empno);
}
package com.msb.service.impl;
import com.msb.dao.EmpDao;
import com.msb.pojo.Emp;
import com.msb.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;@Service
public class EmpServiceImpl implements EmpService {@Autowiredprivate EmpDao empDao;@Overridepublic int findEmpCount() {return empDao.findEmpCount();}@Overridepublic Emp findByEmpno(int empno) {return empDao.findByEmpno( empno);}@Overridepublic List<Emp> findByDeptno(int deptno) {return empDao.findByDeptno( deptno);}@Overridepublic int addEmp(Emp emp) {return empDao.addEmp(emp);}@Overridepublic int updateEmp(Emp emp) {return empDao.updateEmp(emp);}@Overridepublic int deleteEmp(int empno) {return empDao.deleteEmp(empno);}
}

准备dao层接口和实现类

package com.msb.dao;
import com.msb.pojo.Emp;
import java.util.List;public interface EmpDao {int findEmpCount();Emp findByEmpno(int empno);List<Emp> findByDeptno(int deptno);int addEmp(Emp emp);int updateEmp(Emp emp);int deleteEmp(int empno);
}
package com.msb.dao.impl;
import com.msb.dao.EmpDao;
import com.msb.pojo.Emp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;@Repository
public class EmpDaoImpl implements EmpDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic int findEmpCount() {/*查询员工个数* queryForObject 两个参数* 1 SQL语句* 2 返回值类型** */Integer empCount = jdbcTemplate.queryForObject("select count(1) from emp", Integer.class);return empCount;}@Overridepublic Emp findByEmpno(int empno) {/** 查询单个员工对象* queryForObject三个参数* 1 SQL语句* 2 RowMapper接口的实现类对象,用于执行返回的结果用哪个类来进行封装 ,实现类为BeanPropertyRowMapper* 3 SQL语句中需要的参数 (可变参数)* */BeanPropertyRowMapper<Emp> rowMapper =new BeanPropertyRowMapper<>(Emp.class);Emp emp = jdbcTemplate.queryForObject("select * from emp where empno =?", rowMapper, empno);return emp;}@Overridepublic List<Emp> findByDeptno(int deptno) {/** 查询单个员工对象* query三个参数* 1 SQL语句* 2 RowMapper接口的实现类对象,用于执行返回的结果用哪个类来进行封装 ,实现类为BeanPropertyRowMapper* 3 SQL语句中需要的参数 (可变参数)* */BeanPropertyRowMapper<Emp> rowMapper =new BeanPropertyRowMapper<>(Emp.class);List<Emp> emps = jdbcTemplate.query("select * from emp where deptno =?", rowMapper, deptno);return emps;}@Overridepublic int addEmp(Emp emp) {/*增删改* 统统用update方法 两个参数* 1 SQL语句* 2 SQL语句需要的参数 (可变参数)** */String sql ="insert into emp values(DEFAULT ,?,?,?,?,?,?,?)";Object[] args ={emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHiredate(),emp.getSal(),emp.getComm(),emp.getDeptno()};return jdbcTemplate.update(sql,args);}@Overridepublic int updateEmp(Emp emp) {String sql ="update emp set ename =? , job =?, mgr=? , hiredate =?, sal=?, comm=?, deptno =? where empno =?";Object[] args ={emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHiredate(),emp.getSal(),emp.getComm(),emp.getDeptno(),emp.getEmpno()};return jdbcTemplate.update(sql,args);}@Overridepublic int deleteEmp(int empno) {String sql ="delete  from emp where empno =?";return jdbcTemplate.update(sql, empno);}
}

测试代码

package com.msb.test;
import com.msb.pojo.Emp;
import com.msb.service.EmpService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;
import java.util.List;public class Test1 {@Testpublic void testEmpService(){ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");EmpService empService = context.getBean(EmpService.class);// 查询员工个数/*int empCount = empService.findEmpCount();System.out.println(empCount);*/// 根据员工编号查询员工对象/* Emp byEmpno = empService.findByEmpno(7521);System.out.println(byEmpno);*//*根据部门编号查询多个员工对象集合*//*List<Emp> emps = empService.findByDeptno(20);emps.forEach(System.out::println);*//*增加员工信息*//*int rows = empService.addEmp(new Emp(null, "TOM", "SALESMAN", 7521, new Date(), 2000.0, 100.0, 10));System.out.println(rows);*//*根据员工编号修改员工信息*//*int rows = empService.updateEmp(new Emp(7939, "JERRY", "MANAGER", 7839, new Date(), 3000.0, 0.0, 20));System.out.println(rows);*//*根据员工编号删除员工信息*//*int rows = empService.deleteEmp(7939);System.out.println(rows);*/}
}

批量操作
1 批量增加
2 批量修改
3 批量删除

实体类

package com.msb.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;@AllArgsConstructor
@NoArgsConstructor
@Data
public class Dept implements Serializable {private Integer deptno;private String dname;private String loc;
}

DeptService

package com.msb.service;
import com.msb.pojo.Dept;
import java.util.List;public interface DeptService {int[] deptBatchAdd(List<Dept> depts);int[] deptBatchUpdate(List<Dept> depts);int[] deptBatchDelete(List<Integer> deptnos);
}
package com.msb.service.impl;
import com.msb.dao.DeptDao;
import com.msb.pojo.Dept;
import com.msb.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;@Service
public class DeptServiceImpl implements DeptService {@Autowiredprivate DeptDao deptDao;@Overridepublic int[] deptBatchAdd(List<Dept> depts) {return deptDao.deptBatchAdd(depts);}@Overridepublic int[] deptBatchUpdate(List<Dept> depts) {return  deptDao.deptBatchUpdate(depts);}@Overridepublic int[] deptBatchDelete(List<Integer> deptnos) {return  deptDao.deptBatchDelete(deptnos);}
}

DeptDao

package com.msb.dao;
import com.msb.pojo.Dept;
import java.util.List;public interface DeptDao {int[] deptBatchAdd(List<Dept> depts);int[] deptBatchUpdate(List<Dept> depts);int[] deptBatchDelete(List<Integer> deptnos);
}
package com.msb.dao.impl;
import com.msb.dao.DeptDao;
import com.msb.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.LinkedList;
import java.util.List;@Repository
public class DeptDaoImpl implements DeptDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic int[] deptBatchAdd(List<Dept> depts) {String sql ="insert into dept values(DEFAULT,?,?)";List<Object[]> args =new LinkedList<>();for (Dept dept : depts) {Object[] arg ={dept.getDname(),dept.getLoc()};args.add(arg);}return jdbcTemplate.batchUpdate(sql, args);}@Overridepublic int[] deptBatchUpdate(List<Dept> depts) {String sql ="update dept set dname =? ,loc =? where deptno=?";List<Object[]> args =new LinkedList<>();for (Dept dept : depts) {Object[] arg ={dept.getDname(),dept.getLoc(),dept.getDeptno()};args.add(arg);}return jdbcTemplate.batchUpdate(sql, args);}@Overridepublic int[] deptBatchDelete(List<Integer> deptnos) {String sql ="delete from dept where deptno =?";List<Object[]> args =new LinkedList<>();for (Integer deptno : deptnos) {Object[] arg ={deptno};args.add(arg);}return jdbcTemplate.batchUpdate(sql, args);}
}

测试

package com.msb.test;
import com.msb.pojo.Dept;
import com.msb.service.DeptService;
import com.msb.service.EmpService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class Test2 {@Testpublic void testBatchAdd(){ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");DeptService deptService = context.getBean(DeptService.class);List<Dept> depts =new ArrayList<>();for (int i = 0; i < 10; i++) {depts.add(new Dept(null,"name"+i,"loc"+i));}int[] ints = deptService.deptBatchAdd(depts);System.out.println(Arrays.toString(ints));}@Testpublic void testBatchUpdate(){ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");DeptService deptService = context.getBean(DeptService.class);List<Dept> depts =new ArrayList<>();for (int i = 51; i <=60; i++) {depts.add(new Dept(i,"newname","newLoc"));}int[] ints = deptService.deptBatchUpdate(depts);System.out.println(Arrays.toString(ints));}@Testpublic void testBatchDelete(){ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");DeptService deptService = context.getBean(DeptService.class);List<Integer> deptnos =new ArrayList<>();for (int i = 51; i <=69; i++) {deptnos.add(i);}int[] ints = deptService.deptBatchDelete(deptnos);System.out.println(Arrays.toString(ints));}
}

事务

1.事务的概念

事务(Transaction)指的是一个操作序列,该操作序列中的多个操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

目前常用的存储引擎有InnoDB(MySQL5.5以后默认的存储引擎)和MyISAM(MySQL5.5之前默认的存储引擎),其中InnoDB支持事务处理机制,而MyISAM不支持。

2.事务的特性

事务处理可以确保除非事务性序列内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的序列,可以简化错误恢复并使应用程序更加可靠。

但并不是所有的操作序列都可以称为事务,这是因为一个操作序列要成为事务,必须满足事务的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这四个特性简称为ACID特性。
在这里插入图片描述

  1. 原子性

原子是自然界最小的颗粒,具有不可再分的特性。事务中的所有操作可以看做一个原子,事务是应用中不可再分的最小的逻辑执行体。

使用事务对数据进行修改的操作序列,要么全部执行,要么全不执行。通常,某个事务中的操作都具有共同的目标,并且是相互依赖的。如果数据库系统只执行这些操作中的一部分,则可能会破坏事务的总体目标,而原子性消除了系统只处理部分操作的可能性。

2)一致性

一致性是指事务执行的结果必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。

例如:在转账时,只有保证转出和转入的金额一致才能构成事务。也就是说事务发生前和发生后,数据的总额依然匹配。

  1. 隔离性

隔离性是指各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间既不能看到对方的中间状态,也不能相互影响。

例如:在转账时,只有当A账户中的转出和B账户中转入操作都执行成功后才能看到A账户中的金额减少以及B账户中的金额增多。并且其他的事务对于转账操作的事务是不能产生任何影响的。

4)持久性

持久性指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库,即使数据库出现故障,提交的数据也应该能够恢复。但如果是由于外部原因导致的数据库故障,如硬盘被损坏,那么之前提交的数据则有可能会丢失。

3.事务的并发问题

脏读(Dirty read)

当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
在这里插入图片描述
不可重复读

(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
在这里插入图片描述
幻读

(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

在这里插入图片描述
不可重复度和幻读区别:

不可重复读的重点是修改,幻读的重点在于新增或者删除。

解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。

例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读

4.事务的隔离级别

事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。

事务的隔离级别从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。
在这里插入图片描述
spring中可以使用如下方式实现事务的控制

1 编程式(不推荐)

2 声明式(掌握)

1)注解(简单,必会)

2)XML配置(繁琐,了解)

demo搭建
通过张三给李四转账案例演示事务的控制
1 数据库中准备表格
在这里插入图片描述
applicationContext.xml
jdbc.properties 如上边项目配置
项目中准备实体类

package com.msb.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;@AllArgsConstructor
@NoArgsConstructor
@Data
public class Account implements Serializable {private Integer id;private String name;private Integer money;
}

准备DAO层,创建一个根据id修改money的方法

package com.msb.dao;public interface AccountDao {int transMoney(int id,int money);
}
package com.msb.dao.impl;
import com.msb.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class AccountDaoImpl implements AccountDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic int transMoney(int id, int money) {String sql ="update account set money =money +? where id =?";return jdbcTemplate.update(sql,money,id);}
}

准备Service,创建一个转账的业务方法

package com.msb.service;public interface AccountService {int transMoney(int from ,int to,int money);
}
package com.msb.service.impl;
import com.msb.dao.AccountDao;
import com.msb.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Overridepublic int transMoney(int from, int to, int money) {int rows=0;rows+=accountDao.transMoney(from, 0 - money);       rows+=accountDao.transMoney(to, money);        return rows;}
}

测试代码,测试转账

package com.msb.test;
import com.msb.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestTx {@Test()public void testTransaction(){ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml");AccountService accountService = context.getBean(AccountService.class);int rows = accountService.transMoney(1, 2, 100);System.out.println(rows);}}

基于注解方式实现

Spring声明式事务的实现方式,底层就是AOP,AOP的底层就是动态代理

Spring事务管理相关的API

事务管理器接口: PlatformTransactionManager 针对不同的框架,提供了不同的实现类
在这里插入图片描述
注解方式实现事务控制
在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:p="http://www.springframework.org/schema/p"xmlns:c="http://www.springframework.org/schema/c"xmlns:util="http://www.springframework.org/schema/util"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd
"><!--spring 注解扫描--><context:component-scan base-package="com.msb"/><!--读取jdbc配置文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--配置德鲁伊连接池--><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对象,并向里面注入DataSource--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--通过set方法注入连接池--><property name="dataSource" ref="dataSource"></property></bean><!--配置一个事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--将数据源注入事务管理器--><property name="dataSource"  ref="dataSource"></property></bean><!--开启事务注解--><tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

在Service层中添加事务的注解

package com.msb.service.impl;
import com.msb.dao.AccountDao;
import com.msb.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
//@Transactional //加在类上,代表类中的所有方法都添加了事务控制
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Override@Transactional// 放在方法上,就是仅仅对当前方法增加了事务控制public int transMoney(int from, int to, int money) {int rows=0;rows+=accountDao.transMoney(from, 0 - money);int i =1/0;rows+=accountDao.transMoney(to, money);return rows;}
}

再次测试,就算是service方法运行出现异常,自动会回滚,如果没有,那么自动提交

@Transactional 注解的一些参数和参数的含义
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_UNCOMMITTED,readOnly = true,rollbackFor = ClassCastException.class,noRollbackFor = NullPointerException.class,timeout = 10)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {@AliasFor("transactionManager")String value() default "";@AliasFor("value")String transactionManager() default "";String[] label() default {};Propagation propagation() default Propagation.REQUIRED;Isolation isolation() default Isolation.DEFAULT;int timeout() default -1;String timeoutString() default "";boolean readOnly() default false;Class<? extends Throwable>[] rollbackFor() default {};String[] rollbackForClassName() default {};Class<? extends Throwable>[] noRollbackFor() default {};String[] noRollbackForClassName() default {};
}

propagation 事务的传播行为(面试)
多事务方法之间调用,事务是如何管理的
在这里插入图片描述
在这里插入图片描述
如果service层 add方法调用了 addDept和addEmp两个方法

PROPAGATION_REQUIRED
如果add方法有事务,那么addDept和addEmp就加入到add方法里的事务
如果add方法没有事务,那么就新建一个事务,将addDept和addEmp加入到这个新的事务中

PROPAGATION_REQUIRES_NEW
无论add是否有事务,都建立一个新的事务,所有的方法都加入到新的事务中,add原来的事务就不用了

isolation 事务的隔离级别

  1. DEFAULT (默认)
    这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
    MySQL默认REPEATABLE_READ
    Oracle默认READ_COMMITTED

  2. READ_UNCOMMITTED (读未提交)
    这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。

  3. READ_COMMITTED (读已提交)
    保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。

  4. REPEATABLE_READ (可重复读)
    这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。

  5. SERIALIZABLE(串行化)
    这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。

timeout 超时时间
事务一定要在多长时间之内提交,如果不提交就会回滚

readOnly 只读事务
事务是否只能读取数据库的数据,如果为true,则不允许进行增删改

rollbackFor 指定发生回滚的异常
当方法发生哪些异常时才会回滚

noRollbackFor 指定不发生回滚的异常
当方法发生哪些异常时,不会回滚

事务的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:p="http://www.springframework.org/schema/p"xmlns:c="http://www.springframework.org/schema/c"xmlns:util="http://www.springframework.org/schema/util"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd
"><context:component-scan base-package="com.msb"/><!--读取jdbc配置文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--配置德鲁伊连接池--><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对象,并向里面注入DataSource--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--通过set方法注入连接池--><property name="dataSource" ref="dataSource"></property></bean><!--配置一个事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--将数据源注入事务管理器--><property name="dataSource"  ref="dataSource"></property></bean><!--配置通知--><tx:advice id="txAdvice"><!--配置事务参数--><tx:attributes><tx:method name="transMoney" isolation="DEFAULT" propagation="REQUIRED"/></tx:attributes></tx:advice><!--配置AOP--><aop:config><!--配置切入点--><aop:pointcut id="pt" expression="execution(* com.msb.service.AccountService.transMoney(..))"/><!--配置切面--><aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor></aop:config>
</beans>

创建零配置文件事务(配置类配置)

package com.msb.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;@Configuration  // 配置类标志注解
@ComponentScan(basePackages = "com.msb") // spring包扫描
@PropertySource("classpath:jdbc.properties") // 读取属性配置文件
@EnableTransactionManagement // 开启事务注解
public class SpringConfig {@Value("${jdbc_driver}")private String driver;@Value("${jdbc_url}")private String url;@Value("${jdbc_username}")private String username;@Value("${jdbc_password}")private String password;/*创建数据库连接池*/@Beanpublic DruidDataSource getDruidDataSource(){DruidDataSource dataSource=new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}no/*创建JdbcTemplate对象*/@Beanpublic JdbcTemplate getJdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate=new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}/*创建事务管理器*/@Beanpublic PlatformTransactionManager getPlatformTransactionManager(DataSource dataSource){DataSourceTransactionManager transactionManager =new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;}
}

测试代码

    @Test()public void testTransaction3(){ApplicationContext context =new AnnotationConfigApplicationContext(SpringConfig.class);AccountService accountService = context.getBean(AccountService.class);int rows = accountService.transMoney(1, 2, 100);System.out.println(rows);}

附Spring测试组件

spring5框架自带了通用的日志封装,也可以整合自己的日志
1)spring移除了 LOG4jConfigListener,官方建议使用log4j2
2)spring5整合log4j2
导入log4j2依赖

        <!--log4j2 依赖--><!--<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version></dependency>--><!--slf4-impl 包含了log4j2 依赖 引入它即可--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version><scope>test</scope></dependency>

在resources目录下准备log4j2.xml的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" /></Console></Appenders><Loggers><Root level="debug"><AppenderRef ref="Console" /></Root></Loggers>
</Configuration>

spring5关于测试工具的支持

整合junit4
依赖的jar

        <!--Junit4单元测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version><scope>test</scope></dependency><!--spring test测试支持包--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.5</version><scope>test</scope></dependency>

测试代码编写方式

//省略了加载容器配置文件代码编写

package com.msb.test;
import com.msb.config.SpringConfig;
import com.msb.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)// 指定测试支持类
@ContextConfiguration("classpath:applicationContext.xml")// 指定核心配置文件位置
public class Test2 {@Autowired // 注入要获取的beanprivate  AccountService accountService;@Test()public void testTransaction(){int rows = accountService.transMoney(1, 2, 100);System.out.println(rows);}
}

整合junit5

依赖的jar

 <!--junit5单元测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope></dependency>

测试代码编写方式

package com.msb.test;
import com.msb.service.AccountService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;/*使用ExtentWith和ContextConfiguration注解*/
/*@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:applicationContext.xml")*/
// 使用复合注解代替前两个注解
@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class Test3 {@Autowired // 注入要获取的beanprivate  AccountService accountService;@Testpublic void testTransaction(){int rows = accountService.transMoney(1, 2, 100);System.out.println(rows);}
}

相关文章:

Spring JdbcTemplate 和 事务

JdbcTemplate概述 JdbcTemplate是spring框架中提供的一个对象&#xff0c;是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如&#xff1a;操作关系型数据的JdbcTemplate和&#xff0c;操作nosql数据库的RedisTemplate&#xff0c;操作消息…...

C/C++:程序环境和预处理/宏

程序的翻译环境和执行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境&#xff0c;它用于实际执行代码。 编译和链接 一份源代码(比如test.c)需要通过编译&#xf…...

什么是死锁?死锁产生的四个必要条件是啥?如何避免和预防死锁的产生?

点个关注&#xff0c;必回关 文章目录什么是死锁死锁产生的原因&#xff1a;1、系统资源的竞争2、进程运行推进顺序不当引起死锁产生死锁的四个必要条件&#xff1a;死锁的避免与预防什么是死锁 死锁是指两个或两个以上的线程在执行过程中&#xff0c;由于竞争资源或者由于彼此…...

工程管理系统源码-物料管理-工程项目管理系统-建筑施工管理软件

如今建筑行业竞争激烈&#xff0c;内卷严重&#xff0c;发展趋势呈现两极分化&#xff0c;中小微企业的生存空间被逐步压缩&#xff0c;利润逐年减少。事实证明&#xff0c;工地上粗放式的人管人管理模式已经落于时代&#xff0c;劳动力纠纷和物料浪费现象尤其普遍&#xff0c;…...

Roboguide与TIA V16通讯

软件需求:1. roboguide;2. TIA V16;3. KEPServer; 在之前的文章中介绍过KEPServer与TIA V16的通讯,此处不再介绍。接下来,介绍roboguide与KEPServer的仿真通讯。 创建一个roboguide项目。选择【外部设备】➡【添加外部设备】 选择【OPC Server】➡【OK】 OPC服务器名称命…...

利用PyTorch深度学习框架进行多元回归

目录前言数据加载数据转换模型定义模型训练模型评估模型保存与加载完整代码讨论参考文献前言 大多数数据科学家以往经常常是利用传统的机器学习框架sklearn搭建多元回归模型&#xff0c;随着深度学习和强化学习越来越普及&#xff0c;很多数据科学家尝试使用深度学习框架来进行…...

EBS常用接口开发

整理了一些工作中常用的Oracle EBS接口和API&#xff0c;最早是看着大神黄建华文档起来的&#xff0c;格式内容参考他的文档&#xff0c;加上一些自己开发的程序和经历而已。 PO PO接收、检验、入库、退货-InterfaceAPI_刘文钊1的博客-CSDN博客 基础数据 EBS物料、bom、工艺导入…...

【完整】UR机械臂逆运动学求解过程及c++代码实现

有任何问题请在评论区留言&#xff0c;我尽可能的回复大家 一. 逆运动学的求解需要以下数学运算 利用DH参数得到每个关节的变换矩阵&#xff1b;利用变换矩阵求出机械臂整个链的变换矩阵&#xff1b;求出末端位姿&#xff1b;利用已知末端位姿和整个链的变换矩阵&#xff0c;…...

68. Python的相对路径

68. Python的相对路径 文章目录68. Python的相对路径1. 知识回顾2. 什么是相对路径3. 相对路径的语法4. 查看相对路径的方法5. 写出所有txt文件的相对路径5.1 同目录5.2 上级目录6. 用相对路径读取txt文件6.1 读取旅游.txt6.2 读取旅游经费.txt6.3 读取笔记.txt和new.txt6.4 读…...

java数据类型

数据类型 类型分类&#xff0c;存储范围&#xff0c;字面量&#xff0c;默认值&#xff0c;类型转换 类型分类 存储范围 数据类型字节数表示范围byte1-128~127short2-32768~32767&#xff0c;正负3万左右int4-2147483648~2147483647&#xff0c;正负21亿左右long8-922337203…...

Kotlin 替换非空断言的几种方式

Kotlin 出现断言的两种情形 IDE java 与 kotlin 自动转换时&#xff0c;自动添加非空断言的代码Smart Cast 失效 代码展示&#xff1a; class JavaConvertExample {private var name: String? nullfun init() {name ""}fun foo() {name null;}fun test() {if (…...

2023年了,来试试前端格式化工具

在大前端时代&#xff0c;前端的各种工具链穷出不断&#xff0c;有eslint, prettier, husky, commitlint 等, 东西太多有的时候也是trouble&#x1f602;&#x1f602;&#x1f602;,怎么正确的使用这个是每一个前端开发者都需要掌握的内容&#xff0c;请上车&#x1f697;&…...

spring cloud 企业工程项目管理系统源码+项目模块功能清单

工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#xff1a;实现对数据字典标签的增删改查操作 2、编码管理&#xff1a;实现对系统编码的增删改查操作 3、用户管理&#xff1a;管理和查看用户角色 4、菜单管理&#xff1a;实现对系统菜单的增删改查操…...

TCP分片解析

本文目录什么是IP分片为什么会产生IP分片为什么要避免IP分片如何避免IP分片什么是IP分片 IP协议栈将TCP/UDP传输层要求它发送的&#xff0c;但长度大于发送端口MTU的一个数据包&#xff0c;分割成多个IP报文后分多次发送。这些分成多次发送的多个IP报文就是IP分片。 为什么会…...

开发了一款基于 Flask 框架的在线电影网站系统(附 Python 源码)

文章目录前言项目介绍源码获取运行环境安装依赖库项目截图首页展示图视频展示页视频播放页后台管理页整体架构设计图项目目录结构图前台功能模块图后台功能模块图本地运行图前言 今天我给大家分享的是基于 Python 的 Flask 框架开发的在线电影网站系统&#xff0c;大家平时需要…...

如何获得CSM--敏捷教练证书

1、什么是CSM&#xff1f;CSM即Certified Scrum Master,Scrum Master负责确保所有人都能正确地理解并实施Scrum&#xff0c;确保Scrum团队遵循Scrum的理论、实践和规则。Scrum Master是Scrum团队中的服务型领导&#xff0c;帮助Scrum团队外的人员了解他们如何与Scrum团队交互是…...

Java面试数据库

目录 一、关系型数据库 数据库权限 表设计及创建 表数据相关 数据库架构优化 二、非关系型数据库 redis 今天给大家稍微整理了一下&#xff0c;内容有数据表设计的三大范式原则、sql查询如何优化、redis数据的击穿、穿透、雪崩等...&#xff0c;以及相关的面试题&#xff0…...

关于进行vue-cli过程中的解决错误的问题

好久没发文章了&#xff0c;直到今天终于开始更新了&#xff0c;最近想进军全端&#xff0c;准备学习下vue&#xff0c;但是这东西真的太难了&#xff0c;我用了一天的时间来解决在配置中遇到的问题&#xff01;主要问题&#xff1a;cnpm文件夹和vue-cli文件夹的位置不对并且vu…...

Rockchip Linux USB Gadget

一:概述 USB Gadget 是运行在 USB Peripheral 上配置 USB 功能的子系统,正常可被枚举的 USB 设备至少有 3 层逻辑层,有些功能还会在用户空间多跑一层逻辑代码。Gadget API 就是具体功能和硬件底层交互的中间层。从上到下,逻辑层分布为: USB Controller: USB上最底层的软…...

Linux -文件系统操作与帮助命令

1、Linux -文件系统操作 df — 查看磁盘的容量 df -h —以人类可以看懂的方式显示磁盘的容量&#xff0c;易读 du 命令查看目录的容量 # 默认同样以块的大小展示 du # 加上 -h 参数&#xff0c;以更易读的方式展示 du -h-d 参数指定查看目录的深度&#xff1a; # 只查看 1…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么&#xff1f;它的作用是什么&#xff1f; Spring框架的核心容器是IoC&#xff08;控制反转&#xff09;容器。它的主要作用是管理对…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...

Spring Boot + MyBatis 集成支付宝支付流程

Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例&#xff08;电脑网站支付&#xff09; 1. 添加依赖 <!…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器

一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下&#xff0c;音视频内容犹如璀璨繁星&#xff0c;点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频&#xff0c;到在线课堂中知识渊博的专家授课&#xff0c;再到影视平台上扣人心弦的高清大片&#xff0c;音…...