java基础

java简介

java的历史

Java最早是由SUN公司(已被Oracle收购)的詹姆斯·高斯林(高司令,人称Java之父)在上个世纪90年代初开发的一种编程语言,最初被命名为Oak.1995正式更名为java

Java介于编译型语言和解释型语言之间。而Java是将代码编译成一种“字节码”,它类似于抽象的CPU指令,然后,针对不同平台编写虚拟机,不同平台的虚拟机负责加载字节码并执行,这样就实现了“一次编写,到处运行”的效果。当然,这是针对Java开发者而言。

Java版本

从1995年发布1.0版本开始,到目前为止,最新的Java版本是Java 14:

2014发布的JDK1.8

java结构

简单地说,JRE就是运行Java字节码的虚拟机

因为JDK除了包含JRE,还提供了编译器、调试器等开发工具。

基础语法

main函数

1.public static void main(String[] args){ }

接收的是跟在javac 后面的参数

2.args==arguments

3.可以重载main函数,一个类也可以有多个main函数

不推荐这样写

命名

类名要求:以单词首个字母大写

方法:单词首个字母小写.

变量通常都是小写字母

常量通常都是大写字母。

数据类型

基本数据类型

CPU可以直接进行运算的类型。(8种)

Java定义了以下几种基本数据类型:

整数类型:byte 1,short2,int4,long8

浮点数类型:float4,double8

字符类型:char2

布尔类型:boolean1

1048576

数组

数组的定义

int[] a= {1,3,100,2,33,46,32,35,25,15};
int[] b=new int[6];

遍历

foreach

for (int x : arr) {            
System.out.println(x); //逐个输出数组元素的值      
}  

面向对象

多态

3 个必要条件:继承、重写和向上转型。
向上转型:对于不同的子类,会调用不同的方法(调用的属性和方法都是子类的)

(以父类的身份运行子类的方法)

多态的出现大大提高了程序的扩展性。不需要添加重复的代码。

方法的特点是:编译看左边,运行看右边。

变量和静态方法:无论编译和运行都是看左边。

单例模式

创建唯一的一个实例(地址相同)

1.先创建一个对象

2.私有化构造方法

3.提供一个访问方法给外部

饿汗式

private static eHan eHan = new eHan();
private eHan(){}
public static eHan getinstance(){
   return eHan;
}

懒汉式

private static lanHan lanhan;
private lanHan(){};
public static lanHan getinstance(){
    if (lanhan == null) {
        lanHan lanhan = new lanHan();
    }
    return lanhan;
}

关键字

this 是指当前对象的引用

super 是父类对象的引用

static (一般不定义静态,生命周期太长,而且清不掉。且总是优于实例成员加载)

​ 静态变量和方法加载进内存中的方法区,是所有成员共享的

​ 静态代码块只加载一次

内部类 简化创建对象使用一次方法的问题

普通内部类

内部类可直接访问外部类,外部类要创建对象才内访问内部类

成员内部类和外部类不存在继承关系

其他类中生成成员内部类的方式:Outer.Inner oi = new Outer().new Inner();

匿名内部类

使用场景:适用于一个对象只使用一次的场景

好处:匿名对象使用完毕就变成垃圾被回收

注意:匿名对象可以作为实际参数传递

public abstract class Person
{
    public abstract void work();
}

new Person()
{
    public void work()
    {
        System.out.println("work");
    }
}.work();

静态内部类

适用于多个外部类的对象可以共享同一个内部类的对象

静态内部类无需依赖于外部类,它可以独立于外部对象而存在静态内部类,

使用静态内部类的好处是加强了代码的封装性以及提高了代码的可读性。
普通内部类不能声明static的方法和变量,注意这里说的是变量,常量还是可以的。

API

String系

String

特点:一旦初始化不可改变,是一个常量

==是比较地址值,equals比较的是常量值

常用方法

tostring()转换成字符串 split()切割和替换 trim()比较和去除空格

replace(,) valueOf()

String substring(int beginIndex,int endIndex)  截取字符串 

trim();  去掉字符串左右空格

StringBuffer

特点:1.线程安全 2.长度可变化,字符缓冲区,是一个容器 3.可以操作多个数据类型

常用方法

append() insert() tostring()

StringBuilder

jdk1.5提供

类似StringBuffer,但不保证同步。

异常

