<<上篇 | 首页 | 下篇>>

简单介绍Oracle驱动表 - 51CTO.COM

CBO根据统计信息选择Oracle驱动表,假如没有统计信息,则在from 子句中从左到右的顺序选择Oracle驱动表。这与RBO选择的顺序正好相反。这是英文原文(CBO determines join order from costs derived from gathered statistics. If there are no stats then CBO chooses the driving order of tables from LEFT to RIGHT in the FROM clause. This is OPPOSITE to the RBO) 。

 

我还是没法证实这句话的正确性。不过经过验证:“如果用ordered 提示(此时肯定用CBO),则以from 子句中按从左到右的顺序选择Oracle驱动表”这句话是正确的。实际上在CBO中,如果有统计数据(即对表与索引进行了分析),则优化器会自动根据cost值决定采用哪种连接类型,并选择合适的Oracle驱动表,这与where子句中各个限制条件的位置没有任何关系。如果我们要改变优化器选择的连接类型或Oracle驱动表,则就需要使用 hints了,具体hints的用法在后面会给予介绍。

 

阅读全文……

标签 : ,

Oracle 配置查询优化器 - liwenshui322的专栏 - 博客频道 - CSDN.NET

查询优化器参数

        1. optimizer_mode 

        查询优化器是为了找一个最高效的执行计划,这个参数用来定义什么是“高效”,比如是更快还是占用资源更少。在oracle10g中只支持两个参数值:

        all_rows:提供全部数据

        first_rows(n):n为大于0的自然数,表示尽快传输前面n条数据(比如分页查询的时候,我第一次只查询前面10条数据)

        oracle10g默认为all_rows,可以再数据库级别,会话级别,或者执行SQL的时候修改该参数的值。

        数据库级别:alter system set optimizer_mode=first_rows(10) scope=spfile;

        会话级别:ALTER SESSION SET OPTIMIZER_MODE=first_rows(10);

        SQL级别:SELECT /*+ first_rows(10) */ id,name from t1 order by id;

        其实,默认all_rows是最好的方式,如果确实是只要查询小部分数据,可以在sql级别加上提示,看是否能提高性能。

阅读全文……

标签 : ,

SiteMesh工作原理 - George.Le的个人页面 - 开源中国社区

总的来说,SiteMesh就是用来让你脱离<jsp:include/>标签的苦海的,它会为你自动地添加页头、脚注或者导航栏。公司里总会有人问我:你是怎么看源码的?而我总是告诉他们:如果你在高中阶段不是填鸭式学习的话,你应该会知道怎么看源码。他们总是一脸疑惑的看着我。事实上,我看源码是基于“猜想-验证”这样的步骤去做的,那么,要实现SiteMesh装饰器那样的效果,我的猜想是:

1、在装饰页面上留下类似于<dec:body/>这样的标记。

2、当jsp解释器遇到这个标记时,就把用户真正请求的页面塞进去。

然而,要做到这样的要求,要解决的问题有两个:

1、如何截取用户真正的请求页面

2、如何塞进去

幸好,这两个都不算什么大难题,我们可以使用Filter来拦截返回(Response)客户端浏览器的内容,从而实现内容的截取。第二个我们可以使用自定义JSP标签的方法实现“一遇到,则填充”这样的效果。

     事实上,SiteMesh作者的想法跟我猜想的思路是一致的,SiteMesh所使用的Filter在web.xml中写得很清楚:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<web-app>
    <!-- Start of SiteMesh stuff -->
    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class> 
    </filter>
 
    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>*</url-pattern>
    </filter-mapping>
 
    <taglib>
        <taglib-uri>sitemesh-page</taglib-uri>
        <taglib-location>/WEB-INF/sitemesh-page.tld</taglib-location>
    </taglib>
 
    <taglib>
        <taglib-uri>sitemesh-decorator</taglib-uri>
        <taglib-location>/WEB-INF/sitemesh-decorator.tld</taglib-location>
    </taglib>
</web-app>

 

 

