JavaSE基础知识回顾

JavaSE基础知识回顾

1. java的数据类型

(1)基本类型:byte(单字节---8位)、short(双字节---16位)、int(4字节---32位)、long(8字节---64位)、float(四字节---32位)、double(8字节---64位)、char(双字节---16位)、boolean(1位---0/1)
(2)引用类型:除了八大基本类型之外的数据类型,常见的有Object、Array和装箱后的基本类型。
(3)JVM存储位置:
	1)、基本类型:存在JVM中的虚拟机栈中
	2)、引用类型:把引用(在堆中的数据地址编号)存在虚拟机栈,把数据存在堆中。

2. 数据类型的装箱和拆箱(JDK5以后的特性)
(1)包装类型(引用类型):每一个基本类型的数据类型都会一一对应一个包装类型。
byte —— Byte
short—— Short
int —— Integer
long —— Long
float —— Float
double —— Double
char —— Character
boolean —— Boolean

装箱:把基本的数据类型转换成对应的包装类型。
Integer.valueOf(int i)
Integer i = 1;自动装箱,实际上在编译时会调用 Integer.valueOf(1) 方法来装箱

拆箱:就是把包装类型转换成为基本数据类型,
Integer i = 1;
int j = i;自动拆箱,实际上在编译时会调用intValue();手动拆箱

java是一个面向对象的语言,而基本的数据类型,不具备面向对象的特性
缓存值:对象缓存(Integer值范围在一个字节内,如果缓存区有相同数值对象则直接取该对象引用,而不会去重新创建对象,即-128~127),Integer i = 1;Integer j = 1;i == j

3. 面向对象的四大特征
(1)封装性:讲一个对象封装成一个高度自治和相对封闭的个体。高度自治体现在它的属性只能有自己的行为来读取和改变、相对封闭体现在它可以能用自己的行为操作自己的属性(对象状态)

package cn.zdxh.lcy.demo03.model;
//对一个职位的信息封装成Department 类
public class Department {
    private Integer id;
    private String department;

    public Department() {
    }

    public Department(Integer id, String department) {
        this.id = id;
        this.department = department;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", department='" + department + '\'' +
                '}';
    }
}

(2)抽象性:就是找出一些事物的相识性和共性之处,然后将这些生物归为一类。这个类只考虑事物的相似性和共性之处,并且会忽略与当前主题和目标无关的方面。将注意力集中在当前目标有关的方面,就是把生活中的实体对象抽象为类。
(3)继承性:在定义和实现一个类的时候,可以在一个已经存在的类基础上进行把这已存在
的所定义的内容作为自己的内容,并可以自行加入若干属于自己内容,或修改原来方法是指更符合需求这就是继承。
(4)多态性:多态就是指程序中定义的引用变量的所指向具体类型和通过该引用变量发出的方法调用在编程时并不确定,只有在程序运行期间才确定,即一个引用变量到底会指向那个实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在有程序运行期间才能决定。

原则:回答比较抽象问题的时候,要举例说明

4. “==” 和 “equals”的区别
(1)“==”:

  1. 基本类型:就是直接比较两个变量的值是否相等。
  2. 引用类型:比较两个变量的引用是(内存的首地址)否相等,即是否为同一个对象。

(2)“equals”:这是引用类型才有的,

  1. 类没有重写equals方法:等价于“==”方式,比较两个对象是否一样
	public boolean equals(Object obj) {
        return (this == obj);
    }
  1. 类重写equals方法:先判断两个对象是否一样,如果不一样在判断内容是否一样。
	public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
  1. 注意:Float 和 Double 两个引用类型对 equals方法进行了重写,只比较他们的值在一定为范围内就为相等(由于浮点数精度的问题)
	// Double 类型源码
	public boolean equals(Object obj) {
	       return (obj instanceof Double)
	              && (doubleToLongBits(((Double)obj).value) ==
	                     doubleToLongBits(value));
	   }
	public static long doubleToLongBits(double value) {
	    long result = doubleToRawLongBits(value);
	    // Check for NaN based on values of bit fields, maximum
	    // exponent and nonzero significand.
	    if ( ((result & DoubleConsts.EXP_BIT_MASK) ==
	          DoubleConsts.EXP_BIT_MASK) &&
	         (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)
	        result = 0x7ff8000000000000L;
	    return result;
	}

	// Float类型源码
	public boolean equals(Object obj) {
        return (obj instanceof Float)
               && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
    }
    public static int floatToIntBits(float value) {
        int result = floatToRawIntBits(value);
        // Check for NaN based on values of bit fields, maximum
        // exponent and nonzero significand.
        if ( ((result & FloatConsts.EXP_BIT_MASK) ==
              FloatConsts.EXP_BIT_MASK) &&
             (result & FloatConsts.SIGNIF_BIT_MASK) != 0)
            result = 0x7fc00000;
        return result;
    }

5. String 和 StringBuilder 的区别{final}? StringBuffer 和 StringBuilder 的区别?
在Java中提供三个类 String、StringBuilder和StringBuffer 来表示和操作字符串。字符串就是多个字符的集合。

  1. String:是内容不可变的字符串,String 底层使用了一个不可变的字符数组(final char[] value)
	// String类源码
	public final class String
	    implements java.io.Serializable, Comparable<String>, CharSequence {
	    /** The value is used for character storage. */
	    private final char value[];
	
	/**
	 * 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的
	 * 值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。
	 */
  1. StringBuilder 和 StringBuffer:两个类都是继承自AbstractStringBuilder 抽象类,而该抽象类使用一个没有final 修饰的字符数组,因此是可变长。
	// AbstractStringBuilder抽象类源码
	abstract class AbstractStringBuilder implements Appendable, CharSequence {
	    /**
	     * The value is used for character storage.
	     */
	    char[] value;
	
	    /**
	     * The count is the number of characters used.
	     */
	    int count;
  1. 拼接字符串:
    1、String:每次改变值都会重新创建对象并改变对象引用指向,所以速度快,费内存
    String c = “a” + “b” == new String(“a”) + new String(“b”);
    2、StringBuilder 和 StringBuffer:每次改变值都是操纵当前对象
    StringBuilder sb = new StringBuilder().apend(“a”).apend(“b”);
    StringBuffer sb = new StringBuffer().apend(“a”).apend(“b”);
    注意:拼接字符串不能使用String进行拼接,要使用StringBuilder 或者 StringBuffer
	// StringBuilder 和 StringBuffer 的安全性比较
	1、StringBuilder :是线程不安全的,效率高
	public StringBuilder append(String str) {		//源码
        super.append(str);
        return this;
    }
	2、StringBuffer:是线程安全的,效率低(底层方法用 synchronized 同步锁修饰)
	public synchronized StringBuffer append(String str) {		//源码
        toStringCache = null;
        super.append(str);
        return this;
    }

6. Java中的集合
Java中的集合分为value(Collection),key—value(Map)两种

一、Collection:子接口有List 和 Set
	1、List:有序可重复(实现类有:ArrayList、LinkedList)
		① ArrayList:底层使用的是数组,在内存中是一块连续的内存,如果要插入或删除是需要移动内存的,(特性:查询快,插入、删除和修改慢。)
		② LinkedList:底层使用的是链表,链表不要求内存连续,在当前元素中存放下一个或上一个元素的地址。查询时需要从头部开始,一个一个的找,所以查询效率低,插入时不需要移动内存,只需改变引用指向即可,所以插入或者删除效率高(特性:查询慢,插入、删除和修改快)
	使用场景:
			1)、ArrayList:使用在查询比较多,但是插入和删除比较少的情况
			2)、LinkedList:使用在查询比较少,而插入和删除比较多的情况
			
	2、Set:无序不可重复,根据equals 方法 和 hashCode 方法判断,也就是如果一个对象要存储在Set中,必须重写 equals 和 hashCode 方法	
	
二、Map:key---value(键值对的形式)存储数据
	#案例:HashMap 和 HashTable 的区别? HashMap 和 CurrentHashMap 的区别?
	1、相同点:HashMap 和 HashTable 都可以使用key --- value 来存储数据。
	
	2、不同点:
		① HashMap 是可以把null 作为key的(仅此一个)或者value的值,而HashTable是不可以的
		② HashMap 是线程不安全的,效率高。而Hashtable是线程安全的,效率较低。
		——public V put(K key, V value) {		##HashMap 类源码
			        return putVal(hash(key), key, value, false, true);
			    }
		——public boolean containsValue(Object value) {		##Hashtable 类源码
			        return contains(value);
			    }

	3、想要线程安全又要线程效率高
	——通过把整个Map 分为N个segment (类似Hashtable),可以提供相同的线程安全,但是效率提升N倍,默认提升 16倍

7. 实现拷贝一个文件的工具使用字节流还是字符流
我们拷贝的文件不确定是只包含字符流,有能有字节流(图片、音频、图像等),为考虑到通用性,要使用字节流。

8. 讲一下线程的几种实现方式
(1)通过继承Thread类实现一个线程

public class ThreadDemo {
	public static void main(String[] args) throws InterruptedException {
			
//		TestThread t = new TestThread();
//		t.setDaemon(true);		//设置线程可置于后台
//		t.start();		//线程启动方法
//		t.getName();	//获取线程名称
//		t.getPriority();		//获取线程的优先级
//		t.destroy();		//线程销毁
//		t.interrupt();		//线程中断
//		t.isAlive();		//线程是否存活
//		t.isDaemon();		//线程置于后台
//		t.join();		//线程强制进行
		
		TestThread t = new TestThread();		//获取线程对象
		t.start();		//启动线程
		for(int i=0; i < 10; i++) {
				System.out.println(Thread.currentThread().getName()+"测试多线程,run("+i+")在运行!");
		}
		
}


class TestThread extends Thread {//继承多线程实现类
	//run方法为多线程的执行入口
	public void run() {
		for(int i=0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+"测试多线程,run("+i+")在运行!");
		}
	}
}
####继承扩展性不强,java总支持单继承,如果一个类继承Thread 就不能继承其他的类了。

(2)通过实现Runnable接口实现一个线程

public class ThreadDemo {
	public static void main(String[] args) throws InterruptedException {
		
		TestRunable  t = new TestRunable ();
		new Thread(t).start();//通过Runable的实现类Thread的start方法开启多线程
		for(int i=0; i < 10; i++) {
				System.out.println(Thread.currentThread().getName()+"测试多线程,run("+i+")在运行!");
		}
		
}
class TestRunable implements Runnable {//实现多线程接口
	public void run() {
		for(int i=0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName()+"测试多线程,runnable()在运行!");
		}
	}
}
####实现接口则还可以实现其他接口或者继承其他类,扩展性强

启动:
Thread thread = new Thread(继承了Thread的对象/实现了Runnable接口的对象)
thread.setName(“avc”);设置线程的名称
thread.start();启动线程
区分线程:通过给线程设置名称的方式来区分线程

9. 线程并发库
(1)JDK5中增加了 Doug Lea 的并发库,这一引进给java线程的管理和使用提供了强大的便利性。java.util.current 包中提供了对线程优化,管理的各项操作,使得线程的使用变得得心应手。该包提供了线程的运行,线程池的创建,线程生命周期的控制。
(2)java通过 Executors 提供四个静态方法来创建四种线程池,分别为:
1)、new CachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2)、new FixedThreadPool 创建一个定长线程池,可控制线成最大并发数,超出的线程会在队列中等待。
3)、new ScheduledThreadPool 创建一个定时线程池,支持定时周期性任务执行。
4)、new SimpleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照执行顺序(FIFO,UFO,优先级)执行,这是为了避免产生线程死锁的一种可行方法。