异常的分类
必须捕获的异常,Exception中的异常(不包括RuntimeException及其子类)
不需要捕获的异常,包括Error及其子类,RuntimeException及其子类。

继承关系

object---Throwable

error 子类

Exception----RuntimeException(NullPointerException..)

----IOException

异常处理

捕获异常

使用(try---catch---finally)可以使用多个catch语句

(多个catch的时候:子类必须写在前面,否则只抛出首个异常)

抛出异常

throws Exception //声明了该功能有可能会出现问题。

(处理异常要么处理,要么也不管直接抛出给jvm)

自定义异常

对特有异常自己进行封装,生成对象

class FuShuException extends Exception{   
    FuShuException(String msg){
        super(msg);
    }
}  

1.自定义异常只能手动抛出

2.异常信息需要复写Exception的方法。

3.抛出异常时,尽量复用JDK已定义的异常类型;

多线程

简介

一个Java程序实际上是一个JVM进程

JVM除了主线程外,还有负责垃圾回收的其他工作线程。

在main()方法内部,我们可以启动多个线程。

作用

系统接受实现多用户多请求的高并发时,通过多线程来实现。

开线程把花大量时间处理的任务放在线程处理,这样线程在后台处理时,主程序也可以继续执行下去

创建线程

1.继承Thread类并重写run方法(通过继承重写run方法)

2.创建Thread实例时,传入一个线程实例(多态)

jdk8引入的lambda语法简写

 Thread t = new Thread(() -> {System.out.println("start new thread!");});    

线程状态

Runnable:运行中的线程,正在执行run()方法

Blocked:运行中的线程,因为某些操作被阻塞而挂起;

Waiting:运行中的线程,因为某些操作在等待中;

Timed Waiting:运行中的线程,因为sleep()方法正在计时等待;

Terminated:run()方法执行完毕,线程终止。

(通过对另一个线程对象调用join()方法可以等待其执行结束;)

线程操作

获取当前线程名 Thread.curentTread().getName()

中断线程

对目标线程调用interrupt()方法可以请求中断一个线程

守护线程

只是在调用start()方法前,调用setDaemon(true)把该线程标记为守护线程

守护线程是指为其他线程服务的线程。非守护线程结束,就结束了

线程同步

通过synchronized同步。

方法:用synchronized修饰方法可以把整个方法变为同步代码块,synchronized方法加锁对象是this

配置线程

CPU 密集型任务

CPU 密集型任务应配置尽可能小的线程 如果线程太多,会造成线程在CPU内部的上下文切换。

IO 密集型任务

IO 密集型任务线程并不是一直在执行任务,不会经常在CPU内切换线程上下文,则应配置尽可能多的线程

对于 IO 型的任务的最佳线程数,有个公式可以计算 Nthreads = NCPU * UCPU * (1 + W/C)

❑NCPU 是处理器的核的数目

❑UCPU 是期望的 CPU 利用率(该值应该介于 0 和 1 之间)

❑W/C 是等待时间与计算时间的比率。等待时间与计算时间我们在 Linux 下使用相关的 vmstat 命令或者 top 命令查看。

多线程安全

概念:同一个对象被多个线程操作 锁:景区厕所门

锁的弊端:1.降低性能 2.优先级高的那不到锁,性能倒置

好处:安全性能提升

并发案例

synchronized关键字,synchronized代码块(volicate 唯一关键字)

synchronized默认锁的是本身this

锁的对象是变化的量,需要增删改的量

线程安全的类

ArrayList 不安全 CopyOnWriteArrayList 安全

HashMap 不安全 CourrentHashMap 安全

死锁

Java的synchronized锁是可重入锁;

多个线程互相抱着对方需要的资源,然后形成僵持。

避免死锁的方法是多线程获取锁的顺序要一致。

解决死锁的方法:

waitnotify

用于多线程协调运行

  • synchronized内部可以调用wait()使线程进入等待状态;
  • 必须在已获得的锁对象上调用wait()方法;
  • synchronized内部可以调用notify()notifyAll()唤醒其他等待线程;
  • 必须在已获得的锁对象上调用notify()notifyAll()方法;
  • 已唤醒的线程还需要重新获得锁后才能继续执行。

线程池

ExecuterService

JDK提供了ExecutorService实现了线程池功能:

  • 线程池内部维护一组线程,可以高效执行大量小任务;
  • Executors提供了静态方法创建不同类型的ExecutorService
  • 必须调用shutdown()关闭ExecutorService
  • ScheduledThreadPool可以定期调度多个任务。

