Spring概念
1.是轻量级的开源的javaee框架。
2.解决企业应用开发的复杂性。
3.核心部分 IOC和AOP
(1)IOC:控制反转,把创建对象过程交给spring进行管理。
(2)AOP:面向切面,不修改源码进行功能增强。
4.特点
(1)方便解耦,简化开发。
(2)AOP编程
(3)方便程序的检测
(4)方便和其他框架进行整合
(5)降低javaee API的使用难度
(6)方便进行事务操作
演示案例
(1)下载spring
(2)idear创建新项目
(3)导入spring5 相关jar包
Beans、Core、Contert、Expression、commons
idear中新建文件夹lib,jar包复制到lib中,将jar包导入项目中
peoject structure -> Modules -> "+" 选择5个jar包
(4)创建普通类,在这个类创建普通方法
创建包com.xqn.spring5, 创建类User,并删除自带main
public class User {
public void add () {
system.out.println("add.......");
}
}
(5)创建对象
创建对象有两种方式:配置文件和注解。本次用配置文件。
创建spring配置文件,在配置文件配置创建的对象。
spring配置文件用xml格式(resource Bundle)
<bean id ="user" class = "com.xqn.spring5.User"></bean>
id:取的名字
class:写全路径
bean:专门创建对象
(6)进行测试代码编写
只在测试时用,实际应用中不写,而是通过web层调用。
创建testdome包,TestSpring5类,引入Test包
public class TestSpring5{
@Test
public void testAdd(){
//1.加载spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("类路径");
//2.获取配置创建的对象
User user = context.getBean ('user',User.class);//转换类型
System.out.println(user);
user.add();
}
}
IOC容器
什么是IOC
控制反转,通过控制反转,把对象创建和对象之间的调用过程(一个类调用另一个类的方法),交给spring进行管理。
目的是降低代码之间的耦合度。
IOC底层原理
xml解析、工厂模式、反射
原始方法
创建对象调用
耦合度太高了
优化方案:工厂模式
目的:耦合度降低到最低限度
IOC过程
第一步 xml配置文件,配置创建对象
第二步 创建工厂类
IOC(接口)
1.IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2.spring提供IOC容器实现两种方式:(两个接口)
BeanFactory:IOC容器基本实现,spring内部使用接口,不提供开发人员进行使用。加载配置文件时不会创建对象在获取对象(使用)才去创建对象(很像懒汉式)
ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。加载配置文件时就会创建对象。
常用第二种,(加载时间长一些)服务器启动时加载,耗时耗资源的过程交给服务器。
3.ApplicationContext接口有实现类
两个主要实现类:
FileSystemXmlApplicationContext 全路径
ClassPathXmlApplicationContext 类路径
IOC操作Bean管理
什么是Bean
指的是两个操作:创建对象和注入属性
Bean管理操作有两种方式:基于xml配置文件方式实现和基于注解方式实现。
基于xml方式
基于xml方式创建对象
1.在spring配置文件中,使用bean标签,标签里添加对应属性,就可以实现对象的创建。
2.在bean标签有很多属性,介绍常用的属性:
id:唯一标识
class:类全路径(包类路径)
3.创建对象时,默认执行无参构造方法
基于xml方式注入属性
DI:依赖注入,注入属性,是IOC中一种具体实现,在创建对象基础之上。
第一种注入方式:使用set方法
类里面要有set方法
<bean id = "..." class = "...">
<property name ="属性名" value = "值"> </property>
<property name ="属性名" value = "值"> </property>
</bean>
第二种注入方法:有参构造注入
<bean id = "..." class = "...">
<constructor-arg name ="属性名" value = "值"> </constructor-arg>
</bean>
或者使用属性索引
<bean id = "..." class = "...">
<constructor-arg index ="属性索引" value = "值"> </constructor-arg>
</bean>
简化set方法注入(属性连续注入)
1.添加p名称空间在配置文件中
2.进行属性注入,在bean标签里面进行操作
<bean id = "..." class = "..." p:属性1名 = "值" p:属性2名 = "值">
</bean>
注入其他类型属性
字面量类型
字面量:设置属性固定值
1.null
<property name = "属性名">
<null/>
</property>
2.属性包含特殊符号
(1)转义 使用<和>
(2)把带特殊符号内容写到CDATA中
<property name = "属性名">
<value> <![CDATA[《三国》]]> </value>
</property>
class类型
创建两个类service和继承dao接口的类
在service调用dao里面的方法
在spring配置文件中配置
1.外部bean
外部创建对象然后ref引用
<bean id = "userService" class = "...">
<property name ="userDao" ref = "userDaoImpl"> </property>
</bean>
<bean id = "userDaoImpl" class = "...">
</bean>
2.内部bean
<bean id = "userService" class = "...">
<property name ="userDao" >
<bean id = "userDaoImpl" class = "..."> </bean>
<property name = "" value = ""> </property>
</property>
</bean>
3.级联赋值
(1)一对多关系:部门和员工
一个部门有多个员工,一个员工属于一个部门
创建Dept 部门类和Emp 员工类
第一种
<bean id = "emp" class = "...">
<property name ="属性名" value = "值"> </property>
<property name ="dept" ref = "dept"> </property>
</bean>
<bean id = "dept" class = "...">
<property name ="属性名" value = "值"> </property>
</bean>
第二种
需要在Emp中生成get方法
<bean id = "emp" class = "...">
<property name ="属性名" value = "值"> </property>
<property name ="dept" ref = "dept"> </property>
</bean>
<bean id = "dept" class = "...">
<property name ="dept.属性名" value = "值"> </property>
</bean>
集合类型
1.注入数组类型属性
2.注入List类型
3.注入Map类型
4.注入set类型
第一步 创建类,定义数组、list、map、set类型属性,生成对应的set方法
第二步 在spring配置文件进行配置
<bean id = "stu" class = " ">
<!--注入数组类型,<list><array>都支持String数组-->
<property name = "coursers">
<array>
<value> 值 </value>
<value> 值 </value>
</array>
</property>
<!--注入list类型-->
<property name = "list">
<list>
<value> 值 </value>
<value> 值 </value>
</list>
</property>
<!--注入map类型-->
<property name = "maps">
<map>
<entry key = "键" value = "值"> </entry>
<entry key = "键" value = "值"> </entry>
</map>
</property>
<!--注入set类型-->
<property name = "sets">
<set>
<value> 值 </value>
<value> 值 </value>
</set>
</property>
</bean>
5.在集合里面设置对象类型值
<bean id = "course" class = "...">
<property name ="courselist">
<list>
<ref bean = "course1"> </ref>
<ref bean = "course2"> </ref>
</list>
</property>
</bean>
<bean id = "course1" class = "...">
<property name ="属性名" value = "值"> </property>
</bean>
<bean id = "course2" class = "...">
<property name ="属性名" value = "值"> </property>
</bean>
6.把集合注入部分提取出来
第一步 在spring配置文件中引入名称空间util
第二步 使用util标签完成list集合注入提取
<util:list id = "bookList">
<value> 值 </value>
...
</util:list>
<bean id = "book" class = " ">
<property name = "list" ref = "bookList">
</property>
</bean>
FactoryBean(内置bean)
1.spring有两种类型bean,一种是普通bean,另一种是工厂bean
2.普通bean:class = 定义什么类型,返回什么类型,在配置文件中定义bean类型就是返回类型
3.工厂bean:返回类型可以和在配置文件定义bean类型不一样
第一步 创建类,让这个类作为工厂bean,实现接口FactoryBean
第二步 实现接口里面的方法,在实现的方法中定义返回的bean类型
bean的作用域
1.在spring里面,设置bean创建单实例还是多实例
2.在spring里面,默认情况下,bean是单实例对象
3.如何设置单实例还是多实例
(1)在spring配置文件bean标签里面有属性(scope)用于设置单属性还是多属性
(2)scope属性值:
第一个值 singleton,默认值,表示单实例对象。
第二个值 prototype,表示多实例对象。
<bean id = "" class = "" scope = "prototype或者singleton">
...
</bean>
(3)singleton 和 prototype的区别
a).singleton是单实例 prototype是多实例
b).设置scope值是singleton时,加载spring配置文件时会创建单实例对象
c).设置scope值是prototype时,不是在加载spring配置文件时会创建,而是在调用getBean方法 时,创建多实例对象
bean生命周期
从对象创建到对象销毁的过程
1.通过构造器创建bean实例(无参构造)
2.为bean的属性设置值和对其他bean的引用(调用set方法)
3.调用bean的初始化方法(需要进行配置)
4.bean可以使用了(对象获取到了)
5.当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
bean的后置处理器,bean生命周期有七步
创建类,实现接口BeanPostProcessor,创建后置处理器,重写postProcessBeforeInitialization(Object, String)和postProcessAfterInitialization(Object, String)需要在配置文件中配置
1.通过构造器创建bean实例(无参构造)
2.为bean的属性设置值和对其他bean的引用(调用set方法)
3.把bean实例传递bean后置处理器的方法postProcessBeforeInitialization(Object, String)
4..调用bean的初始化方法(需要进行配置)
5.把bean实例传递bean后置处理器的方法postProcessAfterInitialization(Object, String)
6.bean可以使用了(对象获取到了)
7.当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
xml自动装配
手动装配:通过property标签手动设置name和value
1.根据指定装配规则(属性名称或者属性类型)spring自动将匹配的属性值进行注入
2.bean标签属性autowire配置自动装配,autowire属性常用两个值:
byName根据属性名称注入
byType根据属性类型注入
<bean id = "" class = "" autowire = "byName或者byType"
...
</bean>
引入外部属性文件
1.直接配置数据库信息
(1)引入德鲁伊连接池依赖的jar包(复制,导入)
(2)配置德鲁伊连接池
配置文件:
<bean id = "dateSource" class = "com.alibaba.druid.pool.DruidDataSource">
<property name = "driverClassName" value = "com.mysql.jdbc.Driver"></property><!--驱动名称-->
<property name = "url" value = "————"></property><!--数据库地址-->
<property name = "username" value = "root"></property><!--数据库用户-->
<property name = "password" value = "root"></property><!--数据库密码-->
</bean>
2.引入外部属性文件配置数据库连接池
(1)创建外部属性文件,properties格式文件,写数据信息
prop.driverClass = com.mysql.jbdc.Driver
prop.url =
prop.userName =
prop.password =
“=”左边可以随便写,建议不只写一个单词,容易冲突。
(2)把外部properties属性文件引入到spring配置文件中
引入context名称空间
在spring配置文件使用标签引入外部属性文件
<!--引入外部属性文件-->
<context:property-placeHolder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id = "dateSource" class = "com.alibaba.druid.pool.DruidDataSource">
<property name = "driverClassName" value = "${prop.driverClass}"></property><!--驱动名称-->
<property name = "url" value = "${prop.url}"></property><!--数据库地址-->
<property name = "username" value = "${prop.userName}t"></property><!--数据库用户-->
<property name = "password" value = "${prop.password}"></property><!--数据库密码-->
</bean>
基于注解方式
什么是注解
1.注解是代码特殊标记格式:@注解名称(属性名称=属性值,...)
2.注解作用在类上面、方法上面、属性上面
3.使用注解目的:简化xml配置
创建对象4个注解
1.@Component 一般用于普通注解
2.@Service 一般用于业务逻辑层,service层
3.@Controller 一般用于web层
4.@Repository 一般用于到层(持久层)
上面4个注解功能是一样的,都可以用来创建bean实例
基于注解实现对象创建
第一步 引入依赖 spring-aop-....RELEASE.jar
第二步 开启组件扫描,配置文件context
<context:component-scan base_package="">
</context:component-scan>
如果扫描多个包
方法一:使用逗号隔开
方法二:扫描包上层目录
第三步 创建类,在类上面添加创建对象注解
@Component (value = "userService")
public class UserService{
public void add(){
...
}
}
在注解里面value属性值可以省略不写,默认值是类名称,首字母小写
注意:
<context:component-scan base_package="——————"
use-default-filters = "false">
<context:include-filter <!--设置扫描哪些内容-->
type = "annotation" <!--根据注解扫描-->
expression = "org.springframework.stereotype.Controller"/>
<!--表达式,只扫描Controller注解-->
</context:component-scan>
context:exclude-filter :设置哪些内容不进行扫描
use-default-filters = "false"表示现在不使用默认filter(全部扫描),自己配置filter。
context:include-filter 设置扫描哪些内容
type = "annotation" 根据注解扫描
expression = "org.springframework.stereotype.Controller" 表达式,只扫描Controller注解
context:exclude-filter :设置哪些内容不进行扫描
基于注解实现属性注入
1.@Autowired 根据属性类型进行自动装配
2.@Qualifier 根据属性名称进行注入
3.@Resource 可以根据类型注入,也可以根据名称注入
4.@Value 注入普通类型属性
演示:
第一步 把service和dao对象创建,在service和dao类添加创建对象注解
第二步 在service注入dao对象,在service类添加dao类型属性,在属性上面使用(Autowired)注解(不需要添加set方法)
@Qualitier:根据属性名称进行注入,需要和@Autowired一起使用
完全注解开发
1.创建配置类,替代xml配置文件
@Configuration
@ComponentScan(basePackages={"包路径"})//相当于开启扫描
public class SpringConfig{
...
}
2.编写测试类
public void testService(){
ApplicationContext context = new AnnotainConfigApplicationContext(SpringConfig.class);//加载配置类
}
AOP
什么是AOP
面向切面编程(方面)
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通俗描述:不通过修改源代码方式,在主干功能里面添加新功能。
AOP底层原理
AOP底层使用动态代理
两种情况的动态代理
有接口
使用jdk动态代理
创建接口实现类代理对象,增强类的方法。
1.使用jdk动态代理,使用Proxy(java.long包)类里面的方法创建代理对象
static object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
第一个参数:类加载器
第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口。
第三个参数:实现这个接口InvocationHandler的类,创建代理对象,写增强方法。
2.编写JDK动态代理代码
(1)创建接口,实现方法
public interface UserDao{
public int add (int a , int b);
public int update(String id);
}
(2)创建接口实现类,实现方法
public class UserDaoImpl implements UserDao{
public int add(int a , int b) {
return a+b;
}
public String update(String id) {
return id;
}
}
(3)使用Proxy类创建接口代理对象
public calss JDKProxy{
public static void main(String[] args){
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};//此源代码只有一个接口,接口数组,第二个参数
UserDaoImpl UserDao = new UserDaoImpl();
UserDao dao =(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(UserDao))
dao.add(1,2);
}
class UserDaoProxy implements InvocationHandle{
//重写方法,需要传入之前方法(使用有参构造器传递之前类),再写新增方法,第三个参数
private Object obj;
public UserDaoProxy(Object, obj){
this.obj = obj;
}
public Object invoke(Object Proxy, Method, Object[] args) throws Throwable {
System.out.Println("方法之前执行。。。" + Method.getName() + "传递参数" +
Arrays.toString(args));
Object res = Method.invoke(obj, args);
System.out.Println("方法之后执行。。。" + obj;
return res;
}
}
没有接口
使用CGLIB动态代理。
创建当前类子类的代理对象,增强类的方法。
AOP术语
1.连接点
类里面哪些方法可以被增强,这些方法称为连接点。
2.切入点
实际被真正增强的方法,称为切入点。
3.通知(增强)
实际增强的逻辑部分称为通知(增强)。
通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知
4.切面
是个动作,把通知应用到切入点过程。
AOP操作(准备)
1.spring框架一般基于AspectJ实现AOP操作。
AspectJ不是spring组成部分,独立于AOP框架,一般把AspectJ和spring一起使用,进行AOP操作。
2.基于AspectJ实现AOP操作
(1)基于xml配置文件
(2)基于注解方式(常使用)
3.在项目工程里面引入AOP相关依赖
4.切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强。
(2)语法结构
execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))
如:
对com.xqn.dao.BookDao类里面的add进行增强
execution(* com.xqn.dao.BookDao.add (..))
对com.xqn.dao.BookDao类里面的所有方法进行增强
execution(* com.xqn.dao.BookDao.* (..))
对com.xqn.dao.包里面的所有类,类里所有方法进行增强
execution(* com.xqn.dao.*.* (..))
基于注解方式对AspectJ操作
1.创建类,在类里面定义方法
public class User {
public void add(){
System.out.println("add.....");
}
}
2.创建增强类
public class UserProxy {
public void before(){
System.out.println("before.....");
}
}
3.进行通知的配置
(1)在spring配置文件中,开启注解扫描。
添加aop名称空间
开启注解扫描
<context: component-scan base-package = "包路径">
</context: component-scan>
(2)使用注解创建User和UserProxy对象
在被增强的类和增强类上面添加@Component
(3)在增强类上面添加注解@Aspect
(4)在spring配置文件中开启生成代理对象
<aop:aspectj-atuoproxy></aop:aspectj-atuoproxy>
4.配置不同类型的解
(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
public class UserProxy {
//前置通知
@Before(value = "execution(* com.xqn.dao.User.add (..))
public void before(){
System.out.println("before.....");
}
//后置通知,在方法返回结果之后执行,有异常不执行
@AfterRrturning = "execution(* com.xqn.dao.User.add (..))
public void afterRrturning(){
System.out.println("AfterRrturning.....");
}
//最终通知,在方法执行之后执行,不管有没有异常都执行
@After = "execution(* com.xqn.dao.User.add (..))
public void after(){
System.out.println("After.....");
}
//环绕通知
@Around(value = "execution(* com.xqn.dao.User.add (..))
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕之前.....");
proceedingJoinPoint.proceed();//被增强的方法执行
System.out.println("环绕之后.....");
}
//异常通知
@AfterThrow(value = "")
}
5.相同切入点抽取
在增强的类中创建一个方法
@Pointcut(value= " ")
public void pointdemo(){
}
通知变为:@After(value = "Pointdemo()")
6.有多个增强类对同一个方法进行增强,设置增强类优先级
在增强类上面添加注解@Order(数字类型值)
数字类型值越小优先级越高
完全注解
创建配置类,不需要创建xml配置文件
@Configuration
@ComponentScan(basePackages={" 包路径"}//组件扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)//生成对象
public class ConfigAop{
}
基于配置文件对AspectJ操作
1.创建两个类,增强类和被增强类,创建方法
public class Book{
public void bug() {
System.out.println(" bug....");
}
}
public class BookProxy{
public void before() {
System.out.println(" before....");
}
}
2.在spring配置文件中创建两个类对象
<!--创建对象-->
<bean id = "book" class = " "> </bean>
<bean id = "bookProxy" class = " "> </bean>
<!--配置aop增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id = "p"
expression="execution( )"/>
<!--配置切面-->
<aop:aspect ref:"bookProxy">
<!--增强作用在具体方法上-->
<aop: before method = "before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
3.在spring配置中配置
JdbcTemplate
什么是JdbcTemplate
Spring框架对JDBC进行封装,使用jdbcTemplate方便实现对数据库操作
准备工作
(1)引入相关jar包
(2)在spring配置文件配置数据库连接池
<bean id ="dataSource" class ="comalibaba.druid.pool.DruidDataSource" destory-method = "close">
<property name ="url" value = " "/>
<property name ="username" value = "root"/>
<property name = "password" value = "root"/>
<property name = "driverClassName" value = "...."/>
</bean>
(3)配置jbdcTemplate对象,注入DataSource
<bean id ="JdbcTemplate" class ="...."></bean>
<property name = "dataSource" ref = "dataSource"></property>
</bean>
(4)创建service,创建dao类,在dao注入jdbcTemplate对象
class BookService
@Service
public class BookService{
}
interface BookDao
public interface BookDao{
}
class BookDaoImpl
@Repository
public class BookDaoImpl implements BookDao{
}
(5)开启组件扫描
<context:component-scan base-package=" "></context:component-scan>
(6)注入属性
@Service
public class BookService{
@Autowired
private BookDao bookDao;
public void addBook(Book book){
bookDao.add(book);
}
}
public interface BookDao{
}
@Repository
public class BookDaoImpl implements BookDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
jdbcTemplate操作数据库(添加)
1.对应数据库创建实体类
public class Book{
private String userId;
private String username;
private String status;
//再加上get和set方法
}
2.编写service和dao
(1)在dao进行数据库添加操作
public interface BookDao{
public void add(Book book);
}
@Service
public class BookService{
@Autowired
private BookDao bookDao;
public void addBook(Book book){
bookDao.add(book);
}
}
(2)调用jdbcTemplate对象里面update方法实现添加操作
update(String sql, Object ... args)用于增加修改和删除
有两个参数
第一个参数:sql语句
第二个参数:可变参数,设置sql语句值
@Repository
public class BookDaoImpl implements BookDao{
@Autowired
private JdbcTemplate jdbcTemplate;
public void add(Book book){
//1.创建sql语句
String sql = "inset into t_book values(?,?,?)";
//2.调用方法实现
Object[] args ={book.getUserId(), book.getUsername(), book.getUserstatus()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
}
(3)测试类
public void testJdbcTemplate(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml";
BookSource bookService = context.getBean("bookService", BookService.class);
Book book = new Book();
book.setUserId("1");
book.setUsername("java");
book.serUserstatus("a");
bookService.addBook(book);
}
jdbcTemplate操作数据库(查询)
1.查询返回某个值
调用jdbcTemplate对象里面queryForObject方法实现查询操作
queryForObject(String sql, Class<T>requiredType)
有两个参数
第一个参数:sql语句
第二个参数:返回类型class
@Repository
public class BookDaoImpl implements BookDao{
@Autowired
private JdbcTemplate jdbcTemplate;
public int select(){
//1.创建sql语句
String sql = "select count(*) from t_book";
//2.调用方法实现
Interger count = jdbcTemplate.queryForObject(sql, Interger.class);
return count;
}
}
2.查询返回对象
调用jdbcTemplate对象里面queryForObject方法实现查询对象操作
queryForObject(String sql, RowMapper<T> rowMapper, Object ...args)
有两个参数
第一个参数:sql语句
第二个参数:RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装。
第三个参数:sql语句值
public Book findBookInfo(String id){
String sql = "select * from t_book where user_id = ?";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book> (Book.class), id);
return book;
}
3.查询返回集合
调用jdbcTemplate对象里面query方法实现查询集合操作
query(String sql, RowMapper<T> rowMapper, Object ...args)
有两个参数
第一个参数:sql语句
第二个参数:RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装。
第三个参数:sql语句值
public Book findAllBook(){
String sql = "select * from t_book where user_id = ?";
List<Book> bookList = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book> (Book.class), id);
return bookList;
}
批量操作
1.批量操作,操作表里面多条记录。
2.jdbcTemplate实现批量添加操作
batchUpdate(String sql, LIst<Object[]> batchArgs)
有两个参数
第一个参数:sql语句
第二个参数:List集合,添加多条记录数据。
public void batchAddBook(List<Object[]> batchArgs){
//遍历添加
String sql = "insert into t_book values(?,?,?)";
//返回影响行数
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Array.toString(ints));
}
事务管理
什么是事务
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败。
事务特性(ACID)
(1)原子性
要么都成功,要么都失败。
(2)一致性
操作之前与操作之后总量不变
(3)隔离性
多事务操作时,各事务之间不会产生影响
(4)持久性
提交后表中数据固定
事务操作例子(转账)
web | service | dao |
视图部分 | 业务操作 1.调用dao两个的方法 | 数据库操作 不写业务 创建两个方法 1.少钱的方法 2.多钱的方法 |
1.创建数据库表,添加记录
2.创建service搭建dao完成对象创建和注入关系
(1)service注入dao,在dao注入jdbcTemplate,在jdbcTemplate注入DataSource
3.dao创建两个方法,多钱和少钱的方法,在service创建方法(转账的方法)
事务操作过程
try{
//第一步 开启事务
//第二步 业务操作
//第三步 没有发生异常,提交事务
} catch (Exception e) {
//第四步 出现异常,事务回滚
}
Spring事务管理介绍
1.事务添加到javaee三层结构里面service层(业务逻辑层)
2.在Spring进行事务管理操作
(1)有两种方式,编程式事务管理和声明式事务管理(常使用)
3.声明式事务管理
(1)基于注解方式
(2)基于xml配置文件方式
4.在spring进行声明式事务管理,底层使用AOP原理
5.Spring事务管理API
提供一个接口,代替事务管理器,这个接口针对不同的框架提供不同的实现类。
注解声明式事务管理
1.在spring配置文件配置事务管理器
<bean id = "transactionManager" class = "....">
<!--注入数据源-->
<property name = "dataSource" ref = "dataSource"> </property>
</bean>
2.在spring配置文件,开启事务注解
(1)在spring配置文件引入名称空间tx
(2)开启事务注解
<tx:annotation-driven transaction-manager = "transactionManager"></tx:annotation-driven>
3.在service类上面(或者service类里面方法上面)添加事务注解
(1)@Transactional这个注解添加到类上面,也可以添加方法上面
(2)如果把这个注解添加到类上面,这个类里面所有的都方法添加事务。
(3)如果把这个注解添加方法上面,为这个方法添加事务
声明式事务管理参数配置
1.propagation :事务传播行为
当一个事务方法被另一个事务方法调用的时候,这个事务方法如何进行。
REQUIRED(required):如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行。
REQUIRED_NEW(required_new):当前的方法必须启动新事物,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起。(分内外层,内外层互不相关,外层出现异常,内层依旧可以提交。)
SUPPORTS(supports):如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中。
2.ioslation:事务隔离级别
(1)事务有特性称为隔离性,多事务操作之间不会产生影响,不考虑隔离性会产生很多问题。
(2)有三个读问题:脏读、不可重复读、幻读
(3)脏读:一个未提交事务读取到另一个未提交事务的数据
(4)不可重复读:一个未提交事务读取到另一提交的修改数据
(5)幻读:一个未提交事务读取到另一提交事务的添加事务
通过设置事务隔离级别解决读问题。
脏读 | 不可重复读 | 幻读 | |
READ UNCOMMITTED read uncommited (读未提交) | √ | √ | √ |
READ COMMITTED read committed (读已提交) | × | √ | √ |
REPEATABLE READ repeatable read (可重复读) | × | × | √ |
SERIALIZABLE serializable (串行化) | × | × | × |
设置:@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
3.timeOut:超时时间
(1)事务需要在一定时间内提交,如果不提交进行回滚。
(2)默认值是-1,设置时间以秒为单位进行计算。
4.readOnly:是否只读
(1)默认值false,值为true只能查询。
5.rollbackFor:回滚,出现哪些异常进行回滚
6.noRollbackFor: 出现哪些异常不进行事务回滚。
xml声明式管理事务
1.在spring配置文件中进行配置
第一步:配置事务管理器
<bean id = "transactionManager" class = " ">
<prooerty name = "dataSource" ref= "dataSource"></property>
</bean>
第二步:配置事务相关参数,通知
<tx:advice id = "txadvice">
<!--配置事务参数-->
<tx:attributes> <!--指定哪种规则的方法上面添加事务-->
<tx:method name "accountMoney"/>
<!--tx:method name "account*"/-->
</tx:attributes>
</tx:advice>
第三步:配置切入点和切面
<aop:config>
<!--切入点-->
<aop:pointcut id = "pt" expression = "execution(....)"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
完全注解声明式事务管理
@Configuration
@ComponentScan(basePackages=" ")//组件扫描
@EnableTransactionManagement//开启事务
public class TxConfig{
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
dataSource.setDriverClassName(" ");
....
}
//创建jdbc对象
@Bean
public JdbcTemplate (DataSource dataSource){//到ioc容器中根据类型找到dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入DataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//事务管理器对象
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource datasource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}