(3)线程池的作用:
1)、限定线程的个数,不会导致由于线程过多导致系统运行缓慢或崩溃
2)、线程池不需要每次都去创建或销毁,节约了资源
3)、线程池不需要每次都去创建(分配资源)或销毁(回收资源)浪费时间,所以响应时间更快

10. 设计模式
1)、设计模式就是经过前人无数次的实践总结出来的,设计过程中可以反复使用的、可以解决特定问题的设计方法。
2)、常用的设计模式:

一、单例模式:单例设计模式,即某个类在整个系统中只有一个实例对象可被捕获和使用的代码模式(如:代表JVM的运行环境Runtime 类)
	1、要点:
		① 它只能有一个实例
			——>构造方法私有化,只能在自己类中能创建而其他地方都不能创创建
		② 它必须自行创建这个实例
			——>在自己的类中自行创建一个单实例(饱汉模式是需要的时候才创建,而饥汉模式是一出来就创建单实例)
		③ 它必须向整个系统提供这个实例
			——>提供一个对外的方法获取该实例对象(创建时需要进行方法同步)
			——>a:直接暴露,b:用静态变量的get 方法获取
			
	2、饿汉式:直接创建对象,不存在线程安全问题
			a:直接实例化饿汉式(简单直观)
				public class Singleton{
					//提供一个私有的构造器
					private Singleton(){};
					//这种是采用直接暴露的方式
					public static final Singleton HUNGER1 = new Singleton();	//需要的这个实例的时候直接Singleton.HUNGER1
					//采用get 方法的方式
					private Singleton hunger2 = new Singleton();
					public static Singleton getHunger2() {
						return this.hunger2;	
					}
				}
			b:枚举式(最简洁)
				public enum Singleton{
					//枚举类型:表示该类型的对象是有限的几个,我们可以限定为一个,就成了单实例
					HUNGER		//获取的时候也是直接 Singleton.HUNGER 
				}
			c:静态代码块饿汉式(复合复杂实例化)
				public class Singleton{
					//提供一个私有的构造器
					private Singleton(){};
					public static final Singleton HUNGER;
					//采用静态代码块的方式
					static {
						HUNGER = new Singleton();
					}
				}
			
	3、懒汉式:延迟创建对象
			a:程不安全(适用于单线程)
				public class Singleton{
					//提供一个私有的构造器
					private Singleton(){};
					private static Singleton dawdler;
					//提供一个返回实例对象的方法
					public static Singleton getDawdler(){	//只有在调用这个方法的时候才会创建实例
						return new Singleton();	//返回创建的实例对象
					}
				}
			b:线程安全(适用于多线程)
				public class Singleton{
					//提供一个私有的构造器
					private Singleton(){};
					private static Singleton dawdler;
					//提供一个返回实例对象的方法
					public static Singleton getDawdler(){
						//在调用方法的时候先判断是否已经有了实例对象
						if(dawdler == null){
							//采用同步锁的方式解决线程安全问题
							synchronized(Singleton.class){
								 dawdler = new Singleton();
							}
						}
						return dawdler;
					}
				}
			c:静态内部类形式(适用于多线程)
				#在内部类被加载和初始化,才创建DAWDLER 实例对象
				#静态内部类不会自动随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的。
				#因为是在内部类加载和初始化,创建的而线程安全。
				public class Singleton{
					//提供一个私有的构造器
					private Singleton(){};
					private class Inner{
						private static final Singleton DAWDLER = new Singleton();
					}
					public Singleton getDawdler(){
						return Singleton.Inner.DAWDLER;
					}
				}
			
	
2、工厂模式:Spring IOC 就是使用了工厂模式,对象的创建交给一个工厂去创建。
3、代理模式:Spring AOP 就是使用的动态代理模式
4、包装模式:

11. Http中 get 和 post 请求的区别
GET 和POST 请求都是http 的请求方式,用户通过不同的 http 的请求方式完成对资源(URL)的不同操作。GET、POST、PUT、DELETE 就对应着对这个资源的查、增、改、删 4个操作,具体点来讲GET 一般用于查询资源信息,POST一般用户增加资源信息、PUT则对应着资源信息的修改、DELETE就是对资源信息的删除了。
(1)GET 请求提交的数据会在地址栏显示出来,而POST请求不会在地址栏显示出来。
GET 提交,请求的数据会附在URL之后(就是把数据放置在HTTP 协议头中),以 “?” 分割 URL 和传输数据,多个参数用 “&” 连接;
POST 把提交的数据放置在HTTP包的包体中,因此,GET 提交的数据会在地址栏提交,地址栏不会改变。
(2)传输数据的大小
http GET 请求由于浏览器对地址长度的限制而导致传输的数据有限;
POST请求不会因为地址长度的限制传输数据限制;
(3)安全性,post 的安全性要比 get 的安全性高。由于数据会在地址中呈现出来,所以可以通过历史记录找到传递的数据信息

12. Servlet 的讲解
(1)Servlet(Server Applet),全称 java Servlet,使用 java 编写的服务器端程序。而这些 Servlet 都要实现Servlet 这个接口。其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。Servlet 运行与支持 Java的应用服务器中。HttpServlet 重写 doGet 和 doPost 方法或者你也可以重写 service 方法完成对 get 和 post 请求的响应。

public class MyHttpServlet extends HttpServlet {
	//接受客户端发送get请求
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String method = req.getMethod();//获取客户端的请求方式
		System.out.println("method:"+method);
		System.out.println("这个是doGet请求");
	}
	
	//接受客户端发送的post请求
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String method = req.getMethod();
		System.out.println("method:"+method);
		System.out.println("这个是doPost请求");
	}

}

(2)Servlet 有良好的生存期的定义,包括加载和实例化、初始化、处理请求和服务结束。这个生存期由 javax.servlet.Servlet 接口的init、service 和 destroy 方法表达。
(3)Servlet 启动时,开始加载 Servlet 生命周期开始。Servlet 被服务器实例化后,容器运行它的 init 方法,请求到达时运行它的 service 方法,service 方法自然派遣运行与请求对应的doXXX 方法(doGet,doPost)等,当服务器决定将实例销毁的时候就会执行它的 destroy 方法。
(4)流程:加载Servlet 的 class —> 实例化 Servlet —> 调用 Servlet 的init 完成初始化 —> 响应请求(Servlet 的service 方法) —> Servlet 容器关闭时(Servlet 的 destroy 方法)

13. Servlet API 中 forward 和 redirect
前者仅是容器控制权的转向,在客户端浏览器地址栏中不会显示转向的地址;后者则是完全的跳转,浏览器将会得到跳转的地址。并重新发送请求链接。forward还是原来的请求(带原来数据)而redirect就像重新发送的请求所以(不带原来数据)。这样,从浏览器的地址栏中可以看到跳转后的链接地址,所以也有助于隐藏真实的链接。在有些情况下,比如需要跳转到一个sendRedirect()方法。
(1)forward是服务器端的转向而redirect是客户端的跳转。
(2)使用forward浏览器的地址不会发生改变,而redirect会发生改变。
(3)forward是一次请求中完成,而redirect是重新发送请求。
(4)forward是在服务器端完成。而不用客户端重新发起,效率较高。

14. JSP 和 Servlet 的区别
(1)相同点:JSP 是 Servlet 技术的扩展,所有的JSP文件都会被翻译成一个继承
HttpServlet 的Servlet 类。也就是jsp 是最终也是一个Servlet,这个Servlet对外提供服务。
(2)不同点:Servlet 和 JSP 最主要的不同点在于, Servlet 的应用逻辑是在 java 文件中,并且完全从View视图层的HTML里分离出来,而 JSP 的情况是 Java 和 HTML 可以组合成一个扩展为.jsp 的文件。JSP 侧重于视图(做界面展示比较方便,嵌入逻辑比较难),Servlet 主要用于控制逻辑

15. jsp 的9大内置对象
(1)request 客户端的请求,此请求会包含来自GET/POST 请求的参数
(2)response 服务端传回客户端的回应
(3)pageContext 网页的属性是在这里管理(当前页面有效)
(4)session 请求有关的会话域(当前会话有效)
(5)application 正在执行的内存整一个程序(进程)
(6)out 用来传送响应的的输出客户端页面
(7)config servlet的构架部件
(8)page JSP 网页本身
(9)exception 针对错误网页,未捕捉的例外(异常)

四大作用域:pageContext、request、session、application 可以通过jstl 从四大作用域中取值。jsp 传递值 request、session、application、cookie 也能传值。

16. session 和 cookie 的区别
(1)session 和 cookie 都是会话(Session)跟踪技术。Cookie 通过在客户端记录信息确定用户身份,Session 通过在服务器端记录信息确定用户身份。但是 Session 的实现依赖于 Cookie,SessionId(session的唯一标识需要存放客户端)。
(2)cookie 数据存放在客户端的浏览器上,session数据存放在服务器上。
(3)cookie 不是很安全,别人可以分析存放在本地的COOKIE 并进行 COOKIE 欺骗,所以考虑到安全问题应当使用session。
(4)单个cookie 保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
(5)所以个人推荐:
将登陆信息等信息存放到SESSION中
将其他信息如果需要保留,可以放在 COOKIE 中,比如购物车。
购物车最好是用 cookie,但是 cookie 是可以在客户端禁用的,这时候我们要使用 cookie+数据库的方式实现,当从 cookie 中不能取出数据时,就从数据库中取。

17. MVC 的各个部分都有哪些技术来实现
MVC 全名是Model View Controller,是模型(model)——视图(view)——控制器(controller)的缩写,一种软件设计模式典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到Controller(控制层) 这一个部件里面,在修改和个性化定制界面以及用户交互的同时,不需要重新编写业务逻辑。
(1)M(model):模型层 JavaBean
(2)V(view):视图层 html、jsp、volicity、freemaker
(3)C(controller):控制层 Servlet,Action

jsp + Servlet + JavaBean 最经典 MVC 模式,实际上就是 model2 的实现方式,就是把视图和逻辑隔离开发
Model1 的方式(jsp + servlet + dao)
Model2 的方式(jsp + servlet + service +dao)

使用struts2 和 SpringMVC 这样的MVC框架后, jsp(负责视图) + 核心控制器(视图和逻辑之间的交互) + action(逻辑处理)+ JavaBean(数据模型)
在这里插入图片描述

执行流程:
	1、当控制器收到来自用户的请求
	2、控制器调用 JavaBean 完成业务逻辑
	3、完成业务逻辑通过跳转到JSP 页面的方式给用户反馈信息
	4、JSP 给用户做出界面响应