没错,PageFilter就是SiteMesh用来拦截数据的类,那么再看看PageFilter类的doFilter方法:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public void doFilter(ServletRequest rq, ServletResponse rs, FilterChain chain)
            throws IOException, ServletException {
 
   if (rq.getAttribute(FILTER_APPLIED) != null) {
            // ensure that filter is only applied once per request
      chain.doFilter(rq, rs);
   }
   else {
      HttpServletRequest request = (HttpServletRequest) rq;
      HttpServletResponse response = (HttpServletResponse) rs;
 
      request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
 
      // force creation of the session now because Tomcat 4 had problems with
      // creating sessions after the response had been committed
      if (Container.get() == Container.TOMCAT) {
         request.getSession(true);
      }
 
      // parse data into Page object (or continue as normal if Page not parseable)
      Page page = parsePage(request, response, chain);
 
      if (page != null) {
         page.setRequest(request);
 
         Decorator decorator = factory.getDecoratorMapper().getDecorator(request, page);
         if (decorator != null && decorator.getPage() != null) {
            applyDecorator(page, decorator, request, response);
            page = null;
            return;
         }
 
         // if we got here, an exception occured or the decorator was null,
         // what we don't want is an exception printed to the user, so
         // we write the original page
         writeOriginal(response, page);
         page = null;
      }
   }
}

 

 

老实说,虽然代码注释很烂,但是基本的逻辑都体现在了doFilter方法里了,如果看代码不能让你拨开云雾的话,我还在网上扒了一幅图片:

 

clip_image001

 

当用户请求home.jsp,并且服务器处理完毕正准备返回数据之时,它被SiteMesh Filter拦截了下来,并且把数据包装成一个Page对象,具体是Page page = parsePage(request, response, chain)的调用,然后,它会去查询decorators.xml文件,看看该页面是否需要装饰[if (decorator != null && decorator.getPage() != null)]?是,则应用装饰器[applyDecorator(page, decorator, request, response)],否则,就发送原来的没经过装饰的页面[writeOriginal(response, page);]。

 

     然而,我们到底如何做才能把返回内容剥离出来呢?答案就是使用自定义的响应结果包装器,其实就是一个继承了HttpServletResponseWrapper类的java类,如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package servlet.util;
import java.io.CharArrayWriter;
/**
 * 自定义一个响应结果包装器,将在这里提供一个基于内存的输出器来存储所有
 * 返回给客户端的原始HTML代码。
 * @author lee
 */
public class MyResponseWrapper extends HttpServletResponseWrapper {
   private PrintWriter cacheWriter;
   private CharArrayWriter bufferWriter;//用于保存截获的jsp内容
    
    
   public MyResponseWrapper(HttpServletResponse response) {
      super(response);
      bufferWriter = new CharArrayWriter();
      // 这个是包装PrintWriter的,让所有结果通过这个PrintWriter写入到bufferedWriter中
      cacheWriter = new PrintWriter(bufferWriter);
   }
 
   /**
    *当一个继承了HttpServletResponseWrapper的包装器复写了getWriter()方法时
    *tomcat会把响应的内容塞入自定义的PrintWriter(cacheWriter)中
    */
   @Override
   public PrintWriter getWriter() throws IOException {
      return cacheWriter;
   }
    
   /**
    * 获取原始的HTML页面内容。
    * @return
    */
   public String getResult(){
      return bufferWriter.toString();
   }
 
}

 

 

一个很简单的类,然后,只需要在doFilter方法中如下调用,就可以截取jsp的页面内容了:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void doFilter(ServletRequest servletrequest,
   ServletResponse servletresponse, FilterChain filterchain) throws IOException, ServletException {
       
   // 使用我们自定义的响应包装器来包装原始的ServletResponse
   MyResponseWrapper wrapper = new MyResponseWrapper((HttpServletResponse) servletresponse);
       
   // 这句话非常重要,注意看到第二个参数是我们的包装器而不是原始的servletresponse
   //这样容器才会把响应内容写入自定义的包装器中
   filterchain.doFilter(servletrequest, wrapper);
       
   // 截获的结果并进行处理
   String result = wrapper.getResult();
   ......
}

 

 

而SiteMesh就是这样做的,只不过复杂点罢了,你可以去看看PageFilter类的parsePage方法。

     好了,现在我们可以截取jsp页面了,剩下的就是考虑如何把这些内容塞进我们的标签<dec:body/>中,而这就属于自定义JSP标签的范畴了,网上一搜一大把,总的来说就是在web.xml中指定标签库,又在标签库中指定处理类:

web.xml

1
2
3
4
5
6
7
8
9
<web-app>
......
   <!-- 自定义JSP标签  -->
   <taglib>
      <taglib-uri>http://customtag.com</taglib-uri>
      <taglib-location>/mytld/custom.tld</taglib-location>
   </taglib>
......
</web-app>

 

 

