2022Java面试题,非常全面

JAVA基础


1.基本数据类型 : byte short int long float double char boolean
byte char short 平级 int float long double

2.引用数据类型:数组,类,接口     

3.语句 if swich do while for
判断固定个数,用if或switch
switch效率相对高
switch(值){

   case 值:要执行的代码;
   break;
}
break可以省略,省略就一直执行直到遇见break,break用来跳出循环,循环嵌套时,break只跳出当前所在循环continue:结束本次循环,开始下次循环  "值" 是byte char short int类型
while, for 可以互换,如需控制循环次数,用for,for循环结束后变量会在内存释放

4.函数
函数就是单独的功能体现,可以理解为一个方法
作用:1.定义功能 2.封装代码,提高复用性,一个函数可以多次使用
注意:函数中只能调用函数,不能定义函数
主函数的作用:1.保证类独立运行 2.程序的入口 3.被jvm调用
重载 :函数名一样,参数不一样
重写 :函数名一样,内容不一样

5.数组
用于储存同一类型数据的容器,编号从0开始,是封装数据的具体实体
定义方式   
int a = new int[5]
int a ={0,1,2}
int a = new int[]{0,1,2}

 //二分查找法。必须有前提:数组中的元素要有序。 
    public static int halfSeach_2(int[] arr,int key){
      int min,max,mid;
      max = arr.length—1;
      mid = (max+min)>>1; //(max+min)/2;
      while(arr[mid]!=key){
      if(key>arr[mid]){
        min = mid + 1;
      }

      else if(key         max = mid — 1;
        if(max         return —1;
        mid = (max+min)>>1;
      }     
 return mid;    }
内存java分5片内存 1.寄存器 2.本地方法区 3.方法区 4.栈 5.堆
栈 后进先出,储存局部变量,即用即释放
堆 先进先出,储存实体,每个实体都有内存首地址

6.面向对象
特点:
1.将复杂的事情简单化 
2.符合现在人们的思考习惯
3.从执行者变为指挥者
面向过程:一步一步来,偏过程化,强调功能行为,先后顺序,用函数将步骤一步步实现,使用时依次调用函数。
面向对象:站在对象的角度思考问题,将功能放在不同的对象里,强调的是具备某些功能对象,最小的程序单元是类

7.成员变量和局部变量
成员变量 
定义在类中,整个类有效,存在堆内存,随该对象的消失而消失
局部变量 
定义在方法,参数,语句上,只在该大括号内生效,存在栈中,大括号结束释放

8.构造函数
用于给对象初始化,名字与类的名字相同,不需要定义返回值类型,没有具体返回值。所有类都需要初始化才能使用,一个类定义时如果没有定义构造函数(有参构造),会默认生成一个无参构造,如果定义了有参构造,无参构造会被覆盖。一个类可以有多个构造函数,以重载体现

什么时候使用构造函数:分析该事务,发现该事务出生时就有一些特征,这些特征要定义在构造函数里
构造代码块:给所有对象进行初始化时调用,所有对象共同调用一个构造代码块,对象每建立一次就会调用一次

Person p = new Person();
创建一个对象都在内存中做了什么事情?    
1:先将硬盘上指定位置的Person.class文件加载进内存。    
2:执行main方法时,在栈内存中开辟了main方法的空间(压栈—进栈),然后main方法的栈区分配了一个变量p。    
3:在堆内存中开辟一个实体空间,分配了一个内存首地址值。    4:在该实体空间中进行属性的空间分配,并进行了默认初始化。    5:对空间中的属性进行显示初始化。    
6:进行实体的构造代码块初始化。    
7:调用该实体对应的构造函数,进行构造函数初始化。
8:将首地址赋值给p ,p变量就引用了该实体。(指向了该对象)

9.面向对象的三大特征:封装,继承,多态
封装:隐藏对象的属性和实现细节,只对外提供访问方式    
优点:安全性,重用性高,方便使用

static:静态修饰符,用于修饰成员变量和成员函数
特点:
1.要共享这个对象,用static修饰          
2.被static修饰的成员可以直接被类名调用          
3.跟这类一起加载,意味着类产生的时候这个对象就产生了          4.存在于方法区中,随类的加载而加载,类的消失而消失
成员变量是对象的特有数据,静态变量是对象的共享数据
静态代码块:定义在类中,可以完成类的初始化,随类的加载而执行,只执行一次,如果主函数在一个类,优于主函数执行。
特点:
1.提高代码复用性 
2.让类与类之间产生关系,提供多态的前提

单继承:一个类只有一个父类
多继承:一个类有多个父类。java中只支持单继承,但接口实际上支持了多继承
子类调用父类属性值用super关键字,this和super都要放在函数第一行,二者不能共存,子类构造函数运行时会先运行父类构造函数,因为子类构造函数第一行会有隐藏的super语句,子类继承了父类的属性,就要先将父类的对象初始化,如果父类没有无参构造,子类的构造函数中必须用super访问父类中的构造函数
final:用来修饰类,方法,变量
被修饰的类,不能被继承;被修饰的方法,不能被覆盖;被修饰的变量,只能赋值一次
抽象类 abstract
特点 
1.只定义在抽象类中,必须用关键词修饰        
2.只注重方法声明,不注重方法实现         
3.不能被实例化,必须通过子类继承该抽象类中的所有抽象方法,该子类才能被实例化        
4.抽象类中是有构造函数的,用来给子类初始化        
5.抽象类中是可以定义非抽象方法的        
6.abstract不能和final,private,static共存.

接口 interface
接口里有抽象方法,也不能实例化,类似抽象类,类与类之间是继承extends,接口与接口之间是实现 implements接口可以被多实现,是多继承改良的后果,避免了单继承的局限性
接口与接口之间可以多继承
接口是对外提供的规则,功能的扩展,降低了耦合性

抽象类跟接口都是不断向上抽取的结果
区别:
1.抽象类只能被继承,单继承,接口是被实现,可以被多实现
2.抽象类中可以定义抽象和非抽象方法,子类直接继承使用,接口中的抽象方法需要子类实现
3.抽象类的成员修饰符可以自定义,接口都是Public

多态
体现:父类引用或者接口引用指向自己的子类  Animal a = new Cat();
好处: 提高程序扩展性
弊端:父类引用指向子类,只能访问父类中的方法,不能访问子类中的方法,前期不能用后期的功能,访问的局限性
前提:要有关系,继承,实现,通常会有覆盖操作

理解:多态里,自始至终都是子类对象在做着类型变化。父与子,父是高,子是低,子类对象可以调用父类对象里的方法,但父类对象不能调用子类中的方法,除非父类对象转成子类,高转低需要强转,因为子类里面有很多新东西父类是不一定有的,低转高不需要强转,因为父类里有的东西子类一定有
如果想用子类对象的特有方法,如何判断对象是哪个具体的子类类型呢?可以通过一个关键字 instanceof ;
//判断对象是否实现了指定的接口或继承了指定的类格式:<对象 instanceof 类型> ,判断一个对象是否所属于指定的类型。Student instanceof Person = true;//st

异常
Throwable:可抛出的    
Error:错误,通常是jvm产生的
Exception:异常,可以有针对性的处理
这个体系中的所有类和对象都具备一个独有的特点,就是可抛性。
可抛性的体现:就是这个体系中的类和对象都可以被throws和throw两个关键字所操作                  
throw用于抛出异常对象,后面跟异常对象,用在函数内
throws用于抛出异常类,后面跟类名,用在函数上
异常分为编译时被检查的异常跟运行时异常,Exception及其子类都是编译时异常Excepiton特殊子类RuntimeException及它的子类是运行时异常
区别:编译时异常在函数内被抛出,函数必须声明,否则编译失败,声明原因时需要调用者对该异常进行处理,运行时异常在函数内被抛出不需要声明
什么时候try什么时候throws
功能内部异常,如果内部可以处理,用try,如果处理不了,必须声明出来让调用者处理。
常见异常
脚标越界异常(IndexOutOfBoundsException)包括数组、字符串;
空指针异常(NullPointerException)
类型转换异常:ClassCastException
没有这个元素异常:NoSuchElementException
不支持操作异常:UnsupportedOperationException

常见软件包
java.lang 语言
java.awt图形界面开发对象
javax.swing提供所有的windows桌面应用程序包括的控件
java.net 网络编程对象
java.io 操作设备上数据的对象
java.util工具包,时间对象,集合框架
java.applet客户端java小程序多线程

进程:正在进行中的程序。进程是一个应用程序运行时的内存分配空间
线程:进程中的一个程序执行控制单元。一个进程至少有一个线程在运行,如果进程中有多个线程,这就是多线程程序,每个线程在栈区都有各自的执行空间,方法,变量。jvm在启动时,首先有一个主线程负责程序的执行,调用main函数
随机性的原理:因为cpu高速切换,哪个线程获得了cpu执行权,那个线程就执行。
返回当前线程的名称:Thread.currentThread().getName()
线程的名称是由:Thread—编号定义的。编号从0开始。线程要运行的代码都统一存放在了run方法中。线程要运行必须通过start方法开启,启动了线程,让jvm调用run方法

创建线程
1.继承Thread类,子类复写run方法。定义一个类继承Thread类,复写run方法,将要运行的代码写进run方法里,通过创建该子类对象创建线程对象,调用start方法开启线程,执行run方法
2.实现Runnable接口,定义一个类实现Runnable接口,覆盖run方法,通过Thread类创建线程对象,将该实现类的子类对象作为实参传给Thread类的构造函数,调用start方法开启线程,运行run方法
线程的状态
被创建 
start()
运行 
具备执行资格,具备执行权
冻结 
线程释放了执行权
临时阻塞 
有执行资格,但没执行权
消亡
stop()
为什么会有Runnable接口
单继承的局限性,如果一个类已经有了父类,那就不能继承Thread类,实现该接口可以避免单继承的局限性,它将线程要执行的任务封装成一个对象

run方法和start方法的区别
程序调用start方法时一个新线程会被创建,并且在run方法中的代码会在新线程上运行,直接调用run方法不会创建新线程,run内部的代码会在当前线程上运行。如果要运行需要消耗大量时间的任务,最好使用start方法,否则调用run方法时主线程会被卡住。一旦一个线程被启动,不能重复调用该thread对象的start方法,调用已启动线程的start方法会报异常,但run方法可以重复调用。

多线程的安全问题
1.多个线程操作共享数据
2.多条语句对共享数据进行运算这些语句在某一时刻被一个线程执行时,还未执行完就被其他线程执行了
解决原理:执行完前不让其他线程执行
解决方法:同步代码块
同步
优点:解决线程安全问题
缺点:降低性能
前提
1.有两个或以上的线程
2.多个线程必须保证使用同一个锁
同步函数和同步代码块
同步代码块使用的锁可以是任意对象,同步函数使用的锁是this,一个类中只能有一个同步时,可以使用同步函数,如果有多个同步,必须使用同步代码块。同步代码块更灵活
常见的javaAPI
String字符串一旦被初始化就不能改变
常见的String字符串方法
length();获取长度
char charAt(int index)获取指定位置的字符
int indexOf(int ch)获取指定字符的位置
String substring截取子符串
equals判断是否相同
String[] split分割字符串
String replace(old,new)字符串内容替换
compareTo();参数字符串=该字符串,返回0,小于,返回<0,大于返>0

StringBuffer和StringBuilder
可以对字符串的内容进行修改,是一个可变长度的容器,可以储存任意类型的数据,最后要变成字符串
固定方法
append追加数据到尾部
insert插入数据
delete删除
replace替换
indexOf查找
reverse反转
二者的区别
Buffer线程安全,效率低,Builder不安全,效率高,单线程用Builder,多线程用Buffer

包装类
byte Byte
short Shor
int Integer
long Long
float Float
double Double 
char Character
Boolean Boolean
除了Character,都有 XXX parseXXX方法

集合框架特点
1.用于储存对象
2.可变长度

和数组的区别
1.数组长度固定,集合可变
2.数组可以储存基本数据类型和引用数据类型(接口,类,数组),集合只能储存引用数据型
3.数组必须保证储存的都是同一数据类型,集合不用
数据结构:就是容器中存储数据的方式。
类型有
线性表(ArryaList)链表(LinkedList)队列(Queue)图(Map)树(Tree)

Collection接口
List:有序,存入和取出顺序一致,元素有索引,可以重复
Set:无序,不可重复
Iterator接口迭代器,是一个接口,用于取集合中的元素

List接口是Collection接口的子接口,最大特点是List的特有方法都有索引有序,元素都有索引,可以重复
1.ArrayList:线程不同步,查找快,增删慢,底层数据结构是数组2.LinkedList:线程不同步,增删快,查找慢,底层数据结构是双链表3.Vector:线程同步,查找增删都慢,底层数据结构是数组
常用方法
add插入一个元素
addAll插入一堆元素
remove删除
indexOf获取指定元素第一次出现的索引为
Object set修改
listIterator获取所有元素,list特有的迭代器List集合支持对元素的增删改查

Set接口
Set接口取出方式只有使用迭代器
1.HashSet:线程不同步,无序,高效,保证唯一性通过元素的HashCode方法结合equals实现。元素的HashCode相同,才使用equals,不同则不用使用
2.LinkedHashSet:有序,是HashSet的子类
3.TreeSet:不同步,底层是二叉树,对Set集合中的元素进行指定顺序的排序
哈希表的原理
1.对对象元素中的关键字进行哈希算法运算,得到哈希值
2.哈希值是这个元素的位置
3.如果元素的哈希值相同,再判断这两个元素是否相同,如果相同,不用重复储存,如果不,向后延伸储存
4.存储哈希值的结构叫哈希表

对于ArrayList,判断元素是否存在或者删除元素,依据equals方法
对于HashSet,依据HashCode和equals

TreeSet
用于对Set集合进行元素的指定顺序排序,排序需要依据元素自身具备的比较性如果元素不具备比较性,运行时会发生ClassCastException异常

所以需要元素实现Comparable接口,强制让元素具备比较性,复写CompareTo方法依据返回值,确定元素的位置,如果返回0,证明该元素存在,不储存,这就是保证唯一性方法判断是,需要分主要条件和次要条件,主要条件相同,再判断次要条件,按照次要条件排序两种排序方式 

Comparable Comparetor
1.让元素自身具备比较性,需要元素对象实现Comparable 接口,覆盖compareTo方法
2.让集合自身具备比较性,需要定义一个实现了Comparator接口的比较器,覆盖compare法,将该类对象作为实参传递给TreeSet集合构造函数,第二种方法较为灵活

Map集合
HashTable:线程同步,不可存null,底层哈希表
HashMap:线程不同步,可以存null底层哈希表
TreeMap:可以对map集合中的键进行指定顺序的排序,底层二叉树
Map集合存储和Collection有很大不同
Collection一次存一个元素,Map存一对
Collection是单例结合,Map是双列Map存的是键值对,要保证键的唯一性

Collection 和 Collections的区别:      
Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法实现对集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。    
Collection是个java.util下的接口,它是各种集合结构的父接口,继承于它的接口要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历。

get请求和post请求的区别
get请求的数据多附加在url之后以?分割URL和传输数据,多个参数用&连接。URL的编码格式时ASCII格式,不是uniclde,所有的非ASCII字符都要编码后再传输
post请求会把请求的数据放置在HTTP请求包的包体中,上面的item=bandsaw就是实际传输的数据
总结就是,get请求的数据会暴露在地址栏中,且传输数据会收到url长度的限制,post不会暴露且不受限制,所以post请求的安全性要比get请求高
后端接受前端参数的三种注解方式
1.@RequestParam注解
作用:将指定的请求参数赋值给方法中的形参
接受形式:get传参请求
属性:value:绑定请求参数名,默认绑定同名形参;required:是否必须,默认true,表示请求中一定要有相应参数,否则报错;defaultValue默认值,表示如果请求中没有接收到该值的默认值
2.@PathVariable注解
1.    作用:接受请求路径中占位符的值
2.    接受形式:get路径请求
3.    属性:value:String形式,绑定请求的参数,默认绑定同名形参
3.@RequestBody注解
1.    作用:接受前端传递的json字符串
2.    接受形式:post请求

增强for循环
for(元素类型 变量名 : 集合/数组){
}

枚举类enum
针对对象的某个属性的值不能任意,必须是固定一组数值中的某一个

常用API
Math数学运算工具类
Date日期类月份0-11日期转字符串 
formatCalendar日历类

IO流 用于处理设备上的数据分类
1.输入流和输出流(读和写)
2.字节流和字符流
字节流:InputStream  OutputStream
字符流:Reader Writer

File类:将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。
File类常见方法:
1:创建。
boolean createNewFile():在指定目录下创建文件,如果该文件已存在,则不创建。而对作文件的输出流而言,输出流对象已建立,就会创建文件,如果文件已存在,会覆盖。除续写。
boolean mkdir():创建此抽象路径名指定的目录。
boolean mkdirs():创建多级目录。 
2:删除。
boolean delete():删除此抽象路径名表示的文件或目录。
void deleteOnExit():在虚拟机退出时删除。注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。window的删除动作,是从里往外删。注意:java删除文件不走回收站。要慎用。
3:获取.
long length():获取文件大小。
String getName():返回由此抽象路径名表示的文件或目录的名称。String getPath():将此抽象路径名转换为一个路径名字符串。
String getAbsolutePath():返回此抽象路径名的绝对路径名字符串。String getParent():返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目,则返回 null。
long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
File.pathSeparator:返回当前系统默认的路径分隔符,windows默认为 “;”。
File.Separator:返回当前系统默认的目录分隔符,windows默认为 “\”。4:判断:
boolean exists():判断文件或者文件夹是否存在。
boolean isDirectory():测试此抽象路径名表示的文件是否是一个目录。boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。boolean isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件。
boolean isAbsolute():测试此抽象路径名是否为绝对路径名。
5:重命名。
boolean renameTo(File dest):可以实现移动的效果。剪切+重命名。String[] list():列出指定目录下的当前的文件和文件夹的名称。包含隐藏文件。如果调用list方法的File 对象中封装的是一个文件,那么list方法返回数组为null。如果封装对象不存在也会返回null。只有封装的对象存在并且是文件夹时,这个方法才有效。

反射
动态加载一个指定的类,并获得该类中的所有内容,反射技术可以对一个类进行解剖
好处:大大增强了程序的扩展性
基本步骤:
1.获得Class对象,就是获取到指定的名称的字节码文件对象    
2.实例化对象,获得类的属性,方法和构造函数    
3.访问属性,调用方法,调用构造函数创建对象

如何获取Class对象
1.通过getClasss,但必须要创建该类对象才可以调用
2.静态的属性class,但必须先声明该类
前两种都不利于程序的扩展,因为都需要在程序使用具体的类完成
3.使用Class类中的forName方法指定什么类名,就获取什么类的字节码文件对象,只需要将类名的字符串传入即可
// 1. 根据给定的类名来获得  用于类加载
String classname='xxxxxxxx';
Class clazz = Class.forName(classname);
// 2. 如果拿到了对象,不知道是什么类型   用于获得对象的类型Object obj = new Person();
Class clazz1 = obj.getClass();// 获得对象具体的类型
// 3. 如果是明确地获得某个类的Class对象  主要用于传参
Class clazz2 = Person.class;

反射的用法:
1)、需要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三方式:
Class.forName(classname) 用于做类加载
obj.getClass() 用于获得对象的类型
类名.class     用于获得指定的类型,传参用
2)、反射类的成员方法:
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Clas[{paramClazz1,paramClazz2});
method.invoke();
3)、反射类的构造函数:
Constructor con = clazz.getConstructor(new Clas[{paramClazz1,paramClazz2,...})
con.newInstance(params...)
4)、反射类的属性:
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);

