Spring
Spring课程介绍
Spring面向接口编程
面向接口编程
上面时javaweb项目的一般流程
耦合度太高,当修改功能时需要直接修改代码,如何改进?就例如上面只能调用2的方法,如果想要调用3就得改代码
解决思路如下:
如何降低耦合度?不直接使用new 创建对象
我们可以通过配置文件在创建servlet实例的时候就给相应的父类赋值,这样就不需要创建多个servlet类,通过配置文件动态的为父类productService赋值这样就不需要直接为父类new子类对象
Spring简介
Spring体系架构
官网:
Spring(IoC)架构图:
1. Core Container
2. AOP,Aspects
3. web
也就是说SpringMVC实际上是一个组件,不过我们将其当成一个框架进行学习
4.Data Access
5.Test
框架部署
Spring IoC
Spring IoC架构部署
首先如上图所示,我们需要通过dependency来进行导包操作,可是因为maven具有传递依赖性所以我们导入context后就会自动将其他的依赖也会导入
在项目的pom.xml下面添加依赖:
代码如下:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
创建Spring配置文件
<?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">
<!--对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则-->
<!--通常一个框架为了开发者能够正确的配置,都会提供xml的规范文件(dtd\xsd)-->
<!--xsd 可以根据配置引入多个,只要引入了相应的xsd,可以使用相应标签规则属性-->
</beans>
Spring IoC的使用
使用IoC创建对象
创建一个实体类
注意创建实体类时一定要设置构造方法(有参,无参)不然会报错
public class Student {
private String stuNum;
private String stuName;
private String stuGender;
private int stuAge;
private Date enterenceTime;//入学日期
}
在Spring配置文件中配置实体类
初始化Spring对象工厂,获取对象
//通过Spring容器创建Student对象
//1、初始化Spring容器,加载Spring配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、通过Spring容器,获取Student对象
Student student2 = (Student) context.getBean("stu");
IoC和DI
DI依赖注入
set方法注入
简单类型及字符串赋值
注意我们赋值的时候即使是给的字符串,可是底层会自动检测你对象属性的类型然后进行相应的类型转换
日期对象类型赋值
自定义对象类型赋值
集合类型
构造器注入
简单类型、对象、字符串
注意使用构造器注入时注意赋值的顺序,他会按照你所给的顺序为对象赋值,不会像set注入那么只能,但是你可以通过index属性调整顺序
public class Student {
private List<String> hobbies;
private Set<String> sets;
private Map<String,Object> maps;
private Properties properties;
public Student(List<String> hobbies, Set<String> sets, Map<String, Object> maps, Properties properties) {
this.hobbies = hobbies;
this.sets = sets;
this.maps = maps;
this.properties = properties;
}
}
-----------------------------------------------------------------------------------------------
<bean id="stu2" class="com.liguoqing.ioc.bean.Student">
<constructor-arg index="0">
<list>
<value>11</value>
<value>13</value>
<value>12</value>
</list>
</constructor-arg>
<constructor-arg index="1">
<set>
<value>11</value>
<value>13</value>
<value>12</value>
</set>
</constructor-arg>
<constructor-arg index="2">
<map>
<entry>
<key><value>1</value></key>
<value>value</value>
</entry>
<entry>
<key><value>2</value></key>
<value>value2</value>
</entry>
</map>
</constructor-arg>
<constructor-arg index="3">
<props>
<prop key="k1">value1</prop>
<prop key="k2">value2</prop>
</props>
</constructor-arg>
</bean>
bean的作用域
bean的生命周期方法
自动装配
Ioc工作原理
注意加载解析文件时,只会创建解析文件中的(单例模式-饿汉)的对象
SpringIoc-注释配置
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<?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">
<!-- 声明使用注解配置-->
<context:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围-->
<context:component-scan base-package=""/>
</beans>
Ioc常用注解
@Component
类注解的意思就是这个注释是直接声明在实体类里面的
@Scope
@Lazy
@PostConstruct
注意方法注解指的是声明在方法之前的注解
@PreDestory
@Autowired
注意上面的@Qualifier就相当于声明了根据类设置的名字进行装配
@Resource
@Component
public class House {
//@Resource的name属性的值与Person类的@Component的value值相对应【都是 renlei】,所以可以进行装配
@Resource(name = "renlei")
private Person p1;
//@Resource的name属性的值与Person类的@Component的value值不对应,一个是relei,一个是person,所以装配失败
@Resource(name = "person")
private Person p1;
//@Reource 没有指定name的值,所以容器会拿这个p1变量的名字去Bean池子中查找id为renlei的bean并装配到这个renlei变量中。装配成功
@Reource
private Person renlei;
//@Reource 没有指定name的值,所以容器会拿这个p1变量的名字去Bean池子中查找id为p1的bean并装配到这个p1变量中。如果找不到,就按照类型来进行装配,都是Person类,所以装配成功
@Reource
private Person p1;
}
代理设计模式
静态代理
动态代理
动态代理,几乎可以为所有的类产生代理对象
动态代理的实现方式有两种: JDK动态代理 CGLib动态代理 jdk动态代理实现,
代码如下:
JDK动态代理
需要注意什么东西?因为我们的动态代理获取代理对象时,需要知道被代理对象接口以及类加载器,所以只有实现了接口的对象才能产生代理
package com.liguoqing.dao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理:是通过被代理对象实现的接口产生其代理对象
* 1:创建一个类,实现InvocationHandler接口,重写invoke方法
* 2:在类中定义一个Object类型的变量,用于传递被代理对象,并提供这个变量的有参构造器,用于将被代理对象传递进来
* 3:创建getProxy方法,用于创建并返回代理对象
* */
public class JDKMyDynamicProxy implements InvocationHandler {
//被代理对象
private Object obj;
public JDKMyDynamicProxy(Object obj) {
this.obj = obj;
}
//产生代理对象,返回代理对象
public Object getProxy(){
//1、获取被代理对象的类加载器
ClassLoader classLoader = obj.getClass().getClassLoader();
//2、获取被代理对象的类所实现的接口
Class<?>[] interfaces = obj.getClass().getInterfaces();
//3、产生代理对象(通过被代理对象的类加载器及实现的接口)
//第一个参数:被代理对象的类加载器
//第二个参数:被代理对象实现的接口
//第三个参数:使用产生的代理对象调用方法时,用于拦截方法执行的处理器
Object proxy = Proxy.newProxyInstance(classLoader, interfaces,this);
return proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
begin();
Object returnValue = method.invoke(obj,args);//执行method方法(入参)
commit();
return returnValue;
}
public void begin(){
System.out.println("~~~~~~~开启事务");
}
public void commit(){
System.out.println("~~~~~~~提交事务");
}
}
----------------------------------------------------------------
测试类:
package com.liguoqing.test;
import com.liguoqing.dao.BookDAOImpl;
import com.liguoqing.dao.GenaralDAO;
import com.liguoqing.dao.JDKMyDynamicProxy;
import com.liguoqing.dao.StudentDAOImpl;
public class TestDynamicProxy {
public static void main(String[] args) {
//被代理对象
BookDAOImpl bookDAO = new BookDAOImpl();
StudentDAOImpl studentDAO = new StudentDAOImpl();
//创建动态代理类对象,并将被代理对象传递到代理类中,赋值给obj
JDKMyDynamicProxy jdkMyDynamicProxy = new JDKMyDynamicProxy(studentDAO);
//proxy就是产生的代理对象,产生的代理对象可以强转成被代理对象实现的接口类型
GenaralDAO proxy = (GenaralDAO) jdkMyDynamicProxy.getProxy();
//使用代理对象调用方法,并不会执行调用的方法,而是进入到创建代理对象时指定的InvocationHandler类中的invoke方法
//调用的方法作为一个Method参数,传递给了invoke方法
proxy.delete();
}
}
CGLib动态代理
使用步骤如下:
1.添加CGLib依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
2.CGLib动态代理代码实现
package com.liguoqing.dao;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 1:添加cglib依赖
* 2:创建一个类,实现MethodInterceptor接口,同时实现接口中的intercept方法
* 3:在类中定义一个Object类型的变量,并提供这个变量的有参构造器,用于传递被代理对象
* 4:定义getProxy方法创建并返回代理对象(代理对象是通过创建被代理类的子类来创建的)
* */
public class CGLibDynamicProxy implements MethodInterceptor {
private Object obj;
public CGLibDynamicProxy(Object obj) {
this.obj = obj;
}
public Object getProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(this);
Object proxy = enhancer.create();
return proxy;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
begin();
Object returnValue = method.invoke(obj,objects);//通过反射调用被代理类的方法
commit();
return returnValue;
}
public void begin(){
System.out.println("~~~~~~~开启事务");
}
public void commit(){
System.out.println("~~~~~~~提交事务");
}
}
------------------------------------------------------------------------------------
测试类:
package com.liguoqing.test;
import com.liguoqing.dao.*;
public class TestDynamicProxy {
public static void main(String[] args) {
//创建被代理对象
BookDAOImpl bookDAO = new BookDAOImpl();
StudentDAOImpl studentDAO = new StudentDAOImpl();
//通过cglib动态代理类创建代理对象
CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(bookDAO);
//代理对象实际上是被代理对象子类,因此代理对象可以直接强转为被代理类类型
BookDAOImpl proxy = (BookDAOImpl)cgLibDynamicProxy.getProxy();
//使用代理对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法,将当前调用的方法以及方法中的参数
//传递到intercept方法
proxy.update();
}
}
Spring AOP
AOP的使用
AOP框架部署
- 首先创建Maven项目
- 添加依赖(context,aspects)
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
因为引入context时无法引入aspect的相关依赖,所以需要额外引入相应的依赖
- 创建spring配置文件
<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"
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">
</beans>
AOP配置-基于XML配置
在DAO的方法前后添加开启事务和提交事务的逻辑
- 创建一个类作为切面类(同时在里面编写我们需要添加的逻辑)
public class TxManager {
public void begin(){
System.out.println("______开启事务");
}
public void commit(){
System.out.println("______提交事务");
}
}
- 配置AOP
<?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"
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">
<bean id="bookDAO" class="com.liguoqing.dao.BookDAOImpl"></bean>
<bean id="studentDAO" class="com.liguoqing.dao.StudentDAOImpl"></bean>
<!-- 将我们要声明为切面类的类交给Spring管理-->
<bean id="txManager" class="com.liguoqing.utils.TxManager"></bean>
<!-- 配置切面-->
<aop:config>
<!-- 声明切入点-->
<aop:pointcut id="book_all" expression="execution(* com.liguoqing.dao.*.*(..))"/>
<!-- 声明txManager为切面类-->
<aop:aspect ref="txManager">
<!-- 通知-->
<aop:before method="begin" pointcut-ref="book_all"/>
<aop:after method="commit" pointcut-ref="book_all"/>
</aop:aspect>
</aop:config>
</beans>
AOP开发步骤
切入点的声明
<!-- 使用aop:pointcut声明切入点;切入点可以是一个方法,-->
<aop:pointcut id="book-insert" expression="execution(* com.liguoqing.dao.BookDAOImpl.insert())"/>
<!-- BookDAOImpl类中所有无参数无返回值的方法-->
<aop:pointcut id="book_pc1" expression="execution(void com.liguoqing.dao.BookDAOImpl.*())"/>
<!-- BookDAOImpl类中所有无返回值的方法(对参数无要求)-->
<aop:pointcut id="book_pc2" expression="execution(void com.liguoqing.dao.BookDAOImpl.*(..))"/>
<!-- BookDAOImpl类中所有无参数的方法(对返回值无要求)-->
<aop:pointcut id="book_pc3" expression="execution(* com.liguoqing.dao.BookDAOImpl.*())"/>
<!-- BookDAOImpl类中所有的方法(对参数、返回值都无要求)-->
<aop:pointcut id="book_pc4" expression="execution(* com.liguoqing.dao.BookDAOImpl.*(..))"/>
<!-- dao包中所有类中的所有方法(对参数、返回值都无要求)-->
<aop:pointcut id="pc5" expression="execution(* com.liguoqing.dao.*.*(..))"/>
<!-- dao包中所有类中的insert方法(对参数、返回值都无要求)-->
<aop:pointcut id="pc6" expression="execution(* com.liguoqing.dao.*.insert(..))"/>
<!-- dao包中所有类中的insert方法(对参数、返回值都无要求)-->
<aop:pointcut id="pc7" expression="execution(* *(..))"/>
AOP通知策略
AOP通知策略:就是声明将切面类中的切点方法如何织入到切入点
before
after
after-throwing
after-returning
around
- 定义切面类
package com.liguoqing.utils;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
public void method1(){
System.out.println("~~~~~~~~~~~~~method1");
}
public void method2(){
System.out.println("~~~~~~~~~~~~~method2");
}
public void method3(){
System.out.println("~~~~~~~~~~~~~method3");
}
public void method4(){
System.out.println("~~~~~~~~~~~~~method4");
}
//环绕通知的方法,必须遵守如下的定义规则
//1:必须带有一个ProceedingJoinPoint类型的参数,
//2:必须有Object类型的返回值
//3:在前后增强的业务逻辑之间执行Object v = point.proceed();
//4: 方法最后返回 return v;
public Object method5(ProceedingJoinPoint point) throws Throwable {
System.out.println("~~~~~~~~~~~~~method5----before");
//此句代码的执行就表示切入点方法的执行
Object v = point.proceed();
System.out.println("~~~~~~~~~~~~~method5----after");
return v;
}
}
- 配置切面类
<?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"
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">
<bean id="myAspect" class="com.liguoqing.utils.MyAspect"></bean>
<aop:config>
<!-- 使用aop:pointcut声明切入点;切入点可以是一个方法,-->
<aop:pointcut id="book-insert" expression="execution(* com.liguoqing.dao.BookDAOImpl.insert())"/>
<aop:aspect ref="myAspect">
<!-- aop:before 前置通知,切入到指定切入点之前-->
<aop:before method="method1" pointcut-ref="book-insert"/>
<!-- aop:after 后置通知,切入到指定切入点之后-->
<aop:after method="method2" pointcut-ref="book-insert"/>
<!-- aop:after-throwing 异常通知,切入点抛出异常之后-->
<aop:after-throwing method="method3" pointcut-ref="book-insert"/>
<!-- aop:after-returning 方法返回值返回之后,对于一个java方法而言return返回值也是方法的一部分
因此”方法返回值返回之后“和”方法执行结束“是同一个时间点,所以after和after-returning根据配置的顺序决定执行的顺序-->
<aop:after-returning method="method4" pointcut-ref="book-insert"/>
<!-- aop:around 环绕通知-->
<aop:around method="method5" pointcut-ref="book-insert"/>
</aop:aspect>
</aop:config>
</beans>
AOP注解配置
- 添加有关的依赖(context,aspects)
- spring配置文件
<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"
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">
<context:annotation-config/>
<context:component-scan base-package="com.liguoqing"/>
<!--基于注解配置的AOP代理-->
<aop:aspectj-autoproxy/>
</beans>
- 配置切面类
package com.liguoqing.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class TransactionManager {
@Pointcut("execution(* com.liguoqing.dao.*.*(..))")
public void pc1(){
}
@Before("pc1()")
public void begin(){
System.out.println("~~~开启事务");
}
@After("pc1()")
public void commit(){
System.out.println("~~~提交事务");
}
@Around("pc1()")
public Object printExecuteTime(ProceedingJoinPoint point) throws Throwable {
long time1 = System.currentTimeMillis();
Object v = point.proceed();
long time2 = System.currentTimeMillis();
System.out.println("~~~time:" + (time2 - time1));
return v;
}
}
注意:注解使用虽然方便,但是只能在源码上添加注解,因此我们的自定义类提倡使用注解配置,但是如果使用到第三方提供的类则需要通过XML配置形式完成配置。
MyBaits6
框架介绍
个人的理解:其实MVC框架就是负责接受前端传过来的数据,而Mybaits就是负责将接收到的数据放到数据库,而spring的作用就是协调这两个框架
Mybits介绍
简单的说:MyBits封装了一些固定的操作但是sql语句还是需要自己写
MyBits部署
创建Maven项目
下载相应的依赖
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
创建MyBaits配置文件,配置数据库连接
<?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>
</configuration>
<?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配置数据库连接信息 -->
<!-- environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!-- default属性,用来执行使用哪个environment 标签,-->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager用于配置数据库管理方法 -->
<transactionManager type="JDBC"></transactionManager>
<!-- dataSource标签用来配置数据库连接信息-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql"/>
<property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
MyBaits框架使用
案例:学生信息的数据库操作
1:创建数据表
CREATE TABLE tb_students(
sid int PRIMARY key auto_increment,
stu_num CHAR(5) not null UNIQUE,
stu_name VARCHAR(20) not null,
stu_gender CHAR(2) not null,
stu_age int not null
)
2:创建实体类
导入lombok
然后添加注释
package com.liguoqing.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private int stuId;
private String stuNum;
private String stuName;
private String stuGender;
private int stuAge;
}
注意上面的插件无非就是以注释的形式省略了get.set等方法,如果不想要导入插件的话可以自己直接完善这些方法
3:创建DAO接口,并创建操作方法
package com.liguoqing.dao;
import com.liguoqing.pojo.Student;
public interface StudentDAO {
public int insertStudent(Student student);
public int deleteStudent(Student stuNum);
}
4:创建DAO接口的映射文件
在resources目录下,新建mappers文件夹
在mappers中新建“StudentMapper”.xml的文件,这个可以根据上面的模板进行创建,注意这里的StudentMapper名称建议跟DAO联系起来
在映射文件中,对DAO中的方法进行实现
模版如下:
<?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="">
</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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.liguoqing.dao.StudentDAO">
<!--注意id要与接口中的相应的方法名一致-->
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
<insert id="insertStudent" parameterType="com.liguoqing.pojo.Student">
insert into tb_student(stu_num,stu_name,stu_gender,stu_age)
values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>
<delete id="deleteStudent">
delete from tb_student where stu_num = #{stuNum}
</delete>
</mapper>
5.将映射文件添加到主配置文件
<?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配置数据库连接信息 -->
<!-- environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!-- default属性,用来执行使用哪个environment 标签,-->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager用于配置数据库管理方法 -->
<transactionManager type="JDBC"></transactionManager>
<!-- dataSource标签用来配置数据库连接信息-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql"/>
<property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/StudentMapper.xml"></mapper>
</mappers>
</configuration>
6.单元测试(测试类往往是在测试类名后加Test,测试方法往往是在测试方法名前加Test)
1: 添加单元测试依赖 junit
2:创建测试类
在被测试接口类名后面 alt + insert ——> test
package com.liguoqing.dao;
import com.liguoqing.pojo.Student;
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 javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.Assert.*;
public class StudentDAOTest {
@Test
public void insertStudent() {
try {
//加载mybatis配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//创建builder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//会话工厂,连接工厂
SqlSessionFactory factory = builder.build(is);
//sqlsession 代表数据库的连接,也代表数据库的连接对象
//会话(连接)
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
// System.out.println(studentDAO);
int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24));
//需要手动提交
sqlSession.commit();
System.out.println(i);
} catch (IOException e) {
e.printStackTrace();
}
}
@org.junit.Test
public void deleteStudent() {
}
}
删除操作
修改操作
查询操作
注意编写查询语句时需要注意两个问题:
1.查询语句中的属性名需要与对象中的属性名一致
2.需要使用resultType来标注返回值的数据的类型
不使用起别名的方式来解决,属性名步一致的问题:推荐使用下面这种方式
查询一条记录的操作
首先接口类中定义方法:
然后在接口类中实现该方法:
测试:
分页查询操作–多参数查询
查询总记录数
注意查询总记录数的时候需要注意写什么?需要将返回值类型声明为整型
添加操作-主键回填
CRUD工具类封装
MyBatisUtil代码如下:
package com.liguoqing.utils;
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 java.io.IOException;
public class MyBatisUtil {
private static SqlSessionFactory factory;//单例的
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
static {
try {
//加载myBatis配置文件,创建会话工厂
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//得到sqlSession对象
public static SqlSession getSqlSession() throws IOException {
SqlSession sqlSession = local.get();
if (sqlSession == null){
sqlSession = factory.openSession();
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object>T getMapper(Class<T> C) throws IOException {
SqlSession sqlSession = getSqlSession();
T dao = sqlSession.getMapper(C);
return dao;
}
public static SqlSessionFactory getFactory() throws IOException {
if (factory == null){
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
return factory;
}
}
事务管理
手动事务管理
sqlSession .commit() 提交事务
sqlSession.rollback() 事务回滚
@Test
public void insertStudent() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
//1:当我们获取sqlSession对象时,就默认开启了事务
try{
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
Student student = new Student(0, "10005", "java少年", "男", 24);
int i = studentDAO.insertStudent(student);
//2:操作完成并成功以后,需要手动提交
sqlSession.commit();
}catch (Exception e){
//3:当操作出现异常,调用rollback进行回滚
sqlSession.rollback();
}
}
注意增删改操作,需要commit
自动提交事务
sqlSession = factory.openSession(true);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
Mybaits优化:
package com.liguoqing.utils;
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 java.io.IOException;
public class MyBatisUtil {
private static SqlSessionFactory factory;//单例的
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
static {
try {
//加载myBatis配置文件,创建会话工厂
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//得到sqlSession对象
public static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
SqlSession sqlSession = local.get();
if (sqlSession == null){
sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object>T getMapper(Class<T> C) throws IOException {
SqlSession sqlSession = getSqlSession(true);
T dao = sqlSession.getMapper(C);
return dao;
}
public static SqlSessionFactory getFactory() throws IOException {
if (factory == null){
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
return factory;
}
}
测试操作:
@Test
public void testDeleteStudent() {
try {
// InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// //SqlSessionFactory表示mybatis的会话工厂,工厂模式
// SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
// //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系)
// SqlSession sqlSession = sqlSessionFactory.openSession();
// //通过SqlSession 对象调用getMapper方法获取DAO接口对象
// StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
// //调用被测试方法
// int i = studentDAO.deleteStudent("10001");
// //提交事务
// sqlSession.commit();
// System.out.println(i);
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
int i = studentDAO.deleteStudent("10001");
} catch (IOException e) {
e.printStackTrace();
}
}
Mybaits的最终封装:
package com.liguoqing.utils;
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 java.io.IOException;
public class MyBatisUtil {
private static SqlSessionFactory factory;//单例的
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
static {
try {
//加载myBatis配置文件,创建会话工厂
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//得到sqlSession对象
private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
SqlSession sqlSession = local.get();
if (sqlSession == null){
sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
local.set(sqlSession);
}
return sqlSession;
}
//手动事务管理
public static SqlSession getSqlSession() throws IOException {
return getSqlSession(false);
}
//自动事务管理
public static <T extends Object>T getMapper(Class<T> C) throws IOException {
SqlSession sqlSession = getSqlSession(true);
T dao = sqlSession.getMapper(C);
return dao;
}
public static SqlSessionFactory getFactory() throws IOException {
if (factory == null){
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
return factory;
}
}
mybaits-config主配置文件
注意上面出现的都是配置文件的主要属性,注意这些属性使用的顺序必须和上面的一样
properties标签
setting标签
这个标签的作用时设置mybaits的工作属性
typeAliases
作用:为实体类取别名这样mapper中使用实体类时就不需要使用它的全路径了,如上
plugins标签
作用:配置MyBaits插件
environments标签
mappers
映射文件
Mybaits初始化
Mapper文件配置
分页插件的使用
添加依赖
首先这个插件是一个独立的插件所以如果想要使用得先下载依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
进行配置操作
<!-- plugins主要用于配置MyBatis插件,例如分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
分页实例
@Test
public void testGetStudentByPage() throws IOException {
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);//sqlSession
//将分页信息交给sqlSession
PageHelper.startPage(1,4);//分页,从第1页开始,往后的4条数据
//studentList 就是我们需要查找的分页数据
List<Student> studentList = studentDAO.listStudents();//查全量就可以,分页组件会自动将数据进行分页
//为什么需要将数据交给PageInfo,因为我们需要的步仅仅是分页的信息,还需要其他的相关信息
PageInfo<Student> pageInfo = new PageInfo<Student>(studentList);
//返回的时候只需要将pageInfo 返回就可以,因为pageInfo中就包含了分页以及数据信信息
}
带条件分页
其实带不带条件都是一样的,变化的只是我们查询数据的方法
关联映射
一对一关联
一对多关联,多对一关联
多对多关联
基于maven创建web项目
步骤1:创建maven项目
步骤2:在pom.xml中设置打包方式
步骤2:创建一个与resources平级的目录webapp,里面创建WEB-INF文件夹,然后创建web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
</web-app>
步骤3:在pom中添加web依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
步骤4:将web项目放到服务器运行
点击Add configuration
并配置tomcat,点击fix部署项目
以上就是搭建web项目的过程,下面开始创建mybaits框架
步骤5:添加mybaits依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
步骤6:创建数据库连接文件jdbc.properties,并配置好enviroment
步骤7:在java文件夹下创建工具类MyBatisUtil
package com.guoqing.utils;
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 java.io.IOException;
public class MyBatisUtil {
private static SqlSessionFactory factory;//单例的
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
static {
try {
//加载myBatis配置文件,创建会话工厂
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
//得到sqlSession对象
private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
SqlSession sqlSession = local.get();
if (sqlSession == null){
sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
local.set(sqlSession);
}
return sqlSession;
}
//手动事务管理
public static SqlSession getSqlSession() throws IOException {
return getSqlSession(false);
}
//自动事务管理
public static <T extends Object>T getMapper(Class<T> C) throws IOException {
SqlSession sqlSession = getSqlSession(true);
T dao = sqlSession.getMapper(C);
return dao;
}
public static SqlSessionFactory getFactory() throws IOException {
if (factory == null){
//加载主配置文件
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
return factory;
}
}
以上就是所有的框架部署
项目实例
一对一关联
创建数据表
创建实体类
注意里面的注释是使用lookbom实现的,你要不想用直接完善实体类即可
创建dao操作类接口,并建立对应的mapper
package com.guoqing.dao;
import com.guoqing.pojo.User;
public interface UserDao {
public int insertUser(User user);
}
上面typeAliase的作用是给实体类取别名,这样就不需要使用其完整的引用路径
测试代码:注意因为涉及了多表的操作所以需要手动提交事务
@Test
public void testInsertUser() throws IOException {
//用户注册提交了基本信息和详情到Servlet,Servlet接收到注册信息,分装到User和Detail对象中
User user = new User(0,"zhangsan6","123123","张三","01.jpg",null);
Detail detail = new Detail(0,"山东省青岛市","15866666666","heihei",0);
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try{
UserDao userDao = sqlSession.getMapper(UserDao.class);
int i = userDao.insertUser(user);//添加用户
System.out.println(i);
detail.setUserId(user.getUserId());
DetailDao detailDao = sqlSession.getMapper(DetailDao.class);
int n = detailDao.insertDetail(detail);//添加详情
System.out.println(n);
sqlSession.commit();//提交事务
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();//回滚事务
}
}
一对一关联查询
首先如上图所示,我们需要对实体类进行修改,将Detail作为User的一个属性,其次修改resultMap中的代码如下:
方法1:连接查询
方法2:子查询
association中id属性的作用,在实验中因该是起到区分其他对象的作用,如果两个对象id相同那么即使有不一样的属性,resultMap也只会注入一个属性
column属性的作用:注意嵌套查询,也就是上面的子查询,两个查询之间的参数传递,可以通过column进行传递,如果是一个参数可以直接写上你要传递的参数的参数名,如果是多个参数传递
<resultMap id="StudentDAOMapper" type="Beans.Student">
<id column="sno" property="sno"></id>
<result property="sname" column="sname"></result>
<result property="age" column="age"></result>
<result property="address" column="address"></result>
<!-- -->
<collection property="slist" ofType="Score">
<result property="sno" column="sno"></result>
<result property="cno" column="cno"></result>
<result property="score" column="score"></result>
<result property="number" column="number"></result>
<!-- 对应的课程属性我们在使用嵌套查询玩一玩,当然直接连接查询也行,我不用,就是玩-->
<association property="course" select="DAO.CourseDAO.queryCourseByCno" column="cno"></association>
</collection>
</resultMap>
<select id="queryScoreByName" resultMap="StudentDAOMapper">
SELECT stu.sno sno,sname,age,address,cno,score,number
FROM student stu
INNER JOIN score sc
ON stu.sno=sc.sno
WHERE stu.sname LIKE #{sname}
</select>
一对多查询
方法1:连接查询
方法2:子查询
对上面的代码进行解释,其实上面的代码时位于两个mapper文件中的,分别使用了两次单表查询,第一次查询时根据班级号查询班级信息,然后在这个查询里面使用collection嵌套一个子查询,这个子查询也是一个mapper,作用时根据班级号查询这个班级的所有学生并以列表的形式返回
多对一关联查询
多对一关联映射其实和一对多关联映射是一样的,这里就不添加实例了
连接查询
子查询
多对多查询
实际上就是拆分成两个一对多
创建相应查询的实体类
查询一
连接查询
子查询
查询二
·
动态SQL
首先动态sql的作用是什么?就是我们有时候进行搜索时可能不同的情形下会有不同的sql语句,因为搜索的条件不可能每次都一样,所以动态sql的作用就在此,可以根据不同的条件生成不同的sql语句
使用案例
编写DAO方法
当然里面函数的参数也可以时我们自定义的对象,xml可以自动识别相应的参数
if标签
where标签
trim标签
foreach标签
-----MemberDAOTest
@Test
public void TestSearchMember() throws IOException {
HashMap<String,Object> params = new HashMap<String,Object>();
params.put("gender","女");
// params.put("minAge",19);
// params.put("maxAge",23);
// params.put("city","武汉");
//当向Map中存放参数时,key必须与动态Sql中的参数保持一致
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMember(params);
System.out.println(members);
}
foreach测试类:
@Test
public void testSearchMemberByCity() throws IOException {
List<String> cities = new ArrayList<String>();
cities.add("武汉");
cities.add("厦门");
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMemberByCity(cities);
for (Member m : members){
System.out.println(m);
}
}
模糊查询
${}与#{}的区别
${keyWord} 表示获取参数,先获取参数的值,拼接到SQL语句中,再编译执行SQL语句 可能引起SQL注入问题
#{keyWord} 表示获取参数,先完成SQL的编译(预编译),预编译之后再将获取的参数设置到SQL中 可以避免SQL注入问题
parameterType属性
日志配置
配置日志框架
代码如下:
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
添加日志文件
#声明日志的输出级别及输出格式
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output ...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#定义日志的打印格式 %t表示 线程名称 %5p 日志级别 %msg 日志信息 \:%m%n 换行
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p -%msg \:%m%n
日志信息的级别
我们可以在配置文件中改变输出信息的级别,从而改变输出的信息
配置数据库连接池–整合Druid
添加依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
创建Druid连接池工厂
public class DruidDataSourceFactory extends PooledDataSourceFactory {
public DruidDataSourceFactory() {
this.dataSource = new DruidDataSource();
}
}
修改mybaits-config配置
为什么要将下面的name进行修改?因为Druid所需要的参数就是这个名字
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<!--POOLED 使用MyBatis内置的连接池实现 -->
<!--mybatis 需要一个连接池工厂 这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
<!--使用了多态-->
<dataSource type="com.guoqing.utils.DruidDataSourceFactory">
<property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
MyBaits缓存机制
缓存的工作原理
MyBaits缓存
注意上面的代码中如果我们没有去清空内存,那么第2次查询将会得出第一次查询修改候的结果,也就是会获得缓冲区中的内容
一级缓存的使用
首先是一级缓存存在的问题,首先时每一个线程只有一个servel,不同的servlet中的属性时不相同的,就例如上面的两个servlet,他们具有不同的studentDAO,也就是所具有不同的sqlsession,也就是所一个sqlsession中的数据库的修改并不会导致另一个缓存区失效,这也就导致了数据之间的不同步
二级缓存
二级缓存的使用
修改mybiat-config
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
在需要使用的xml实例中,设置cache
被缓存的实体类实现序列化接口
查询操作的缓存开关
注意只有select标签支持useCache
延迟加载
Spring整合Mybaits
整合Mybaits的准备工作
Spring整合MyBaits整合配置
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
druid.driver=com.mysql.cj.jdbc.Driver
druid.url=jdbc:mysql://localhost:3306/db_2010_mybatis?characterEncoding = utf-8&serverTimezone=GMT%2B8&useSSL=false
druid.username=root
druid.password=123456
#连接池参数
#init:初始连接数 minIdle:最小连接数 maxActive:最大连接数 timeout:超时时间
druid.pool.init = 3
druid.pool.minIdle = 5
druid.pool.maxActive = 20
druid.pool.timeout = 30
总的依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>实验5</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 添加mybaits依赖-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 添加spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<!-- 添加补丁依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
</dependencies>
</project>
整合Mybaits创建druidDateSource
下面这步就和我们之前的不一样,我们之前是在mybaits配置文件中,添加的属性,但是我们现在需要将这些东西交给spring管理
<context:property-placeholder location="classpath:druid.properties"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${druid.driver}"/>
<property name="url" value="${druid.url}"/>
<property name="username" value="${druid.username}"/>
<property name="password" value="${druid.password}"/>
<property name="initialSize" value="${druid.pool.init}"/>
<property name="minIdle" value="${druid.pool.minIdle}"/>
<property name="maxActive" value="${druid.pool.maxActive}"/>
<property name="maxWait" value="${druid.pool.timeout}"/>
</bean>
整合Mybaits创建SqlSessionFactory
回想一下,我们之前使用build函数的时候也需要将mybaits(包含dataSource,mapper文件),所以现在不适用build使用mapper一样需要将这些资源交给spring
依赖Spring使用druidDateSource配置创建SqlSessionFactory
在这里在详细的说明一下4个property的作用:
第一个property配置dataSource没什么好说的
第二个是将mapper也交给spring管理,后面的*Mapper.xml指的是所有以xml结尾的文件
第三个是为包里的所有文件添加别名,包里的文件自动拥有别名
第四个防止mybaits中还有其他的配置,所以将其也交给spring
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
<property name="typeAliasesPackage" value="com.liguoqing.beans"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
在看书的时候发现书本里配置文件时,没有配置mapperlocations上网查了查后发现
结论是:如果Mapper.xml与Mapper.class在同一个包下且同名,spring扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。
在没有注册mapper映射文件的情况下,如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。此时spring是通过识别mapper.xml中的 namespace的值来确定对应的Mapper.class的。
详细讲解:(摘抄自别的文章)
mapper-locations
顾名思义是一个定义mapper位置的属性
在yml或properties下配置,作用是实现mapper接口配置见mapper和接口的绑定。
使用场景:
当mapper接口和mapper接口对应的配置文件在
命名上相同
所在的路径相同
则mapper-locations可以不用配置,配置也不会生效。
但是,如果
当mapper接口和mapper接口对应的配置文件在
命名上不同或
所在的路径不同
之一不同,需要配置mapper-locations才能实现接口的绑定
mapper接口:com.liuzeyu.mapper.UserMapper.java
配置文件:mapper.UserMapper.xml
实现接口绑定需要在配置文件中配置:
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
整合Mybaits配置Mapper
完成上述步骤后我们就可以通过Spring来获取SqlSessionFactory,进而获取mapper来创建dao,但是这样依旧十分的麻烦,所以我们还可以将创建mapper的工作交给spring,只要我们将我们的接口告诉spring
首先我们要创建一个dao扫描工具,并将我们的factory交给他
然后告诉它包所在的路径的可,即声明需要扫描的包,这个类就相当于dao的生产工厂
它的底层就相当于实现上面的两句代码,所i我们需要将factory,以及接口类型交给他,这样的话扫描到几个接口,他就可以创建几个对象放到spring容器中
按上面的步骤配置完后,我们就可以直接获取dao对象
Spring 整合Mybaits整合AOP配置
事务的隔离级别
事务的传播机制
配置AOP详细步骤-xml配置
注意:我们一般将事务定义在service层这样一个service就是一个事务,定义在dao层的意义不大,因为,每个dao一般就涉及一个操作
配置AOP详细步骤-注解配置
SpringMVC
SpringMVC框架部署
- 创建maven工程
- 在pom.xml下面设置打包方式packging
- 在main目录下新建一个目录叫webapp,然后因为我们上面配置了packaging,所以刷新目录结构后,会更新web文件夹,然后在webapp文件夹下面创建,一个WEB-INF文件夹,并在该目录下添加web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd">
</web-app>
- 添加jsp和servlet依赖
我们可以直接按下面的步骤直接添加即可
- 配置tomcat配置
点击Add Configuration进行配置
点击fix并按下面的步骤进行操作
- 添加SpringMVC的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.liguoqing</groupId>
<artifactId>springmvc-demo2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>5.2.13.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
- 添加配置文件
以前我们的Spring配置文件时applicationContext现在我们在SpringMVC中有了个新的名字
<?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:mvc="http://www.springframework.org/schema/mvc"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 声明使用注解配置-->
<context:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围-->
<context:component-scan base-package="com.liguoqing"/>
<!-- 声明mvc使用注解驱动-->
<mvc:annotation-driven/>
</beans>
8. 在web.xml中配置SpringMVC的前端控制器
SpringMVC提供了一个名为DispatcherServlet的类(SpringMVC前端控制器),用于拦截用户请求交由SpringMVC处理
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 使用注解配置spring自带的servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数,在初始化sevlet的时候加载spring配置文件 ,从而使我们的spring正常工作-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<!-- 服务器初始化首先创建web实例,这个不是必须项-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- /* 拦截所有的HTTP请求,包括.jsp的请求,都做为控制器类的请求路径来处理-->
<!-- / 拦截所有的HTTP请求,但不包括.jsp的请求,但不会放行静态资源请求,html/js/css/图片-->
</web-app>
MVC框架使用
创建控制器
创建控制器类
在控制器类中定义处理请求的方法
使用mvc最大的好处就是一个控制器可以处理多个请求,不需要一个servlet对应一个请求,这就时强大之处
放行静态资源请求
所以即使我们将url改为/依然无法访问静态资源,即使是在同一个项目里面的资源调用,例如css的引用,所以需要额外为静态资源进行配置,代码如下图所示:
前端数据提交到控制器
控制器中接收前端提交的数据
接受请求行的数据
接受请求头传递的数据
接受请求体传递的数据
控制器响应同步请求
注意上面返回页面的路径中加入了一条/,使其从根路径开始跳转,这样跳转时就不会发生路径上的错误
控制器响应异步请求
一般前后端分离的项目都是采用异步请求的方式
-
使用respon中的输出流返回响应对象
注意我们响应数据时需要将响应的数据类型声明为json对象,这样我们才会返回一个json对象而不是字符串 -
直接在控制器中返回响应的对象
注意如果一个类中所有的请求都是ajax请求,我们可以直接将ResponBody声明在类的上面
控制器响应同步请求的数据传递
处理中文乱码的问题
只要前端,后端,传输过程中的编码方式一致就不会有问题
前端编码设置
服务器编码设置
SpringMVC编码设置
在web.xml中配置SpringMVC编码过滤器的编码方式
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
SpringMVC的请求处理流程
SpringMVC核心组件介绍
处理器映射器
首先时BeanNameUrlHandlerMapping的使用,我们直接将这个类交给spring管理,它就会生效,而且它时根据bean的id来查找相应的controler,也就是所我们查找的路径就是器id,其次是我们默认处理器的使用上面是它的xml配置,不过我们一般使用他的注解配置prop的值是它的id
视图解析器配置
注意如果我们想要使用第一种视图解析器需要自己下载相关的依赖包,第二种可以直接使用但是一般我们不会去配置,如果不配置的话就是,前后缀都没有,写路径是需要写完
日期格式处理
在控制器方法中使用对象接受数据
注意如果想要使用对象接受数据必须得是发送的数据的key与对象key一致
日期格式处理
将我们的自定义转换器配置给spring,首先使用bean标签将转换器配置给Formatting…,然后在mvc驱动中声明conversion-service
MVC框架部署总结
- 老样子创建maven工程
- 将maven变为web工程
(1)添加打包方式
<packaging>war</packaging>
(2) 在我们的main目录下新建一个目录叫webapp,然后在其下创建WEB-INF目录,最后创建web.xml文件
(4)添加服务器的支持,在file -> project Structure ->moudles->±>Librery
(5) 配置tomcat Add Configuration->fix->修改项目路劲名(可不改)
此上就是创建web架构的步骤
- 添加SpringMVC依赖
<properties>
<spring.version>5.2.13.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
</dependencies>
- 创建SpringMVC配置文件spring-servlet(多配置文件分开配置)
(1) spring-context.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"
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">
<!--声明使用注解配置-->
<context:annotation-config/>
<!--声明Spring工厂注解的扫描范围-->
<context:component-scan base-package="com.liguoqing"/>
</beans>
(2)spring-mvc.xml 进行mvc相关配置,例如静态资源配置,拦截器配置等
<?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:mvc="http://www.springframework.org/schema/mvc"
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">
<!--声明mvc使用注解配置-->
<mvc:annotation-driven/>
</beans>
(3)spring-mybatis.xml 进行Spring与MyBatis整合相关配置
<?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:mvc="http://www.springframework.org/schema/mvc"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"">
</beans>
- 配置SpringMVC的前端控制器(DispatcherServlet)
在 web.xml 进行配置,注意 classpath:spring-*.xml
url-pattern:就是/和/*的区别的那部分内容
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
//初始化时指定spring配置文件的路劲
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 配置SpringMVC静态资源处理策略
文件上传
文件上传案例
注意因为我们将数据类型设置为multipart不压缩,所以mvc无法直接接收数据,所以需要借助文件解析器
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--一些属性,自己百度-->
<property name="maxUploadSize" value="10240000"/>
<property name="maxInMemorySize" value="10240"/>
<property name="defaultEncoding" value="utf-8"/>
</bean>
在controler中接收传输过来的数据:
package com.liguoqing.controller;
import com.liguoqing.beans.Book;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public String addBook(Book book, MultipartFile ImgFile, HttpServletRequest request) throws IOException {
System.out.println("~~~~addBook");
System.out.println(book);
System.out.println(ImgFile);
//ImgFile表示上传的图片
//1:截取上传文件的后缀名,生成新的文件名
String originalFilename = ImgFile.getOriginalFilename();
String ext = originalFilename.substring( originalFilename.lastIndexOf(".") );// .jpg
String filename = System.currentTimeMillis()+ext;
//2:获取imgs目录在服务器的路径
String dir = request.getServletContext().getRealPath("imgs");
String savePath = dir + "/" + filename;//存储路径(C:/asd/ad/asdf/img.jpg)
//3:保存文件
ImgFile.transferTo(new File(savePath));
//4:将图片的访问路径设置到对象
book.setBookImg("imgs/"+ filename);
//5:调用service保存book到数据库
return "/tips.jsp";
}
}
文件下载案例
其他的代码就先不看了,以后有需要再重看视频
显示文件目录:
@RequestMapping("/list")
@ResponseBody
public String[] listImgs(HttpServletRequest request){
//1:从imgs目录下获取所有的图片信息
String dir = request.getServletContext().getRealPath("imgs");
File imgDir = new File(dir);
String[] filenames = imgDir.list();
return filenames;
}
实现文件下载:
拦截器
拦截器介绍
首先针对上面的两幅图讲讲拦截器和过滤器的区别首先过滤器是针对tomcat而言的,所有的请求都会先被过滤器拦截,只有符合web.xml配置的请求才会被放行,而拦截器是相对SpringMVC而言的,直白点就是相对于DispatcherServlet而言的,只有能到达分配器的请求才有机会被拦截
自定义拦截器
创建拦截器
在我们的预处理方法中通过返回boolean来判断是否放行该请求,如果是true则放行,否则不放行
配置拦截器
对上面的代码进行解析:首先通过bean标签将我们自定义的bean类交给spring管理,然后通过mvc:mapping对需要拦截的控制器及其方法进行配置,类似与上面的第一个和第二个mapping的作用是拦截book controler下的query方法和add方法,第三个mapping的作用是对student control下的所有方法进行拦截,但是第4个mapping的作用是放行student 下的add方法
拦截器链
按照我的理解就是预处理的话就是先配置先执行,而后处理的话就是后配置先执行,就例如上面的顺序就是1221
完结撒花