框架之后面试题

文章目录

8. Java8新特性

1)Lambda表达式和函数式接口

Lambda表达式(也称为闭包)是Java 8中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理:函数式开发者非常熟悉这些概念。很多JVM平台上的语言(Groovy、Scala等)从诞生之日就支持Lambda表达式,但是Java开发者没有选择,只能使用匿名内部类代替Lambda表达式。

2)接口的默认方法和静态方法

Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。默认方法使得接口有点类似traits,不过要实现的目标不一样。默认方法使得开发者可以在 不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。

默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写

3)方法引用

方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。

4)重复注解

自从Java 5中引入注解以来,这个特性开始变得非常流行,并在各个框架和项目中被广泛使用。不过,注解有一个很大的限制是:在同一个地方不能多次使用同一个注解。Java 8打破了这个限制,引入了重复注解的概念,允许在同一个地方多次使用同一个注解。

在Java 8中使用**@Repeatable**注解定义重复注解

9. 你知道哪些数据结构?

​ 线性表

​ 队列

​ 栈和堆

​ 二叉树

​ 二叉树可以为空。二叉树结点的子树要区分左子树和右子树,即使只有一棵子树也要进行区分,说明它是左子树,还是右子树。这是二叉树与树的最主要的差别。

web前端

14. HTTP 中 GET、POST请求的区别

1、GET请求: 请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。URL的编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码之后再传输。
POST请求:POST请求会把请求的数据放置在HTTP请求包的包体中。
因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。

2、传输数据的大小
在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。因此,在使用GET请求时,传输数据会受到URL长度的限制。
对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的配置。
3、安全性
POST的安全性比GET的高。这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全仅仅是不修改服务器的数据。比如,在进行登录操作,通过GET请求,用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人查看浏览器的历史记录的原因,此时的用户名和密码就很容易被他人拿到了。

2.$.ajax( ) 方法常见参数属性

$.ajax({
	url: ...,
	method: ...,
	contentType: ...,
	data: ...,
	dataType: ...,
	success : function(result) { ... }
});

属性名 类型 说明
url string 所请求的 URL
method string 请求方式
contentType string 所发送的参数数据的格式/类型,默认值为: application/x-www-form-urlencoded
data string 所发送的参数数据
dataType string (预期的)服务器返回的数据格式/类型
success function 成功收到服务器回的数据之后执行的回调函数
对于 get 请求

contentType: 忽略(即使用默认值)。
data: 可忽略,因为 get 请求的参数通常是拼接在 URL 之后。

 var parentId=$("#p").val();
 $.ajax({
	url:'/sxnd-erp/api/cat/'+parentId,
	type:'get',
	dataType:'json',
	success:function(data){
		var t="<option value='0'>----二级标题----</option>";
		for(var i=0;i<data.rows.length;i++){
			t+="<option value="+data.rows[i].id+">"+data.rows[i].name+"</option>";
		}
		$("#c").html(t);
	 },
	error:function(data){
		alert("二级标题!");
	 }
})

对于 post 的普通请求

method : post
contentType: 忽略(即使用默认值)
data: 格式为 key1=val1&key2=val2&key3=val3&… 如此键值对的形式

对于 post 的 json 请求

method : post
contentType: application/json 表示传递的参数是 json 格式的字符串,而非传统形式。
data: JSON.stringify(obj) ,一个符合JSON格式规则的字符串,通常是直接来自 jOSN 对象。
dataType
dataType 是预期的服务器回的数据的格式。例如有: text/html (默认值,表示是 HTML格式的字符串)和 application/json (表示是 JSON 格式的字符串).

$.ajax({
	url:'/sxnd-erp/api/cat',
	type:'post',
	data:{'id':parentId},
	dataType:'json',
	success:function(data){
		var t="<option value='0'>----三级标题----</option>";
		for(var i=0;i<data.rows.length;i++){
			t+="<option value="+data.rows[i].id+">"+data.rows[i].name+"</option>";
		}
		$("#a").html(t);
	 },
	error:function(data){
		alert("三级标题!");
	 }
})

JSP

一、服务器

1. Tomcat,Apache,JBoss 的区别?

Apache解析静态的html文件;Tomcat可解析jsp动态页面、也可充当servlet容器。

Apache是Http服务器;Tomcat是web服务器,只支持jsp+servlet

JBoss是应用服务器,支持EJB. Weblogic,WebSphere

2. 虚拟目录

设置虚拟路径的优势有两点。

第一、我们开发一个项目,在未定版之前需要进行无数次的修改,如果部署测试项目的时候直接将项目打成war包放到webapps目录下,那么每一次版本变更都需要将新的版本放到webapps目录下覆盖原先的版本,这样对于我们测试项目来说是不是非常麻烦呢?如果建立一个虚拟路径,直接映射到项目原件,这样一来在测试阶段是非常的方便

第二、Tomcat是一个服务器,既然是一个服务器是不是就有容量这么一说呢?虽然这个容量是你说了算,但是再怎么大是不是也有盘符容量限制?如果将所有的项目都部署到webapps目录下,当项目比较大而且项目较多的时候是不是需要考虑一下盘符的感受呢?所以使用虚拟路径也可以为搭载Tomcat服务器的盘符分压

添加虚拟目录

tomcat6\conf\server.xml

​ 在之上进行配置

path=“/test”表示此虚拟目录的名称:http://localhost:8080/test

docBase=“d:/testweb”表示虚拟目录在硬盘上的绝对路径

3. HTTP响应的结构是怎么样的?

HTTP 响应由三个部分组成:

状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果 Servlet 没有返回状态码,默认会返回成功的状态码 HttpServletResponse.SC_OK

HTTP 头部(HTTP Header):它们包含了更多关于响应的信息比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。如何在 Serlet中检索 HTTP 的头部看这里。

主体(Body):它包含了响应的内容。它可以包含 HTML 代码,图片,等等。主体是由传输在HTTP 消息中紧跟在头部后面的数据字节组成的

二、 JSP

1. jsp有哪些内置对象?

哪些作用域?

作用分别是什么?

内置对象名 类型 作用
request HttpServletRequest 用户端请求,此请求会包含来自 GET/POST 请求的参数
response HttpServletResponse 网页传回用户端的回应
config ServletConfig 与 jsp 配置对象的对象, 一般无用
application ServletContext 与当前应用对应的 ServletContext 对象, 应用中只有一个
session HttpSession 与请求有关的会话期
exception Throwable 针对错误网页,未捕捉的异常对象
page Object(this) jsp 对应的 Servlet 对象
out JspWriter 用来传送回应的输出 {}<%=%>
pageContext PageContext 网页的属性是在这里管理
范围
page域 当前jsp页面使用
request域 同一个请求中使用
session域 同一个会话(session对象)中使用
context域 同一个web应用中使用

2. request 中 getParameter 和 getAttribute 的区别?

首先 request对象 代表客户端的一次请求,可以用它来存储客户端请求的一些参数。

getParameter()方法:
​ 该方法是用来接收从客户端通过 get 或者 post 传递过来的参数,它的返回值类型永远是字符串类型 ;
注意:这里强调的是客户端请求时,是客户端发送给服务器的参数,这个赋值动作是由客户端完成的。

用户名: 密码:

这里的请求参数有userName和password

getAttribute()方法 :
​ 该方法用于获取 request对象 中的 attribute值,这个值是之前在服务器端才放入到 request对象 里的,即通过setAttribute(key ,value)放入 request
注意:这里强调的是attribute中的值是在服务器端赋予的,而非客户端送过来的。

其实 getParameter( ) 和 getAttribute( ) 最简单的两点区别就是 :
1)赋值方式不一样,前者是客户端将请求参数值送给服务器端,而后者则是在请求到达服务器端之后,在服务器进行存放进去
2)两者的返回值类型不一样,前者永远返回字符串,后者返回任意对象

问题
既然 parameter 和 attribute 都是传递参数,为什么不直接使用parameter呢?
原因有2:
1)从上面分析可以找到 getParameter 获取的是客户端发送的参数,而且在服务器端不能通过 setParameter(key, value)来添加参数,因为没有这个函数 。
所以如果需要在服务器端进行跳转,并需要想下个页面发送新的参数时,则没法实现。但是attribute可以,可以通过setAttribute(),将值放入到request对象,然后在其他页面使用getAttribute获取对应的值,这样就达到一次请求可以在多个页面共享一些对象信息

2)parameter返回值是字符串,意味着不能传递其他的对象,如List,但是attribute则可以存放任意类型的Java对象

2. 在JSP中提供了四种属性保存范围

在一个页面范围内:pagecontext

在一次服务器请求范围内:request

在一次会话范围内:session

在一个应用服务器范围内:application

3. JSP请求是如何被处理的?

​ 浏览器首先要请求一个以 .jsp 扩展名结尾的页面,发起 JSP 请求,然后,Web 服务器读取这个请求,使用 JSP 编译器把 JSP 页面转化成一个 Servlet 类。需要注意的是,只有当第一次请求页面或者是 JSP 文件发生改变的时候 JSP 文件才会被编译,然后服务器调用 servlet 类,处理浏览器的请求。一旦请求执行结束,servlet 会把响应发送给客户端。这里看下如何在 JSP中获取请求参数。

​ => 客户端发起jsp请求

​ => web服务器接收请求,并将jsp文件编译成servlet类

​ => 服务器调用servlet类

​ => servlet将响应发送到客户端

4. jsp有哪些动作?作用分别是什么?

jsp:include:在页面被请求的时候引入一个文件。

jsp:forward:把请求转到一个新的页面。

jsp:useBean:寻找或者实例化一个 JavaBean。

jsp:setProperty:设置 JavaBean 的属性。

jsp:getProperty:输出某个 JavaBean 的属性。

5. JSP中动态 include与静态 include的区别?

  1. 动态包含: 用<jsp:include >, 包含的动作是在 jsp 对应的 Serlet 处理请求时去执行的,每次请求都会执行.
  2. 静态包含: 用 include 指令, 包含的动作是在 jsp 被编译成 java 文件时执行的,只有第一次请求时执行.

6. Cookie

Cookie是服务器端保存在客户端的一组资源。

应用: 在登陆时会问是否要记住密码,或是在一定时间不用再登陆,这样的功能就是通过Cookie实现的。

​ public void addCookie(Cookie cookie)

​ Cookie是通过服务器设置到客户端上去的,用response对象。

​ 如果要在服务器端取得Cookie,用request对象。

​ 如果要Cookie长留在本地计算机上,可以设置Cookie的保存时间。(此时就可以在新的页面中取出Cookie的值)

​ Cookie是保存在客户端上的信息,安全性较差,不要把过多的隐秘信息保留在Cookie中,这样不安全。

​ Cookie可以用setMaxAge设置最大保留时间

​ 服务器端在客户端第一次访问之后,会设置一个默认的Cookie在客户端上: JsessionID

7. Session

​ session对象: 主要用于保存用户的各种信息,直到它的生命周期(一般为900s)超时或人为释放掉为止。用户只要一连接到服务器,就立刻分配一个session给用户。

​ 可以通过session对象来判断此用户是否是合法用户

Session的主要方法:

 服务器上通过 sessionID 来区分不同的用户, 任何连接到服务器上的用户,服务器都会为之分配唯一的一个不会重复的sessionID。session.getId() ,长度为:32

 判断是否是新的session。Session.isNew( )

 设置属性:

​ public void setAttribute(String name,Object value)

 取得属性:

​ public Object getAttribute(String name)

 删除属性

​ public void removeAttribute(String name)

让用户的session失效

​ Session.invalidate()

 取得用户最后一次操作的时间

​ public long getLastAccessedTime()

获得创建时间

​ public long getCreationTime();

8. 说说你对 Cookie与 Session技术的理解?

Session和cookie都是会话跟踪技术。

cookie 是一种浏览器端的缓存技术, 而 Session 是一种服务器端的缓存技术(依赖 cookie)

session和cookie用于跨网页共享数据

session:记录独有的个人信息,在不同的页面中传递。

主要方法:setAttribute(),getAttribute()

cookie:保存于客户端,供浏览器与Web服务器互通数据用的纯文字文件。当IE执行时,会在计算机中产生一个cookie。

session将信息保存在服务器上,而cookie保存在客户端上。

session比cookie更安全,session比cookie更占用资源。

开发原则: session少用。尽量少向session中保存信息。Session使用了cookie的机制,如果cookie禁用,则session也无法使用。此时可以使用URL地址重定向解决。

当客户端第一次访问web应用或者第一次使用 request.getSession( ) 获取 HttpSession 时,Servlet容器会创建Session,生成一个long类型的唯一ID(你可以使用session.getId()获取它)并把它保存在服务器的内存里。Servlet容器同样会在HTTP响应里设置一个Cookie,cookie的名是JSESSIONID并且cookie的值是session的唯一ID。

根据HTTP cookie规范(正规的web浏览器和web服务器必须遵守的约定),在cookie的有效期间,客户端(web浏览器)之后的请求都要把这个cookie返回给服务器。Servlet容器会利用带有名为JSESSIONID的cookie检测每一个到来的HTTP请求头,并使用cookie的值从服务器内容里获取相关的HttpSession。

HttpSession会一直存活着,除非超过一段时间没使用。你可以在web.xml里设定这个时间段,默认时间段是30分钟。因此,如果客户端已经超过30分钟没有访问web应用的话,Servlet容器就会销毁Session。之后的每一个请求,即使带有特定的cookie,都再也不会访问到同一个Session了。servletcontainer会创建一个新的Session。

另外,在客户端的session cookie拥有一个默认的存活时间,这个时间与浏览器的运行时间相同。因此,当用户关闭浏览器后(所有的标签或者窗口),客户端的Session就会被销毁。重新打开浏览器后,与之前的Session关联的cookie就再也不会被发送出去了。再次使用request.getSession()会返回一个全新的HttpSession并且使用一个全新的session ID来设置cookie。

购物车最好使用cookie,但是cookie是可以在客户端禁用的,这时候我们要使用cookie+数据库的方式实现,当从cookie中不能取出数据时,就从数据库获取。

9. 为什么说 session 依赖 cookie,以及 cookie 的常用知识

