Hibernate的Fetch

越来越发现其实掌握 hibernate并不容易,Spring用起来其实简单多了,但是在用hibernate的时候真的是需要一定的时间积累,对一个项目组来说如果采用hibernate最好有一个对hibernate比较清楚的人否则碰到问题就会成为项目的风险。
我想告诉各位的是,掌握hibernate可能比你预期的难多了,当你轻松的告诉我,hibernate很简单的时候该是你自己多反省了. (只有一种情况例外,你是一个牛人)

好了,一个引子废话那么多,其实今天只是想先说一说hibernate里的Fetch的作用.

大家都知道,在hibernate里为了性能考虑,引进了lazy的概念,这里我们以Parent和Child为模型来说明,

public class Parent implements Serializable {

    /** identifier field */
    private Long id;

    /** persistent field */
    private List childs;

    //skip all getter/setter method

  
}  

public class Child implements Serializable {

    /** identifier field */
    private Long id;

    /** persistent field */
    private net.foxlog.model.Parent parent;

    //skip all getter/setter method

}
在我们查询Parent对象的时候,默认只有Parent的内容,并不包含childs的信息,如果在Parent.hbm.xml里设置lazy=”false”的话才同时取出关联的所有childs内容.

问题是我既想要hibernate默认的性能又想要临时的灵活性该怎么办?  这就是fetch的功能。我们可以把fetch与lazy=”true”的关系类比为事务当中的编程式事务与声明式事务,不太准确,但是大概是这个意思。

总值,fetch就是在代码这一层给你一个主动抓取得机会.

Parent parent = (Parent)hibernateTemplate.execute(new HibernateCallback() {
            public Object doInHibernate(Session session) 
throws HibernateException, SQLException {
                Query q = session.createQuery(
                        ”from Parent as parent ”+
                                ” left outer join fetch parent.childs ” +
                                ” where parent.id = :id”
                );
                q.setParameter(”id”,new Long(15));
                return (Parent)q.uniqueResult();
            }

        });

        Assert.assertTrue(parent.getChilds().size() > 0);

你可以在lazy=”true”的情况下把fetch去掉,就会报异常. 当然,如果lazy=”false”就不需要fetch了

有一个问题,使用Fetch会有重复记录的现象发生,我们可以理解为Fetch实际上不是为Parent服务的,而是为
Child服务的.所以直接取Parent会有不匹配的问题.

============================================================

update:以上有些结论错误,实际上在hibernate3.2.1版本下测试,可以不出现重复记录,

public void testNPlusOne() throws Exception{
        List list = (List)hibernateTemplate.execute(new HibernateCallback() {
            public Object doInHibernate(Session session)
 throws HibernateException, SQLException {
                Query q = session.createQuery(
                        ”select distinct p from net.foxlog.model.Parent
                           p inner join fetch p.childs”
                );
                return q.list();
            }

        });

        //((Parent)(list.get(0))).getChilds();
        System.out.println(”list size = ” + list.size());
        for(int i=0;i<list.size();i++){
            Parent p = (Parent)list.get(i);
            System.out.println(”===parent = ” + p);
            System.out.println(”===parent’s child’s length = ” + p.getChilds().size());
        }

    }

打印结果如下:
Hibernate: select distinct parent0_.id as id2_0_, childs1_.id as id0_1_, 
childs1_.parent_id as parent2_0_1_, childs1_.parent_id as parent2_0__, 
childs1_.id as id0__ from parent parent0_ inner join child childs1_ on
 parent0_.id=childs1_.parent_id
list size = 3
===parent = net.foxlog.model.Parent@1401d28[id=14]
===parent’s child’s length = 1
===parent = net.foxlog.model.Parent@14e0e90[id=15]
===parent’s child’s length = 2
===parent = net.foxlog.model.Parent@62610b[id=17]
===parent’s child’s length = 3

另外,如果用open session in view模式的话一般不用fetch,但首先推荐fetch,如果非用的话因为有N
+1的现象,所以可以结合batch模式来改善下性能.

08月 26th, 2008 | 3 Comments

Tapestry学习笔记

因为项目原因要用Tapestry。在网上找了一通资料,都是零零碎碎,没有一个完整的学习过程,而且国内技术论坛就那么几篇文章来回抄,一点技术含量都没有。好象国内的技术人员都喜欢把学会一样新东西当成自己的私家法宝,那点经验收着藏着,生怕别人学了去。其实技术这个东西和手机一样,刚出新产品的时候很新潮,过一阵就掉价掉得吐血。整理了自己的学习笔记,也算是给同行们贡献一小爪。。。嘿嘿,其实也是另有居心,拿去让同事学,他会了,我的活儿就交给他了。。。奸笑中。。。。
目的
用Ecplise开发Tapestry项目。
搭建环境
本机现有:tomcat5.5,jdk1.5(java5.0)
下载
下载Eclipse3.1.2解压。
下载Tapestry4.0 源码包,解压。- Tapestry的类库
下载HiveMind1.1.1源码包,解压。- Tapestry的微内核安装
Eclipse
Eclipse解压即可使用,但需要调整Jdk配置为1.5。这个设置必须保证实现,因为Tapestry4.0中某些特性只在jdk1.5下有效。
Spindle (Eclipse插件,用于开发Tapestry项目)
以下方法通过Eclipse自动下载安装Spindle。
1. 选择更新菜单项
图1-1:

2. 选择”查找新安装功能”
图1-2:

3. 选择”新建远程站点”,填写与Tapestry相关的参数。
图1-3:

4. 完成后,Eclipse会自动查找下载站点和镜像站点,按OK即可。

图1-4:
 

建议采用自动下载安装方式,否则运行可能会有问题

第一个例子HelloWorld
1. 新建一个工程,选择Tapestry Web Project
图1-5:
 

2. 工程名取为MyTapestry,其它按照默认。
图1-6:
 

3. 自动得到下列工程模块:
图1-7:
 

4. 得到工程后,可再调整工程参数,增加一个bin目录,作为输出目录。增加一个build作为建筑目录,增加一个output作为输出目录。
图1-8:
 

5. 将工作的输出目录调整为MyTapestry/bin
图1-9:
 

6. 将context/WEB-INF目录下的Home.html移动到context目录中。在<body>中增加一句话:
<span jwcid=”$content$”> Hello,Tapesty! </span>
7. 在build目录中写build.xml,按最简单的写法,将war文件输出到output目录,并同时发布到tomcat发布目录下。用Ant编译工程。
8. 启动tomcat5.5,输入以下地址:http://localhost:8080/MyTapestry/Home.html,应可成功显示:Hello,Tapesty! 第一个例子完成。

第一个带Tapestry组件的例子
1. 在开始之前,必须要重新编译一下Tapestry4.0源码包。

1) 修改Tapestry源码包解压目录下config中的build.properties,其它属性都可以注释掉,添加下面两条:
hivebuild.dir=E:/hivemind1.1.1/hivebuild
tomcat.dir=E:/tomcat5.5.9
具体的安装路径根据实际情况修改。
2) 在Eclipse中新建一个工程,Tapestry,指向Tapestry4.0源码包解压目录。
图1-10:
 

3) 工程建好之后,可以利用Eclipse的Ant工具对源码进行编译。注意要选择扩展选项,因为有参数需要设置。
图1-11:
 

4) 在Ant参数中,Properties参数里增加一条参数,如下。这条参数的意义在于能跳过编译过程中对Tapestry的校验。这个校验需要依赖于Junit和JBoss,会让过程变得非常复杂。
图1-12:
 

