文章目录
- 1. 什么是Srping
- 2. 下载Spring
- 3. IOC与DI
- 4. Spring中Bean的相关配置
- 4.1 bean的生命周期配置:
- 4.2 bean的作用范围配置:
- 5. Spring的属性注入
- 6. Spring的分模块开发
- 6.1 代码加载xml
- 6.2 xml文件包含
- 7. Spring的IOC的注解详解
- 7.1 @Component:组件
- 7.2 属性注入的注解
- 8. Spring的AOP开发
- 8.1 AOP相关术语
- 8.2 AOP基于AspectJ的XML开发
- 8.3 AOP基于AspectJ的注解开发
- 9. Spring的JDBC模板的使用
- 9.1 使用默认连接池的示例
- 9.2 DBCP与C3P0连接池的配置
- 9.3 引入外部属性文件
- 9.4 JDBC的CRUD操作
- 10. Spring的事务管理
- 10.1 事务的特性
- 10.2 不考虑隔离性引发的安全问题
- 10.3 解决读问题
- 10.4 Spring事务管理的API
- 10.4.1 PlatformTransactionManager
- 10.4.2 TransactionDefinition
- 10.4.3 TransactionStatus
- 10.4.4 事务管理的API关系
- 10.5 Spring的事务传播行为
- 10.6 Spring的事务管理示例
- 10.6.1 编程式事务管理
- 10.6.2 声明式事务管理
- 10.6.2.1 XML方式声明式事务管理
- 10.6.2.2 注解方式声明式事务管理
1. 什么是Srping
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架.
Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
- 目的:解决企业应用开发的复杂性
- 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
- 范围:任何Java应用
Spring作为SE、EE开发的一站式框架具有以下优点:
- IOC方便解耦
- AOP对程序进行扩展
- 轻量级框架
- 方便与其他框架整合
2. 下载Spring
下载指定版本的Spring(我这里用的4.2.4):https://repo.spring.io/release/org/springframework/spring/ 下载解压后的目录如下:
3. IOC与DI
IOC控制反转将对象的创建权交给了Spring管理,而DI的前提是必须有IOC环境(Spring管理类的时候将类依赖的 属性注入)
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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Spring入门配置 -->
<bean id="userDao" name="userDao" class="com.itheima.spring.demo1.UserDaoImpl" init-method="setup" destroy-method="destory">
<property name="name" value="张三bean"></property>
</bean>
</beans>
代码示例:
package com.itheima.spring.demo1;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sun.glass.ui.Application;
import org.springframework.context.ApplicationContext;
public class SpringDemo1 {
@Test
//传统方式调用
public void demo1() {
UserDao userDao = new UserDaoImpl();
userDao.setName("张三");
userDao.save();
}
@Test
//Spring方式调用
public void demo2() {
//ApplicationContext
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao)applicationContext.getBean("userDao");
userDao.save();
applicationContext.close();
}
}
4. Spring中Bean的相关配置
bean标签的id和name,其中id使用了唯一约束里面不能出现特殊字符,name没有使用唯一约束里面可以出现特殊字符(例如spring和structs1整合的时候<bean name="/user" class="" …>)
4.1 bean的生命周期配置:
- init-method :bean初始化的时候执行的方法
- destroy-method :bean销毁的时候执行的方法(bean是单例创建,工厂关闭时销毁执行)
4.2 bean的作用范围配置:
scope属性
- singleton :默认scope属性配置,单例创建bean
- prototype :多例模式
- request :应用于web项目,spring创建这个类后,将这个类保存到request中
- session :应用于web项目,spring创建这个类后,将这个类保存到session中
- globalsession :应用于web项目,必须在porlet环境下使用
5. Spring的属性注入
支持构造方法方式和set方法方式,但前提是类支持set方法和构造方法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- spring的属性注入方式 -->
<!-- 构造方法方式 -->
<bean id="car" class="com.itheima.spring.demo4.Car">
<constructor-arg name="name" value="大众"></constructor-arg>
<constructor-arg name="price" value="100000"></constructor-arg>
</bean>
<!-- set方法方式 -->
<bean id="car2" class="com.itheima.spring.demo4.Car">
<property name="name" value="宝马"></property>
<property name="price" value="1000000"></property>
</bean>
<bean id="employee" class="com.itheima.spring.demo4.Employee">
<property name="name" value="张三"></property>
<!-- value:设置普通类型的值,ref:设置其他的类的id或name -->
<property name="car" ref="car2"></property>
</bean>
<!-- p名称空间的方法,需要引用p名称空间 -->
<bean id="car3" class="com.itheima.spring.demo4.Car" p:name="奇瑞" p:price="1000">
<bean id="employee1" class="com.itheima.spring.demo4.Employee" p:name="李四" p:car3-ref="car3">
<!-- SpEL方式属性注入 -->
<bean id="carInfo" class="com.itheima.spring.demo4.CarInfo"></bean>
<bean id="car4" class="com.itheima.spring.demo4.Car">
<property name="name" value="#{carInfo.name}"></property>
<property name="price" value="#{carInfo.calculatorPrice()}"></property>
</bean>
<bean id="employee4" class="com.itheima.spring.demo4.Employee">
<property name="name" value="#{'王五'}"></property>
<property name="car" value="#{car4}"></property>
</bean>
<!-- spring集合类型的属性注入 -->
<bean id="collectionBean" class="com.itheima.spring.demo4.CollectionBean">
<property name="arrs">
<!-- 字符串数组类型 和list一样 String[]与List<String> -->
<list>
<value>aaa</value>
<value>bbb</value>
<!-- <ref/> -->
</list>
</property>
<property name="list">
<!-- 字符串数组类型 和list一样 String[]与List<String>-->
<list>
<value>ccc</value>
<value>ddd</value>
<!-- <ref/> -->
</list>
</property>
<property name="set">
<!-- 注入set集合-->
<set>
<value>eee</value>
<value>fff</value>
<!-- <ref/> -->
</set>
</property>
<property name="map">
<!-- 注入map集合-->
<map>
<entry key="ggg" value="111"></entry>
<entry key="hhh" value="222"></entry>
</map>
</property>
</bean>
</beans>
6. Spring的分模块开发
有两种方式,一种是代码加载xml,另一种是xml文件包含
6.1 代码加载xml
可以在代码中加载多个applicationContext.xml文件,如下:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
6.2 xml文件包含
也可以使用import将applicationContext2.xml包含到applicationContext.xml中,如下:
<import resource="applicationContext2.xml"/>
7. Spring的IOC的注解详解
@Component(value=“userDao”)//相当于<bean id=“userDao” class=“com.tiheima.spring.demo1.UserDaoImpl” .>
@PostConstruct//相当于<bean id="" class="" init-method="" .>
@Predestroy//相当于<bean id="" class="" destroy-method="" .> 注意工厂关闭时才执行
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Spring的IOC的注解入门 -->
<!-- 使用IOC的注解开发,需要配置组件扫描(哪些包下的类使用IOC注解) -->
<context:component-scan base-package="com.tiheima.spring.demo1"></context:component-scan>
</beans>
7.1 @Component:组件
- 修饰一个类,将这个类交给Spring管理
- 这个注解有三个衍生注解(功能类似)
- @Controller:web层
- @Service:service层
- @Repository:dao层
7.2 属性注入的注解
- 普通数据:
- @Value:设置普通属性的值
- 对象类型属性:
- @Autowired:设置对象类型属性的值
- 一般配合**@Qualifier**一起使用,可以按名称完成属性注入
- @Resource:完成对象类型的属性注入,按照名称完成属性注入
package com.tiheima.spring.demo1;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value="userDao")//相当于<bean id="userDao" class="com.tiheima.spring.demo1.UserDaoImpl">
public class UserDaoImpl implements UserDao {
private String name;
@Value(value="张三啊")//假如没有set方法,则将属性注解加到属性上
public void setName(String name) {
this.name = name;
}
@Override
public void save() {
System.out.println("UserDaoImpl中的save()方法执行了..."+name);
}
}
package com.tiheima.spring.demo1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
//注入DAO
@Autowired
@Qualifier(value="userDao")
private UserDao userDao;
@Override
public void save() {
System.out.println("UserService的save方法执行了");
userDao.save();
}
}
8. Spring的AOP开发
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
8.1 AOP相关术语
- Joinpoint(连接点)
可以被拦截到的点,称作连接点。如一个类中的方法。 - Pointcut(切入点)
真正被拦截到的点,称作切入点。如只对save方法增强,save称作切入点。 - Advice(通知、增强)
方法层面的增强。如对save方法进行权限校验,权限校验的方法称为是通知。 - Introduction(引介)
类层面的增强。 - Target(被增强对象)
例如对UserDao增强,UserDao则称为目标。 - Weaving(织入)
将通知应用(Advice)到目标(Target)的过程,将权限校验的方法代码应用到UserDao的 save方法上的过程。 - Proxy(代理对象)
- Aspect(切面)
多个通知和多个切入点组合。
8.2 AOP基于AspectJ的XML开发
AOP通知类型有:前置、后置、环绕、异常抛出与最终通知。
ProductDaoImpl代码:
package com.itheima.spring.demo3;
public class ProductDaoImpl implements ProductDao {
@Override
public void save() {
System.out.println("save...");
}
@Override
public void update() {
System.out.println("update...");
}
@Override
public void find() {
System.out.println("find...");
int num = 1/0;
}
@Override
public String delete() {
System.out.println("delete...");
return "后置通知返回值";
}
}
MyAspectXML代码:
package com.itheima.spring.demo3;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspectXML {
public void checkPri(JoinPoint joinPoint) {
System.out.println("权限校验==========="+joinPoint);
}
public void writeLog(Object result) {
System.out.println("日志记录==========="+result);
}
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知===========");
Object obj = joinPoint.proceed();
System.out.println("环绕后通知===========");
return obj;
}
public void afterThrowing(Throwable ex) {
System.out.println("异常抛出通知==========="+ex.getMessage());
}
public void after() {
System.out.println("最终通知===========");
}
}
SpringDemo3代码:
package com.itheima.spring.demo3;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
@Resource(name="productDao")
private ProductDao productDao;
@Test
public void demo1() {
productDao.save();
productDao.update();
productDao.find();
productDao.delete();
}
}
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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
<!-- 配置目标对象:被增强的对象 -->
<bean id="productDao" class="com.itheima.spring.demo3.ProductDaoImpl"></bean>
<!-- 将切面类交给Spring管理 -->
<bean id="myAspect" class="com.itheima.spring.demo3.MyAspectXML"></bean>
<!-- 通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!-- 表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* com.itheima.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* com.itheima.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/>
<aop:pointcut expression="execution(* com.itheima.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/>
<aop:pointcut expression="execution(* com.itheima.spring.demo3.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<!-- 前置通知 -->
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
<!-- 后置通知 -->
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>
<!-- 异常抛出通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut4"/>
</aop:aspect>
</aop:config>
</beans>
运行结果:
切入点表达式语法:
- 基于execution函数
- 语法
- [访问修饰符] 方法返回值 包名.类名.方法名(参数)
- public void com.itheima.spring.CustomerDao.save(…)
- 等等
8.3 AOP基于AspectJ的注解开发
OrderDao代码:
package com.itheima.spring.demo1;
public class OrderDao {
public void save() {
System.out.println("保存订单...");
}
public void update() {
System.out.println("修改订单...");
}
public String delete() {
System.out.println("删除订单...");
return "后置增强返回值";
}
public void find() {
System.out.println("查询订单...");
int sum = 1 / 0;
}
}
MyAspectAnno代码:
package com.itheima.spring.demo1;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* 切面类:注解的切面类
* @author cgs
*
*/
@Aspect
public class MyAspectAnno {
@Before(value = "execution(* com.itheima.spring.demo1.OrderDao.save(..))")
public void before() {
System.out.println("前置增强===========");
}
@AfterReturning(value="execution(* com.itheima.spring.demo1.OrderDao.delete(..))",returning="result")
public void afterReturning(Object result) {
System.out.println("后置增强==========="+result);
}
@Around(value="execution(* com.itheima.spring.demo1.OrderDao.update(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前增强===========");
Object obj = joinPoint.proceed();
System.out.println("环绕后增强===========");
return obj;
}
@AfterThrowing(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))",throwing="e")
public void afterThrowing(Throwable e) {
System.out.println("异常抛出增强==========="+e.getMessage());
}
@After(value="MyAspectAnno.pointcut1()")
public void after() {
System.out.println("最终增强===========");
}
@Pointcut(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))")
private void pointcut1() {}
}
SpringDemo1代码:
package com.itheima.spring.demo1;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Spring的AOP的注解开发
* @author cgs
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo1 {
@Resource(name="orderDao")
private OrderDao orderDao;
@Test
public void demo1() {
orderDao.save();
orderDao.update();
orderDao.delete();
orderDao.find();
}
}
applicationContext.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 在配置文件中开启注解的AOP开发 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置目标类 -->
<bean id="orderDao" class="com.itheima.spring.demo1.OrderDao" />
<!-- 配置切面类 -->
<bean id="myAspect" class="com.itheima.spring.demo1.MyAspectAnno" />
</beans>
运行结果:
9. Spring的JDBC模板的使用
9.1 使用默认连接池的示例
JdbcDemo1代码:
package com.itheima.jdbc.demo1;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
/**
* JDBC模板的使用
* @author cgs
*/
public class JdbcDemo1 {
@Test
// jdbc模板的使用类似于Dbutils
public void demo1() {
// 创建连接池
DriverManagerDataSource dataSource = new DriverManagerDataSource();//spring默认内置连接池
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring4_day03");
dataSource.setUsername("root");
dataSource.setPassword("p@ssw0rd");
// 创建模板
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("insert into account values(null,?,?)","cgs",10000d);
}
}
JdbcDemo2代码:
package com.itheima.jdbc.demo1;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
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("classpath:applicationContext.xml")
public class JdbcDemo2 {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
public void demo1() {
jdbcTemplate.update("insert into account values(null,?,?)","cylycgs",20000d);
}
}
applicationContext.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置Spring的内置连接池 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 属性注入 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring4_day03"></property>
<property name="username" value="root"></property>
<property name="password" value="p@ssw0rd"></property>
</bean>
<!-- 配置Spring的JDBC的模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
9.2 DBCP与C3P0连接池的配置
applicationContext.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置Spring的内置连接池 -->
<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
属性注入
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring4_day03"></property>
<property name="username" value="root"></property>
<property name="password" value="p@ssw0rd"></property>
</bean> -->
<!-- 配置DBCP连接池 -->
<!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring4_day03"></property>
<property name="username" value="root"></property>
<property name="password" value="p@ssw0rd"></property>
</bean> -->
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///spring4_day03"></property>
<property name="user" value="root"></property>
<property name="password" value="p@ssw0rd"></property>
</bean>
<!-- 配置Spring的JDBC的模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
9.3 引入外部属性文件
jdbc.properties代码:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring4_day03
jdbc.username=root
jdbc.password=p@ssw0rd
applicationContext.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置Spring的内置连接池 -->
<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
属性注入
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring4_day03"></property>
<property name="username" value="root"></property>
<property name="password" value="p@ssw0rd"></property>
</bean> -->
<!-- 配置DBCP连接池 -->
<!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring4_day03"></property>
<property name="username" value="root"></property>
<property name="password" value="p@ssw0rd"></property>
</bean> -->
<!-- 引入属性文件 -->
<!-- 1.通过bean标签引入 -->
<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"></property>
</bean> -->
<!-- 2.通过context标签引入 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置Spring的JDBC的模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
9.4 JDBC的CRUD操作
JdbcDemo2代码:
package com.itheima.jdbc.demo1;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcDemo2 {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
// 保存操作
public void demo1() {
jdbcTemplate.update("insert into account values(null,?,?)","菲菲",10000d);
}
@Test
// 修改操作
public void demo2() {
jdbcTemplate.update("update account set money=? where id=?",50000d,1);
}
@Test
// 删除操作
public void demo3() {
jdbcTemplate.update("delete from account where id=?",1);
}
@Test
// 查询操作 -- 查询某个字段
public void demo4() {
String name = jdbcTemplate.queryForObject("select name from account where id=?",String.class,2 );
}
@Test
// 查询操作 -- 封装到对象中
public void demo5() {
Account account =jdbcTemplate.queryForObject("select * from account where id=?",new MyRowMapper() , 3);
System.out.println(account);
}
@Test
// 查询操作 -- 封装到对象集合中
public void demo6() {
List<Account> list =jdbcTemplate.query("select * from account",new MyRowMapper());
for (Account account : list) {
System.out.println(account);
}
}
class MyRowMapper implements RowMapper<Account>{
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
return account;
}
}
class Account{
private Integer id;
private String name;
private double 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 double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
}
}
}
10. Spring的事务管理
事务可以理解为逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。
10.1 事务的特性
- 原子性:事务不可分割
- 一致性:事务执行前后数据完整性保持一致
- 隔离性:一个事务的执行不应该受到其他事务的干扰
- 持久性:一旦事务结束,数据就会持久化到数据库
10.2 不考虑隔离性引发的安全问题
- 读问题
- 脏读:一个事务读到另一个事务未提交的数据
- 不可重复读:一个事务读到另一个事务已提交的update的数据,导致一个事务中多次查询结果不一致
- 虚读:一个事务读到另一个事务已提交的inser的t数据,导致一个事务中多次查询结果不一致
- 写问题
- 丢失更新
10.3 解决读问题
- 设置事务的隔离级别
- read uncommitted:未提交读,任何读问题都解决不了,安全性低,效率高
- read committed:已提交读,解决脏读,但是有可能发生不可重复读和虚读
- repeatable read:重复度,解决脏读和不可重复读,但可能发生虚读
- serialzable:解决所有读问题,安全但效率低
10.4 Spring事务管理的API
10.4.1 PlatformTransactionManager
- 平台事务管理器:接口,是Spring用于管理事务的真正的对象
- DataSourceTransactionManager:底层使用JDBC事务管理
- HibernateTransactionManager:底层使用Hibernate事务管理
10.4.2 TransactionDefinition
- 事务定义信息:用于定义事务的相关信息,隔离级别、超时信息、传播行为、是否只读
10.4.3 TransactionStatus
- 事务状态:用于记录在事务管理过程中,事务的状态的对象
10.4.4 事务管理的API关系
- Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,在管理过程中产生各种状态,将这些状态的信息记录到事务状态的对象中,这就是API间的关系
10.5 Spring的事务传播行为
- Spring中提供了7种事务传播行为:
- 保证多个操作在同一个事务中
- PROPAGATION_REQUIRED
默认值,如果A中有事务就会使用A中的事务,如果A中没事务,创建一个新的事务将操作包含进来 - PROPAGATION_SUPPORTS
支持事务,如果A中有事务就会使用A中的事务,如果A中没事务,不使用事务 - PROPAGATION_MANDATORY
如果A中有事务就会使用A中的事务,如果A中没事务,抛出异常
- 保证多个操作不在同一个事务中
- PROPAGATION_REQUIRES_NEW
如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没事务,创建一个新事务,包含自身操作 - PROPAGATION_NOT_SUPPORTED
如果A中有事务,将A的事务挂起(暂停),不使用事务 - PROPAGATION_NEVER
如果A中有事务,抛出异常
- 嵌套式事务
- PTOPAGATION_NESTED
嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点
10.6 Spring的事务管理示例
以转账为例,A给B转账需要从A的账户中扣钱,并给B的账户中加钱,这两个步骤,但是如何保证这一组操作是一致性的呢?确保不会出现A的账户扣钱了,但B的账户没有收到的情况,这就需要事务了!
10.6.1 编程式事务管理
AccountService.java代码:
package com.itheima.tx.demo1;
/**
* 转账的业务层接口
* @author cgs
*/
public interface AccountService {
public void transfer(String from,String to,Double money);
}
AccountServiceImpl.java代码:
package com.itheima.tx.demo1;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
/**
* 转账的业务层实现类
* @author cgs
*/
public class AccountServiceImpl implements AccountService {
//注入DAO
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
//注入事务管理模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
/**
* from:转出账号
* to:转入账号
* money:金额
*/
public void transfer(final String from,final String to,final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
accountDao.outMoney(from, money);//转出
int num = 1 / 0;//当使用事务时,遇到异常,A账户的钱不会减少
accountDao.inMoney(to, money);//转入
}
});
}
}
AccountDao.java代码:
package com.itheima.tx.demo1;
/**
* 转账的DAO接口
* @author cgs
*/
public interface AccountDao {
public void outMoney(String from,Double money);
public void inMoney(String to,Double money);
}
AccountDaoImpl.java代码:
package com.itheima.tx.demo1;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* 转账的DAO实现类
* @author cgs
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
//由于继承了JdbcDaoSupport类,所以tx.xml中直接配置dataSource就可以了,看JdbcDaoSupport源码
@Override
public void outMoney(String from, Double money) {
this.getJdbcTemplate().update("update account set money=money-? where name=?",money,from);
}
@Override
public void inMoney(String to, Double money) {
this.getJdbcTemplate().update("update account set money=money+? where name=?",money,to);
}
}
SpringDemo1.java代码:
package com.itheima.tx.demo1;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 测试转账环境
* @author cgs
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx.xml")
public class SpringDemo1 {
@Resource(name="accountService")
private AccountService accountService;
@Test
public void demo1() {
//转账,如果没有事务保障,就会出问题,例如转出人已扣钱,被转入人没有收到钱
accountService.transfer("如花", "菲菲", 1000d);
}
}
tx.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置Service -->
<bean id="accountService" class="com.itheima.tx.demo1.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<!-- 注入事务管理模板 -->
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.itheima.tx.demo1.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置连接池和JDBC模板 -->
<!-- 配置C3P0连接池 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置平台事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
</beans>
10.6.2 声明式事务管理
10.6.2.1 XML方式声明式事务管理
AccountDaoImpl.java代码:
package com.itheima.tx.demo2;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* 转账的DAO实现类
* @author cgs
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
//由于继承了JdbcDaoSupport类,所以tx.xml中直接配置dataSource就可以了,看JdbcDaoSupport源码
@Override
public void outMoney(String from, Double money) {
this.getJdbcTemplate().update("update account set money=money-? where name=?",money,from);
}
@Override
public void inMoney(String to, Double money) {
this.getJdbcTemplate().update("update account set money=money+? where name=?",money,to);
}
}
AccountServiceImpl.java代码:
package com.itheima.tx.demo2;
/**
* 转账的业务层实现类
* @author cgs
*/
public class AccountServiceImpl implements AccountService {
//注入DAO
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
/**
* from:转出账号
* to:转入账号
* money:金额
*/
public void transfer(final String from,final String to,final Double money) {
accountDao.outMoney(from, money);
int num = 1 / 0;
accountDao.inMoney(to, money);
}
}
SpringDemo1.java代码:
package com.itheima.tx.demo2;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 测试转账环境
* @author cgs
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx2.xml")
public class SpringDemo1 {
@Resource(name="accountService")
private AccountService accountService;
@Test
public void demo1() {
//转账,如果没有事务保障,就会出问题,例如转出人已扣钱,被转入人没有收到钱
accountService.transfer("如花", "菲菲", 1000d);
}
}
tx2.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置Service -->
<bean id="accountService" class="com.itheima.tx.demo2.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.itheima.tx.demo2.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置连接池和JDBC模板 -->
<!-- 配置C3P0连接池 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置平台事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务增强,也就是切面 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
<!-- 事务管理规则示例,此例中没用 -->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="find" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- aop的配置 -->
<aop:config>
<aop:pointcut expression="execution(* com.itheima.tx.demo2.AccountServiceImpl.transfer(..))" id="pointcut1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
</beans>
10.6.2.2 注解方式声明式事务管理
AccountDaoImpl.java代码:
package com.itheima.tx.demo3;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* 转账的DAO实现类
* @author cgs
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
//由于继承了JdbcDaoSupport类,所以tx.xml中直接配置dataSource就可以了,看JdbcDaoSupport源码
@Override
public void outMoney(String from, Double money) {
this.getJdbcTemplate().update("update account set money=money-? where name=?",money,from);
}
@Override
public void inMoney(String to, Double money) {
this.getJdbcTemplate().update("update account set money=money+? where name=?",money,to);
}
}
AccountServiceImpl.java代码:
package com.itheima.tx.demo3;
import org.springframework.transaction.annotation.Transactional;
/**
* 转账的业务层实现类
* @author cgs
*/
@Transactional
public class AccountServiceImpl implements AccountService {
//注入DAO
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
/**
* from:转出账号
* to:转入账号
* money:金额
*/
public void transfer(final String from,final String to,final Double money) {
accountDao.outMoney(from, money);
int num = 1 / 0;
accountDao.inMoney(to, money);
}
}
SpringDemo1.java代码:
package com.itheima.tx.demo3;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 测试转账环境
* @author cgs
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx3.xml")
public class SpringDemo1 {
@Resource(name="accountService")
private AccountService accountService;
@Test
public void demo1() {
//转账,如果没有事务保障,就会出问题,例如转出人已扣钱,被转入人没有收到钱
accountService.transfer("如花", "菲菲", 1000d);
}
}
tx3.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置Service -->
<bean id="accountService" class="com.itheima.tx.demo3.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.itheima.tx.demo3.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置连接池和JDBC模板 -->
<!-- 配置C3P0连接池 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></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>