​ 平时使用浏览器访问接口,很难注意到cookie和session的区别与联系,使用curl就把区别暴露了出来.
上面的curl交互可以看出 session是会以 set-cookie的方式 设置到浏览器中,之后的访问都会将 cookie 带上请求其他接口,若使用 curl 而不带上 cookie 就会导致错误,这也是为什么说 session 是依赖 cookie 存在的.

  1. cookie存在的目的:

    因为http协议属于无状态协议,它不跟踪从一个客户端到另一个客户端的请求信息.也就是说即使第一次和服务器连接后并且登录成功,第二次请求服务器依然不知道请求的是哪个用户.
    所以使用cookie来解决这个问题:第一次登录成功后,服务器返回cookie给浏览器,然后浏览器保存在本地,当用户发送第二次请求时,就会自动的把上次请求存储的cookie数据携带给服务器,服务器再根据cookie数据判断是哪个用户.

  2. session和cookie的作用类似:
    也是用来存储用户相关的信息,存储在服务器端.把用户的信息经过加密后存储在session中,然后产生一个唯一的session_id.

  3. cookie和session的结合使用

    一般有两种存储方式:

     (1) 存储在服务器端:通过cookie存储一个session_id,然后具体的数据则是保存在session中.如果用户已经登录,则服务器会在cookie中保存一个session_id,下次再请求时,会把该session_id携带上来,服务器根据session_id在session库中获取用户的session数据,就知道用户到底是谁了.以及之前保存的一些状态信息,这种专业术语叫做server side session.
    
     (2) 存储在客户端:将session数据加密,然后存储在cookie中.这种专业术语叫做 client side session.flask框架采用的就是这种方式,但是可以替换成其他形式.
    

9. 自动登陆功能的编码实现?

  1. 登陆功能是用 Session 实现的,就是向 Session 对象中保存当前用户的对象
  2. 自动的功能由Cookie 实现, 在登陆时将用户的信息保存为持久化 Cookie
  3. 下次访问时, 读取请求中如果有用户信息的 Cookie 就可以自动登陆

10. 如何防止表单重复提交?

答: 使用 Session 技术:

  1. 在 regist.jsp 页面中生成一个唯一随机值, 将其保存到 Session 中, 同时将其保存为表单的隐藏域的值
  2. 在处理注册的请求时,获取 Session 中值,获取请求参数的值,比较两者是否相同, 如果相同说明不是重复提交,请求通过同时删除 session 中保存的值,如果不相同则是重复提交, 不能通过。

11. 结合项目谈谈你对MVC的理解

MVC是Model—View—Controler的简称。即模型—视图—控制器。MVC是一种设计模式,它强制性的把应用程序的输入、处理和输出分开。

MVC中的模型、视图、控制器它们分别担负着不同的任务。

​ 视图: 视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并接受用户的输入。视图不进行任何业务逻辑处理。

​ 模型: 模型表示业务数据和业务处理。相当于JavaBean。一个模型能为多个视图提供数据。这提高了应用程序的重用性

​ 控制器: 当用户单击Web页面中的提交按钮时,控制器接受请求并调用相应的模型去处理请求。

然后根据处理的结果调用相应的视图来显示处理的结果。

MVC的处理过程:首先控制器接受用户的请求,调用相应的模型来进行业务处理,并返回数据给控制器。控制器调用相应的视图来显示处理的结果。并通过视图呈现给用户。

如在项目中要对应MVC的话:View 对应项目中Jsp,Controler对应Action,Model 对应service+dao层的业务逻辑和持久层的操作。

JDBC

1.简单说一下你对JDBC(Java DataBase Connection)的理解

​ java数据库连接,数据库关系系统很多,每个数据库关系管理系统支持的命令是不一样的.
Java只定义接口,让数据库厂商自己实现接口,对于我们开发人员而言,只需要导入对应厂商开发的实现即可,然后以接口的方式进行调用(mysql+mysql驱动(实现)+jdbc)

2. JDBC 调用存储过程

用JDBC来实现访问数据库记录可以采用下面的几个步骤

​ 加载驱动
​ 获取连接
​ 设置参数
​ 执行
​ 释放连接

  1.     通过驱动器管理器获取连接接口(Connection)。
    
  2.     获得Statement或它的子类。
    
  3.     指定Statement中的参数。
    
  4.     通过Statement发送SQL语句。
    
  5.     检查并处理返回的结果。
    
  6.     关闭Statement。
    
  7.     关闭连接
    

3. 写一个JDBC的访问Oracle的列子

public class OracleUtils {
static {
    try {
        Class.forName("oracle.jdbc.driver.OracleDriver");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

//获取连接
public static Connection getConnection() throws SQLException { 
    Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@ip:1521:orcl", "username", "password");
    return conn;
}
//关闭资源
public static void close(ResultSet rs, Statement stmt, Connection conn) {
}
}
.....
调用
Connection conn = OracleUtils.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
OracleUtils.close(rs, stmt, conn);

4. Class.forName的作用?

为什么要用?

​ 按参数中指定的字符串形式的类名去搜索并加载相应的类,如果该类字节码已经被加载过,则返回代表该字节码的Class实例对象,否则,按类加载器的委托机制去搜索和加载该类,如果所有的类加载器都无法加载到该类,则抛出ClassNotFoundException。

​ 加载完这个Class字节码后,接着就可以使用Class字节码的 newInstance 方法去创建该类的实例对象了。

​ 有时候,我们程序中所有使用的具体类名在设计时(即开发时)无法确定,只有程序运行时才能确定,这时候就需要使用 Class.forName 去动态加载该类,这个类名通常是在配置文件中配置的.

​ 例如,spring的IOC中每次依赖注入的具体类就是这样配置的,jdbc的驱动类名通常也是通过配置文件来配置的,以便在产品交付使用后不用修改源程序就可以更换驱动类名。

5. 用JDBC如何调用存储过程?

​ CallableStatement 用来执行存储过程。存储过程是由数据库存储和提供的。存储过程可以接受输入参数,也可以有返回结果。非常鼓励使用存储过程,因为它提供了安全性和模块化。准备一个CallableStatement 的方法是:CallableStament.prepareCall( );

6. 使用JDBC操作数据库时,如何提升读取数据的性能?

如何提升更新数据的性能?

​ 要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的 setFetchSize() 方法指定每次抓取的记录数(典型的空间换时间策略);

​ 要提升更新数据的性能可以使用PreparedStatement语句构建批处理,将若干SQL语句置于一个批处理中执行。

7. Statement接口与PreparedStatement接口区别

Statement类的对象将SQL语句发送给数据库,如果SQL语句运行后产后结果集,Statement对象会将结果集返回 给一个ResultSet对象。

PreparedStatement可以接受一个带有占位符 ? 的查询语句,并且 PreparedStatement 对象会将传入的SQL语句进行编译并暂时保存在内存中。

动态SQL语句:就是在SQL语句中可以提供参数。

SQL 注入是用户利用某些系统没有对输入数据进行充分的检查,从而进行恶意破坏的行为。

PreparedStatement 是预编译的,可以防止sql注入

8. executeQuery()和executeUpdate()区别

executeQuery()方法是处理查询操作,当用select对数据库做查询时,用该方法。且executeQuery()方法会建立一个ResultSet的对象保存查询的结果。

Insert、Update、delete等操作,可用executeUpdate()方法执行。

9. 用 JDBC 查询学生成绩单,把主要代码写出来

Connection cn = null;
PreparedStatement pstmt =null;
Resultset rs = null;
try
{
   Class.forname(driveClassName);
   cn =  DriverManager.getConnection(url,username,password);
   pstmt = cn.prepareStatement(“select  score.* from score ,student “ + 
            “where score.stuId = student.id and student.name = ?);
   pstmt.setString(1,studentName);
   Resultset rs = pstmt.executeQuery();
   while(rs.next())
   {
      System.out.println(rs.getInt(“subject”)  +  “ ” + rs.getFloat(“score”) );
   }
}catch(Exception e){e.printStackTrace();}
finally{
   if(rs != null) try{ rs.close() }catch(exception e){}
   if(pstmt != null) try{pstmt.close()}catch(exception e){}
   if(cn != null) try{ cn.close() }catch(exception e){}
}

10. 在进行数据库编程时,连接池有什么作用?

答:由于创建连接和释放连接都有很大的开销(尤其是数据库服务器不在本地时,每次建立连接都需要进行TCP的三次握手,释放连接需要进行TCP四次握手,造成的开销是不可忽视的),为了提升系统访问数据库的性能,可以事先创建若干连接置于连接池中,需要时直接从连接池获取,使用结束时归还连接池而不必关闭连接,从而避免频繁创建和释放连接所造成的开销,这是典型的用空间换取时间的策略(浪费了空间存储连接,但节省了创建和释放连接的时间)。池化技术在Java开发中是很常见的,在使用线程时创建线程池的道理与此相同。基于Java的开源数据库连接池主要有:C3P0、DBCP、Druid等。

补充:在计算机系统中时间和空间是不可调和的矛盾,理解这一点对设计满足性能要求的算法是至关重要的。大型网站性能优化的一个关键就是使用缓存,而缓存跟上面讲的连接池道理非常类似,也是使用空间换时间的策略。可以将热点数据置于缓存中,当用户查询这些数据时可以直接从缓存中得到,这无论如何也快过去数据库中查询。当然,缓存的置换策略等也会对系统性能产生重要影响

11.数据库连接池的作用

  1. 限定数据库的连接个数,提升系统访问数据库的性能。
  2. 数据库连接不需要每次都去创建或销毁,节约了资源。
  3. 数据库连接不需要每次都去创建,响应时间更快。

12. 什么是DAO(Data Access Object)模式

答:DAO是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作。在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中。用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储。DAO模式实际上包含了两个模式,一是Data Accessor(数据访问器),二是Data Object(数据对象),前者要解决如何访问数据的问题,而后者要解决的是如何用对象封装数据。

13. 事务的ACID是指什么?

- 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;

- 一致性(Consistent):事务结束后系统状态是一致的;

- 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;

- 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。

关于事务,在面试中被问到的概率是很高的,可以问的问题也是很多的。首先需要知道的是,只有存在并发数据访问时才需要事务。当多个事务访问同一数据时,可能会存在5类问题,包括3类数据读取问题(脏读、不可重复读和幻读)和2类数据更新问题(第1类丢失更新和第2类丢失更新)。

事务并发会引起什么问题,怎么解决?

事务并发会引起 脏读,幻读,不可重复读等问题, 设定事务的隔离级别可以解决

脏读(Dirty Read):A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的数据就是脏数据。

不可重复读(Unrepeatable Read):事务A重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务B修改过了。

幻读(Phantom Read):事务A重新执行一个查询,返回一系列符合查询条件的行,发现其中插入了被事务B提交的行

13. JDBC中如何进行事务处理?

答:Connection提供了事务处理的方法,通过调用 setAutoCommit(false) 可以设置手动提交事务;

当事务完成后用commit()显式提交事务;

如果在事务处理过程中发生异常则通过 rollback( ) 进行事务回滚。

除此之外还引入了Savepoint(保存点)的概念,允许通过代码设置保存点并让事务回滚到指定的保存点。

14. JDBC 能否处理 Blob 和 Clob ?

答: Blob是指二进制大对象(Binary Large Object),而Clob是指大字符对象(Character Large Objec),因此其中Blob是为存储大的二进制数据而设计的,而Clob是为存储大的文本数据而设计的。JDBC的 PreparedStatement 和 ResultSet 都提供了相应的方法来支持Blob和Clob操作。

Servlet

1. 说说你对Servlet的理解

Servlet是一个运行在Web服务器上的Java程序,用来接收和响应从客户端发送过来的请求,通常使用HTTP协议.
使用:1、编写一个Java类,实现servlet接口。
  2、把开发好的Java类部署到web服务器中。
按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet
​ Servlet :接口
​ |
​ GenericServlet :通用的Servlet
​ |
​ HttpServlet :HttpServlet

  • 编写一个类继承HttpServlet,重写doGet和doPost方法.
  • 配置

1.1 HTTP响应的结构是怎么样的?

HTTP 响应由三个部分组成:

状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果 Servlet 没有返回状态码,默认会返回成功的状态码 HttpServletResponse.SC_OK

HTTP 头部(HTTP Header):它们包含了更多关于响应的信息比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。如何在 Serlet中检索 HTTP 的头部看这里。

主体(Body):它包含了响应的内容。它可以包含 HTML 代码,图片,等等。主体是由传输在HTTP 消息中紧跟在头部后面的数据字节组成的

2. Servlet的生命周期

  1. 用户第一次访问Servlet的时候,服务器会创建一个Servlet的实例,那么Servlet中init方法就会执行.
  2. 任何一次请求服务器都会创建一个新的线程访问Servlet中的service的方法.在service方法内部根据请求的方式的不同调用doXXX的方法.(get请求调用doGet,post请求调用doPost).
  3. 当Servlet中服务器中移除掉,或者关闭服务器,Servlet的实例就会被销毁,那么destroy方法就会执行.

3. Servlet中 forward 和 redirect 的区别

  1. 重定向(redirect):其实是两次request,第一次,客户端request,A服务器响应,并response回来,告诉浏览器,你应该去B。这个时候IE可以看到地址变了,而且历史的回退按钮也亮了。重定向可以访问自己web应用以外的资源。在重定向的过程中,传输的信息会被丢失。 效率高。
  2. 请求转发(forward):是服务器内部把对一个request/response的处理权,移交给另外一个.对于客户端而言,它只知道自己最早请求的那个A,而不知道中间的B,甚至C、D。传输的信息不会丢失。效率低。

4. JSP和Servlet的相同点和不同点

联系:
​ JSP 是 Servlet 技术的扩展,本质上是 Servlet 的简易方式,更强调应用的外表表达。
​ JSP编译后是**”类 servlet”**。
不同点:

  • Servlet 的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。Servlet如果要实现html功能,必须使用Writer输出对应的html.
  • JSP 的情况是Java和HTML可以组合成一个扩展名为.jsp 的文件。做界面展示比较方便,而嵌入逻辑复杂.
  • JSP 侧重于视图,Servlet 主要用于控制逻辑

5. HttpServlet中的方法

doGet() / doPost( ) 方法

通过覆盖 HttpServlet类 中的 doGet() 方法,可以处理浏览器端发送过来的 GET 请求

post 是一种邮寄的方式,在浏览器的地址栏不显示提交的信息,但是这种方式传送的数据是没有限制的;

get 是一种手把手的提交到服务器上,最大的信息量是1K,而且提交的信息显示在浏览器上。

init()

在第一次调用servlet时,会创建servlet实例,在创建这个实例时,会调用HttpServlet中的init()方法(这个方法只会被调用一次)

service()

当客户端有一个对servlet的请求发送过来,则服务器会产生一个新的线程,并让它调用servlet的service()方法,service()会根据收到的客户端请求类型,决定调用doGet()还是doPost()方法

destroy()

在删除servlet实例之前,服务器会先调用destroy()方法

6. 什么是ServletContext?它由谁创建?

​ 当Servlet容器启动时,它会部署并加载所有的web应用。

当web应用被加载时,Servlet容器会一次性为每个应用创建Servlet上下文(ServletContext)并把它保存在内存里。

​ Servlet容器 会处理web应用 的 web.xml 文件,并且一次性创建在 web.xml 里定义的 Servlet、Filter 和 Listener,同样也会把它们保存在内存里。

​ 当Servlet容器关闭时,它会卸载所有的 web应用 和 ServletContext,所有的 Servlet、Filter 和 Listner实例 都会被销毁。

从Java文档可知,ServletContext 定义了一组方法,Servlet 使用这些方法来与它的 Servlet 容器进行通信。

7. Session 和 Cookie 的区别和使用场景

Session和cookie都是会话跟踪技术。cookie在客户端记录信息确定用户身份,而session是在服务器端记录信息确定用户身份。但是session的实现依赖于cookie机制来保存JESESSIONID(session的唯一标识,需要存在客户端)
区别:

  1. cookie的数据存储在客户端,session的数据存储在服务器上
  2. cookie不是很安全,别人可以通过分析存放在本地的cookie并进行cookie欺骗,考虑到安全应该使用session
  3. session会在一定时间内保存在服务器上,当访问增多时,会影响服务器的性能.考虑到服务器性能,应当使用cookie.
  4. 单个cookie保存数据不能超过4k,很多浏览器显示一个站点最多保存20个cookie
  5. 将重要信息保存在session中(登录),将其他需要保留的信心存放在cookie中(购物车,cookie是可以在客户端禁用的,这时候要使用cookie+数据库的方式实现购物车,当cookie中不能取出数据,就从数据库中取)

8. MVC设计模式

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

最简单的、最经典就是Jsp(view) +Servlet(controller) + JavaBean(model)

1、当控制器收到来自用户的请求

2、控制器调用JavaBean完成业务

3、完成业务后通过控制器跳转JSP页面的方式给用户反馈信息

9. 传统 MVC 模式问题

1、所有的 Servlet 和 Servlet映射 都要配置在web.xml中,如果项目太大,web.xml就太庞大,并且不能实现模块化管理。

2、 Servlet的主要功能就是接受参数、调用逻辑、跳转页面,比如像其他字符编码、文件上传等功能也要写在Servlet中, 不适合。

3、接受参数比较麻烦

​ (String name = request.getParameter(“name”),

​ User user = new User user.setName(name) ),

​ 不能通过model接收,只能单个接收,接收完成后转换封装model.

4、跳转页面方式比较单一(forword,redirect),并且当我的页面名称发生改变时需要修改Servlet源代码.

现在比较常用的MVC框架有:

​ Struts2、Spring MVC、SpringBoot(推荐)

10. Filter与listener

1) Filter接口中的方法:

​ void init(FilterConfig filterConfig) throws ServletException