5) 编译时,Eclipse需要到网上下载一些相关的包,所以必须保证机器挂在网上。下载的东西比较多,编译时间看网速来定。这时可以走开去喝杯茶。
6) 编译完成之后,Tapestry4.0源码包解压目录中会多出一个目录ext-package,这个目录的子目录lib下的jar文件就是正常执行一个Tapestry应用所需的库文件。
7) Tapestry的工程在Eclipse中会显示很多错误,那是因为该工程的Libraries中缺少相关的类库,但这不影响Ant的build工作。如果看不过眼,可以自己找到错误的地方把缺少的jar添加进去。不过,Example包里始终有一个类找不着,反正只是Example,看看别人怎么写的就行。
2. 制作一个带组件能互动的只有一个页面的最小的Tapestry应用。

1) 修改一下Home.html,换成下面的代码,先运行起来再慢慢说里面的东西是什么意思:
<html>
  <head>
    <title>MyTapestry: DirectLink</title>
  </head>
  <body>
    <h1>This is my first test</h1>
       
<p>
  The current value is: <span style=”font-size:xx-large”><span jwcid=”@Insert” value=”ognl:counter”>37</span></span>
</p>       

<p>
  <a href=”#” mce_href=”#” jwcid=”by1@DirectLink” listener=”listener:doClick” parameters=”ognl:1″>increment counter by 1</a>
</p>

<p>
  <a href=”#” mce_href=”#” jwcid=”by5@DirectLink” listener=”listener:doClick” parameters=”ognl:5″>increment counter by 5</a>
</p>

<p>
  <a href=”#” mce_href=”#” jwcid=”by10@DirectLink” listener=”listener:doClick” parameters=”ognl:10″>increment counter by 10</a>
</p>
 
    <p>
  <a href=”#” mce_href=”#” jwcid=”clear@DirectLink” listener=”listener:doClear”>clear counter</a>
</p>
       
<p>
  <a href=”#” mce_href=”#” jwcid=”@PageLink” page=”Home”>refresh</a>
</p>       
       
  </body>
</html>
2) 为Home.html建立对应的类,Home.java,这个类放在自己的包中,包结构自己定,建在src目录下。
图1-13:
 

3) 这个Home.java的内容很简单,如下,一看就知道是个计数器:

package test.tapestry.pages;

import org.apache.tapestry.html.BasePage;
import org.apache.tapestry.annotations.Persist;

public abstract class Home extends BasePage {
    @Persist
    public abstract int getCounter();
    public abstract void setCounter(int counter);
   
    public void doClick(int increment)
    {
        int counter = getCounter();
       
        counter += increment;
       
        setCounter(counter);
    }
   
    public void doClear()
    {
        setCounter(0);
    }
}
4) Eclipse已经为Home.html自动生成Home.page了,在context/WEB-INF目录下。这个page文件是Home.html和Home.java之间的纽带,因为我自己的类没有在原来的包中,需要重定义一下,说明在哪里可以找到Home.java这个类。
 

5) 查看web.xml,在生成工程的时候,已经自动生成了以下代码:
<web-app>
    <display-name>MyTapestry</display-name>
    <servlet>
        <servlet-name>MyTapestry</servlet-name>
        <servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyTapestry</servlet-name>
        <url-pattern>/app</url-pattern>
    </servlet-mapping>
</web-app>
注意servlet-name为MyTapestry。那么下一个要修改的配置文件就是MyTapestry.application。
6) 修改MyTapestry.application,改成下面这个样子,这是为了把Home.html和Home.page连接起来。
<application name=”MyTapestry” engine-class=”org.apache.tapestry.engine.BaseEngine”>
   
    <description>add a description</description>
    <meta key=”org.apache.tapestry.page-class-packages” value=”test.tapestry.pages”/>
    <!– page name=”Home” specification-path=”Home.page”/ –>
   
</application>
7) 准备build.xml,我的代码如下,里面的值需要根据实际情况修改。
<?xml version=”1.0″?>
<project name=”MyTapestry” default=”deploy”>

 <!– Customize this to match the desired name. –>
 
 <property name=”project.name” value=”MyTapestry”/>
 
 <!– Find out where other project’s distributions are. –>
 
 <!– property file=”../common/build.properties”/ –>
 
 <!– All derived files end up somewhere under target. –>
 
 <property name=”target.dir” value=”f:/workspace/MyTapestry”/>
 
 <property name=”src.dir” value=”${target.dir}/src”/>
 <property name=”context.dir” value=”${target.dir}/context”/>
 <property name=”conf.dir” value=”${target.dir}/conf”/>
   
 <property name=”classes.dir” value=”${target.dir}/bin”/>
 <property name=”output.dir” value=”${target.dir}/output”/>
 
 <property name=”war.file” value=”${target.dir}/output/${project.name}.war”/>
 
 <property name=”tomcat.dir” value=”e:/tomcat5.5.9″/>
 <property name=”tapestry.dist.dir” value=”${context.dir}/WEB-INF”/>
 
 <!– We expect Tomcat to be available. –>
 
 <property name=”servlet.jar” value=”${tomcat.dir}/common/lib/servlet.jar”/>
  
 <path id=”project.class.path”>
  <fileset dir=”${tapestry.dist.dir}/lib”>
  
   <!– The Tapestry JARs –>
   <include name=”*.jar”/>
   
   <!– Dependencies of Tapestry. –>
   
   <include name=”**/*.jar”/>
  </fileset>
  <pathelement location=”${servlet.jar}”/>
 </path>
 
 <target name=”clean” description=”Deletes derived files.”>
  <echo>…Deletes derived files…</echo>
  <delete dir=”${output.dir}” quiet=”true”/>
 </target>
 
 <target name=”compile” description=”Compile Java classes.”>
  <echo>…Compile Java classes….</echo>
  <mkdir dir=”${classes.dir}”/>
  <javac srcdir=”${src.dir}” destdir=”${classes.dir}” debug=”on”
   classpathref=”project.class.path” mce_href=”project.class.path”/>
 </target>
 
 <target name=”war” depends=”compile”
  description=”Compile all classes and build the WAR.”>
  <echo>…Compile all classes and build the WAR….</echo>
  <war warfile=”${war.file}” webxml=”${context.dir}/WEB-INF/web.xml”>
  
   <!– Copy everything in the context directory to the root of the WAR, including
        everything in WEB-INF, except for the web.xml, which the <war> task
        strangely demands be seperate. –>
       
   <fileset dir=”${context.dir}”>
    <exclude name=”WEB-INF/web.xml”/>
    </fileset>
    
    <!– Pick up compiled classes.  These go into WEB-INF/classes.  –>
    
   <classes dir=”${classes.dir}”/>
      
   <!– Pick up properties files and such from the Java source directory. –>
   
   <classes dir=”${src.dir}”>
    <exclude name=”**/*.java”/>
    <exclude name=”**/package.html”/>
   </classes>
   
   <!– There isn’t a way to flatten, so we need to do this XXX times. –>
   
   <!– lib dir=”${tapestry.dist.dir}/lib”>
     <include name=”*.jar”/>
   </lib –>
   
   <!– Pick up the main Tapestry dependencies. –>
   
   <!– lib dir=”${tapestry.dist.dir}/lib/ext”>
    <include name=”*.jar”/>
   </lib –>
   
   <!– lib dir=”${tapestry.dist.dir}/lib/runtime”>
    <include name=”*.jar”/>
   </lib–>

  </war>
 </target>
 
 <target name=”deploy” depends=”war”
  description=”Deploy the WAR into Tomcat.”>
  <echo>…Deploy the WAR into Tomcat…..</echo>
  <!– You can deploy elsewhere by setting deploy.dir in the build.properties file. –>
  
  <property name=”deploy.dir” value=”${tomcat.dir}/webapps”/>
  
  <copy file=”${war.file}” todir=”${deploy.dir}”/>
 </target>
