Spring(IOC、AOP和事务)
1763392456 人气:0目录
Spring介绍
Spring IOC
传统代码对象管理的弊端
实现过程
bean标签属性介绍
对象创建方式
工厂bean
bean的作用域
SpringBean的生命周期***
依赖注入
注解实现IOC
完全注解开发(JavaConfig)
Spring AOP
AOP的实现机制
JDK动态代理实现
CGlib代理
Spring中使用AOP
切入点方法的定义
获取切入点信息
使用AspectJ注解开发
Spring-JDBC 操作数据库
事务
Spring介绍
Spring是一个设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
特点
1.方便解耦,简化开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2.AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统面向对象实现的功能可以通过AOP轻松应付。
3.声明式事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
4.方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
5.方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
6.降低Java EE API的使用难度
组织结构
ORM- object relation mapping
OXM-Object xml mapping
JMS - Java消息服务(Java Message Service ,JMS)
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。Socket是传输控制层协议,WebSocket是应用层协议。
Portlet是一种Web组件-就像servlets-是专为将合成页面里的内容聚集在一起而设计的。通常请求一个portal页面会引发多个portlets被调用。每个portlet都会生成标记段,并与别的portlets生成的标记段组合在一起嵌入到portal页面的标记内
spring全家桶:spring,Spring Data、Spring MVC、Spring Boot、Spring Cloud(微服务)
Spring核心模块
- spring-core:依赖注入IOC与DI的最基本实现
- spring-beans:Bean工厂与bean的装配
- spring-context:spring的context上下文即IoC容器
- spring-context-support
- spring-expression:spring表达式语言
Spring IOC
IOC是 Inverse of Control 的简写,意思是控制反转。是降低对象之间的耦合关系的设计思想。就是将对象统一管理,统一分配
传统代码对象管理的弊端
传统的调用方式时service层调用dao层,但是当我们的需求发生改变的时候,我们需要可能需要在写一个dao的实现类,然后再改变service的继承关系。但是这种改变源代码的方式不利于测试。我们可以用一种方式,就是把所有的对象提取出来,让一个容器来统一的管理。
实现过程
1、添加jar包
<!-- Spring的核心工具包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!--在基础IOC功能上提供扩展服务,还提供许多企业级服务的支持,有邮件服务、 任务调度、远程访问、缓存以及多种视图层框架的支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring IOC的基础实现,包含访问配置文件、创建和管理bean等 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring context的扩展支持,用于MVC方面 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring表达式语言 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
2、创建配置文件 applicationContext.xml (放在resource下面)
<?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">
</beans>
3、在配置文件中创建对象
<bean id="对象名" class="类的完整路径">
<property name="属性名" ref="对象的id值"></property>
</bean>
4、加载配置文件,获得对象
ApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");
Users users=(Users)app.getBean("u1");
举例:
模拟一个传统Service调用dao的过程
这里我们设计两个对象的调用过程, 我们可以把这两个对象写到配置文件中
首先new 两个对象
因为在UserServiceImpl中有个UserDao的属性
最后再在从配置文件中得到已经创建好的对象
注意:给属性赋值的时候,系统可以调用set方法,但是通过测试类得到属性的时候,只能调用配置文件
bean标签属性介绍
单列:每次拿到的对象都是同一个
对象创建方式
1、无参构造
2、有参构造
3、静态方法创建对象
4、非静态方法
工厂bean
spring有两种类型的bean,一种是普通的bean(和上面一样),另一种是工厂bean(FactoryBean)
普通bean:在配置文件中定义bean类型就是返回类型
工厂bean:配置文件中定义的类型和返回的类型不一样
实现步骤:
(1)创建类,让类实现FactoryBean接口
(2)实现接口方法,在实现的方法中定义bean的类型
bean的作用域
设置bean的实例是单例还是多例
单例(默认)
<bean id="myBean" class="com.vv.bean.MyBean" scope="singleton"></bean>
多例
<bean id="myBean" class="com.vv.bean.MyBean" scope="prototype"></bean>
设置指为singleton时:加载配置文件的时候就会创建对象
设置值为prototype时:在调用getBean方法的时候才创建多实例对象
SpringBean的生命周期***
1)根据配置文件调用 Bean 构造方法或工厂方法实例化 Bean。
2)利用依赖注入(DI)完成 Bean 中属性值的配置注入。
3)如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前Bean 的 id 值。
4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
5)如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方postProcessBeforeInitialzation() 对Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
7)如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。初始化bean的时候执行,可以针对某个具体的bean进行配置。afterPropertiesSet 必须实现 InitializingBean接口。实现 InitializingBean接口必须实现afterPropertiesSet方法。
8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
10)如果在中指定了该 Bean 的作用范围为 scope="singleton",则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在中指定了该 Bean 的作用范围为scope="prototype",则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该Bean。
11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
依赖注入
DI是Dependency Injection的缩写,意思是依赖注入,说的是创建对象实例时,同时为这个对象注入它所依赖的属性
分类:一种是调用属性的set方法赋值,第二种使用构造方法赋值
1、set注入指(调用属性的set方法)
2、构造方法注入
c、p命名空间
3、复杂类型的注入(Object[],list,set,map,Properties类型)
<bean id="t1" class="com.vv.bean.Teacher">
<property name="objects">
<list>
<value>vv</value><!--字符串-->
<value>19</value><!--数-->
<ref bean="u1"></ref><!--引用类型-->
</list>
</property>
<property name="list">
<list>
<value>xx</value><!--字符串-->
<value>19</value><!--数-->
</list>
</property>
<property name="set">
<set>
<value>77</value><!--字符串-->
<value>19</value><!--数-->
<ref bean="u1"></ref><!--引用类型-->
</set>
</property>
<property name="map">
<map>
<entry key="班长" value="77"></entry>
<entry key="user" value-ref="u1"></entry><!--引用类型-->
</map>
</property>
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">123</prop>
</props>
</property>
</bean>
4、自动装配(注入):Spring会在上下文中自动寻找,并自动给bean装配属性
autowire:
no 不自动装配(默认值)
byName 属性名=id名 ,调取set方法赋值
byType 属性的类型和id对象的类型相同,当找到多个同类型的对象时报错,调取set方法赋值(保证id唯一)
constructor 构造方法的参数类型和id对象的类型相同,当没有找到时报错。调取构造方法赋值(保证class唯一)
注解实现IOC
1、配置文件中添加约束
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
2、配置注解扫描:指定扫描包下所有类中的注解,扫描包时,会扫描包所有的子孙包
<!--扫描包设置-->
<context:component-scan base-package="com.vv"></context:component-scan>
3、注解
(1)添加到类名上
@Component("对象名") :将某个类注册到Sping中,装配Bean componet的衍生注解: @Service("对象名") // service层 @Controller("对象名") // controller层 @Repository("对象名") // dao层 @Scope(scopeName="singleton") //单例对象 @Scope(scopeName="prototype") //多例对象
(2)添加到属性
@Value("属性值") private String name; @Autowired//默认使用byType,如果通过Repository注解了两个相同类型的对象就会报错 @Qualifier("uDao")//这样就可以指定对应的对象了 private UserDao userDao; //@Resource(name="对象名") == @Autowired + @Qualifier("name") @Resource(name="uDao") private UserDao userDao; 注意:通过注解配置,对象里面的属性就不需要有set方法了
可能会没有Resource这个注解,需要加入依赖包
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.1</version> </dependency>
(3)添加到方法上
@PostConstruct //等价于init-method属性 public void init(){ System.out.println("初始化方法"); } @PreDestroy //等价于destroy-method属性 public void destroy(){ System.out.println("销毁方法"); }
完全注解开发(JavaConfig)
1、创建配置类,替代xml配置文件
@Configuration//作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.vv"})//开启组件扫描
public class SpringConfig {
}
2、加载配置类
ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
此方法在springboot中使用
Spring AOP
AOP(Aspect Oriented Programming)即面向切面编程。即在不改变原程序的基础上为代码段增加新的功能。应用在权限认证、日志、事务。
AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
AOP的实现机制
- JDK 的动态代理:针对实现了接口的类产生代理。InvocationHandler接口
- CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术 生成当前类的子类对象,MethodInterceptor接口
JDK动态代理实现
创建接口实现类的代理对象,增强方法
//代理类
public class dai implements InvocationHandler {
private UserService service;//目标对象
public dai(UserService service) {
this.service = service;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//本方法中的其他输出输入增强
//proxy 代理方法被调用的代理实例
System.out.println("日志开始");
//调取真实的方法
Object invoke = method.invoke(service, args);
System.out.println("日志结束");
return invoke;
}
}
public class Test {
public static void main(String[] args) {
UserService service = new UserServiceImpl();
dai d = new dai(service);
//生成代理对象,这里不能转换成一个实际的类,必须是接口类型
UserService us = (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), d);
us.insert(new User());
}
}
CGlib代理
使用JDK创建代理有一个限制,它只能为接口创建代理实例.这一点可以从Proxy的接口方法 newProxyInstance(ClassLoader loader,Class [] interfaces,InvocarionHandler h)中看的很清楚,第二个入参 interfaces就是需要代理实例实现的接口列表.
CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中完成类的增强
1、添加依赖包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
2、创建个工具类(代理器)
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("日志开始");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("日志结束");
return o1;
}
}
3、测试
public class Test {
public static void main(String[] args) {
Try t = new Try();
//创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(t.getClass());
enhancer.setCallback(new CglibProxy());
Try t2 = (Try) enhancer.create();
t2.test();
}
}
两种代理方式的区别:
1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;
注:spring同时使用了这两种方式,底层会自行判断应该使用哪种
Spring中使用AOP
AOP中的术语
(1)连接点:类中可以被增强的方法
(2)切点:实际被真正增强的方法
(3)增强(通知):增强逻辑的部分(日志处理、权限判断)
(4)切面:把增强运用到切点的过程
步骤
1、添加依赖包
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
2、创建增强类(本质上就是一个普通类)
public class MyAop {
//前置增强-调用目标方法之前执行
public void before(){
System.out.println("日志开始");
}
//后置增强-调用目标方法之后执行
public void after(){
System.out.println("日志开始");
}
/**
* @param joinPoint 获取切入点
*/
public void around(ProceedingJoinPoint joinPoint){
System.out.println("环绕开始");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕结束");
}
//异常增强
public void ex(){
System.out.println("异常增强");
}
//最终增强
public void end(){
System.out.println("最终增强");
}
}
3、添加aop命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
3、配置文件
增强类型:
前置增强:目标方法运行之前调用 aop:before
后置增强(如果出现异常不会调用):在目标方法运行之后调用 aop:after-returning
环绕增强:在目标方法之前和之后都调用 aop:around
异常增强:程序出现异常时执行(要求:程序代码中不要处理异常) aop:after-throwing
最终增强(无论是否出现 异常都会调用):在目标方法运行之后调用 aop:after
<bean id="udao" class="com.vv.dao.impl.UserDaoImpl"></bean>
<bean id="uService" class="com.vv.service.impl.UserServiceImpl" >
<property name="userDao" ref="udao"></property>
</bean>
<!--创建增强类对象-->
<bean id="myaop" class="com.vv.util.MyAop"></bean>
<!--建立增强类和目标方法的关系-->
<aop:config>
<aop:pointcut id="mypc" expression="execution(void com.vv.service.UserService.insert())"/>
<aop:aspect ref="myaop">
<!--前置增强-->
<aop:before method="before" pointcut-ref="mypc"></aop:before>
<!--后置增强-->
<aop:after method="after" pointcut-ref="mypc"></aop:after>
<!--环绕增强-->
<aop:around method="around" pointcut-ref="mypc"></aop:around>
<!--异常增强-->
<aop:after-throwing method="ex" pointcut-ref="mypc"></aop:after-throwing>
<!--最终增强-->
<aop:after-returning method="end" pointcut-ref="mypc"></aop:after-returning>
</aop:aspect>
</aop:config>
4、测试
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = app.getBean("uService", UserService.class);
service.insert();
注意:(1)环绕增强需要使用ProceedingJoinPoint 作为参数(2)注意标签顺序
另一种方式
特殊的前置增强-->Advisor前置增强实现
(1)创建增强类,要求该类实现MethodBeforeAdvice接口
(2)设置配置文件,定义增强和切入点的关系
public class agency implements MethodBeforeAdvice {
/**
* @param method 要执行目标对象的方法
* @param objects 参数
* @param o 目标对象
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+" 的 "+method.getName()+" 被执行了");
}
}
<bean id="userDao" class="com.vv.dao.Impl.UserDaoImpl"></bean>
<bean id="agency" class="com.vv.agency"></bean>
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.vv.dao.Impl.*.*(..))"/>
<aop:advisor advice-ref="agency" pointcut-ref="pointcut"/>
</aop:config>
切入点方法的定义
表达式匹配规则举例:
1、public * addUser(com.pb.entity.User):“*”表示匹配所有类型的返回值。
public int addUser(User u); public String addUser(User u);
2、public void *(com.pb.entity.User):“*”表示匹配所有方法名
public void selectUser(User u); public void a(User u);
3、public void addUser (..):“..”表示匹配所有参数个数和类型。
public void addUser(int a); public void addUser(int b,int c);
4、com.pb.service.*.*(..):匹配com.pb.service 包下所有类的所有方法。
public void com.pb.service.A.a(); public String com.pb.service.B.a();
5、* com.pb.service..*(..):匹配com.pb.service 包及子包下所有类的所有方法
获取切入点信息
通过JoinPoint对象获取信息
public void before(JoinPoint joinPoint){
System.out.println("日志开始");
System.out.println(new Date());
System.out.println("对象信息:"+joinPoint.getTarget().getClass().getSimpleName());
System.out.println("方法信息:"+joinPoint.getSignature());
System.out.println("参数信息:"+joinPoint.getArgs());
}
使用AspectJ注解开发
1、启动扫描Spring注解的包,启动aspectJ的注解方式
<!--扫描Spring注解包-->
<context:component-scan base-package="com.vv"></context:component-scan>
<!--开启aspectj,生成代理对象-->
<aop:aspectj-autoproxy/>
2、增强类使用@Componet注解,添加@Aspect
3、在增强类中定义切点,指明要修饰的方法
@Component
@Aspect //生成代理对象
public class MyAop {
//定义切点,由于注解必须修饰方法,所以这里要定义一个空方法
@Pointcut("execution(* com.vv.service.*.*(..))")
public void p(){}
//前置增强-调用目标方法之前执行
@Before("p()")
public void before(){
System.out.println("日志开始");
}
//后置增强-调用目标方法之后执行
@AfterReturning("p()")
public void after(){
System.out.println("日志结束");
}
/**
* @param joinPoint 获取切入点
*/
@Around("p()")
public void around(ProceedingJoinPoint joinPoint){
System.out.println("环绕开始");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕结束");
}
//异常增强
@AfterThrowing("p()")
public void ex(){
System.out.println("异常增强");
}
//最终增强
@After("p()")
public void end(){
System.out.println("最终增强");
}
}
或者直接这样使用
注解方式中注解的顺序
Spring-JDBC 操作数据库
导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
原始的使用方式:
public class demo1 {
public static void main(String[] args) throws PropertyVetoException {
//1.创建c3p0链接池
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/try");
dataSource.setUser("root");
dataSource.setPassword("");
//创建jdbcTemplate对象
JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);
//创建sql语句
String sql="insert into users values (?,?,?);";
jdbcTemplate.update(sql, 4, "vv", "321");
}
}
Spring管理jdbcTemplate
让dao实现类继承JdbcDaoSupport,父类中可以提供jdbcTemplate对象
然后修改配置文件,在配置文件中配置数据源信息
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/try"/>
<property name="user" value="root"/>
<property name="password" value=""/>
</bean>
<bean id="userDao" class="com.vv.dao.Impl.UserDaoImpl">
<property name="dataSource" ref="ds"></property>
</bean>
dao实现类
public class UserDaoImpl extends JdbcDaoSupport implements UserDao {
@Override
public int insert(User user) {
String sql = "insert into users values (?,?,?);";
int usid = user.getUsid();
String username = user.getUsername();
String password = user.getPassword();
int update = getJdbcTemplate().update(sql, usid, username, password);
return update;
}
}
之前我们是吧数据库的配置信息放在properties文件中,我们这里也可以用同样的方式
jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/try
username=root
password=
配置文件
<context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="FALLBACK"></context:property-placeholder>
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="user" value="${username}"/><!--这里的username默认调用的系统的username,在上面添加system-properties-mode="FALLBACK-->
<property name="password" value="${password}"/>
</bean>
<bean id="userDao" class="com.vv.dao.Impl.UserDaoImpl">
<property name="dataSource" ref="ds"></property>
</bean>
CRUD操作操作
public class UserDaoImpl extends JdbcDaoSupport implements UserDao {
@Override
public int insert(User user) {
String sql = "insert into users values (?,?,?);";
int usid = user.getUsid();
String username = user.getUsername();
String password = user.getPassword();
int update = getJdbcTemplate().update(sql, usid, username, password);
return update;
}
@Override
public User findbyid(int id) {
String sql ="select * from users where usid = ?";
User user = getJdbcTemplate().queryForObject(sql, new Object[]{id}, new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setUsid(resultSet.getInt("usid"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
});
return user;
}
@Override
public List<User> findAll() {
String sql = "select * from users";
List<User> list = getJdbcTemplate().query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setUsid(resultSet.getInt("usid"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
});
return list;
}
@Override
public int update(int id, User user) {
String sql = "update users set username = ?, password = ? where usid =?";
int update = getJdbcTemplate().update(sql,user.getUsername(),user.getPassword(),id);
return update;
}
@Override
public int delete(int id) {
String sql = "delete from users where usid = ?";
int update = getJdbcTemplate().update(sql, id);
return update;
}
}
事务
通过sql将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性(准确性)。
事务通常是以begin transaction开始,以commit或rollback结束。
事务执行的流程:开启事务->执行insert,update,delete->commit/rollback
事务的特性ACID
1 - 原子性(atomicity)
事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。
2、一致性(consistency)
事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。
3、隔离性(isolation)
一个事务的执行不能被其他事务所影响。企业级的数据库每一秒钟都可能应付成千上万的并发访问,因而带来了并发控制的问题。
4、持久性(durability)
一个事务一旦提交,事务的操作便永久性的保存在DB中。即使此时再执行回滚操作也不能撤消所做的更改
事务传播行为propagation
多事务方法(对数据库数据进行改变)之间进行调用,这个过程中事务是如何进行管理的。
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。规定了事务方法和事务方法发生嵌套调用时事务如何进行传播例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
七种传播行为*
事务传播行为失效的情况
spring事务是基于代理来实现的,所以:
(1)private、fifinal、static 方法无法被代理,所以添加事务无效
(2)当绕过代理对象, 直接调用添加事务管理的方法时, 事务管理将无法生效。比如直接new出的对象。
(3)在同一个类下,有2个方法,A、B,A没有事务,B有事务,但是A调用B时,方法B被标记的事务无效。 究其原因,因为此类的调用对象为代理对象,代理方法A调用真正的被代理方法A后,在被代理方法A中才会去调用方法B,此时this对象为被代理的对象,所以是不会通知到代理对象,也就变成了第二种情况,绕过了代理对象。所以无事务隔离级别
事务的隔离级别
脏读:读取到一个未提交事务的数据,若事务回滚可能导致数据不一致
不可重复读:一个未提交的事务两次读取的数据不一致
幻读:一个未提交的事务读取到另一个提交事务添加数据
1. Serializable(串行化):可避免脏读、不可重复读,幻读情况的发生。
2. Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。
3. Read committed(读已提交):可避免脏读情况发生。
4. Read uncommitted(读未提交):最低级别,以上情况均无法保证。
事务操作(Spring事务管理)
一般添加到JavaEE三层结构里面的Service层
有两种事务管理操作:编程式事务管理(在代码前后添加代码,不常用)和声明式事务管理(常用)
声明式事务管理(底层使用到了AOP):
1、基于注解
修改配置文件
<!--创建事务管理器--> <bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="ds"/> </bean> <!--开启事务注解--> <tx:annotation-driven transaction-manager="manager"></tx:annotation-driven>
添加注解
注解中可配置的参数
propagation:事务传播行为(默认Propagation.REQUIRED)
isolation:隔离级别
timeout:超时时间(在一定时间不提交就会回滚,默认-1不超速)
readOnly:是否只读(默认false,可以进行增伤改查,true:只能查询)
rollbackFor:回滚
norollbackFor:不回滚
2、基于XML
<!--创建事务管理器--><!--这里id好像必须写成transactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="ds"/>
</bean>
<!--配置增强-->
<tx:advice id="txAdvice">
<tx:attributes>
<!--哪种规则的方法添加事务-->
<tx:method name="insert*" propagation="REQUIRED"/><!--以insert开头的方法-->
<tx:method name="update*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--配置切入点和切面-->
<aop:config>
<!--切入点-->
<aop:pointcut id="mypoint" expression="execution(* com.vv.service.*.*(..))"/>
<!--切面 把事务通知配置到切点上-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"></aop:advisor>
</aop:config>
加载全部内容