	  过滤器初始化是在启动容器(Tomcat)时自动初始化

void **doFilter**(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException

如果过滤器要将内容传递到目的地,则需要FilterChain,将请求继续向下转发。

​ chain.doFilter(request,response) ;

​ 过滤器会执行两次:FilterChain之前执行一次,之后再执行一次

void destroy()

​ 在标准Servlet中的doGet或doPost方法中,传递的参数为HttpServletRequest、HttpServletResponse

在过滤Servlet中的doFilter中传递的是ServletRequest、ServletResponse

Hibernate基础

1. Hibernate 工作原理 -----------------------------

  1. 通过 Configuration对象 读取并解析配置文件
  2. 读取并解析映射信息,创建 SessionFactory对象
  3. 打开session
  4. 创建事务Transaction
  5. 持久化操作,对对象进行CRUD操作
  6. 提交事务
  7. 关闭 session 和 SessionFactory对象

2. Hibernate优缺点 -----------------------------

优点:

​ 对 JDBC 访问数据库的代码做了封装,简化了数据访问层繁琐的重复性代码

​ 映射的灵活性, 它支持各种关系数据库, 从一对一到多对多的各种复杂关系.

​ 非侵入性、移植性会好

​ 缓存机制: 提供一级缓存和二级缓存

缺点:

​ 无法对 SQL 进行优化

​ 框架中使用 ORM 原则, 导致配置过于复杂

​ 执行效率和原生的 JDBC 相比偏差: 特别是在批量数据处理的时候

​ 不支持批量修改、删除

3. 描述使用 Hibernate 进行大批量更新的经验.

直接通过 JDBC API 执行相关的 SQl 语句或调用相关的存储过程是最佳的方式

4. 在 Hibernate 中Java 对象的状态有哪些

临时状态(transient):不处于 Session 的缓存中,OID为null

持久化状态(persistent):加入到 Session 的缓存中。

游离状态(detached):已经被持久化,但不再处于 Session 的缓存中。

5. Session中一些重要方法的区别

session.save(obj); 保存一个对象

session.update(emp); 更新一个对象

session.saveOrUpdate(emp); 保存或者更新的方法:

​ 没有设置主键,执行保存;

​ 有设置主键,执行更新操作;

​ 如果设置主键不存在报错!

主键查询:

session.get(Employee.class, 1); 主键查询

session.load(Employee.class, 1); 主键查询 (支持懒加载)

6. Session的清理和清空有什么区别

清理缓存调用的是session.flush() 方法 .

清空调用的是session.clear()方法.

Session 清理缓存是指按照缓存中对象的状态的变化来同步更新数据库,但不清空缓存;

Session 清空是把 Session 的缓存置空, 但不同步更新数据库;

7. Hibernate 的检索方式有哪些 ?

导航对象图检索 HQL 检索 QBC 检索 本地 SQL 检索

8. 主键的生成策略

native 自增长【会根据底层数据库自增长的方式选择identity或sequence】

​ 如果是mysql数据库, 采用的自增长方式是identity

​ 如果是oracle数据库, 使用sequence序列的方式实现自增长
increment 自增长(会有并发访问的问题,一般在服务器集群环境使用会存在问题。)

assigned 指定主键生成策略为手动指定主键的值

uuid 指定uuid随机生成的唯一的值

foreign (外键的方式, one-to-one)

9. Inverse属性 =====

Inverse属性,是在维护关联关系的时候起作用的。

表示控制权是否转移。(在一的一方起作用)

Inverse , 控制反转。

Inverse = false 不反转; 当前方有控制权

True 控制反转; 当前方没有控制权

10. cascade属性 =====

cascade 表示级联操作 【可以设置到一的一方或多的一方】

none 不级联操作, 默认值

save-update 级联保存或更新

delete 级联删除

save-update,delete 级联保存、更新、删除

all 同上。级联保存、更新、删除

11. list 与 iterator 查询的区别?

​ list() 一次把所有的记录都查询出来,会放入缓存,但不会从缓存中获取数据

​ Iterator N+1查询; N表示所有的记录总数,即会先发送一条语句查询所有记录的主键(1),再根据每一个主键再去数据库查询(N)!会放入缓存,也会从缓存中取数据!

12. Hibernate的OpenSessionView问题

①. 用于解决懒加载异常, 主要功能就是把 Hibernate Session和一个请求的线程绑定在一起, 直到页面完整输出, 这样就可以保证页面读取数据的时候 Session一直是开启的状态, 如果去获取延迟加载对象也不会报错。

②. 问题: 如果在业务处理阶段大批量处理数据, 有可能导致一级缓存里

的对象占用内存过多导致内存溢出, 另外一个是连接问题: Session 和数据库Connection 是绑定在一起的, 如果业务处理缓慢也会导致数据库连接得不到及时的释放, 造成连接池连接不够. 所以在并发量较大的项目中不建议使用此种方式, 可以考虑使用迫切左外连接 (LEFT OUTER JOIN FETCH) 或手工对关联的对象进行初始化.

13. Hibernate的缓存

Hibernate 缓存包括两大类 :Hibernate一级缓存和Hibernate 二级缓存:

1)Hibernate 一级缓存又称为“Session 的缓存”,它是内置的,不能被卸载。由于 Session 对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。在第一级缓存中,持久化类的每个实例都具有唯一的 OID。

2)Hibernate 二级缓存又称为“SessionFactory 的缓存”,由于SessionFactory 对象的生命周期和应用程序的整个过程对应,因此 Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,在默认情况下,SessionFactory 不会启用这个插件。当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照 ID 放入到缓存删除、更新、增加数据的时候,同时更新缓存。HashtableCacheProvider

映射关系

1) 一对多映射

部门方(一方)

员工方(多方)

2) 一对一映射

外键

主键

3) 多对多映射

Struts2

1. Struts工作原理

① 请求发送给 StrutsPrepareAndExecuteFilter

②. StrutsPrepareAndExecuteFilter判定该请求是否是一个Struts2请求

③. 若该请求是一个 Struts2 请求,则 StrutsPrepareAndExecuteFilter把请求的处理交给 ActionProxy

④. ActionProxy 创建一个 ActionInvocation 的实例,并进行初始化

⑤. ActionInvocation 实例在调用 Action 的过程前后,涉及到相关拦截器(Intercepter)的调用。

⑥. Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置,找到对应的返回结果。调用结果的 execute 方法,渲染结果。

⑦. 执行各个拦截器 invocation.invoke() 之后的代码

⑧. 把结果发送到客户端Struts执行流程

2. Struts有什么优缺点?

优点:

  1. 实现MVC模式,结构清晰;
  2. 丰富的struts的标记库,利用好能大大提高开发效率;
  3. 全局结果与声明式异常;
  4. 可使用OGNL进行参数传递
  5. 各个类方便使用的拦截器

缺点:

​ 1. 转到表现层时,需要配置结果页面;页面多了比较繁杂;

​ 2. 对Servlet的依赖性过强

​ 3. struts标签稍微比el表达式繁重

3. 常用拦截器

​ conversionError:类型转换错误拦截器

​ exception:异常拦截器

​ fileUpload:文件上传拦截器

​ i18n:国际化拦截器

​ logger:日志拦截器

​ params:解析请求参数拦截器

​ validation:校验拦截器

​ timer:这个拦截器负责输出Action的执行时间,以分析该Action的性能瓶颈。

​ token:避免重复提交的校验拦截器。

​ modelDriven:Action执行该拦截器时候可以将getModel方法得到的result值放入值栈中

​ roles:进行权限配置的拦截器,如果登录用户拥有相应权限才去执行某一特定Action。

4. 拦截器执行流程

每个拦截器都是需要实现 Interceptor接口

> init():在拦截器被创建后立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化;

> intercept(ActionInvocation invocation):每拦截一个动作请求,该方法就会被调用一次;

> destroy:该方法将在拦截器被销毁之前被调用, 它在拦截器的生命周期内也只被调用一次;

5. 数据封装方式

jsp表单数据填充到action中的属性

​ jsp表单数据填充到action的对象中的属性

​ ModelDriven模型驱动(推荐)

6. OGNL表达式

​ OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。

​ 它是一个功能强大的表达式语言,用来获取和设置Java对象的属性,它旨在提供一个更高的更抽象的层次来对Java对象图进行导航。

​ Ognl的类中包含的很方便的方法实现OGNL表达式的赋值。实现这个功能需要两步,解析一个表达式使之称为一种内部的形式然后再用这种内部的形式对属性赋值或取值;

​ Struts框架默认就支持Ognl表达式语言。(struts必须引用包ognl.jar)

​ 作用:页面取值用。

​ El表达式语言,用于页面取值,jsp页面取值的标准。(默认直接可以使用)

​ Ognl表达式语言, struts标签默认支持的表达式语言。

​ 缺点:必须配置struts标签用,不能离开struts标签直接用。

Spring

一、Spring特点

1. 为何使用Spring?

​ Spring 是个java企业级应用的开源开发框架。Spring是一个轻量级J2EE框架。它的 主要功能有控制反转(IoC)、面向切面编程 AOP)、面向接口开发、事务管理、还可以包容其它框架,使系统中用到的其它框架耦合程度大大降低,拓展性强、简单易用好管理。

二、IOC和DI

IoC 控制反转:指将对象的创建权,反转到Spring容器

​ 对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。

DI 依赖注入: 指Spring创建对象的过程中,将对象依赖属性通过配置进行注入

看过很多对Spring的Ioc理解的文章,好多人对Ioc和DI的解释都晦涩难懂,反正就是一种说不清,道不明的感觉,读完之后依然是一头雾水,感觉就是开涛这位技术牛人写得特别通俗易懂,他清楚地解释了IoC(控制反转)DI(依赖注入)中的每一个字,读完之后给人一种豁然开朗的感觉。我相信对于初学Spring框架的人对Ioc的理解应该是有很大帮助的。

分享对IoC与DI浅显易懂的讲解

2.1IoC(控制反转)
首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

  那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此, /*所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。*/

2.2DI(依赖注入)

 IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如: /*对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。*/那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

  理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。

我对IoC(控制反转)DI(依赖注入)的理解
	在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。

  所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

  这是我对Spring的IoC(控制反转)的理解。DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转。


1. IOC控制反转

IoC是一种设计模式,是一种思想,相当于一个容器,而DI就好比是实现IOC的一种方式。所谓依赖注入,就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。

2. DI的实现方式

​ 构造器注入(Constructor Injection):Ioc容器会智能地选择和调用合适的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,Ioc容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;

​ 属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,Ioc容器会自动初始化该属性;

​ 方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,Ioc容器会自动调用该方法。

Spring中,如何给对象的属性赋值?

​ 1. 通过构造函数

​ 2. 通过set方法给属性注入值

​ 3. 自动装配(了解)

byName 通过参数名自动装配,如果一个bean的name 和另外一个bean的property 相同就自动装配。

byType 通过参数的数据类型自动自动装配,如果一个bean的数据类型和另外一个bean的property属性的数据类型兼容,就自动装配必须确保该类型在IOC容器中只有一个对象;否则报错。

​ 4.注解

使用注解步骤:

​ 1)先引入context名称空间

​ xmlns:context=“http://www.springframework.org/schema/context

​ 2)开启注解扫描

​ <context:component-scan base-package=“cn.itcast.e_anno2”></context:component-scan>

​ 3)使用注解

​ 通过注解的方式,把对象加入ioc容器。

创建对象以及处理对象依赖关系,相关的注解:

​ @Component 指定把一个对象加入IOC容器

​ @Repository 作用同@Component; 在持久层使用

​ @Service 作用同@Component; 在业务逻辑层使用

​ @Controller 作用同@Component; 在控制层使用

​ @Resource 属性注入

总结:

​ 1) 使用注解,可以简化配置,且可以把对象加入IOC容器,及处理依赖关系(DI)

​ 2) 注解可以和XML配置一起使用。

​ 3) @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了

3. bean对象创建的细节

  1. 对象创建: 单例/多例

​ scope=“singleton”, 默认值, 即 默认是单例 【service/dao/工具类】

​ scope=“prototype”, 多例;【Action对象】

​ Spring的单例bean是线程安全的

  1. 什么时候创建?

​ scope=“prototype” 在用到对象的时候,才创建对象。

​ scope=“singleton” 在启动(容器初始化之前), 就已经创建了bean,且整个应用只有一个。

3)是否延迟创建

​ lazy-init=“false” 默认为false, 不延迟创建,即在启动时候就创建对象

​ lazy-init=“true” 延迟初始化, 在用到对象的时候才创建对象(只对单例有效)

  1. 创建对象之后,初始化/销毁

​ init-method=“init_user” 【对应对象的init_user方法,在对象创建爱之后执行】

​ destroy-method=“destroy_user” 【在调用容器对象的destriy方法时候执行,(容器用实现类)】

4. Bean的作用域?

  1. singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
  2. prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
  3. request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
  4. Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。

5. IOC 容器对 Bean的生命周期

​ 1)通过构造器或工厂方法创建 Bean 实例

​ 2)为 Bean 的属性设置值和对其他 Bean 的引用

​ 3)将 Bean实例传递 给 Bean 后 置 处 理 器 的 postProcessBeforeInitialization 方法

​ 4) 调用 Bean 的初始化方法(init-method)将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的 postProcessAfterInitialization 方法,Bean 可以使用了

​ 5)当容器关闭时, 调用 Bean 的销毁方法(destroy-method)

6. 讲下 BeanFactory 和 ApplicationContext 的区别?

​ BeanFactory是Spring容器顶级核心接口,比较早,但功能比较少,getBean就是BeanFactory定义的。

ApplicationContext 是 Spring 里面的另外一个容器顶级接口,它继承于 BeanFactory ,但是提供的功能譬如校验,国际化,监听,对Bean的管理功能比较多,一般使用ApplicationContext。

三、AOP

1. 什么是AOP?

​ AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。

下面我举个例子给大家说明一下:

有A,B,C三个方法,但是在调用每一个方法之前,要求打印一个日志:某一个方法被开始调用了!

在调用每个方法之后,也要求打印日志:某个方法被调用完了!

一般人会在每一个方法的开始和结尾部分都会添加一句日志打印吧,这样做如果方法多了,就会有很多重复的代码,显得很麻烦,这时候有人会想到,为什么不把打印日志这个功能封装一下,然后让它能在指定的地方(比如执行方法前,或者执行方法后)自动的去调用呢?如果可以的话,业务功能代码中就不会掺杂这一下其他的代码,所以AOP就是做了这一类的工作,比如,日志输出,事务控制,异常的处理等。。
     缓存、权限、错误处理、持久化、资源池、同步、事物、日志跟踪等

二、有了这三个疑问,加上上面的讲解,下面我们来说一下AOP的一些术语(一下看不懂不要紧,慢慢理解)

1.通知(Advice)
  就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。

2.连接点(JoinPoint)
  这个更好解释了,就是spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,只要记住,和方法有关的前前后后(抛出异常),都是连接点。

3.切入点(Pointcut)
  上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

4.切面(Aspect)
  切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

5.引入(introduction)
  允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗

6.目标(target)
  引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。

7.代理(proxy)
  怎么实现整套aop机制的,都是通过代理,这个一会给细说。

8.织入(weaving)
  把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时,为什么是运行时,后面解释。

关键就是:切点定义了哪些连接点会得到通知

实现AOP的技术,主要分为两大类:

​ 一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;

​ 二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

2. 为什么用AOP?

  1. 就是为了方便,看一个国外很有名的大师说,编程的人都是“懒人”,因为他把自己做的事情都让程序做了。用了aop能让你少写很多代码,这点就够充分了吧
  2. 就是为了更清晰的逻辑,可以让你的业务逻辑去关注自己本身的业务,而不去想一些其他的事情,这些其他的事情包括:安全,事物,日志等。

3. 代理模式

代理(Proxy)是一种设计模式, 提供了对目标对象另外的访问方式;即通过代理访问目标对象。

好处: 可以在目标对象实现的基础上,增强额外的功能操作。(扩展目标对象的功能)。

静态代理:要实现与目标对象一样的接口

​ 总结静态代理:

​ 1)可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。

​ 2)缺点:

​ --》 因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。

​ --》 一旦接口增加方法,目标对象与代理对象都要维护。

​ 解决:可以使用动态代理。

**动态代理: **

​ 1)代理对象,不需要实现接口;

​ 2)代理对象的生成,是利用JDKAPI, 动态的在内存中构建代理对象

​ 动态代理总结:代理对象不需要实现接口,但是目标对象一定要实现接口;否则不能用动态代理!

Cglib代理:

​ 也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。

​ JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。

​ CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。

​ CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

四、事务

1. 描述在系统中如何使用了 Spring 的事务控制

​ Spring事务包括编程式事务和声明式事务。在系统中使用了声明式的事务管理是用Spring的AOP来实现的;配置了只读事务和回滚事务(传播行为为REQUIRED)当出现错误后进行回滚操作。在项目中通过aop切入事务到serivce层,这样做能使一次业务逻辑操作如果包括几个数据库操作都控制在一个事务中。

2. 如何管理事务,事务是只读的还是读写的,对于查询的 find( ) 是只读,对于保存的 save( ) 是读写?

​ 如果一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL 执行期间的读一致性;
​ 如果一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL 查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一 致的状态,此时,应该启用事务支持 read-only=“true” 表示该事务为只读事务,

​ 比如上面说的多条查询的这种情况可以使用只读事务,由于只读事务不存在数据的修改,因此数据库将会为只读事 务提供一些优化手段,例如Oracle对于只读事务,不启动回滚段,不记录回滚log。

​ 指定只读事务的办法为:

​ bean配置文件中,prop属性增加“read-Only”

​ 或者用注解方式@Transactional(readOnly=true)
​ Spring中设置只读事务是利用上面两种方式(根据实际情况)
​ 在将事务设置成只读后,相当于将数据库设置成只读数据库,此时若要进行写 的操作,会出现错误。

3. Spring 的事务是如何配置的?

​ 先配置事务管理器 TransactionManager ,不同的框架有不同属性。

​ 再配置事务通知和属性,通过 tx:advice。

​ 配置 < aop:config >,设置那些方法或者类需要加入事务。

4. 事务传播行为

Propagation.REQUIRED(spring默认的事务)

指定当前的方法必须在事务的环境下执行;

​ 如果当前运行的方法,已经存在事务, 就会加入当前的事务;

Propagation.SUPPORTS 如果有事务则加入事务,没有则无事务运行。

Propagation.REQUIRED_NEW

指定当前的方法必须在事务的环境下执行;

​ 如果当前运行的方法,已经存在事务: 事务会挂起; 会始终开启一个新的事务,执行完后; 刚才挂起的事务才继续运行。

5. 事务并发会引起什么问题,怎么解决?

事务并发会引起 脏读,幻读,不可重复读等问题, 设定事务的隔离级别可以解决

Mybatis

1.Mybatis 架构或工作流程

  1. mybatis配置

    SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了 mybatis 的运行环境等信息。

    mapper.xml 文件即 sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。

  2. 通过 mybatis 环境 等配置信息构造 SqlSessionFactory,即会话工厂

  3. 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。

  4. mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。

  5. Mapped Statement 也是 mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件 中一个sql对应一个 Mapped Statement 对象,sql的 id 即是Mapped statement的id。

  6. Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。

  7. Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

2. Mapper.xml 中 statement 中属性含义

  1.    id:sql语句唯一标识
    
  2.    parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼          接在sql中。
    
  3.   resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。
    
  4.   resultMap:resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 resultMap实质上还需要将查询结果映射到pojo对象中。
    

​ resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象 中包括pojo和list实现一对一查询和一对多查询。

  1.    #{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
    
  2.    ${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进    行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
    

3. SqlSessionFactory

​ SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。

4. SqlSession

​ SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作方法。

每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。

private static ThreadLocal threadLocal = new ThreadLocal();

5. Mybatis中自主主键如何获取

​ #{}:如果传入的是pojo类型,那么#{}中的变量名称必须是pojo中对应的属性.属性.属性… 如果要返回数据库自增主键:可以使用select LAST_INSERT_ID()

​ 执行 select LAST_INSERT_ID()数据库函数,返回自增的主键 keyProperty:将返回的主键放入传入参数的Id中保存.

​ order:当前函数相对于insert语句的执行顺序,在insert前执行是before,在insert后执行是AFTER resultType:id的类型,也就是keyproperties中属性的类型

6. Mybatis中uuid主键如何获取?

需要增加通过select uuid()得到uuid值

要将User中的id改成String类型,并且将User表中的id字段改为varchar(36)

7. Mapper接口开发方法

通常Mybatis开发Dao方法有两种。即原始Dao开发方法和Mapper接口开发方法。

​ Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

Mapper接口开发需要遵循以下规范:

  1.     Mapper.xml文件中的namespace与mapper接口的类路径相同。
    
  2.     Mapper接口方法名和Mapper.xml中定义的每个statement的id相同 
    
  3.     Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
    
  4.     Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
    

8.selectOne和selectList

动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。

9.动态sql

通过mybatis提供的各种标签方法实现动态拼接sql。

foreach标签:循环传入的集合参数

​ collection:传入的集合的变量名称

​ item:每次循环将循环出的数据放入这个变量中

​ open:循环开始拼接的字符串

​ close:循环结束拼接的字符串

​ separator:循环中拼接的分隔符

where标签作用:会自动向sql语句中添加where关键字,会去掉第一个条件的and关键字。

include标签:调用sql条件

10.Mybatis缓存机制

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

​ mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

​ mybaits提供一级缓存,和二级缓存。

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession 之间的缓存数据区域(HashMap)是互相不影响的。

​ 一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。

​ 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

​ 二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同,即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis 默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。

​ 如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

二级缓存的应用场景

​ 对查询频率高,变化频率低的数据建议使用二级缓存。

​ 对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用 mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较 高的统计分析sql、电话账单查询sql等。

​ 实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓 存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60 分钟、24小时等,根据需求而定。

Mybatis****缓存的局限性

​ mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信 息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品 信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商 品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper 为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此 类问题需要在业务层根据需求对数据有针对性缓存。

11. Mybatis实现分布式缓存

​ ehcache是一个分布式缓存框架。

​ EhCache 是一个纯Java的进程内缓存框架,是一种广泛使用的开源Java分布式缓 存,具有快速、精干等特点,是Hibernate中默认的CacheProvider。

​ 我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)

​ 不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统开发。所以要使用 分布式缓存对缓存数据进行集中管理。

​ mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

12. Mybatis 逆向工程

使用官方网站的mapper自动生成工具mybatis-generator-core-1.3.2来生成po类和mapper映射文件。

作用:mybatis官方提供逆向工程,可以使用它通过数据库中的表来自动生成Mapper接口和映射文件(单表增删改查)和Po类.

13. Mybatis 解决jdbc 编程的问题

  1.   数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
    

​ 解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。

  1.   Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
    

​ 解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

  1.   向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
    

​ 解决:Mybatis自动将java对象映射至sql语句,通过statement中的 parameterType定义输入参数的类型。

  1.   对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
    

14. Hibernate 与 Mybatis 对比---------------------------

首先简单介绍下两者的概念

Hibernate :Hibernate 是当前最流行的ORM框架,对数据库结构提供了较为完整的封装。

Mybatis:Mybatis同样也是非常流行的ORM框架,主要着力点在于POJO 与SQL之间的映射关系。

其次具体从几个方面说一下两者的区别:

1. 两者最大的区别

​ 针对简单逻辑,Hibernate和MyBatis都有相应的代码生成工具,可以生成简单基本的DAO层方法。

​ 针对高级查询,Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL生成与结果映射,可更专注于业务流程。

**2.**开发难度对比

​ Hibernate的开发难度要大于Mybatis。主要由于Hibernate比较复杂、庞大,学习周期较长。

​ 而Mybatis则相对简单一些,并且Mybatis主要依赖于sql的书写,让开发者感觉更熟悉。

3. sql书写比较

​ Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。不过没有自己的日志统计,所以要借助log4j来记录日志。

​ Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。不过Hibernate具有自己的日志统计。

**4.**数据库扩展性比较

​ Mybatis由于所有SQL都是依赖数据库书写的,所以扩展性,迁移性比较差。

​ Hibernate与数据库具体的关联都在XML中,所以HQL对具体是用什么数据库并不是很关心。

**5.**缓存机制比较

​ 相同点:Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现自己的缓存或为其他第三方缓存方案,创建适配器来覆盖缓存行为。

​ 不同点:Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是那种缓存。

​ MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。

​ 两者比较:因为Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示。

​ 而MyBatis在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免Cache的盲目使用。否则,脏数据的出现会给系统的正常 运行带来很大的隐患。

**6.**总结

​ Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成 SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。

​ 而MyBatis的优势是MyBatis可以进行更为细致的SQL优化,可以减少查询字段,并且容易掌握。

​ Hibernate的优势是DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。

51. ORM是什么?ORM框架是什么?====================

​ 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单来说,将程序中的兑现自动持久化到关系数据库中。那么,到底如何实现持久化呢?一种简单的反感是采用硬编码的方式(jdbc操作sql方式),为每一种可能的数据库访问操作提供单独的方法。这种方法存在很多缺陷,所以使用ORM框架(为了解决面型对象与关系数据库存在的互不匹配的现象的框架)来解决。
Java典型的ORM框架有:Hibernate,mybatits。

52.Mybatis和Hibernate有什么不同