Math和Random

Date系

包装类

作用:使基础数据类型可以成为引用数据类型

基础类型->字符串 调用toString()

字符串->基础数据类型 调用对应包装类的parsexxx()

枚举类

定义一组常量时,强烈建议用枚举

int定义的常量相比,使用enum定义枚举有如下好处:

首先,enum常量本身带有类型信息,编译器会自动检查出类型错误。

集合

Collection

迭代器

访问集合总是通过统一的迭代器(Iterator)

Iterator it = l.iterator();//获取迭代器,用于取出集合中的元素。       
 while(it.hasNext())        {            
    sop(it.next());       
}

List

Set

map

TreeMap

HashMap

泛型

jdk1.5特性

泛型就是编写模板代码来适应任意类型;泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查;

泛型的使用

泛型类 通过泛型可以完成对一组类的操作对外开放相同的接口。

泛型接口 泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中

泛型方法 是在调用方法的时候指明泛型的具体类型 。

输入输出流

File

File 类是 java.io 包中唯一代表磁盘文件本身的对象,也就是说,如果希望在程序中操作文件和目录,则都可以通过 File 类来完成。

输入输出流

所有输入流类都是 InputStream 抽象类(字节输入流)和 Reader 抽象类(字符输入流)的子类。
所有输出流类都是 OutputStream 抽象类(字节输出流)和 Writer 抽象类(字符输出流)的子类。

java字节流

包括 ByteArrayInputStream 类、ByteArrayOutputStream 类、FileInputStream 类和 FileOutputStream 类。

java字符流

Stream是字节流,即以byte为单位读取,而Reader是一个字符流,即以char为单位读取

java转换流

正常情况下,字节流可以对所有的数据进行操作,但是有些时候在处理一些文本时我们要用到字符流,比如,查看文本的中文时就是需要采用字符流更为方便。所以 Java IO 流中提供了两种用于将字节流转换为字符流的转换流。

注解和反射

注解

jdk1.5

注解入门

Java 注解(Annotation)又称 Java 标注,Java 语言中的类、方法、变量、参数和包等都可以被标注。

可以通过反射获取注解内容。

在编译器生成类文件时,注解可以被嵌入到字节码中。

Java 虚拟机可以保留注解内容,在运行时可以获取到注解内容 。

内置注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方***报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。

从 Java 7 开始,额外添加了 3 个注解:

  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

元注解

作用在其他注解的注解:

  • @Retention - 定义注解生命周期
  • @Documented - 标记这些注解是否包含在用户文档中。
  • @Target - 标记这个注解应该是哪种 Java 成员。
  • @Inherited - 标记这个注解是否可继承父类的anontation
  • 使用@Repeatable这个元注解可以定义Annotation是否可重复

反射

三种方式

getclass

可访问的信息

reflect

其他

序列化

为什么要把Java对象序列化呢?因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了。

jdk8

jdk升级的原因:

​ 1提高效率

​ 2简化书写

​ 3提高安全性

jdk

lambda

重写方法作为另一个方法的参数

可取代大部分匿名内部类

通常使用语法为:(()-> {body})

javaweb

version

Tomcat JDK JavaEE Servlet JSP
9.x 8及更高的版本 JavaEE8 4.0 2.4?
8.x 7及更高的版本 JavaEE7 3.1 2.3
7.x 6及更高的版本 JavaEE6 3.0 2.2
6.x 5及更高的版本 JavaEE5 2.5 2.1
5.x 1.4及更高的版本 JavaEE1.4 2.4 2.0
4.x 1.3及更高的版本 JavaEE1.3 2.3 1.2
3.x 1.1及更高的版本 JavaEE1.3 2.2 1.1

http

servlet

生命周期

初始化阶段 创建 Servlet 实例对象,然后通过调用 init() 方法,只能用初始化一次
运行阶段 对于 Servlet 的每一次访问请求,Servlet 容器都会调用一次 Servlet 的 service() 方法,创建新的 ServletRequest 和 ServletResponse 对象
销毁阶段 在销毁 Servlet 之前,Servlet 容器会调用 Servlet 的 destroy() 方法

HttpServlet

HttpServletRequest

获取消息行 获取消息头 获取请求参数的方法 请求转发和重定向