18. 数据库
(1)数据库分类:
1)、关系型数据库:mysql、oracle、sqlserver 等
2)、非关系型数据库:Redis、memcache、MongoDB、Hadoop 等
(2)简单介绍一下数据库的三范式(范式就是规范,数据库表设计时应遵循的规范)
1)、要想满足第二范式必须满足第一范式,要满足第三范式必须满足第二范式。
2)、第一范式(1NF):是指数据库表的每一列都是不可分割的基本数据项,同一列中不能同时有多个值,即实体中的某个属性不能有个值或者不能有重复的属性。列数据的不可分割。
3)、第二范式(2NF):要求数据库表中的每个行都必须可以被唯一地区分(如主键约束),为实现数据行的区分通常要为表加上一个列,用来存储各个实例的唯一标识(主键)
4)、第三范式(3NF):必须先满足第二范式(2NF),简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息(外键)
5)、反三范式:有的为了效率,可以设置重复或者可以推导出的字段(表达式),订单(总价)和单项(单价)

19.事务四个基本特征 或 ACID 特性
事务是并发的单位,是用户定义的一个操作序列。这些操作要么都成功,要么都失败,是一个不可分割的工作单位。
例如:转账操作
一个A账号向B账号操作时,A账号扣钱成功的情况下,B账号加钱也要成功,才算是真正的转账操作成功。
事务必须满足四大特性:原子性、一致性、隔离性、持久性/延续性。

原子性一致性隔离性持久性/延续性
表示事务内操作不可分割,要么都成功,要么都是失败要么都成功,要么都失败,后面的失败了要对前面的操作进行回滚。一个事务开始后,不能受其他事务的干扰。表示事务开始了就不能终止。

20. mysql 数据库的默认的最大连接数
1)、为什么需要最大连接数,特定服务器上面的数据库只能支持一定数目同时连接,这时需要我们设置最大连接数(最多同时服务多少连接)在数据库安装时都会有一个默认的最大连接数。(100)
在这里插入图片描述
2)、mysql 的分页和 oracle 的分页
首先我们需要清楚的是我们为什么要分页?因为数据库存储的数据一般情况下都是巨量的。不可能完全显示数据,所以我们就需要对这些数据进行分段展示。
MySQL 是使用关键字 limit 来进行分页的(limit offset,size)表示从多少索引去多少位。

(limit offset,size)//offset表示开始行,size表示返回的行数
select * from table LIMIT 5,10; #返回第6-15行数据  
select * from table LIMIT 5; #返回前5行  
select * from table LIMIT 0,5; #返回前5行   

Oracle的分页,大部分情况下我们是很难记得住的,就简单说一下思路,要使用三层嵌套查询。

SELECT * FROM
  (
  SELECT A.*, ROWNUM RN
  FROM (SELECT * FROM TABLE_NAME) A//第一层的查询是正常全表查询
  WHERE ROWNUM <= 40//第二层是在第一层的基层上进行设置一个最大值的查询
  )
  WHERE RN >= 21//第三层是在第二层的基础上设置一个最小值的查询

其中最内层的查询SELECT * FROM TABLE_NAME表示不进行翻页的原始查询语句。ROWNUM <= 40和RN >= 21控制分页查询的每页的范围。

上面给出的这个分页查询语句,在大多数情况拥有较高的效率。分页的目的就是控制输出结果集大小,将结果尽快的返回。在上面的分页查询语句中,这种考虑主要体现在WHERE ROWNUM <= 40这句上。

21. 简单讲一下数据的触发器的使用场景
触发器,需要有触发条件,当条件满足以后有什么操作。
触发器用处还是很多的,比如一些网站或者App,当你在上面发表一个日志的时候,会自动通知好友,其实就是在增加日志时做了一个后触发器,再向通知表中写入条目。因为触发器效率高,而UCH 没有用触发器,效率和数据处理能力都很低。
比如:我们有一个论坛,每插入一个帖子,都是我将版面表中的最后发帖时间,帖子总数字段进行同步更新,用触发器做效率就很高。

22. 数据库的存储过程的使用场景
数据库存储过程具有如下优点:
1)、存储过程只在创建时进行编译,以后每次执行存储过程都不需要重新编译,而一般的SQL语句每执行一次就要编译一次,因此如果使用了存储过程就可以大大地提高数据库的执行速度。
2)、通常,复杂的业务逻辑需要多条SQL语句,这些语句要分别从客户端发送到服务器端,当客户端和服务器端之间的交互操作很频繁的时候,就会产生大量的网络传输。如果将这些操作放在一个存储过程中,那么客户端和服务器端之间的网络传输就会大大减少,降低了网络负载量。
3)、存储过程创建一次便可以重复使用,从而可以减少数据库开发人员的工作量。
4)、安全性高,存储过程中可以屏蔽对底层数据库对象的直接访问,使用EXECUTE 权限调用存储过程,无需拥有访问底层数据库对象的显式权限。

正是由于存储过程的纵多优点,目前常用的数据库都支持存储过程,例如 IBM 的DB2、Microsoft 的 SQL Server、Oracle 的 Access 等。开源数据库 MySQL 也在5.0版本的时候实现了对存储过程的支持。

###构建一个存储过程
create procedure insert_Person(_name varchar(255), _age int, out_id int)	//为存储过程设置字段
begin	//开启存储过程
	insert into person value(null, _name, _age);	//存储过程的插入操作
	select max(per_id) into _id from person;		//存储过程的查询操作
end;		//关闭存储过程
call insert_Person('zhangsan', 20, @id);		//执行插入操作
select @id;		//执行查询操作

5)、JDBC 的调用存储过程
①、加载驱动——Class.for(“com.mysql.jdbc.Driver”);
②、获取连接——Connection cn = DriverManager.getConnection(“jdbc:mysql:///数据库名”, “用户名”, “密码”
③、设置参数——cstmt = cn.prepareCall("{call insert_Person(?, ?, ?)});
cstmt.registerOutParameter(3, Types.INTEGER);//第三个参数自增
cstmt.setString(1, ‘lisi’);
cstmt.setInt(2, 27);
④、执行——cstmt.execute();
⑤、释放连接——cstmt.close(); cn.close

public static DataSource ds = null;

		//获取DBCP数据源实现类对象
		BasicDataSource bds = new BasicDataSource();
		//设置连接数据库需要的配置信息
		bds.setDriverClassName("com.mysql.jdbc.Driver");
		bds.setUrl("jdbc:mysql://localhost:3306/driver");
		bds.setUsername("root");
		bds.setPassword("123456");
		//设置连接池的参数
		bds.setInitialSize(5);
		bds.setMaxActive(5);
		ds = bds;
		Connection cn = ds.getConnection();
		cstmt = cn.prepareCall("{call insert_Person(?, ?, ?)});
		cstmt.registerOutParameter(3, Types.INTEGER);//第三个参数自增
		cstmt.setString(1, 'lisi');
		cstmt.setInt(2, 27);
		cstmt.execute();
		cstmt.close();

23. 对JDBC 的理解
JDBC——java database connection(java 数据库连接),数据库管理系统(mysql、Oracle 等)有很多种,每个数据库管理系统支持的命令都不一样。
Java 之定义接口,让数据库厂商自己实现接口,对于开发者而言只需要引用厂商开发出来的接口实现即可。然后以接口方式进行调用(mysql + mysql驱动(接口实现)+ jdbc)
在这里插入图片描述
JDBC 的工作流程
1.加载驱动
MySQL:Class.for(“com.mysql.jdbc.Driver”);
Oracle:Class.for(“oracle.jdbc.driver.OracleDriver”);
2.通过drivermanager获取连接
MySQL:Connection cn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/数据库名称”, “用户名”, “密码”);
Oracle:DataSource ds = DriverManager.getConnection(“jdbc:oracle://localhost:3306/数据库名称”, “用户名”, “密码”);
3.通过连接获取会话
Connection cn = ds.getConnection();
4.通过对会话的增删改查获取结果集
cstmt = cn.prepareCall("{call insert_Person(?, ?, ?)});
cstmt.registerOutParameter(3, Types.INTEGER);//第三个参数自增
cstmt.setString(1, ‘lisi’);
cstmt.setInt(2, 27);
cstmt.execute();
5.处理结果集

Statement st = connection.createStatement();

  Statement接口类还派生出两个接口类PreparedStatement和CallableStatement,这两个接口类对象为我们提供了更加强大的数据訪问功能。

  PreparedStatement能够对SQL语句进行预编译,这样防止了   SQL注入 提高了安全性。

  PreparedStatement  ps=connection.prepareStatement( "update user set id=? where username=?”); ————sql语句中的 ?作为通配符,变量值通过参数设入:ps.setObject(1, object);

  而且预编译结果能够存储在PreparedStatement对象中。当多次运行SQL语句时能够提高效率。

  作为Statement的子类,PreparedStatement继承了Statement的全部函数。

6.关闭资源
cstmt.close();
cn.close();

24. PreparedStatement 和 Statement 的比较
大多数情况下我们会选择使用preparedStatement 而不会使用Statement
1)、PreparedStatement 是预编译的,执行速度要比Statement 快。
2)、PreparedStatement 代码的可读性和可维护性要比Statement 要高,虽然用PreparedStatement 来代替Statement 会使用代码多出几行,但这样的代码无论从可读性还是可维护性上来说都是比直接使用Statement 的代码高很多层次。

stmt.executeUpdate("insert into test(name, age, sex) values("'+var1+', '+var2+', '+var3')");		//Statement
stmt.setString(1, "张三");
stmt.setInt(2, 25);
stmt.setString(3, "男");
stmt.preparedStatement("insert into test(name, age, sex) values(?, ?, ?));		//PreparedStatement
stmt.setString(1, "李四");
stmt.setInt(2, 39);
stmt.setString(3, "男");

3)、安全性PreparedStatement 可以防止SQL 的注入攻击,而Statement 却不能。
就是说,有的人在填写web 表单的时候恶意将一些SQL的命令行通过表单提交传递到后台数据库中,如果使用的是Statement 它没有对一些系统设置好的SQL语句进行提前也编译,而是调用SQL命令的时候才进行编译,所以就会造成web表单的参数(可能是SQL命令)一起编译,这样就不区分是SQL语句的参数还是命令了。就会造成一些破坏操作,而使用PreparedStatement 是提前把系统设置好的SQL语句预编译一遍,当有调用的时候直接填入参数用就行了(不会跟参数再进行编译),所以参数只能是参数就算参数中有SQL命令也只能当成参数处理了,就达到了预防SQL注入攻击操作。

stmt.executeUpdate("insert into test(name, age, sex) values("'+var1+', '+var2+', '+var3')");		//Statement
stmt.setString(1, "'var1+', 'var4+'");		//恶意操作
stmt.setInt(2, 25);
stmt.setString(3, "男");
Statement 会进行SQL语句编译
——>insert into test(name, age, sex) values('var1', 'var4', 25, '男')		//由原来的三个参数变成了四个参数就会报错了

stmt.preparedStatement("insert into test(name, age, sex) values(?, ?, ?));		//PreparedStatement
stmt.setString(1, "'var1+', 'var4+'");		//恶意操作
stmt.setInt(2, 39);
stmt.setString(3, "男");
PreparedStatement 在启动的时候已经进行了预编译,不会再进行编译操作
——>insert into test(name, age, sex) values("'var1', 'var4'", 39, '男');		//'var1', 'var4' 只是被当做参数处理

25. 数据库连接池的作用
1)、限定数据的个数,不会由于数据库连接数过多而导致系统运行缓慢或崩溃
2)、在申请数据库连接的时候不需要每次都去创建或销毁,节约了资源
3)、数据库连接不需要每次都是创建,响应时间快更

26. 前端部分知识解说
1)、HTML、CSS、JavaScript 在网页开发中的定位
HTML(超文本标记语言):定义网页的结构
CSS:层叠样式表,用来美化页面
JavaScript:主要用来验证表单,做动态交互和页面的简单逻辑(其中 Ajax)