​ 相同:都是java中的orm框架,屏蔽jdbc的api底层访问细节,使我们不用域jdbc的挨批打交道,就可以完成对数据的持久化操作.jdbc的api编程流程固定,还将sql与java代码混在在一起,经常需要拼凑sql语句,细节繁琐,开发不方便.
Mybatis的好处:屏蔽jdbc api的底层访问细节,将sql语句域java代码分离,提供将结果集自动封装为实体对象和对象的集合的功能,queryForList返回对象集合.用queryForObject返回单个对象.提供自动将实体对象的属性传递给sql语句的参数
Hibernate的好处:hibernate是一个全自动的映射工具,它可以自动生成sql语句,执行并返回java结果.
不同点:

  1. hibernate要比mybatis功能强大很多,因为hibernate自动生成sql语句.
  2. Mybatis需要我们自己在xml配置文件中写sql语句,hibernate我们无法直接控制该语句,我们就无法去写特定的高效的sql,对于一些不太复杂的sql查询,hibernate可以很好帮我们完成.但是,对于特别复杂的查询,hibernate就很难适应了,这时候用mybatis就是不错的选择,因为mybatis还是由我们自己写sql语句.mybatis可以处理复杂语句,而hibernate不能.
  3. Mybatis要比hibernate简单的多,mybatis是面向sql的,不用考虑对象间一些复杂的映射关系.

53. HIBERNATE对象状态及其转换

瞬时态:刚new的,没有持久化,不在session中
持久态:session中的持久化对象
脱管态:不在session中的持久化对象

54.HIBERNATE的缓存

缓存:为了提高访问速度,把磁盘或数据库访问变成内存访问.
一级缓存(session缓存):session缓存内置不能被卸载session:是事务范围的缓存(session对象的生命周期通常对应一个数据库事务或者一个应用事务).一级缓存中,持久化类的每个实例都具有唯一的oid
二级缓存(SessionFactory缓存):由于SessionFactory对象的生命周期和一个用程序的整个过程对应,因此hibernate二级缓存是进城范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别.二级缓存是可选的,是一个可配置的插件,默认下SessionFactory不会启用这个插件.
概括:hibernate中的缓存分一级缓存和二级缓存
一级缓存就是session级别的缓存, 在事务范围内是有效的,是内置的不能被卸载.二级缓存是SessionFactory级别的缓存,从应用启动到应用结束有效,是可选的,默认没有二级缓存,需要手动开启
保存数据库后,在内存中保存一份,如果更新了数据库就要同步更新缓存
什么样的数据适合放到缓存中(访问频率,读写比例,数据一致性):
很少被修改的数据
经常被查询的数据
不是很重要的数据,允许出现偶尔并发的数据
不会被并发访问的数据
常量数据
Hibernate的二级缓存默认是不支持分布式缓存的,使用memcache,redis等重要缓存来代替二级缓存

SpringMVC

1. SpringMVC比较 Struts2---------------------------

​ 1) SpringMVC的入口是一个servlet即前端控制器,而struts2入口是一个filter过虑器。

​ 2) SpringMVC是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。

​ 3) Struts采用值栈存储请求和响应的数据,通过OGNL存取数据, springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。

2. SpringMVC处理流程

1551622371516

  1.    向服务器发送HTTP请求,请求被前端控制器 DispatcherServlet 捕获。
    
  2.    DispatcherServlet 根据 <servlet-name>-servlet.xml 中的配置对请求的URL进行解析,得到请求资源标识符(URI)。 然后根据该URI,调用 HandlerMapping 获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。
    
  3.    DispatcherServlet 根据获得的Handler,选择一个合适的 HandlerAdapter。
    
  4.    提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
    

HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。

数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。

数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等。

数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。

  1.    Handler(Controller)执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象
    
  2.    根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet。
    
  3.    ViewResolver 结合Model和View,来渲染视图。
    
  4.    视图负责将渲染结果返回给客户端
    

3. SpringMVC中的组件及各个组件的作用

  1. DispatherServlet:前置控制器,负责接收并处理所有的web请求,根据handlerMapping找到具体的Controller,由controller完成具体的处理逻辑。
  2. HandlerMapping:负责处理web请求和具体的Controller之间的映射关系匹配。
  3. Controller:DispatherServlet的次级控制器,web请求的具体处理者。DispatherServlet获得handlerMapping的返回结果后,调用controller的处理方法处理当前的业务请求,处理完成后返回ModelAndView对象。
  4. ViewResolver:用来处理视图名与具体的view实例之间的映射对应关系。根据ModelAndView中的视图名查找相应的View实现类,然后将查找的结果返回给DispatcherServlet,DispatcherServlet最终会将ModelAndView中的模型数据交给返回的View处理最终的视图渲染工作。
  5. View:为支持多种视图技术,统一抽象视图的生成策略,根据模型数据输出具体的视图。

而存在,统一抽象视图的生成策略,根据模型数据输出具体的视图。

4. Spring MVC常用的注解 ======

@RequestMapping: 设置请求映射url

@RequestParam:常用于处理简单类型的绑定

@RequestBody:注解用于读取http请求的内容(字符串),通过springmvc提供的 HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。

@ResponseBody:该注解用于将Controller的方法返回的对象,通过 HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端

扫描指定的包中的类上的注解,常用的注解有:

@Controller 声明Action组件
@Service 声明Service组件 @Service(“myMovieLister”)
@Repository 声明Dao组件
@Component 泛指组件, 当不好归类时.
@RequestMapping("/menu") 请求映射
@Resource 用于注入,( j2ee提供的 ) 默认按名称装配,@Resource(name=“beanName”)
@Autowired 用于注入,(srping提供的) 默认按类型装配
@Transactional( rollbackFor={Exception.class}) 事务管理
@ResponseBody
@Scope(“prototype”) 设定bean的作用域

5. 如何使用 SpringMVC 完成 JSON 操作

配置 MappingJacksonHttpMessageConverter

如果使用mvc:annotation-driven就不需要配置。

使用 @RequestBody 注解作为请求参数或 ResponseBody作为返回值

6. mvc:annotation-driven

使用mvc:annotation-driven代替注解映射器和注解适配器配置器

mvc:annotation-driven默认加载很多的参数绑定方法,比如json转换解析器就默认加载了,如果使用mvc:annotation-driven就不用配置

RequestMappingHandlerMapping和RequestMappingHandlerAdapter

7. restful

一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。(强调以资源为导向)

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。

资源定位:互联网所有的事物都是资源,要求url中没有动词,只有名词。没有参数

Url格式:http://blog.csdn.net/beat_the_world/article/details/45621673

(使用restful后,utl中不能用?传参或.action)

资源操作:使用put、delete、post、get,使用不同方法对资源进行操作。分别对应添加、删除、修改、查询。一般使用时还是post和get。Put和Delete几乎不使用。

客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。

它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

URI 的设计只要负责把资源通过合理方式暴露出来就可以了。对资源的操作与它无关,操作是通过 HTTP动词来体现,所以REST 通过 URI 暴露资源时,会强调不要在 URI 中出现动词。

比如:左边是错误的设计,而右边是正确的

GET /rest/api/getDogs --> GET /rest/api/dogs 获取所有小狗狗

GET /rest/api/addDogs --> PUT /rest/api/dog 添加一个小狗狗

GET /rest/api/editDogs/:dog_id --> POST /rest/api/dogs/:dog_id 修改一个小 狗狗

GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 删除一 个小狗狗

左边的这种设计,很明显不符合REST风格,上面已经说了,URI 只负责准确无误的暴露资源,而 getDogs/addDogs…已经包含了对资源的操作,这是不对的。相反右边却满足了,它的操作是使用标准的HTTP动词来体现。

8. SpringMVC 里面拦截器是怎么实现的?

有两种方式,一种是实现接口,另外一种是继承适配器类,然后在SpringMvc的配置文件中配置拦截器即可。

使用场景:

用户登录判断,在执行Action的前面判断是否已经登录,如果没有登录的跳转到登录页面。

用户权限判断,在执行Action的前面判断是否具有,如果没有权限就给出提示信息。

操作日志

SpringBoot

1. Springboot和springcloud?

现在很多公司已经开始使用微服务架构(SpringCloud或Dubbo),而SpringBoot对于微服务的环境支持很好。

l 微架构,与Spring4一起诞生。比如@RestController

l 可以快速上手,整合一些子项目(开源框架或第三方开源库)

l 可以依赖很少的配置就可以快速搭建并运行项目

l 基于spring,开发者快速入门,门槛很低。

l Springboot可以创建独立运行的应用而不依赖容器

l 不需要打成war包,可以放入tomcat中直接运行。

l 提供maven极简配置,缺点是会引入些项目不需要的包。

l 提供可视化的相关功能,方便监控,比如性能、应用的健康程度。

l 简化配置,不需要过多的xml

l 为微服务springClouod铺路,springBoot可以整合各种架构来构建微服务,比如dubbo、thrift等。

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

微服务架构需要的功能或使用场景

1:我们把整个系统根据业务拆分成几个子系统。

2:每个子系统可以部署多个应用,多个应用之间使用负载均衡。

3:需要一个服务注册中心,所有的服务都在注册中心注册,负载均衡也是通过在注册中心注册的服务来使用一定策略来实现。

4:所有的客户端都通过同一个网关地址访问后台的服务,通过路由配置,网关来判断一个URL请求由哪个服务处理。请求转发到服务上的时候也使用负载均衡。

5:服务之间有时候也需要相互访问。例如有一个用户模块,其他服务在处理一些业务的时候,要获取用户服务的用户数据。

6:需要一个断路器,及时处理服务调用时的超时和错误,防止由于其中一个服务的问题而导致整体系统的瘫痪。

7:还需要一个监控功能,监控每个服务调用花费的时间等。

目前主流的微服务框架:Dubbo、 SpringCloud、thrift、Hessian等,目前国内的中小企业用的大多数都是Dubbo,SpringCloud估计很少,也许有些开发同学都没听说过。

springCloud是基于SpringBoot的一整套实现微服务的框架。他提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。最重要的是,跟spring boot框架一起使用的话,会让你开发微服务架构的云服务非常好的方便。

SpringBoot旨在简化创建产品级的 Spring 应用和服务,简化了配置文件,使用嵌入式web服务器,含有诸多开箱即用微服务功能

数据库

一、SQL基础

1. SQL概念

​ SQL(Structured Query Language 结构查询语言)是一个功能强大的数据库语言。

​ DML(数据操作语言): 用于检索或修改数据

​ DDL(数据定义语言): 用于定义数据的结构,创建、修改、删除数据库对象

​ DCL(数据控制语言): 用于定义数据库用户的权限

2. 常用函数

2.1 字符函数

UPPER: 将输入的字符串变为大写返回

Select upper(‘hello’) from dual;  //dual为数据库提供的一张虚拟表

LOWER: 将输入的字符串变为小写返回

Select lower(ename) from emp;

INITCAP: 开头首字母大写

Select initcap(ename) from emp;

LENGTH: 求出字符串的长度

Select ename, length(ename) from emp;

查询出姓名长度为5的雇员信息

Select ename,length(ename) from emp where length(ename)=5;

REPLACE: 字符串进行替换

使用”_”替换姓名中所有字母”A”

Select replace(ename,’A’,’_’) from emp;

SUBSTR: 字符串截取

Select ename,substr(ename,0,3) from emp ;

截取每个雇员姓名的后三个字母

Select ename,substr(ename,length(ename)-2) from emp;

Select ename,substr(ename,-3) from emp;

2.2 数字函数

ROUND: 四舍五入的操作

TRUNC: 截取指定位置的内容

MOD: 取模或取余

2.3通用函数

NVL() 处理null

查询出每个雇员的全部年薪

Select ename,sal,comm,(sal+comm)*12 from emp; //此时comm字段有null值

//此时要将null变为0
Select ename,sal,comm,(sal+nvl(comm,0))*12,nvl(comm,0) from emp; 

DECODE() 多数值判断

类似于if…else语,不同的是decode()函数判断的是数值,而不是逻辑条件。

显示全部雇员的职位,但是这些职位要求替换为中文显示

Select empno,ename,job,
	decode(job,’CLERK’,’办事员’,’SALESMAN’,’销售人员’,
           ’MANAGER’,’经理’ ,’ANALYST’,’分析员’ ,’PRESIDENT’,’总裁’) 
	from emp

3. 常用的数据字段(oracle)

每一张数据表实际是由若干字段组成,而每一个字段会有其对应的数据类型。

数据类型 关键字 描述
字符串 VARCHAR2(n) n表示字符串所能保存的最大长度200字符左右
整数 NUMBER(n) 最多为n位的整数,有时可也可以用INT代替
小数 NUMBER(n,m) m为小数位,n-m为整数位,可以用FLOAT代替
日期 DATE 存放日期时间
大文本 CLOB 存储海量文字(4G)
大对象 BLOB 存放二进制数据,如:电影、mp3、图片、文字

4. 集合操作

在数学的操作中存在交、差、并、补的概念,而在数据的查询中也存在此概念。

UNION 连接两个,相同部分不显示。

UNION ALL连接两个,相同部分显示。

INTERSECT 返回两个查询中相同部分

MINUS 返回两个查询中不同部分

5. 视图和游标

​ 视图是一种虚拟表,具有与物理表相同的功能,可对视图增删改查。视图通常是一个或多个表的行或列的子集。对视图的修改不影响基本表。使获取数据更容易,相比多表查询。

​ 游标: 对查询出来的结果集做一个单元来有效处理。游标可定在该单元中的特定行,从结果集的当前行检索一行或多行。可对结果集当前行做修改。

一般不使用游标,但当需逐条处理数据时,游标显得很重要。

6. 数据库视图和表的区别,什么是视图?怎么使用视图?物化视图?----------------------

视图是种虚表,不是真正的物理表,只是为业务查询方便 ,将几张物理表虚拟组成一个视图。

第一点:

使用视图,可以定制用户数据,聚焦特定的数据。

解释:

在实际过程中,公司有不同角色的工作人员,我们以销售公司为例的话,

采购人员,可以需要一些与其有关的数据而与他无关的数据,对他没

有任何意义,我们可以根据这一实际情况,专门为采购人员创建一个视

图,以后他在查询数据时,只需select * from view_caigou 就可以啦。

第二点:使用视图,可以简化数据操作。

解释:我们在使用查询时,在很多时候我们要使用聚合函数,同时还要

显示其它字段的信息,可能还会需要关联到其它表,这时写的语句可能

会很长,如果这个动作频繁发生的话,我们可以创建视图,这以后,我

们只需要select * from view1就可以

第三点:使用视图,基表中的数据就有了一定的安全性

因为视图是虚拟的,物理上是不存在的,只是存储了数据的集合,我们可以

将基表中重要的字段信息,可以不通过视图给用户,视图是动态的数据的集

