进阶
# Spring中的JDBCTemplate
# 基本概述
上面这张图是持久层总图,我们今天的主角是
JdbcTemplate
,可以看到对JDBC
进行了薄薄封装
它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装。spring 框架为我们提供了很多的操作模板类。
- 操作关系型数据的:
JdbcTemplate
HibernateTemplate
- 操作 nosql 数据库的:
RedisTemplate
- 操作消息队列的:
JmsTemplate
- 我们今天的主角在
spring-jdbc-5.0.2.RELEASE.jar
中- 还需要导入一个
spring-tx-5.0.2.RELEASE.jar
(它是和事务相关的)。- 不可避免地要导入数据库驱动
# JDBCTemplate的作用
与数据库进行交互,实现对表的CRUD
# 如何创建该对象
# 原始方式
1、依赖
<?xml version="1.0" encoding="UTF-8"?>
<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>com.bean</groupId>
<artifactId>SpringAOP</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
</project>
2、表
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
3、实体类
package com.bean.domain;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
public Account() {
}
public Account(Integer id, String name, Float money) {
this.id = id;
this.name = name;
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
4、使用JDBCTemplate
package com.bean.jdbcTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class JdbcTemplateDemo {
public static void main(String[] args) {
//准备数据源:JdbcTemplate内置数据源
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
dataSource.setUsername("root");
dataSource.setPassword("root");
//JdbcTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//设置数据源
jdbcTemplate.setDataSource(dataSource);
jdbcTemplate.execute("select * from account");
}
}
# 使用Spring来创建
# XML创建
1、bean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
2、JDBCTemplateDemo
package com.bean.jdbcTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class JdbcTemplateDemo {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplate");
jdbcTemplate.execute("insert into account (name,money) values ('dd',1000)");
}
}
# 注解创建
1、注解配置
- config.SpringConfiguration
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
@ComponentScan("com.bean")
public class SpringConfiguration {
@Bean
public JdbcTemplate jdbcTemplateBean(){
return new JdbcTemplate(dataSourceBean());
}
@Bean
public DriverManagerDataSource dataSourceBean(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
- JDBCTemplateDemo
package com.bean.jdbcTemplate;
import config.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class JdbcTemplateDemo {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplateBean");
jdbcTemplate.execute("insert into account (name,money) values ('dd',1000)");
}
}
注解不能和
xml
一起,否则报错,前面已经讲过了
# JDBCTemplate的CRUD
# 一个Demo
- JDBCTemplateDemo
package com.bean.jdbcTemplate;
import com.bean.domain.Account;
import config.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.util.List;
public class JdbcTemplateDemo {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplateBean");
// 增加:使用update,没啥好说的的,注意类型是float
jdbcTemplate.update("insert into account (name,money) values(?,?)","new Account",1000f);
// 删除:使用update,没啥好说的的,注意类型是float
jdbcTemplate.update("delete from account where id=?",5);
// 更新:没啥好说的的,注意类型是float
jdbcTemplate.update("update account set name=?,money=? where id=?","update test",100f,6);
// 查询
// 查询所有:查询所有,使用query方法,spring使用了这个BeanPropertyRowMapper封装进去了类型,然后直接封装进去
List<Account> accounts = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
for (Account account : accounts) {
System.out.println(account);
}
// 查询一个:这里也可以使用查询出List的,不过输出第0位就可以了,注意这里使用的构造函数中的最后一个参数为可变参数,只有在jdk1.5之后才能使用
List<Account> account = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), 7);
System.out.println(account.isEmpty()?"无参数":account.get(0));
// 查询一行一列(聚合函数):这里需要的就是一个类型,当查到的时候会自动转变为这个类型,不过一般推荐Long,因为假如int范围不够了呢
//注意这里使用的是queryForObject
jdbcTemplate.queryForObject("select count(id) from account",Long.class);
//加参数:jdbcTemplate.queryForObject("select count(id) from account where money>?",Long.class,1500);
}
}
下面是实际开发中的写法,结合
Spring
使用
# 使用XML配置
1、bean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--首先配置dao-->
<bean id="accountDaoImpl" class="com.bean.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplateBean"></property>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--配置JdbcTemplate-->
<bean id="jdbcTemplateBean" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
2、Account
package com.bean.domain;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
public Account() {
}
public Account(Integer id, String name, Float money) {
this.id = id;
this.name = name;
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
- AccountDaoImpl
package com.bean.dao.impl;
import com.bean.dao.IAccountDao;
import com.bean.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Account findById(Integer id) {
List<Account> accounts = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), id);
return accounts.isEmpty()?null:accounts.get(0);
}
public Account findByName(String name) {
List<Account> accounts = jdbcTemplate.query("select * form account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name);
//假如没有查到,返回null
if (accounts.isEmpty()){
return null;
}
//假如有多个,返回异常
if (accounts.size()>1)
{
throw new RuntimeException("结果不唯一");
}
//否则返回唯一一个
return accounts.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?,money=?",account.getName(),account.getMoney());
}
}
- JDBCTemplateDemo
package com.bean.jdbcTemplate;
import com.bean.dao.IAccountDao;
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.domain.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JdbcTemplateDemo {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
IAccountDao accountDao = (IAccountDao) context.getBean("accountDaoImpl");
System.out.println(accountDao.findById(7));
}
}
# 使用注解配置
- SpringConfiguration
package config;
import com.bean.dao.impl.AccountDaoImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
@Configuration
@ComponentScan("com.bean")
public class SpringConfiguration {
// 声明dao
@Bean
public AccountDaoImpl accountDao(){
return new AccountDaoImpl();
}
// 声明JdbcTemplate
@Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
// 声明数据源
@Bean
public DriverManagerDataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/spring");
driverManagerDataSource.setUsername("root");
driverManagerDataSource.setPassword("root");
return driverManagerDataSource;
}
}
- Account
package com.bean.domain;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
public Account() {
}
public Account(Integer id, String name, Float money) {
this.id = id;
this.name = name;
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
- AccountDaoImpl
package com.bean.dao.impl;
import com.bean.dao.IAccountDao;
import com.bean.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class AccountDaoImpl implements IAccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
// public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
//// this.jdbcTemplate = jdbcTemplate;
//// }
public Account findById(Integer id) {
List<Account> accounts = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), id);
return accounts.isEmpty()?null:accounts.get(0);
}
public Account findByName(String name) {
List<Account> accounts = jdbcTemplate.query("select * form account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name);
//假如没有查到,返回null
if (accounts.isEmpty()){
return null;
}
//假如有多个,返回异常
if (accounts.size()>1)
{
throw new RuntimeException("结果不唯一");
}
//否则返回唯一一个
return accounts.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?,money=?",account.getName(),account.getMoney());
}
}
- JDBCTemplateDemo
package com.bean.jdbcTemplate;
import com.bean.dao.IAccountDao;
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.domain.Account;
import config.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JdbcTemplateDemo {
public static void main(String[] args) {
// ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
IAccountDao accountDao = (IAccountDao) context.getBean("accountDaoImpl");
System.out.println(accountDao.findById(7));
}
}
# Spring中的事务控制
# Spring控制我们需要明确的
1、javaEE体系分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案
2、Spring框架为我们提供了一组事务控制的接口,具体后面介绍,这组接口是在spring-tx-5.0.2.RELEASSE.jar中
3、Spring中的事务控制都是基于AOP的,它既可以使用编程的方式进行实现,也可以使用配置的方式进行实现
# Spring中事务控制的API介绍
1、依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2、事务管理器
PlatformTransacionManager管理器,这是个事务接口,包含三个具体操作
- 获取事务状态信息:
TransactionStatus getTransaction(TransactionDefinition definition)
- 提交事务:
void commit(TransactionStatus status)
- 回滚事务:
void rollback(TransactionStatus status)
实现类
DataSourceTransactionManager
- 包:
org.springframework.jdbc.datasource.DataSourceTransactionManager
- 使用
Spring JDBC
或者iBatis
进行持久化数据时使用
- 包:
HibernateTransactionManager
- 包:
org.springframework.orm.hibernate5.HibernateTransactionManager
- 使用
Hibernate
版本进行持久化数据时使用
- 包:
3、事务的定义信息
TransactionDefinition
:事物的定义信息对象,里面有如下方法
String getName()
:获取事务对象名称int getIsolationLevel()
:获取事务隔离级别ISOLATION_DEFAULT
:默认级别,归属下列某一种ISOLATION_READ_UNCOMMITTED
:可以读取未提交数据ISOLATION_READ_COMMITTED
:只能读取已提交数据,解决脏读问题(Oracle默认级别)ISOLATION_REPEATABLE_READ
:是否读取其他事务提交修改后的数据,解决不可重复度的问题(MYSQL默认)ISOLATION_SERIALIZABLE
:是否读取其他事务提交添加后的数据,解决幻影读问题
int getPropagetionBehavior()
:获取事务传播行为REQUIRED
:如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务(默认),增删改的事务SUPPORTS
:支持当前事务,加入当前没有事务,就以非事务方式执行(没有事务),只有查询才能用的事务MANDATORY
:使用当前事务,如果当前没有事务,抛出异常REQUERS_NEW
:新建事务,如果当前在事务中,就把当前事务挂起NOT_SUPPORTED
:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起NEVER
:以非事务方式运行,加入当前存在事务,抛出异常NESTED
:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行REQUIRED
类似的操作
int getTimeout()
:获取事务超时时间:默认值为-1(没有超时时间)。假如有,则以秒为单位进行设置boolean isReadOnly()
:获取事务是否只读:建议查询时设置为只读
4、事务的状态信息
TransactionStatus
接口描述了某个时间点上事务的状态信息,包含有六个基本操作
- 刷新事务:
void flush()
- 获取是否是存在存储点:
boolean hasSavepoint()
事务是以节点来提交的,每一个节点都是一个储存点,当事务回滚的时候,只会回滚到上个节点,而不是从头来
- 获取事务是否完成:
boolean isCompleted()
- 获取事务是否为新的事务:
boolean isNewTransaction()
- 获取事务是否回滚:
boolean isRollbackOnly()
- 设置事务回滚:
void setRollbackOnly()
# 环境准备
<?xml version="1.0" encoding="UTF-8"?>
<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>com.bean</groupId>
<artifactId>SpringAOP</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</project>
# 基于XML的事务控制
1、配置事务管理器:注入DataSource
2、配置事务通知
- 注入事务的约束
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
- 配置事务管理器的bean对象:包为:
org.springframework.jdbc.datasource.DataSourceTransactionManager
- 使用标签
<tx:advice></tx:advice>
标签配置事务通知id
:配置唯一标识transaction-manager
:事务管理器
- 在
<tx:advice></tx:advice>
内部配置事务的属性<tx:attributes></tx:attributes>
<tx:method>
name
指定service
中的方法isolation
用于指定事物的隔离级别,默认值为
DEFAULT
,表示使用数据库的默认隔离级别
propagetion
- 用于指定事物的传播行为,默认值为
REQUIRED
,表示一定会有事务,这是增删改的选择- 查询可以选择为
SUPPORTS
read-only
指定事物是否只读,只有查询方法才能设置为
true
,默认值为false
,表示读写
timeout
用于指定事物的超时时间,默认值为-1,表示永不超时,如果指定数值则以秒为单位
rollback-for
用于指定一个异常,碰到这个异常则回滚,其他异常不会滚。
没有默认值,表示任何异常都回滚
no-rollback-for
用于指定一个异常,碰到这个异常不回滚,其他异常都回滚。
没有默认值,表示任何异常都回滚
3、配置AOP
的通用切入点表达式
4、建立事务通知和切入点表达式的对应关系:使用<aop:advisor></aop:advicor>
- bean.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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="com.bean.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao" class="com.bean.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!--上面是配的bean对象,下面开始编写事务-->
<!--首先要有一个事务管理器,包为:org.springframework.jdbc.datasource.DataSourceTransactionManager
事务管理器内要配置dataSource
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--现在配置事务的状态
id 为唯一标识
transaction-manager为配置事务管理器
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务属性-->
<tx:attributes>
<!--
<tx:method></tx:method>
name:要配置的方法名称,比如我们这里用AOP配置的全都是service.impl包下的,所以
* :匹配全部的方法(虽然也就那么一个transer方法)
find*:将来假如有方法为find开头的时候,匹配折现方法
优先级:find* 高于 *
isolation:指定事物的隔离级别,默认值为DEFAULT,表示数据库的默认隔离级别
read-only:是否只读
propagation:指定事物的传播行为,默认为REQUIRED,为增删改的,查询可以为SUPPORTS
timeout:指定超时时间
rollback-for:用于指定一个异常,发生了此异常则回滚,其他异常不回滚。默认值无,表示任何异常都回滚。
no-rollback-for:指定一个异常,发生此异常不回滚,其余异常全都回滚。默认值无,表示任何异常都回滚。
-->
<tx:method name="*" read-only="false" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"></tx:method>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointCut" expression="execution(* com.bean.service.impl.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"></aop:advisor>
</aop:config>
</beans>
- AccountDaoImpl
package com.bean.dao.impl;
import com.bean.dao.IAccountDao;
import com.bean.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class AccountDaoImpl implements IAccountDao {
JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Account findById(Integer id) {
List<Account> accounts = jdbcTemplate.query("select * from account where id=?",new BeanPropertyRowMapper<Account>(Account.class),id);
return accounts.isEmpty()?null:accounts.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
}
}
- AccountServiceImpl
package com.bean.service.impl;
import com.bean.dao.IAccountDao;
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.domain.Account;
import com.bean.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(Integer Transferer, Integer Payee, Float money) {
System.out.println("transer");
Account accountMinus = accountDao.findById(1);
Account accountAdd = accountDao.findById(2);
accountMinus.setMoney(accountMinus.getMoney()-money);
accountAdd.setMoney(accountAdd.getMoney()+money);
accountDao.updateAccount(accountMinus);
// int i = 1/0;
accountDao.updateAccount(accountAdd);
}
}
- 测试
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.service.IAccountService;
import com.bean.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig
public class TransactionTest {
@Test
public void Test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = (IAccountService) context.getBean("accountService");
accountService.transfer(1,2,100F);
}
}
- 效果
效果就是:
- 当没有报错时:转账正常执行
- 当出现错误时:回滚
# 基于注解的事务控制
1、导入关于context的名称空间
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
2、配置事务管理器
3、开启Spring对注解的支持,使用<tx:annotation-drivern/>
4、在需要事务支持的时候使用@Transactional注解
- bean.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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--首先配置一下spring创建容器时要扫描的包-->
<context:component-scan base-package="com.bean"></context:component-scan>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
使用注解进行事务控制的配置
1. 配置事务管理器
2. 开启spring对注解事务的支持:使用<tx:annotation-driven/>,里面配置事务管理器
3. 在需要事务支持的时候使用@Transactional注解
-->
<!--事务管理器还得留下-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
- AccountDaoImpl
package com.bean.dao.impl;
import com.bean.dao.IAccountDao;
import com.bean.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import java.util.List;
@Component
public class AccountDaoImpl implements IAccountDao {
@Autowired
JdbcTemplate jdbcTemplate;
public Account findById(Integer id) {
List<Account> accounts = jdbcTemplate.query("select * from account where id=?",new BeanPropertyRowMapper<Account>(Account.class),id);
return accounts.isEmpty()?null:accounts.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
}
}
- AccountServiceImpl
package com.bean.service.impl;
import com.bean.dao.IAccountDao;
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.domain.Account;
import com.bean.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true) //这里是配置事务控制的注解的,配置的全局为只读型的配置
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
//这里单独配置,配置可写型的配置
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public void transfer(Integer Transferer, Integer Payee, Float money) {
System.out.println("transer");
Account accountMinus = accountDao.findById(1);
Account accountAdd = accountDao.findById(2);
accountMinus.setMoney(accountMinus.getMoney()-money);
accountAdd.setMoney(accountAdd.getMoney()+money);
accountDao.updateAccount(accountMinus);
// int i = 1/0;
accountDao.updateAccount(accountAdd);
}
}
- 测试
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.service.IAccountService;
import com.bean.service.impl.AccountServiceImpl;
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.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class TransactionTest {
@Autowired
private IAccountService accountService;
@Test
public void Test(){
accountService.transfer(1,2,100F);
}
}
# 事务回滚规则
默认只接受不受检查(Error和RuntimeException),但是可以利用rollbackFor的参数(类的class数组)指定遇到这个异常来回滚
可以通过noRollbackFor来定义参数(类的class数组)来指定遇到异常不回滚
# Spring5新特性介绍
# 与JDK相关的升级
1、JDK版本要求
spring5.0
在2017-9
发布了GA(通用)
版本,该版本基于jdk8
编写的,所以jdk8
之前的版本无法使用同时
tomcat
要求8.5
以上我们使用
jdk8
构建工程,可以降低版本编译,但是不能使用jdk8
以下版本构建工程
IDE
同时需要更新,eclipse 4.7.2
之前的就甭想了
2、升级说明
1、基于JDK8的反射增强
2、@NonNu11注解和@Nullable注解的使用
用@Nullable和@NotNull注解来显示表明可为空的参数和以及返回值。
这样就够在编译的时候理空值而不是在运行时抛出NullPointerExceptions。
3、日志记录方面
Spring Framework 5.0带来了Commons Logging 桥接模块的封装,它被叫做spring-jcl 而不是标准的Commons Logging。
当然,无需任何额外的桥接,新版本也会对Log4j2.x,SLF4J,JUL( java.util. logging) 进行自动检测。
# 核心容器的升级
Spring Framework 5.0现在支持候选组件索引作为类路径扫描的替代方案。
该功能已经在类路径扫描器中添加,以简化添加候选组件标识的步骤。
应用程序构建任务可以定义当前项目自己的META-INE/spring.components文件。
在编译时,源模型是自包含的,JPA实体和Spring组件是已被标记的。
从索引读取实体而不是扫描类路径对于小于200 个类的小型项目是没有明显差异,但对大型项目影响较大。
加载组件索引开销更低。因此,随着类数的增加,索引读取的启动时间将保持不变。
加载组件索引的耗费是廉价的。因此当类的数量不断增长,加上构建索引的启动时间仍然可以维持一一个常数,
不过对于组件扫描而言,启动时间则会有明显的增长。
这个对于我们处于大型spring项目的开发者所意味着的,是应用程序的启动时间将被大大缩减。
虽然20或者30秒钟看似没什么,但如果每天要这样登上好几百次,加起来就够你受的了。使用了组件索引的话,就能帮
助你每天过的更加高效。
你可以在Spring的Jira上了解更多关于组件索引的相关信息。
# JetBarins Kotlin语言支持
这个不多赘述
# 响应式编程风格
此次spring发行版本的一个激动人心的特性就是新的响应式堆栈WEB框架。
这个堆栈完全的响应阻塞,适合于事件循环风格的处理,可以进行少量线程的扩展。
# Junit5支持
完全支持JUnit 5 Jupiter, 所以可以使用JUnit 5来编写测试以及扩展。
此外还提供了一个编程以及扩展模型,Jupiter子项目提供了一个测试引擎来在Spring上运行基于 Jupiter的测试。
另外,spring Framework 5还提供了在Spring TestContext Framework 中进行并行测试的扩展。
针对响应式编程模型,spring-test 现在还引入了支持spring webFlux的WebTestclient 集成测试的支持,类似于MockMvc,并不需要一一个运行着的服务端。
使用一个模拟的请求或者响应,WebTestClient就可以直接绑定到webFlux服务端设施。
你可以在这里找到这个激动人心的TestContext 框架所带来的增强功能的完整列表。
# 依赖库的更新
终止支持的类库:
- Portlet .
- Velocity .
- JasperReports .
- XMLBeans .
- JDO.
- Guava .
支持的类库:
- Jackson 2.6+
- EhCache 2.10+ 1 3.0 GA
- Hibernate 5.0+
- JDBC 4.0+
- XmlUnit 2 .x+
- OkHttp 3.X+
- Netty 4.1+