Spring的配置
在pom.xml里面导入坐标
<dependencies> <!--spring数据源--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!-- 数据驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.12</version> </dependency> <!-- 数据源--> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!-- 导入spring集成Junit的包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!-- junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>创建dao及其实现
创建配置文件
<?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"> <bean id="userDao" class="com.example.dao.impl.UserDaoImpl"></bean> </beans>测试类测试
Spring-Mvc
一、简介
MVC(module,view,controller)
客户端–>Tomcat服务器(Tomcat引擎(接收客户端请求,封装res和req,调用请求资源)+web应用(Spring-Mvc代替servlet执行共有的行为)+POJO特有行为)
配置
导入spring—mvc包
配置Servlet
编写pojo,简单的Javabean(简称为控制器controller)
通过注解的方式将Controller配置到Spring容器当中@Component/@Controller
配置spring-mvc.xml配置文件(配置组件扫描)
```xml
<beans xmlns=”http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" 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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd“>
<!--1、mvc注解驱动--> <mvc:annotation-driven/> <!--2、配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean> <!--3、静态资源权限开放--> <mvc:default-servlet-handler/> <!--4、组件扫描 扫描Controller--> <context:component-scan base-package="com.itheima.controller"/>
- 执行访问测试
开发配置
- 导入SpringMvc相关坐标
- 配置SpringMvc核心控制器DispathcerServlet
- 创建Controller和视图
- 使用注解配置
- 配置Spring-Mvc核心文件spring-mvc.xml(组件扫描)
- 客户端发请求,测试
<!--在pom.xml文件中 1.导入spring-mvc的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.5.RELEASE</version> </dependency><!-- 在web.xml中 2.配置前端控制器 注入初始化的参数告知spring-mcv.xml的位置--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mcv.xml</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>package com.example.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; //3.创建视图和容器 //4.使用注解配置 @Controller public class UserController { @RequestMapping("quick") public String save(){ System.out.println("save running"); return "success.jsp"; } }<?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 "> <!--5,组件扫描--> <context:component-scan base-package="com.example.controller"/> </beans>
SpringMvc拦截器(interceptor)
一、作用
类似于Servlet开发中的过滤器Filter,对处理器进行预处理(比如说解决乱码的问题)和后处理。
- 可以被连成链子,即拦截器链,在访问被拦截的方法或者字段时会被顺序调用。
过滤器和拦截器的区别
区别 过滤器 拦截器 使用范围 servlet规范的一部分,任何web项目(Java) SpringMvc框架的工程 拦截范围 在url-pattern配置了/*之后,可以拦截所有要访问的资源 <mvc:mapping path=””>配置拦截路径(/**排除所有)<mvc:exclude-mapping path=””>配置排除的路径 快速入门
自定义拦截器
创建拦截器类实现HanderInterceptor接口
配置拦截器spring-mvc.xml
<!-- 配置拦截器--> <mvc:interceptors> <mvc:interceptor> <!-- 对那些资源执行拦截操作 对所有--> <mvc:mapping path="/**"/> <bean class="com.example.interceptor.MyInterceptorl"/> </mvc:interceptor> </mvc:interceptors>测试效果
package com.example.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptorl implements HandlerInterceptor {
//在目标方法执行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
//在目标方法执行之后 视图返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle....");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
//在整个流程都执行完毕后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
拦截器方法说明
- preHandle:请求之前去调用,返回值为 true 则放行,返回值为 false 则不放行,请求结束
- postHandle:preHandle返回值为true才会调用,会在DispatcherServlet进行视图返回渲染之前被调用,可以对在Controller中处理之后的ModeAndView对象进行操作。
- afterCompletion:整个请求结束之后,preHandle返回值为true,DispatcherServlet渲染了对应的视图的前提下。
拦截器放行 静态资源和用户的登录验证
package com.example.demo5.interceptor; import com.example.demo5.domain.User; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class PrivilegeInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //逻辑判断用户是否登录,即Session中有没有user HttpSession session = request.getSession(); User user = (User) session.getAttribute("user"); String requestURI = request.getRequestURI(); if (requestURI.endsWith("js")||requestURI.endsWith("css")||requestURI.endsWith("jpg")){ return true; } if (user==null) { response.sendRedirect(request.getContextPath()+"/login.jsp"); return false; } //放行,访问目标资源 return true; } }
二、异常处理机制
1.异常处理的思路
- 把异常层层抛出,throw Exception的方式, dao抛到service,service抛到controller,controller抛给前端控制器交由SpringMvc去处理(HandleExceptionResolver) 。
2.异常处理的两种方式
- 使用SpringMvc提供的简单异常处理器SimpleMappingExceptionResolver,按照异常返回页面
异常与视图的映射配置。
<!--配置简单映射异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error"/><!--默认错误视图-->
<property name="exceptionMappings">
<map>
<entry key="com.itheima.exception.MyException" value="error"/>
<entry key="java.lang.ClassNotFoundException" value="error"/>
</map>
</property>
</bean>
- 使用SpringMvc提供的HandleExceptionResolver自定义自己的异常处理器
创建异常处理器实现类HandleExceptionResolver
package com.itheima.resolver; import com.itheima.exception.MyException; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyExceptionResolver implements HandlerExceptionResolver { //关键参数:Exception 异常对象 //返回值 ModelAndView:跳转的一个错误视图的信息 public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView modelAndView=new ModelAndView(); if (e instanceof MyException){ modelAndView.addObject("info","自定义异常"); }else if(e instanceof ClassCastException){ modelAndView.addObject("info","类型转换异常"); } modelAndView.setViewName("error"); return modelAndView; } }配置异常处理器
<!-- 自定义异常配置--> <bean class="com.itheima.resolver.MyExceptionResolver" />编写异常处理界面
package com.itheima.resolver;
import com.itheima.exception.MyException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyExceptionResolver implements HandlerExceptionResolver {
//关键参数:Exception 异常对象
//返回值 ModelAndView:跳转的一个错误视图的信息
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView=new ModelAndView();
if (e instanceof MyException){
modelAndView.addObject("info","自定义异常");
}else if(e instanceof ClassCastException){
modelAndView.addObject("info","类型转换异常");
}
modelAndView.setViewName("error");
return modelAndView;
}
}
Aop(Aspect Oriented Programming)
一、简介
1. 什么是aop
- 面向切面(目标方法和功能增强方法的结合)编程,通过预编译方式和运行期动态代理(完成程序功能之间的去耦合)实现程序功能的统一维护的一种技术。
- opp的一个延续,软件开发的热点,Spring框架的一个重要内容,函数式编程泛型。
- 可以隔离业务逻辑,降低业务逻辑各部分的耦合性,提高程序的可重用性和开发效率。
2. aop的作用和优势
- 作用:程序运行期间,在不修改源码的情况下对方法进行功能增强
- 优势:减少重复代码,提高效率,便于维护
3. aop的底层实现原理–动态代理
- 通过spring提供的动态代理技术实现,在运行期间,spring生成动态代理对象,在执行时进行增强功能的介入,再去调用其方法,增强功能。
4. 常用的动态代理技术
- JDK代理:基于接口
package com.example.spring_aop.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
//目标对象
final Target target=new Target();
//获得增强对象
Advice advice=new Advice();
//参数分别是:1.目标对象加载器2.目标对象相同的接口字节码的对象数组2.InvocationHandler接口
//代理对象返回的对象实际上是一个系统动态生成的proxy对象,这个对象和“真实对象”之间的关联是“有同样的接口”,而不是“能互相转型”。
TargetInterface proxy= (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
//调用代理对象的任何方法,实质执行的都是invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行目标方法
// 前置增强
advice.before();
Object invoke = method.invoke(target, args);
//后置增强
advice.afterReturning();
return invoke;
}
});
//调用代理对象方法
proxy.save();
}
}
cglib:基于父类
package com.example.spring_aop.proxy.cglib; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class ProxyTest { public static void main(String[] args) { //目标对象 final Target target=new Target(); //获得增强对象 Advice advice=new Advice(); //返回值就是动态代理生成的代理对象 基于cglib //1.创建增强器 Enhancer enhancer=new Enhancer(); //2.设置父类(目标) enhancer.setSuperclass(Target.class); //3.设置回调 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //执行前置 advice.before(); //执行目标(反射) Object invoke = method.invoke(target, args); //3。执行后置 advice.afterReturning(); return invoke; } }); //4.创建代理对象 Target target1 = (Target) enhancer.create(); target1.save(); } }
5. aop相关概念
- Target:代理的目标对象
- Proxy:代理对象
- JoinPoint:连接点,spring指那些被拦截到的点(可以被增强的目标对象的方法)可以增强的方法
- Pointcut(切入点):对哪些JointPoint进行拦截的定义 真正被增强了
- Advice(通知/增强):对目标方法进行增强的方法
- Aspect(切面):目标加增强即 切点加通知
- Weaving(织入):将切点和增强结合的过程
6. aop开发注意事项
需要编写的内容
- 编写核心业务代码(目标类中的目标方法)
- 编写切面类,由通知(增强方法 )
- 在配置文件中,配置织入关系 即哪些通知将和哪些连接点结合
技术实现
配置好,执行切点,spring监控到,创建代理对象,调用代理对象同名方法的同时,进行功能增强。
底层代理
有接口:jdk
没有:cglib
二、基于XML的AOP开发
1. 快速入门
导入AOP相关坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- 第三方aspectj配置-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
创建目标接口和目标类(内部有切点)
//目标接口 package com.example.spring_aop.aop; public interface TargetInterface { public void save(); } //目标类 package com.example.spring_aop.aop; public class Target implements TargetInterface { @Override public void save() { System.out.println("save running......"); } }创建切面类(含增强方法)
package com.example.spring_aop.aop; public class MyAspect { public void before(){ System.out.println("前置增强、、、、"); } }将目标类和切面类的创建权限交给spring容器
<bean class="com.example.spring_aop.aop.Target" id="target"/> <!-- 切面对象--> <bean id="myAspect" class="com.example.spring_aop.aop.MyAspect"/>在applicationContext中配置织入关系
<!-- 5.配置织入 告诉框架哪些方法需要进行哪些增强(前置、后置、、、、)--> <!-- 5.1映入aop的命名空间--> <aop:config> <!-- 声明切面--> <aop:aspect ref="myAspect"> <!-- 切点加通知--> <aop:before method="before" pointcut="execution(public void com.example.spring_aop.aop.Target.save())"></aop:before> </aop:aspect> </aop:config>测试代码
package com.itheima.test; import com.example.spring_aop.aop.Target; import com.example.spring_aop.aop.TargetInterface; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AopTest { @Autowired private TargetInterface targetInterface; @Test public void test1(){ targetInterface.save(); } }
2. xml配置AOP详解
切点表达式的写法
execution([修饰符]返回值类型 包名.类名.方法名(参数))
pointcut="execution(public void com.example.spring_aop.aop.Target.save())"访问修饰符可以不写 返回值类型 包名.类名.方法名可以用*(任意)代替
包名与类名之间的**”.”代表当前包下的类,两个..**代表当前包及其子包下的类。
参数列表使用**”..”**代表任意个数,任意类型的参数列表
第三个最常用
execution(* com.example.spring_aop.aop.*.*(..))
通知的类型
配置语法 <aop:通知类型 method=”切面类中的方法名” pointcut=”切点表达式”></aop:通知类型>

package com.example.spring_aop.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { public void before(){ System.out.println("前置增强、、、、"); } public void afterReturning(){ System.out.println("后置增强、、、、"); } //ProceedingJoinPoint正在执行的连接点:切点 public Object around(ProceedingJoinPoint point) throws Throwable { System.out.println("环绕前增强、、、、"); //切点方法 Object proceed = point.proceed(); System.out.println("环绕后增强、、、、"); return proceed; } public void throwing(){ System.out.println("异常抛出增强、、、、"); } public void after(){ System.out.println("最终增强、、、、"); } }小细节:切点表达式的抽取

<aop:config> <!-- 声明切面--> <aop:aspect ref="myAspect"> <!-- 切点加通知--> <aop:before method="before" pointcut="execution(* com.example.spring_aop.aop.*.*(..))"></aop:before> <aop:after-returning method="afterReturning" pointcut="execution(* com.example.spring_aop.aop.*.*(..))"></aop:after-returning> <aop:around method="around" pointcut="execution(* com.example.spring_aop.aop.*.*(..))"></aop:around> <aop:after-throwing method="throwing" pointcut="execution(* com.example.spring_aop.aop.*.*(..))"></aop:after-throwing> <aop:after method="after" pointcut="execution(* com.example.spring_aop.aop.*.*(..))"></aop:after> </aop:aspect> </aop:config>
3. 基于注解的AOP开发
快速入门
开发步骤(前2步同基于xml的AOP开发)
创建目标接口和目标类(内部有切点)
创建切面类(内部有增强方法)
将目标类和切面类的创建权限交给spring容器@compoent

在切面类中使用注解配置织入关系
package com.example.spring_aop.anno; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component("myAspect") @Aspect//标注当前MyAspect是一个切面类 public class MyAspect { //配置前置通知 @Before(value = "execution(* com.example.spring_aop.anno.*.*(..))") public void before(){ System.out.println("前置增强、、、、"); } @AfterReturning(value = "execution(* com.example.spring_aop.anno.*.*(..))") public void afterReturning(){ System.out.println("后置增强、、、、"); } //ProceedingJoinPoint正在执行的连接点:切点 @Around(value = "execution(* com.example.spring_aop.anno.*.*(..))") public Object around(ProceedingJoinPoint point) throws Throwable { System.out.println("环绕前增强、、、、"); //切点方法 Object proceed = point.proceed(); System.out.println("环绕后增强、、、、"); return proceed; } @AfterThrowing(value = "execution(* com.example.spring_aop.anno.*.*(..))") public void throwing(){ System.out.println("异常抛出增强、、、、"); } @After(value = "execution(* com.example.spring_aop.anno.*.*(..))") public void after(){ System.out.println("最终增强、、、、"); } }在配置文件中开启组件扫描和AOP的自动代理
<!--开启组件扫描 --> <context:component-scan base-package="com.example.spring_aop.anno"/> <!-- 开启AOP的自动代理--> <aop:aspectj-autoproxy/>测试
注解配置AOP详解
注解通知的类型

切点表达式的抽取

@AfterThrowing("pointcut()") public void throwing(){ System.out.println("异常抛出增强、、、、"); } @After("MyAspect.pointcut()") public void after(){ System.out.println("最终增强、、、、"); } @Pointcut(("execution(* com.example.spring_aop.anno.*.*(..))")) public void pointcut(){}
编程式事务控制
一、编程式事务控制相关对象
1. PlantformTransactionManager

TransactionDefinition 事务的定义信息对象
事务的隔离级别
读未提交,读已提交,不可重复读,串行化 解决 脏读,不可重复读,和虚读(幻读)
事务的传播行为 维护事务的相关参数
TransactionStatus 提供事务的运行状态
二、基于XML的声明式事务控制
1. 什么是声明式事务控制?
声明的方式控制事务,在配置文件中声明,用在Spring配置文件中声明式的处理事务来代替代码式的处理事务
- 解耦
- 不需要事务管理的时候,修改一下配置文件就OK了,不用改代码
小例子—转账业务
//切点:service层的 transfer方法 通知/增强:事务控制的功能 public void transfer(String outMan, String inMan, double money) { //开启事务 accountDao.out(outMan,money); int i=9/0; accountDao.in(inMan,money); //提交事务 }声明式事务控制的实现
注意事项
切点
通知
配置切面
<?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 http://www.springframework.org/schema/beans/spring-beans.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 "> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/guoshuo?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true"/> <property name="user" value="root"/> <property name="password" value="111111"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> <!--目标对象 内部的方法就是切点--> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置要点11. 配置平台事务管理器 dao实现技术不同,该项的class配置也会不一样--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置要点2.通知 事务增强--> <!-- transactionManager 平台事务管理器--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 设置事务的属性信息--> <tx:attributes> <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/> <tx:method name="save" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/> <tx:method name="findAll" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- 配置要点3配置事务的AOP织入--> <aop:config> <!-- advisor代表只有一个通知 里面的切点表达式也可以抽取--> <!-- <aop:pointcut id="txPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>--> <!-- <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"></aop:advisor>--> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:advisor> </aop:config> </beans>
基于注解的声明式事务控制
<!--组件扫描--> <context:component-scan base-package="com.itheima"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/guoshuo?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true"/> <property name="user" value="root"/> <property name="password" value="111111"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--事务的注解驱动--> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>package com.itheima.service.impl; import com.itheima.dao.AccountDao; import com.itheima.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; //使用注解可以删除set方法 // public void setAccountDao(AccountDao accountDao) { // this.accountDao = accountDao; // } @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED) public void transfer(String outMan, String inMan, double money) { accountDao.out(outMan,money); int i=9/0; accountDao.in(inMan,money); } }package com.itheima.dao.impl; import com.itheima.dao.AccountDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void out(String outMan, double money) { jdbcTemplate.update("update account set money=money-? where name=?",money,outMan); } public void in(String inMan, double money) { jdbcTemplate.update("update account set money=money+? where name=?",money,inMan); } }注解配置声明事务控制解析
- 使用@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)可以在类上或者方法上使用
- 注解使用在类上,那么该类下所有的方法都使用同一套 注解参数配置
- 注解使用在方法上,不同方法可以使用不同的参数配置
- 配置文件要开启注解驱动 <tx:annotation-driven transaction-manager=”dataSourceTransactionManager”/>
MyBatis(持久层框架)
一.简介
1. 原始jdbc操作

2. 原始的jdbc操作 问题分析
数据库连接创建释放 频繁,浪费资源,影响性能
sql语句在代码中硬编码,不宜维护,sql变动需要改代码,耦合
查询操作,需手动将结果进行封装;插入需手动将实体的数据设置到sql的占位符位置
解决方案
- 数据库连接池
- sql抽取到xml配置文件
- 反射、内省等技术,自动映射
3. 什么是MyBatis?
持久层框架、隐藏jdbc繁杂的api,只需要关注sql语句,实体对象和表中数据自动映射
二、快速入门
2.1 开发步骤
添加MyBatis坐标 必须: 数据库驱动和mybatis框架的驱动
<!-- mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.12</version> </dependency> <!-- mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!--junit--> <dependency <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- 日志--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>创建user数据表
编写实体类
编写映射文件UserMapper.xml 在test/mapping里面创建 namespace id resultType(sql语句查询结果返回的结果集)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="userMapper"> <select id="findAll" resultType="com.itheima.domain.User"> select * from user </select> </mapper>编写核心文件SqlMapConfig.xml 配置mybatis的核心内容
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 数据源的环境--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true"/> <property name="username" value="root"/> <property name="password" value="111111"/> </dataSource> </environment> </environments> <!-- 加载映射文件--> <mappers> <mapper resource="com/itheima/mapper/UserMapper.xml"></mapper> </mappers> </configuration>编写测试类
import com.itheima.domain.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class MyBatisTest { @Test public void Test1() throws IOException { //获得核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); //获得SQL session工厂对象 SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); //获得session会话对象 SqlSession sqlSession = build.openSession(); //执行操作 参数就是namespace+id List<User> userList = sqlSession.selectList("userMapper.findAll"); //打印数据 System.out.println(userList); //释放资源 sqlSession.close(); } }
三、Mybatis映射文件概述
四、Mybatis的增删改查的操作
插入和查询代码实例
import com.itheima.domain.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class MyBatisTest { @Test public void Test2() throws IOException { //获得核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); //获得SQL session工厂对象 SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); //获得session会话对象 SqlSession sqlSession = build.openSession(); //创建user对象 User user=new User(); user.setId(7); user.setUsername("guoshuo"); user.setPassword("224583gs"); //执行操作 int insert = sqlSession.insert("userMapper.addOne", user); //mybatis要想执行更新操作,就得提交事务 sqlSession.commit(); //打印数据 System.out.println(insert); //释放资源 sqlSession.close(); } @Test public void Test1() throws IOException { //获得核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); //获得SQL session工厂对象 SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); //获得session会话对象 SqlSession sqlSession = build.openSession(); //执行操作 参数就是namespace+id List<User> userList = sqlSession.selectList("userMapper.findAll"); //打印数据 System.out.println(userList); //释放资源 sqlSession.close(); } }注意事项
插入语句使用insert标签,
在映射文件中使用parameterType属性来指定要插入的数据类型
Sql语句中使用#{实体属性名}方式引用实体中的属性值
插入操作使用的API是sqlSession.insert(“命名空间.id”,实体对象)
插入的操作涉及数据库数据的变化,所以要使用sqlSession.commit()来提交事务
<mapper namespace="userMapper"> <!--插入操作--> <insert id="addOne" parameterType="com.itheima.domain.User"> insert into user values(#{id},#{username},#{password}) </insert> <!--查询操作--> <select id="findAll" resultType="com.itheima.domain.User"> select * from user </select> </mapper>修改操作实例及其注意事项
- 实例
//修改 @Test public void Test3() throws IOException { //获得核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); //获得SQL session工厂对象 SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); //获得session会话对象 SqlSession sqlSession = build.openSession(); //创建user对象 User user=new User(); user.setId(4); user.setUsername("hanrui"); user.setPassword("hanrui1"); //执行操作 int insert = sqlSession.update("userMapper.update", user); //mybatis要想执行更新操作,就得提交事务 sqlSession.commit(); //打印数据 System.out.println(insert); //释放资源 sqlSession.close(); }<!--修改操作--> <insert id="update" parameterType="com.itheima.domain.User"> update user set username=#{username},password=#{password} where id=#{id} </insert>注意事项
- 使用的是update标签。但是我不小心int insert = sqlSession.insert(“userMapper.update”, user);这样子也成功了
- 修改的Api是sqlSession.update();
删除操作
语句
int delete = sqlSession.delete("userMapper.delete", user);配置
<!--删除操作--> <insert id="delete" parameterType="com.itheima.domain.User"> delete from user where id=#{id} </insert>传单个参数的时候{}里面 任意字符串 都行 id=#{id}
小结 光听自己不去想,永远不会发展的好!!!比如说那个 带条件的查询也是需要传递参数的parameterType
五、Mybatis核心配置文件概述 sqlMapperConfig.xml
Mybatis核心配置文件层级关系
详解
enviroments标签
数据库环境的配置,支持多环境配置
mappers 第一种着重看
properties标签
typeAliases标签
mybatis框架已经为我们设置好了一些常用类型的别名
别名 数据类型 string String int Integer double Double boolean Boolean …. …. long Long 小结

六、Mybatis的相应api
SqlSession工厂构造器SqlSessionFactoryBuilder
七、Mybatis的Dao层实现
传统的开发方式 实现接口
三层架构,
service调用dao。。。。。
代理开发方式 不用实现接口
介绍
namespace和mapper接口的全限定名称相同
<mapper namespace="com.itheima.dao.UserMapper">接口的方法名和Mapper.xml里面的id相同 返回值和xml里面定义的resultType相同
<select id="findAll" resultType="user"> select * from user </select>接口方法的参数和xml里面定义的parameterType相同
<insert id="addOne" parameterType="com.itheima.domain.User"> insert into user values(#{id},#{username},#{password}) </insert>
八、Mybatis映射文件深入
动态sql语句
概述
业务逻辑复杂的时候,我们的SQL就是动态变化的
package com.itheima.test; import com.itheima.domain.User; import com.itheima.mapper.UserMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class MapperTest { @Test public void Test1() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = build.openSession(); //mapper就是代理对象 UserMapper mapper = sqlSession.getMapper(UserMapper.class); //模拟条件 真正开发中的user是从web层传递而来的 User condition=new User(); condition.setId(1); condition.setUsername("zhangsan"); condition.setPassword("2245"); List<User> byCondition = mapper.findByCondition(condition); System.out.println(byCondition); sqlSession.close(); } }动态sql之if
根据实体类的不同取值,使用不同的sql语句
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itheima.mapper.UserMapper"> <select id="findByCondition" parameterType="com.itheima.domain.User" resultType="user"> select * from user <where> <if test="id!=0"> id=#{id} </if> <if test="username!=null"> and username=#{username} </if> <if test="password!=null"> and password=#{password} </if> </where> </select> </mapper>for
</select> <select id="findByIds" resultType="user" parameterType="list"> select * from user <where> <foreach collection="list" open="id in (" close=")" item="id" separator=","> #{id} </foreach> </where> </select>sql片段的抽取
<!-- sql语句的抽取--> <sql id="selectUser"> select * from user </sql> <select id="findByCondition" parameterType="com.itheima.domain.User" resultType="user"> <include refid="selectUser"></include> <where> <if test="id!=0"> id=#{id} </if> <if test="username!=null"> and username=#{username} </if> <if test="password!=null"> and password=#{password} </if> </where> </select>
标签深入
typeHandles 类型处理器
可以重写或者创建自定义的转换器来处理不支持或者非标准的类型
步骤一二
package com.itheima.handler; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; public class DateTypeHandler extends BaseTypeHandler<Date> { //负责将java类型转换成数据库需要的类型 public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException { long time = date.getTime(); preparedStatement.setLong(i,time); } //将数据库中的 某些数据的类型 转换成Java类型 //String参数是数据库字段的名称,要转换的那个字段的名称,resultSet是查询出的结果集 public Date getNullableResult(ResultSet resultSet, String s) throws SQLException { //获取结果集中需要的数据(long) 转化为date类型 返回 long aLong = resultSet.getLong(s); Date date=new Date(aLong); return date; } public Date getNullableResult(ResultSet resultSet, int i) throws SQLException { long aLong = resultSet.getLong(i); Date date=new Date(aLong); return date; } public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException { long aLong = callableStatement.getLong(i); Date date=new Date(aLong); return date; } }步骤三 sqlMapConfig.xml里面
<!--注册类型处理器--> <typeHandlers> <typeHandler handler="com.itheima.handler.DateTypeHandler"></typeHandler> </typeHandlers>plugins标签
可以使用第三方插件来拓展功能, 分页助手:PageHandle 真他娘的牛逼
导入坐标
<--分页助手--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5</version> </dependency> <--解析器--> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>0.9.1</version> </dependency>核心文件配置
<!--配置分页助手插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"></property> </plugin> </plugins>测试
@Test public void test3() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); //设置分页的参数 当前页+每页的显示条数 PageHelper.startPage(2,3); //执行查询 List<User> all = mapper.findAll(); for (User u:all ) { System.out.println(u); } //获得与分页相关的参数 PageInfo<User> pageInfo=new PageInfo<User>(all); System.out.println("当前页:"+pageInfo.getPageNum()); System.out.println("每页显示条数"+pageInfo.getPageSize()); System.out.println("总条数"+pageInfo.getTotal()); System.out.println("总页数"+pageInfo.getPages()); System.out.println("上一页"+pageInfo.getPrePage()); System.out.println("下一页"+pageInfo.getNextPage()); // sqlSession.commit(); sqlSession.close(); }
九、核心文件常用标签
十、Mybatis的多表操作
一对一
实体类
package com.itheima.domain;
import java.util.Date;
public class Orders {
private int id;
private Date orderTime;
private Double total;
private int uid;
//当前订单属于哪一个用户
private User user;
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", orderTime=" + orderTime +
", total=" + total +
", uid=" + uid +
", user=" + user +
'}';
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getOrderTime() {
return orderTime;
}
public void setOrderTime(Date orderTime) {
this.orderTime = orderTime;
}
public Double getTotal() {
return total;
}
public void setTotal(Double total) {
this.total = total;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
OrderMapper.xml resultMap 手动指定表中字段和实体属性的关系
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.OrderMapper">
<resultMap id="orderMap" type="order">
<!-- 手动指定字段与实体属性的映射关系
column:数据表的字段名称
property:实体属性名称
-->
<!-- order表的主键是id,单独有一个叫做id的标签 普通属性用result-->
<id column="id" property="id"></id>
<result column="ordertime" property="orderTime"></result>
<result column="total" property="total"></result>
<result column="uid" property="uid"></result>
<!-- <result column="uid" property="user.uid"></result>-->
<!-- <result column="username" property="user.username"></result>-->
<!-- <result column="password" property="user.password"></result>-->
<!-- <result column="birthday" property="user.birthday"></result>-->
<!-- property当前实体(order)属性名称(private User user) javaType当前实体(order)中的属性的类型(User) -->
<association property="user" javaType="user">
<id column="uid" property="uid"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<select id="findAll" resultMap="orderMap">
select * from user u,orders o where o.uid=u.uid;
</select>
</mapper>
一对多
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper">
<resultMap id="UserMap" type="user">
<id column="uid" property="uid"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<!-- 配置集合信息 property集合名称-->
<collection property="order" ofType="order">
<!-- 封装order的数据-->
<id column="id" property="id"></id>
<result column="ordertime" property="orderTime"></result>
<result column="total" property="total"></result>
<result column="uid" property="uid"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="UserMap">
select * from user u,orders o where u.uid=o.uid
</select>
</mapper>
多对多
<!--多对多-->
<resultMap id="userRoleMap" type="user1">
<!-- 封装user内部的roleList信息-->
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="email" property="email"></result>
<result column="password" property="password"></result>
<result column="phoneNum" property="phoneNumber"></result>
<result column="userId" property="userId"></result>
<result column="roleId" property="roleId"></result>
<!--封装 roleList-->
<collection property="roleList" ofType="role">
<id column="id" property="id"></id>
<result column="roleName" property="roleName"></result>
<result column="roleDesc" property="roleDesc"></result>
</collection>
</resultMap>
<select id="findUserAndRoleAll" resultMap="userRoleMap">
select *
from sys_user u,
sys_user_role ur,
sys_role r
where u.id = ur.userId
and ur.roleId = r.id
</select>
十一、Mybatis注解开发常用注解
常用注解
简单查询
在核心配置文件sqlMapConfig.xml里面指定接口所在的包。
<mappers> <!-- 指定接口所在的包--> <package name="com.itheima.mapper"/> </mappers>在接口上面添加注解
package com.itheima.mapper; import com.itheima.domain.User; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; public interface UserMapper { @Insert(" insert into user values(null,#{username},#{password},null)") void save(User user); @Update(" update user set username=#{username},password=#{password} where uid=#{uid}") void update(User user); @Delete(" delete from user where uid=#{id}") void delete(int uid); @Select("select * from user where uid=#{id}") User findById(int uid); @Select("select * from user") List<User> findAll(); }insert
update用update更新多个字段的时候一定要用逗号隔开,而不是AND,切忌粗心大意!!!
delete
select
复杂查询
一对一
@Select(" select * from orders o,user u where o.uid=u.uid") @Results( {@Result(column = "id",property ="id"), @Result(column ="ordertime",property ="orderTime" ), @Result(column ="total",property = "total"), @Result( javaType = User.class,// 要封装的实体类型 property = "user",// 要封装的属性名称 column = "uid", //根据哪个字段去封装user表的数据 one = @One(select ="com.itheima.mapper.UserMapper.findById" )// select属性,代表查询哪个接口的方法获得所要的对应的数据 ), } ) public List<Orders> findAll();一对多
<--UserMapper--> @Select("select * from user") @Results( { @Result(column = "uid",property = "uid"), @Result(column = "username",property = "username"), @Result(column = "password",property = "password"), @Result( javaType = List.class, property = "ordersList", column = "uid", many = @Many(select = "com.itheima.mapper.OrderMapper.findOrderById") ) } ) public List<User> findUserAndOrderAll(); <--OrderMap--> @Select("select * from orders where uid=#{uid}") @Results( { @Result(column = "id",property = "id",id = true), @Result(column = "ordertime",property = "orderTime"), @Result(column = "total",property = "total"), @Result(column = "uid",property = "uid"), } ) public List<Orders> findOrderById(int uid);result 结果集封装
results 封装多个结果
one 一对一
many 一对多
@Select("select * from user")
@Results({
@Result(id=true,column = "uid",property = "uid"),
@Result(id=true,column = "username",property = "username"),
@Result(id=true,column = "password",property = "password"),
@Result(
property = "roleList",
column = "uid",
javaType = List.class,
many = @Many(select = "com.itheima.mapper.RoleMapper.findByUid")
//public interface RoleMapper{
// @Select("select * from sys_user_role ur,sys_role r where ur.roleId=r.id and ur.userId=#{id}")
// public List<Role> findByUid(int uid);
// }
)
})
public List<User> findUserAndRoleAll();
}
SSM框架整合
准备工作
mybatis–dao spring-三层融合 spring-mvc-web




