合,数据是随着基表的更新而更新。同时,用户对视图,不可以随意的更改

和删除,可以保证数据的安全性。

第四点:可以合并分离的数据,创建分区视图

随着社会的发展,公司的业务量的不断的扩大,一个大公司,下属都设有很

多的分公司,为了管理方便,我们需要统一表的结构,定期查看各公司业务

情况,而分别看各个公司的数据很不方便,没有很好的可比性,如果将这些

数据合并为一个表格里,就方便多啦,这时我们就可以使用union关键字,

将各分公司的数据合并为一个视图。

二、索引

1. 数据库的索引有什么用,带来的问题是什么?

​ 1、索引定义

数据库索引好比是一本书前面的目录,能加快数据库的查询速度。索引是对数据库表中一个或多个列(例如,employee 表的姓氏 (lname) 列)的值进行排序的结构。如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行相比,索引有助于更快地获取信息。

​ 2、建立索引的优缺点

优点: 1.大大加快数据的检索速度;
​ 2.创建唯一性索引,保证数据库表中每一行数据的唯一性;
​ 3.加速表和表之间的连接;
​ 4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排 序的时间。

缺点:
  1.索引需要占用数据表以外的物理存储空间
  2.创建索引和维护索引要花费一定的时间
  3.当对表进行更新操作时,索引需要被重建,这样降低了数据的维护速度。

2. 索引类型

根据数据库的功能,可以在数据库设计器中创建索引:唯一索引、主键索引和聚集索引。 尽管唯一索引有助于定位信息,但为获得最佳性能结果,建议改用主键或唯一约束。
唯一索引: UNIQUE

例如:create unique index stusno on student(sno);
表明此索引的每一个索引值只对应唯一的数据记录,对于单列惟一性索引,这保证单列不包含重复的值。对于多列惟一性索引,保证多个值的组合不重复。

主键索引: primary key
数据库表经常有一列或列组合,其值唯一标识表中的每一行。该列称为表的主键。 在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。

聚集索引(也叫聚簇索引):cluster
在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。 如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。

3. 索引的实现方式

1、B+树

经常听到B+树就是这个概念,用这个树的目的和红黑树差不多,也是为了尽量保持树的平衡,当然红黑树是二叉树,但B+树就不是二叉树了,节点下面可以有多个子节点,数据库开发商会设置子节点数的一个最大值,这个值不会太小,所以B+树一般来说比较矮胖,而红黑树就比较瘦高了。
关于B+树的插入,删除,会涉及到一些算法以保持树的平衡,这里就不详述了。ORACLE的默认索引就是这种结构的。
如果经常需要同时对两个字段进行AND查询,那么使用两个单独索引不如建立一个复合索引,因为两个单独索引通常数据库只能使用其中一个,而使用复合索引因为索引本身就对应到两个字段上的,效率会有很大提高。
2、散列索引 第二种索引叫做散列索引,就是通过散列函数来定位的一种索引,不过很少有单独使用散列索引的,反而是散列文件组织用的比较多。
散列文件组织就是根据一个键通过散列计算把对应的记录都放到同一个槽中,这样的话相同的键值对应的记录就一定是放在同一个文件里了,也就减少了文件读取的次数,提高了效率。
散列索引呢就是根据对应键的散列码来找到最终的索引项的技术,其实和B树就差不多了,也就是一种索引之上的二级辅助索引,我理解散列索引都是二级或更高级的稀疏索引,否则桶就太多了,效率也不会很高。
3、位图索引
​ 位图索引是一种针对多个字段的简单查询设计一种特殊的索引,适用范围比较小,只适用于字段值固定并且值的种类很少的情况,比如性别,只能有男和女,或者级别,状态等等,并且只有在同时对多个这样的字段查询时才能体现出位图的优势。
位图的基本思想就是对每一个条件都用0或者1来表示,如有5条记录,性别分别是男,女,男,男,女,那么如果使用位图索引就会建立两个位图,对应男的10110和对应女的01001,这样做有什么好处呢,就是如果同时对多个这种类型的字段进行and或or查询时,可以使用按位与和按位或来直接得到结果了。
B+树最常用,性能也不差,用于范围查询和单值查询都可以。特别是范围查询,非得用B+树这种顺序的才可以了。
HASH的如果只是对单值查询的话速度会比B+树快一点,但是ORACLE好像不支持HASH索引,只支持HASH表空间。
位图的使用情况很局限,只有很少的情况才能用,一定要确定真正适合使用这种索引才用(值的类型很少并且需要复合查询),否则建立一大堆位图就一点意义都没有了。

4. 索引失效

​ 1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少 用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引

​ 2.对于多列索引,不是使用的第一部分,则不会使用索引

​ 3.like查询是以%开头

​ 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不 使用索引

​ 5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引

​ 此外,查看索引的使用情况

​ show status like ‘Handler_read%’;

​ 大家可以注意:

​ handler_read_key:这个值越高越好,越高表示使用索引查询到的次数

5. 怎么用索引? 建立索引的思想?

索引是建立在数据库表中的某些列的上面。在创建索引的时候,应该考虑在哪些列上可以创建索引,在哪些列上不能创建索引。一般来说,应该在这些列上创建索引:

在经常需要搜索的列上,可以加快搜索的速度;

在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;

在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;

在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;

在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

同样,对于有些列不应该创建索引。一般来说,不应该创建索引的这些列具有下列特点:

第一,对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。

第二,对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。

第三,对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少,不利于使用索引。

第四,当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改操作远远多于检索操作时,不应该创建索引。

6. 如何防止sql注入?----------------

1)简单又有效的方法是使用PreparedStatement

采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。

使用好处:

(1).代码的可读性和可维护性.

(2).PreparedStatement尽最大可能提高性能.

(3).最重要的一点是极大地提高了安全性.

原理:

sql注入只对sql语句的准备(编译)过程有破坏作用而PreparedStatement已经准备好了,执行阶段只是把输入串作为数据处理,而不再对sql语句进行解析,准备,因此也就避免了sql注入问题.

2)使用正则表达式过滤传入的参数

3)使用javascript在客户端进行不安全字符屏蔽

功能介绍:检查是否含有”‘”,”\”,”/”

参数说明:要检查的字符串

返回值:0:是1:不是

三、MyIASM引擎

MyIASM是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和Innodb不同,MyIASM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。

\1. 不支持事务,但是并不代表着有事务操作的项目不能用MyIsam存储引擎, 可以在service层进行根据自己的业务需求进行相应的控制。

\2. 不支持外键。

\3. 查询速度很快,如果数据库insert和update的操作比较多的话比较适用。

\4. 对表进行加锁。

两种引擎的选择

大尺寸的数据集趋向于选择InnoDB引擎,因为它支持事务处理和故障恢复。数据库的大小决定了故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这会比较快。主键查询在InnoDB引擎下也会相当快,不过需要注意的是如果主键太长也会导致性能问题。大批的INSERT语句(在每个INSERT语句中写入多行,批量插入)在MyISAM下会快一些,但是UPDATE语句在InnoDB下则会更快一些,尤其是在并发量大的时候。

四、事务

  1.     **数据库引擎种类及区别**
    

数据库引擎是用于存储、处理和保护数据的核心服务。利用数据库引擎可控制访问权限并快速处理事务,从而满足企业内大多数需要处理大量数据的应用程序的要求。 使用数据库引擎创建用于联机事务处理或联机分析处理数据的关系数据库。这包括创建用于存储数据的表和用于查看、管理和保护数据安全的数据库对象(如索引、视图和存储过程)。

Innodb引擎: Innodb引擎提供了对数据库ACID事务的支持,并且实现了SQL标准的四种隔离级别,该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统,它本身其实就是基于MySQL后台的完整数据库系统,MySQL运行时Innodb会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持FULLTEXT类型的索引,而且它没有保存表的行数,当SELECT COUNT(*) FROM TABLE时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用Innodb引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表。

InnoDB是一个事务型的存储引擎,有行级锁定和外键约束,适用于以下的场合:

\1. 更新多的表,适合处理多重并发的更新请求。

\2. 支持事务。

\3. 可以从灾难中恢复(通过bin-log日志等)。

\4. 外键约束。只有他支持外键。

\5. 支持自动增加列属性auto_increment。

ACID

A 事务的原子性(Atomicity):指一个事务要么全部执行,要么不执行.也就是说一个事务不可能只执行了一半就停止了.比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱.不可能划了卡,而钱却没出来.这两步必须同时完成.要么就不完成.

C 事务的一致性(Consistency):指事务的运行并不改变数据库中数据的一致性.例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变.

I 独立性(Isolation):事务的独立性也有称作隔离性,是指两个以上的事务不会出现交错执行的状态.因为这样可能会导致数据不一致.

D 持久性(Durability):事务的持久性是指事务执行成功以后,该事务所对数据库所作的更改便是持久的保存在数据库之中,不会无缘无故的回滚.

2) 事务

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。

3) 事务和锁

事务是绑定到一起作为一个逻辑工作单元的sql语句分组,如任一个语句操作失败就视作整个操作失败。操作将回滚到操作前状态。

锁: 实现事务的关键。可以保证事务的完整性和并发性。可以使某些数据的拥有者在某段时间内不能使用某些数据或数据结构。

3. 存储过程和触发器

1) 存储过程和函数

存储过程重在处理数据,函数可以返回值

​ 存储过程是procedure用户定义的一系列sql语句的集合,涉及特定表或其它对象 的任务,用户可以调用存储过程

​ 函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定 用户表。

​ 存储过程和函数存在以下几个区别:

1) 一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强。

存储过程,功能强大,可以执行包括修改表等一系列数据库操作;用户定义函数不能用于执行一组修改全局数据库状态的操作。

​ 2)对于存储过程来说可以返回参数,如记录集,而函数只能返回值或者表对象。函数只 能返回一个变量;而存储过程可以返回多个。存储过程的参数可以有IN,OUT,INOUT三 种类型,而函数只能有IN类~~存储过程声明时不需要返回类型,而函数声明时需要描 述返回类型,且函数体中必须包含一个有效的RETURN语句。

​ 3)存储过程一般是作为一个独立的部分来执行( EXECUTE 语句执行),而函数可以作为 查询语句的一个部分来调用(SELECT调用),由于函数可以返回一个表对象,因此它可 以在查询语句中位于FROM关键字的后面。 SQL语句中不可用存储过程,而可以使用 函数。

2) 存储过程的概念、优点(或特点),写一个简单的存储过程

存储过程: 是一组为了完成特定功能的SQL语句集,利用SQL Server所提供的T-SQL语言所编写的程序,经 编译后存储在数据库中。

优点:

1.执行速度快。存储过程只在创造时进行编译,以后每次执行不需再重新编译,一般SQL语句每执行一次就编译一次

2.存储过程可重复使用

3.安全性高。(可设定只有某些用户才具有对指定存储过程的使用权)

4.当对数据库进行复杂操作时,可完成复杂的判断和较复杂的运算,可用存储过程封装起来

5.易于维护和集中控制。当企业规则变化时在服务器中改变存储过程即可,无须修改应用程序

简单的存储过程:

create proc select_query @year int 
as
select * from tmp where year=@year 

3) 触发器

  1.     触发器:触发器可以看成一个特殊的存储过程,存储过程是要显式调用去完成,而触发器可以自动完成。比如:当数据库中的表发生增删改操作时,对应的触发器就可以执行对应的PL/SQL语句块。
    
  2.     作用:维护表的完整性,记录表的修改来审计表的相关信息。
    

分为:

DML触发器: 当数据库服务器中发生数据操作语言事件时执行的存储过程。分为:After触发器和Instead Of触发器

DDL触发器 特殊的触发器,在响应数据定义语言(DDL)语句时触发。一般用于数据库中执行管理任务

DDL触发器是响应Create、 Alter或Drop开头的语句而激活

五、Sql

1) **Sql****语法

SELECT[DISTINCT] * |分组字段1 [别名][,分组字段2 [别名],] | 统计函数
           FROM表名 [表名],[表名,[别名],...]
           [WHERE 条件]
           [GROUP BY 分组字段1 [,分组字2,]]
           [HAVING 分组后的过滤条件]
           [ORDER BY 排序字段 ASCDESC [,排序字段 ASC |DESC]];

2) 左、右连接

1、内联接**(典型的联接运算,使用像 = 或 <> 之类的比较运算符)。包括相等联接和自然联接。
内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。例如,检索 students和courses表中学生标识号相同的所有行。
2**、外联接。**外联接可以是左向外联接、右向外联接或完整外部联接。
在 FROM子句中指定外联接时,可以由下列几组关键字中的一组指定:
1)LEFT JOIN或LEFT OUTER JOIN
左向外联接的结果集包括 LEFT OUTER子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
2)RIGHT JOIN 或 RIGHT OUTER JOIN
右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
3)FULL JOIN 或 FULL OUTER JOIN
完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。

3**、交叉联接** 交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
FROM 子句中的表或视图可通过内联接或完整外部联接按任意顺序指定;但是,用左或右向外联接指定表或视图时,表或视图的顺序很重要。有关使用左或右向外联接排列表的更多信息,请参见使用外联接。

3) 数据库设计的三范式

**第一范式1NF:**做到每列不可拆分

中国北京可以拆分成两个字段中国和北京

**第二范式2NF:**确保一个表只做一件事情

id name sex address grade

1 tom male beijin 80

对于grade可以专门定义一张成绩表

第三范式3NF:在满足2NF下,消除表中的传递依赖

推理规则:A->B,B->C,则A->C 如果这种情况存在,就要消除这种关系。目的是为 了使空间最省。

id name number price totalprice

1 风扇 20 200 4000

id是A , number和price是B totalprice是C

id ->number,price->totalprice

可以将totalprice字段删除,节省空间。

**反三范式(用空间换时间):**在进行数据库设计时,首先要确保表结构要达到第三 范式的要求。但在实际开发时,为满足业务的需要,会在符合三范式的表中加入冗 余字段,结果导致不符合第三范式的要求。

因为存在冗余字段需要维护它,如购销合同中的合同总金额就是一个冗余字段(由 于在页面上要显示总金额,不将计算的操作放在页面上进行,而是在数据库中计算 好直接获取到页面上。),就需要在添加货物或添加附件时,都要去更新购销合同中 的总金额。

4) 分页

六、Mysql

0表示第一条记录的索引号,索引号从0开始, 2表示最多选取二个记录

select * from users limit 0,2select * from users limit 2;