2)、Ajax(异步的JavaScript 和 XML)
异步的JavaScript 和 XML,
① 作用:通过Ajax 与服务器进行数据交互,Ajax 可以是网页实现布局更新,这意味着可以在不重新加载整个页面的情况下,对网页的局部进行数据更新。
② 现实:获取Ajax 的XmlHttpRequest 对象,使用这个对象可以异步向服务器发送请求,获取响应,完成页面的局部更新操作,Open send responseText/responseXML 局部响应。
③ 使用场景:登录失败时不跳转页面,注册时提示用户名是否已存在,二级联动等等使用场景

3)、JavaScript 和 JQuery
JQuery 是JavaScript 的一个库,封装了JavaScript 的属性和方法,并且增强了JavaScript 的功能,让用户用起来更加便利。
原生使用JavaScript 是要处理很多的兼容性问题,由于JQuery 是用JavaScript 封装好了底层,就不用处理兼容性问题。
原生的JavaScript 的dom 操作和事件绑定和 Ajax 等操作非常麻烦,JQuery 封装了以后操作起来非常方便

4)、JQuery 的常用选择器
① ID 选择器——通过ID 获取该元素的控制权
② Class 选择器——通过类(css)获取该元素的控制权
③ 标签选择器——通过标签获取该元素的控制权
④ 通用选择器——通过(*)获取所有元素的控制权
⑤ 复合选择器(input.myIpu)——通过组合的类名(myIpu)获取该元素的控制权
⑥ 层次选择器

	儿子选择器	>	获取下面的子元素的控制权
	后代选择器	 空格	获取下面后代(包括儿子,孙子等后代)元素的控制权

⑦ 属性选择器——通过该标签中的某属性获取该元素的控制

Tag[attrName="test"]	获取有属性名为 attrName 并且属性值为 test 的所有 Tag 标签
##例子
<input type="checkbox" name="hobby" />打篮球 <br />
<input type="checkbox" name="hobby" />跳舞 <br />
<input type="checkbox" name="hobby" />跑步 <br />
Input[name="hobby"]	这个就是获取属性名为 name 并且属性值为 hobby 的所有input 标签

5)、JQuery 的页面加载完毕事件
很多时候我们需要获取元素,但是必须等到该元素被加载完毕以后才能获取,我们可以把JavaScript 代码放到该元素的后面,但是这样会造成JavaScript 在我们的body 中存在不好管理,所有页面加载完毕后所有的元素当然已经加载完毕,一般获取元素做操作的时候都是要在页面加载完毕后操作。

第一种第二种
$(document).ready(function(){})$(function(){})
$(document)把原生的document 这个dom 对象转换为JQuery 对象,转换完成后才能调用ready() 方法,ready(fun) 表示的是页面结构被加载完毕后执行传入的函数fun当页面加载完毕后执行里面的函数所以采用JQuery的方式更简单简单一点

window.onload 的区别
1、JQuery 中的页面加载完毕事件,表示的是页面结构被加载完毕。
2、window.onload 表示的是页面的全部加载点(包括一些图像、音频等的远程链接)都加载完毕。

<img src="http://baidu.com/first.jpg" />	
//window.onload 必须等页面的全部加载加载完毕后才能调用ready()方法,而JQuery 则只是等页面的结构(HTML元素,CSS样式)被加载完成后就可以调用ready()方法了

6)、JQuery 的Ajax 和原生实现的Ajax
JQuery 中的Ajax 也是通过封装原生的JavaScript 实现的。封装完成后可以让我们以后的每次调用起来更加方便,不用考虑底层实现和一些兼容性问题。
如果采用原生的JavaScript 实现Ajax 是非常麻烦的,并且每次的实现代码都是一样的,如果我们不是用JQuery 我们也要封装对象的方法和属性 。有像这些已经封装完成,并经过很多企业实际的框架,比较可靠并且开源。我们就不行封装,直接使用成熟的框架(JQuery)即可。

7)、HTML5 和 CSS3 的新特性
① HTML5
HTML5 增加一些像花板、声音、视频、web存储等高级功能。但是HTML5 有一个不好的地方就是HTML5 态强调语义了,导致开发中都不知道要选哪个标签对应那种功能。
比如:在做一个页面布局的时候,无论是头部还是尾部或者主题、导航等都可以使用div 盒子来表示,但是HTML5的规范,需要使用不同的标签来表示(头部——header、尾部——footer等等)
② CSS3
Css3 是最新版本的Css,是对原来Css2 的功能增强。
Css3 中提供了一些原来Css2 中实现起来比较困难或者不能实现的功能。

##新功能
	1、盒子的圆角边框
	2、盒子和文字的阴影
	3、渐变
	4、转换、移动、缩放、旋转等等
	5、过渡、动画都可以使用动画
	6、可以使用媒体查询实现响应式网站。

Css3 最大的缺点就是包根据不同的浏览器处理兼容性问题,对应有一些处理兼容性问题的工具,不用担心、

8)、Bootstrap
bootstrap 是一个移动设备优先使用的UI 组件库,我们可以不用写任何的Css,JavaScript 代码就能实现比较漂亮的有交互性的页面,由于我们自己来美化页面的话需要花费更多的时间和精力在上面,所以我们可以直接使用像bootstrap 这样的UI 组库。

平时使用最多的:
	1、模态框
	2、表单,表单项
	3、布局
	4、栅格系统

26. 框架部分
框架(Framework)是一个框子——指的是它的约束性,也是一个架子——指的是它的支撑性。
IT语境中的框架,特指为解决一个开发性问题而设计的具有一定约束性的支撑结构,因此结构上可以根据具体问题进行相应的扩展、安插更多的组成部分。从而更迅速和方便地构建完整的解决问题的方案。
1)、框架本身一般不完整,可以解决一些特定的问题,但是可以帮助我们快速地解决一些特定问题:
没有框架所有的工作都是从零开始做,有了框架之后,我们就可以直接调用框架中提供的特定功能实现来完成我们的需求,这样就可以在框架的基础上开发,从而极大地节约了我们的开发时间和解放了生产力。
2)、框架天生就是为扩展而设计的;
3)、框架里面可以为后续扩展的组件提供很多辅助性、支撑性的方便易用的使用工具(Utils),也就是说框架时常配套了一些帮助解决某些问题的库(libraries)或工具(tools)。
在java 中就是一系列的jar包,其本质就是对jdk 的扩展(就是在底层封装好了某些特定的功能实现方法以便后面可以直接调用) 。

27. MVC 框架
MVC 框架是为了解决传统的MVC 模式(jsp + Servlet + JavaBean)中存在的问题而出现的框架。

传统MVC 模式存在的问题:
	1、所有的Servlet 和 Servlet 映射都要配置在web.xml 中,如果项目的工程量太大,web.xml 中的内容就会太庞大了。并且不能实现模块化管理。
	2、Servlet 的主要功能就是接受参数、调用逻辑、页面跳转,比如像其他的字符编码、文件上传等的功能也要写在Servlet 中,不能让Servlet 只要实现主要功能即可,但它往往还需要嵌套一些别的附加特例。如果用框架,则可以把这个附加特例交给框架,Servlet 只要实现主要功能即可。
	3、接受参数比较麻烦,不能通过model(JavaBean) 接受,只能单个接收,接收完成后在封装到麦model 中。
		 String name = request.getParamter("username");
		 User user = new User();
		 user.setUsername(name);
	4、页面跳转方式比较单一(forward、redirect),当我们的页面名称发生改变时需要修改Servlet 中的源码。


#####web.xml
		<?xml version="1.0" encoding="UTF-8"?>
		<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
		  <display-name>ch03</display-name>
		  <context-param>
		    <param-name>user1</param-name>
		    <param-value>MisChen</param-value>
		  </context-param>
		  <context-param>
		    <param-name>user2</param-name>
		    <param-value>MrLi</param-value>
		  </context-param>
		  <welcome-file-list>
		  <!-- 默认配置页面 -->
		    <welcome-file>index.html</welcome-file>
		    <welcome-file>index.htm</welcome-file>
		    <welcome-file>index.jsp</welcome-file>
		    <welcome-file>default.html</welcome-file>
		    <welcome-file>default.htm</welcome-file>
		    <welcome-file>default.jsp</welcome-file>
		  </welcome-file-list>
		  <servlet>
		    <description></description>
		    <display-name>helloWorldServlet</display-name>
		    <servlet-name>helloWorldServlet</servlet-name>
		    <servlet-class>cn.zdxh.lcy.helloWorldServlet</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>helloWorldServlet</servlet-name>
		    <url-pattern>/helloWorldServlet</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>LifeServlet</display-name>
		    <servlet-name>LifeServlet</servlet-name>
		    <servlet-class>cn.zdxh.lcy.LifeServlet</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>LifeServlet</servlet-name>
		    <url-pattern>/LifeServlet</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>MyHttpServlet</display-name>
		    <servlet-name>MyHttpServlet</servlet-name>
		    <servlet-class>cn.zdxh.lcy.MyHttpServlet</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>MyHttpServlet</servlet-name>
		    <url-pattern>/MyHttpServlet</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>Example01</display-name>
		    <servlet-name>Example01</servlet-name>
		    <servlet-class>cn.zdxh.lcy.Example01</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>Example01</servlet-name>
		    <url-pattern>/Example01</url-pattern>
		  </servlet-mapping>
		  <servlet-mapping>
		    <servlet-name>Example01</servlet-name>
		    <url-pattern>/hello</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>defaultServlet</display-name>
		    <servlet-name>defaultServlet</servlet-name>
		    <servlet-class>cn.zdxh.lcy.defaultServlet</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>defaultServlet</servlet-name>
		    <url-pattern>/defaultServlet</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>defaultServletDemo</display-name>
		    <servlet-name>defaultServletDemo</servlet-name>
		    <servlet-class>cn.zdxh.lcy.defaultServletDemo</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>defaultServletDemo</servlet-name>
		    <url-pattern>/</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>servletConfigDemo</display-name>
		    <servlet-name>servletConfigDemo</servlet-name>
		    <servlet-class>cn.zdxh.lcy.servletConfigDemo</servlet-class>
		    <init-param>
		      <param-name>encoding</param-name>
		      <param-value>UTF-8</param-value>
		    </init-param>
		    <init-param>
		      <param-name>user</param-name>
		      <param-value>陈慧玲你傻吊</param-value>
		    </init-param>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>servletConfigDemo</servlet-name>
		    <url-pattern>/servletConfigDemo</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>servletContextDemo</display-name>
		    <servlet-name>servletContextDemo</servlet-name>
		    <servlet-class>cn.zdxh.lcy.servletContextDemo</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>servletContextDemo</servlet-name>
		    <url-pattern>/servletContextDemo</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>TestServlet04</display-name>
		    <servlet-name>TestServlet04</servlet-name>
		    <servlet-class>cn.zdxh.lcy.TestServlet04</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>TestServlet04</servlet-name>
		    <url-pattern>/TestServlet04</url-pattern>
		  </servlet-mapping>
		  <servlet>
		    <description></description>
		    <display-name>TestServlet05</display-name>
		    <servlet-name>TestServlet05</servlet-name>
		    <servlet-class>cn.zdxh.lcy.TestServlet05</servlet-class>
		  </servlet>
		  <servlet-mapping>
		    <servlet-name>TestServlet05</servlet-name>
		    <url-pattern>/TestServlet05</url-pattern>
		  </servlet-mapping>
		</web-app>