然后,你需要在项目的根目录下建立好mytld文件夹,在里面建立好custom.tld文件,使用任何一种编辑器打开并敲入下面的代码:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
   <tlibversion>1.0</tlibversion>
   <jspversion>1.1</jspversion>
   <shortname>Custom Tags</shortname>
   <uri>http://customtag.com</uri>
    
   <tag>
      <name>body</name>
      <tagclass>custom.tag.BodyTag</tagclass>
      <bodycontent>JSP</bodycontent>
   </tag>
</taglib>

 

 

在custom.tld中我们指定了遇到<dec:body/>标签就交给BodyTag处理,而BodyTag事实上就是一个继承了TagSupport的java类,并且,你需要重写doStartTag方法和doEndTag方法:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package custom.tag;
import javax.servlet.jsp.tagext.TagSupport;
public class BodyTag extends TagSupport{
   public int doStartTag(){
      try{
         String reqPage = (String) pageContext.getAttribute("reqPage", pageContext.REQUEST_SCOPE);
         if(reqPage == null){
            pageContext.getOut().print("标签开始了<font color=\"red\">Hello</font>");
         }else{
            pageContext.getOut().print(reqPage);
         }
      }catch (Exception e) {
         e.printStackTrace();
      }
      return EVAL_BODY_INCLUDE;
   }
    
   public int doEndTag(){
      return EVAL_PAGE;
   }
}

 

 

嗯,到底把截获的jsp页面放到哪里,才能让BodyTag类取到并使用呢?我把它放到了request对象当中:

image

 

下面看看自定义的MyFilter类的doFilter方法:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package servlet.demo;
import java.io.IOException;
public class MyFilter implements Filter {
   private ServletContext servletContext = null;
    
   public void destroy() {
 
   }
 
   public void doFilter(ServletRequest servletrequest,
         ServletResponse servletresponse, FilterChain filterchain)
         throws IOException, ServletException {
       
      // 使用我们自定义的响应包装器来包装原始的ServletResponse
      MyResponseWrapper wrapper = new MyResponseWrapper((HttpServletResponse) servletresponse);
       
      // 这句话非常重要,注意看到第二个参数是我们的包装器而不是原始的response
      //这样容器才会把响应内容写入自定义的包装器中
      filterchain.doFilter(servletrequest, wrapper);
       
      // 处理截获的结果并进行处理
      String result = wrapper.getResult();
      System.out.println(result);
      //把jsp页面放到request中
      servletrequest.setAttribute("reqPage", result);
      //把smart.jsp包含进请求中,这一步会触发jsp解释器去解释smart.jsp页面
      //当遇上<dec:body/>时,塞入result
      servletrequest.getRequestDispatcher("decorators/smart.jsp").include(servletrequest, servletresponse);
   }
 
   public void init(FilterConfig filterconfig) throws ServletException {
      servletContext = filterconfig.getServletContext();
   }
}

 

 

这里的servletrequest.getRequestDispatcher("decorators/smart.jsp").include(servletrequest,servletresponse)很重要,因为程序执行到这里会触发jsp解释器去解释jsp页面,而smart.jsp就是我们的装饰页面:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%@ taglib uri="http://customtag.com" prefix="dec"%>
<html>
<head>
   <title>欢迎访问 </title>
</head>
<body>
   <div style="margin-bottom:10px;padding:6px;border:1px solid gray;">我这里是头部,页面装饰器头部定义</div>
    
   <div style="margin-bottom:10px;padding:6px;border:1px solid gray;">
      功能菜单:<a href="#"/>用户管理</a>
   </div>
    
   <!-- 这里是操作信息提示区域 -->
   <div></div>
    
   <!-- 这里是功能的内容区域 -->
   <div>
      <dec:body />
   </div>
    
   <div style="margin-top:10px;padding:6px;border:1px solid gray;">我这里是尾部,页面装饰器尾部定义</div>
</body>
</html>

 

 

当jsp解释器解释smart.jsp的过程中遇到了<dec:body/>,就会跑到BodyTag中执行标签解释工作,此时,我们就可以把早已准备好的用户真正请求的页面内容塞进去:

 

1
2
3
4
//从request中取出数据
String reqPage = (String) pageContext.getAttribute("reqPage", pageContext.REQUEST_SCOPE);
//填充
pageContext.getOut().print(reqPage);

 

 

SiteMesh正是使用这种方式实现自动装饰功能,这里面并没有什么高深的技术(可能是我还没有发现)。

 

现在,写一个需要应用装饰的页面index.jsp:

 

1
2
<!-- index.jsp -->
<h1>Hello World!!</h1>

 

在index.jsp中你甚至不用写<body/>标签

阅读全文……

标签 :