#查询出users第2条到第4条记录#
select * from users limit 1,3;

Oracle

方式一使用集合减运算: 显示emp表中3-8条记录

select rownum “伪列”,emp.* from emp where rownum<=8

minus

select rownum,emp.* from emp where rownum<=2;

方式二:使用子查询,在from子句中使用,重点

select xx.*

from (select rownum ids,emp.* from emp where rownum<=8) xx

where ids>=3;

其中的xx是子查询结果表的别名

**5) **唯一序列号

Mysql: auto_increment

Oracle:sequence

分布式系统:snowflake 雪花算法,适用于订单号

用sql来成生特殊的序列号​

常用SQL语句

1、 创建数据库

CREATE DATABASE database-name;

2、 删除数据库

 DROP DATABASE database-name;

3、 创建新表

create table depart (
    dept_id int(11) NOT NULL AUTO_INCREMENT, 
 	dept_name varchar(255) DEFAULT NULL, 
    PRIMARY KEY (dept_id)
); 

根据已有的表创建新表:
create table tab_new like tab_old (使用旧表B创建新表A)
备注:此种方式在将表B复制到A时候会将表B完整的字段结构和索引复制到表A中来
create table tab_new as select col1,col2… from tab_old definition only
备注:此种方式只会将表B的字段结构复制到表A中来,但不会复制表B中的索引到表A中来。这种方式比较灵活可以在复制原表表结构的同时指定要复制哪些字段,并且自身复制表也可以根据需要增加字段结构。
create table as select 会将原表中的数据完整复制一份,但表结构中的索引会丢失。
create table like 只会完整复制原表的建表语句,但不会复制数据。

4、 删除新表

drop table tabname;

5、 增加一个列

alter table tabname add column column_name type

6、 添加主键: Alter table tabname add primary key(col)
删除主键: Alter table tabname drop primary key
​ 一个数据表只可以有一个主键,所以不存在删除某一列的主键.

7、 创建索引:create [unique] index idxname on tabname(col….)
删除索引:drop index idxname
​ 注:索引是不可更改的,想更改必须删除重新建。

8、 创建视图:create view viewname as select statement
​ 删除视图:drop view viewname

9. 几个简单的基本的sql语句 ------------------------

 #选择:#
 	select * from table1 where 范围 
 #插入:#
 	insert into table1(field1,field2) values(value1,value2) 
 #删除:#
 	delete from table1 where 范围 
 #更新:#
 	update table1 set field1=value1 where 范围 
 #查找:#
 	select * from table1 where field1 like%value1%#排序:#
 	select * from table1 order by field1,field2 [desc] 
 		desc:降序,asc:升序 
 #总数:#
 	select count as totalcount from table1 
 #求和:#
 	select sum(field1) as sumvalue from table1 
 #平均:#
 	select avg(field1) as avgvalue from table1 
 #最大:#
 	select max(field1) as maxvalue from table1 
 #最小:#
 	select min(field1) as minvalue from table1

10、 分组:Group by:
一张表,一旦分组完成后,查询后只能得到组相关的信息。
组相关的信息:(统计信息) count,sum,max,min,avg 分组的标准)

11、 复制表

select * into b from a where 1<>1(仅用于SQlServer) 

12、 子查询(表名1:a 表名2:b)

select a,b,c from a where a IN (select d from b ) 

13、 显示文章、提交人和最后回复时间

select a.title,a.username,b.adddate 
	from table a,(select max(adddate) adddate 
                  from table where table.title=a.title) b

14、 视图查询(表名1:a )

select * from (SELECT a,b,c FROM a) T where t.a > 1;

15、 between的用法,between限制查询数据范围时包括了边界值,not between不包括

select * from table1 where time between time1 and time2 
select a,b,c, from table1 where a not between 数值1 and 数值2

16、 in 的使用方法

select * from table1 where a [not] in (‘值1,’值2,’值4,’值6)

17、 两张关联表,删除主表中已经在副表中没有的信息

delete from table1 
	where not exists ( 
        select * from table2 
        	where table1.field1=table2.field1 )

18、 日程安排提前五分钟提醒

select * from 日程安排 where datediff(minute,f开始时间,getdate())>5

19、 前10条记录


select top 10 * form table1 where 范围

20、 选择在每一组b值相同的数据中对应的a最大的记录的所有信息(类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排名,等等.)

select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b)

21、 包括所有在 TableA 中但不在 TableB和TableC 中的行并消除所有重复行而派生出一个结果表

(select a from tableA ) except (select a from tableB) except (select a from tableC)

22、 随机取出10条数据

select top 10 * from tablename order by newid()

七、优化

1) 维护数据库完整性和一致性

​ 尽可能使用约束:check、主外键、非空字段等,方便效率高;

​ 其次使用触发器。

​ 最后是自写业务逻辑

2) 连接池优点

JDBC数据库连接池的必要性 没使用数据库连接池之前

  1. 使用时连接数据库,使用后断开数据库。
  2. 对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库
  3. 如果成千上万的用户同时使用数据库,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃

新的资源分配手段对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置实现某一应用最大可用数据库连接数的限制避免某一应用独占所有的数据库资源.

统一的连接管理,避免数据库连接泄露在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。

数据库连接池技术的优点

(1)资源重用:由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。

(2)更快的系统反应速度:数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间

3) 数据库优化方面的经验

​ 1) 用PreparedStatement 一般来说比Statement性能高,而且可以防止SQL注入攻击

​ 2) 有外键约束会影响插入和删除性能,如果程序能够保证数据的完整性,那在设计数据库时就去掉外键。在大型电商中尽量使用单表

​ 3) 表中允许适当冗余,利用空间换时间,提高查询效率

​ 4) 用户名和密码单独从用户表中独立出来。 在登录业务中只需要查询用户名和密码。

​ 5) 使用索引。

select尽量不用*,即使查询全部字段也要尽量全部写出来

能用连接查询绝不用子查询,因为子查询的的底层就是连接查询

合理使用索引提升效率(索引的本质是提高效率的机制,本质上是一棵树)

为经常出现在where子句中的列创建索引

为经常出现在order by,distinct中的字段建立索引

不要经常在DML(检索或修改)的表上建立索引(容易造成索引不准, 解决方法为rebuild重新建立,任何 DML操作都是更新index,这是代价)

不要在小表上简历索引(索引简历和维护也是要消耗资源的,小表上建立 索引会造成索引的维护时间大于数据查询时间)

限制表上的索引数目,索引并不是越多越好(索引会占用表空间,还要额 外的维护)

删除很少被使用的,不合理的索引

  1.     **数据库数据量过大,数据库接近崩溃的时候怎么办?**
    

1、索引优化和SQL语句优化是必须的,避免模糊查询和非索引查询,删改操作根据聚集索引进行,删改操作太频繁的话还是需要考虑分表

2、看需求,如果需求不限制,那就分表

3、一般都是把历史数据定期转存其他表(一样的表名后加年月例如TABLE201205)归档。这样该表本年度的查询的压力也小点(90%查询量集中在本年度),即使查询历史数据也不影响性能,强力推荐!

4结合你的业务去优化表结构。有时候可以考虑用空间去换时间。

八、练习

1) 行列转换

CREATE TABLE Scores(
	ID INT,
	Student varchar(10),
	Subject varchar(10),
	Score INT
);

INSERT INTO Scores VALUES(2, '张三', '语文', 93);
INSERT INTO Scores VALUES(3, '张三', '英语', 90);
INSERT INTO Scores VALUES(5, '李四', '语文', 88);
INSERT INTO Scores VALUES(7, '李四', '英语', 78);
INSERT INTO Scores VALUES(8, '王五', '语文', 98);
INSERT INTO Scores VALUES(9, '王五', '英语', 86);
   
SELECT student, max(yw) as '语文', max(yu) as '英语'
	FROM(
		SELECT student,
			CASE subject WHEN '语文' THEN score ELSE 0 END as yw,
			CASE subject WHEN '英语' THEN score ELSE 0 END as yu
		FROM Scores
	) A           
	GROUP BY student
  1. 删除重复数据
DELETE FROM person WHERE id NOT IN (SELECT tab.id FROM 
     (SELECT  MAX(id) AS id FROM person GROUP BY NAME ,PASSWORD) AS tab )

​ 这里面的第一个关键点是如何确定哪些数据是重复的呢?这里通过 group by name,password来确定。只要同时满足这两个条件,就可以归为相同一组数据

​ 第二个关键点是通过max(id) 确定保留下的数据是id最大的。

​ 第三点是 最内层的select语句的查询结果需要重启起名称为tab ,再从tab中查出id .如果缺少这一步,语句会报错。

​ 第四步是根据 id not in 删除除最大id外的所有数据

3) 省市县三级联动数据库是怎么设计的

CREATE TABLE t_area (
  id int(11) NOT NULL auto_increment,
  area_id varchar(50) default NULL,
  area_name varchar(60) default NULL,
  area_parentId varchar(6) default NULL,
  primary key(id)
) ENGINE=MyISAM AUTO_INCREMENT=3145 DEFAULT CHARSET=utf8;

INSERT INTO t_area VALUES (1,'100','河北省','0');
INSERT INTO t_area VALUES (2,'220999','石家庄市','100');

Sql练习1