HttpServletResponse

状态行、响应消息头、消息体(大量的数据都是通过响应消息体传递的)getwriter()方法

Cookie和Session

cookie

当客户端第一次访问Web应用时,服务器以响应头的形式将Cookie发送给客户端,
客户端会把Cookie保存到本地。当浏览器再次请求该Web应用时,客户端会把请求的网址和Cookie一起提交给服务器。

session

客户端第一次访问Web应用时,服务器为该客户端创建一个Session,同时,服务器把该Session的ID放入Cookie返回给客户端。

Filter

jsp

语句

JSP声明语句:<%!声明语句%>,声明全局变量、常量、方法
JSP Scriptlet:<%java代码%>,声明局部变量、常量、方法
JSP表达式:<%=java 代码%>

指令3

page、include 和 taglib

include
当前 JSP 页面与静态引入的文件紧密结合为一个 Servlet。
这些文件可以是 JSP 页面、HTML 页面、文本文件或是一段 Java 代码。
include 指令的包含过程为静态包含

动作7

1jsp:include 动作的包含过程为动态包含,通常用来包含那些经常需要改动的文件。

2 jsp:param 动作标记向这个程序传递参数信息。

3jsp:forward 动作实现页面的跳转

4jsp:plugin 动作在页面中插入 Java Applet 小程序或 JavaBean

5.jsp:useBean 动作标记用于在 JSP 页面中创建 bean 实例

6.jsp:setProperty 给创建的 JavaBean 中对应的属性赋值

7.jsp:getProperty 标记用来获得 bean 中的属性,并将其转换为字符串,再在 JSP 页面中输出

内置对象8

​ application 是 javax.servlet.ServletContext 类的实例
​ out 会通过 JSP 容器变换为 java.io.PrintWriter 类的对象。
​ request 是 HttpServletRequest(接口)的实例
​ response HttpServletResponse 的实例
​ session
​ pageContext
​ page 为了执行当前页面应答请求而设置的 Servlet 类的实体
​ config javax.servlet.ServletConfig 类的实例

El

判断和取值

内置对象11

pageContext pageScope requestScope sessionScope
applicationScope 5个作用域对象
param paramValues cookie initParam header header Values

注:

当要存取的数择名称中包含不是字母或数字的特殊字符时,只能使用 []。
当取得的数据为动态值时,只能使用 []。

jstl

条件语句

<c:if test="${salary > 2000}">

RestFul

概念

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

功能

资源:互联网所有的事物都可以被抽象为资源

资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。

分别对应 添加、 删除、修改、查询。

传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get

http://127.0.0.1/item/queryItem.action?id=1 查询,GET

使用RESTful操作资源http://127.0.0.1/item/queryItem.action/1

使用method属性指定请求类型

用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等

Spring

2020.6 最新更新到5.3

优点

1、Spring是一个开源免费的框架 , 容器 .

2、Spring是一个轻量级的框架 , 非侵入式的 .

3、控制反转 IoC , 面向切面 Aop

4、对事物的支持 , 对框架的支持

.......

一句话概括:

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。

控制反转(IOC)

依赖注入

控制反转IoC

创建对象由以前的程序员自己new 构造方法来调用,变成了交由Spring创建对象