常用的MVC 框架:Struts2、SpringMVC

28. Struts2 框架原理
一个请求在Struts2 框架中的处理流程:
在这里插入图片描述
1)、客户端浏览器发起请求
2)、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做 ActionContextCleanUp 的可选过滤器,这个过滤器对于Struts2 和其他框架的集成有很大的帮助,例如:SiteMesh Plugin);

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">

    <display-name>SH</display-name>
    <!-- struts过滤器 -->
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <!-- 过滤所有请求 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

3)、接着FilterDispatcher(StrutsPrepareAndExecuteFilter——Struts2 的核心控制器)被调用,FilterDispatcher(StrutsPrepareAndExecuteFilter)询问ActionMapper(action 映射器) 来决定这个请求是否需要调用某个Action 进行处理;
4)、如果ActionMapper 决定需要调用某个Action 处理,FilterDispatcher(StrutsPrepareAndExecuteFilter)会把请求的处理权交给ActionProxy(Action 代理);
5)、ActionProxy 通过ConfigurationMapper 询问框架的配置文件(struts.xml),找到需要调用的Action 类;
6)、ActionProxy 创建一个ActionInvocation 的实例
7)、ActionInvocation 实例使用命名模式来调用,在调用Action 类的过程前后涉及到相关的拦截器(Interceptor)的调用。
8)、一旦Action 执行完毕,ActionInvocation 负责根据struts.xml 的配置信息中找到对应的返回路径,返回的结果通常是(但不总是,也有可能是另外一个Action 类)一个需要被表示的JSP 或者 FreeMarker 的模板。在表示的过程中可以使用Struts2 框架中继承标签,在这个过程中需要涉及到ActionMapper。

总结:拦截(FilterDispatcher/StrutsPrepareAndExecuteFilter)、判断(ActionMapper)、寻找(ActionProxy+ConfigurationMapper+struts.xml)、执行(ActionInvocation )、响应

29. Struts2 的拦截器
1)、java 里的拦截器是动态拦截Action 调用的对象,它提供了一种机制可以使开发者可以自定义一些能在action 执行的前后调用的需求功能代码,也可以在一个action 执行前阻止其执行,同时也提供了一种可以提取 action 中可重用部分设计成拦截器(比如:jdbc 的调用,可以把数据源获取的操作设置成拦截器的前置部分,而关闭资源的操作设置成拦截器的后置部分)

/*
 * 功能需求:在登录页面的用户名和密码输入框各种情况的拦截器处理
 *
 * 功能分析:
 *     A:拦截器类继承自内置虚拟拦截器类
 *     B:获取得到ActionContext集合
 *     C:在ActionContext集合取出user对象
 *     D:判断user对象中的属性是否有值
 *     F:处理两种情况
 *            a:为空时就是用户还没有登录系统
 *            b:不为空时就是用户已经向文本框输入值
 *
 * */


public class PrivilegeInterceptor extends AbstractInterceptor {
    //定义一个序列化作为此类的标识
    private static final long serialVersionUID = 1L;

    public String intercept(ActionInvocation invocation) throws Exception {
        //得到ActionContext
        ActionContext actionContext = invocation.getInvocationContext();

        //在ActionContext集合中获取user对象
        User user = (User) actionContext.getSession().get("user");
        if (user != null) {
            return invocation.invoke();
        } else {//user=null,没有username和password存放到集合中,及没有用户成功登陆
            actionContext.getContext().put("msg", "您还未登录,请先登录");

            return Action.ERROR;//如果用户不存在,返回login的值
        }
    }


}

2)、在AOP(Aspect-Oriented-Programming)中拦截器用于在某个方法或者字段被访问之前,进行拦截然后在方法执行完成后加入某些需求功能操作。

总结:Struts2 中的功能(参数处理、文件上传、字符编码等)都是通过系统拦截器实现的,当然我们也可以自定义拦截器进行插拔配置,通过动态配置方式,可以在执行Action 的方法的前后,来完成一些特殊需求的业务。
使用场景:
① 用户登录判断:在执行Action 的前面判断是否已经登录,如果没有登录就跳转到登录页面并提示用户只有进行登陆后才可继续操作。
② 用户的权限判断:在执行Action 的前面判断是否具有使用权限,如果没有使用权限就给出提示信息。
③ 操作日志:在执行Action 的前后均加入添加日志记录操作,方便后期检查工作。
④ …

30. springMVC
(1)核心处理机制
在这里插入图片描述
执行流程:
1)捕获:用户向服务器发送请求,请求被SpringMVC 的核心控制器(DispatcherServlet)捕获

<servlet>
      <servlet-name>dispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
      <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
     <servlet-name>dispatcher</servlet-name>
     <url-pattern>*.action</url-pattern>
 </servlet-mapping>

2)查找:DispatcherServlet 对请求URL 进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping 获得该Handler 配置的所有相关的对象(包括Handler 对象以及Handler 对象对应的拦截器),最后一HandlerExecutionChain 对象的形式返回;
3)执行:DispatcherServlet 根据捕获的Handler,选择一个合适的HandlerAdapter(处理方法),提取Request 中的模型数据,填充Handler 入参,开始执行Handler(Controller),Handler 执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
4)选择:DispatcherServlet 会根据返回的ModelAndView 中的视图信息,选择一个合适的VIewResolver(必须已经注册到Spring 容器中的ViewResolver)

//2、3、4 的代码实现
@Controller//控制器标识
@RequestMapping("/admin")
public class    loginController {
    //设置用户注册信息实体类
    Register regist;

    //用户登录处理方法
    @RequestMapping("/login")//请求路径
    public ModelAndView loginSuccess(Login login, HttpServletRequest request) {
        //获取数据源工厂
        SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
        //获取对数据库的操作对象
        SqlSession session = factory.openSession();
        //指定持久化的操作方法
        String statement = "cn.zdxh.lcy.mapper.registMapper.selectUserById";
        System.out.println(login.getUsername() + "------" + login.getPassword());
        //查询数据库校验用户信息是否有注册
        Register re = session.selectOne("cn.zdxh.lcy.mapper.registMapper.selectUserById", login.getUsername());
        request.getSession().setAttribute("admin", re);

        //数据参数存放到map集合中
        Map<String, String> model = new HashMap<String, String>();
        Map<String, Register> model2 = new HashMap<String, Register>();
        ModelAndView mv;
        if (re != null) {
            model2.put("register", re);
            mv = new ModelAndView("index.jsp", model2);
        } else {
            //把参数存放到ModelAndView中
            model.put("judge","error");
            mv = new ModelAndView("login.jsp", model);
        }

        return mv;
        //return "redirect : 路径" 请求重定向
        //return "forward : 路径" 请求转发
    }
  }

5)渲染返回:通过ViewResolver 结果Model 和View 来渲染视图,DispatcherServlet 将渲染结果返回到客户端。

总结:捕获——核心过滤器(DispatcherServlet )捕获请求、查找——处理映射器(HandlerMapping)处理方法、执行——Handler、选择——视图解析器(ViewResolver)选择视图、渲染返回——ViewResolver 把数据渲染到响应视图

30. Struts2 和 SpringMVC 的比较
目前企业中使用SpringMVC 的比例已经远远超过了Struts2 ,两者之间有什么区别呢,这个是很多初学者都很关注的问题,下面就行对两者各方面之间的比较
1)、核心控制器(前端控制器,预处理控制器):对于使用过MVC 框架的人来说这个词应该是不陌生的,核心控制器的主要用途是处理所有的请求,然后对那些特殊的请求(控制器)统一的进行处理(字符编码、文件上传、参数接收、异常处理等等),SpringMVC 核心控制器是Servlet,而Struts2 是Filter。
2)、控制器实例:SpringMVC 会比Struts2 快一些(理论上),SpringMVC 是基于方法设计的,而Struts2 是基于action 类设计的。每次发起一次请求都会实例一个action,每个action 都会被注入属性。SpringMVC 更像Servlet 一样,只有一个实例,每次请求执行对应的方法即可(注意:由于是单实例,所以应当避免全局变量的修改,这样会造成线程安全问题)
3)、管理方式:事务管理型的核心架构中,就会使用到Spring,而SpringMVC 又是Spring 中的一个模块,所以Spring 对于SpringMVC 的控制器管理更加简单方便(近水楼台先得月),而且提供了全注解的方式进行管理,各种功能的注解都比较全面使用简单,而Struts2 需要采用XML 设置很多的配置参数来管理(虽然也提供了注解,但是几何没有公司采用)
4)、参数传递:Struts2 中自身提供多种参数接受,其实都是通过(ValueStack)值栈进行传递和赋值,而SpringMVC 是通过方法的参数进行接受。
5)、学习难度:Struts2 增加了很多新的技术点,比如:拦截器、值栈以及OGNL 表达式,学习起来更费时间。SpringMVC 比较简单轻量,使用很少的时间就可以上手操作了。
6)、interceptor 的实例机制:Struts2 有以自己的interceptor 机制,SpringMVC 用的是独立的AOP 方式。这样导致Struts2 的配置文件量还是比SpringMVC 大,虽然Struts2 的配置具有继承性,但是在使用上讲,SpringMVC 使用起来更加简洁,开发效率更高。SpringMVC 是方法级别的拦截,一个方法对应一个request 上下文,而每个方法同时又跟一个url 对应着,所以说从空间本身上SpringMVC 就容易实现 restful url, Struts2 是类级别的拦截,一个类对应着一个request 上下文;实现 restful url 要更费劲,因为Struts2 的action类中一个方法可以对应一个url;而它的类属性却被所有的方法所共享,这也就是无法使用注解或其他方式标注它的方法原因。SpringMVC 的方法之间基本上是独立的,独享request response 数据,请求数据通过参数获取,处理结果通过ModelMap 交还给框架方法之间不共享变量,而Struts2 搞得就比较混乱,虽然方法之间也是独立的,担起所有Action 变量的共享的,这不会影响程序的运行,却给编码、读码带来不小麻烦。
7)、SpringMVC 处理Ajax 请求直接通过返回数据,方法中使用注解@ResponseBody,SpringMVC自动帮我们把要响应的对象数据转换成json 格式数据,而Struts2 是通过第三方插件的方式进行处理的。