Student(S#,Sname,Sage,Ssex) 学生表 
Course(C#,Cname,T#) 课程表 
SC(S#,C#,score) 成绩表 
Teacher(T#,Tname) 教师表

问题:
1、查询“001”课程比“002”课程成绩高的所有学生的学号;

select a.S# 
	from (select s#,score from SC where C#='001') a,
		 (select s#,score from SC where C#='002') b 
     where a.score > b.score and a.s# = b.s#; 

2、查询平均成绩大于60分的同学的学号和平均成绩;

 ```sql

select S#,avg(score)
​ from sc
​ group by S# having avg(score) >60;
​ ```

3、查询所有同学的学号、姓名、选课数、总成绩;

select Student.S#,Student.Sname,count(SC.C#),sum(score) 
   from Student left Outer join SC on Student.S#=SC.S# 
   group by Student.S#,Sname 

4、查询姓“李”的老师的个数;

select count(distinct(Tname)) 
   from Teacher 
   where Tname like '李%'; 

5、查询没学过“叶平”老师课的同学的学号、姓名;

Student.S#,Student.Sname 
     from Student  
     where S# not in (select distinct( SC.S#) 
                      	from SC,Course,Teacher 
                      	where  SC.C#=Course.C# and Teacher.T#=Course.T# and Teacher.Tname='叶平'); 

6、查询学过“001”并且也学过编号“002”课程的同学的学号、姓名;

select Student.S#,Student.Sname 
	from Student,SC 
	where Student.S#=SC.S# and SC.C#='001'and 
		exists( Select * from SC as SC_2 
               		where SC_2.S#=SC.S# and SC_2.C#='002'); 

7、查询学过“叶平”老师所教的所有课的同学的学号、姓名;

select S#,Sname 
   from Student 
   where S# in (select S# from SC ,Course ,Teacher where SC.C#=Course.C# and Teacher.T#=Course.T# and Teacher.Tname='叶平' group by S# having count(SC.C#)=(select count(C#) from 
Course,Teacher  where Teacher.T#=Course.T# and Tname='叶平')); 

8、查询课程编号“002”的成绩比课程编号“001”课程低的所有同学的学号、姓名;

Select S#,Sname from (select Student.S#,Student.Sname,score ,(select score from SC SC_2 where SC_2.S#=Student.S# and SC_2.C#='002') score2 
   from Student,SC where Student.S#=SC.S# and C#='001') S_2 where score2 <score; 

9、查询所有课程成绩小于60分的同学的学号、姓名;

 select S#,Sname 
   from Student 
   where S# not in (select Student.S# from Student,SC where S.S#=SC.S# and score>**60**); 

10、查询没有学全所有课的同学的学号、姓名;

select Student.S#,Student.Sname 
     from Student,SC 
     where Student.S#=SC.S# group by  Student.S#,Student.Sname having count(C#) <(select count(C#) from Course); 

11、查询至少有一门课与学号为“1001”的同学所学相同的同学的学号和姓名;


12、查询至少学过学号为“001”同学所有一门课的其他同学学号和姓名;

select distinct SC.S#,Sname 
     from Student,SC 
     where Student.S#=SC.S# and C# in (select C# from SC where S#='001'); 

13、把“SC”表中“叶平”老师教的课的成绩都更改为此课程的平均成绩;

 update SC set score=(select avg(SC_2.score) 
     from SC SC_2 
     where SC_2.C#=SC.C# ) from Course,Teacher where Course.C#=SC.C# and Course.T#=Teacher.T# and Teacher.Tname='叶平'); 

14、查询和“1002”号的同学学习的课程完全相同的其他同学学号和姓名;

select S# from SC where C# in (select C# from SC where S#='1002') 
     group by
S# having count(*)=(select count(*) from SC where S#='1002'); 

15、删除学习“叶平”老师课的SC表记录;

Delect SC 
     from course ,Teacher  
     where Course.C#=SC.C# and Course.T#= Teacher.T# and Tname='叶平'; 

16、向SC表中插入一些记录,这些记录要求符合以下条件:没有上过编号“003”课程的同学学号、2
​ 号课的平均成绩;

 Insert SC select S#,'002',(Select avg(score) 
     from SC where C#='002') from Student where S# not in (Select S# from SC where C#='002'); 

17、按平均成绩从高到低显示所有学生的“数据库”、“企业管理”、“英语”三门的课程成绩,按如下形式显示: 学生ID,数据库,企业管理,英语,有效课程数,有效平均分

 SELECT S# as 学生ID 
         ,(SELECT score FROM SC WHERE SC.S#=t.S# AND C#='004') AS 数据库
,(SELECT score FROM SC WHERE SC.S#=t.S# AND C#='001') AS 企业管理 
         ,(SELECT score FROM SC WHERE SC.S#=t.S# AND C#='006') AS 英语 
         ,COUNT(*) AS 有效课程数, AVG(t.score) AS 平均成绩 
     FROM SC AS t 
     GROUP BY S# 
     ORDER BY avg(t.score)  

18、查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分

SELECT L.C# As 课程ID,L.score AS 最高分,R.score AS 最低分 
     FROM SC L ,SC AS R 
     WHERE L.C# = R.C# and 
         L.score = (SELECT MAX(IL.score) 
                       FROM SC AS IL,Student AS IM 
                       WHERE L.C# = IL.C# and IM.S#=IL.S# 
                       GROUP BY IL.C#) 
         AND 
         R.Score
= (SELECT MIN(IR.score) 
                       FROM SC AS IR 
                       WHERE R.C# = IR.C# 
                   GROUP BY IR.C# 
                     ); 

19、按各科平均成绩从低到高和及格率的百分数从高到低顺序

SELECT t.C# AS 课程号,max(course.Cname)AS 课程名,isnull(AVG(score),**0**) AS 平均成绩 
         ,**100** * SUM(CASE WHEN  isnull(score,**0**)>=**60** THEN **1** ELSE **0** END)/COUNT(*) AS 及格百分数 
     FROM SC T,Course 
     where t.C#=course.C# 
     GROUP BY t.C# 
     ORDER BY **100** * SUM(CASE WHEN  isnull(score,**0**)>=**60** THEN **1** ELSE
0 END)/COUNT(*) DESC 

20、查询如下课程平均成绩和及格率的百分数(用"1行"显示): 企业管理(001),马克思(002),OO&UML (003),数据库(004

SELECT SUM(CASE WHEN C# ='001' THEN score ELSE **0** END)/SUM(CASE C# WHEN '001' THEN **1** ELSE **0** END) AS 企业管理平均分 
         ,**100** * SUM(CASE WHEN C# = '001' AND score >= **60** THEN **1** ELSE **0** END)/SUM(CASE WHEN C# = '001' THEN **1** ELSE **0** END

) AS 企业管理及格百分数 
         ,SUM(CASE WHEN C# = '002' THEN score ELSE **0** END)/SUM(CASE C# WHEN '002' THEN **1** ELSE **0** END) AS 马克思平均分 
         ,**100** * SUM(CASE WHEN C# = '002' AND score >= **60** THEN **1** ELSE **0** END)/SUM(CASE WHEN C# = '002' THEN **1** ELSE **0** END) AS 马克思及格百分数 
         ,SUM(CASE WHEN C# = '003' THEN score ELSE **0** END)/

SUM(CASE C# WHEN '003' THEN **1** ELSE **0** END) AS UML平均分 
         ,**100** * SUM(CASE WHEN C# = '003' AND score >= **60** THEN **1** ELSE **0** END)/SUM(CASE WHEN C# = '003' THEN **1** ELSE **0** END) AS UML及格百分数 
         ,SUM(CASE WHEN C# = '004' THEN score ELSE **0** END)/SUM(CASE C# WHEN '004' THEN **1** ELSE **0** END) AS 数据库平均分 
         ,**100**

\* SUM(CASE WHEN C# = '004' AND score >= **60** THEN **1** ELSE **0** END)/SUM(CASE WHEN C# = '004' THEN **1** ELSE **0** END) AS 数据库及格百分数 
   FROM SC 

21、查询不同老师所教不同课程平均分从高到低显示

 SELECT max(Z.T#) AS 教师ID, MAX(Z.Tname) AS 教师姓名, 
 			C.C# AS 课程ID,MAX(C.Cname) AS 课程名称, AVG(Score) AS 平均成绩 
     FROM SC AS T,Course AS C ,Teacher AS Z 
     where T.C#=C.C# and C.T#=Z.T# 
     GROUP BY C.C# 
     ORDER BY AVG(Score) DESC 

22、查询如下课程成绩第 3 名到第 6 名的学生成绩单:企业管理(001),马克思(002),UML (003),数据库(004)[学生ID],[学生姓名],企业管理,马克思,UML,数据库,平均成绩

SELECT  DISTINCT top **3** 
       SC.S# As 学生学号, 
         Student.Sname AS 学生姓名 , 
       T1.score AS 企业管理, 
       T2.score AS 马克思, 
       T3.score AS UML, 
       T4.score AS 数据库, 
       ISNULL(T1.score,**0**) + ISNULL(T2.score,**0**) + ISNULL(T3.score,**0**) + ISNULL(T4.score,**0**) as 总分 
       FROM Student,SC  LEFT JOIN SC AS T1 
                       ON SC.S# = T1.S# AND T1.C# = '001' 
             LEFT JOIN SC AS T2 
                       ON SC.S# = T2.S# AND T2.C# = '002' 
             LEFT JOIN SC AS T3 
                       ON SC.S# =T3.S# AND T3.C# = '003' 
             LEFT JOIN SC AS T4 
                       ON SC.S# = T4.S# AND T4.C# = '004' 
       WHERE student.S#=SC.S# and 
       ISNULL(T1.score,**0**) + ISNULL(T2.score,**0**) + ISNULL(T3.score,**0**) + ISNULL(T4.score,**0**) 
       NOT IN 
       (SELECT 
             DISTINCT 
             TOP **15** WITH TIES 
             ISNULL(T1.score,**0**) + ISNULL(T2.score,**0**) + ISNULL(T3.score,**0**) + ISNULL(T4.score,**0**) 
       FROM sc 
             LEFT JOIN sc AS T1 
                       ON sc.S# = T1.S# AND T1.C#= 'k1' 
             LEFT JOIN sc AS T2 
                       ON sc.S# = T2.S# AND T2.C# = 'k2' 
             LEFT JOIN sc AS T3 
                       ON sc.S# = T3.S# AND T3.C# = 'k3' 
             LEFT JOIN sc AS T4 
                       ON sc.S# = T4.S# AND T4.C# = 'k4' 
       ORDER BY ISNULL(T1.score,**0**) + ISNULL(T2.score,**0**) + ISNULL(T3.score,**0**) + ISNULL(T4.score,0) DESC); 

23、统计列印各科成绩,各分数段人数:课程ID,课程名称,[100-85],[85-70],[70-60],[ <60]

 ```sql

SELECT SC.C# as 课程ID, Cname
as 课程名称
​ ,SUM(CASE WHEN score BETWEEN 85 AND 100 THEN 1 ELSE 0 END) AS [100 - 85]
​ ,SUM(CASE WHEN score BETWEEN 70 AND 85 THEN 1 ELSE 0 END) AS [85 - 70]
​ ,SUM(CASE WHEN score BETWEEN 60 AND 70 THEN 1 ELSE 0 END) AS [70 - 60]
​ ,SUM(CASE WHEN score < 60 THEN 1 ELSE 0 END) AS [60 -]
​ FROM SC,Coursewhere SC.C#=Course.C#
​ GROUP BY SC.C#,Cname;
​ ```

24、查询学生平均成绩及其名次

SELECT 1+(SELECT COUNT( distinct 平均成绩) 
              FROM (SELECT S#,AVG(score) AS 平均成绩 
                       FROM SC 
                   GROUP BY S# 
                   ) AS T1 
             WHERE 平均成绩 > T2.平均成绩) as 名次, 
       S# as 学生学号,平均成绩 
     FROM (SELECT S#,AVG(score) 平均成绩 
             FROM SC 
         GROUP BY S# 
         ) AS T2 
     ORDER BY 平均成绩 desc; 

25、查询各科成绩前三名的记录:(不考虑成绩并列情况)

SELECT t1.S# as 学生ID,t1.C# as 课程ID,Score as 分数 
       FROM SC t1 
       WHERE score IN (SELECT TOP **3** score 
               FROM SC 
               WHERE t1.C#= C#     ORDER BY score DESC 
               ) 
       ORDER BY t1.C#; 

26、查询每门课程被选修的学生数

select c#,count(S#) from sc group by C#; 

27、查询出只选修了一门课程的全部学生的学号和姓名

select SC.S#,Student.Sname,count(C#) AS 选课数 
   from SC ,Student 
   where SC.S#=Student.S# group by SC.S# ,Student.Sname having count(C#)=1; 

28、查询男生、女生人数

Select count(Ssex) as 男生人数 from Student group by Ssex having Ssex='男'; 
Select count(Ssex) as 女生人数 from Student group by Ssex having Ssex='女'

29、查询姓“张”的学生名单

SELECT Sname FROM Student WHERE Sname like '张%'; 

30、查询同名同性学生名单,并统计同名人数

select Sname,count(*) from Studentgroup by Sname having  count(*)>1;

31、1981年出生的学生名单(注:Student表中Sage列的类型是datetime)

select Sname,  CONVERT(char (11),DATEPART(year,Sage)) as age 
     from student 
     where  CONVERT(char(11),DATEPART(year,Sage))='1981'; 

32、查询每门课程的平均成绩,结果按平均成绩升序排列,平均成绩相同时,按课程号降序排列

Select C#,Avg(score) from SC group by C# order by Avg(score),C# DESC ; 

33、查询平均成绩大于85的所有学生的学号、姓名和平均成绩

select Sname,SC.S# ,avg(score) 
     from Student,SC 
     where Student.S#=SC.S# group by SC.S#,Sname having    avg(score)>85; 

34、查询课程名称为“数据库”,且分数低于60的学生姓名和分数

Select Sname,isnull(score, 0) 
     from Student,SC,Course 
     where SC.S#=Student.S# and SC.C#=Course.C#and  Course.Cname='数据库'and score <60; 

35、查询所有学生的选课情况;

SELECT SC.S#,SC.C#,Sname,Cname 
     FROM SC,Student,Course 
     where SC.S#=Student.S# and SC.C#=Course.C# ; 

36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数;

SELECT  distinct student.S#,student.Sname,SC.C#,SC.score 
     FROM student,Sc 
     WHERE SC.score>=70 AND SC.S#=student.S#; 

37、查询不及格的课程,并按课程号从大到小排列

select c# from sc where scor e <**60** order by C# ; 

38、查询课程编号为003且课程成绩在80分以上的学生的学号和姓名;

select SC.S#,Student.Sname 
	from SC,Student 
	where SC.S#=Student.S# and Score>80 and C#='003'; 

39、求选了课程的学生人数

select count(*) from sc; 

40、查询选修“叶平”老师所授课程的学生中,成绩最高的学生姓名及其成绩

select Student.Sname,score 
     from Student,SC,Course C,Teacher 
     where Student.S#=SC.S# and SC.C#=C.C# and
			C.T#=Teacher.T# and Teacher.Tname='叶平' 
			and SC.score=(select max(score) 
                          	from SC where C#=C.C# ); 

41、查询各个课程及相应的选修人数

select count(*) from sc group by C#; 

42、查询不同课程成绩相同的学生的学号、课程号、学生成绩 *

select distinct  A.S#,B.score 
	from SC A  ,SC B 
	where A.Score=B.Score and A.C# <>B.C# ; 

43、查询每门功成绩最好的前两名

SELECT t1.S# as 学生ID,t1.C# as 课程ID,Score as 分数 
       FROM SC t1 
       WHERE score IN (SELECT TOP **2** score 
               FROM SC 
               WHERE t1.C#= C# 
             ORDER BY score DESC 
               ) 
       ORDER BY t1.C#; 

44、统计每门课程的学生选修人数(超过10人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,查询结果按人数降序排列,若人数相同,按课程号升序排列

select  C# as 课程号,count(*) as 人数
     from  sc  
     group  by  C# 
     order  by  count(*) desc,c#  

45、检索至少选修两门课程的学生学号 *

 select  S#  
     from  sc  
     group  by  s# 
     having  count(*) >= 2 

46、查询全部学生都选修的课程的课程号和课程名

select  C#,Cname  
     from  Course  
     where  C#  in  (select  c#  from  sc group  by  c#)  

47、查询没学过“叶平”老师讲授的任一门课程的学生姓名

 select Sname 
 	from Student 
 	where S# not in (
        	select S# from Course,Teacher,SC 
        		where Course.T#=Teacher.T# and SC.C#=course.C# and Tname='叶平'); 

48、查询两门以上不及格课程的同学的学号及其平均成绩

select S#,avg(isnull(score,**0**)) 
	from SC 
	where S# in (
        select S# from SC 
        	where score <60 group by S# having count(*)>2)
     group by S#; 

49、检索“004”课程分数小于60,按分数降序排列的同学学号

select S# from SC where C#='004'and score <60 order by score desc; 

50、删除“002”同学的“001”课程的成绩

 delete from Sc where S#='001'and C#='001';

算法

1. 冒泡排序

 /*
    冒泡排序。
    比较方式:相邻两个元素进行比较。如果满足条件就进行位置置换。
    原理:内循环结束一次,最值出现在尾角标位置。
*/
public static void bubbleSort(int[] arr){
     for(int x=0; x<arr.length-1; x++){
         //-1:避免角标越界。
         //-x:让每次参与比较的元减。
         for(int y=0; y<arr.length-x-1; y++){
            if(arr[y]>arr[y+1]){
                int temp = arr[y];
                arr[y] = arr[y+1];
                arr[y+1] = temp;
            }
         }
     }
}

2. 折半查找(二分法)

/*
    为了提高查找效率,可使用折半查找的方式,注意:这种查找只对有序的数组有效。
    这种方式也成为二分查找法。
*/
public static int halfSeach(int[] arr,int key)
    {
       int min,mid,max;
       min = 0;
       max = arr.length-1;
       mid = (max+min)/2;
 
       while(arr[mid]!=key)
       {
           if(key>arr[mid])
              min = mid + 1;
           else if(key<arr[mid])
              max = mid - 1;
           
           if(min>max)
              return -1;
    
          mid = (max+min)/2;
       }
       return mid;
    }

3. 递归算法题

第1个人10,第2个比第1个人大2岁,依次递推,请用递归方式计算出第8个人多大?

package dao;
public class A {
    public static void main(String[] args) {
        System.out.println(computeAge(8));      
    }
    public static int   computeAge(int n) {
        if (n == 1)
            return 10;
        return computeAge(n-1) + 2;
    }
    public static void toBinary(int n, StringBuffer result) {
        if(n/2!=0)
            toBinary(n/2, result);
        result.append(n%2);
    }
}  

4. 输入一行字符,请统计出这中间的空格、英文字母、数字以及其他字符的个数

5. 完数

一个数如果恰好等于它的因子之和,这个数就称为 "完数 "。例如6=1+2+3.编程 找出1000以内的所有完数。

6. 给一个不多于5位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。

7. 有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数

8. 如何把一段逗号分割的字符串转换成一个数组?

1)用正则表达式,代码大概为:String [] result = orgStr.split(“,”, -1);

2)用 StingTokenizer ,代码为:

StringTokenizer tokener = new StringTokenizer(s, “,”);

String[] result = new String[tokener.countTokens()];

Integer i = 0;

while (tokener.hasMoreTokens()) {

result[i++] = tokener.nextToken();

}

------------------------------

发布了92 篇原创文章 · 获赞 49 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Xxacker/article/details/88160931
今日推荐