Spring 应用之Spring JDBC实现
陈彦斌 人气:0jdbcTemplate类的入门
方式一
POM.XML
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring-aop</groupId> <artifactId>spring-aop</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- spring ioc组件需要的依赖包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <!-- 基于AspectJ的aop依赖包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> <https://img.qb5200.com/download-x/dependency> <!-- spring 事务管理和JDBC依赖包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <!-- spring 单元测试组件包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.1.RELEASE</version> <scope>test</scope> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> <https://img.qb5200.com/download-x/dependency> <!-- MySql数据库驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> <https://img.qb5200.com/download-x/dependency> <https://img.qb5200.com/download-x/dependencies> </project>
修改jdk版本
<build> <plugins> <!-- 配置Maven的JDK编译级别 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build>
测试单元
package com.cyb.spring.test; import org.junit.Test; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; public class TestJdbcTemplate { @Test public void test() { try { //创建连接池,先使用spring框架内置的连接池 DriverManagerDataSource dataSource =new DriverManagerDataSource(); //数据库驱动程序 /https://img.qb5200.com/download-x/dataSource.setDriverClassName("com.mysql.jdbc.Driver"); //旧驱动程序 dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); //新驱动程序 //数据库连接字符串 dataSource.setUrl("jdbc:mysql://localhost:3306https://img.qb5200.com/download-x/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"); //账号 dataSource.setUsername("root"); //密码 dataSource.setPassword("root"); //创建模板类 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); //完成数据的添加 int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"测试人员"); } catch (Exception e) { e.printStackTrace(); } } }
方式二
spring.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 管理DataSource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!-- set方法注入属性,和类中的成员属性无关,和set方法名称有关,比如有一个属性叫username,但是set方法:setName --> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306https://img.qb5200.com/download-x/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 管理jdbcTemplate --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> </beans>
单元测试
package com.cyb.spring.test; 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; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"测试人员"); } catch (Exception e) { e.printStackTrace(); } } }
Spring 管理第三方DataSource
常用数据源连接池
- dbcp
- c3p0
- druid(阿里出品)
管理DBCP连接池
maven工程加入依赖
pom.xml
<dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> <https://img.qb5200.com/download-x/dependency>
spring.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 管理第三方DataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- set方法注入属性,和类中的成员属性无关,和set方法名称有关,比如有一个属性叫username,但是set方法:setName --> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306https://img.qb5200.com/download-x/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 管理jdbcTemplate --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> </beans>
单元测试
package com.cyb.spring.test; 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.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"测试人员22"); } catch (Exception e) { e.printStackTrace(); } } @Test public void test2() { try { //创建连接池,先使用spring框架内置的连接池 DriverManagerDataSource dataSource =new DriverManagerDataSource(); //数据库驱动程序 /https://img.qb5200.com/download-x/dataSource.setDriverClassName("com.mysql.jdbc.Driver"); //旧驱动程序 dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); //新驱动程序 //数据库连接字符串 dataSource.setUrl("jdbc:mysql://localhost:3306https://img.qb5200.com/download-x/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"); //账号 dataSource.setUsername("root"); //密码 dataSource.setPassword("root"); //创建模板类 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); //完成数据的添加 int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"测试人员"); } catch (Exception e) { e.printStackTrace(); } } }
使用JdbcTemplate完成增删改查操作
s_user.java
package com.cyb.spring.test; public class s_user { private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "s_user [id=" + id + ", name=" + name + ", age=" + age + "]"; } private String name; private int age; }
TestjdbcTemplate.java
查询
package com.cyb.spring.test; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; 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.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { //第一个参数:执行的sql语句 //第二个参数:结果映射处理器(RowMapper) //第三个参数:sql语句中的入参 List<s_user> queryList = jdbcTemplate.query("select * from s_user",new MyBeanMapper(),null); System.out.println(queryList); } } class MyBeanMapper implements RowMapper { public s_user mapRow(ResultSet rs, int rowNum) throws SQLException { s_user user=new s_user(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setAge(rs.getInt("age")); return user; } }
插入
package com.cyb.spring.test; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; 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.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"测试人员22"); } catch (Exception e) { e.printStackTrace(); } } }
更新
package com.cyb.spring.test; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; 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.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("update s_user set name=?,age=? where id=?","测试人员",19,5); } catch (Exception e) { e.printStackTrace(); } } }
删除
package com.cyb.spring.test; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; 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.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("delete from s_user where id=?",5); } catch (Exception e) { e.printStackTrace(); } } }
Spring DAO 开发之JdbcDaoSupport
案例设计
- 编写转账案例(包括业务层和持久层)
- 编写DAO时引入JdbcDaoSupport的使用
实现
项目结构图
数据库表字段
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring-aop</groupId> <artifactId>spring-aop</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- spring ioc组件需要的依赖包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <!-- 基于AspectJ的aop依赖包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> <https://img.qb5200.com/download-x/dependency> <!-- spring 事务管理和JDBC依赖包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.1.RELEASE</version> <https://img.qb5200.com/download-x/dependency> <!-- spring 单元测试组件包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.1.RELEASE</version> <scope>test</scope> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> <https://img.qb5200.com/download-x/dependency> <!-- MySql数据库驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> <https://img.qb5200.com/download-x/dependency> <!-- dbcp连接池依赖包 --> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> <https://img.qb5200.com/download-x/dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.1</version> <https://img.qb5200.com/download-x/dependency> <https://img.qb5200.com/download-x/dependencies> <build> <plugins> <!-- 配置Maven的JDK编译级别 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
AccountDao.java
package com.cyb.spring.dao; public interface AccountDao { void update(String name, int money); int queryMoney(String name); }
AccountDaoImpl.java
方式一
package com.cyb.spring.dao; import java.sql.ResultSet; import java.sql.SQLException; import javax.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; @Repository public class AccountDaoImpl implements AccountDao { @Resource private JdbcTemplate jdbcTemplate; public void update(String name, int money) { // jdbc操作 // MyBatis操作 // JdbcTemplate操作 jdbcTemplate.update("UPDATE s_account set money=" + money + " where name='" + name + "'"); } public int queryMoney(String name) { int money = jdbcTemplate.queryForObject("select money from s_account where name=?", new IntMapper(), name); return money; } } class IntMapper implements RowMapper<Integer> { public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getInt("money"); } }
方式二
package com.cyb.spring.dao; import java.sql.ResultSet; import java.sql.SQLException; import javax.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.stereotype.Repository; @Repository public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { public void update(String name, int money) { this.getJdbcTemplate().update("UPDATE s_account set money=" + money + " where name='" + name + "'"); } public int queryMoney(String name) { int money=this.getJdbcTemplate().queryForObject("select money from s_account where name=?", new IntMapper(), name); return money; } } class IntMapper implements RowMapper<Integer> { public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getInt("money"); } }
AccountService.java
package com.cyb.spring.service; public interface AccountService { void transfer(String from, String to, int money); }
AccountServiceImpl.java
方式一
package com.cyb.spring.service; import javax.annotation.Resource; import org.springframework.stereotype.Service; import com.cyb.spring.dao.AccountDao; @Service public class AccountServiceImpl implements AccountService { @Resource private AccountDao dao; public void transfer(String from, String to, int money) { //先查询from账户的钱 int fromMoney=dao.queryMoney(from); // 对from账户进行扣钱操作 dao.update(from, fromMoney-money); // 对to账户加钱操作 int toMoney=dao.queryMoney(to); dao.update(to, toMoney+money); } }
方式二
package com.cyb.spring.service; import javax.annotation.Resource; import org.springframework.stereotype.Service; import com.cyb.spring.dao.AccountDao; @Service public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public AccountDao getAccountDao() { return accountDao; } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void transfer(String from, String to, int money) { //先查询from账户的钱 int fromMoney=accountDao.queryMoney(from); // 对from账户进行扣钱操作 accountDao.update(from, fromMoney-money); // 对to账户加钱操作 int toMoney=accountDao.queryMoney(to); accountDao.update(to, toMoney+money); } }
spring-transfer.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:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 从底层往上层配 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- set方法注入属性,和类中的成员属性无关,和set方法名称有关,比如有一个属性叫username,但是set方法:setName --> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306https://img.qb5200.com/download-x/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 管理jdbcTemplate --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <!-- AccountDao和AccountService --> <context:component-scan base-package="com.cyb.spring"></context:component-scan> </beans>
方式二
<?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:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 从底层往上层配 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- set方法注入属性,和类中的成员属性无关,和set方法名称有关,比如有一个属性叫username,但是set方法:setName --> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306https://img.qb5200.com/download-x/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 管理jdbcTemplate --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <!-- AccountDao和AccountService --> <bean id="accountService" class="com.cyb.spring.service.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <bean id="accountDao" class="com.cyb.spring.dao.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
AccountServiceTest.java
package com.cyb.spring.service; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-transfer.xml") public class AccountServiceTest { @Autowired private AccountService Service; @Test public void testTransfer() { Service.transfer("老公", "老婆", 500); } }
Spring 应用之事务
事务介绍
- 事务:指的是逻辑上一组操作,组成这个事务的各个执行单元,要么一起成功,要么一起失败!
- 事务的特性(ACID)
- 原子性(Atomicty)
- 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。
- 一致性(Consistency)
- 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
- 拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,赚多次帐,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
- 隔离性(Isolation)
- 隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability)
- 持久性是指一个事务一旦被提交了,那么对数据库种的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
- 原子性(Atomicty)
事务并发问题(隔离性导致)
在事务的并发操作中可能会出现一些问题
- 脏读:一个事务读取到另一个事务未提交的数据
- 不可重复读:一个事务因读取到另一个事务已提交的数据。导致对同一条记录读取两次以上的结果不一致。update操作
- 幻读:一个事务因读取到另一个事务已提交的数据。导致对同一张表读取两次以上结果不一致。insert、delete操作
事务隔离级别
为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
四种隔离级别(由低到高):
- Read uncommitted(读未提交):最低级别,任何情况都无法保证。
- Read committed(读已提交):可避免脏读的发生
- Repeatable read(可重复读):可避免脏读、不可重复读的发生
- Serializable(串行化):可避免脏读、不可重复读、幻读的发生
默认隔离级别
大多数数据库的默认隔离级别是Read committed,比如Oracle、DB2等。
MySQL数据库的默认隔离级别是Repeatable read。
注意事项
级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
Spring 框架的事务管理相关的类和API
Spring 并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。Spring事务管理器的接口是PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事儿了。
1. PlatformTransactionManager接口 -- 平台事务管理器.(真正管理事务的类)。该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类!
2. TransactionDefinition接口 -- 事务定义信息.(事务的隔离级别,传播行为,超时,只读)
3. TransactionStatus接口 -- 事务的状态(是否新事务、是否已提交、是否有保存点、是否回滚)
4. 总结:上述对象之间的关系:平台事务管理器真正管理事务对象.根据事务定义的信息TransactionDefinition 进行事务管理,在管理事务中产生一些状态.将状态记录到TransactionStatus中
5. PlatformTransactionManager接口中实现类和常用的方法
1. 接口的实现类
* 如果使用的Spring的JDBC模板或者MyBatis(IBatis)框架,需要选择DataSourceTransactionManager实现类
* 如果使用的是Hibernate的框架,需要选择HibernateTransactionManager实现类
2. 该接口的常用方法
* void commit(TransactionStatus status)
* TransactionStatus getTransaction(TransactionDefinition definition)
* void rollback(TransactionStatus status)
6. TransactionDefinition
1. 事务隔离级别的常量
* static int ISOLATION_DEFAULT -- 采用数据库的默认隔离级别
* static int ISOLATION_READ_UNCOMMITTED
* static int ISOLATION_READ_COMMITTED
* static int ISOLATION_REPEATABLE_READ
* static int ISOLATION_SERIALIZABLE
2. 事务的传播行为常量(不用设置,使用默认值)
* 先解释什么是事务的传播行为:解决的是业务层之间的方法调用!!
* PROPAGATION_REQUIRED(默认值) -- A中有事务,使用A中的事务.如果没有,B就会开启一个新的事务,将A包含进来.(保证A,B在同一个事务中),默认值!!
* PROPAGATION_SUPPORTS -- A中有事务,使用A中的事务.如果A中没有事务.那么B也不使用事务.
* PROPAGATION_MANDATORY -- A中有事务,使用A中的事务.如果A没有事务.抛出异常.
* PROPAGATION_REQUIRES_NEW -- A中有事务,将A中的事务挂起.B创建一个新的事务.(保证A,B没有在一个事务中)
* PROPAGATION_NOT_SUPPORTED -- A中有事务,将A中的事务挂起.
* PROPAGATION_NEVER -- A中有事务,抛出异常.
* PROPAGATION_NESTED -- 嵌套事务.当A执行之后,就会在这个位置设置一个保存点.如果B没有问题.执行通过.如果B出现异常,运行客户根据需求回滚(选择回滚到保存点或者是最初始状态)
Spring 框架事务管理的分类
1. Spring的编程式事务管理(不推荐使用)
* 通过手动编写代码的方式完成事务的管理(不推荐)
2. Spring的声明式事务管理(底层采用AOP的技术)
* 通过一段配置的方式完成事务的管理
编程式事务管理(了解)
1. 说明:Spring为了简化事务管理的代码:提供了模板类 TransactionTemplate,所以手动编程的方式来管理事务,只需要使用该模板类即可!!
2. 手动编程方式的具体步骤如下:
1. 步骤一:配置一个事务管理器,Spring使用PlatformTransactionManager接口来管理事务,所以咱们需要使用到他的实现类!!
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2. 步骤二:配置事务管理的模板
<!-- 配置事务管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
3. 步骤三:在需要进行事务管理的类中,注入事务管理的模板
<bean id="accountService" class="com.itheima.demo1.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
4. 步骤四:在业务层使用模板管理事务:
// 注入事务模板对象
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void pay(final String out, final String in, final double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
// 扣钱
accountDao.outMoney(out, money);
int a = 10/0;
// 加钱
accountDao.inMoney(in, money);
}
});
声明式事务管理(重点)
- 基于AspectJ的XML方式(重点掌握)
- 基于AspectJ的注解方式(重点掌握)
实现
准备转账环境:
***业务层:
***AccountService
***AccountServiceImpl
***持久层:
***AccountDao
***AccountDaoImpl
***spring配置:
***单元测试代码:
***配置事务管理的AOP
***平台事务管理器:DataSourceTransactionManager
***事务通知:<tx:advice id=”” transaction-manager=””/>
***AOP配置:
<aop:config>
<aop:advisor advice-ref=”” pointcut=””/>
</aop:config>
源码分析tx:advisor
源码入口
此处需要了解TxAdviceBeanDefinitionParser的继承体系,TxAdviceBeanDefinitionParseràAbstractSingleBeanDefinitionParseràAbstractBeanDefinitionParser,因为根据上面loadBeanDefinitions流程源码分析,我们知道自定义元素的解析工作是从一个namespaceHandler.parser方法开始的,该方法在AbstractBeanDefinitionParser类中
我们重点关心如何获取BeanDefinition对象的,所以接下来,我们进入parseInternal方法,该方法在AbstractSingleBeanDefinitionParser中(参考上面继承体系)
接下来,我们来到了TxAdviceBeanDefinitionParser类,因为getBeanClass方法和doParser方法都在该类里面
此时我们重点了解一下TransactionInterceptor这个类,它是我们分析的最终目标
invokeWithInTransaction方法在TransactionInterceptor类的父类TransactionAspectSupport中:
对于事务源码,了解到此处基本上可以了,如果想再了解事务是如何开启和提交的,请继续往下看,接下来我们进入createTransactionIfNecessary方法看看,事务是如何开启的
我们进入AbstractPlatformTransactionManager中的getTransaction方法继续了解事务是如何开启的:
接下来,该进入doBegin方法了,不过该方法在具体的平台事务管理器的子类中,我们此处使用DataSourceTransactionManager子类进行源码跟踪:
DataSourceTransactionManager的事务管理是通过底层的JDBC代码实现的,但是不同的平台事务管理器,它们底层的事务处理也是不同的。
事务管理之基于AspectJ的注解方式(重点掌握)
***service类上或者方法上加注解:
***类上加@Transactional:表示该类中所有的方法都被事务管理
***方法上加@Transactional:表示只有该方法被事务管理
***开启事务注解:
加载全部内容