获取了字节码文件对象后,最终都需要创建指定类的对象:
创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):1,调用空参数的构造函数:使用了Class类中的newInstance()方法。2,调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数对象的newInstance(实际参数) 进行对象的初始化。 综上所述,第二种方式,必须要先明确具体的构造函数的参数类型,不便于扩展。所以一情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数

B/S 浏览器/服务器程序
C/S 客户端/服务     

                                                                                                
MySql

创建表
create table '表名' (
      ‘xx’列类型 [属性][索引][注释]

数值类型
tinyint 非常小的数据
smallint 较小
mediumint中等大小
int 标准中等整数
bigint 较大整数
float 单精度浮点数
double 双精度浮点数
decimal 字符串形式浮点数

字符串类型
char[(M)] 固定长字符串,检索快,费空间
varchar[(M)] 可变字符串
tinytext 微型文本串
text 文本串

日期和时间类
DATE YYYY-MM-DD,日期格式
TIME Hh:mm:ss,时间格式
DATETIME YYYY-MM-DD hh:mm:ss
TIMESTAMP  YYYYMMDDhhmmss 格式表示的时间戳
YEAR YYYY格式的年份

数据表类型
MyISAM,InnoDB

名称        MyISAM        InnoDB
事务            不支持         支持
数据行锁定  不支持         支持
外键            不支持         支持
全文索引          支持         不支持
表空间大小     小         大,约2倍
适用场合    节约空间及响应速度         安全性,事务处理,多用户操作

修改数据库
修改表名   ALTER TABLE 旧 RENAME AS 新
添加字段   ALTER TABLE 表名 ADD字段名 列属性[属性]
修改字段   ALTER TABLE 表名 MODIFY 字段名 列类型[属性]
             ALTER TABLE 表名 CHANGE 旧 新 列属性[属性]
删除字段   ALTER TABLE 表名 DROP 字段名

删除表       DROP TABLE 表名
如果删除不存在的表会报错
                                                                                                                                                                                                                                                                      外键
将一个表的值放在第二个表中表示关联,这个值是第一个表的主键,第二个表保存这些值的属性称为外键 foreign key
作用 保持数据一致性,完整性,主要目的是控制储存在外键表中的数据,约束,使两张表形成关联,外键只能引用外表中列的值或空值

删除具有主外键关系的表时,先删子再删主

添加 INSERT
INSERT INTO 表名[(字段1,字段2,字段3)] VALUES('值1','值2','值3')

更新 UPDATE  

UPDATE 表名 SET column_name= value [,column_name2=value2,...] [WHEREcondition];   

删除  DELETE                                                                                                                        

DELETE FROM 表名 [WHERE condition]; 
                                                                                                                                                            TRUNCATE

清空表数据,结构索引约束不变,比DELETE速度快,但不删除表结构,会重置自增计数器,对事物没有影响
TRUNCATE [TABLE] 表名;

SELECT
SELECT * FROM TABLE 
inner/left/right join TABLE2
WHERE  条件
GROUP BY 按照字段分组
HAVING  过滤分组的记录需要满足的次要条件
ORDER BY 排序
LIMIT 分页 

DISTINCT 去掉重复结果
SELECT DISTINCT  ID  FROM TABLE;


模糊查询
IS NULL  a IS NULL, a是NULL,为真
IS NOT NULL   a IS NOT NULL,a不是NULL,为真
BETWEEN    a BETWEEN b AND c,a在b和c之间
LIKE  a LIKE b,模糊查询,a匹配b

LIKE '佐%' 查询佐
LIKE '佐_'  查询佐后面有一个字的
LIKE '佐__'  查询佐后面有两个字的
LIKE '%佐%' ,查询名字里有佐字的

IN  a IN(b,c,d),a等于bcd中的某一个

连接查询
INNER JOIN 内连接,查交集,表中有至少一个匹配,返回行
LEFT JOIN 左连接,有匹配返回匹配,无匹配返回左表所有行,右表用NULL填充
RIGHT JOIN 右链接,与左连接相反


SELECT s.studentno,studentname,subjectno,StudentResult  
FROM  student s 
INNER JOIN result r 
ON r.studentno = s.studentno   
                                                                                                                                                                                                                                                                                                                         排序和分页
ORDER BY 排序
默认按照ASC升序对记录排序,如果想降序使用DESC关键字
ORDER BY 属性名 
DESC
                                                                                                                                                            LIMIT分页
LIMITE [offset,x]
每页显示五条数据
LIMIT 0,5
前面的是第几页开始,后面的是大小规格


子查询
在一个查询里嵌套另一个查询,使用连接查询可以达到相同效果

 -- 方法一:使用连接查询
SELECT studentno,r.subjectno,StudentResult 
FROM result r 
INNER JOIN `subject` sub 
ON r.`SubjectNo`=sub.`SubjectNo`
 WHERE subjectname = '数据库结构-1' 
ORDER BY studentresult DESC;

-- 方法二:使用子查询(执行顺序:由里及外) 
SELECT studentno,subjectno,StudentResult 
FROM result 
WHERE subjectno=(   
            SELECT subjectno  FROM `subject`     
            WHERE 
subjectname = '数据库结构-1' 
) ORDER BY studentresult DESC;                                                                                                                                                                                                                      

MYSQL  函数

数据函数
SELECT ABS 绝对值
SELECT CEILING 向上取整
SELECT FLOOR 向下取整
SELECT RAND  返回随机数
SELECT SIGN   负数返回-1.正数返回1,0返回0

                                                                                                                                   字符串函数      
SELECT CHAR_LENGTH('xxxxxxxxxx') 返回引号里字符串的字数
SELECT CONCAT('A','B','C') 将ABC合并起来
SELECT INSERT('abcd',1,2,'ef') 替换字符串,从规定的位置(1)替换规定的长度 (2)
SELECT LOWER('shuilaiqi')小写
SELECT UPPER  ('shuilaiqi') 大写
SELECT LEFT('XXXXXXXXXX',5) 从左边截取
SELECT RIGHT('XXXXXXXXXX',5) 从右边截取                 
SELECT REPLATE('ABCDEFG','CD','ZZ') 替换
SELECT SUBSTR('ABCDEFG',2,3)截取,从开始截取3个
SELECT REVERSE('adcdefg') 反转

日期和时间函数  
                                                                                                           SELECT CURRENT_DATE();   /*获取当前日期*/
SELECT CURDATE();   /*获取当前日期*/ 
SELECT NOW();   /*获取当前日期和时间*/ 
SELECT LOCALTIME();   /*获取当前日期和时间*/ 
SELECT SYSDATE();   /*获取当前日期和时间*/
-- 获取年月日,时分秒 
SELECT YEAR(NOW()); 
SELECT MONTH(NOW()); 
SELECT DAY(NOW()); 
SELECT HOUR(NOW()); 
SELECT MINUTE(NOW()); 
SELECT SECOND(NOW());   
                                                                                                                                                                                                                                                                                                                         聚合函数
COUNT() 返回满足SELECT条件的记录总和数  SELECT COUNT(*/1)
不建议用COUNT(*),会查出来字段为Null,效率低
COUNT(1)不会查出来NULL

SUM() 返回数字字段或表达式列统计,返回一列的和
AVG() 平均值
MAX  返回最大值
MIN返回最小值   
                                                                                                                                                            SELECT SUM(StudentResult) AS 总和 FROM result; 
SELECT AVG(StudentResult) AS 平均分 FROM result; 
SELECT MAX(StudentResult) AS 最高分 FROM result; 
SELECT MIN(StudentResult) AS 最低分 FROM result; 
                                                                                                                                                            MD5加密
update 表名 set 列名 = md59(列名);      
INSERT INTO testmd5 VALUES(4,'kuangshen3',md5('123456'));          

事务
ACID原则
A 原子性 
事务中所有操作要么都完成,要么都不完成,不能停滞在中间某个环节,出现错误则回滚到最初,就像该事务从未执行

C 一致性 
事务可以封装改变状态,必须保证始终与系统处于一致,主要特征是保护性和一致性,如果五个账户各有100块,无论这五个账户怎么交易,总金额还是500块

I 隔离性
隔离状态执行事务,两个事务在相同时间执行相同的功能,事务的隔离性将保证每个事务在系统中认为只有一个在使用系统,防止事务操作间混淆

D 持久性
事务完成后,该事务对数据库的更改便持久保存在数据库中,不会被回滚

基本语法
改变自动提交模式,MySQL默认自动提交,使用事务要关闭自动提交
SET autocommit =0;                关闭
SET autocommit =1;                开启

START TRANSACTION       开始一个事务,标记事务起始点
COMMIT            提交事务给数据库
ROLLBACK        回滚事务
SET autocommit =1;                 开启自动提交保存点
SAVEPOINT   xxxx   设置一个叫xxxx的保存点
ROLLBACK  TO SAVEPOINT   xxxx   回滚到保存点                                                          RELEASE SAVEPOINT xxxx   删除保存点  
                                                                                                                                                                                                                                                                                                                       

 CREATE DATABASE `shop`CHARACTER SET utf8 COLLATE utf8_general_ci;
 USE `shop`; 
CREATE TABLE `account` ( 
 `id` INT(11) NOT NULL AUTO_INCREMENT,  
`name` VARCHAR(32) NOT NULL,
 `cash` DECIMAL(9,2) NOT NULL,  
PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO account (`name`,`cash`)
 VALUES('A',2000.00),('B',10000.00) 


-- 转账实现
SET autocommit = 0;   -- 关闭自动提交 
START TRANSACTION;  -- 开始一个事务,标记事务的起始点 
UPDATE account SET cash=cash-500 WHERE `name`='A'; 
UPDATE account SET cash=cash+500 WHERE `name`='B';
COMMIT; -- 提交事务 
# rollback; 
SET autocommit = 1; -- 恢复自动提交


 隔离性问题
脏读:一个事务读取并修改了数据,未提交时另一个事务也访问了

丢失修改:一个事务读取数据时,修改了这个数据,另一个事务读取这个数据时也修改了,此时A对数据的修改结果就丢失了

不可重复读:一个事务读取数据时,另一个事务访问并修改了这个数据,第一个事务再次读取这个事务时发现第一次读取和第二次读取的值不一样

幻读:一个事务读取数据时,另一个事务插入了一些输入,导致前一个事务读取数据时凭空出现了一些数据

不可重复读和幻读的区别在于,不可重复读注重值被修改,幻读注重值的添加或删除


默认隔离级别

读取未提交
最低隔离级别,允许读取未提交的数据变更,可能导致脏幻不可重复读

读取已提交
允许读取已提交的数据变更,可能导致幻或不可重复读

可重复读
对同一字段多次读取结果都一样,可能导致幻读

可串行化
最高级别,所有事务一次逐个执行,完全服从ACID的隔离级别    

索引                                                                                                                            
作用
1.提高查询速度
2.确保数据唯一性
3.加速表与表之间的链接,实现表之间参照完整性
4.使用分组和排序子句检索时,显著减少分组和排序的时间
5.全文检索字段进行搜索优化
分类
Primary Key  主键索引
Unique  唯一索引
Index    常规索引
FullText 全文索引

主键索引
特点
最常见的索引类型
确保数据记录的唯一性
确定特定数据记录在数据库中的位置

唯一索引
作用 避免同一个表中某数据列中的值重复
与主键索引的区别  主键索引只能有一个,唯一索引可能有多个
CREATE TABLE `Grade`(   
 `GradeID` INT(11) AUTO_INCREMENT PRIMARYKEY,   
 `GradeName` VARCHAR(32) NOT NULL UNIQUE   
 -- 或 UNIQUE KEY `GradeID` (`GradeID`) )
                                                                                                                                                            常规索引
作用 快速定位特定数据
注意 
index和key关键字都可以设置常规索引
应该加在查找条件的字段
不宜添加太多,影响数据增删改操作

ALTER TABLE '表名' ADD INDEX '索引名' ('列名1','列名2');

全文索引
作用 快速定位特定数据
注意
MySQL 5.6 以前的版本,只有 MyISAM 存储引擎支持全文索引; MySQL 5.6 及以后的版本,MyISAM 和 InnoDB 存储引擎均支持全文索引
只能用于CHAR,VARCHAR,TEXT类型的列
适合大型数据集
                                                                                                                                                            索引准则
1.不是越多越好
2.不要对经常变动的数据加索引
3.小数据表不要加索引
4.索引一般加在查找条件字段

                                                                                                                                  
三大范式
第一范式
确保每列的原子性,即每列都不可再分
第二范式
首先满足第一范式,其实要求每个表只描述一件事
第三范式
首先满足一二范式,除了主键以外其他列都不传递依赖于主键列
                                                                                                                                                            连接池
数据库连接池负责分配,管理和释放数据库链接,它允许应用程序重复使用一个现有的数据库链接,而不是重新建立一个。
数据库连接池在初始化时将创建一定数量的数据库连接放在连接池中,数量由最小数据库链接数来设定,无论是否被使用吗,都将一直保证至少有这么多连接数量。连接池的最大数据库连接数限定了这个连接池能占有的最大链接数,当应用程序向连接池请求的链接数超过最大数,会被加入到等待队列里。

最小连接数:时连接池一直保持的数据库链接,如果应用程序对数据库连接的使用量不大,将会有大量数据库连接资源被浪费
最大连接数:连接池能申请的最大链接数,如果数据库链接请求超过次数,后面的数据库链接请求将被加入到等待队列,影响后续操作
                                                                                                                                                            开源数据库连接池,通常把DataSource的实现,称之为数据源,数据源中都包含了数据库连接池的实现

DBCP,C3P0,在使用了数据库连接池后,项目实际开发就不需要编写连接数据库的代码,直接从数据源中获得数据库的链接

DBCP数据源,使用需要添加Commons-dbcp.jar 连接池的实现  Commons-pool.jar依赖                                                                                                                                                                              C3P0 使用较多,DBCP没有自动回收空闲连接的功能C3P0有
                                                                                                                                                             SQL优化
1.查询语句中减少select *
2.尽量减少子查询,改用连接查询
3.减少in 或 not in,使用 exist,not exist
4.尽量避免where子句中使用!=,会将引擎放弃使用索引而进行全表扫描
5.尽量避免where子句中对字段进行null判断
                                                                                                                                   
MySQL中的锁
表级锁:开销小,加锁快,不会死锁,发生锁冲突的概率最高,并发度最低
行级锁:开销大,加锁慢,会出现死锁,发生锁冲突的概率最低,并发度最高
页面锁:开销和加锁时间趋于表行之间,会出现死锁,并发度一般     
                                                                                                                                                            char和varchar
二者在存储和检索方面有所不同
char长度固定为创建表声明的长度,1-255,当char被储存时,会被用空格填充到特定长度检索char需删除尾随空格       
                                                                                         
MySQL优化顺序
1.sql语句及索引的优化
2.数据库表结构
3.系统配置
4.硬件                                                                                                                             
乐观锁
一个用户读取数据时,别人不会写自己所读的数据,悲观锁时觉得对方会读自己所读的数据,所以先对要读取的数据加锁
 
 


MyBatis


CRUD操作
namespace 
配置文件中的namespace中的名称为对应Mapper接口或Dao接口的完整报名,必须一致

Select
Select是mybatis最常用标签之一
    id  
1.    命名空间唯一的标识符
2.    接口中的方法名与映射文件中的SQL语句ID一一对应
parameterType
1.    传入SQL语句的参数类型
resultType


            ……….
     

使用注解开发
关于接口的理解
接口从更深层次的理解,应该是定义(规范,约束)与实现(名实分离)的分离
接口的本身反映了系统设计人员对系统的抽象理解
接口应有两类,第一类是对一个个体的抽象,可对应为一个抽象体
第二类是对一个个体某一方面的抽象,即形成一个抽象面
一个个体有可能有多个抽象面,抽象体与抽象面是有区别的

面向对象指考虑问题时,以对象为单位,考虑它的属性及方法
面向过程指考虑问题时,以一个具体的流程为单位考虑它的实现
接口设计与非接口设计是针对复用技术而言的,与面向过程(对象)不是一个问题,更多的体现是对系统整体的架构
                                           
具体注解不在这写了,很简单

关于@Param
该注解用于给方法参数起个名字,使用原则如下
1.    在方法只接受一个参数的情况下,可以不使用
2.    在方法接受多个参数的情况下,建议一定使用
3.    如果参数是JavaBean,不能使用
4.    只有在参数只有一个,且是JavaBean时,一定不能使用该注解

#与$的区别
#相当于给前端传入的数据加上‘’符号,可以有效防止SQL注入,#{}会被解析成一个参数占位符,$相当于直接把传入的数据接上去
例如前端传了一个1
where id= #{id} 等效于 where id =’1’
where id=${id} 等效于 where id=1

多对一和一对多的处理
多对一
多个学生对应一个老师,对于学生,这就是多对一

按查询嵌套处理
需求:获得所有学生及其对应老师的信息
1.    获取所有学生的信息
2.    根据学生信息中关联的老师的ID,获得该老师的信息
如何完成
1.    做一个结果集映射StudentTeacher
2.    映射的类型为Student
3.    学生中老师的属性是teacher,关联tid



        
    ${params.dataScope}

未过滤权限sql
select u.user_id, u.dept_id, u.login_name, u.user_name, u.email
    , u.phonenumber, u.password, u.sex, u.avatar, u.salt
    , u.status, u.del_flag, u.login_ip, u.login_date, u.create_by
    , u.create_time, u.remark, d.dept_name
from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
where u.del_flag = '0'

过滤权限sql
select u.user_id, u.dept_id, u.login_name, u.user_name, u.email
    , u.phonenumber, u.password, u.sex, u.avatar, u.salt
    , u.status, u.del_flag, u.login_ip, u.login_date, u.create_by
    , u.create_time, u.remark, d.dept_name
from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
where u.del_flag = '0'
    and u.dept_id in (
        select dept_id
        from sys_role_dept
        where role_id = 2
    )
后面那个嵌套查询就是数据权限过滤
实体只有继承BaseEntity才会处理,sql语句会存放到BaseEntity对象中的params属性中,然后通过xml中的${params.dataScope}获取拼接后的语句

多数据源
开发中会遇到一个应用可能要访问多个数据库的情况,项目中使用注解完成此功能
在需要被切换数据源的Service或mapper方法上添加@DataSource注解
@DataSource(value = DataSourceType.MASTER)
value表示数据源名称,除MASTER和SLAVE其他都需要配置
使用方式
1.在yml配置文件中配置slave从库数据源
2.在DataSourceType类添加数据源枚举
3.在DruidConfig配置读取数据源
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{
    DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
    return druidProperties.dataSource(dataSource);
}
4.在DruidConfig类dataSource方法添加数据源
setDataSource(targetDataSources, DataSourceType.SLAVE.name(),"slaveDataSource");
5.在需要多数据源方法或类上添加@DataSource注解,value表示数据源
@DataSource(value = DataSourceType.SLAVE)
public List selectUserList(SysUser user)
{
    return userMapper.selectUserList(user);
}

手动切换数据源
在需要切换数据源的方法中使用DynamicDataSourceContextHolder类实现手动切换
public List selectUserList(SysUser user)
{
    DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
    List userList = userMapper.selectUserList(user);
    DynamicDataSourceContextHolder.clearDataSourceType();
    return userList;
}
从库需要另外配置,如果不需要不用配置,也可以配置多个数据源

代码生成
在配置文件中可以配置代码生成默认配置
# 代码生成
gen: 
  # 开发者姓名,生成到类注释上
  author: ruoyi
  # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
  packageName: com.ruoyi.system
  # 自动去除表前缀,默认是false
  autoRemovePre: false
  # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
  tablePrefix: sys_
使用(直接在网页操作)
1.登录系统,系统工具,代码生成,导入表
2.代码生成列表中找到需要的表
3.点击生成会得到一个zip,执行sql文件,按照包内目录结构复制到项目中

定时任务
在指定的时间执行

系统接口(接口文档生成)
通过Swagger,在代码上添加注释,自动形成接口文档
作用范围                API            使用位置
协议集描述              @Api            用于controller类上
对象属性         @ApiModelProperty   用在出入参数对象的字段上
协议描述         @ApiOperation         用在controller的方法上
Response集   @ApiResponses         用在controller的方法上
Response         @ApiResponse            用在 @ApiResponses里边
非对象参数集  @ApiImplicitParams      用在controller的方法上
非对象参数描述@ApiImplicitParam      用在@ApiImplicitParams的方法里边
描述返回对象的意义    @ApiModel    用在返回对象类上

防重复提交
在接口方法上添加@RepeatSubmit注解
参数       类型    默认值    描述
interval    int      5000      间隔时间(ms),小于此时间视为重复提交
message    String    不允许重复提交,请稍后再试    提示消息

interval可以自定义值
@RepeatSubmit(interval = 1000, message = "请求过于频繁")
public AjaxResult addSave(...)
{
    return success(...);
}

国际化支持(i18n)
1.修改I18nConfig设置默认语言,如默认中文
slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
2.修改yml文件中basename国际化文件
spring:
  # 资源信息
  messages:
    # 国际化资源文件路径
    basename: static/i18n/messages
3.i18n目录文件下定义资源文件,每个语言分为一个个properties文件
例如
美式英语 messages_en_US.properties
user.login.username=User name
user.login.password=Password
user.login.code=Security code
user.login.remember=Remember me
user.login.submit=Sign In

中文简体 messages_zh_CN.properties
user.login.username=用户名
user.login.password=密码
user.login.code=验证码
user.login.remember=记住我
user.login.submit=登录

代码中使用MessageUtils获取国际化
MessageUtils.message("user.login.username")
MessageUtils.message("user.login.password")
MessageUtils.message("user.login.code")
MessageUtils.message("user.login.remember")
MessageUtils.message("user.login.submit")

新建子模块
1.新建一个子模块
2.在子模块下新建Pom文件及jave,resources目录
3.根目录添加子模块依赖

    com.ruoyi
    ruoyi-test
    ${ruoyi.version}

4.根目录添加子模块业务模块
ruoyi-test
5.ruoyi-admin中pom文件添加子模块依赖


    com.ruoyi
    ruoyi-test

6.测试子模块


SpringSecurity
SpringSecurity主要从两个方面解决安全性问题
1.web请求级别:使用Servlet规范中的过滤器(Filter)保护Web请求并限制URL级别的访问
2.方法调用级别:使用SpringAOP保护方法的调用,确保具有适当权限的用户才能访问安全保护的方法。

注解解析
@EnableWebSecurity:启动Web安全功能,本身并没有什么用,SpringSecurity的配置类需要实现WebSecurityConfigurer或继承WebSecurityConfigurerAdapter类
@EnableWebMvcSecurity:spring4.0已弃用

WebSecurityConfigurerAdapter类:可以通过重载该类的三个configure方法制定Web安全的细解
1.configure(WebSecurity):通过重载该方法,配置SpringSecurity的Filter链
2.configure(HttpSecurity):通过重载该方法,可配置如何通过拦截器保护请求
3.configure(AuthenticationManagerBuilder):通过重载该方法,可配置user-detail服务。

保护路径的配置方法
access(String)  给定的SpEL(Spring Expression Language)表达式计算为true,允许访问
anonymous()  允许匿名用户访问
authenticated()  允许认证过的用户访问
denyAll()    无条件拒绝所有访问
fullyAuthenticated()  如果用户完整认证(不是通过Remember me认证的)就允许访问
hasAnyAuthority(String..) 如果用户具备给定权限中的一个,就允许访问
hasAnyRole(String...)    如果用户具备给定角色中的某一个,就允许访问
hasAuthority(String)    如果用户具备给定权限,就允许访问
haslpAddress(String) 如果请求来自给定ip,就允许访问
hasRole(String)  如果用户具备给定角色,就允许访问
not() 对其他访问方法的结果求反
permilAll() 无条件允许访问
rememberMe() 如果用户是通过Remember-me功能认证的,就允许访问你

支持的SpEL表达式
authentication  用户认证对象
denyAll  结果始终为false
hasAnyRole(list of roles)  如果用户被授予指定任意权限,为true
hasRole(role)  如果用户被授予指定权限,为true
hasIpAddress(IP Adress) 用户地址
isAnonymous() 是否为匿名用户
isAuthenticated() 不是匿名用户
isFullyAuthenticated 不是匿名也不是remember me认证
isRememberMe() remember me认证
permitAll 始终为true
principal 用户主要信息对象

用户信息存储的三种方法
1.使用基于内存的用户存储:通过inMemoryAuthentication()方法,可以启用配置并任意填充基于内存的用户存储,并且可以调用withUser()方法为内存用户存储添加新的用户,这个方法的参数是username.withUser()方法返回的是UserDetailsManagerConfigurer.UserDetailsBuilder,这个对象提供了多个进一步配置用户的方法,包括设置用户密码的password()方法及为指定用户授予一个或多个角色权限的roles()方法。roles()方法是authorities()方法的简写形式。roles()方法所给定的值都会添加一个ROLE_前缀,并将其作为权限授予给用户。因此上述代码用户具有的权限为:ROLE_USER,ROLE_ADMIN.而借助passwordEncoder()方法来指定一个密码转码器(encoder),可以对用户密码进行加密存储
2.基于数据库表的认证:用户数据通常会存储在关系型数据库,并通过JDBC进行访问。为了配置SpringSecurity使用以JDBC为支撑的用户存储,可以使用jdbcAuthentication()方法,并配置他的DataSource,这样就能访问关系型数据库了。
3.基于LDAP进行认证:为了让SpringSecurity使用基于LDAP的认证,可以使用ldapAuthentication()方法。

SpringSecurity方法调用级别的安全性Demo
SpringSecurity提供了三种不同的安全注解:
1.SpringSecurity自带的@Secured注解
2.JSR-250的@RolesAllowed注解
3.表达式驱动的注解
@PreAuthorize 方法调用前,基于表达式的计算结果来限制对方法的访问
@PostAuthorize 允许方法调用,但如果表达式计算结果为false,抛出一个安全性异常
@PreFilter 允许方法调用,但必须按照表达式来过滤方法的结果
@PostFilter 允许方法调用,但必须在进入方法之前过滤输入值

基于注解的方法安全性、
在spring中如果要启用基于注解的方法安全性,关键在于要在配置类(@Configuration)上使用@EnableGlobalMethodSecurity注解
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
securedEnabled=ture 表示spring会创建一个切点,并将带有@Secured注解的方法放入切面中。prePostEnabled=true表示启用表达式驱动的注解,还有jsr250Enable=true表示启用@RoledAllowed

jwt(token)


JSON WEB TOKENS
流行,安全,稳定,易用,支持JSON

跨域身份验证
1.用户向服务器发送用户名和密码
2.验证服务器后,相关数据保存在当前会话
3.服务器向用户返回session_id,session信息写入用户Cookie
4.用户每个后续请求都通过Cookie中取出session_id传给服务器
5.服务器收到session_id并对比之前保存的数据,确认用户身份

问题:没有分布式,不支持横向扩展。如果单个服务器,该模式很好用。但如果是服务器集群或面向服务的跨域体系结构,需要一个统一的session数据库来保存会话数据实现共享,这样负载均衡下的每个服务器才可以正确验证用户身份。
例如,站点A和B提供同一公司的相关服务,现在要求登录A就会自动登录B,如何实现?
一种方法是把session数据持久化,写入数据库或文件持久层,收到请求后直接从持久层请求数据,架构清晰,但架构修改困难。由于依赖持久层的数据库,如果持久层挂了,整个认证系统都会挂掉。

jwt就是用来解决这种问题。客户端保存数据,服务器不保存会话数据,每个请求都被发送回服务器
jwt是在服务器身份验证之后将生成一个json对象发送回用户

{
    “UserName”:"ayane"
      "Role":"Admin"
      "Expire":"2022-4-1 11:11:11"
}
之后当用户与服务器通信时,客户在请求中返回json对象,服务器仅依赖于这个json对象标识用户。为了防止用户篡改数据,服务器会在生成对象时添加签名,服务器不会保存任何会话数据,变为无状态,使其容易扩展

jwt数据结构
该对象是一个很长的字符串,字符之间用“.”分成三个子字符串,本质就是一个长串。
这个长串由jwt头,有效荷载,签名组成。

jwt头
这是一个描述JWT元数据的json对象,通常如下
{
    “alg”: "HS256"
      "typ" :“JWT”
}
alg属性表示签名使用的算法,默认是HS256,typ属性表示令牌的类型,JWT令牌统一写为JWT
最后,使用Base64 URL算法将上述json对象转换为字符串保存

有效荷载
是JWT的主体内容部分,是一个JSON对象,包含需要传递的数据,JWT默认指定七个默认字段供选择
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
除了这几个,还可以自定义私有字段
{
"sub": "1234567890",
"name": "zuocanglingyin",
"admin": true
}
注意:默认情况下JWT是未加密的,任何人都可以解读其内容,所以不要构建隐私信息字段,存放保密信息,防止信息泄露
它也会用Base64URL算法转换成字符串

签名哈希
这是对上面两部分数据的签名,通过指定算法生成哈希确保数据不会被篡改
首先指定一个密码secret,仅仅保存在服务器,不向用户公开,然后使用标头中指定的签名算法,根据以下公式生成签名
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)
计算出签名后,这三个部分组合成一个字符串,用.分隔,构成整个jwt对象

Base64URL算法
如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。
作为令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三个字符是"+","/"和"=",由于在URL中有特殊含义,因此Base64URL中对他们做了替换:"="去掉,"+"用"-"替换,"/"用"_"替换,这就是Base64URL算法。

JWT用法
客户端接受服务器返回的JWT,将其存储在cookie或localstorage中。客户端将在与服务器交互中都会带JWT。如果存在cookie,就可以自动发送,但不跨域。所以一般放入http请求的Header Authorization字段中。
Authorization:Bearer
跨域时,也可以将JWT被放置于POST请求的数据主体中。

JWT问题和趋势
1.JWT默认不加密,但可以加密。生成原始令牌后可以改令牌对其再次加密
2.JWT未加密方法时,一些私密数据无法通过JWT传输
3.JWT不仅可用于认证,也可以用于信息交换。善用JWT有助于减少服务器请求数据库次数
4.JWT最大缺点是服务器不保存会话状态,所以使用期间不可能取消令牌或更改令牌的权限,JWT一旦签发,在有效期内一直有效
5.JWT本身包含认证信息,一旦信息泄露,任何人都能获得令牌的所有权限,为了减少盗用,JWT有效期不宜太长,对于重要操作,用户使用时应该每次都进行身份验证
6.为了减少盗用和窃取,JWT不建议用HTTP协议传输代码,使用加密的HTTPS协议传输
RabbitMQ
前景
一个商品的价格修改了,他是在数据库中修改的,商品详情都有页面静态化处理,不会跟着数据库商品更新而变化,于是在数据库改了之后,前台页面上的价格还是旧的。
解决方法
1.每次修改数据库中商品信息时,同时修改索引库数据并更新静态页面
2.搜索服务和商品页面静态化服务对外提供操作接口,后台改完后重新调用接口
这两个方案都有严重的代码耦合,违背了微服务的独立原则。有一种解决方法:消息队列。

商品服务对商品增删改查后,不去操作索引库和静态页面,只向MQ发送一条消息(如包含商品id的消息),不去关心消息接受对象。搜索服务和静态页面服务监听MQ接收到消息,分别取处理索引库和静态页面(根据商品id操作)

什么是消息队列
MQ:Message Queue。它是消息在传输过程中保存消息的容器。典型的生产者,消费者模型。生产者不断向消息队列中生产消息,消费者不断从队列中获取消息。消息的产生和消费是异步的,并且只关心消息的发送和接受,没有业务逻辑侵入,实现了生产者和消费者的解耦。

应用场景
1.任务异步处理
高并发环境,来不及同步处理,请求往往会发生堵塞。比如大量的insert和update之类的请求同时到达MySQL,直接导致无数的行锁表锁,甚至最后请求会堆积过多,从而触发too many connections错误。使用消息队列,可以异步处理请求,缓解系统压力,不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理,减少了应用程序的响应时间。
2.MQ相当于一个中介,生产方通过MQ与消费方交互,将应用程序进行解耦。

AMQP和JMS
MQ是消息通信的模型,并发具体实现。实现MQ的两种主流方式:AMQP,JMS区别和联系
1.JMS定义了统一的接口来对消息操作进行统一,AMQP是通过规定协议来统一数据交互的格式。
2.JMS限定使用JAVA,AMQP只是协议,不规定实现方式,因此是跨语言的。
3.JMS规定了两种消息模型,AMQP消息模型更丰富。
常见MQ
ActiveMQ:基于JMS
RabbitMQ:基于AMQP协议,erlang语言开发,稳定性好
RocketMQ:基于JMS,阿里产品,交由Apache基金会
Kafka:分布式消息系统,高吞吐量

安装erlang 和rabbitMQ

RabbitMQ组成部分
Broker:消息队列服务进程,此进程包括Exchange和Queue
Exchange:消息队列交换机,按一定规则将消息路由转发到某个队列,对消息进行过滤
Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的
Producer:消息生产者,即生产方客户端,生产方客户端将消息发送
Consumer:消息消费者,即消费方客户端,接受MQ转发的消息

生产者发送消息流程:
1.生产者和Broker建立TCP连接
2.生产者和Broker建立通道
3.生产者通过通道消息发送给Broker,由Exchange将消息进行转发
4.Exchange将消息转发到指定的Queue

消费者接受消息流程:
1.消费者和Broker建立TCP连接
2.消费者和Broker建立通道
3.消费者监听指定的Queue
4.当有消息到达Queue时Broker默认将消息推送给消费者
5.消费者接收到消息
6.ack回复


六种消息模型
1.基本消息模型
p——>队列——>c
P:生产者,要发送消息的程序
C:消费者,消息的接收者,会一直等待消息到来
队列:消息队列,可以缓存消息,生产者向其中投递消息,消费者从其中取出消息。
消费者获取了消息,但程序没有停止,一直在监听队列中是否有新的消息,一旦有新消息进入队列,就会立即打印

消息确认机制(ACK)
消息一旦被消费者接受,队列中的消息就会被删除。
那么RabbitMQ怎么知道消息被接受了?如果消费者领取消息后还未操作就挂掉了,或抛出了异常,消息消费失败,但RabbitMQ无从得知,这样消息就丢失了。
因此,RabbitMQ有一个ACK机制,当消费者获取消息后,会向RabbitMQ发送回执ACK,告诉消息已被接受,回执ACK分两种情况:
1.自动ACK:消息一旦被接受,消费者自动发送ACK
2.手动ACK:消息接收后,不发送ACK,需要手动调用
这两种哪个好需要看消息的重要性,如果消息不太重要,丢失也没有影响,那么自动ACK很方便,如果消息非常重要,不能丢失,那最好在消费完成后手动ACK,否则接受消息就自动ACK,RabbitMQ就会把消息从队列中删除,如果此时消费者宕机,那么消息就丢失了
channel.basicConsume(QUEUE_NAME, true, consumer); 自动是true,手动要设为falser

2.work消息模型
工作队列或竞争消费者模型
p——>队列——>两个不同的消费者C1,C2
与前一个相比,多了一个消费端,两个消费者共同消费同一个队列中的消息,但消息只能被一个消费者获取。
这个模型在WEB应用中很有用,可以处理短的HTTP请求窗口中无法处理复杂的任务。
p:生产者,任务的发布者
C1:消费者1:领取并且完成任务,假设完成速度较慢
C2:消费者2:领取并且完成任务,假设完成速度较快
模拟发现,两个消费者各自消费了不同25条消息,实现了任务的分发

能者多劳
消费者1比2效率低,一次任务耗时长,然而两人最终消费的消息数量是一样的,消费者2大量时间空闲,消费者1一直在干活,现在任务平均分配,正确的做法是消费越快的,消费越多
如何实现
通过BasicQos方法设置prefetchCount=1.这样RabbitMQ就会使得每个Consumer在同一个时间点最多处理1个Message,在接收到该Consumer的ack前,它不会将新的Message分发给它,相反,它会将其分派给不是仍然忙碌的下一个Consumer。
注意,prefetchCount在手动ACK的情况下才生效,自动ACK不生效。
模拟后,消费者2明显消费了更多条消息

订阅模型分类
1.一个生产者多个消费者
2.每个消费者都有一个自己的队列
3.生产者没有将消息直接发给队列,而是发给exchange(交换机,转发器)
4.每个队列都需要绑定到交换机上
5.生产者发送的消息,经过交换机到达队列,实现一个消息被多个消费者消费
例如:注册帐号,同时发邮件和短信

Exchange交换机一方面接受生产者发送的消息,另一方面知道如何处理消息,例如递交给某个特别队列,递交给所有队列,或是将消息丢弃,如何操作取决于Exchange的类型。

Exchange分为以下几种
Fanout:广播,将消息交给所有绑定到交换机的队列
Direct:定向,把消息交给符合指定routing key的队列
Topic:通配符,把消息交给符合routing pattern的队列
Header:取消routingkey,使用header中的kv键值对匹配队列
交换机只负责转发消息,不具备存储消息的能力,如果没有任何队列与Exchange绑定,或没有符合路由规则的队列,消息会丢失。

3.publish/subscribe(交换机类型:Fanout广播)
P——>X——>多个队列——>绑定各自队列的消费者
不同之处:生产者声明交换机,不再声明队列,消费者声明队列,绑定交换机。发送消息给交换机,不在发送给队列
思考:这个跟工作队列有什么区别
1.工作队列不用定义交换机,这个需要定义
2.这个的生产者是向交换机发送消息,工作队列是向队列发送消息
3.这个需要设置队列与交换机的绑定(消费者类设定)
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
相同点:二者实现的发布/订阅效果是一样的,多个消费者监听同一个队列不会重复消费
实际工作建议用p/s模式,p/s模式比工作队列模式更强大,并且可以指定专用的交换机

4.Routing 路由模型(交换机:direct)
p——>X——>routingkey(error,info,warning)——>多个队列——>各自的消费者
P:生产者,向交换机发送消息,发送消息时会指定一个routing key
X:交换机,接受生产者发送的消息,然后把消息递交给与routing key完全匹配的队列
消费者:其所在队列指定了需要的routing key类型的消息
生产者指定一个routing key
channel.basicPublish(EXCHANGE_NAME, "sms", null, message.getBytes());
只有与之匹配的消费者才能接受到生产者发来的消息
 channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "sms");