</project>
8) 做完上面这些,就可以用Ant来编译工程了。打包好的MyTapestry.war会发布在Tomcat发布目录下。
9) 启动Tomcat,输入http://localhost:8080/MyTapestry/app,就可以看到Home.html页面,而且是可以运行的。点击一下链接就应该可以看到变化。如果出错,页面上会有详细的错误提示,是Tapestry自带的跟踪提示,很完美。
10) 为什么URL要写成这个样子?是因为web.xml里已经定义了映射关系:
    <servlet-mapping>
        <servlet-name>MyTapestry</servlet-name>
        <url-pattern>/app</url-pattern>
    </servlet-mapping>
“/app”指向”MyTapestry”。
3. 接下来要解释一下Home.html里出现的特别的代码。

1) <span jwcid=”@Insert” value=”ognl:counter”>

Jwcid表示后面跟着一个Tapestry组件。
@后面跟的是组件类型,Insert这个类型很显然,就是要插入一个值。值的定义就在后面,”ognl:counter”。
这个ognl前缀表示后面是一个表达式,在显示页面的时候需要取该表达式的值。这里是一个变量,也就是在显示的时候,会取这个变量的值。谁的变量?当然是与Home.html对应的Home.java里的变量嘛!

2) <a href=”#” mce_href=”#” jwcid=”by1@DirectLink” listener=”listener:doClick” parameters=”ognl:1″>

DirectLink这个组件的意思是,直接连接到本页面对应的类。
Listener属性表示要调用类的方法,哪个类?就是DirectLink说明的类。Listener属性的值中,listener前缀表示,后面跟的单词就是被调用的方法名,嗯,这说明,可以用其它的单词指向某个方法。在Home.java里找一找,就能看到doClick()这个方法了。
Parameter属性,是针对listener的,也就是提供调用方法的接入参数。这里是”ognl:1″,参数值一个写死的字串:1。
“by1@DirectLink“这个@前面为什么还有一个”by1″呢?因为DirectLink这个组件可以有多个对象,取个名字,可以区分开各个对象,就那么简单。

3) <a href=”#” mce_href=”#” jwcid=”@PageLink” page=”Home”>

PageLink组件,表示要连接到其它页面。连接到哪个页面?在page属性里定义。这里表示连接到自己,那就是现reflash的动作了。
为什么page的值写成”Home”,而不是”Home.html”?因为在Tapestry中,一个页面是与一大串东西连接在一起的,这个”Home”事实上包括了:Home.html Home.page Home.java以及把这三个文件连接在一起的配置文件。这样就清楚了。

第一段笔记完成。至少可以从无到有建一个Tapestry工程了,而且还是能跑的。呵呵! 爽啦~

(工程名称为MaTapestry,区分大小写,因为之前用过一个全小写的,拷屏图没换,大家自己玩的时候别纳闷就好。)

08月 25th, 2008 | Leave a Comment

分析 Tomcat catalina.bat 脚本

Catalina.bat是tomcat所有脚本中最重要的脚本,完成几乎所有的tomcat操作。如启动,关闭等等,都是由catalina.bat脚本来完成的。接下来,我将对Tomcat catalina.bat脚本进行分析。

    首先省去catalina.bat开头诸多注解,这些注解主要是讲解各个变量是干什么的。需要的话,自己看下英文就可以了。这里就不翻译了。

rem Guess CATALINA_HOME if not defined  查看是否在tomcat目录下,与startup.bat里相同,不解释了。需要的话可以看我的另一篇博客。
set CURRENT_DIR=%cd%
if not “%CATALINA_HOME%” == “” goto gotHome
set CATALINA_HOME=%CURRENT_DIR%
if exist “%CATALINA_HOME%\bin\catalina.bat” goto okHome
cd ..
set CATALINA_HOME=%cd%
cd %CURRENT_DIR%
:gotHome
if exist “%CATALINA_HOME%\bin\catalina.bat” goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome

rem Get standard environment variables
if exist “%CATALINA_HOME%\bin\setenv.bat” call “%CATALINA_HOME%\bin\setenv.bat” 如果存在setenv.bat脚本,调用它,我的tomcat 没有这个脚本

rem Get standard Java environment variables
if exist “%CATALINA_HOME%\bin\setclasspath.bat” goto okSetclasspath 查看是否存在setclasspath.bat脚本,如果存在,转到okSetclasspath位置
echo Cannot find %CATALINA_HOME%\bin\setclasspath.bat 否则输出下面两行,并退出
echo This file is needed to run this program
goto end
:okSetclasspath    okSetclasspath位置

set BASEDIR=%CATALINA_HOME%   设定BASEDIR变量与CATALINA_HOME变量值相同
call “%CATALINA_HOME%\bin\setclasspath.bat” %1   调用setclasspath.bat脚本并加上参数
if errorlevel 1 goto end      如果存在错误 退出

rem Add on extra jar files to CLASSPATH   设定JSSE_HOME变量,如果存在加入CLASSPATH,不存在跳过

if “%JSSE_HOME%” == “” goto noJsse    检查是否存在JSSE_HOME变量
set CLASSPATH=%CLASSPATH%;%JSSE_HOME%\lib\jcert.jar;%JSSE_HOME%\lib\jnet.jar;%JSSE_HOME%\lib\jsse.jar 如果有加入到CLASSPATH变量后面
:noJsse                                  
set CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\bootstrap.jar  将bootstrap.jar加入到CLASSPATH里

if not “%CATALINA_BASE%” == “” goto gotBase  如果CATALINA_BASE变量不为空,跳过,转到gotBase位置
set CATALINA_BASE=%CATALINA_HOME%  如果为空,将CATALINA_BASE设为CATALINA_HOME变量的值
:gotBase

if not “%CATALINA_TMPDIR%” == “” goto gotTmpdir   CATALINA_TMPDIR不为空,跳过,转到gotTmpdir位置
set CATALINA_TMPDIR=%CATALINA_BASE%\temp   如果为空,将 CATALINA_TMPDIR设为%CATALINA_BASE%\temp变量的值(即tomcat\temp)
:gotTmpdir

if not exist “%CATALINA_HOME%\bin\tomcat-juli.jar” goto noJuli  如果不存在tomcat-juli.jar这个类,转到noJuli位置
set JAVA_OPTS=%JAVA_OPTS% -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager - Djava.util.logging.config.file=”%CATALINA_BASE%\conf\logging.properties”  如果存在,将变量加入到JAVA_OPTS里
:noJuli

set JAVA_OPTS=%JAVA_OPTS% -Xms128m -Xmx512m -Dfile.encoding=UTF8 -Duser.timezone=GMT -Djava.security.auth.login.config=%CATALINA_HOME%/conf/jaas.config  设定JAVA_OPTS变量

