1.Struts 2的基本流程
Struts 2框架由3个部分组成:核心控制器FilterDispatcher、业务控制器和用户实现的业务逻辑组件。在这3个部分里,Struts 2框架提供了核心控制器FilterDispatcher,而用户需要实现业务控制器和业务逻辑组件。
2.核心控制器:FilterDispatcher
FilterDispatcher是Struts 2框架的核心控制器,该控制器作为一个Filter运行在Web应用中,它负责拦截所有的用户请求,当用户请求到达时,该Filter会过滤用户请求。如果用户请求以action结尾,该请求将被转入Struts 2框架处理。
Struts 2框架获得了*.action请求后,将根据*.action请求的前面部分决定调用哪个业务逻辑组件,例如,对于login.action请求,Struts 2调用名为login的Action来处理该请求。
Struts 2应用中的Action都被定义在struts.xml文件中,在该文件中定义Action时,定义了该Action的name属性和class属性,其中name属性决定了该Action处理哪个用户请求,而class属性决定了该Action的实现类。
Struts 2用于处理用户请求的Action实例,并不是用户实现的业务控制器,而是Action代理——因为用户实现的业务控制器并没有与Servlet API耦合,显然无法处理用户请求。而Struts 2框架提供了系列拦截器,该系列拦截器负责将HttpServletRequest请求中的请求参数解析出来,传入到Action中,并回调Action 的execute方法来处理用户请求。
显然,上面的处理过程是典型的AOP(面向切面编程)处理方式。图3.19显示了这种处理模型。
图3.19 Struts 2的拦截器和Action
从图3.19中可以看出,用户实现的Action类仅仅是Struts 2的Action代理的代理目标。用户实现的业务控制器(Action)则包含了对用户请求的处理。用户的请求数据包含在 HttpServletRequest对象里,而用户的Action类无需访问HttpServletRequest对象。拦截器负责将 HttpServletRequest里的请求数据解析出来,并传给业务逻辑组件Action实例。
3.业务控制器
正如从图3.19所看到的,业务控制器组件就是用户实现Action类的实例,Action类里通常包含了一个execute方法,该方法返回一个字符串——该字符串就是一个逻辑视图名,当业务控制器处理完用户请求后,根据处理结果不同,execute方法返回不同字符串 ——每个字符串对应一个视图名。
程序员开发出系统所需要的业务控制器后,还需要配置Struts 2的Action,即需要配置Action的如下三个部分定义:
— Action所处理的URL。
— Action组件所对应的实现类。
— Action里包含的逻辑视图和物理资源之间的对应关系。
每个Action都要处理一个用户请求,而用户请求总是包含了指定URL。当Filter Dispatcher拦截到用户请求后,根据请求的URL和Action处理URL之间的对应关系来处理转发。
4.Struts 2的模型组件
实际上,模型组件已经超出了MVC框架的覆盖范围。对于Struts 2框架而言,通常没有为模型组件的实现提供太多的帮助。
文本框: 图3.20 控制器调用模型组件Java EE应用里的模型组件,通常指系统的业务逻辑组件。而隐藏在系统的业务逻辑组件下面的,可能还包含了DAO、领域对象等组件。
通常,MVC框架里的业务控制器会调用模型组件的方法来处理用户请求。也就是说,业务逻辑控制器不会对用户请求进行任何实际处理,用户请求最终由模型组件负责处理。业务控制器只是中间负责调度的调度器,这也是称Action为控制器的原因。
图3.20显示了这种处理流程。
提示 在图3.20中看到Action调用业务逻辑组件的方法。当控制器需要获得业务逻辑组件实例时,通常并不会直接获取业务逻辑组件实例,而是通过工厂模式来获得业务逻辑组件的实例;或者利用其他IoC容器(如Spring容器)来管理业务逻辑组件的实例。
5.Struts 2的视图组件
Struts 2已经改变了Struts 1只能使用JSP作为视图技术的现状,Struts 2允许使用其他的模板技术,如FreeMarker、Velocity作为视图技术。
当Struts 2的控制器返回逻辑视图名时,逻辑视图并未与任何的视图技术关联,仅仅是返回一个字符串,该字符串作为逻辑视图名。
当我们在struts.xml文件中配置 Action时,不仅需要指定Action的name属性和class属性,还要为Action元素指定系列result子元素,每个result子元素定义一个逻辑视图和物理视图之间的映射。前面所介绍的应用都使用了JSP技术作为视图,故配置result子元素时没有指定type属性,默认使用JSP 作为视图资源。
如果需要在Struts 2中使用其他视图技术,则可以在配置result子元素时,指定相应的type属性即可。例如,如果需要使用FreeMarker,则为result指定值为freemarker的type属性;如果想使用Velocity模板技术作为视图资源,则为result指定值为velocity的type属性……
6.Struts 2的运行流程
经过上面介绍,我们发现Struts 2框架的运行流程非常类似于
WebWork框架的流程。
提示 在Struts 2的官方站点,我们可以找到如下说法:Essentially,Struts 2.0 is the technical equivalent of
WebWork 2.3。Aside from the package and property renaming,it isn't much different than,say,migrating from
WebWork 2.1 to 2.2——意思是说:Struts 2.0技术等同于
WebWork 2.3框架,除了包和属性被改名外。从
WebWork 2.2迁移到Struts 2不会比从
WebWork 2.1迁移到
WebWork 2.2更复杂。
这里我们可以看到,Struts 2其实就是
WebWork 2.2的升级版,这也就不难理解:为什么
WebWork和Struts 2如此相似!
因此,Struts 2的运行流程与
WebWork的运行流程完全相同,读者可以参看图1.8来了解Struts 2的运行流程。
posted @
2009-04-15 14:03 lanxin1020 阅读(1435) |
评论 (0) |
编辑 收藏
摘要: 问题:
struts2 使用jakarta 上传文件时,如果上传文件的大小超出commons fileupload(jakarta上传文件还是依赖commons-fileupload)设置的大小就会在进入action以前抛出异常.
&nb...
阅读全文
posted @
2009-04-14 12:50 lanxin1020 阅读(2218) |
评论 (0) |
编辑 收藏
下文书中包的版本:commons-fileupload-1.2.1.jar、struts2-core-2.1.2.jar
孙鑫的书《Struts2 深入详解》509页是关于限制上传文件的最大长度的内容。
其中谈到fileUpload拦截器只是当文件上传到服务器上之后,才进行的文件类型和大小判断。
Struts2框架底层默认用的是apache的commons-fileupload组件对上传文件进行接受处理。
通过struts.multipart.maxSize属性来对文件大小进行限定时,将直接影响到commons-fileupload组件的文件大小设定,默认是2M。当上传文件超过了这个尺寸时,将从commons-fileupload组件中抛出SizeLimitExceededException异常。上传文件拦截器捕获到这个异常后,将直接把该异常信息设置为Action级别的错误信息。
经过我的测试和对源代码的Debug,发现确实如孙鑫书中所言,如果上传文件大于2M时,在页面上就出现了一堆英文的错误信息,大致是:the request was rejected because its size....exceeds the configured maximum...并且在fieUpload中将来自MultiPartRequestWrapper型request对象的错误信息给加到了Action的错误中。
这时候,你在ApplicationResources.properties中自定义的上传文件过大的错误信息根本不起作用。原因就如书上所言,在底层commons-fileupload组件中就把异常给抛出来了文件根本没被上传,所以到了fileUpload拦截器时,根据取不到文件,当然也就没法对文件的类型和大小进行判断了。
然而,这个异常直接带来两个问题:
1、在页面上显示了英文的错误信息。这样的信息显然不是我们想要的。
2、由于错误的产生,原来页面上输入的其他文本内容也都不见了,也就是说params注入失败。
带着这两个问题,我们来探寻一下Struts2对于请求的处理过程。
注:这并不是一篇关于Struts2请求过程的介绍,主要是为了解决以上两个问题,才引起的简单分析。
首先当然我们要拿FilterDispatcher开刀。
在doFilter方法中调用了prepareDispatcherAndWrapRequest方法,为了包装出Struts2自己的request对象,在prepareDispatcherAndWrapRequest方法中调用Dispatcher类的wrapRequest方法,在这个方法里,会根据请求内容的类型(提交的是文本的,还是multipart/form-data格式),决定是使用tomcat的HttpServletRequestWrapper类分离出请求中的数据,还是使用Struts2的MultiPartRequestWrapper来分离请求中的数据。
注:向服务器请求时,数据是以流的形式向服务器提交,内容是一些有规则东东,我们平时在jsp中用request内置对象取parameter时,实际上是由tomcat的HttpServletRequestWrapper类分解好了的,无需我们再分解这些东西了。
当然,在这里,我们研究的是上传文件的情况,所以,由于form中设定的提交内容是媒体格式的,所以,Dispatcher类的wrapRequest方法会将请求交由MultiPartRequestWrapper类来处理。
MultiPartRequestWrapper这个类是Struts2的类,并且继承了tomcat的HttpServletRequestWrapper类,也是我们将用来代替HttpServletRequest这个类的类,看名字也知道,是对多媒体请求的包装类。
Struts2本身当然不会再造个轮子,来解析请求,而是交由Apache的commons-fileupload组件来解析了。
在MultiPartRequestWrapper的构造方法中,会调用MultiPartRequest(默认为JakartaMultiPartRequest类)的parse方法来解析请求。
在Struts2的JakartaMultiPartRequest类的parse方法中才会真正来调用commons-fileupload组件的ServletFileUpload类对请求进行解析,至此,Struts2已经实现了将请求转交commons-fileupload组件对请求解析的全过程。剩下的就是等commons-fileupload组件对请求解析完毕后,拿到分解后的数据,根据field名,依次将分解后的field名和值放到params(HashMap类型)里,同时JakartaMultiPartRequest类重置了HttpServletRequest的好多方法,比如熟知的getParameter、getParameterNames、getParameterValues,实际上都是从解析后得到的那个params对象里拿数据,在这个过程,commons-fileupload组件也乖乖的把上传的文件分析好了,JakartaMultiPartRequest也毫不客气的把分解后的文件一个一个的放到了files(HashMap类型)中,实际上此时,commons-fileupload组件已经所有要上传的文件上传完了。至此,Struts2实现了对HttpServletRequest类的包装,当回到MultiPartRequestWrapper类后,再取一下上述解析过程中发生的错误,然后把错误加到了自己的errors列表中了。同样我们会发现在MultiPartRequestWrapper类中,也把HttpServletRequest类的好多方法重载了,毕竟是个包装类嘛,实际上对于上传文件的请求,在Struts2后期的处理中用到的request都是MultiPartRequestWrapper类对象,比如我们调用getParameter时,直接调用的是MultiPartRequestWrapper的getParameter方法,间接调的是JakartaMultiPartRequest类对象的getParameter方法。
注:从这里,我们就可以看出,JakartaMultiPartRequest是完全设计成可以替换的类了。
然后继续向回返,到了Dispatcher类的wrapRequest方法,直接把MultiPartRequestWrapper对象返回了,我们就终于回到了FilterDispatcher类的prepareDispatcherAndWrapRequest方法,此时,我们拿到了完全解析好了的request对象(MultiPartRequestWrapper类),该对象又进一步被返回到了FilterDispatcher类的doFilter方法,也就是回到了出发点,至此,doFilter中拿到的request对象就是一个将请求中的数据分解好的了HttpServletRequest对象,我们完全可以用getParameter方法取其中的数据了,同时,我们也可以用getFiles得到文件数组了。
doFilter方法中,会进一步调用actionMapper的getMapping方法对url进行解析,找出命名空间和action名等,以备后面根据配置文件调用相应的拦截器和action使用。
关于doFilter方法中下一步对Dispatcher类的serviceAction方法的调用,不再描述,总之在action被调用之前,会首先走到fileUpload拦截器(对应的是FileUploadInterceptor类),在这个拦截器中,会先看一下request是不是 MultiPartRequestWrapper,如果不是,就说明不是上传文件用的request,fildUpload拦截器会直接将控制权交给下一个拦截器;如果是,就会把request对象强转为MultiPartRequestWrapper对象,然后调用hasErrors方法,看看有没有上传时候产生的错误,有的话,就直接加到了Action的错误(Action级别的)中了。另外,在fileUpload拦截器中会将MultiPartRequestWrapper对象中放置的文件全取出来,把文件、文件名、文件类型取出来,放到request的parameters中,这样到了params拦截器时,就可以轻松的将这些内容注入到Action中了,这也就是为什么fileUpload拦截器需要放在params拦截器前面的理由。在文件都放到request的parameters对象里之后,fileUpload拦截器会继续调用其他拦截器直到Action等执行完毕,他还要做一个扫尾的工作:把临时文件夹中的文件删除(这些文件是由commons-fileupload组件上传的,供你在自己的Action中将文件copy到指定的目录下,当action执行完了后,这些临时文件当然就没用了)。
你好,你还在看吗?呵呵,是不是太多了,也太乱了,没办法,Struts2就是这样的调用的。也不知道Struts2有没有公开其Sequence图,我是想画一个,不过,太懒,还是看着代码说说吧。
如果上面看烦了,也完全可以不看了,直接看下面的。
在上面一番分析之后,文件上传的全过程就结束了。
我们回到我们的问题上来。
先看第一个:
1、在页面上显示了英文的错误信息。这显然不是我们想要的。
没办法了,commons-fileupload组件没想到国际化,在FileUploadInterceptor拦载器中,也没想着国际化,直接放到Action的错误中了,就没他事了,三种做法:
(1)在错误显示之前,把这条错误给换掉,应该难度不大,我没做留给你做了。
(2)或者重写一下JakartaMultiPartRequest这个类,把捕捉到的异常信息换成自己的,然后,通过Struts2的配置文件,把我们重写的这个parser换上去用。
(3)直接改commons-fileupload组件的类,换成中文的。
我具体说一下第(3)种做法:找到FileUploadBase类,把902行~908行改一下。
FileUploadException ex =
new SizeLimitExceededException(
"the request was rejected because"
+ " its size (" + pCount
+ ") exceeds the configured maximum"
+ " (" + pSizeMax + ")",
pCount, pSizeMax);
=>
FileUploadException ex = new SizeLimitExceededException(
"服务器拒绝了您的请求,原因可能是向服务器提交的数据发生了丢失。", pCount, pSizeMax);
把914行~918行改一下。
throw new SizeLimitExceededException(
"the request was rejected because its size ("
+ requestSize
+ ") exceeds the configured maximum ("
+ sizeMax + ")",
=>
throw new SizeLimitExceededException("服务器拒绝了您的请求,原因是提交数据量过大(通常是由于上传文件过大),请返回上页重试。"
+ " (最大字节数:" + sizeMax / 1024
+ "K)", requestSize, sizeMax);
再看一下第二个问题。
2、由于错误的产生,原来页面上输入的内容也全部不见了,也就是说params注入失败。
关于这个问题我在javaeye上搜索到一篇文章(使用的commons-fileupload组件的jar包似乎比较老)。
http://www.javaeye.com/topic/197345
虽然按照此文,当上传失败时,能够将其他输入内容显示出来,但是这样做的结果是全部的文件肯定会上传到服务器上,也就是说,虽然是页面上报了文件因为太大,请求被拒绝的错,但是文件依然会被上传到服务器上,commons-fileupload组件根本没会去拦文件的上传。
在这里要说明一下,如果你不抛出这个异常,请求的流会继续向服务器上传,只有当整个流上传完了之后,commons-fileupload组件才能正确的分析出文件部分、文本部分。所以,在这里抛出异常是不得已的作法,如果不抛异常,后果是虽然页面报错,但文件还是会被传到服务器的上,这一步根本没挡住输入流的上传,如果没挡住的话,大家想想会有什么后果?
所以,综上所述,对于第二个问题,如果出现了这个异常,我们根本无法让原来输入的内容还显示出来的,因为commons-fileupload组件并没有解析全部的输入内容,直接给出异常了,到了params拦截器中,request里就是空的,根本取不到parameter,所以也就无法注入到Action中了。这种情况下,只能显示一个告知用户由于提交数据量过大,服务器拒绝了请求的错误信息,比较好的方法是,直接跳到一个专门的页面,提示用户,然后让用户点返回来再次输入,否则用户会感觉上传文件大就大吧,怎么连我输入的其他一些内容也没给保存住。当然,如果能用Ajax来上传文件,对客户的操作体验可能是最好的,但是,这样可能会导致服务器上有些挂空的文件(上传后从来没被用过),需要想法清除的。
整个分析下来,我们说第二个问题基本上是无法避免的。
posted @
2009-04-14 12:46 lanxin1020 阅读(365) |
评论 (0) |
编辑 收藏
摘要: Web Service概述
Web Service的定义
W3C组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计。Web Service服务通常被定义为一组模块化的API,它们可以通过网络进行调用,来执行远程系统的请求服务。
这里我们从一个程序员的视角来观察web service。在传统的程序编码中,存在这各种的函数方法调用。通常,我们知道一个程序...
阅读全文
posted @
2009-04-13 18:37 lanxin1020 阅读(228) |
评论 (0) |
编辑 收藏
技术要点
本节代码详细说明文件上传功能的开发流程,介绍知识点如下:
文件上传页面和显示上传成功页面代码内容。
UploadAction类中实现上传功能方法和上传文件属性介绍。
struts.xml中UploadAction配置,以及字符编码、文件临时存放路径配置。
上传后所处路径和最终上传成功后效果展示。
演示代码
上传文件页面,这里笔者定义的是多个文件上传。
- <!---------------------文件名:upload.jsp----------------->
- <%@taglib prefix="s" uri="/struts-tags"%>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
- <title>上传文件</title>
- </head>
- <body>
- <!-- 上传文件表单定义 -->
- <s:form action="upload" method="post" enctype="multipart/form-data">
- <tr>
- <!-- 上传文件标签定义 -->
- <td>上传文件:<s:file name="file"></s:file></td>
- </tr>
- <tr>
- <td>再次上传文件:<s:file name="file"></s:file></td>
- </tr>
- <tr>
- <td align="left"><s:submit name="submit" value="提交"></s:submit></td>
- </tr>
- </s:form>
- </body>
- </html>
<!---------------------文件名:upload.jsp----------------->
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>上传文件</title>
</head>
<body>
<!-- 上传文件表单定义 -->
<s:form action="upload" method="post" enctype="multipart/form-data">
<tr>
<!-- 上传文件标签定义 -->
<td>上传文件:<s:file name="file"></s:file></td>
</tr>
<tr>
<td>再次上传文件:<s:file name="file"></s:file></td>
</tr>
<tr>
<td align="left"><s:submit name="submit" value="提交"></s:submit></td>
</tr>
</s:form>
</body>
</html>
上传文件成功后结果页面
- <!-------------------文件名:result.jsp ----------------->
- <%@taglib prefix="s" uri="/struts-tags"%>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
- <title>上传结果</title>
- </head>
- <body>
- 上传文件:
- <!-- 显示上传成功文件名 -->
- <s:property value="fileFileName" />
- </body>
- </html>
<!-------------------文件名:result.jsp ----------------->
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>上传结果</title>
</head>
<body>
上传文件:
<!-- 显示上传成功文件名 -->
<s:property value="fileFileName" />
</body>
</html>
UploadAction类代码
- <!------------------文件名:UploadAction.java ------------------>
- 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 java.util.List;
-
- import org.apache.struts2.ServletActionContext;
- import com.opensymphony.xwork2.ActionSupport;
-
-
- public class UploadAction extends ActionSupport {
-
- private final static String UPLOADDIR = "/upload";
-
- private List<File> file;
-
- private List<String> fileFileName;
-
- private List<String> fileContentType;
-
- public List<File> getFile() {
- return file;
- }
-
- public void setFile(List<File> file) {
- this.file = file;
- }
-
- public List<String> getFileFileName() {
- return fileFileName;
- }
-
- public void setFileFileName(List<String> fileFileName) {
- this.fileFileName = fileFileName;
- }
-
- public List<String> getFileContentType() {
- return fileContentType;
- }
-
- public void setFileContentType(List<String> fileContentType) {
- this.fileContentType = fileContentType;
- }
-
- public String execute() throws Exception {
- for (int i = 0; i < file.size(); i++) {
-
- uploadFile(i);
- }
- return "success";
- }
-
-
- private void uploadFile(int i) throws FileNotFoundException, IOException {
- try {
- InputStream in = new FileInputStream(file.get(i));
- String dir = ServletActionContext.getRequest().getRealPath(UPLOADDIR);
- File uploadFile = new File(dir, this.getFileFileName().get(i));
- OutputStream out = new FileOutputStream(uploadFile);
- byte[] buffer = new byte[1024 * 1024];
- int length;
- while ((length = in.read(buffer)) > 0) {
- out.write(buffer, 0, length);
- }
-
- in.close();
- out.close();
- } catch (FileNotFoundException ex) {
- ex.printStackTrace();
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- }
<!------------------文件名:UploadAction.java ------------------>
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 java.util.List;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
//文件上传Action
public class UploadAction extends ActionSupport {
//上传文件存放路径
private final static String UPLOADDIR = "/upload";
//上传文件集合
private List<File> file;
//上传文件名集合
private List<String> fileFileName;
//上传文件内容类型集合
private List<String> fileContentType;
public List<File> getFile() {
return file;
}
public void setFile(List<File> file) {
this.file = file;
}
public List<String> getFileFileName() {
return fileFileName;
}
public void setFileFileName(List<String> fileFileName) {
this.fileFileName = fileFileName;
}
public List<String> getFileContentType() {
return fileContentType;
}
public void setFileContentType(List<String> fileContentType) {
this.fileContentType = fileContentType;
}
public String execute() throws Exception {
for (int i = 0; i < file.size(); i++) {
//循环上传每个文件
uploadFile(i);
}
return "success";
}
//执行上传功能
private void uploadFile(int i) throws FileNotFoundException, IOException {
try {
InputStream in = new FileInputStream(file.get(i));
String dir = ServletActionContext.getRequest().getRealPath(UPLOADDIR);
File uploadFile = new File(dir, this.getFileFileName().get(i));
OutputStream out = new FileOutputStream(uploadFile);
byte[] buffer = new byte[1024 * 1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
in.close();
out.close();
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
struts.xml配置文件中有关文件上传的配置:
- <!--------------------文件名:struts.xml------------------->
- <struts>
- <!-- 系统常量定义,定义上传文件字符集编码 -->
- <constant name="struts.i18n.encoding" value="gb2312"></constant>
- <!-- 系统常量定义,定义上传文件临时存放路径 -->
- <constant name="struts.multipart.saveDir" value="c:\"></constant>
- <!-- Action所在包定义 -->
- <package name="C04.4" extends="struts-default">
- <!-- Action名字,类以及导航页面定义 -->
- <!-- 通过Action类处理才导航的的Action定义 -->
- <action name="upload" class="action.UploadAction">
- <result name="input">/jsp/upload.jsp</result>
- <result name="success">/jsp/result.jsp</result>
- </action>
- </package>
- </struts>
<!--------------------文件名:struts.xml------------------->
<struts>
<!-- 系统常量定义,定义上传文件字符集编码 -->
<constant name="struts.i18n.encoding" value="gb2312"></constant>
<!-- 系统常量定义,定义上传文件临时存放路径 -->
<constant name="struts.multipart.saveDir" value="c:\"></constant>
<!-- Action所在包定义 -->
<package name="C04.4" extends="struts-default">
<!-- Action名字,类以及导航页面定义 -->
<!-- 通过Action类处理才导航的的Action定义 -->
<action name="upload" class="action.UploadAction">
<result name="input">/jsp/upload.jsp</result>
<result name="success">/jsp/result.jsp</result>
</action>
</package>
</struts>
(1):文件上传页面如图4.8所示。
图4.8 文件上传
(2):选择文件如图4.9所示。
图4.9 选择上传的文件
(3):单击“提交”按钮后文件上传成功页面,并显示上传文件名,如图4.10所示。
图4.10 上传文件成功后效果
(4):两个被上传文件最终在服务器上存放路径效果如图4.11所示。
图4.11 上传文件存放路径图
代码解释
(1)在upload.jsp中通过Form标签和File标签定义了两个上传文件。Struts2标签笔者会在之后章节里具体介绍,这里只是让读者知道是如何使用标签显示图4.8显示的内容。如果上传成功,笔者在result.jsp中“[”和“]”之间显示上传文件的文件名,如果是多个文件,以“,”相隔。这些显示格式都是用Property标签定义的。
注意:如果上传文件,在JSP的Form中一定要定义如upload.jsp文件中黑体表示的部分。method和enctype属性都必须要如代码中所示。这样Form中上传文件才会起作用。
(2)UploadAction文件中先定义了常量UPLOADDIR,它是上传文件上传后存放的文件夹名字。比如笔者使用的是JBoss(附录中有安装和在MyEclipse中部署的操作说明),则在它的已部署Web项目下的upload文件夹中,会有所有上传成功的文件。如图4.11读者也可以看见它的上传文件最终存放路径。
注意:在MyEclipse中开发的“WebRoot”目录下也要新建一个upload文件夹,否则部署后在JBoss的已部署Web项目下将没有upload文件夹。因为部署的时候会将所有“WebRoot”目录下的文件夹和文件都部署到JBoss的已部署Web项目下。
定义好UPLOADDIR后,在定义上传文件的属性变量。也许其中的“fileFileName”和“fileContentType”读者看了有点别扭,尤其是“fileFileName”感觉不符合Java命名规范,但是这两个属性变量是4.1小节中介绍的“fileUpload”拦截器类中的类公有变量名字,只有这样定义,UploadAction执行时候会把在页面上选择的上传文件的属性值放在这两个变量里面,否则调试UploadAction时候会发现这两个变量都会是“null”即空值。不相信的读者可以自行改变这两个变量名再执行上传文件功能进行调试看一下这两个变量得到的值。
注意:因为这里笔者是进行多个文件上传功能开发,因此“file”、“fileFileName”、“fileFileName”属性变量都设定为List类型,其实还可以设定为数组类型。个人觉得没有啥大区别。完全凭个人喜好而定。还有如果读者自己开发单个文件上传,就没必要把它们设定为List类型或数组类型。直接把“file”定义为Java的IO包中的File类型,“fileFileName”、“fileFileName”定义为普通的String类型即字符串类型。
之后在execute方法中,写一个循环,对所有页面中选择的上传文件一个个进行上传。这里笔者运用了重构中的“抽取方法”的方式,将上传文件的功能封装成一个私有方法,名字为“uploadFile”。其中运用了Java的IO包中很多API方法。有对重构和Java的IO功能不了解的读者可以去查阅相关资料去理解掌握,这里不是本书以及本节重点,因此不再具体记述。
(3)struts.xml中定义了<constant>标签,主要定义了文件名和文件内容显示的字符编码集以及这些被上传文件临时存放路径。
先说明一下<constant>标签,顾名思义这是定义整个Web项目的一些常量属性值,如果不定义则在Struts2自带的default.properties(读者们可到自己安装Struts2的文件路径src\core\src\main\resources\org\apache\struts2\下找到)文件中有这些常量的定义,比如在本节struts.xml文件中的“struts.i18n.encoding”和“struts.multipart.saveDir”在default.properties定义代码如下:
- <!--------------------文件名:default.properties---------------->
- ### This can be used to set your default locale and encoding scheme
- # struts.locale=en_US
- struts.i18n.encoding=UTF-8
-
- ### Parser to handle HTTP POST requests, encoded using the MIME-type multipart/form-data
- # struts.multipart.parser=cos
- # struts.multipart.parser=pell
- struts.multipart.parser=jakarta
- # uses javax.servlet.context.tempdir by default
- struts.multipart.saveDir=
<!--------------------文件名:default.properties---------------->
### This can be used to set your default locale and encoding scheme
# struts.locale=en_US
struts.i18n.encoding=UTF-8
### Parser to handle HTTP POST requests, encoded using the MIME-type multipart/form-data
# struts.multipart.parser=cos
# struts.multipart.parser=pell
struts.multipart.parser=jakarta
# uses javax.servlet.context.tempdir by default
struts.multipart.saveDir=
如果不在struts.xml文件中定义,则Web项目会缺省使用default.properties文件中这两个常量属性的定义。一个将使字符编码集变为“UTF-8”,另一个干脆没有任何文件路径指定。而笔者开发的该Web项目缺省支持的字符编码集是“gb2312”,而且需要指定临时上传文件存放路径。(当然如果读者开发的Web项目缺省编码集就是“UTF-8”,而且也并不需要指定临时路径时候,就没必要在struts.xml中定义这两个<constant>),因此有必要定义这两个属性符合项目开发要求。
注意:也可以如第3章那样,把这两个属性定义在自定义的struts.properties文件中,具体代码可以如下:
- <!------------------------文件名:struts.properties------------------>
- struts.i18n.encoding =gb2312
- struts.multipart.saveDir= c:\
<!------------------------文件名:struts.properties------------------>
struts.i18n.encoding =gb2312
struts.multipart.saveDir= c:\
笔者个人认为比在struts.xml中定义更加好,毕竟Struts2自己也是定义在properties属性文件中,而不是定义在自己的xml配置文件中。(Struts2自带的xml配置文件为struts-default.xml,在4.1小节中已记述)。这里是为了让读者知道struts.xml配置文件也可以配置这些属性,因此写在struts.xml配置文件中。从3.2小节笔者说明struts.xml配置文件时并没有介绍<constant>标签这点也可以知道笔者个人其实是不赞同这样的配置手段即在struts.xml中配置<constant>标签。
在<Action>标签中配置“result”,和第3章类似,将这两个JSP文件的导航流程配置好即可。
(4)开始进行文件上传功能展示,按照如上记述的步骤执行即可。笔者在桌面上新建了两个文本文件,将它们上传到JBoss已部署的Web项目中展示文件上传的upload文件夹下。如图4.11所示。
其实还可以指定上传文件的格式,让它只上传特定类型的文件。比如只能上传文本和xml文件,则在struts.xml需要显示配置“uploadFile”拦截器。如下代码:
- <!-----------------------文件名:struts.xml------------------>
- <struts>
- <!-- Action所在包定义 -->
- <package name="C04.4" extends="struts-default">
- <!-- Action名字,类以及导航页面定义 -->
- <!-- 通过Action类处理才导航的的Action定义 -->
- <action name="upload" class="action.UploadAction">
- <result name="input">/jsp/upload.jsp</result>
- <result name="success">/jsp/result.jsp</result>
- </action>
- <!—显示配置文件上传拦截器 -->
- <interceptor-ref name=”fileUpload”>
- <!—指定特定类型的上传文件 -->
- <param name =”allowedTypes”>text/plain,application/xml</param>
- </ interceptor-ref >
- <interceptor-ref name=”defaultStack”></ interceptor-ref >
- </package>
- </struts>
<!-----------------------文件名:struts.xml------------------>
<struts>
<!-- Action所在包定义 -->
<package name="C04.4" extends="struts-default">
<!-- Action名字,类以及导航页面定义 -->
<!-- 通过Action类处理才导航的的Action定义 -->
<action name="upload" class="action.UploadAction">
<result name="input">/jsp/upload.jsp</result>
<result name="success">/jsp/result.jsp</result>
</action>
<!—显示配置文件上传拦截器 -->
<interceptor-ref name=”fileUpload”>
<!—指定特定类型的上传文件 -->
<param name =”allowedTypes”>text/plain,application/xml</param>
</ interceptor-ref >
<interceptor-ref name=”defaultStack”></ interceptor-ref >
</package>
</struts>
定义了一个名为“allowedTypes”的参数,其中在<param></param>之间的是文件类型,也可以用“,”间隔,表示允许上传多个文件类型。这里允许上传文件类型为txt、xml格式的文件。如果读者不知道各个文件类型的定义,可在自己的JBoss安装目录中的server\default\deploy\jboss-web.deployer\conf\下的web.xml文件中找到(搜索<mime-mapping>即可)。
注意:如果显示配置Struts2自己的缺省拦截器一定要写在“defaultStack”前,否则“fileUpload”拦截器不会执行拦截。因为Struts2中如果某个拦截器执行拦截时候发现自己已经执行过,第二个乃至之后同名的拦截器都不会执行。这里因为“defaultStack”拦截器栈中包含了“fileUpload”拦截器,而“fileUpload”拦截器已经执行拦截了,则不会再执行拦截。如果把“defaultStack”拦截器栈放在“fileUpload”拦截器前配置,则只执行“defaultStack”拦截器栈中的“fileUpload”拦截器,这里是没有定义“allowedTypes”的,Struts2缺省默认的是支持所有文件类型。因此它会支持所有文件类型的文件上传。因此再设定“allowedTypes”就没有任何意义了。
posted @
2009-04-13 15:33 lanxin1020 阅读(226) |
评论 (0) |
编辑 收藏
Struts2文件下载功能开发
技术要点
本节代码详细说明文件下载功能的开发流程,介绍知识点如下:
上传成功页面重修改后支持文件下载代码内容。
DownloadAction文件下载功能开发。
struts.xml中DownloadAction配置,以及支持文件名为中文字符的文件下载。
下载文件流程展示。
演示代码
上传成功页面,这里笔者让其在每个上传文件后提供“下载”链接。
- <!------------------------文件名:result.jsp------------------->
- <%@taglib prefix="s" uri="/struts-tags"%>
- <body>
- 上传文件:
- <table>
- <!-- 循环显示上传成功文件名 -->
- <s:iterator value="fileFileName" status="fn">
- <tr>
- <td>
- <!-- 上传成功文件名 -->
- <s:property />
- </td>
- <td>
- <!-- 下载文件链接内容为定义的下载Action -->
- <!-- 下载文件名作为链接参数fileName值,用OGNL表达式表达 -->
- <a href="<s:url value='download.action'>
- <s:param name='fileName'
- value='fileFileName[#fn.getIndex()]'/>
- </s:url>">下载</a>
- </td>
- </tr>
- </s:iterator>
- </table>
- </body>
<!------------------------文件名:result.jsp------------------->
<%@taglib prefix="s" uri="/struts-tags"%>
<body>
上传文件:
<table>
<!-- 循环显示上传成功文件名 -->
<s:iterator value="fileFileName" status="fn">
<tr>
<td>
<!-- 上传成功文件名 -->
<s:property />
</td>
<td>
<!-- 下载文件链接内容为定义的下载Action -->
<!-- 下载文件名作为链接参数fileName值,用OGNL表达式表达 -->
<a href="<s:url value='download.action'>
<s:param name='fileName'
value='fileFileName[#fn.getIndex()]'/>
</s:url>">下载</a>
</td>
</tr>
</s:iterator>
</table>
</body>
DownLoadAction类代码
- <!------------文件名:DownLoadAction.java ------------------>
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
-
- import org.apache.struts2.ServletActionContext;
- import com.opensymphony.xwork2.ActionSupport;
-
- public class DownLoadAction extends ActionSupport {
-
- private final static String DOWNLOADFILEPATH="/upload/";
-
- private String fileName;
-
- public String getFileName() {
- return fileName;
- }
-
- public void setFileName(String fileName) {
- this.fileName = fileName;
- }
-
-
- public InputStream getDownloadFile() {
- return
- ServletActionContext.getServletContext().getResourceAsStream(DOWNLOADFILEPATH+fileName);
- }
-
- public String getDownloadChineseFileName() {
- String downloadChineseFileName = fileName;
-
- try {
- downloadChineseFileName = new String(downloadChineseFileName.getBytes(), "ISO8859-1");
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
-
- return downloadChineseFileName;
- }
-
- public String execute() {
- return SUCCESS;
- }
- }
<!------------文件名:DownLoadAction.java ------------------>
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class DownLoadAction extends ActionSupport {
//下载文件原始存放路径
private final static String DOWNLOADFILEPATH="/upload/";
//文件名参数变量
private String fileName;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
//从下载文件原始存放路径读取得到文件输出流
public InputStream getDownloadFile() {
return
ServletActionContext.getServletContext().getResourceAsStream(DOWNLOADFILEPATH+fileName);
}
//如果下载文件名为中文,进行字符编码转换
public String getDownloadChineseFileName() {
String downloadChineseFileName = fileName;
try {
downloadChineseFileName = new String(downloadChineseFileName.getBytes(), "ISO8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return downloadChineseFileName;
}
public String execute() {
return SUCCESS;
}
}
struts.xml配置文件中有关文件下载的配置:
- <!------------------文件名:struts.xml----------------->
- <struts>
- <!-- 下载文件的Action定义 -->
- <action name="download" class="action.DownLoadAction">
- <!-- 设置文件名参数,由页面上传入 -->
- <param name="fileName"></param>
- <result name="success" type="stream">
- <!-- 下载文件类型定义 -->
- <param name="contentType">text/plain</param>
- <!-- 下载文件处理方法 -->
- <param name="contentDisposition">
- attachment;filename="${downloadChineseFileName}"
- </param>
- <!-- 下载文件输出流定义 -->
- <param name="inputName">downloadFile</param>
- </result>
- </action>
- </struts>
<!------------------文件名:struts.xml----------------->
<struts>
<!-- 下载文件的Action定义 -->
<action name="download" class="action.DownLoadAction">
<!-- 设置文件名参数,由页面上传入 -->
<param name="fileName"></param>
<result name="success" type="stream">
<!-- 下载文件类型定义 -->
<param name="contentType">text/plain</param>
<!-- 下载文件处理方法 -->
<param name="contentDisposition">
attachment;filename="${downloadChineseFileName}"
</param>
<!-- 下载文件输出流定义 -->
<param name="inputName">downloadFile</param>
</result>
</action>
</struts>
(1):文件开始下载页面如图4.12所示。
图4.12 文件下载
(2):单击“下载”链接,比如点“下载文件1.txt”文件右边“下载”链接,出现对话框如图4.13所示。
图4.13 下载文件处理方式
(3):单击“保存”按钮后选择下载文件存放路径,如图4.14所示。
图4.14 下载文件选择存放路径
代码解释
(1)在result.jsp中通过iterator标签和url标签定义了“fileFileName”的循环显示以及链接。其中有关“status”和OGNL表达式笔者会在之后章节里具体介绍,这里只是让读者知道是如何使用标签显示图4.12显示的内容。特别指出<param>标签为downloadAction定义了一个参数,该参数名为“fileName”,因为在4.4.1小节中笔者定义的“fileFileName”是个List类型的数据集合,因此利用OGNL表达式将文件名作为“fileName”参数值传入downloadAction中。
(2)DownLoadAction文件中先定义了常量DOWNLOADFILEPATH,它是下载文件在服务器存放的路径名,也就是4.4.1小节中上传文件在服务器存放的路径名。
定义好DOWNLOADFILEPATH后,在定义DownLoadAction的属性变量。因为在result.jsp中定义了参数“fileName”,则它作为DownLoadAction的属性变量,需要定义相应的getter、setter方法。
然后定义了getDownloadFile方法,它返回的是一个文件流,表明将被下载文件转换为输出流,方便下载。利用Struts2自带的“ServletActionContext”类的API把下载文件存放路径作为方法参数,读取下载文件,将其转换为文件流。
还有一个getDownloadChineseFileName方法,该方法主要作用是将文件名为中文字符的文件进行文件名的字符编码集合转换。因为在Web系统中由JSP等视图页面传入的变量值,特别是中文字符的变量。缺省的字符编码集合都是“ISO8859-1”,因此利用Java的字符串类的API,将字符编码转成开发需要的字符编码集。防止中文字符乱码问题发生。
(3)struts.xml中定义了名为“download”的Action。其中它自己的参数“fileName”因为在这里它的值会从JSP页面上传入,所以这里只是定义,没有具体给它赋任何值
在<result>标签中定义了type属性,值为“stream”。如果是下载文件功能开发,DownLoadAction一定要设置type属性,而且值为“stream”。这是因为在Struts2自带的xml配置文件为struts-default.xml中有关于“stream”的result返回类型的定义,代码如下:
- <!-------------------文件名:struts-default.xml-------------->
- <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
<!-------------------文件名:struts-default.xml-------------->
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
这里Struts2定义了result返回类型为“stream”,这个result类型主要是处理文件的输入和输出流时候需要使用的。因为下载文件就是把文件转换成输入输出流,将其从一个文件路径放到另外一个文件路径中去。所以肯定要设置这个result类型的。
“contentType”、“contentDisposition”、“inputName”都是这个result的属性。“contentType”就是文件类型。这里因为下载的文件是文本文件,因此设定的值为文本文件类型,具体各个文件类型如何定义,4.4.1小节已经介绍过,这里不再做说明。“contentDisposition”是指定下载文件处理方式,如图4.13就是处理方式的效果。特别指出如果“contentDisposition”定义的值把前面的“attachment”去掉,则下载方式不是以附件方式下载,如果单击“下载”链接,则会把下载文件的内容显示在浏览器中。读者可以去试验一下。这里有个“${downloadChineseFileName}”,这就是在DownLoadAction中定义getDownloadChineseFileName方法的目的,${downloadChineseFileName}是OGNL的表达式,它显示了“downloadChineseFileName”变量的具体值,因为在DownLoadAction中定义getDownloadChineseFileName方法,则把已经转换成符合需要字符编码集的下载文件名作为下载文件方式对话框中显示的名称,不会造成任何乱码问题。“inputName”是最关键的一个属性,也是一定要定义的属性,“inputName”参数中定义的值“downloadFile”就是DownLoadAction中getDownloadFile方法返回的文件流名字。在Struts2中Acion用前缀名为get的方法得到各种属性的值,这些属性有些是在Action中定义,有些就像本示例在配置文件中利用OGNL表达式或直接定义。
(4)开始进行文件下载功能展示,按照如上记述的步骤执行即可。笔者将两个文本文件上传上去,然后在上传成功页面对具体的文件进行下载。在图4.13中单击“保存”按钮就显示图4.14,选择在本机上存放下载文件的路径即可完成下载文件功能。
posted @
2009-04-13 15:30 lanxin1020 阅读(761) |
评论 (0) |
编辑 收藏
Struts2标签使用原理解疑
在笔者下载的Struts2的包中,读者可以在/lib下找到struts2-core-2.0.11.1.jar包,解压该包在其根目录下的/META-INF文件夹下可以看到一个名字为“struts-tags.tld”文件。该文件就是Struts2中所有自带的标签库定义。本节通过对该文件代码的介绍来让读者知晓Struts2内部是如何使用这些标签来进行工作。并简单说明JSP中是如何用其来书写标签代码。
技术要点
本节代码说明Struts2内部定义标签的格式和在JSP中使用方式。
struts-tags.tld文件定标签定义配置格式。
JSP中使用标签功能介绍。
演示代码
- <!------------------文件名: struts-tags.tld----------------->
- <taglib>
- <tlib-version>2.2.3</tlib-version>
- <jsp-version>1.2</jsp-version>
- <short-name>s</short-name>
- <uri>/struts-tags</uri>
- <display-name>"Struts Tags"</display-name>
- <description>………………</description>
- <tag>
- <name>action</name>
- <tag-class>org.apache.struts2.views.jsp.ActionTag</tag-class>
- <body-content>JSP</body-content>
- <description><![CDATA[Execute an action from within a view]]></description>
- <attribute>
- <name>executeResult</name>
- <required>false</required>
- <rtexprvalue>false</rtexprvalue>
- <description><![CDATA[Whether the result of this action (probably a view) should be executed/rendered]]></description>
- </attribute>
- …………………………
- <attribute>
- <name>namespace</name>
- <required>false</required>
- <rtexprvalue>false</rtexprvalue>
- <description><![CDATA[Namespace for action to call]]></description>
- </attribute>
- </tag>
- </taglib>
<!------------------文件名: struts-tags.tld----------------->
<taglib>
<tlib-version>2.2.3</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>s</short-name>
<uri>/struts-tags</uri>
<display-name>"Struts Tags"</display-name>
<description>………………</description>
<tag>
<name>action</name>
<tag-class>org.apache.struts2.views.jsp.ActionTag</tag-class>
<body-content>JSP</body-content>
<description><![CDATA[Execute an action from within a view]]></description>
<attribute>
<name>executeResult</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description><![CDATA[Whether the result of this action (probably a view) should be executed/rendered]]></description>
</attribute>
…………………………
<attribute>
<name>namespace</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description><![CDATA[Namespace for action to call]]></description>
</attribute>
</tag>
</taglib>
代码解释
(1)struts-tags.tld是Struts2自标签定义文件。所有标签定义都是在<tablib>和</taglib>之间定义。以<tag></tag>用来定义一个具体标签。每个标签因为都可以有很多自己的属性。这些属性定义都是以<attribute></attribute>来定义。
(2)<tlib-version></tlib-version>之间定义的是标签库的版本。<jsp-version></jsp-version>定义的是标签库这些标签是支持JSP的哪个版本。<short-name> </short-name>其实是标签库的默认名,也可以认为是其昵称。<uri> </uri>定义的是标签库的URI,在JSP中会使用到。<display-name></display-name>是显示名。<description></description>是标签库的记述,记述标签库的使用用途等等。
(3)<attribute>中<name></name>是属性名称定义。<required></required>表示的该属性是否是必须的属性,如果是必须的则<required></required>之间为true,否则为false。<rtexprvalue></rtexprvalue>表示的是可否使用表达式,大多数标签都是为false。这里不是不能使用表达式,而是恰恰相反表示可以使用表达式。<description></description>定义和前面介绍相同。
(4)在JSP中,如之前章节的演示代码所示,都是在文件头有个使用标签的声明,代码如下。
- <!---------------------文件名: *.jsp------------------------->
- <%@taglib prefix="s" uri="/struts-tags"%>
<!---------------------文件名: *.jsp------------------------->
<%@taglib prefix="s" uri="/struts-tags"%>
有了这个声明,在JSP文件中就可以使用Struts2的标签。比如form标签定义要像如下代码所示。
- <s:form action="upload" ………>
<s:form action="upload" ………>
记住一定要用“s”,它是Struts2中标签的默认名也是相当于一个昵称,当然读者也可以把它改为自己想取的名字,不过在标签声明中的“prefix”中就要改成那个自己取的名字。
注意:因为笔者使用的Servlet版本是2.3之上的版本,因此没必要在web.xml中定义标签库。如果读者使用的Servlet版本比较低,则在web.xml文件中需要定义如下的代码:
- <!----------------------文件名:web.xml----------------------->
- <taglib>
- <!- 定义URI - ->
- <taglib-uri>/Struts 2-tags</taglib-uri>
- <!- 定义标签库支持的jar包位置- ->
- <taglib-location>/WEB-INF/lib/struts2-core-2.0.11.1.jar</taglib-location>
- </taglib>
<!----------------------文件名:web.xml----------------------->
<taglib>
<!- 定义URI - ->
<taglib-uri>/Struts 2-tags</taglib-uri>
<!- 定义标签库支持的jar包位置- ->
<taglib-location>/WEB-INF/lib/struts2-core-2.0.11.1.jar</taglib-location>
</taglib>
只有这样标签库才会在Servlet版本比较低的情况下使用有效果。
posted @
2009-04-13 15:28 lanxin1020 阅读(454) |
评论 (0) |
编辑 收藏
--sql structured query language
--DML--Data Manipulation Language--数据操作语言
query information (SELECT),
add new rows (INSERT),
modify existing rows (UPDATE),
delete existing rows (DELETE),
perform a conditional update or insert operation (MERGE),
see an execution plan of SQL (EXPLAIN PLAN),
and lock a table to restrict access (LOCK TABLE).
--DDL--Data Definition Language--数据定义语言
create, modify,drop, or rename objects (CREATE,ALTER,DROP,RENAME),
remove all rows from a database object without dropping the structure (TRUNCATE),
manage access privileges (GRANT,REVOKE),
audit database use (AUDIT,NOAUDIT)
and add a description about an object to the dictionary (COMMENT).
--Transaction Control事务控制语句
save the changes(COMMIT)
or discard the changes (ROLLBACK) made by DML statements.
Also included in the transaction-control statements are statements to set a point or marker in the transaction for possible rollback (SAVEPOINT)
and to define the properties for the transaction (SET TRANSACTION).
Used to manage the properties of the database.
There isonly one statement in this category (ALTER SYSTEM).
--DCL--Data Control Language--与开发关系不是很密切,用于权限的分配与回收
grant,revoke,data control
--Session Control
control the session properties (ALTER SESSION)
and to enable/disable roles (SET ROLE).
--System Control
--------------------------------------------------------
select的用法
--每个员工的所有信息
select * from emp
--每个人的部门编号,姓名,薪水
select deptno,ename,sal from emp;
--每个人的年薪
select ename,sal*12 from emp;
--计算2*3的值
select 2*3 from emp;
--计算2*3的值(dual)
select 2*3 from dual;
select * from dual;
--得到当前时间
select sysdate from dual
--可以给列起别名,比如求每个人的年薪
select ename,sal*12 salperyear from emp;
--如果别名中有空格,需要用双引号
select ename,sal*12 "sal per year" from emp;
--如果没有内容,则为空
select comm from emp;
--当空字段参与计算,则结果是null
--例如:计算每个人的全年的收入包括月薪和年终奖
select ename,sal*12+comm from emp;
--可以将多个字符串拼在一起。比如:求每个人的薪水,格式为smith-sal-123
select ename||'-sal-'||sal from emp;
--如果字符串中有单引号,需要用另外一个单引号转义,比如:这样一个字符串: he's friend
select ename||'''s sal is'||sal from emp;
--------------------------------------------------------
--distinct 关键词的用法
--求有哪些个部门
select distinct deptno from emp
--可以用来修饰多个字段。比如:求有哪些个部门和job的组合
select distinct deptno,job from emp
--------------------------------------------------------
where关键词的用法
--可以是数值类型的等值判断。比如:求10这个部门的所有员工
select * from emp where deptno=20
--可以是字符串类型的等值判断。比如:求叫KING的这个人的信息
select * from emp where ename = 'KING'
--也可以是不等值判断。比如:求薪水小于2000的员工信息
select * from emp where sal<2000;
--字符串也可以做不等值判断,比如:求所有ename大于'CBA'的员工信息。
select * from emp where ename>'CBA';
--求部门不是10的员工
select * from emp where deptno <> 10;
--求薪水在800和1500之间的员工信息
select * from emp where sal >=800 and sal <=1500;
--也可以写成
select * from emp where sal between 800 and 1500
--这样写则不可以
-----------------------------select * from emp where 800<=sal<=1500
--where...in..的用法。比如:求薪水是800或者1500或正2000的员工信息
select * from emp where sal=800 or sal=1500 or sal=2000
--相当于写成这样
select * from emp where sal in(1500,800,2000,1500,1500,1500,1500);
--再比如求姓名是KING,SMITH,AA的员工信息
select * from emp where ename in ('KING','SMITH','AA')
--求入职时间在20-2月-81之后的员工信息
select * from emp where hiredate < '23-5月 -87';
--------------------------------------------------------
--and or not的用法
--求薪水大于1000或者部门在20这个部门的员工信息
select * from emp where sal>1000 and deptno=20
--求薪水不是800或者不是1500或者不是3000的员工信息
select * from emp where sal not in (800,1500,3000)
--也可以这样来写
select * from emp where sal <>800 and sal <> 1500 and sal<>3000
--------------------------------------------------------
--like的用法
--求名字中包含ALL这三个字符的员工信息
select * from emp where ename like '%E%';
--求名字中的第二个字母是A的员工
select * from emp where ename like '_A%';
--特殊字符需要转义。比如:求员工中包含特殊字符%的员工信息
select * from emp where ename like '%\%%' escape '\'
--------------------------------------------------------
--null的用法
--求没有年终奖的员工
select * from emp where comm is null
--求有年终奖的员工
select * from emp where comm is not null
--------------------------------------------------------
--order by的用法
--员工信息按照姓名正序排列
select * from emp order by ename asc;
--员工信息按照倒叙排列
select * from emp order by ename desc;
--也可以是多个字段组合排列。例如:员工信息按照部门正序排列,并且按照姓名倒叙排列
select * from emp order by deptno asc,ename desc
--------------------------------------------------------
--function的用法
--把所有姓名变成小写
select lower(ename) from emp;
--把所有姓名变成大写
select upper(ename) from emp;
--求所有人名中包含'a'的员工信息不区分大小写
select * from emp where lower(ename) like '%a%'
--截取子字符串,比如求Hello的一部分
select substr('hello',2,2) from dual;
select substr(ename,2,2) from emp;
--求Hello的一部分,并指明长度
--求ascii码对应的字符
select chr(65) from dual
--求字符对应的ascii码
select ascii('中')from dual
--四舍五入
select round(12.456,2) from dual
select round(12.456,-1) from dual
--四舍五入小数点后面多少位
--四舍五入小数点前面多少位
--------------------------------------------------------
--important!日期转换函数
--------------------------------------------------------
--将当前日期转换成1981-03-12 12:00:00这种形式的字符串
select to_char(sysdate,'YYYY-MM-DD HH24:MI:SS') from dual;
--将1981-03-12 12:00:00字符串转换成日期
select to_date('1981-03-12 12:00:00','YYYY-MM-DD HH24:MI:SS') from dual;
--将每个人的薪水转换成固定格式的字符串
select to_char(sal,'$999,999,999.99') from emp;
--将固定格式的字符串转换成数值
select to_number('$8,000.00','$999,999,999.99') from dual;
--当null参与计算时候,可以用nvl这个函数。比如求每个人一年总共的收入
select ename,sal*12+comm from emp
--------------------------------------------------------
--group function组函数
--求所有人的薪水的总和,平均值,最大值,最小值
select sum(sal),avg(sal),max(sal) ,min(sal) from emp;
--求总的行数
select count(*) from emp;
--求总的行树,(可以指定具体的字段)但如果字段有null值的时候需要小心使用
select count(comm) from emp;
--也可以过滤掉重复的行之后统计行数
select count(distinct deptno) from emp
--可以指明按照哪个字段进行分组.比如;分部门统计最高薪水
select deptno,max(sal) from emp where deptno is not null group by deptno
--也可以按照多个字段来分组统计,比如:分部门和岗位,统计最高薪水和行数
select deptno,job,max(sal),count(*) from emp group by deptno,job
--------------------------------------------------------
--重要:出现在select列表中的字段,如果没有在组函数中,那么必须出现在group by 子句中。
--------------------------------------------------------
select ename,deptno,job,max(sal),count(*) from emp group by deptno,job
--求薪水最高的员工姓名
select * from emp where sal=(select max(sal) from emp);
delete from emp where ename='TEST2'
update emp set deptno=10 where deptno=99
select * from dept
insert into dept (deptno,dname,loc) values('10','ACCOUNTING','NEW YORK');
--having从句的用法
--求平均薪水是2000以上的部门
select deptno,avg(sal) as avg_sal from emp group by deptno
having avg(sal) >2000
--------------------------------------------------------
--总结一下select语法
select
from
where
group by
having
order by
--------------------------------------------------------
-- 执行顺序very important!
-- 首先执行where语句将原有记录过滤;
-- 第二执行group by 进行分组;
-- 第三执行having过滤分组;
-- 然后将select 中的字段值选出来;
-- 最后执行order by 进行排序;
--------------------------------------------------------
/*
按照部门分组统计,求最高薪水,平均薪水
只有薪水是1200以上的才参与统计
并且分组结果中只包括平均薪水在1500以上的部门
而且按照平均薪水倒叙排列
*/
select max(sal),avg(sal) from emp
where sal>1200
group by deptno
having avg(sal) >1500
order by avg(sal) desc
--------------------------------------------------------
/*
把雇员按部门分组,
求最高薪水, 部门号,
过滤掉名字中第二个字母是'A'的,
要求分组后的平均薪水>1500,
按照部门编号倒序排列
*/
select max(sal) ,deptno from emp where ename not like '_A%'group by deptno
having avg(sal) >1500
order by deptno desc
/* very very important! */
select ename, deptno from emp;
select deptno, dname from dept;
--------------------------------------------------------------------------------------
--老语法:----------------------------------------------------------------------------
--------------------------------------------------------------------------------------
--等值连接:求员工姓名以及员工所在部门的名字同时显示出来
select ename,emp.deptno,dname,dept.deptno from emp,dept
where emp.deptno = dept.deptno
select ename,e.deptno,dname,d.deptno from emp e,dept d
where e.deptno = d.deptno
--非等值连接:要求每位雇员的薪水等级
select * from salgrade
select ename,sal,grade,losal,hisal from emp,salgrade
where sal >=losal and sal <=hisal
--跨3个表:求工作职位是’PRESIDENT’的雇员姓名,部门名称和薪水等级时
select ename,dname,grade from emp,dept,salgrade
where emp.deptno = dept.deptno
and sal >=losal and sal <=hisal
and job ='PRESIDENT'
--也可以同一个表做跨表连接:求每位员工的姓名,及其上级经理的姓名
select e1.ename,e2.ename from emp e1,emp e2
where e1.mgr = e2.empno
--------------------------------------------------------------------------------------
--新语法------------------------------------------------------------------------------
--在SQL1992的语法规则中,语句过滤的条件和表连接的条件都被放在了where子句中,当条件过多时,容易造成混淆,
--SQL1999修正了这个缺点,将连接条件和数据过滤条件区分开来,
--------------------------------------------------------------------------------------
--交叉连接
--结果会产生这两张表的笛卡尔乘积
select * from emp cross join dept
--要用deptno作为等值连接条件,我们可以这样写
select * from emp join dept using (deptno)
select ename, dname from emp join dept using(deptno);
--相当于
select ename, dname from emp join dept on emp.deptno = dept.deptno
--也可以写成这样
--也可以用于非等值连接
--求每位雇员的薪水等级
select * from emp join salgrade on (sal >=losal and sal<= hisal)
--多个join,where组合使用
--(求工作职位是’PRESIDENT’的雇员姓名,部门名称和薪水等级时)
select * from emp join dept on emp.deptno = dept.deptno
join salgrade on (sal >=losal and sal<= hisal)
where job = 'PRESIDENT'
--外连接--取出表中连接不到一起的多余的数据
--没有全内连接,没有右内连接
--其中outer也可以省略,简写为left join , right join , full join
--left inner join可以缩写成inner join 也可以缩写成join,意思是左内。
--update emp set deptno=20 where ename='SMITH';
--commit;
select * from emp;
select * from dept;
delete from dept where deptno=99;
--左内,从左往右找,匹配不上的记录不显示
select ename,emp.deptno from emp join dept on emp.deptno = dept.deptno;
select ename,emp.deptno from emp inner join dept on emp.deptno = dept.deptno;
--没有这种语法:select ename,emp.deptno from emp left inner join dept on emp.deptno = dept.deptno;
--左外连接,从左往右找,匹配不上的记录也显示一行
select ename,dept.deptno from emp left /*outer*/ join dept on emp.deptno = dept.deptno;
--右外连接,从右往左找,匹配不上的记录,也显示一行
select ename,dept.deptno from emp right /*outer*/ join dept on emp.deptno = dept.deptno;
--没有右内连接:select ename,dept.deptno from emp right inner join dept on emp.deptno = dept.deptno;
--全外连接
select ename,dept.deptno from emp full /*outer*/ join dept on emp.deptno = dept.deptno;
--左外,右外的区别
--什么时候用外连接呢?比如领导向你要所有学生的列表,顺便把所属的班级也列出来,就需要外连接
--在Where语句中使用子查询
-----------------------------------------------------------------
--雇员中最高薪水的人员名称
--1,先求出最高薪水
--2,再求雇员中最高薪水的人员名称
select ename from emp where sal=(select max(sal) from emp)
--有哪些人的薪水是在整个雇员的平均薪水之上的
select ename,sal from emp where sal >(select avg(sal) from emp)
-----------------------------------------------------------------
--雇员中哪些人是经理人
--1,首先查询mgr中有哪些号码
--2,再看有哪些人员的号码在此出现
select distinct mgr from emp where mgr is not null order by mgr
select ename
from emp
where empno in (select distinct mgr from emp where mgr is not null )
--where in 中不让写orderby
select ename
from emp
where empno in (select distinct mgr from emp where mgr is not null order by mgr)
-----------------------------------------------------------------
--在From子句中使用子查询
------------------------------------------------------------------
--部门平均薪水的等级
--1,首先将每个部门的平均薪水求出来
--2,然后把结果当成一张表,再用这张结果表和salgrade表做连接,以此求得薪水等级
select deptno,avg(sal) from emp group by deptno
select * from (select deptno,avg(sal) avg_sal from emp group by deptno) t join salgrade
on avg_sal between losal and hisal;
-----------------------------------------------------------------
--每个部门最高薪水的人员名称
--1,首先将每个部门的最高薪水求出来
--2,然后把结果当成一张表,再用emp和这张结果表做连接,以此求得每个部门最高薪水的人员名称
select deptno,max(sal) from emp where deptno is not null group by deptno
select ename from emp e join
(select deptno,max(sal) max_sal from emp where deptno is not null group by deptno ) t
on sal = max_sal and e.deptno = t.deptno
-----------------------------------------------------------------
--哪些人的薪水在部门的平均薪水之上
--1,首先将每个部门的平均薪水求出来
--2,然后把结果当成一张表,再用emp和这张结果表做连接,以此求得哪些人的薪水在部门的平均薪水之上
select deptno,avg(sal) avg_sal from emp group by deptno
select * from emp join (select deptno,avg(sal) avg_sal from emp group by deptno)t
on (sal>avg_sal and emp.deptno=t.deptno)
-----------------------------------------------------------------
--求部门中(所有人的)平均的薪水等级,形式如:
-- deptno avg_grade
-- 10 3.67
-- 20 2.8
-- 30 2.5
--1,先求每个人的薪水等级
--2,再按照部门分组,求平均数
select deptno,sal,grade from emp join salgrade on sal between losal and hisal
select deptno,avg(grade) from (select deptno,sal,grade from emp join salgrade on sal between losal and hisal)t group by deptno
------------------------------------------------------------------------------------------
--使用伪字段:rownum,----------------------
------------------------------------------------------------------------------------------
--用来标识每条记录的行号,行号从1开始,每次递增1
select rownum,emp.* from emp;
--oracle下rownum只能使用 < <=, 不能使用 = > >= 等比较操作符,
select rownum,emp.* from emp where rownum<5;
--当rownum和order by 一起使用时,会首先选出符合rownum条件的记录,然后再排序
--(错误的写法)例如,当我们要求薪水最高的前5个人时,最直接的想法可以这样写:
select * from emp where rownum<5 order by sal desc
--(正确的写法)可以这样写
select * from
(select * from emp order by sal desc) t
where rownum<=5
--------------------------------------------------------
--不准用组函数(即MAX()),求薪水的最高值(面试题)
--第一种解决办法:
--1,先把所有薪水按照倒序排列
--2,再取第一行
select * from
(select sal from emp order by sal desc) t
where rownum=1
--第二种解决办法:
--1,先跨表查询自己,先求出的结果中,e1.sal不可能出现最大数
--2,然后再not in
select e2.sal from emp e1,emp e2 where e1.sal>e2.sal
select sal from emp where sal not in(select e2.sal from emp e1,emp e2 where e1.sal>e2.sal)
-----------------------------------------------------------------
--求平均薪水最高的部门的部门编号
--第一种解决办法:
--1,先求出每个部门的平均薪水,
select deptno,avg(sal) avg_sal from emp group by deptno
--2,再求每个部门的平均薪水的最高值,
select max(avg_sal) from (1111111111111111111111111)
--3,最后再求第一步结果中avg_sal = 最高薪水的记录.
select deptno from (111111111111) where avg_sal = (22222222)
select deptno
from (select deptno,avg(sal) avg_sal from emp group by deptno)
where avg_sal =
(select max(avg_sal)
from (select deptno,avg(sal) avg_sal from emp group by deptno))
--没法考虑并列第一的情况
select deptno from
(select deptno,avg(sal) avg_sal from emp group by deptno order by avg(sal) desc)
where rownum<=1
--第二种解决办法:
--1,将上面的第一步第二步合并,先求最高平均薪水,用max(avg(sal))的办法
--不能写成select deptno,max(avg(sal)) from emp group by deptno
select max(avg(sal)) from emp group by deptno
--2,求出每个部门的平均薪水
select deptno,avg(sal) avg_sal from emp group by deptno
--3,最后再求第二步结果中(即每个部门的平均薪水),avg_sal = (第一步结果)的记录.即avg_sal =最高薪水的记录.
select deptno from (select deptno,avg(sal) avg_sal from emp group by deptno)
where avg_sal =(select max(avg(sal)) from emp group by deptno)
--第三种解决办法:
--1,先求出每个部门的平均薪水,
select avg(sal) avg_sal from emp group by deptno
--2,求最高平均薪水,用max(avg(sal))的办法
select max(avg(sal)) from emp group by deptno
--3,再使用having语句, avg(sal) = 第二步的结果
注意:为组函数起的别名在having中不能用
select deptno from emp group by deptno
having avg(sal) = (select max(avg(sal)) from emp group by deptno)
-----------------------------------------------------------------
--求平均薪水最高的部门的部门名称
--1,部门平均最高薪水
--2,得到部门编号列表,注意用group by deptno
--3,再应用having子句, having avg(sal) = (第一步的结果)
--4,得到平均最高薪水的那个部门的编号
--5,再得到部门名称
select dname from dept where deptno in
(
select deptno
from (select deptno,avg(sal) avg_sal from emp group by deptno)
where avg_sal =
(select max(avg_sal)
from (select deptno,avg(sal) avg_sal from emp group by deptno))
)
-----------------------------------------------------------------
--求平均薪水的等级最低的部门的部门名称
--第一步:部门平均薪水的等级,分成两个小步骤,第一小步是求部门平均薪水
select * from
(select deptno,avg(sal) avg_sal from emp group by deptno) t
join salgrade on avg_sal between losal and hisal
--第二步:最低的等级值
select min(grade) from (1111111111111111111111111)
--第三步:等于最低值的部门编号
------------有错误,应该是grade=
select deptno from (111111111111) where grade = (22222222222222)
--第四步:求名称
select dname from dept where deptno in(33333333333)
select dname
from dept
where deptno in
(
select deptno
from (select *
from (select deptno, avg(sal) avg_sal
from emp
group by deptno) t
join salgrade on avg_sal between losal and hisal)
where grade =
(select min(grade)
from (select *
from (select deptno, avg(sal) avg_sal
from emp
group by deptno) t
join salgrade on avg_sal between losal and hisal)))
--也可以用视图的方式来解决
--conn sys/bjsxt as sysdba
--grant create table, create view, create sequence to scott
--根据第一步的结果,建立一个view
create or replace view v1 as
--必须明确定义列
select deptno, avg_sal, grade from
(select deptno,avg(sal) avg_sal from emp group by deptno) t
join salgrade on avg_sal between losal and hisal
--查看一下
select * from v1
--查询一下
--带入view
select dname from dept where deptno in
(select deptno from (v1) where grade = (select min(grade) from v1))
-------------------------------------------------------------
--为什么in的后面不能order by ?
---------------------------------------------------------------
--求部门经理人中平均薪水最低的部门名称 (思考题)
第一步,求部门经理的雇员编号
select distinct mgr from emp where mgr is not null
第二步,按部门统计,求部门经理的平均薪水
select deptno,avg(sal) avg_sal from emp where empno in (select distinct mgr from emp where mgr is not null)group by deptno
第三步,求最低值
select min(avg(sal)) from emp where empno in (select distinct mgr from emp where mgr is not null)group by deptno
第四步,求部门经理人中平均薪水最低的部门名称
select deptno from (2222222222222) where avg_sal =(333333333333333333333333)
select dname from dept where deptno in (select deptno from (select deptno,avg(sal) avg_sal from emp where empno in (select distinct mgr from emp where mgr is not null)group by deptno) where avg_sal =(select min(avg(sal)) from emp where empno in (select distinct mgr from emp where mgr is not null)group by deptno))
----------------------------------------------------------------------------
--求比普通员工的最高薪水还要高的经理人名称
--1,求所有经理的编号
create or replace view v1 as
select distinct mgr from emp where mgr is not null
select * from v1
--2,普通员工的最高薪水
select max(sal) from emp where empno not in (select distinct mgr from emp where mgr is not null)
--3,
select ename from emp where empno in (select * from v1)
and sal > (select max(sal) from emp where empno not in (select distinct mgr from emp where mgr is not null))
--即:
select ename from emp where empno in (select distinct mgr from emp where mgr is not null)
and sal > (select max(sal) from emp where empno not in (select distinct mgr from emp where mgr is not null))
------------------------------------------------------------------------------
--求薪水最高的前5名雇员
--1,先观察一下
--2,看看rownum的作用
--3,不是我们想要的结果
select ename,sal from emp where rownum<=5 order by sal desc
--4,先order by,再rownum
select * from
(select ename,sal from emp order by sal desc ) t
where rownum<=5
--------------------------------------------------------------------------------
--求薪水最高的第6到第10名雇员(重点掌握)
--这种没法实现,oracle下rownum只能使用 < <=, 不能使用 = > >= 等比较操作符
--注意里面的rownum和外面的rownum的区别,外面要想访问里面的rownum,必须取得一个别名。
select * from
(select ename,sal from emp order by sal desc ) t
where rownum>=5
and rownum<=10
--所以再套一层select
select * from
(select t.*,rownum r from
(select ename,sal from emp order by sal desc ) t
)
where r>=5
and r<=10
--还有一种排序方式
select * from
(select * from emp order by sal desc)where rownum<=10
minus
select * from
(select * from emp order by sal desc)where rownum<=5
--------------------------------------------------------------------
--练习: 求最后入职的5名员工
--1,每个人的入职时间
--2,取前5行
-----------------------------------------------------------------
--求每个部门中薪水最高的前两名雇员
--1,每个员工的姓名,部门,工资,按部门和工资(倒序)排列
select ename,deptno,sal from emp order by deptno,sal desc
--2,套一层,加上个r
select ename,deptno,sal,rownum r from
(select ename,deptno,sal from emp order by deptno,sal desc) t
--3,创建试图
create or replace view v1
as
select ename,deptno,sal,rownum r from
(select ename,deptno,sal from emp order by deptno,sal desc) t
--观察一下
select * from v1
--每个部门中,薪水最高的第一行,并创建试图
create or replace view v2 as
select deptno,min(r) min_r from v1 group by deptno
--两个view跨表连接,大于薪水最高的行数,小于最高的行数+1,并且部门编号要匹配
select ename from v1 join v2
on ( v1.deptno = v2.deptno and v1.r >=v2.min_r and v1.r<=v2.min_r+1)
-------------------------------------------------------------------------------
--面试题: 比较效率
select * from emp where deptno = 10 and ename like '%A%';
select * from emp where ename like '%A%' and deptno = 10;
---------------------------------------------------------
--使用union、minus
--使用union、minus可以用来实现结果集的合并和去除(可以理解为加和减),例如:
select * from emp where deptno=10
union
select * from emp where deptno=20;
--相当于
select * from emp where deptno=10 or deptno=20
--而下面的语句
select * from emp where deptno in (10,20)
minus
select * from emp where sal < 1500;
--相当于
select * from emp where deptno in(10,20) and sal>=1500
--求分段显示薪水的个数
如:
scale total
<800 0
801-1000 2
1001-2000 3
2001-5000 6
>5000 8
select '<800' as scale ,count(*) as total from emp where sal<800
union
select '<801-1000' as scale ,count(*) as total from emp where sal<=1000 and sal>=801
--或者显示成为
--注意:使用between .. and .. 的时候,包含了最大和最小值。
800-1000 1001-2000 2001-5000
2 3 6
select * from
(select count(*) as "800-1000" from emp where sal >=800 and sal <= 1000),
(select count(*) as "1001-2000" from emp where sal >=1001 and sal <= 2000),
(select count(*) as "2001-5000" from emp where sal >=2001 and sal <= 5000)
--或显示成为
DEPTNO 800-2000 2001-5000
------ ---------- ----------
30 5 1
20 2 3
10 1 2
select t.deptno,"800-2000","2001-5000" from
(select deptno,count(*) as "800-2000" from emp where sal between 800 and 2000 group by deptno) t
join
(select deptno,count(*) as "2001-5000" from emp where sal between 2001 and 5000 group by deptno) t1
on t.deptno = t1.deptno
-----------------------------------------------------------------------------------
--每个薪水等级有多少名雇员 ?
--1,先求出每个雇员的薪水等级
--2,再group一下
posted @
2009-04-13 10:40 lanxin1020 阅读(183) |
评论 (0) |
编辑 收藏
(转)Hibernate支持两种锁机制:
即通常所说的“悲观锁(Pessimistic Locking)”和
“乐观锁(OptimisticLocking)”。
悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
Hibernate的加锁模式有:
Ø LockMode.NONE : 无锁机制。
Ø LockMode.WRITE :Hibernate在Insert和Update记录的时候会自动
获取。
Ø LockMode.READ : Hibernate在读取记录的时候会自动获取。
以上这三种锁机制一般由Hibernate内部使用,如Hibernate为了保证Update
过程中对象不会被外界修改,会在save方法实现中自动为目标对象加上WRITE锁。
Ø LockMode.UPGRADE :利用数据库的for update子句加锁。
Ø LockMode. UPGRADE_NOWAIT :Oracle的特定实现,利用Oracle的for
update nowait子句实现加锁。
乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
悲观锁与乐观锁的比较:
悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受;
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。乐观锁机制往往基于系统中的数据存储逻辑,因此也具备一定的局限性,如在上例中,由于乐观锁机制是在我们的系统中实现,来自外部系统的更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。在
系统设计阶段,我们应该充分考虑到这些情况出现的可能性,并进行相应调整(如将乐观锁策略在数据库存储过程中实现,对外只开放基于此存储过程的数据更新途径,而不是将数据库表直接对外公开)。
Hibernate 在其数据访问引擎中内置了乐观锁实现。如果不用考虑外部系统对数据库的更新操作,利用Hibernate提供的透明化乐观锁实现,将大大提升我们的生产力。
Hibernate中可以通过class描述符的optimistic-lock属性结合version描述符指定。
optimistic-lock属性有如下可选取值:
Ø none
无乐观锁
Ø version
通过版本机制实现乐观锁
Ø dirty
通过检查发生变动过的属性实现乐观锁
Ø all
通过检查所有属性实现乐观锁
其中通过version实现的乐观锁机制是Hibernate官方推荐的乐观锁实现,同时也是Hibernate中,目前唯一在数据对象脱离 Session发生修改的情况下依然有效的锁机制。因此,一般情况下,我们都选择version方式作为Hibernate乐观锁实现机制。
posted @
2009-04-12 16:12 lanxin1020 阅读(175) |
评论 (0) |
编辑 收藏
使用Hibernate自带的工具hbm2ddl,建立根据你的对象建立数据库:
首先建好POJO object, XML Mapping File(也可以使用工具根据POJO class建立),配置文件(hibernate.cfg.xml)
Java代码
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class SchemaUtil {
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SchemaExport schemaExport= new SchemaExport(cfg);
schemaExport.create(false, true);
}
}
posted @
2009-04-11 16:31 lanxin1020 阅读(159) |
评论 (0) |
编辑 收藏