5.Topics通配符模式(交换机类型:topics)
每个消费者监听自己的队列,并设置带通配符(*,#)的routingkey,生产者将消息发给broker,由交换机根据routingkey转发消息到指定队列
这里的routingkey一般都是由一个或多个单词组成,多个单词中间用.分割,如orange.animal,橙色的动物
通配符规则:
#:匹配一个或多个词
*:只匹配1个词
audit.#:能够匹配audit.irs.corporate 或者 audit.irs
audit.*:只能匹配audit.irs


6.RPC
Callback queue回调队列。客户端向服务器发送请求,服务器端处理请求后,将处理结果保存在一个存储体,客户端为了获取处理结果,那么客户在向服务器发送请求时,同时发送一个回调队列地址reply_to。

Correlation id 关联标识,客户端可能会发送多个请求到服务器。服务器处理完后,客户端无法辨别在回调队列中的响应具体和那个请求向对应。为了处理这种情况,客户端在发送每个请求时,同时会附带一个独有的correlation_id,这样客户端在回调队列中根据correlation_id字段的值就可以分辨此响应属于哪个请求。

流程
1.客户端启动时,会创建一个匿名独享的回调队列
2.在RPC请求中,客户端发送带有两个属性的消息:一个是设置回调队列的reply_to属性,一个是设置唯一值的correlation_id属性
3.将请求发送到一个rpc_queue队列
4.服务器等待请求发送道这个队列中,当请求出现时,它执行他的工作并将带有执行结果的消息发送给reply_to指定的队列
5.客户端等待回调队列里的数据。当有消息出现时,会检查correlation_id属性,如果匹配,返回给应用

避免消息堆积
1.采用workqueue,多个消费者监听同一队列
2.接受消息后,通过线程池异步消费

避免消息丢失
消费者ACK机制,防止消费者丢失消息

如果消费之前MQ宕机了,消息没了怎么办?
将消息持久化,前提是队列交换机都持久化

Docker


关键词:软件带环境安装
Docker镜像(images)将应用程序所需要的系统环境由下而上打包,达到应用程序跨平台间的无缝接轨运作
Docker基于Go语言开发的云开源项目,通过对应用组件的封装,分发,部署,运行等生命周期的管理,使用户的APP及其运行环境做到一次封装,到处运行。

虚拟机也是带环境安装的一种解决方案,它可以在一种操作系统中运行另一种操作系统,但它资源占用多,冗余步骤多,启动慢,所以linux发展了另一种虚拟化技术,linux容器。
linux容器不是模拟一个完整的操作系统,而是对进程进行隔离,有了容器,可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置,系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一的运行。

Docker和传统虚拟化方式的不同之处
1.传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整的操作系统,在该系统上再运行所需应用进程
2.容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,也没有进行硬件虚拟,因此容器要比传统虚拟机更为轻便
3.每个容器之间相互隔离,每个容器有自己的文件系统,容器之间进程不会相互影响,能区分计算资源。

更快速的应用交付和部署
传统的应用开发完后,需要提供一堆安装程序和配置说明文档,安装部署后根据配置文档进行繁杂的配置才能正常运行。Docker化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像并运行即可,应用安装配置在镜像内已经安装好,大大节省部署配置和测试验证时间

更便捷的升级和扩缩容
随着微服务架构和Docker的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积木一样,每个Docker容器将变成一块积木,应用的升级将变得非常容易。当现有的容器不足以支撑业务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至妙级。

更简单的系统运维
应用容器化运行后,生产环境运行的应用可与开发,测试环境的应用高度一致,容器会将应用程序相关的环境与状态完全封装,不会因为底层基础架构和操作系统不一致给应用带来影响,产生新的BUG,当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复

更高效的计算资源利用
Docker是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的Hypervisor(管理程序)支持,所以在一台物理机上可以运行很多个容器实例,可大大提升物理服务器的CPU和内存的利用率。

Docker的基本组成
镜像(image)
Docker镜像就是一个只读的模板。镜像可以用来创建Docker容器,一个镜像可以创建很多容器。就好似Java中的类和对象,类是镜像,容器是对象。

容器(container)
Docker利用容器独立运行一个或一组应用。容器是用镜像创建的运行实例、
容器可以被启动,开始,停止,删除,每个容器都是互相隔离的,保证安全的平台,可以把容器看作是一个简易版的Linux环境和运行在其中的应用程序。容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的

仓库(repository)
仓库是集中存放镜像文件的场所。
仓库和仓库注册服务器是有去别的。仓库注册服务器上往往存放着多个仓库,每个仓库又包含多个镜像,每个镜像有不同的标签tag,仓库分为空开仓库和私有仓库两种形式,最大的公开仓库是DockerHUb,存放了数量庞大的镜像供用户下载。阿里云,网易云

小结
1.Docker本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就似乎image镜像文件。只有通过这个镜像文件才能生成Docker容器。image文件可以看作是容器的模板。Docker根据image文件生成容器的实例,同一个image文件可以生成多个同时运行的容器实例。
2.image文件生成的容器实例本身也是一个文件,称为镜像文件
3.一个容器运行一种服务,当我们需要的时候,就可以通过Docker客户端创建一个对应的运行实例,也就是容器
4.仓库就是放了一堆镜像的地方,可以把镜像发布到仓库,需要的时候从仓库拉下来就可以

Docker是怎么工作的
Docker是一个Client-Server(C/S)结构的系统,Docker守护进程运行在主机上,然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。容器是一个运行时环境,就是集装箱

为什么Docker比VM快
1.docker抽象层比虚拟机更少。由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源,因此在CPU,内存利用率上会更战优势
2.docker利用的是宿主机的内核,而不需要Guest OS。因此,当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核,从而避免引寻,加载操作系统内核这些比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载Guest OS,新建过程都是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了这个过程,因此新建一个docker容器只需要几秒钟

Docker常用命令
帮助命令
docker version  版本信息
docker info    系统信息,包括镜像和容器数量
docker --help  帮助

镜像命令
docker images
列出本地主机上的镜像。 
REPOSITORY  镜像的仓库源
TAG    镜像的标签
IMAGE ID  镜像的ID
CREATED   镜像的创建时间
SIZE    镜像的大小
同一个仓库源可以有多个TAG,代表这个仓库源的不同版本,我们使用REPOSITORY:TAG定义不同的镜像,如果不定义镜像的标签版本,docker将默认使用lastest镜像
-a:列出本地所有镜像
-q:只显示镜像id
--digests:显示镜像的摘要信息

docker search
搜索镜像 
例:docker serch mysql
可选项
--filter=stars=50:列出收藏数不小于指定值的镜像

docker pull
下载镜像
例:docker pull mysql
可以指定版本现在  docker pull mysql:5.7

docker rmi
删除镜像
docker rmi -f 镜像id   删除单个
docker rmi -f 镜像名:tag 镜像名:tag   删除多个
docker rmi -f $(docer images -qa)  删除全部

容器命令
有镜像才能创建容器

Thymeleaf


thymeleaf是什么
这是一个模板引擎,因为支持html原型,在有网和无网环境都可运行,在html标签增加额外属性来达到模板+数据的展示方法。通常结合springboot使用

在springboot中的配置
1.添加pom文件依赖
2.创建模板文件夹。springboot自动为thymeleaf注册了一个视图解析器,如果没有在Yml文件中配置,它去找的视图文件默认在resources配置文件夹下的templates文件夹,后缀html。

Controller层返回页面展示
return "admin/index" 返回到admin文件夹下的index.html
return "index"  返回到templates下的index.html
return "redirect:/admin" 重定向到某个请求,即执行一次/admin请求
return "index::blogList" 返回到index.html,但只更新blogList片段,通常就是ajax请求

在html中的基本使用
1.引入名称空间
2.表达式的使用
在html中只需在源标签名前加 th:即可,如th:src,th:href,th:text等

${ },变量表达式,它可以获取到controller层存入的Model的值
controller层:
model.addAttribute("name","ayane");
User user = new User("ayane",29);
model.addAttribute("user",user);
html页面:
获得存入name的值  ayane
获得特定属性age 29
当有数据传递过来,动态数据会替换成静态数据

*{ },选择变量表达式,可以省略对象名直接获取属性值

    
       


       


#{ },Message表达式,主要从国际化配置文件中取值


3.URL的使用
绝对网址,绝对URL用于创建到其他服务器的链接,需要指定协议名称http或https
百度

上下文相关URL,即与项目相关联的的URL,假设war包为app.war,且没有在tomcat的server.xml配置项目的路径(Context),则在tomcat启动后,就会在webapps文件下产生一个app文件夹,此时app就是上下文路径(context)
页面这样写
跳转
thymeleaf解析后是这个格式
跳转

服务器相关URL。与上下文路径相似,主要用于一个tomcat下运行有多个项目的情况。比如当前项目为app,如果tomcat还运行着一个otherApp,我们可以通过该方法访问otherApp的请求路径
页面这样想
跳转
thymeleaf解析后
跳转
页面带参数传递到后端
页面
跳转
解析后
跳转
restful风格
跳转
解析后
跳转

4.字面值
有时候需要在指令中填入基本类型如字符串,数值,布尔值等,不希望thymeleaf解析
字符串字面值
页面
templates
解析后
Thymeleaf3

数字字面值
页面
templates
解析后
4

布尔字面值
true 和 false

5.拼接
传统拼接需要用''来进行普通字符串和表达式的拼接,thymeleaf中进行了简化,只需将拼接的内容块用||包裹

6.运算符
因为html会将<>符号进行解析,所以不能直接使用,但如果在{ }内使用是不需要转换的
>      gt    greater than,大于
<      lt      less than,小于
>=    ge    greater equal,大于等于
<=    le     less equal,小于等于

三元运算符
性别

空值判断
如果user.name为空,则显示空值显示这四个字

7.内联写法
[()],解析输出,会解析内容里的html标签 
[(${user.name})]
[[]],原样输出,不会解析内容里的html标签
[[${user.name}]]

8.局部变量
可以将后端传来的数据赋值给一个局部变量,这个局部变量只能在标签内部用


       昵称
 

9.判断
th:if,满足条件显示内容
显示
年龄大于18岁显示

th:unleff,与if相反,不满足时显示
不显示

th:switch,与switch效果一致


    1
    2
    3

10.迭代
th:each,迭代一个集合/数组,可以用它的内置对象stat获取更多信息

    
    

这个userStat是自定义的一个迭代器,用于获取迭代状态,包含以下属性
1)index 属性:当前迭代索引,从0开始
2)count 属性:当前的迭代计数,从1开始
3)size 属性:迭代变量中元素的总量
4)current 属性:每次迭代的 iter 变量,即当前遍历到的元素
5)even/odd 布尔属性:当前的迭代是偶数还是奇数。
6)first 布尔属性:当前的迭代是否是第⼀个迭代
7)last 布尔属性:当前的迭代是否是最后⼀个迭代。

