struts2、hibernate的知识点
以下内容是我在复习struts2、hibernate和spring的时候记下得到,部分书上找不到的内容来自网络
以下是网络部分的原文网站:
http://blog.csdn.net/frankaqi/article/details/51873557
http://blog.sina.com.cn/s/blog_75115c8d0102vy1i.html
http://www.cnblogs.com/xiohao/p/3561175.html
http://www.cnblogs.com/oumyye/p/4356149.html
https://yq.aliyun.com/articles/18651
http://www.cnblogs.com/tengpan-cn/p/5970796.html
http://blog.csdn.net/chenleixing/article/details/44572637
http://blog.csdn.net/wild_elegance_k/article/details/47683725
http://www.cnblogs.com/joyang/p/4973435.html
http://blog.csdn.net/firejuly/article/details/8190229
http://blog.csdn.net/ljl18566743868/article/details/52130583
http://www.cnblogs.com/langtianya/p/4628967.html
Struts2
1:Struts2与Struts1的联系
struts1与struts2都是mvc框架的经典实现模式。
Struts2是Struts1和Webwork结合的产物,但却是以WebWork的设计思想为核心,采用拦截器的机制来处理用户的请求,所以Struts 2可以理解为WebWork的更新产品
2:Struts2与Struts1的区别
1:因为Stuts2是针对拦截器开发的,也就是所谓的AOP思想,可以配置多个action,用起来比较方便,依赖性也更弱一点
2:因为请求之前的拦截器有一些注入的操作,速度相对Stuts1来说慢一点。
3:Struts1是使用ActionServlet做为其中心处理器,Struts2则使用一个拦截器(FilterDispatcher)做为其中心处理器,这样做的一个好处就是将Action类和Servlet API进行了分离。Struts1的Action类依赖于Servlet API,Struts2则不依赖于Servlet API
4:Struts1的Action类是单例模式,必须设计成线程安全的,Struts2则为每一个请求产生一个实例
5:因为Struts1依赖于Servlet API这些Web元素,因此对Struts1的Action进行测试的时候是很困难的,需要借助于其他的测试工具,Struts2的Action可以像测试其他的一些Model层的Service类一样进行测试
6:Struts1的Action与View通过ActionForm或者其子类进行数据传递,struts2可以通过POJO(Plain Ordinary Java Object 简单的Java对象)进行数据传递
7:Struts1绑定了JSTL,为页面的编写带来方便,Struts2整合了OGNL,也可以使用JSTL,因此,Struts2下的表达式语言更加强大
3:Struts2的简单处理流程
1:客户端产生一个HttpServletRequest的请求
2:客户端产生的请求被提交到一系列的标准过滤器(Filter)组建链中
3:核心控制器组建FilterDispatcher被调用,并询问ActionMapper来决定这个请求是否需要调用某个Action
4:ActionMapper决定要调用那一个Action,FilterDispatcher把请求交给ActionProxy。
5:ActionProxy通过Configurate Manager询问Struts配置文件,找到要调用的Action类
6: ActionProxy创建一个ActionInvocation实例
7: ActionInvocation实例使用命令模式来调用,回调Action的exeute方法
8: 一旦Action执行完毕,ActionInvocation负责根据Struts.xml的配置返回结果。
4:Struts2如何进行校验
A:编程校验
1:针对所有方法:继承ActionSupport,重写validate方法。
2:针对某个方法进行效验:validateXxx方法。
B:校验框架
每个Action类有一个校验文件,命名为 Action类名-validation.xml,且与Action类同目录,这是对action里面所有的方法进行校验。
对Action里面的指定方法做校验使用Action的类名-访问路径_方法名-validation.xml。
5:什么是OGNL,有什么用途?
OGNL的全程是(Object-Graph Navigation Language 对象图导航语言)
它是一种功能强大的开源表达式语言,使用这种表达式语言,可以通过某种表达式语法,存取java对象的任意属性,调用java对象的方法,同时能够自动实现必要的类型转换
struts2默认的表达式语言就是OGNL,它具有以下特点:
1:支持对象方法调用:对象名.方法名
2:支持类静态方法调用和值访问:@包括路径的类全名@方法名(参数)
3:支持赋值操作和表达式串联
6:模型驱动与属性驱动是什么
模型驱动与属性驱动都是用来封装数据的。
A:模型驱动:模型驱动指通过JavaBean模型进行数据传递
在实现类中实现ModelDriven<T>接口使用泛型把属性类封装起来,重写getModel()方法,然后在实现类里创建一个属性类的实例。
B:属性驱动:属性驱动是指通过字段进行数据传递
属性驱动分为
1):基本数据类型字段驱动方式的数据传递
直接在action里面定义各种java基本 数据类型的字段
2):直接使用域对象字段驱动方式的数据传递
就方法A而言,当需要传入的数据过于庞大的时候,会使action非常臃肿,不够简洁所以需要使用方法B(这也是我常用的方法)。将属性和响应的get/set方法提取出来单独作为一个域对象封装起来,在action里面直接使用域对象
7:dispatcher和redirect
dispatcher结果类型是将请求转发到jsp视图资源 ---->请求转发(是struts2的默认结果类型)
redirect类型是将请求重定向到jsp视图资源或者重定向到action---->重定向 (重定向请求,将丢失所有参数,包括action的处理结果)
<action> <result type="redirect"></result> <result type="dispatcher"></result> </action>
8:Struts2中result type的类型有哪些
1:dispatcher : 默认的类型,相当于servlet的foward,服务器端跳转。客户端看到的是struts2中配置的地址,而不是真正页面的地址。一般用于跳转到jsp页面
2:redirect、redirect-action : 页面重定向,客户端跳转;前者用于跳转到jsp页面,后者用于跳转到action
3:chain : 将请求转发到一个action
4:stream : 一般用于下载文件用
5:PlainText : 普通文本
6:Velocity(Velocity) : 用于与Velocity技术的集成
7:XSLT(xslt) : 用于与XML/XSLT技术的集成
8:HttpHeader : 返回报文头给用户
9:json
9:Struts2中的拦截器有什么用?
拦截器是struts2的核心组成部分,拦截器提供了一种机制,使得开发者可以定义一个特定的功能模块,这个模块会在action执行之前或者之后执行用于对数据进行处理,也可以在action执行之前阻止action执行。同时拦截器也可以让你将通用代码模块作为一个可重用的类。拦截器是AOP(面向切面编程)的一种实现策略
10:列举框架提供的拦截器名称
1:conversation:这是一个处理类型转换错误的拦截器,它负责将类型转换错误从ActionContext中取出,并转换成Action的FieldError错误。
2:Exception:这个拦截器负责处理异常,它将异常映射成结果。
3:fileUpload:这个拦截器主要用于文件上传,它负责解析表单中文件域的内容。
4:i18n:这是支持国际化的拦截器,它负责把所选的语言、区域放入用户Session中。
5:params:这是最基本的一个拦截器,它负责解析HTTP请求中的参数,并将参数值设置成Action对应的属性值。
6:scope:这是范围转换拦截器,它可以将Action状态信息保存到HttpSession范围,或者保存到ServletContext范围内。
7:token:这个拦截器主要用于阻止重复提交,它检查传到Action中的token,从而防止多次提交。
只要我们定义的包继承了struts-default包,就可以直接使用这些拦截器。
11:struts2常用标签
12:struts2上传文件
1:创建web项目并导入相应包
2:修改web.xml文件里面的配置
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- 指定struts2的核心过滤器 --> <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:在WebRoot目录下面创建upload文件夹
4:创建上传文件对应test.ServletTest
package test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.imageio.stream.FileImageInputStream; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class ServletTest extends ActionSupport{ private static final long seriaVersionUID=1L; private File picture;//提交过来的文件 private String pictureFileName;//提交的文件名 private String pictureContentType;//提交的文件类型 private String mess; public File getPicture() { return picture; } public void setPicture(File picture) { this.picture = picture; } public String getPictureFileName() { return pictureFileName; } public void setPictureFileName(String pictureFileName) { this.pictureFileName = pictureFileName; } public String getPictureContentType() { return pictureContentType; } public void setPictureContentType(String pictureContentType) { this.pictureContentType = pictureContentType; } public String getMess() { return mess; } public void setMess(String mess) { this.mess = mess; } public String execute() throws IOException{ System.out.println("提交过来的文件: "+picture); System.out.println("提交的文件名: "+pictureFileName); System.out.println("提交的文件类型: "+pictureContentType); //判断是否有提交的文件 if(picture==null){ mess="请上传图片!!"; return "wrong"; } //文件输出流 InputStream is = new FileInputStream(picture); //设置文件保存目录 String uploadPath=ServletActionContext.getServletContext().getRealPath("\\upload"); //设置目标文件 File toFile = new File(uploadPath,this.getPictureFileName()); //文件输出流 OutputStream os = new FileOutputStream(toFile); byte[]buffer = new byte[1024]; int length=0; //读取file文件到tofile文件中 while (-1 != (length = is.read(buffer))) { os.write(buffer, 0, length); } is.close(); os.close(); return "success"; } }
5:创建对应的struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="default" extends="struts-default"> <action name="upload" class="test.ServletTest" method="execute"> <result name="success">/success.jsp</result> <result name="wrong">/index.jsp</result> </action> </package> </struts>
6:创建对应的success.jsp和index.jsp
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <%@taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>主页面</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> ${mess } <s:form action="upload" method="post" enctype="multipart/form-data" > <s:file name="picture" label="上传图片" /> <s:submit value="上传"/> <s:reset value="重置" /> </s:form> </body> </html>
success.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>成功页面</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <h1>文件上传成功</h1><hr/> <p>上传文件名:${pictureFileName}</p> <p>文件上传类型:${pictureContentType}</p> <img src="upload/${pictureFileName}" /> </body> </html>
13:自定义一个拦截器
1:创建一个简单的struts2项目。可以参照http://www.cnblogs.com/minuobaci/p/7536229.html
2:删除src目录下面的包,在src目录下面创建一个com.gx.entity.User
package com.gx.entity; public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
3:在src目录下面创建一个com.gx.action.LoginAction
package com.gx.action; import com.gx.entity.User; import com.opensymphony.xwork2.ActionContext; public class LoginAction { private User user; private String mess; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getMess() { return mess; } public void setMess(String mess) { this.mess = mess; } public String login(){ System.out.println("login is working......"); ActionContext actionContext = ActionContext.getContext(); if(user.getUsername().equals("admin")&&user.getPassword().equals("123")){ //将用户存储在session里面 actionContext.getSession().put("user", user); return "success"; }else{ mess = "登录失败!!"; return "wrong"; } } }
4:在src目录下面创建一个com.gx.action.BookAction
package com.gx.action; public class BookAction { private String mess; public String getMess() { return mess; } public void setMess(String mess) { this.mess = mess; } public String add(){ System.out.println("add is working......"); mess = "添加成功"; return "success"; } public String delete(){ System.out.println("delete is working......"); mess = "删除成功"; return "success"; } public String find(){ System.out.println("find is working......"); mess = "查找成功"; return "success"; } public String update(){ System.out.println("update is working......"); mess = "修改成功"; return "success"; } }
5:创建拦截器com.gx.intercepter.LoginIntercepter
package com.gx.intercepter; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class LoginIntercepter extends AbstractInterceptor{ public String intercept(ActionInvocation invocation) throws Exception{ System.out.println("LoginIntercepter is working......"); ActionContext actionContext = invocation.getInvocationContext(); Object user = actionContext.getSession().get("user"); if(user!=null){ //继续向下执行 return invocation.invoke(); }else{ actionContext.put("msg", "你还没有登录"); return "wrong"; } } }
6:在WebRoot下面创建main.jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>main page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <!-- 这里的struts2_test是我的项目名 --> <a href="/struts2_test/book_add">增加</a> <a href="/struts2_test/book_delete">删除</a> <a href="/struts2_test/book_find">查找</a> <a href="/struts2_test/book_update">修改</a> </body> </html>
7:修改index.jsp里面<body>的代码如下
${requestScope.msg} <form action="login" method="post"> 请输入用户名:<input type="text" name="user.username"><br/> 请输入用户密码:<input type="password" name="user.password"><br/><br/> <input type="submit" value="提交"/> </form>
8:在struts.xml里面声明拦截器
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="default" extends="struts-default"> <!-- 声明拦截器 --> <interceptors> <interceptor name="checkLogin" class="com.gx.intercepter.LoginIntercepter" /> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="checkLogin" /> </interceptor-stack> </interceptors> <!-- 用户登录 --> <action name="login" class="com.gx.action.LoginAction" method="login"> <result name="success">/main.jsp</result> <result name="wrong">/wrong.jsp</result> </action> <!-- book操作 --> <action name="book_*" class="com.gx.action.BookAction" method="{1}"> <!-- 此处的name与test.ServletTest.java里面的方法返回值相对应 --> <result name="success">/success.jsp</result> <!-- 使用自定义拦截器 --> <interceptor-ref name="myStack" /> <result name="wrong">/index.jsp</result> </action> </package> </struts>
9:在网页上输入网址:http://localhost:8080/struts2_test/main.jsp
10:随便点击一个超链接,在这里我选择修改
11:可以看到,被拦截器拦截到了,转到了请求登录页面
输入用户名:admin和密码:123点击登录
13:点击修改
14:拦截器和过滤器的区别联系
过滤器,可以在用户传入的request,response进入servlet或者struts的action进行业务逻辑处理之前过滤掉一些信息,或者提前设置一些参数。比如过滤掉非法url(不是login.do的地址请求,如果用户没有登录都过滤掉),或者在传入servlet或者struts的action前统一设置字符集,或者去除掉一些非法字符
拦截器是struts2的核心组成部分,拦截器提供了一种机制,使得开发者可以定义一个特定的功能模块,这个模块会在action执行之前或者之后执行用于对数据进行处理,也可以在action执行之前阻止action执行。同时拦截器也可以让你将通用代码模块作为一个可重用的类。拦截器是AOP(面向切面编程)的一种实现策略
两者之间的区别
1:拦截器是基于java的反射机制的,而过滤器是基于函数回调。
2:拦截器不依赖于servlet容器,过滤器依赖与servlet容器。
3:拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4:拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问
5:在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
15:struts2的优缺点
优点
1:实现了MVC模式,层次结构清晰,减少了代码的维护量,方便了我们对Action进行的测试,使程序员只需关注业务逻辑的实现,HTML前端程序员可以集中精力于表现层的开发,有利于缩短产品的开发时间。
2: 丰富的标签库,大大提高了开发的效率。
3:通过配置文件,就可以掌握整个系统各个部分之间的关系。降低模块间耦合度
4:强大的核心拦截器机制,struts2中的拦截器是Action级别的AOP,通过拦截器,可以很方便让我们对业务进行扩展
缺点
安全性有待提高。Struts2曝出2个高危安全漏洞,一个是使用缩写的导航参数前缀时的远程代码执行漏洞,另一个是使用缩写的重定向参数前缀时的开放式重定向漏洞。
hibernate
1:hibernate的持久化对象状态三种状态
hibernate是持久层的ORM映射框架,专注于数据的持久化工作。
所谓的持久化就是将内存中的数据永久存储到关系型数据库中。
持久化的三种状态:
瞬时态:也称临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,没有与hibernate session关联,在数据库中也没有记录,失去引用后将被JVM回收
持久态:加入到session缓存中,并且相关联的session没有关闭,在数据库里面有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务尚未提交前变成持久态的
脱管态:也称离线态或者游离态,当持久态的实例与session的关联被关闭的时候就成了脱管态。与数据库里面的数据存在关联,只是失去了与当前session的关联,脱管态改变的时候hibernate不能检测到
2:hibernate持久化状态之间的转化
3:hibernate的一级缓存
hibernate的一级缓存为hibernate的内置缓存,不能被卸载
*** 什么是hibernate的一级缓存? ***
hibernate的一级缓存就是指session缓存,session缓存是一块内存空间,用来存放相互管理的java对象,在使用hibernate查询对象的时候,首先会使用对象属性的OID值在hibernate的一级缓存中进行查找,如果找到匹配的OID值的对象就会直接将该对象从一级缓存中取出使用,不会再去查询数据库,如果,没有找到就回去数据库查找。当从数据库中查询到所需数据的时候,该数据信息也会放置到一级缓存中。
hibernate一级缓存的作用就是减少对数据库的访问次数。
4:hibernate一级缓存的特点
1:当应用程序调用session接口的save、update、saveOrUpdate方法的时候,如果session缓存中没有相应的对象,hibernate会自动把从数据库中查询到的相应对象的信息加入到一级缓存里
2:当调用session接口的load、get方法以及query接口的list、Iterator方法的时候,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果没有,就会去数据库中查找对应对象,并将其添加到一级缓存里面
3:调用session的close方法的时候session缓存会被清空
4:session能够在某些时间点按照缓存对象的变化执行相关的SQL语句来同步更新数据库,这一过程被称为刷出缓存(flush)
5:hibernate的session接口的load方法与get方法有什么区别
1:当数据库不存在对应ID数据时,调用load()方法将会抛出ObjectNotFoundException异常,get()方法将返回null
2:当对象.hbm.xml配置文件<class>元素的lazy属性设置为true时(延迟加载),调用load()方法时则返回持久对象的代理类实例,此时的代理类实例是由运行时动态生成的类,该代理类实例包括原目标对象的所有属性和方法,该代理类实例的属性除了ID不为null外,所在属性为null值,查看日志并没有Hibernate SQL输出,说明没有执行查询操作,当代理类实例通过getXXX()方法获取属性值时,Hiberante才真正执行数据库查询操作。当对象.hbm.xml配置文件<class>元素的lazy属性设置为false时,调用load()方法则是立即执行数据库并直接返回实体类,并不返回代理类。而调用get()方法时不管lazy为何值,都直接返回实体类。
*** 联系 ***
load()和get()都会先从Session缓存中查找,如果没有找到对应的对象,则查询Hibernate二级缓存,再找不到该对象,则发送一条SQL语句查询。
6:默认情况下session刷出缓存的时间点
1:当应用程序调用transaction的commit方法的时候,该方法先刷出缓存(session.flush()),然后再向数据库提交事务(tx.commit())
2:当应用程序执行一些查询操作的时候,如果缓存中持久化对象的属性已经发生了变化,会先刷出缓存,以保证查询结果能够反映持久化对象的最新状态
3:调用session的flush()方法
7:hibernate二级缓存
*** hibernate提供两个级别的缓存 ***
第一级缓存是session级别的缓存,属于事务范围的缓存,由hibernate管理,一般情况下无需干预
第二级缓存是一个可插拔的缓存插件,是由sessionFactory负责管理的
当执行某个查询获得的结果集是实体对象的时候,hibernate就会把获得的实体对象按照ID加载到二级缓存中。在访问指定的对象的时候,首先从一级缓存中查找,找到了就直接使用,找不到就转到二级缓存中查找(在配置和启动二级缓存的情况下)。如果找到就直接使用,否则查询数据库,并将找到的结果放到一级缓存和二级缓存中
*** sessionFactory的缓存可以分为两类 ***
1:内置缓存:hibernate自带的不可以卸载。在hibernate的初始化阶段将映射元数据(映射文件中数据的复制)和预定义的SQL语句(hibernate根据映射元数据推导出来的)放到sessionFactory的缓存中。该内置缓存是只读的
2:外置缓存(二级缓存):一个可配置的缓存插件,默认情况下,sessionFactory不会启用这个缓存插件,外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或者硬盘
*** 二级缓存可以分成4类 ***
1:类级别的缓存:主要用于存储实体对象
2:集合级别的缓存:用于存储几何数据
3:查询缓存:缓存一些常用查询语句和查询结果
4:更新时间戳:该区域放了与查询结果相关的表在进行插入,更新或者删除操作的时间戳,hibernate通过更新时间戳来判断被缓存的查询结果是否过期
*** 二级缓存的并发事务隔离级别 ***
只读型:用于重来不会被修改的数据
读写型:用于常读但是很少被修改的数据,防止脏读
非严格读写:不保证缓存与数据库一致性,用于极少被修改且允许脏读的数据
事务型:对于常读但很少修改的数据,可以防止脏读和不可重复读
8:关联关系映射
1:一对多
class one{ private set<more> more; } class more{ private one one; }
配置文件
one.hbm.xml(放在<class>里面的<property>下面)
<!-- 这里的more是one.java里面的属性名 --> <set name="more"> <!-- 确定数据库里面的关联外键,one_id是外键的名称 --> <key column = "one_id" /> <!-- 映射到关联类属性 --> <one-to-many class="com.gx.more" /> </set>
more.hbm.xml(放在<class>里面的<property>下面)
<!-- 设置多对一关系映射 --> <!-- 这里的one是more.java里面的属性名 --> <many-to-one name="one" class="com.gx.one" column="one_id"/>
2:多对多
class A{ private set<B> b } class B{ private set<A> a; }
配置文件
A.hbm.xml
<set name="b" table="a_b"> <key column="a_id" /> <many-to-many class="com.gx.B" column="b_id" /> </set>
B.hbm.xml
<set name="a" table="a_b"> <key column="b_id" /> <many-to-many class="com.gx.A" column="a_id" /> </set>
9:反转操作
反转操作在映射文件里面通过对集合的inverse属性的设置,来控制关联关系和对象的级联关系
inverse的默认属性使false,也就是说关系的两端都能控制。这就会造成更新等操作的时候出现重复更新,会报错。一般来说,如果是一对多的关系,会将一的一方的inverse的值设置为true,即通过一方来维护关联关系,这样就不会产生多余的SQL语句,如果是多对多,设置其中的一方就可以了
10:级联操作
级联操作是指当主控方执行保存、更新或者删除操作的时候,其关联对象也执行相同的操作
在映射文件里面通过对cascade属性的设置来控制是否对关联对象采用级联操作
cascade属性的设置
1:save-update:在执行save、update或saveOrUpdate的时候进行关联操作
2:delete:在执行delete操作的时候进行关联操作
3:delete-orphan:表示孤儿删除,即删除所有和当前对象解除关联关系的对象
4:all:表示所有情况下均进行关联操作,但是不包括delete-orphan操作
5:all-delete-orphan:所有情况下均进行关联操作,包括delete-orphan操作
6:none:任何情况下都不进行关联操作
11:hibernate的检索方式
12:HQL
HQL(面向对象的查询语言)和SQL查询语言有点相似,但是HQL使用的是类、对象和属性的概念,没有表和字段的概念
HQL检索方式具有以下功能:
1:在查询语句中设定各种查询条件
2:支持投影查询,即仅仅检索出对象的部分属性
有些时候只需要查询部分属性,并不需要查询一个类的所有属性。如果在这种情况下查询出所有的属性使非常影响查询性能的,为此提供了投影查询。使用投影查询查找到的集合里面的每一个元素都是一个Object数组而不是一个对象
3:支持分页查询
4:支持分组查询,允许使用group by和having关键字
5:提供内置聚集函数,如sum、min、max
6: 支持子查询,即嵌套查询
7:支持动态绑定参数
1:原子性(Atomicity):事务中所有的操作被捆绑成一个不可分割的单元,即对事物所进行的数据修改等操作,要么全部执行成功,要么全部执行失败
2:一致性(Consistency):表示事务完成的时候,必须使所有的数据都保持一致
3:隔离性(Isolation):指一个事务的执行不能被其他事务干扰,即一个事物内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
4:持久性(Durability):也称为永久性,指一个事务一旦提交,他对数据库中数据的改变就是永久的,提交后的其他操作或者故障不会对其有任何影响
14:事务并发问题
1:脏读:一个事务读取另一个事务未提交的数据
2:不可重复读:一个事务对同一行数据重复读取两次,却得到不一样的结果
3:虚读/幻读:事务A在操作过程中进行两次查询,第二次得到了第一次没有出现过的数据。因为两次查询的过程中另一个事务B插入了新的数据
4:更新丢失:两个事务更新同一条数据,后提交(或者撤销)的事务将前面的事务提交的数据覆盖
第一类丢失更新:两个事务同时操作同一个数据的时候,A事务撤销时,把已经提交的B事务的更新数据覆盖,对B事务来说造成了数据的丢失
第二类丢失更新:两个事务同时操作同哟个数据时,事务A将修改结果成功提交后,对事务B已经提交了的修改结果进行了覆盖,对事物B造成了数据丢失
15:事务隔离级别
为了避免并发事务问题出现,在标准SQL规范里面定义了4个事务隔离级别
1:读未提交(1级):一个事务在执行过程中,既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另一个事务不允许同时进行写操作,但允许其他事务读取此行数据。可以防止丢失数据
2:读已提交(2级):一个事务在执行过程中,既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但未提交的写事务将会禁止其他事务访问该行。可以避免脏读
3:可重复读取(4级):一个事务在执行过程中,可以访问其他事务成功提交的新插入的数据,但不可以访问成功修改的数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。防止不可重复读和脏读
4:序列化(8级):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接一个的执行,不能并发执行。可以防止脏读、不可重复读和幻读
16:操作管理事务的方法
1:通过代码来操作管理事务
//开启事务 Transaction tx = session.beginTransaction(); …… //提交事务 tx.commit //回滚事务 tx.rollback
2:通过hibernate的配置文件(hibernate.cfg.xml里面的<session-factory>里面写下面的配置)对事务进行配置
<!-- 使用本地事务 --> <property name="hibernate.current_session_context_class" > thread </property> <!-- 使用全局事务 --> <property name="hibernate.current_session_context_class">jta</property> <!-- 设置事务隔离级别 --> <property name="hibernate.connection.isolation">4</property>
本地事务和全局事务是hibernate对事物处理从两种方法:
本地事务指针对一个数据库的操作,即只针对一个事务性资源进行操作。
全局事务是由应用服务器进行管理的事务,他需要使用JTA,并且可以用于多个事务性资源(跨多个数据库)
由于JTA的API非常笨重,并且通常只在服务器的环境下使用,因此全局事务的使用限制了应用代码的重用性,通常情况下对于hibernate的事务管理都是选择本地事务
17:悲观锁,乐观锁
当多个事务访问数据库里面相同的数据的时候,如果没有采取必要的隔离措施,将会导致各种事务并发问题,可以采用悲观锁或者乐观锁对其进行控制
悲观锁:每次操作数据的时候,总是悲观的认为会有其他的事务来操作相同的数据,因此,在整个数据处理过程中,将会把数据处于锁定状态,悲观锁具有排他性,一般由数据库来实现,在锁定时间内,其他事务不能对数据进行存取等操作,这样可能导致长时间的等待问题。
乐观锁:乐观锁通常认为许多事务同时操作一个数据的情况很少,所以乐观锁不会做数据库层次的锁定,而是基于数据版本表示实现应用程序级别上的锁定机制。(数据版本标识是指通过为数据表增加一个“version”字段,实现在读取数据的时候,将版本号一起读出,之后更新次数据的时候,将版本号加一,提交数据的时候,如果提交数据的版本号大于数据库版本号则允许跟新否则禁止)
hibernate和mybatis的对比
1:Mybatis框架相对简单很容易上手,但也相对简陋些。
2:Hibernate 与Mybatis都是流行的持久层开发框架,但Hibernate开发社区相对多热闹些,支持的工具也多,更新也快。而Mybatis相对平静,工具较少。
3:针对高级查询,Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。
4:Hibernate具有自己的日志统计。但Mybatis本身不带日志统计,需要使用Log4j进行日志记录。
5:Hibernate与具体数据库的关联只需在XML文件中配置即可,所有的HQL语句与具体使用的数据库无关,移植性很好。MyBatis项目中所有的SQL语句都是依赖所用的数据库的,所以不同数据库类型的支持不好
6:hibernate的开发者不必考虑 SQL 语句的执行。这部分细节已经由 Hibernate 掌管妥当,只有开发者在进行系统性能调优的时候才需要进行了解。而MyBatis在这一块没有文档说明,用户需要对对象自己进行详细的管理。
7:因为Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示。
而MyBatis在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免Cache的盲目使用。否则,脏数据的出现会给系统的正常运行带来很大的隐患。
*** mybatis优势 ***
MyBatis可以进行更为细致的SQL优化,可以减少查询字段
MyBatis容易掌握,而Hibernate门槛较高。
*** hibernate优势 ***
Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射
Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便
Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL
Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳
最核心的一点,mybatis和hibernate的思想不一样,mybatis是面向数据库的,hibernate是面向对象的,所以hibernate的包装非常好,甚至对于简单的SQL我们都不需要书写代码,可以直接继承HibernateDaoSupport类就可以直接使用不需要写SQL了
面临的问题,hibernate包装的太好,以至于优化SQL的时候不太方便,排错也不太方便
mybatis
1:简介
ORM的全称是Object/Relation Mapping即对象/关系数据库映射。可以将ORM理解为一种规范,他概述了这类框架的基本特征,完成面向对象的编程语言到关系数据库的映射。当ORM框架完成映射之后,程序员既可以利用面向对象程序设计语言的简单易用性,又可以利用关系数据库的技术优势,因此可以把ORM当成是应用程序和数据库的桥梁
确切的说,mybatis并不是一种ORM框架,他的设计思想和ORM相似,只是他允许开发人员直接编写SQL语句,访问更加灵活,更确切,他应该是一种“SQL Mapping”框架
2:创建一个mybatis项目,实现根据ID查询用户,并测试
1:创建实体类com.gx.mybatis.entity.User
package com.gx.mybatis.entity; public class User { private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
2:创建com.gx.mybatis.dao.UserMapper接口
package com.gx.mybatis.dao; import com.gx.mybatis.entity.User; public interface UserMapper { /** * 根据ID查询用户 * @param id * @return */ public User selectUserById(int id); }
3:在com.gx.mybatis.dao目录下创建实现selectUserById方法的配置文件UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 此处的名称与DAO对应 --> <mapper namespace="com.gx.mybatis.dao.UserMapper"> <!--ID要与接口中的方法名相同 parameterType传入的参数类型 resultType 返回的类型这里也User类的全路径--> <select id="selectUserById" parameterType="int" resultType="com.gx.mybatis.entity.User"> select * from user where id=#{id} </select> </mapper>
#{}是sql的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql的?号占位符设置参数值,比如ps.setInt(0, parameterValue)。
4:创建文件夹config
5:configuration.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--配置 --> <configuration> <!-- 加载资源文件 --> <properties resource="db.properties"/> <!--配置环境 --> <environments default="development"> <environment id="development"> <!--事务管理 --> <transactionManager type="JDBC"/> <!--数据源 通过Properties加载配置 --> <dataSource type="POOLED"> <!--驱动driver --> <property name="driver" value="${driver}"/> <!--连接URL --> <property name="url" value="${url}"/> <!--用户名 --> <property name="username" value="${username}"/> <!--密码 --> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!--建立映射 --> <mappers> <mapper resource="com/gx/mybatis/dao/UserMapper.xml"/> </mappers> </configuration>
${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver
6:db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3307/test?characterEncoding=utf8
username=root
password=root
7:创建测试类com.gx.mybatis.test.TestMyBatis1
package com.gx.mybatis.test; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import com.gx.mybatis.dao.UserMapper; import com.gx.mybatis.entity.User; public class TestMyBatis1 { //Session工厂 static SqlSessionFactory sqlSessionFactory=null; //Session static SqlSession session=null; //字符流 static Reader reader=null; public static void main(String[] args) { //加载配置文件 try { reader=Resources.getResourceAsReader("configuration.xml"); //建立SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader); //打开Session session=sqlSessionFactory.openSession(); //获取用户接口对象 UserMapper userMapper=session.getMapper(UserMapper.class); //调用查询方法 User user=userMapper.selectUserById(1); System.out.println(user.toString()); } catch (IOException e) { e.printStackTrace(); } } }
8:运行测试类,测试结果为
3:使用别名
1:修改UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 此处的名称与DAO对应 --> <mapper namespace="com.gx.mybatis.dao.UserMapper"> <!--结果映射 不区别大小写,建议数据库的列写为大写 --> <resultMap type="User" id="userMap"> <!--主键 --> <id property="id" column="ID"/> <!--姓名 --> <id property="name" column="NAME"/> <!--年龄 --> <id property="age" column="AGE"/> </resultMap> <!--修改前resultType返回的类型是User类的全路径 ,这里resultType里面的是别名-->
<select id="selectUserById" parameterType="int" resultType="User">
select * from user where id=#{id}
</select>
</mapper>
2:修改configuration.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--配置 --> <configuration> <!-- 加载资源文件 --> <properties resource="db.properties"/> <!--使用别名 --> <typeAliases> <!--用户类别名 --> <typeAlias type="com.gx.mybatis.entity.User" alias="User"/> </typeAliases> <!--配置环境 --> <environments default="development"> <environment id="development"> <!--事务管理 --> <transactionManager type="JDBC"/> <!--数据源 通过Properties加载配置 --> <dataSource type="POOLED"> <!--驱动driver --> <property name="driver" value="${driver}"/> <!--连接URL --> <property name="url" value="${url}"/> <!--用户名 --> <property name="username" value="${username}"/> <!--密码 --> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!--建立映射 --> <mappers> <mapper resource="com/gx/mybatis/dao/UserMapper.xml"/> </mappers> </configuration>
3:创建测试类TestMybatis2
package com.gx.mybatis.test; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.gx.mybatis.dao.UserMapper; import com.gx.mybatis.entity.User; public class TestMyBatis2 { //Session工厂 static SqlSessionFactory sqlSessionFactory=null; //Session static SqlSession session=null; //字符流 static Reader reader=null; public static void main(String[] args) { //加载配置文件 try { reader=Resources.getResourceAsReader("configuration.xml"); //建立SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader); //打开Session session=sqlSessionFactory.openSession(); //获取用户接口对象 UserMapper userMapper=session.getMapper(UserMapper.class); //调用查询方法 User user=userMapper.selectUserById(2); System.out.println(user.toString()); } catch (IOException e) { e.printStackTrace(); } } }
4:测试结果
4:在使用别名的情况下添加其他方法
1:修改UserMapper
package com.gx.mybatis.dao; import java.util.List; import com.gx.mybatis.entity.User; public interface UserMapper { //增加 public void addUser(User user); //删除 public void deleteUser(int id); //查询 public User selectUserById(int id); public List<User> selectUserLikeName(String name); public List<User> selectAll(); //修改 public void updateUser(User user); }
2:修改UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 此处的名称与DAO对应 --> <mapper namespace="com.gx.mybatis.dao.UserMapper"> <!--结果映射 不区另大小写,建议数据库的列写为大写 --> <resultMap type="User" id="userMap"> <!--主键 --> <id property="id" column="ID"/> <!--姓名 --> <id property="name" column="NAME"/> <!--年龄 --> <id property="age" column="AGE"/> </resultMap> <!--resultType里面的是别名--> <!-- 增加 --> <insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id"> insert into user(name,age)values(#{name},#{age}) </insert> <!-- 删除 --> <delete id="deleteUser" parameterType="int"> delete from user where id=#{id} </delete> <!-- 查询 --> <select id="selectUserById" parameterType="int" resultType="User"> select * from user where id=#{id} </select> <select id="selectUserLikeName" parameterType="String" resultMap="userMap"> select * from user where name like "%"#{name}"%" </select> <select id="selectAll" resultMap="userMap"> select * from user </select> <!-- 修改 --> <update id="updateUser" parameterType="User"> update user set name=#{name},age=#{age} where id=#{id} </update> </mapper>
3:创建测试类TestMybatis3
package com.gx.mybatis.test; import java.io.IOException; import java.io.Reader; import java.util.List; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.gx.mybatis.dao.UserMapper; import com.gx.mybatis.entity.User; public class TestMyBatis3 { //Session工厂 static SqlSessionFactory sqlSessionFactory=null; //Session static SqlSession session=null; //字符流 static Reader reader=null; public static void main(String[] args) { //加载配置文件 try { reader=Resources.getResourceAsReader("configuration.xml"); //建立SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader); //打开Session session=sqlSessionFactory.openSession(); //获取用户接口对象 UserMapper userMapper=session.getMapper(UserMapper.class); //调用添加方法 User user1 = new User(); user1.setAge(12); user1.setName("lili"); userMapper.addUser(user1); //使用查询姓名方法 List<User> lists1 = userMapper.selectUserLikeName("lili"); System.out.println("使用查询姓名方法:"); for(User u : lists1){ System.out.println(u.toString()); } //使用查询ID方法 User user2 = userMapper.selectUserById(1); System.out.println("使用查询ID方法: "+user2.toString()); //使用修改方法 user2.setName("mary"); userMapper.updateUser(user2); System.out.println("使用修改方法: "+user2.toString()); //使用删除方法 userMapper.deleteUser(user2.getId()); //使用查询所有方法 List<User> lists2 = userMapper.selectAll(); System.out.println("使用查询所有方法:"); for(User u : lists2){ System.out.println(u.toString()); } } catch (IOException e) { e.printStackTrace(); } } }
4:测试结果