echo Using CATALINA_BASE:   %CATALINA_BASE%     输出CATALINA_BASE变量值
echo Using CATALINA_HOME:   %CATALINA_HOME%     输出CATALINA_HOME变量值
echo Using CATALINA_TMPDIR: %CATALINA_TMPDIR% 输出CATALINA_TMPDIR变量值
if “”%1″” == “”debug”" goto use_jdk        如果变量%1里存在debug ,转到use_jdk位置
echo Using JRE_HOME:        %JRE_HOME%     输出JRE_HOME变量值
goto java_dir_displayed      转到java_dir_displayed
:use_jdk
echo Using JAVA_HOME:       %JAVA_HOME%    输出JAVA_HOME变量值
:java_dir_displayed
                        下面几行设定相应变量
set _EXECJAVA=%_RUNJAVA%       
set MAINCLASS=org.apache.catalina.startup.Bootstrap
set ACTION=start
set SECURITY_POLICY_FILE=
set DEBUG_OPTS=
set JPDA=

if not “”%1″” == “”jpda”" goto noJpda
set JPDA=jpda
if not “%JPDA_TRANSPORT%” == “” goto gotJpdaTransport
set JPDA_TRANSPORT=dt_shmem
:gotJpdaTransport
if not “%JPDA_ADDRESS%” == “” goto gotJpdaAddress
set JPDA_ADDRESS=jdbconn
:gotJpdaAddress
if not “%JPDA_SUSPEND%” == “” goto gotJpdaSuspend
set JPDA_SUSPEND=n
:gotJpdaSuspend
if not “%JPDA_OPTS%” == “” goto gotJpdaOpts
set JPDA_OPTS=-Xdebug -Xrunjdwp:transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
:gotJpdaOpts
shift
:noJpda

if “”%1″” == “”debug”" goto doDebug    如果%1为debug,转到doDebug,运行debug模式
if “”%1″” == “”run”" goto doRun        如果%1为run,转到doRun,运行正常模式
if “”%1″” == “”start”" goto doStart    如果%1为start,转到doStart,启动tomcat
if “”%1″” == “”stop”" goto doStop      如果%1为stop,转到doStop,关闭tocmat
if “”%1″” == “”version”" goto doVersion 如果%1为version,转到doVersion,显示tomcat的版本号

echo Usage:  catalina ( commands … ) 如果%1没有上述内容,输出下面几行,并结束
echo commands:
echo   debug             Start Catalina in a debugger
echo   debug -security   Debug Catalina with a security manager
echo   jpda start        Start Catalina under JPDA debugger
echo   run               Start Catalina in the current window
echo   run -security     Start in the current window with security manager
echo   start             Start Catalina in a separate window
echo   start -security   Start in a separate window with security manager
echo   stop              Stop Catalina
echo   version           What version of tomcat are you running?
goto end

:doDebug
shift                    将%2里的值转到%1
set _EXECJAVA=%_RUNJDB%  将变量 _EXECJAVA设为_RUNJDB变量的值
set DEBUG_OPTS=-sourcepath “%CATALINA_HOME%\..\..\jakarta-tomcat-catalina\catalina\src\share”
设定DEBUG_OPTS变量

if not “”%1″” == “”-security”" goto execCmd   
如果%1不为-security,转到execCmd位置

shift       将%2里的值转到%1
echo Using Security Manager       输出该行
set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy
设定SECURITY_POLICY_FILE变量的值

goto execCmd     转到execCmd位置

:doRun
shift        将%2里的值转到%1
if not “”%1″” == “”-security”" goto execCmd   如果%1不为-security,转到execCmd位置
shift        将%2里的值转到%1
echo Using Security Manager   输出该行
set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy
设定SECURITY_POLICY_FILE变量的值

goto execCmd  转到execCmd位置

:doStart
shift        将%2里的值转到%1
if not “%OS%” == “Windows_NT” goto noTitle  如果OS变量不为Windows_NT,转到noTitle
set _EXECJAVA=start “Tomcat” %_RUNJAVA%       设定_EXECJAVA变量的值
goto gotTitle      转到gotTitle位置
:noTitle
set _EXECJAVA=start %_RUNJAVA%    设定 _EXECJAVA 变量的值
:gotTitle              
if not “”%1″” == “”-security”" goto execCmd   如果%1不为-security,转到execCmd位置
shift                    将%2里的值转到%1
echo Using Security Manager        输出该行
set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy  
设定SECURITY_POLICY_FILE变量的值

goto execCmd       转到execCmd位置

:doStop
shift                 将%2里的值转到%1
set ACTION=stop       将ACTION的变量设为stop
set CATALINA_OPTS=    设CATALINA_OPTS为空
goto execCmd          转到execCmd位置

:doVersion            显示tomcat版本号
%_EXECJAVA% -classpath “%CATALINA_HOME%\server\lib\catalina.jar” org.apache.catalina.util.ServerInfo   执行该命令
goto end              结束该程序

:execCmd          
rem Get remaining unshifted command line arguments and save them in the
以下几行将命令参数存入CMD_LINE_ARGS变量中

set CMD_LINE_ARGS=
:setArgs
if “”%1″”==”"”" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs

rem Execute Java with the applicable properties
if not “%JPDA%” == “” goto doJpda        如果JPDA变量不为空,转到doJpda位置
if not “%SECURITY_POLICY_FILE%” == “” goto doSecurity
如果SECURITY_POLICY_FILE变量不为空,转到doSecurity位置

 如果都没有执行下面命令,并结束该程序
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs=”%JAVA_ENDORSED_DIRS%” -classpath “%CLASSPATH%” -Dcatalina.base=”%CATALINA_BASE%” -Dcatalina.home=”%CATALINA_HOME%” -Djava.io.tmpdir=”%CATALINA_TMPDIR%” %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurity        执行下面命令,并结束该程序
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs=”%JAVA_ENDORSED_DIRS%” -classpath “%CLASSPATH%” -Djava.security.manager -Djava.security.policy==”%SECURITY_POLICY_FILE%” -Dcatalina.base=”%CATALINA_BASE%” -Dcatalina.home=”%CATALINA_HOME%” -Djava.io.tmpdir=”%CATALINA_TMPDIR%” %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doJpda  
如果SECURITY_POLICY_FILE变量不为空,转到doSecurityJpda位置,为空执行下面命令,并结束该程序  

if not “%SECURITY_POLICY_FILE%” == “” goto doSecurityJpda
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs=”%JAVA_ENDORSED_DIRS%” -classpath “%CLASSPATH%” -Dcatalina.base=”%CATALINA_BASE%” -Dcatalina.home=”%CATALINA_HOME%” -Djava.io.tmpdir=”%CATALINA_TMPDIR%” %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurityJpda  执行下面命令,并结束该程序
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs=”%JAVA_ENDORSED_DIRS%” -classpath “%CLASSPATH%” -Djava.security.manager -Djava.security.policy==”%SECURITY_POLICY_FILE%” -Dcatalina.base=”%CATALINA_BASE%” -Dcatalina.home=”%CATALINA_HOME%” -Djava.io.tmpdir=”%CATALINA_TMPDIR%” %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end

:end