在SpringMVC 流行起来之前,Struts2 在MVC框架中占据核心地位,随着SpringMVC的出现。SpringMVC 慢慢取代了Struts2,很多企业都是原来打架的框架,使用的是Struts2 比较多。

31. spring
Spring 是J2EE 应用程序框架,是轻量级的IoC 和 AOP的容器框架,主要针对JavaBean 的生命周期进行管理的轻量级容器(相对于重量级的EJB),可以单独使用,可以和Struts2 框架、SpringMVC、Hibernate、Mybatis 等框架的组合使用。
在spring 中,它会认为一切java类都是资源,而资源都是Bean,容纳这些Bean的是Spring所提供的IoC容器,所以Spring是一种基于Bean的编程。它不依赖于Spring本身提供的API,也就是无入侵性或者低入侵性,即使java程序离开了Spring下依然可以运行,这就使得Spring在运用的时候具有十分的灵活性(就像插座一样,用的时候插上不用的时候拔掉)。
(1)POJO的开发潜力:通过配置(XML、注解等)来扩展POJO的功能,通过依赖注入的方向为实现类注入别的操作对象(低耦合度)而不是像以前一样通过new 创建的方式直接把实现类和操作对象绑定在一起(高耦合度)。
(2)整合各个框架:Spring提供了大量的模板类,通过这些模板类可以整合各个框架和技术。
(3)Spring IoC:(Inversion of Controller)控制反转(工厂模式),这是Spring框架的核心内容,贯穿始终。这个就是日常生活中通过第三方的婚姻介绍所(存储了大量的对象信息)介绍对象结婚一样,我们想要找对象结婚,就把自己的请求(身高、体重、三维等信息)发送到介绍所。然后介绍所就会根据我们的请求信息进行匹配。如果匹配成功了就会把这个对象返回给你。而不是由自己主动地根据自己的请求信息直接匹配对象。在java程序中的我们把大量的类信息注入到Spring 容器中,而Spring 容器就会把这些类信息以Bean的形式存储起来。等到某个实现类需要这些Bean类的时候再向 Spring 容器匹配Bean类,找到了就把Bean类注入到实现类中(所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。),找不到就会报错。这就是控制反转的精髓,它最大的好处就是实现类不用自己手动 new 创建Bean类,而是直接交给代理的Spring 容器把需求的Bean类注入到自己的类体中,这样做大大的降低了Bean类和实现类之间的耦合度。后期维护的时候要修改实现类需求的Bean类的时候就可以直接在Spring 容器中修改就行了,而不需要一个一个实现类中修改。

IoC Inversion of Controller)控制反转(工厂模式)
	原来:我的service 需要调用Dao ,service 就需要自己手动创建Dao 
	Spring:Spring 发现service 依赖于Dao,就给service 自动注入Dao(前提是这个Dao 已经被注册到Spring容器中了)
	核心原理:就是配置文件 + 反射 + 容器(map)

(4)依赖注入:(Dependent Injection)
1)、构造器注入:依赖于构造方法实现,而构造方法可以是有参数或者无参数的。通常我们都是使用有参构造方法把成员变量的值注入进来完成赋值操作。这种注入方式的好处是代码量少,但是对于有大量成员变量的JavaBean 来说会导致有参数构造方法过于复杂了,可读性也变差了。

///JavaBean
package cn.zdxh.lcy.demo03.model;

import java.util.Date;

public class Person {
    private String name;
    private Integer age;
    
    public Person(String name, Integer age) {//构造器注入
        this.name = name;
        this.age = age;

    }
}

//构造器注入配置 
<bean id="person1" class=" cn.zdxh.lcy.demo03.model.Person">
	<constructor-arg index="0" value="张三" />//第一个参数
	<constructor-arg index="1" value="20" />//第二个参数
</bean>

2)、setter 注入:setter 注入是Spring 中最主流的的注入方式,它利用JavaBean 规范所定义的setter 方法来完成注入,灵活且可读性高。首先可以把构造方法设置为无参数的。然后使用setter 注入完成对应成员变量的赋值操作,这也体现了java的反射技术。

///JavaBean
package cn.zdxh.lcy.demo03.model;

import java.util.Date;

public class Person {
    private String name;
    private Integer age;
    
    public Person() {//无参构造方法
    }
}

//setter 注入配置 
<bean id="person2" class=" cn.zdxh.lcy.demo03.model.Person">
	<property name="name" value="张三" />//第一个成员变量
	<property name="age" value="20" />//第二个成员变量
</bean>

3)、接口注入:有些时候字眼并非来自自身系统,而是来自于外界,比如我们经常使用的JDBC 操作数据库的时候。需要把数据库资源加载到系统中才能使用数据库资源。所以我们可以采用Spring 的接口注入的方式把来自外界的数据库资源注入到本系统中。

<bean id="dataSource" class="org.springframework.jndi.JndiObectFactoryBean">
	<!-- 指定方言 -->
        <property name="dialect">
            org.hibernate.dialect.MySQLDialect
        </property>
        <!-- 数据库驱动 -->
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <!-- 数据库url -->
        <property name="connection.url">
            jdbc:mysql:///hotel
        </property>
        <property name="connection.username">
            root
        </property>
        <property name="connection.password">
            123456
        </property>
</bean>

(5)Spring AOP:面向切面编程

核心原理:使用动态代理的设计模式在执行前后或出现异常后做相关逻辑操作。
使用场景:
	1、事务处理:执行方法开启事务,执行完成后关闭事务,出现异常后回滚事务(jdbc 事务)
	2、权限判断:在方法执行前做出用户权限的判断
	3、日志记录:在方法执行前后均做系统运行日志的记录 
	4、........

6)、Spring 事务的传播特性
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播,例如:方法可能继续在现有事务中运行,也可能开启一个新的事务,并在自己的事务中运行。(一个方法运行在一个开启了事务的方法中时,当前方法是使用原来的的事务还是开启一个新的事务)
多个事务存在时怎么处理的策略
① PROPAGATION_REQUIRED(需要):如果存在一个事务,则支持当前事务,如果没有事务则开启一个新的事务
② PROPAGATION_SUPPORTS(支持):如果存在一个事务,则支持当前事务,如果没有事务则非事务
③ PROPAGATION_MANDATORY(必要的):如果存在一个事务,则支持当前事务,如果没有一个活动的事务则则抛出异常
④ PROPAGATION_REQUIRES_NEW(总是开启一个新的):总是开启一个新的事务,如果已经存在一个活动的事务,则把当前事务挂起。
⑤ PROPAGATION_NOT_SUPPORTED(不支持):总是非事务的执行,并挂起任何存在的事务。
⑥ PROPAGATION_NEVER(局部):总是非事务的执行,如果存在一个活动的事务,则抛出异常
⑦ PROPAGATION_NESTED(嵌套):如果存在一个活动的事务,则运行在一个嵌套的事务中,如果没有活动事务,则按TransienttionDefinition.PROPATION_REQUIRED 属性执行

7)、Bean 的作用域

spring 中bean 的作用域:可以通过scope 属性来指定bean 的作用域
		singleton:默认值,当IOC 容器一创建同时也会创建bean 实例,而且是单例模式,每次得到的都是同一个
		prototype:原型的,当IOC容器一创建不在实例化该bean,而是在调用getBean 方法时再实例化该bean,而且每次调用都会重新给创建一个该bean的实例返回。
		request:每次请求实例化一个bean
		session:在一次会话中共享一个bean

31. ORM
对象关系映射(Object Relational Mapping),模式是一种为了解决面向对象与关系数据库存在互补匹配的现象的技术。简单地说,ORM 是通过使用描述对象和数据库之间映射的元数据,将程序中的对象数据自动持久化到关系数据库中,一种简单的方案就是采用硬编码方式(jdbc 操作 SQL方式),为每一种可能的数据库访问操作提供简单的方法。
这种方案存在以下不足:
1)、持久化层缺乏弹性,一旦出现业务需要的变更,就必须修改持久化层的接口
2)、持久化层同时与模型层与关系数据库模型进行绑定,不管域模型还是关系数据库模型发生变化,都要进行修改持久化层的相关代码,降低了软件的可维护性。

ORM 提供了实现持久层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM 中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。java典型的 ORM框架有:Hibernate、Mybatis、SpeedFramework。
ORM 的方法基于三个核心原则:
简单:以最基本的形式建模数据。
传达性:数据库结构被任何人都能理解的语言文档化
精确性:基于数据库模型创建正确标准化的映射对象结构

32. Hibernate 和 Mybatis 的比较
1)、相同点:
都是Java 中ORM 框架、屏蔽了jdbc api 的底层访问细节,使用这些框架可以不用与jdbc api 打交道,就可以完成对数据库的持久化操作。jdbc pai 编程流程固定,还将SQL 语句与java 代码混杂在了一起,经常需要拼凑SQL 语句,细节很繁琐。
① mybatis:屏蔽jdbc api 的底层访问细节;将SQL 语句与java 代码进行分离,提供了将结果集自动封装为实体对象和对象的集合的功能,queryForList 返回对象集合,用queryForList 返回单个对象;提供了自动将实体对象的属性传递给SQL 语句的参数。
② hibernate:Hibernate是一个全自动的ORM 映射工具,它可以自动生成SQL 语句,并执行返回Java结果。
2)、不同点:
① hibernate 要比mybatis 的功能负责和强大很大,因为hibernate 自动生成SQL 语句,
② mybatis 需要我们自己在xml 配置文件中写SQL 语句,hibernate 我们无法直接控制SQL 语句,我们就不能去写特定的高效SQL语句。对于一些不太复杂的SQL 操作,hibernate 可以很快速便捷地完成,但是对于一些比较复杂的SQL 操作,hibernate 就很难适应了,这时mybatis 就凸显它的半自动优势了,由于mybatis 可以由自己编写SQL 语句所以就能随我们所需编写特定复杂的SQL操作。
③ mybatis 要比hibernate 简单得多,mybatis 是面向SQL的,不用考虑对象间一些复杂的映射关系。

33. Hibernate 的映射状态
临时状态/瞬时状态(transient):刚刚用new 语句创建,没有被持久化,无ID不处于 session中(没有使用session 的方法去操作临时对象),该对象称为临时对象。
持久化状态/托管状态(persistent):已经被持久化,加入到session 的缓存中,session 是没有关闭该状态的对象称为持久化对象
游离状态/脱管状态(detached):已经被持久化,但不处于session 中,该状态称为游离对象
删除状态(deleted):对象有关联的ID,并且在session 管理下,但是已经被计划(事务提交的时候,commit())删除。如果没有事务就不能删除。

//方法一:经理对员工的招聘,即往数据库中添加员工信息
public boolean insert(Worker worker) {
    //获取与数据库的连接
    Session session = hibernateUtils.getSession();
    //开启事务操作
    Transaction t = session.beginTransaction();
    //创建员工bean对象,添加要招聘员工信息
    Worker worker1 = new Worker();		//临时状态/瞬时状态(transient)
    worker.setId(worker.getId());
    worker.setName(worker.getName());
    worker.setAge(worker.getAge());
    worker.setSex(worker.getSex());
    worker.setPosition(worker.getPosition());
    //将数据存储到数据库中
    session.save(worker);		//持久化状态/托管状态(persistent)
    //提交事务
    t.commit();
    //关闭资源
    session.close();		//游离状态/脱管状态(detached)
    return true;
    session.delete();		//删除状态(deleted)
}