(用 IOC:相当于去馆子(Sprin***了一只鸡,交到你手上的时候,已经五味俱全,你就只管吃就行了。

setter 和构造方法注入

实现ioc的方法

概念

  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

构造器注入

我们在之前的案例已经讲过了

Set 注入

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .

1、常量注入

2、Bean注入

3、数组注入

...

p命名和c命名注入

1、P命名空间注入 : 需要在头文件中加入约束文件

 导入约束 : xmlns:p="http://www.springframework.org/schema/p"

 <!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
 <bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>

2、c 命名空间注入 : 需要在头文件中加入约束文件

 导入约束 : xmlns:c="http://www.springframework.org/schema/c"
 <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
 <bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>

bean作用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象 .

img

自动装配

Spring中bean有三种装配机制,分别是:

  1. 在xml中显式配置;
  2. 在java中显式配置;
  3. 隐式的bean发现机制和自动装配。

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
  2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。

推荐不使用自动装配xml配置 , 而使用注解 .

byName

autowire byName (按名称自动装配)

由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。

采用自动装配将避免这些错误,并且使配置简单化。

byType

autowire byType (按类型自动装配)

使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。

作用域

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Controller("user")
@Scope("prototype")
public class User {
   @Value("秦疆")
   public String name;
}

注解开发

说明

在spring4之后,想要使用注解形式,必须得要引入aop的包

在配置文件当中,还得要引入一个context约束

Bean的实现

我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

1、配置扫描哪些包下的注解

<!--指定注解扫描包-->
<context:component-scan base-package="com.kuang.pojo"/>

2、在指定包下编写类,增加注解

@Component("user")

属性注入

使用注解注入属性

1、可以不用提供set方法,直接在直接名上添加@value("值")

2、如果提供了set方法,在set方法上添加@value("值");

自动装配

使用注解

jdk1.5开始支持注解,spring2.5开始全面支持注解。

准备工作:利用注解的方式注入属性。

1、在spring配置文件中引入context文件头

xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

2、开启属性注解支持!

<context:annotation-config/>

@Autowired

  • @Autowired是按类型自动转配的,不支持id匹配。
  • 需要导入 spring-aop的包!

@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用。

@Resource

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

小结

@Autowired与@Resource异同:

1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

衍生注解

我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

小结

XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>  

作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null!

JavaConfig

java中显式配置 装配机制

原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。

测试:

1、编写一个实体类,Dog

@Component  //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
   public String name = "dog";
}

2、新建一个config配置包,编写一个MyConfig配置类

@Configuration  //代表这是一个配置类
public class MyConfig {

   @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
   public Dog dog(){
       return new Dog();
  }

}

面向切面AOP

代理模式

为什么要学习代理模式,因为AOP的底层机制就是动态代理!

代理模式:

  • 静态代理
  • 动态代理

静态代理

静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 使用代理角色来进行一些操作 .

分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想

动态代理

  • 动态代理的角色和静态代理的一样 .

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理--cglib
    • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
    • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!

JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看

【InvocationHandler:调用处理程序】

静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口!

声明式事务

事务

  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
  • 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

编程式事务管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理

  • 一般情况下比编程式事务好用。
  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

使用Spring管理事务,注意头文件的约束导入 : tx

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

事务管理器

  • 无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。
  • 就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。

JDBC事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
</bean>

配置好事务管理器后我们需要去配置事务的通知

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
       <tx:method name="add" propagation="REQUIRED"/>
       <tx:method name="delete" propagation="REQUIRED"/>
       <tx:method name="update" propagation="REQUIRED"/>
       <tx:method name="search*" propagation="REQUIRED"/>
       <tx:method name="get" read-only="true"/>
       <tx:method name="*" propagation="REQUIRED"/>
   </tx:attributes>
</tx:advice>

spring事务传播特性:

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!

配置AOP

导入aop的头文件!

<!--配置aop织入事务-->
<aop:config>
   <aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

SpringMVC

原理

​ 当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

img

SpringMVC执行原理

img

图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。

创建流程

实现步骤其实非常的简单:

  1. 新建一个web项目
  2. 导入相关jar包
  3. 编写web.xml , 注册DispatcherServlet
  4. 编写springmvc配置文件
  5. 接下来就是去创建对应的控制类 , controller
  6. 最后完善前端视图和controller之间的对应
  7. 测试运行调试.

使用springMVC必须配置的三大件:

处理器映射器、处理器适配器、视图解析器

通常,我们只需要手动配置视图解析器,而处理器映射器处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置

配置

/ 和 /* 的区别:< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;即:.jsp 不会进入spring的 DispatcherServlet类 。< url-pattern > /* </ url-pattern > 会匹配 *.jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。

    • 注意web.xml版本问题,要最新版!
    • 注册DispatcherServlet
    • 关联SpringMVC的配置文件
    • 启动级别为1
    • 映射路径为 / 【不要用/*,会404】
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.hhtc.controller"/>

<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />

<!--
支持mvc注解驱动
    在spring中一般采用@RequestMapping注解来完成映射关系
    要想使@RequestMapping注解生效
    必须向上下文中注册DefaultAnnotationHandlerMapping
    和一个AnnotationMethodHandlerAdapter实例
    这两个实例分别在类级别和方法级别处理。
    而annotation-driven配置帮助我们自动完成上述两个实例的注入。
 -->
<mvc:annotation-driven />

功能

常用注解

@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。

@RequestMapping RequestMapping是一个用来处理请求地址映射的注解

​ 方法级别的注解变体有如下几个:组合注解

    @GetMapping
    @PostMapping
    @PutMapping
    @DeleteMapping
    @PatchMapping

@GetMapping 是一个组合注解,平时使用的会比较多!

它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式。

@RestController 让Return返回字符串

@PathVariable 让方法参数的值对应绑定到一个URI模板变量上(Restful风格)

@RequestParam("username") String name 提交的域名称和处理方法的参数名不一致的问题

@ResponseBody 直接返回字符串

数据处理及跳转

转发: return "/index.jsp";

​ return "forward:/index.jsp";

重定向: return "redirect:/index.jsp";

数据显示到前端

第一种 : 通过ModelAndView

第二种 : 通过ModelMap

第三种 : 通过Model

就对于新手而言简单来说使用区别就是:

Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

当然更多的以后开发考虑的更多的是性能和优化,就不能单单仅限于此的了解。

乱码问题

SpringMVC给我们提供了一个过滤器 , 可以在web.xml中配置 .

修改了xml文件需要重启服务器!

<filter>
   <filter-name>encoding</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>
</filter>
<filter-mapping>
   <filter-name>encoding</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

但是我们发现 , 有些极端情况下.这个过滤器对get的支持不好 .

方法

1、修改tomcat配置文件 :设置编码!

<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
          connectionTimeout="20000"
          redirectPort="8443" />

2、自定义过滤器

Json

FastJson

fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是一样的。

fastjson 的 pom依赖!

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.60</version>
</dependency>

fastjson 三个主要的类:

JSONObject 代表 json 对象

  • JSONObject实现了Map接口, 猜想 JSONObject底层操作是由Map实现的。
  • JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取"键:值"对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。

JSONArray 代表 json 对象数组

  • 内部是有List接口中的方法来完成操作的。

JSON代表 JSONObject和JSONArray的转化

  • JSON类源码分析与使用
  • 仔细观察这些方法,主要是实现json对象,json对象数组,javabean对象,json字符串之间的相互转化。

拦截器

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

过滤器与拦截器的区别:拦截器是AOP思想的具体应用。

文件上传下载

准备工作

文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。

前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;

上传

文件上传

1、导入文件上传的jar包,commons-fileupload , Maven会自动帮我们导入他的依赖包 commons-io包;

2、配置bean:multipartResolver【注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!

3、Controller

下载

1、设置 response 响应头

2、读取文件 -- InputStream

3、写出文件 -- OutputStream

4、执行操作

5、关闭流 (先开后关)

SpringBoot

原理学习

自动配置:SpringBoot的版本控制中心

静态资源

1.webjars localhost:8080/webjars

2.public,static,/**,resource localhost:8080

优先级:resource>static(默认)>public

定制首页

themeleaf

只要需要使用,只需要导入对应的依赖就可以了

我们将html放在我们的templates即可

Mybatis

最新版本3.5.2

什么是Mybatis

  • MyBatis 是一款优秀的持久层框架
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。

为什么需要MyBatis

  • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .
  • 传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等... , 通过框架可以减少重复代码,提高开发效率 .
  • MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射
  • 所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别

运行流程

​ 1.mybatis从数据库中找来数据
​ 1.1 通过mybatis-config.xml 定位哪个数据库 (找数据库)
​ 1.2 通过Category.xml执行对应的select语句 (找表)
​ 1.3 把返回的数据库记录封装在Category对象中 (执行语句)
​ 1.4 把多个Category对象装在一个Category集合中 (返回封装对象的集合)
​ 2 返回一个Category集合

配置

  • mybatis-config.xml 系统核心配置文件
  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
  • 能配置的内容如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->
  • 子元素节点:environment

    • <!-- 事务管理 -->
      <transactionManager type="[ JDBC | MANAGED ]"/>
    • dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
    • 有三种内建的数据源类型
  • type="[UNPOOLED|POOLED|JNDI]")
  • 设置(settings)相关 => 查看帮助文档

    • 懒加载
    • 日志实现
    • 缓存开启关闭

CRUD

小结:

  • 所有的增删改操作都需要提交事务!

  • 接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!

  • 有时候根据业务的需求,可以考虑使用map传递参数!

  • 为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!

  • 模糊查询like语句该怎么写?**

    第1种:在Java代码中添加sql通配符。

    第2种:在sql语句中拼接通配符,会引起sql注入

作用域

  • SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

  • SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。

  • 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。

  • 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。

  • 如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try...catch...finally... 语句来保证其正确关闭。

  • 所以 SqlSession 的最佳的作用域是请求或方法作用域。

日志

Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。

标准日志实现

指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。

Log4j

简介:

  • Log4j是Apache的一个开源项目

  • 通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件....

  • 我们也可以控制每一条日志的输出格式;

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  • 使用步骤:

    1、导入log4j的包

    2、配置文件编写

    3、setting设置日志实现

    4、在程序中使用Log4j进行输出!

    5、测试,看控制台输出!

    • 使用Log4j 输出日志
    • 可以看到还生成了一个日志的文件 【需要修改file的日志级别】

分页

在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。

使用Limit实现分页

#语法
SELECT * FROM table LIMIT stratIndex,pageSize

SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15  

#为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:   
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.  

#如果只给定一个参数,它表示返回最大的记录行数目:   
SELECT * FROM table LIMIT 5; //检索前 5 个记录行  

#换句话说,LIMIT n 等价于 LIMIT 0,n。

步骤:

1、修改Mapper文件

2、Mapper接口,参数为map

3、在测试类中传入参数测试

注解

@Param注解用于给方法参数起一个名字。以下是总结的使用原则:

  • 在方法只接受一个参数的情况下,可以不使用@Param。
  • 在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
  • 如果参数是 JavaBean , 则不能使用@Param。
  • 不使用@Param注解时,参数只能有一个,并且是Javabean。

#与$的区别

  • #{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】

    INSERT INTO user (name) VALUES (#{name});
    INSERT INTO user (name) VALUES (?);
  • ${} 的作用是直接进行字符串替换

    INSERT INTO user (name) VALUES ('${name}');
    INSERT INTO user (name) VALUES ('kuangshen');

多表查询

多对一:多对一的理解:

  • 多个学生对应一个老师
  • 如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!

一对多:一对多的理解:

  • 一个老师拥有多个学生
  • 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!

小结

按照查询进行嵌套处理就像SQL中的子查询

按照结果进行嵌套处理就像SQL中的联表查询

1、关联-association

2、集合-collection

3、所以association是用于一对一和多对一,而collection是用于一对多的关系

4、JavaType和ofType都是用来指定对象类型的

  • JavaType是用来指定pojo中属性的类型
  • ofType指定的是映射到list集合属性中pojo的类型。

动态sql

所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

<select id="findActiveBlogWithTitleLike" resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果

小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。

choose、when、otherwise

传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

trim、where、set

where

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

trim

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

set

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

sql片段

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

提取SQL片段:

<sql id="if-title-author">
   <if test="title != null">
      title = #{title}
   </if>
   <if test="author != null">
      and author = #{author}
   </if>
</sql>

引用SQL片段:

<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
       <include refid="if-title-author"></include>
       <!-- 在这里还可以引用其他的 sql 片段 -->
   </where>
</select>

注意:

①、最好基于 单表来定义 sql 片段,提高片段的可重用性

②、在 sql 片段中不要包括 where

ForEach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

select * from user where 1=l and id=1 or id=2 or id=3

 <foreach collection="ids"  item="id" 
 open="and (" close=")" separator="or">
      id=#{id}
 </foreach>

缓存

简介

为了提高查询的效率

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3、什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

一级缓存失效的四种情况

一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!

1、sqlSession不同

2、sqlSession相同,查询条件不同

3、sqlSession相同,两次查询之间执行了增删改操作!

4、sqlSession相同,手动清除一级缓存

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;\

使用步骤

  • 1、开启全局缓存 【mybatis-config.xml】

  • 2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】

  • 3、代码测试

    • 所有的实体类先实现序列化接口
    • 测试代码

结论

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

第三方缓存

第三方缓存实现--EhCache: 查看百度百科

Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;

要在应用程序中使用Ehcache,需要引入依赖的jar包

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
   <groupId>org.mybatis.caches</groupId>
   <artifactId>mybatis-ehcache</artifactId>
   <version>1.1.0</version>
</dependency>

在mapper.xml中使用对应的缓存即可

<mapper namespace = “org.acme.FooMapper” >
   <cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
</mapper>

合理的使用缓存,可以让我们程序的性能大大提升!

MybatisPlus