终结:

   catalina.bat咋一看还以为是什么windows程序,可以双击运行,实际上就是调用java命令运行Bootstrap类。从上面代码可以看出tomcat确实是一个纯java的程序,脚本最后都变成直接使用java命令执行程序,与我们普通写的java程序,没有什么不同。只不过由于 tomcat可以使用各种众多的模式(如debug,Security等),以及各种需要各种参数所以不得不使用脚本来执行。

   如果你想看看你到底使用了什么命令可以在”if not “%SECURITY_POLICY_FILE%” == “” goto doSecurity”这行下面添加两行。
       echo  %_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dir ….(下面那段,是一行但太长,折行了)
       pause
   第一行的命令是打印这具话,系统会将% %里面的变量提换成找到的值并输出。第二行是暂停程序,你可以通过任意键来恢复运行。
   下面是我的程序打印的结果:

start “Tomcat” “C:\Program Files\Java\jdk1.6.0_01\bin\java”  -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=”C:\work\tomcat\conf\logging.properties” -Xms128m -Xmx512m -Dfile.encoding=UTF8 -Duser.timezone=GMT -Djava.security.auth.login.config=C:\work\tomcat/conf/jaas.config   -Djava.endorsed.dirs=”C:\work\tomcat\common\endorsed” -classpath “C:\Program Files\Java\jdk1.6.0_01\lib\tools.jar;C:\work\tomcat\bin\bootstrap.jar” -Dcatalina.base=”C:\work\tomcat” -Dcatalina.home=”C:\work\tomcat” -Djava.io.tmpdir=”C:\work\tomcat\temp” org.apache.catalina.startup.Bootstrap  start

    start “tomcat”是另开一个窗口,窗口名是tomcat的意思,你可以去掉这部分,完将你程序打印的拷贝到命令行下,点回车,看是不是一样运行 tomcat了。Catalina.bat startup实际时就将各种系统变量加以总结,输出成这个命令。   呵呵!

08月 20th, 2008 | Leave a Comment

分析 Tomcat startup.bat 启动脚本

说来tomcat的脚本确实不难,启动脚本更是没有几行,以下是我解释的tomcat startup.bat脚本。

rem Guess CATALINA_HOME if not defined             确定该命令(脚本)是否在tomcat目录里

set CURRENT_DIR=%cd%                                          设定 CURRENT_DIR 为当前目录
if not “%CATALINA_HOME%” == “” goto gotHome   如果 CATALINA_HOME 不为空,跳转到 gotHome 位置
set CATALINA_HOME=%CURRENT_DIR%              如果为空,CATALINA_HOME设成 CURRENT_DIR(即当前目录)
if exist “%CATALINA_HOME%\bin\catalina.bat” goto okHome  如果存在catalina.bat, 就去 gotHome
cd ..                            否则返回上级目录  (你可以尝试把startup.bat拷贝到上级目录,它一样可以启动)
set CATALINA_HOME=%cd%            把CATALINA_HOME 重新设为当前目录。(应该是tomcat\)
cd %CURRENT_DIR%                       转到 CURRENT_DIR目录,即 tomcat\bin\下
:gotHome                                               gotHome 位置
if exist “%CATALINA_HOME%\bin\catalina.bat” goto okHome     如果存在catalina.bat,就去 okHome
echo The CATALINA_HOME environment variable is not defined correctly   否则输出下面两行,并终结
echo This environment variable is needed to run this program
goto end
:okHome

set EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat     为EXECUTABLE变量设定值

rem Check that target executable exists         查询catalina.bat 命令是否存在

if exist “%EXECUTABLE%” goto okExec         查看 catalina.bat 是否存在,如果存在,就去okExec位置
echo Cannot find %EXECUTABLE%               否则输出下面两行,并终结
echo This file is needed to run this program
goto end
:okExec

rem Get remaining unshifted command line arguments and save them in the  将命令参数存入变量里
set CMD_LINE_ARGS=
:setArgs                                                       前面两行是注释,这里是setArgs 位置的返回点
if “”%1″”==”"”" goto doneSetArgs            如果没有参数,转到doneSetArgs位置   
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1        如果有参数,将参数放入CMD_LINE_ARGS里,加在CMD_LINE_ARGS变量的后面
shift                                                              把变量%2 里移到%1
goto setArgs                                               返回setArgs位置,进行循环
:doneSetArgs

call “%EXECUTABLE%” start %CMD_LINE_ARGS%       如果一切顺利调用 “%EXECUTABLE%” start 并加上 结尾参数

:end

         从上文的分析中我们可以看见,startup.bat脚本实际上并没有做什么实际的工作,主要是查看当前命令所在目录。看是否在tomcat\或tomcat\bin\目录下。以及查看将调用该命令的参数传递给catalina.bat。最后调用catalina.bat start命令。与tomcat书中说startup.bat与catalina.bat start功能一样,相吻合。即startup.bat的主要作用是调用catalina.bat start命令。

08月 19th, 2008 | Leave a Comment

Hibernate 高级查询技巧——集合过滤与子查询

   连接查询:    关系型数据库之所以强大,其中一个原因就是可以统一使用表来管理同类数据信息,并且可以在相关数据之间建立关系。作为支持关系型数据库的SQL语句来说,自然要对全面发挥这种强大功能提供支持,这个支持就是连接查询。同样作为一种关系型数据库的持久层框架,Hibernate也对连接查询提供了丰富的支持,在Hibernate中通过HQL与QBC两种查询方式都可以支持连接查询。下面这一部分我们将通过这两种查询技术,来详细讨论有关Hibernate对连接查询支持的各个细节。在讲解连接查询之前,我们先来回忆一下在第一部分中讲解的有关实体关联关系的映射,在实体的配置文件中可以通过配置集合元素来指定对关联实体的映射以及检索策略。因此我们可以在实体映射配置文件中,指定关联实体检索策略,对关联实体的检索策略可以指定为”延迟检索”,”立即检索”,”迫切左外连接检索”,如下所示对与Customer实体关联的Order实体设置延迟加载:
<set name=”orders” inverse=”true” lazy=”true”>
这种在实体映射配置文件中设定的检索策略,称为默认检索策略,但是这种默认检索策略是可以被覆盖的,那就是在程序代码当中可以动态指定各种迫切检索策略来覆盖默认检索策略。
  1、    迫切左外连接查询和左外连接查询: 我们看以下代码,这段代码将覆盖映射文件中的检索策略,显示指定采用迫切左外连接查询。  