在这里插入图片描述

34. HIbernate 的缓存机制
1)、为什么要使用Hibernate 缓存
Hibernate 是一个持久化框架,经常访问物理数据源,但是访问物理数据源的效率是很低的。所以为了降低应用程序对物理数据源访的频次,从而提高应用程序的运行性能。
缓存内的数据时对物理数据源中的数据的复制。应用程序在运行时从缓存读写数据,在特定的时候或者事件会同步缓存和物理数据源的数据。为了提供访问速度,把磁盘或数据库访问变成内存访问。
2)、什么是Hibernate 缓存:分为两大类(一级缓存、二级缓存)
① hibernate 一级缓存又称为“session缓存”。
session 缓存内置不能被卸载,session 的缓存是事务范围的缓存(session 对象的生命周期通常对应一个数据库或者一个应用事务)。一级缓存中,持久化类的每个实例都具有唯一OID。
② Hibernate 二级缓存又称为“SessionFactory 缓存”。
由于SessionFactory 对象的生命周期和应用程序的整个过程对应,因此Hibernate 二级缓存是进程范围的或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供事务隔离级别。
第二级缓存是可选的,是一个可配置的小插件,默认下SessionFactory 不会开启这个小插件。
Hibernate 提供了org.hibernate.cache.CacheProvider 接口,它充当缓存插件与hibernate 之间的适配器。

总结:Hibernate 中的缓存分一级缓存和二级缓存。
一级缓存就是Session 级别的缓存,在事务范围内有效是内置的不可卸载的,二级缓存是SessionFactory 级别的缓存,从应用开启到应用结束都有效,是可选的,默认是没有二级缓存的,需要手动去开启。
保存数据库后,在内存中备份一份,如果更新了数据库的数据就要同步更新内存的数据

hibernate 二级缓存的使用场景:保存数据库后,在内存中备份一份,如果更新了数据库的数据就要同步更新内存的数据
	1、很少被修改的数据	(tiez帖子的最后回复时间)
	2、不是很重要的数据,允许出现偶尔并发的数据
	3、不会被并发访问的数据
	4、经常被查询的数据	(电商的地点)
	5、常量数据

扩展:hibernate 的二级缓存是不支持分布式缓存的,可以使用mencache、Redis 等中央缓存来代替二级缓存。

35. webservice 使用场景
webservice 是一个SOA(面向服务的编程)的架构,它不是依赖于语言,不依赖于平台,可以实现不同的语言间的互相调用,通过Internet 进行基于Http 协议的网络应用间的交互。
1)、异构系统(不同语言)的整合
2)、不同客户端的整合:PC端、移动端(IOS、Android、塞班)、移动微信端、PC微信端来访问。
在这里插入图片描述
36. Linux 学习
1)、Linux 是一个长时间运行比较稳定的操作系统,所以我们一般使用它作为部署服务器。
2)、Linux 本身具有C 编译环境,我们的一些软件是没有软件包(redis、nginx等)的,需要在Linux的C 编译环境中编译得到软件包。

常用命令:
	pwd——获取当前路径
	cd——跳转到目录
	su -u——切换到管理员
	ls ls——列举目录
文件操作命令:
	文件:
		tall——查看
		rm -rf——删除
		vi——修改
	文件夹:
		mkdir——创建文件夹
		rm -r——删除文件夹

3)、远程连接Linux 服务器
需要依赖于Linux 服务器安装ssh 服务端,一般这个ssh 服务端口为 22
使用ssh 客户端连接Linux 服务器,就像windows 下面的远程连接,但是Linux 通过ssh 连接上以后是没有图形界面,是命令行窗口。

连接ssh 客户端工具
	Putty、Xshell
使用sftp 客户端来连接sftp 服务端,来上传和下载文件(上传安装包,修改了配置文件上传等)
	Winscp、xftp
企业中常用的组合:putty + Winscp、Xshell + xftp

总结:使用Xshell、putty 等ssh 客户端来连接Linux 服务器,使用 xftp、Winscp 连接sftp 客户端来上传和下载文件,连接和上传、下载都必须依赖于服务器的ssh 、sftp 服务器,也就是Linux 服务器需要启动这两个服务。

37. 数据库优化的操作
1)定位:查找、定位慢查询
使用场景:在项目自验或项目转测试之前,在启动mysql 数据库时开启慢查询,并且把执行慢的语句写到日志中,在运行一定时间后,通过查看日志找到慢查询语句。

1、临时开启慢查询日志(如果需要长时间开启,则需要更改mysql配置文件,第6点有介绍)
#set global slow_query_log = on; 
注:如果想关闭慢查询日志,只需要执行 set global slow_query_log = off; 即可
	
2、临时设置慢查询时间临界点  查询时间高于这个临界点的都会被记录到慢查询日志中(如果需要长时间开启,则需要更改mysql配置文件,第6点有介绍)。
#set long_query_time = 1;
现在起,所有执行时间超过1秒的sql都将被记录到慢查询文件中(我这里就是 /data/mysql/mysql-slow.log)。
	
3、设置慢查询存储的方式(set globle log_output = file;)
	说明: 可以看到,我这里设置为了file,就是说我的慢查询日志是通过file体现的,默认是none,我们可以设置为table或者file,如果是table则慢查询信息会保存到mysql库下的slow_log表中
	
	 
	
4、查询慢查询日志的开启状态和慢查询日志储存的位置(show variables like '%quer%';)
	参数说明:
		slow_query_log : 是否已经开启慢查询
		slow_query_log_file : 慢查询日志文件路径
		long_query_time :  超过多少秒的查询就写入日志 
		log_queries_not_using_indexes 如果值设置为ON,则会记录所有没有利用索引的查询(性能优化时开启此项,平时不要开启)
	
5、使用慢查询日志示例(cat -n  /data/mysql/mysql-slow.log)
	从慢查询日志中,我们可以看到每一条查询时间高于1s钟的sql语句,并可以看到执行的时间是多少。
	比如上面,就表示 sql语句  select * from comic where comic_id < 1952000;  执行时间为3.902864秒,超出了我们设置的慢查询时间临界点1s,所以被记录下来了
	
6、永久设置慢查询日志开启,以及设置慢查询日志时间临界点
	linux中,mysql配置文件一般默认在 /etc/my.cnf
	更改对应参数即可。

2)、优化策略:

	1、创建索引:创建合适的索引,我们就可以实现索引中查询,查询到以后直接找对应的记录。/2、分表:当一张表的数据比较多或者一张表的某些字段的值比较多并且很少使用时,采用水平分表和垂直分表来优化、
	3、读写分离:当一台服务器不能满足需求时,采用读写分离的方式进行进群。
	4、缓存:使用redis 来进行设置中央缓存
	5、一些常用的优化技巧

3)、遵循范式
数据库表在设计时需要遵循范式:首先符合1NF,才能满足2NF,进一步满足3NF
① 1NF:表的列具有原子性、不可再分解,即列的信息,不能分解,只有数据库时关系型数据库(mysql、oracle、db2、sysbase、sql server),就自动的满足1NF,关系型数据库中式不允许分割列的。
② 2NF:表中的记录是唯一的,通常我们设计一个主键来实现
③ 3NF:即表中不要有冗余数据,就是说表的信息如果能被其他字段推导出来,就不应该单独的设计一个字段来存放。

反3NF:没有冗余的数据库未必是最好的数据库,有时候为了提高运行效率,就必须降低范式标准,适当保留冗余数据,具体做法是:在概念数据模型设计时遵循第三范式,降低范式标准的工作放到物理数据模型设计时考虑,降低范式就是增加字段,允许冗余。(如:订单和订单项、相册浏览次数和照片的浏览次数)

4)、存储引擎
在开发中,我们经常使用的存储引擎:MyIsam、INNODB、Memory

三大常用存储引擎
	1、MyIsam:如果表对事务的要求不高,同时是以查询和添加为主的,我们考虑使用MyIsam 存储引擎,比如 bbs 中的发帖表、回复表。
	2、INNODB:对事物要求高,保存的数据都是重要的数据,建议使用INNODB,(比如:订单表、账号表)
	3、Memory:数据变化频繁,不需要入库,同时又频繁的查询和修改,则可以考虑使用memory,速度极快。
MyIsam 和 Innodb 的比较
	1、事务安全:InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务; 
	2、查询和添加速度:InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快; 
	3、支持全文索引:Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高; 
	4、锁机制:MyIsam 支持表锁,而INNODB 支持行锁(事务)
	5、外键:InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
	注意:当数据库无法确定,所找的行时,也会变为锁定整个表。 
	如: update table set num = 10 where username like “%test%”;  

5)、创建索引
索引(index)是帮助DBMS 高效获取数据的数据结构
分离:普通索引、唯一索引、主键索引、全文索引
① 普通索引:允许重复的值出现
② 唯一索引:除了不能有重复的记录外,其他和普通索引一样(用户名、用户身份证、邮箱、tel)
③ 主键索引:是随着设定主键而创建的,也就是把某个列设为主键的时候,数据库就会修改创建索引,这就是主键索引,唯一且没有null 值
④ 全文索引:用来对表中的文本域(char、varchar、text)进行索引,全文索引针对MyIsam

	explain select * from test where match(title, body)against('database');		//会使用全文索引

6)、使用索引的小技巧
索引弊端
1、占用磁盘空间
2、对dml(插入、修改、删除)操作有影响,变慢。
使用场景:
满足一下条件的字段,才应该创建索引
a:肯定在where 条件该创建索引,如果不做查询没有意义
b:该字段的内容不是唯一的几个值(sex)
c:字段内容不是频繁变化
不会出现在where 子句中字段创建索引

具体技巧
	1、对于创建的多列索引(复合索引),不是使用的第一部分就不会使用索引。
		alter table test add index my_tables(dname, ioc);		//dname 左边的列,ioc 就是右边的列
		explain select * from test where dname=“aaa”;	//会使用索引
		explain select * from test where ioc=“aaa”;	//就不会使用索引
		
	2、对于使用 like 的查询,查询如果是‘%abc’ 就不会使用索引而 ‘abc%’ 就会使用索引。
		explain select * from test like dname="abc%";	//会使用索引
		explain select * from test like dname="%abc";	//就不会使用索引
		所以在like 查询时,‘关键字’的最前面不能使用(% 或者(_)这样的字符,如果一定要前面有变化的值,则考虑使用 全文索引 ——>sphinx
		
	3、如果条件中有 or,有条件没有使用索引,即使其中有条件带索引也不会使用,换言之,就是要求使用的所有字段,都必须在单独使用时能用索引时才会使用索引。
		explain select * from test where dname="abc";	//会使用索引
		explain select * from test where ioc="abc";	//就不会使用索引
		explain select * from test where deptno="123";	//会使用索引
		select * from test where dname="abc" or ioc="abc";
		select * from test where ioc="abc" or deptno=100;
		select * from test where dname="abc" or ioc="abc" or deptno=100;
	
	4、如果列类型是字符串,那一定要在条件中将数据使用引号引起来,否则不使用索引
		explain select * from test where dname="abc";	//会使用索引
		explain select * from test where dname=123;	//数字自定转换字符串
		explain select * from test where dname=abc;	//会报错
	
	5、如果mysql 估计使用全表扫描要比使用索引要快,则不使用索引(比如:表里只有几条记录)

7)、分表:水平分表(按行)、垂直分表(按列)
根据经验,MySQL 表数据一般达到百万级别,查询效率会很低,容易造成表锁,甚至堆积很多连接(因为操作慢),直接崩溃;水平分表就能够很大程度上减少这些压力。
如果一张表的字段非常多(长文本、二进制),而且只有在很少的情况下会查询,这时候就可以把字段多个单独放到一个表,通过外键进行关联起来。