11、环境相关对象
1、${#ctx}:上下文对象,可用于获取其他内置对象
2、${#vars}:上下文变量。
3、${#locale}:上下文区域设置。
4、${#request}:HttpServletRequest对象。
5、${#response}:HttpServletResponse对象。
6、${#session}:HttpSession对象。
7、${#servletContext}:ServletContext对象。

12、全局对象功能
1、#strings:字符串工具类
2、#lists:List 工具类
3、#arrays:数组工具类
4、#sets:Set 工具类
5、#maps:常用Map方法。
6、#objects:一般对象类,通常用来判断非空
7、#bools:常用的布尔方法。
8、#execInfo:获取页面模板的处理信息。
9、#messages:在变量表达式中获取外部消息的方法,与使用#{…}语法获取的方法相同。
10、#uris:转义部分URL / URI的方法。
11、#conversions:用于执行已配置的转换服务的方法。
12、#dates:时间操作和时间格式化等。
13、#calendars:用于更复杂时间的格式化。
14、#numbers:格式化数字对象的方法。
15、#aggregates:在数组或集合上创建聚合的方法。
16、#ids:处理可能重复的id属性的方法。

13、class属性增加
使用th:classappend,可以增加class元素类名。通常用在如导航栏上,当导航栏上某个标签被选中,它会与其他没有被选中的有不同的样式。
首页

首页


在html页面中的布局使用
定义片段和引入片段功能用的很多,在一个项目的多个页面,大部分导航栏和底部是一样的,可以创建一个页面专门来定义重复使用的片段,在其他页面做引入。
1.定义片段
使用th:fragment定义通用片段-普通片段

使用th:fragement定义通用片段-带参片段,根据“n”来判断样式

2.引入片段
将公共的标签插入到指定标签中-~{ }不可省略


         //被引入的片段会插入到div标签内部

将公共的标签替换指定的标签-可以省略~{ }
推荐该引用方式,可以保证在没有网络(动态数据)的情况下其他页面也有内容可以显示

    
          //被引入的片段会替换div标签的内容(含div)
         链接

将公共的标签包含到指定的标签-可以省略~{}


          //被引入的片段会被包含到div标签内部
          //但是最外层的标签不会被包含进来,即只包含th:fragment所在标签内部的内容

上诉三种方法,可以用来引入使用id定义片段的 - 不推荐

引入带参片段

3.th:fragment更新数据
有时在页面中需要使用Ajax异步请求数据,我们希望只更新某一块的数据,其他地方不变。
html页面:


    九月
    18


后台:
public String userInfo(Model model){
    model.addAttribute(user,new User("柳成荫",22));
    return "index :: userInfo";
}

4.块级标签th:block
thymeleaf处理这个标签时会删掉标签本身,不会显示标签但会保留其内容
常见用法-html部分


    

我和div2一起

    
我和div1一起




    
        
...
        ...
    

常见用法-JS部分
有时候js的引用也可能是其他页面共有的,想定义一个Js片段在其他页面引入,可以使用

把js片段包裹起来,然后将这个
定义成一个片段,但这个方法不好,所以用th:block。


   
   

引用

    
    

这个标签不会影响静态页面。


MySQL存储过程


SQL语句需要先编译再执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果有)来调用执行它。
存储过程是可编程的函数,在数据库中创建并保存,可以由SQL语句和控制结构组成。当想要在不同的应用程序或平台上执行相同的函数或封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看作是对编程中面向对象方法的模拟,它允许控制数据的访问方式。
存储过程的优点:
1.增强SQL语言的功能和灵活性:存储过程可以用控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。
2.标准组件式编程:存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。数据库专业人员可以随时对存储过程进行修改,对应用程序源代码无影响。
3.较快的执行速度:如果某一操作包含大量的Transaction-SQL代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。因为存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划。而批处理的Transaction-SQL语句在每次运行时都要进行编译和优化,速度相对慢一些。
4.减少网络流量:针对同意数据库对象的操作(如查询,修改),如果这一操作所涉及的Transaction-SQL语句被组织进存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,减少网络流量并降低网络负载。
5.作为一种安全机制来充分利用:通过对执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全。

MySQL5.0后开始支持存储过程,提高了数据库的处理速度,编程的灵活性。

MySQL存储过程的创建
语法
CREATE PROCEDURE 过程名  ([IN  |  OUT  |  INOUT ])参数名 数据类型[IN | OUT | INOUT ] 参数名 数据类型...]]) [特性.....]  过程体
例子:
DELIMITER    //
     CREATE PROCEDURE myproc (OUT  s   INT)
            BEGIN
        SELECT COUNT(*) INTO  s   FROM  students;
            END
    //
DELIMITER;
分隔符
MySQL默认以";"为分隔符,如果没有声明分隔符,编译器会把存储过程当成SQL语句处理,因此编译过程会报错。所以要实现用"DELIMITER //"声明当前段分隔符,让编译器把两个
“//”之间的内容当作存储过程的代码,不会执行这些代码;
最后一句"DELIMITER;"的意思是把分隔符还原

参数
存储过程根据需要可能会有输入,输出,输入输出参数,如有多个参数用,分隔开.MySQL存储过程的参数必须用在存储过程的定义,共有三种参数类型:IN OUT INOUT

过程体
过程体的开始与结束使用BEGIN与END进行标识。

IN参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值。
DELIMITER //
      CREATE PROCEDURE in_param(IN p_in  int)
    BEGIN
    SELECT p_in;
    SET p_in=2;
    SELECT p_in;
    END;
    //
DELIMITER ;
#调用
SET @p_in = 1;
CALL in_param(@p_int);
SELECT @p_in;
执行结果:
1
2
1 存储过程中的set p_in =2 没有返回。

OUT该值可在存储过程内部被改变,并可返回
DELIMITER //
      CREATE PROCEDURE   out_param(OUT p_out int)
    BEGIN
    SELECT p_out;
    SET p_out=2;
    SELECT p_out;
    END;
    //
DELIMITER ;
#调用
SET @p_out = 1;
CALL out_param(@p_out);

SELECT @p_out;
执行结果:
null?好像是说这个参数传入存储过程后被清空了
2
2

INOUT调用时指定,并且可被改变和返回
DELIMITER //
  CREATE PROCEDURE inout_param(INOUT p_inout int)
    BEGIN
      SELECT p_inout;
      SET p_inout=2;
      SELECT p_inout;
    END;
    //
DELIMITER ;
#调用
SET @p_inout=1;
CALL inout_param(@p_inout) ;
SELECT @p_inout;
执行结果
1
2
2

变量
语法:DECLARE 变量名1 [,变量名2....] 数据类型 [默认值];
数据类型为MySQL的数据类型,这不写了!

变量赋值
语法:SET 变量名 = 变量值 

用户变量
用户变量一般以@开头
注意:滥用用户变量会导致程序难以理解及管理

#在MySQL客户端使用用户变量
SELECT 'HELLO WORLD' into @x;
SELECT @x;

SET @y='GoodBye';
SELECT @y;
SET @z=1+2+3;
SELECT @z;
结果:
HELLO WORLD
GoodBye
6

#在存储过程中使用用户变量
CREATE PROCEDURE GreetWorld() SELECT CONCAT (@greeting,'world');
SET @greeting = 'Hello';
CALL GreetWorld();
执行结果
Helloworld

#在存储过程间传递全局范围的用户变量
CREATE PROCEDURE p1() SET @last_proc='p1';
CREATE PROCEDURE p2() SELECT CONCAT('Last procedure was ',@last_proc);
CALL p1();
CALL p2();
执行结果
Last procedure was p1

注释
存储过程注释两种风格
1.双杠:--一般用于单行注释
2.C风格:一般用于多行注释

存储过程的调用
用call和过程名以及一个括号,括号内根据需要加入参数,如上述例子所示

存储过程的查询
#查询存储过程
SELECT name FROM mysql.proc WHERE db='数据库名';
SELECT routine_name FROM information_schema.routines WHERE routine_schema='数据库名';
SHOW PROCEDURE STATUS WHERE db='数据库名';

#查看存储过程详细信息
SHOW CREATE PROCEDURE 数据库.存储过程名;


存储过程的修改
ALTER PROCEDURE 更改用CREATE PROCEDURE建立的预先指定的存储过程,其不会影响存储过程或存储功能。
ALTER {PROCEDURE | FUNCTION } sp_name [characteristic ....]
characteristic:
    {CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA | 
SQL SECURITY {DEFINER | INVOKER} | COMMENT 'string'}
sp_name参数表示存储过程或函数的名字;
characteristic参数指定存储函数的特性
CONTAINS SQL表示子程序包含SQL语句,单不包含读或写数据的语句;
NO SQL表示子程序中不包含SQL语句
READS SQL DATA 表示子程序中包含读数据的语句
MODIFIES SQL DATA 表示子程序中包含写数据的语句
SQL SECURITY{DEFINER|INVOKER}指明谁有权限来执行,DEFINER表示只有定义者自己才能执行,INVOKER表示调用者可以执行
COMMENT 'string'是注释信息
例子:
#将读写权限改为MODIFIES SQL DATA ,并指明调用者可以执行
ALTER PROCEDURE num_from_employee
     MODIFIES SQL DATA
     SQL SECURITY INVOKER;

#将读写权限改为RAEDS SQL DATA,并加上注释信息“FIND NAME”
ALTER PROCEDURE name_from_employee
     READS SQL DATA
     COMMENT 'FIND NAME';

MySQL存储过程的删除
DROP PROCEDURE [过程1 [,过程2....]]
从MySQL的表格中删除一个或多个存储过程


MySQL存储过程的控制语句
变量作用域
内部变量在其作用域范围内享有更高的优先权,当执行到end时,内部变量消失,不再可见,在存储过程外再也找不到这个内部变量,但是可以通过Out参数或者将其值指派给会话变量来保存其值。

#变量作用域
DELIMITER //
  CREATE PROCEDURE proc()
    BEGIN
      DECLARE x1 VARCHAR(5) DEFAULT 'outer';
        BEGIN
          DECLARE x1 VARCHAR(5) DEFAULT 'inner';
          SELECT x1;
        END;
      SELECT x1;
    END;
    //
DELIMITER ;

#调用
CALL proc();
执行结果:
inner
outer


条件语句

IF-THEN-ELSE语句

#条件语句IF-THEN-ELSE
DROP PROCEDURE IF EXISTS proc3;
DELIMITER //
CREATE PROCEDURE proc3(IN parameter int)
  BEGIN
    DECLARE var int;
    SET var=parameter+1;
    IF var=0 THEN
      INSERT INTO t VALUES (17);
    END IF ;
    IF parameter=0 THEN
      UPDATE t SET s1=s1+1;
    ELSE
      UPDATE t SET s1=s1+2;
    END IF ;
  END ;
  //
DELIMITER ;
CASE-WHEN-THEN-ELSE语句

#CASE-WHEN-THEN-ELSE语句
DELIMITER //
  CREATE PROCEDURE proc4 (IN parameter INT)
    BEGIN
      DECLARE var INT;
      SET var=parameter+1;
      CASE var
        WHEN 0 THEN
          INSERT INTO t VALUES (17);
        WHEN 1 THEN
          INSERT INTO t VALUES (18);
        ELSE
          INSERT INTO t VALUES (19);
      END CASE ;
    END ;
  //
DELIMITER ;
 
循环语句
WHILE-DO…END-WHILE
DELIMITER //
  CREATE PROCEDURE proc5()
    BEGIN
      DECLARE var INT;
      SET var=0;
      WHILE var<6 DO
        INSERT INTO t VALUES (var);
        SET var=var+1;
      END WHILE ;
    END;
  //
DELIMITER ;
 
REPEAT...END REPEAT
此语句的特点是执行操作后检查结果
DELIMITER //
  CREATE PROCEDURE proc6 ()
    BEGIN
      DECLARE v INT;
      SET v=0;
      REPEAT
        INSERT INTO t VALUES(v);
        SET v=v+1;
        UNTIL v>=5
      END REPEAT;
    END;
  //
DELIMITER ;
 
LOOP...END LOOP
DELIMITER //
  CREATE PROCEDURE proc7 ()
    BEGIN
      DECLARE v INT;
      SET v=0;
      LOOP_LABLE:LOOP
        INSERT INTO t VALUES(v);
        SET v=v+1;
        IF v >=5 THEN
          LEAVE LOOP_LABLE;
        END IF;
      END LOOP;
    END;
  //
DELIMITER ;
 
LABLES标号
标号可以用在begin repeat while 或者loop 语句前,语句标号只能在合法的语句前面使用。可以跳出循环,使运行指令达到复合语句的最后一步。

ITERATE迭代

通过引用复合语句的标号,来从新开始复合语句

#ITERATE
DELIMITER //
  CREATE PROCEDURE proc8()
  BEGIN
    DECLARE v INT;
    SET v=0;
    LOOP_LABLE:LOOP
      IF v=3 THEN
        SET v=v+1;
        ITERATE LOOP_LABLE;
      END IF;
      INSERT INTO t VALUES(v);
      SET v=v+1;
      IF v>=5 THEN
        LEAVE LOOP_LABLE;
      END IF;
    END LOOP;
  END;
  //
DELIMITER ;
 

MySQL存储过程的基本函数
字符串类
CHARSET(str) //返回字串字符集
CONCAT (string2 [,... ]) //连接字串
INSTR (string ,substring ) //返回substring首次在string中出现的位置,不存在返回0
LCASE (string2 ) //转换成小写
LEFT (string2 ,length ) //从string2中的左边起取length个字符
LENGTH (string ) //string长度
LOAD_FILE (file_name ) //从文件读取内容
LOCATE (substring , string [,start_position ] ) 同INSTR,但可指定开始位置
LPAD (string2 ,length ,pad ) //重复用pad加在string开头,直到字串长度为length
LTRIM (string2 ) //去除前端空格
REPEAT (string2 ,count ) //重复count次
REPLACE (str ,search_str ,replace_str ) //在str中用replace_str替换search_str
RPAD (string2 ,length ,pad) //在str后用pad补充,直到长度为length
RTRIM (string2 ) //去除后端空格
STRCMP (string1 ,string2 ) //逐字符比较两字串大小,
SUBSTRING (str , position [,length ]) //从str的position开始,取length个字符,
注:mysql中处理字符串时,默认第一个字符下标为1,即参数position必须大于等于1
SELECT SUBSTRING('abcd',0,2);
结果:无
SELECT SUBSTRING('abcd',1,2);
结果:ab
TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符
UCASE (string2 ) //转换成大写
RIGHT(string2,length) //取string2最后length个字符
SPACE(count) //生成count个空格

数学类

ABS (number2 ) //绝对值
BIN (decimal_number ) //十进制转二进制
CEILING (number2 ) //向上取整
CONV(number2,from_base,to_base) //进制转换
FLOOR (number2 ) //向下取整
FORMAT (number,decimal_places ) //保留小数位数
HEX (DecimalNumber ) //转十六进制
注:HEX()中可传入字符串,则返回其ASC-11码,如HEX('DEF')返回4142143
也可以传入十进制整数,返回其十六进制编码,如HEX(25)返回19
LEAST (number , number2 [,..]) //求最小值
MOD (numerator ,denominator ) //求余
POWER (number ,power ) //求指数
RAND([seed]) //随机数
ROUND (number [,decimals ]) //四舍五入,decimals为小数位数] 注:返回类型并非均为整数,如:
SELECT ROUND(1.23);
1
SELECT ROUND(1.56);
2
#设定小数位数,返回浮点型数据
SELECT ROUND(1.567,2);
1.57
SIGN (number2 ) // 正数返回1,负数返回-1


日期时间类
ADDTIME (date2 ,time_interval ) //将time_interval加到date2
CONVERT_TZ (datetime2 ,fromTZ ,toTZ ) //转换时区
CURRENT_DATE ( ) //当前日期
CURRENT_TIME ( ) //当前时间
CURRENT_TIMESTAMP ( ) //当前时间戳
DATE (datetime ) //返回datetime的日期部分
DATE_ADD (date2 , INTERVAL d_value d_type ) //在date2中加上日期或时间
DATE_FORMAT (datetime ,FormatCodes ) //使用formatcodes格式显示datetime
DATE_SUB (date2 , INTERVAL d_value d_type ) //在date2上减去一个时间
DATEDIFF (date1 ,date2 ) //两个日期差
DAY (date ) //返回日期的天
DAYNAME (date ) //英文星期
DAYOFWEEK (date ) //星期(1-7) ,1为星期天
DAYOFYEAR (date ) //一年中的第几天
EXTRACT (interval_name FROM date ) //从date中提取日期的指定部分
MAKEDATE (year ,day ) //给出年及年中的第几天,生成日期串
MAKETIME (hour ,minute ,second ) //生成时间串
MONTHNAME (date ) //英文月份名
NOW ( ) //当前时间
SEC_TO_TIME (seconds ) //秒数转成时间
STR_TO_DATE (string ,format ) //字串转成时间,以format格式显示
TIMEDIFF (datetime1 ,datetime2 ) //两个时间差
TIME_TO_SEC (time ) //时间转秒数]
WEEK (date_time [,start_of_week ]) //第几周
YEAR (datetime ) //年份
DAYOFMONTH(datetime) //月的第几天
HOUR(datetime) //小时
LAST_DAY(date) //date的月的最后日期
MICROSECOND(datetime) //微秒
MONTH(datetime) //月
MINUTE(datetime) //分返回符号,正负或0
SQRT(number2) //开平方
 

你可能感兴趣的:(java,spring,mysql,后端)