HQL查询方式:
Query query=session.createQuery(”from Customer c left join fetch c.orders o where c.name       like ‘zhao%’ “);
List list=query.list(); for(int i=0;i<list.size();i++){  Customer customer=(Customer)list.get(i); }
//QBC检索方式: List list=session.createCriteria(Customer.class)
                 .setFetchMode(”orders”,FetchMode.EAGER).add(Expression.like(”name”,”zhao%”,MatchMode.START).list();
for(int i=0;i<list.size();i++){  Customer customer=(Customer)list.get(i); }
我们看到在HQL以及QBC查询中分别通过left join fetch和FetchMode.EAGER来指定采用迫切左外连接检索策略,当采用了迫切左外连接检索策略时,当进行检索时即执行查询的list()方法时,将会立即初始化用来容纳关联实体的集合对象元素,如果在实体映射配置文件中对关联实体设置了延迟加载,那么此时将会忽略延迟加载设置,而采用迫切左外连接策略,并且立即用关联实体对象填充集合对象元素,即使用Order对象填充Customer对象的orders集合。因此这种检索策略会马上创建关联实体对象,此时我想你一定会想到这种检索策略会同时检索出Customer和Order实体对象对应的数据,并且分别创建这两个对象。恭喜你答对了,因此上面代码会生成类似如下的SQL语句: Select * from customer c left join order o on c.id=o.id where c.name like ‘zhao%’;
如果我们忽略了fetch关键字,就变成了左外连接查询,
如下面代码: Query query=session.createQuery(”from Customer c left join c.orders o where c.name       like ‘zhao%’ “);
List list=query.list(); for(int i=0;i<list.size();i++){  Object[] objs=(Object[])list.get(i);
 Customer customer=(Customer) objs[0];  
Order order=(Order)objs[1]; }
我们可以看到采用左外连接查询返回的结果集中包含的是对象数组,对象数组中的每个元素存放了一对相互关联的Customer对象和Order对象,而迫切左外连接会返回Customer对象,与Customer对象相关联的Order对象存放在Customer对象的集合元素对象中,这就是迫切左外连接和左外连接查询的其中一个区别(这两种检索生成的SQL语句是一样的),另一个区别是当使用左外连接时,对关联对象的检索会依照实体映射配置文件所指定的策略,而不会像迫切左外连接那样忽略它,比如此时对Customer对象关联的Order对象采用延迟加载,那么左外连接检索也会使用延迟加载机制检索Order对象。

   2、内连接,迫切内连接以及隐式内连接:  若采用迫切内连接通过一下代码可以实现:
Query query=session.createQuery(”from Customer c inner join fetch c.orders o where c.name       like ‘zhao%’ “);
List list=query.list();
for(int i=0;i<list.size();i++){
 Customer customer=(Customer)list.get(i); }
这段代码将会采用迫切内连接检索,对集合元素的检索策略以及返回结果集中的对象类型都采用与迫切左外连接一样的方式,我这里就不再赘述,另外QBC查询不支持迫切内连接检索。 如果去掉fetch就是内连接检索,如下面代码:
Query query=session.createQuery(”from Customer c innerjoin c.orders o where c.name       like ‘zhao%’ “);
List list=query.list(); for(int i=0;i<list.size();i++){  Object[] objs=(Object[])list.get(i);  
Customer customer=(Customer) objs[0];  Order order=(Order)objs[1]; }
内连接检索,对集合元素的检索策略以及返回结果集中的对象类型都采用与左外连接一样的方式,QBC查询也同样支持内连接检索,如下代码:
List list=session.createCriteria(Customer.class) .add(Expression.like(”name”,”zhao%”,MatchMode.START)) .createCriteria(”orders”) .add(Expression.like(”ordernumber”,”T”,MatchMode.START)).list();  
     上面代码等价于如下的HQL语句:       Select c from Customer c join c.orders o where c.name like ‘zhao%’ and o.ordernummber like ‘T%’;因此可以采用下面的方式访问结果集:
for(int i=0;i<list.size();i++){  Customer customer=(Customer)list.get(i); }   
   由此可见,采用内连接查询时,HQL与QBC查询有不同的默认行为,HQL会检索出成对的Customer和Order对象,而QBC仅会检索出Customer对象。如果QBC查询想检索出成对的Customer和Order对象,
可以采用如下代码: List list=session.createCriteria(Customer.class) .createAlias(”orders”,”o”) .add(Expression.like(”this.name”,”zhao%”,MatchMode.START)) .add(Expression.like(”ordernumber”,”T”,MatchMode.START)) .returnMap() .list(); for(int i=0;i<list.size();i++){    Map map=(Map)list.get(i);  Customer customer=(Customer)map.get(”this”);  Order order=(Order)map.get(”o”); }     “o”和”this”分别是orders集合和Customer对象的别名。
    在HQL查询中,还有一种查询成为隐式内连接,我们看下面的HQL语句,    
From Order o where o.customer.name like ‘ zhao% ‘;这个语句通过o.customer.name访问与Order对象关联的Customer对象的name属性,尽管没有使用join关键字,其实隐式指定了采用内连接检索,
它和下面这条HQL语句等价: From Order o join o.customer c where c.name like ‘zhao%’; 隐式内连接只适用于多对一和一对一关联,不适用于一对多和多对多关联,另外QBC查询不支持隐式内连接检索。

     3、右外连接检索: 由于fetch关键字只能应用于innner join和left join,因此对于右外连接检索而言,就不存在所谓的迫切右外连接查询了,使用右外连接见如下代码: Query query=session.createQuery(”from Customer c right join c.orders o where c.name       like ‘zhao%’ “);
List list=query.list(); for(int i=0;i<list.size();i++){  Object[] objs=(Object[])list.get(i);  Customer customer=(Customer) objs[0];  Order order=(Order)objs[1]; }    
   右外连接检索,对集合元素的检索策略以及返回结果集中的对象类型都采用与左外连接一样的方式。
    4、交叉连接:  对于不存在关联关系的两个实体对象,不能使用内连接查询,也不能使用外连接查询,此时可以使用具有SQL风格的交叉连接,
如下面代码: Select c.ID,c.name,c.age,o.ID,o.ordernumber,o.customer_ID From Customer c,Order o; 这个HQL语句将会执行交叉连接检索,而且将会返回customer表和order表的笛卡儿积关联结果。
    5、连接查询运行时检索策略总结: ①、如果在HQL和QBC查询中没有指定检索策略,那么将会使用映射配置为件中指定的检索策略,但是这里有一个例外,那就是HQL检索总是会忽略实体映射配置文件中对关联实体指定的迫切左外连接检索策略,也就是说如果配置文件中指定对关联实体采用迫切走外连接检索,但是在HQL查询语句中没有指定这种检索策略,此时Hibernate将会忽略这种检索策略,而依然采用立即检索。因此如果希望采用迫切左外连接检索,就必须在HQL语句中明确指定。 ②、如果在HQL或者QBC检索中明确指定了检索策略,就会覆盖配置文件中的默认检索策略,在HQL查询中通过left join fetch和inner join fetch来明确指定检索策略,在QBC查询中通过FetchMode.DEFAULT,FetchMode.EAGER,FetchMode.LAZY来明确指定检索策略。 ①、        
     目前的Hibernate的各种版本中,只允许在一个查询中迫切左外连接检索一个集合,即只允许存在一个一对多关联,但是允许存在多个一对一和多对多关联。

08月 1st, 2008 | 1 Comment

MySQL中的各种JOIN(CROSS JOIN, INNER JOIN, LEFT [OUTER] JOIN)

MySQL中的各种JOIN

1. 笛卡尔积(交叉连接)
在MySQL中可以为CROSS JOIN或者省略CROSS即JOIN,或者使用’,’

SELECT * FROM table1 CROSS JOIN table2
SELECT * FROM table1 JOIN table2
SELECT * FROM table1,table2

由于其返回的结果为被连接的两个数据表的乘积,因此当有WHERE, ON或USING条件的时候一般不建议使用,因为当数据表项目太多的时候,会非常慢。
一般使用LEFT [OUTER] JOIN或者RIGHT [OUTER] JOIN

2. 内连接INNER JOIN
在MySQL中把INNER JOIN叫做等值连接,即需要指定等值连接条件
在MySQL中CROSS和INNER JOIN被划分在一起,不明白。
参看MySQL帮助手册
http://dev.mysql.com/doc/refman/5.0/en/join.html
join_table:
    table_reference [INNER | CROSS] JOIN table_factor [join_condition]

3. MySQL中的外连接,分为左外连接和右连接,
即除了返回符合连接条件的结果之外,还要返回左表(左连接)或者右表(右连接)中不符合连接条件的结果,相对应的使用NULL对应。

a. LEFT [OUTER] JOIN
SELECT column_name FROM table1 LEFT [OUTER] JOIN table2 ON table1.column=table2.column
除了返回符合连接条件的结果之外,还需要显示左表中不符合连接条件的数据列,相对应使用NULL对应

b. RIGHT [OUTER] JOIN
SELECT column_name FROM table1 RIGHT [OUTER] JOIN table2 ON table1.column=table2.column
RIGHT与LEFT JOIN相似不同的仅仅是除了显示符合连接条件的结果之外,还需要显示右表中不符合连接条件的数据列,相应使用NULL对应

——————————————–
添加显示条件WHERE, ON, USING
1. WHERE子句
2. ON
3. USING子句,如果连接的两个表连接条件的两个列具有相同的名字的话可以使用USING
例如
SELECT <column_name> FROM <table1> LEFT JOIN <table2> USING (<column_name>)

连接多余两个表的情况
举例:
mysql> SELECT  artists.Artist, cds.title, genres.genre
    -> FROM cds
    -> LEFT JOIN genres
    -> ON cds.genreID = genres.genreID
    -> LEFT JOIN artists
    -> ON cds.artistID = artists.artistID;
或者
mysql> SELECT artists.Artist, cds.title, genres.genre
    -> FROM cds
    -> LEFT JOIN genres
    -> ON cds.genreID = genres.genreID
    -> LEFT JOIN artists
    -> ON cds.artistID = artists.artistID
    -> WHERE (genres.genre = ‘Pop’);
——————————————–

另外需要注意的地方

在MySQL中涉及到多表查询的时候,需要根据查询的情况,想好使用哪种连接方式效率更高。
1. 交叉连接(笛卡尔积)或者内连接
[INNER | CROSS] JOIN
2. 左外连接LEFT [OUTER] JOIN或者右外连接RIGHT [OUTER] JOIN

注意指定连接条件WHERE, ON,USING.

07月 1st, 2008 | 3 Comments

jquery radio取值,checkbox取值,select取值,radio选中,checkbox选中,select选中,及其相关

获取一组radio被选中项的值
var item = $(’input[@name=items][@checked]‘).val();
获取select被选中项的文本
var item = $(”select[@name=items] option[@selected]“).text();
select下拉框的第二个元素为当前选中值
$(’#select_id’)[0].selectedIndex = 1;
radio单选组的第二个元素为当前选中值
$(’input[@name=items]‘).get(1).checked = true;

获取值:

文本框,文本区域:$(”#txt”).attr(”value”);
多选框checkbox:$(”#checkbox_id”).attr(”value”);
单选组radio:   $(”input[@type=radio][@checked]“).val();
下拉框select: $(’#sel’).val();

控制表单元素:
文本框,文本区域:$(”#txt”).attr(”value”,”);//清空内容
                 $(”#txt”).attr(”value”,’11′);//填充内容

多选框checkbox: $(”#chk1″).attr(”checked”,”);//不打勾
                 $(”#chk2″).attr(”checked”,true);//打勾
                 if($(”#chk1″).attr(’checked’)==undefined) //判断是否已经打勾

单选组radio:    $(”input[@type=radio]“).attr(”checked”,’2′);//设置value=2的项目为当前选中项
下拉框select:   $(”#sel”).attr(”value”,’-sel3′);//设置value=-sel3的项目为当前选中项
                $(”<option value=’1′>1111</option><option value=’2′>2222</option>”).appendTo(”#sel”)//添加下拉框的option
                $(”#sel”).empty();//清空下拉框

06月 22nd, 2008 | 1 Comment

整理一些常用的正则表达式

正则表达式是一种通用的标准,大部分计算机语言都支持正则表达式,包括as3,这里转摘出了一些常用的正则表达式语句,大家用到的时候就不用自己写了

^\d+$  //匹配非负整数(正整数 + 0)
^[0-9]*[1-9][0-9]*$  //匹配正整数
^((-\d+)|(0+))$  //匹配非正整数(负整数 + 0)
^-[0-9]*[1-9][0-9]*$  //匹配负整数
^-?\d+$    //匹配整数
^\d+(\.\d+)?$  //匹配非负浮点数(正浮点数 + 0)
^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$  //匹配正浮点数
^((-\d+(\.\d+)?)|(0+(\.0+)?))$  //匹配非正浮点数(负浮点数 + 0)
^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$  //匹配负浮点数
^(-?\d+)(\.\d+)?$  //匹配浮点数
^[A-Za-z]+$  //匹配由26个英文字母组成的字符串
^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串
^[a-z]+$  //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串
^\w+$  //匹配由数字、26个英文字母或者下划线组成的字符串
^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$    //匹配email地址
^[a-zA-z]+://匹配(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$  //匹配url

匹配中文字符的正则表达式: [\u4e00-\u9fa5]
匹配双字节字符(包括汉字在内):[^\x00-\xff]
匹配空行的正则表达式:\n[\s| ]*\r
匹配HTML标记的正则表达式:/<(.*)>.*<\/>|<(.*) \/>/
匹配首尾空格的正则表达式:(^\s*)|(\s*$)
匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
匹配网址URL的正则表达式:^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
匹配国内电话号码:(\d{3}-|\d{4}-)?(\d{8}|\d{7})?
匹配腾讯QQ号:^[1-9]*[1-9][0-9]*$
下表是元字符及其在正则表达式上下文中的行为的一个完整列表:
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个后向引用、或一个八进制转义符。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的Multiline 属性,^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了 RegExp 对象的Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。
* 匹配前面的子表达式零次或多次。
+ 匹配前面的子表达式一次或多次。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。? 等价于 {0,1}。
{n} n 是一个非负整数,匹配确定的n 次。
{n,} n 是一个非负整数,至少匹配n 次。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。
. 匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用象 ‘[.\n]‘ 的模式。
(pattern) 匹配pattern 并获取这一匹配。
(?:pattern) 匹配pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。
(?=pattern) 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。
(?!pattern) 负向预查,与(?=pattern)作用相反
x|y 匹配 x 或 y。
[xyz] 字符集合。
[^xyz] 负值字符集合。
[a-z] 字符范围,匹配指定范围内的任意字符。
[^a-z] 负值字符范围,匹配任何不在指定范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。
\B 匹配非单词边界。
\cx 匹配由x指明的控制字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
\w 匹配包括下划线的任何单词字符。等价于’[A-Za-z0-9_]‘。
\W 匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]‘。
\xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。
\num 匹配 num,其中num是一个正整数。对所获取的匹配的引用。
\n 标识一个八进制转义值或一个后向引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为后向引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm 标识一个八进制转义值或一个后向引用。如果 \nm 之前至少有is preceded by at least nm 个获取得子表达式,则 nm 为后向引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的后向引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
\nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八

06月 4th, 2008 | 1 Comment

美国高级工程师谈Struts 2走向

     自从2000年Apache Struts出现以来,它在大多数的标准下都运行良好,帮助开发出了许许多多基于Java的Web应用程序。Struts是利用服务器端生成的HTML和客户端验证的Javascript的完美结合,使开发和维护变得更加容易。随着时间的推移,用户对Web应用程序的要求不断增加,Struts 1.0几乎还滞留于原地,给Web开发者留下了越来越多无趣的”衔接”代码,如何才能建立一个完美的框架结合体呢?

Struts的前世今生

     在JavaOne 2005大会上,一些Struts的开发者们与Rich Feit(Apache蜂巢计划的提交者)还有一些Struts的用户共聚一堂,讨论Struts的未来。与会者提出了Struts Ti的项目,它描述了这样一个框架,能够集众家所长,重新组合成一个堪称完美的框架。可是Struts本身的问题出现了,Struts 1.0的代码库不适合大幅度的改进,而它本身的特性设置也相当有限,缺乏了像Ajax的快速和可扩展性。

同样在JavaOne会上,笔者还与Webwork的核心架构师Jason Carreira讨论了关于OpenSymphony WebWork 2的项目,探讨我们如何能更好地协同工作。对于在XWork上进行开发,笔者还是十分感兴趣的,特别是它们核心的命令模式框架,但Jason Carreira建议笔者直接在WebWork 2上进行开发。当我和Rich使用了最初几个Struts Ti的版本后,我们决定采纳Jason的建议。

     我们认为,Struts应该满足高级应用程序的需求,并且在WebWork 2框架中的开发经历也证明了这点,Rich和笔者大多数时间都与一位WebWork 2的高级开发者Patrick Lightbody一起工作,在这段时期内,Patrick和Spring WebFlow项目的创建者Keith Donald从各个角度考虑了关于一个全新的Web框架的构想,希望能将它们结合在一起,也就是将Keith的Spring WebFlow和Ted Husted与笔者的Struts以及Patrick和Jason的WebWork与Rich的Beehive结合在一起,探讨了将这些平台结合到一个框架中的可能性。

     不幸的是,细节方面的困难出现了,Beehive和WebFlow无法将其压缩和转换方面的特性进一步融合,同时还有项目的所有者、商标以及身份等问题,不久,这个团队就解散了。

     我们不想就这样解散,笔者和Ted(Apache软件基金会的成员)开始与Patrick(WebWork 2的高级开发者)和Jason(Webwork核心架构师)探讨如何能让我们更好地合作,Ted产生了Struts与WebWork合并的想法。

由于Struts Ti还是基于WebWork设计的,那么将WebWork的代码转向Struts项目并不是件难事。我们在一月开始了关于WebWork 2的Apache Incubator进程,并完成了WebWork 2代码。

Struts 2的由来

    同时,Struts本身也在为项目的核心识别,进行了激烈的竞争,到底它是不是多重Web框架,Struts包括了Apache Shale,它是一个包含了JSF的Web框架。作为一个Struts的子项目,有着Struts Action 1(现在称之为Struts 1)与Struts Action 2(完成了的WebWork 2代码)的一些特征。不幸的是,这些子项目让开发者们有些混淆不清,他们都用一个单一框架表示”Struts”。

    在尝试将Struts Action 2与Shale的子项目结合到一个单独的Struts 2之后,Shale的开发者意识到,如果这些能成为他们以后工程中的开发框架,也是不错的选择。Struts Action 2很快就更名为简洁的Struts 2。

如今,Apache Struts项目已经有它的框架的两个主要版本,但它仍是一个基于Action的框架项目。并且WebWork仍然在定期发布程序补丁,直到Struts 2达到GA或是最终稳定,但所有新的开发却都是使用Struts 2代码。

由此看来,想要在Struts与WebWork的合并中寻找什么奇迹是不可能的,还是另寻途径更好。但是我们不会放弃当初Struts Ti的构想,为将来做出一个集众家所长的完美框架而努力。

编后:作者在其BLOG中声称,写作本文的目的是为了说清楚Struts 2.0项目的由来,以及为什么它包含了WebWork 2代码。合并的本身就是一件难处理的事,甚至在开源社区也能看到端倪,两个项目之间构成一个好的合并,是非常罕见的,且还需要经过争辩,同时也希望合并的热潮能遍布开源的世界。

05月 29th, 2008 | 1 Comment

第七章 struts2配置文件介绍

一、Struts2配置文件
     Struts2相关的配置文件有web.xml,struts.xml,struts.properties,struts-default.xml,velocity.properties,struts-default.vm。其中web.xml,struts.xml是必须的,其它的配置文件可选择。

它们在web应用中的功能如下:
web.xml:包含所有必须的框架组件的web部署描述符。
Struts.xml:配置包含result/view类型、action映射、拦截器等的Struts2的主要配置文件。
Struts.properties:配置struts2的框架属性。
Struts-default.xml:在文件在struts-action-x.x.jar中,该文件是应该被包含在struts.xml中的缺省配置。
Welocity.properties:重写了velocity的配置文件。
Struts-default.vm:相对于velocity的缺省配置。

二、Struts2配置元素
     Struts2核心的配置文件是缺省的struts.xml。
必要的时候,缺省的配置文件可以包含其它的配置文件;struts文件可以放入jar中,并自动插入应用程序,这样每个模块可以包含自己的配置文件并自动配置。在Freemarker和Velocity模块中,模板也能从classpath中加载,所以整个模块可以作为一个简单的jar文件被包含。
Struts.xml配置文件可以包含Interceptor、Action类和Results。
Struts.xml配置元素说明:

1、Packages
Packages:packages把actions、results、results types、interceptors
和interceptor-stacks组装到一个逻辑单元中,从概念上讲,packages就像一个对象,可以被其它子包从写,而且可以拥有自己独立的部分。
Name属性是packages的必填元素,它作为一个关键字被后边的包引用;extends元素是可选的,它允许包扩展一个和多个前边定义的包。注意,
struts.xml文件是至上而下处理的,所有被扩展的包,需要在扩展包前定义。
Abstract元素是可选的,它可以申明一个不包含actions的配置文件。

2、Namespace
Namespace元素把actions细分到逻辑模块,每一个namespace都有自己的
前缀(prefix),namespace避免了action之间的名字冲突,当前缀出现在URI中时,这些标签都是名字空间感知的(”namespace aware”),所以这些namespace prefix不必嵌入到表单或连接中。
Default的namespace是一个空字符串”",如果在指定的配置文件中,没有找到action,缺省的namespace也会被查找。Local/global策略允许应用程序在action “extends”元素层次结构之外,有全局的action配置。缺省的namespace也可以不在package中申明。
Namespace prefix可以注册为java的申明式安全,以确保授权的用户才能访问namespace的资源。
Root namespace(”/”)也被支持,root就是当直接请求context path的时候的namespace。

[个人理解:namespace的用法就像struts1.x中的path一样,只不过它在package中什么路径,而在action中申明action名子罢了]

3、Include
Include元素使得框架能应用”divide and conquer”来配置文件。被include
的每个配置文件必须和struts.xml有一样的格式,一个大的项目可以采用这样方式来组织程序模块。
Include元素也可以和package交替出现,框架将按照顺序加载配置文件。

4、Interceptor configuration
Interceptor允许应用程序在Action方法执行前后定义执行代码,
Interceptor在应用程序开发中十分重要,对于Interceptor有许多用例,如validation, property population, security, logging, 和profiling。
Interceptor被定义为一个Java类,Interceptor也可以组装成Interceptor-stack,他们将按照定义的顺序执行。
在struts-default.xml中定义了一些缺省的Interceptor-stack,以便框架能很好地运行。

5、Action
Action是框架的”工作单元”。Action可以指定一个Interceptor-stack、
一序列的return type和一序列的异常处理,但只有name属性是必须的。

05月 28th, 2008 | 2 Comments

Powered by WordPress | Blue Weed by Blog Oh! Blog | Entries (RSS) and Comments (RSS)