水平分表策略:
	1、按时间分表
		这种分表方式有一定的局限性,当数据有较强的时效性,如微博发送记录、微信消息记录等等,这种数据很少有用户会查询几个月的数据记录,就可以按月分表了。
	2、按区间范围分表
		一般在有严格的自增 id 需求上,如按照 user_id 水平分表:
		table_1 user_id between 1~100w
		table_2 user_id between 101~200w
		table_3 user_id between 201~300w
	3、hash 分表(最常用)
		通过一个原始目标的 ID 或者名称通过一定的hash 算法计算出数据存储表的表名,然后访问相应的表。

8)、读写分离
一台数据库支持的最大并发连接数是有限的,如果用户并发访问太多,一台服务器满足不了需求时就可以集群处理(读写分离就是其中之一),MySQL 的集群处理技术最常用的就是读写分离。

1、主从同步:
	数据库最终会把数据库持久化到磁盘中,如果集群必须确保每个数据库服务器的数据是一致性的,能改变数据库数据的操作都往主数据库去写,而其他的数据库从主数据库上同步数据。
2、读写分离:
	使用负载均衡来实现写的操作都往主数据库中去,而读的操作都往从数据库中去。

9)、缓存
在持久层(dao)和数据库(db)之间添加一个缓存,如果用户访问的数据已经缓存起来时,在用户访问时直接从缓存中获取,不用访问数据库,而缓存是操作内存级别的,访问速度快。
作用:减少数据库服务器压力,减少访问时间。
java 中常用的缓存有:
① hibernate 的二级缓存,该缓存不能完成分布式缓存。
在这里插入图片描述
② redis 来作为中央缓存。
对缓存数据进行集中处理
在这里插入图片描述
10)、优化小技巧

1、DDL 优化:
	1、通过禁用索引来提供导入数据性能,这个操作主要针对有数据库的表,追加数据
		//去除键
		alter table test DISABLE keys;
		//批量插入数据
		insert into test select * from test;
		//恢复键
		alter table test ENABLE keys
	2、关闭唯一键
		set unique_checks=0;		//关闭
		set unique_checks=1;		//开启
	3、修改事务提交方式(导入)(变多次提交为一次)
		set autocommit=0;	//关闭
		//批量插入
		set autocommit=1;	//开启
		
2、DML优化
	insert into test values(1, 3);
	insert into test values(2, 4);
	insert into test values(3,5);
	//合并多条为一条
	insert into test values(1, 3), (2, 4), (3, 5);
	
3、DQL优化
	3.1、order by 优化
			①、多用索引排序
			②、普通结果排序(非索引排序)Fliesort
	3.2、group by 优化
			使用 order by null,取消默认排序
	3.3、子查询优化
			在客户列表找到不在支付列表的客户
			#在客户列表找到不在“支付列表”的客户,查询没有买过东西的客户
			explain
			select * from customer where customer_id not in (select DISTINCT customer_id from payment); 	#子查询			——基于 funct 外链
			explain
			select * from customer c left join payment p on(c.customer_id = c.customer_id) where p.customer_id is null; 	#子查询			——基于 "索引" 外链

4、or 优化(在两个独立索引上使用 or 的性能优于)
	4.1、or 两边都是用索引字段做判断,性能好
	4.2、or 两边,有一边不用,性能差
	4.3、如果employee 表的 name 和email 这两列是一个复合索引,但是如果是:name=‘张三’ OR email = ‘李四’ 这种方式,不会使用索引;

5、Limit 优化
	select film_id, description from film order by title limit 50, 5;		//分页查询
	select a.film_id, a.description from film a inner join(select film_id from film order by title limit 50, 5)b on a.film_id=b.film_id;

11)、jdbc 批量插入几百万条数据
① 变多次提交为一次
② 使用批量操作
③ 像这样的批量插入操作能不使用代码操作就不使用,可以使用存储过程来实现。

###构建一个存储过程
create procedure insert_Person(_name varchar(255), _age int, out_id int)	//为存储过程设置字段
begin	//开启存储过程
	insert into person value(null, _name, _age);	//存储过程的插入操作
	select max(per_id) into _id from person;		//存储过程的查询操作
end;		//关闭存储过程
call insert_Person('zhangsan', 20, @id);		//执行插入操作
select @id;		//执行查询操作

######################################################################################
public static DataSource ds = null;
		//获取DBCP数据源实现类对象
		BasicDataSource bds = new BasicDataSource();
		//设置连接数据库需要的配置信息
		bds.setDriverClassName("com.mysql.jdbc.Driver");
		bds.setUrl("jdbc:mysql://localhost:3306/driver");
		bds.setUsername("root");
		bds.setPassword("123456");
		//设置连接池的参数
		bds.setInitialSize(5);
		bds.setMaxActive(5);
		ds = bds;
		Connection cn = ds.getConnection();
		con.setAutoCommit(false);		//设置关闭自动提交功能
		cstmt = cn.prepareCall("{call insert_Person(?, ?, ?)});
		cstmt.registerOutParameter(3, Types.INTEGER);//第三个参数自增
		for(int i=0; i< 2000000; i++){
			cstmt.setString('张'+i);
			cstmt.setInt(25);
		}
		cstmt.executeBatch();		//批量提交
		cstmt.close();

38. Redis
1)、使用场景:
① 缓存:常用查询很少修改操作的数据,放到读速度很快的空间(内存)中,以便下次访问减少时间,减轻压力,减少访问时间
② 计数器:
redis 中的计数器是原子性的内存操作,可以解决库存溢出问题,进销存系统库存溢出
③ session 缓存服务器:web 集群时作为 session 缓存服务器缓存队列等
在这里插入图片描述
2)、对象保存方式
① json 字符串:需要把对象转换成json 字符串,当做字符串处理,直接使用set、get 来设置值(优点:设置获取比较简单,缺点:没有提供专门的方法,需要把对象转换为json,只能使用第三方插件——jsonlib)
② 字节:需要做序列化,就是把对象序列化为字节保存
如果是数据完全可以使用JSON 格式,毕竟redis 直接set、get 使用起来门槛低很多,redis是没有提供专门的设置对象方法,需要自己进行改写。
如果是担心JSON 转对象会消耗资源问题的情况,这个问题需要考量几个地方
第一点:就是用得JSON 转换lib 是否存在性能问题。
第二点:就是数据量的级别,如果是存储百万级别的大数据对象,建议采用存储序列化对象方式,如果是少量的数据对象,或者是数据对象字段不多,还是建议使用JSON 转换成String方式。
毕竟redis 对存储字符类型这部分优化的非常好,具体采用哪种方式与方法,还要看所处的场景。

3)、redis 数据淘汰机制
在redis 中,允许用户设置最大使用内存大小 server_maxmemory,在内存限定的情况下是很有用的。譬如,在一台8G 的机器上部署了4 个redis 服务点,每个服务点分配1.5G 的内存大小,减少内存紧张的情况,由此获取更为稳健的服务。
内存大小是有限的,需要保存有效的数据,redis 内存数据集大小上升到一定大小的时候,就会实施数据淘汰策略,redis 提供了6 种淘汰策略:

redis 提供了6 种淘汰策略:
	1、volatile-lru:从已设置过去时间的数据集(server.db[i].expires)中挑选最少使用的数据淘汰
	2、volatile-ttl:从已设置过去时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
	3、volatile-random:从已设置过去时间的数据集(server.db[i].expires)中任意挑选数据淘汰
	4、allkeys-lru:从数据集(server.db[i].dict)中挑选最少使用的数据淘汰
	5、allkeys-random:从数据集(server.db[i].dict)中任意挑选数据淘汰
	6、no-enviction(驱逐):禁止驱逐数据
	reredis 确定驱逐某个jianzhi键值对后,会删除这个数据,并将这个数据变更消息发布到本地(AOF 持久化)和从机(主从连接)。

4)、Redis 的访问和集群
① 使用jedis java客户端来访问 redis 服务器,优点类似通过jdbc 访问 mysql 一样。
② 当然如果 spring 进行集群时,可以使用spring data 来访问redis,spring data 只是对jedis 的二次封装,类似于 jdbcTemplate 和 jdbc 一样。
③ 当一台数据库无满足要求时,可以使用redis 集群啦处理,类似于mysql 的读写分离。

39. 数据库事务的隔离级别
1)、数据库事务并发问题

假设现在有两个事务:Transation01 和 transaction02 两个事务并发执行
1、脏读——读取到了无效的数据
	① Transaction01 将某条记录的AGE 值从20修改为了30
	② Transaction02 读取了 Transaction01修改后的值:30
	③ Transaction01 回滚,AGE 值恢复到了20
	④ Transaction02 读取到的值 30 就是一个无效的值
2、不可重复读
	① Transaction01 读取了 AGE 值为 20
	② Transaction02 将 AGE 值更新为 30
	③ Transaction01 再次读取 AGE 值为30,和第一次读取的数据不一致
3、幻读
	① Transaction01 读取了 STUDENT 表中的一部分数据
	② Transaction02 向 STUDENT 表中插入了一部分数据
	③ Transaction01 再次读取 STUDENT 表时,多出了一部分数据

2)、事务隔离级别
数据库系统必须具有在多个并发运行事务时的隔离能力,使他们不会互相影响,避免各种并发问题。一个事物与其他事务隔离的程度称为隔离级别。SQL 标注中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱。

隔离级别:
	① 读未提交:READ UNCOMMITTED
			允许 Transaction01 读取Transaction02 未提交的修改
	② 读已提交:READ COMMITTED
			要求Transaction01 只能读取Transaction02 已提交的修改
	③ 可重复读:REPEATABLE READ
			确保Transaction01 可以多次从一个字段中读取到相同的值,即Transaction01 执行期间禁止其它事务对这个字段进行更新。
	④ 串行化:SERIALIZABLE
			确保Transaction01 可以多次从一个表中读取到相同的行数据,在Transaction01 执行期间,禁止其他事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

3)、各个隔离级别解决并发问题的能力见下表

脏读不可重复读幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

4)、各种数据库产品对事务隔离级别的支持程度

oraclemysql
READ UNCOMMITTED×
READ COMMITTED√(默认)
REPEATABLE READ×√(默认)
SERIALIZABLE
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值