webber

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  11 Posts :: 2 Stories :: 3 Comments :: 0 Trackbacks

2010年3月26日 #

在Java的世界里,无论类还是各种数据,其结构的处理是整个程序的逻辑以及性能的关键。由于本人接触了一个有关性能与逻辑同时并存的问题,于是就开始研究这方面的问题。找遍了大大小小的论坛,也把《Java 虚拟机规范》,《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》,和《Thinking in Java》翻了也找不到很好的答案,于是一气之下把JDK的 src 解压出来研究,扩然开朗,遂写此文,跟大家分享感受和顺便验证我理解还有没有漏洞。 这里就拿HashMap来研究吧。



  HashMap可谓JDK的一大实用工具,把各个Object映射起来,实现了“键--值”对应的快速存取。但实际里面做了些什么呢?

  在这之前,先介绍一下负载因子和容量的属性。大家都知道其实一个 HashMap 的实际容量就 因子*容量,其默认值是 16×0.75=12; 这个很重要,对效率很一定影响!当存入HashMap的对象超过这个容量时,HashMap 就会重新构造存取表。这就是一个大问题,我后面慢慢介绍,反正,如果你已经知道你大概要存放多少个对象,最好设为该实际容量的能接受的数字。

  两个关键的方法,put和get:

  先有这样一个概念,HashMap是声明了 Map,Cloneable, Serializable 接口,和继承了 AbstractMap 类,里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现,当然还有一个很重要的继承了Map.Entry 的 Entry 内部类,由于大家都有源代码,大家有兴趣可以看看这部分,我主要想说明的是 Entry 内部类。它包含了hash,value,key 和next 这四个属性,很重要。put的源码如下

public Object put(Object key, Object value) {
Object k = maskNull(key);

  这个就是判断键值是否为空,并不很深奥,其实如果为空,它会返回一个static Object 作为键值,这就是为什么HashMap允许空键值的原因。

int hash = hash(k);
int i = indexFor(hash, table.length);

  这连续的两步就是 HashMap 最牛的地方!研究完我都汗颜了,其中 hash 就是通过 key 这个Object的 hashcode 进行 hash,然后通过 indexFor 获得在Object table的索引值。

  table???不要惊讶,其实HashMap也神不到哪里去,它就是用 table 来放的。最牛的就是用 hash 能正确的返回索引。其中的hash算法,我跟JDK的作者 Doug 联系过,他建议我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他这样一提,我就更加急了,可惜口袋空空啊!!!

  不知道大家有没有留意 put 其实是一个有返回的方法,它会把相同键值的 put 覆盖掉并返回旧的值!如下方法彻底说明了 HashMap 的结构,其实就是一个表加上在相应位置的Entry的链表:

for (Entry e = table[i]; e != null; e = e.next) {
 if (e.hash == hash && eq(k, e.key)) {
  Object oldvalue = e.value;
  e.value = value; //把新的值赋予给对应键值。
  e.recordAccess(this); //空方法,留待实现
  return oldvalue; //返回相同键值的对应的旧的值。
 }
}
modCount++; //结构性更改的次数
addEntry(hash, k, value, i); //添加新元素,关键所在!
return null; //没有相同的键值返回
}

  我们把关键的方法拿出来分析:

void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);

  因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它经过indexfor之后的索引一定都为i,这样在new的时候这个Entry的next就会指向这个原本的table[i],再有下一个也如此,形成一个链表,和put的循环对定e.next获得旧的值。到这里,HashMap的结构,大家也十分明白了吧?

if (size++ >= threshold) //这个threshold就是能实际容纳的量
resize(2 * table.length); //超出这个容量就会将Object table重构

  所谓的重构也不神,就是建一个两倍大的table(我在别的论坛上看到有人说是两倍加1,把我骗了),然后再一个个indexfor进去!注意!!这就是效率!!如果你能让你的HashMap不需要重构那么多次,效率会大大提高!

  说到这里也差不多了,get比put简单得多,大家,了解put,get也差不了多少了。对于collections我是认为,它是适合广泛的,当不完全适合特有的,如果大家的程序需要特殊的用途,自己写吧,其实很简单。(作者是这样跟我说的,他还建议我用LinkedHashMap,我看了源码以后发现,LinkHashMap其实就是继承HashMap的,然后override相应的方法,有兴趣的同人,自己looklook)建个 Object table,写相应的算法,就ok啦。

  举个例子吧,像 Vector,list 啊什么的其实都很简单,最多就多了的同步的声明,其实如果要实现像Vector那种,插入,删除不多的,可以用一个Object table来实现,按索引存取,添加等。

  如果插入,删除比较多的,可以建两个Object table,然后每个元素用含有next结构的,一个table,如果要插入到i,但是i已经有元素,用next连起来,然后size++,并在另一个table记录其位置

HashMap用法

 

 

package hashmap;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class HashMap1 {
//初始化
private void init(Map map,String kind)
{
   if(map != null)
   {
    for(int i=1; i<6; i++)
    {
     map.put(String.valueOf(i),kind+i);
    }
   }
}

//结果输出
private void outPut(Map map)
{
   if(map != null)
   {
    Object key    = null;
    Object value = null;
    Iterator iterater = map.keySet().iterator();
    while(iterater.hasNext())
    {
     key = iterater.next();
     value = map.get(key);
     System.out.print(key+": "+value+"\t");
    }
    System.out.println("\n");
   }
}
public static void main(String args[])
{
    HashMap hashmap = new HashMap();
    hashmap.put("x", "1");
    hashmap.put("u", "2");
    hashmap.put("z", "3");
    hashmap.put("h", "4");
    hashmap.put("a", "5");
    hashmap.put("o", "6");
    hashmap.put("g", "7");
    hashmap.put("u", "8");
    hashmap.put("a", "9");
    hashmap.put("n", "10");
    hashmap.put("g", "11");

     Object key    = null;
     Object value = null;
     Iterator iterater = hashmap.keySet().iterator();
     while(iterater.hasNext())
     {
      key = iterater.next();
      value = hashmap.get(key);
      System.out.print(key+": "+value+"\t");
     }
     System.out.println("\n");

}
//声明HashMap对象
private void setHashMap()
{
   HashMap hashMap = new HashMap();
   init(hashMap,"HashMap");
   hashMap.put(null,"键值为空");
   hashMap.put("值为空",null);
   System.out.println("这是HashMap对象的键与值:");
   outPut(hashMap);
}
     //声明Hashtable对象
private void setHashtable(){
   Hashtable hashtable = new Hashtable();
   init(hashtable,"Hashtable");
   //hashtable.put(null,"键值为空"); Hashtable不允许键或值为null;
   //hashtable.put("值为空",null);
   System.out.println("这是Hashtable对象的键与值:");
   outPut(hashtable);
}
     //声明LinkedHashMap对象
private void setLinkedHashMap(){
   LinkedHashMap linkedHashMap = new LinkedHashMap();
   init(linkedHashMap,"LinkedHashMap");
   linkedHashMap.put(null,"键值为空");
   linkedHashMap.put("值为空",null);
   System.out.println("这是LinkedHashMap对象的键与值:");
   outPut(linkedHashMap);
}
     //声明TreeMap对象
private void setTreeMap(){
   TreeMap treeMap = new TreeMap();
   //TreeMap treeMap = new TreeMap(new MySort());//按自定义的方式排序
   init(treeMap,"TreeMap");
   treeMap.put("0", "后插入的值");
   //treeMap.put(null,"键值为空"); TreeMap不允许键或值为null
   //treeMap.put("值为空",null);
   System.out.println("这是TreeMap对象的键与值:");
   outPut(treeMap);
}
// public static void main(String[] args){
//   HashMapDemo tm = new HashMapDemo();
//   tm.setHashMap();
//   tm.setHashtable();
//   tm.setLinkedHashMap();
//   tm.setTreeMap();
//
//   Map hashMap = new HashMap();
//   hashMap.put(null, "键值为null");
//   hashMap.put("值为null", null);
//   System.out.println("新建HashMap对象元素的记录数是:"+hashMap.size());
//   hashMap.remove(null);
//   System.out.println("删除键值为null的HashMap对象元素的记录数是:"+hashMap.size());
// }

posted @ 2010-03-26 17:08 webber 阅读(590) | 评论 (0)编辑 收藏

 

第一种:
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
    Map.Entry entry = (Map.Entry) iter.next();
    Object key = entry.getKey();
    Object val = entry.getValue();
}
效率高,以后一定要使用此种方式!
                                                                                    
第二种:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
    Object key = iter.next();
    Object val = map.get(key);
}
效率低,以后尽量少使用!
HashMap的遍历有两种常用的方法,那就是使用keyset及entryset来进行遍历,但两者的遍历速度是有差别的,下面请看实例:
import java.util.*;
            public class HashMapTest {
            public static void main(String[] args) {
            HashMap< Integer,String> hashmap = new HashMap< Integer,String>();
            for (int i = 0; i <1000; i++ ) {
            hashmap.put(i, "thanks");
            }
            long bs = Calendar.getInstance().getTimeInMillis();
            Iterator iterator = hashmap.keySet().iterator();
            while (iterator.hasNext()) {
            System.out.print(hashmap.get(iterator.next()));
            }
            System.out.println();
            System.out.println(Calendar.getInstance().getTimeInMillis() - bs);
            listHashMap();
            }
            public static void listHashMap() {
            java.util.HashMap< Integer,String> hashmap = new java.util.HashMap< Integer,String>();
            for (int i = 0; i < 1000; i++ ) {
            hashmap.put(i, "thanks");
            }
            long bs = Calendar.getInstance().getTimeInMillis();
            Iterator< Map.Entry< Integer,String>> it = hashmap.entrySet().iterator();
            while (it.hasNext()) {
            Map.Entry< Integer,String> entry = it.next();
            // entry.getKey() 返回与此项对应的键
            // entry.getValue() 返回与此项对应的值
            System.out.print(entry.getValue());
            }
            System.out.println();
            System.out.println(Calendar.getInstance().getTimeInMillis() - bs);
            } 

对于keySet其实是遍历了2次,一次是转为iterator,一次就从hashmap中取出key所对于的value。而entryset只是遍历了第一次,他把key和value都放到了entry中,所以就快了。

注:Hashtable的遍历方法和以上的差不多!

posted @ 2010-03-26 16:57 webber 阅读(222) | 评论 (0)编辑 收藏

2010年3月15日 #

web.xml  
// 这里不需要配置字符过滤,网上有的例子加了,实际上
webwork.properties里设置如下就可以了页面也是GBK
webwork.locale=zh_CN
webwork.i18n.encoding=GBK
---------------------------
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

     <context-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
     </context-param>

<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
   <listener-class>com.atlassian.xwork.ext.ResolverSetupServletContextListener</listener-class>
</listener>
     <!--
     <servlet>
     <servlet-name>context</servlet-name>
              <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
              <load-on-startup>1</load-on-startup>
      </servlet>
      -->
     <servlet>
         <servlet-name>webwork</servlet-name>
         <servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>
         <load-on-startup>3</load-on-startup>
     </servlet>

<servlet>
   <servlet-name>freemarker</servlet-name>
   <servlet-class>com.opensymphony.webwork.views.freemarker.FreemarkerServlet</servlet-class>
   <load-on-startup>10</load-on-startup>
</servlet>

     <servlet-mapping>
         <servlet-name>webwork</servlet-name>
         <url-pattern>*.action</url-pattern>
     </servlet-mapping>

<servlet-mapping>
   <servlet-name>freemarker</servlet-name>
   <url-pattern>*.ftl</url-pattern>
</servlet-mapping>

     <welcome-file-list>
         <welcome-file>index.html</welcome-file>
     </welcome-file-list>

     <taglib>
         <taglib-uri>webwork</taglib-uri>
         <taglib-location>/WEB-INF/webwork.tld</taglib-location>
     </taglib>

</web-app>

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

xwork.xml

==================---------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>

   <include file="webwork-default.xml"/>
     <package name="users" extends="webwork-default"
         externalReferenceResolver="com.atlassian.xwork.ext.SpringServletContextReferenceResolver">

         <interceptors>
             <interceptor name="reference-resolver" class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/>
             <interceptor-stack name="myDefaultWebStack">
                 <interceptor-ref name="defaultStack"/>
                 <interceptor-ref name="reference-resolver"/>
                 <interceptor-ref name="model-driven"/>
        <interceptor-ref name="params"/>
             </interceptor-stack>
         </interceptors>

<default-interceptor-ref name="myDefaultWebStack"/>
         <action name="blogUser" class="com.jsblog.action.BlogUserAction">
    <external-ref name="baseDao">baseDaoTarget</external-ref>      //这里是把applicationContext里配置的DAO 注入action里 action里要有baseDao属性
    <result name="success">/add.htm</result>
   </action>
-------------------------------------------------------------------------

applicationContext.xml

---------------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="no" default-dependency-check="none" default-lazy-init="false">
     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
         <property name="driverClassName">
             <value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
         </property>
         <property name="url">
             <value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=jsblog;SelectMethod=cursor</value>
         </property>
         <property name="username">
             <value>sa</value>
         </property>
         <property name="password">
             <value>jfy</value>
         </property>
     </bean>

     <bean id="sessionFactory"
           class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
         <property name="dataSource">
             <ref local="dataSource"/>
         </property>
         <property name="mappingResources">
             <list>
                 <value>com/jsblog/BlogUserForm.hbm.xml</value>
             </list>
         </property>
         <property name="hibernateProperties">
             <props>
                 <prop key="hibernate.dialect">
                     net.sf.hibernate.dialect.SQLServerDialect
                 </prop>
                 <prop key="hibernate.show_sql">true</prop>
             </props>
         </property>
     </bean>

     <bean id="transactionManager"
           class="org.springframework.orm.hibernate.HibernateTransactionManager">
         <property name="sessionFactory">
             <ref local="sessionFactory"/>
         </property>
     </bean>


     <bean id="baseDaoTarget" class="com.jsblog.dao.BlogUserDao">
         <property name="sessionFactory">
             <ref local="sessionFactory"/>
         </property>
     </bean>


</beans>
---------------------------------------------------------------------------

BlogUserDao.java
---------------------------------------------------------------------------
package com.jsblog.dao;

import org.springframework.orm.hibernate.support.HibernateDaoSupport;
import org.springframework.orm.hibernate.HibernateCallback;
import org.springframework.orm.hibernate.SessionFactoryUtils;
import com.jsblog.BlogUserForm;

import java.io.Serializable;
import java.util.List;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;

public class BlogUserDao extends HibernateDaoSupport implements BaseDao {

     public void insert(BlogUserForm bloguser) {
         getHibernateTemplate().save(bloguser);
     }

}

posted @ 2010-03-15 10:10 webber 阅读(1053) | 评论 (0)编辑 收藏

 

基于webwork spring hibernate 项目的开发
这三者的结合,应该是java web编程最好的模式。

首先说明三者各自负责的职务:

1 hibernate 负责数据库的操作

2 spring 负责真正的业务操作

3 webwork 负责请求转交,并把spring的处理结果返回给用户


以往的开发中,很多人注重MVC模式。的确,这种模式把程序以面向对象的思想分成 了三个部分。但在web开发中,并不能单纯的运用此种模式:web开发的View是固定的(页面),而在引入hibernate后,model这一块也非常简单和清晰。就剩下control了,这是web开发的关键部分,现在流行的做法便是将control细分成两个部分:dispacher(转交器)和business object(处理业务逻辑的对象)。并将后者抽出接口,甚至和model共享接口,一边真正做到对dispacher隐藏逻辑实现。

而这种M-V-D-B(model-view-dispacher-business object)模式的实现有好多方式。比如一个bo(business object)对象的创建,你可以直接 new,也可以动态加载,采用工厂方法,抽象工厂。但最好的就是用spring容器。dispacher只管用接口就行了,具体类已经有spring的 AOP给注入了。

当然spring也可以很好地和hibernate结合,你可以采用DAO,也可以用spring的hibernate 模板。但这都不重要,因为你的业务对象已经和调用层彻底分开了,当业务层需要和hibernate打交道的时候,直接做个HibernateUtil也未尝不可呀。怎么做都已经不是关键。

下面就具体介绍spring webwork的结合方式。

在webwork 中的wiki doc中有三种结合方式(google查),我这里采用的最后一种--采用一个自动装配的拦截器com.opensymphony.xwork.spring.interceptor.ActionAutowiringInterceptor关键代码如下:


   ApplicationContext applicationContext = (ApplicationContext)ActionContext.getContext().getApplication().get(
          WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        factory = new SpringObjectFactory();
        factory.setApplicationContext(getApplicationContext());
    Action bean = invocation.getAction();
    factory.autoWireBean(bean);
   
    ActionContext.getContext().put(APPLICATION_CONTEXT, context);

1、webwork、spring的集成
  (1)、开启spring的集成:
         首先将最新的spring的jar加到classpath中,然后在src目录下建立webwork.properties文件,文件只包含下面的内容
        webwork.objectFactory=spring
         这种情况下,所有的对象都至少会试图使用Spring来创建.如果它们不能被Spring创建,然后WebWork会自己创建对象.接下来,在
        web.xml打开Spring的Listener
           <listener>
             <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
           </listener>
        由于使用标准的Listener来集成Spring,它可以被配置来支持除了applicationContext.xml之外的配置文件.把下面的几行添加到
       web.xml会让Spring的ApplicationContext从所有匹配给定的规则的文件中初始化:

       <!-- Context Configuration locations for Spring XML files -->
        <context-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>/WEB-INF/applicationContext-*.xml,classpath*:applicationContext   
       </context-param>
       根据需要配置相应的spring上下文文。
   (2)、在spring中初始化Action
         正常情况下,在xwork.xml里可以为每个action指定类.当你使用SpringObjectFactory时WebWork会请求Spring来
         创建action并按照缺省指定的自动装配行为来装配依赖的组件.SpringObjectFactory 也会设置所有的bean的后置处理程序
        (post processors)来完成类似对Action进行事务,安全等等方面的代理的事情.Spring可以不依赖外在的配置来自动确定.
         对于大多数的使用,这就是全部需要的了,用来配置action,设置它们获取服务和依赖组件.
         强烈推荐使用一种声明式的方法来让Spring知道为action提供什么.这包括让bean能够自动装配,无论是把Action里的
         依赖的属性命名为和Spring应该提供的Bean的名字一致(这允许基于名字的自动装配),或者使用by type方式的自动装配,也就是在注册到
        Spring的Bean中需要的类型仅拥有一个.也可以包括使用JDK5的标准来声明事务和安全需求,而不是必须在你的Spring配置里明确设置代理.
         如果能找到方法让Spring在没有任何明确的配置(在_applicationContext.xml_中)的情况下知道需要为action做什么,那么就不
         需要在两个地方维护这个配置了.
         当然,有时候可能想要Spring完全来管理bean.这是有实际意义的,例如,如果想要为bean设置更复杂的AOP或者Spring相关的技术,
         例如Acegi.为了达到这个目的,所有必须要做的事情就是在Spring的 applicationContext.xml 里配置bean,然后在 xwork.xml
         里改变你的WebWork action的类属性来使用在Spring里面定义的bean的名字,而不再使用类名. 
        xwork.xml文件也会改变action类的属性,最后留下的就像这样    
         
        <xwork>
       <!-- Include webwork defaults (from WebWork JAR). -->
         <include file="webwork-default.xml" />

       <!-- Configuration for the default package. -->
         <package name="default" extends="webwork-default">
           <action name="register" class="userAction" method="register">
              <result name="success">/pages/registerSuccess.jsp</result>
           </action>
         </package>
       </xwork>
        在applicationContext.xml 里定义了一个名字为 "userAction"的Spring的bean.注意cn.com.nawang.Action.UserAction不需要
        改变,因为它可能是自动装配的:
        <bean id="userAction" class="cn.com.nawang.action.UserAction" > 
             <property name="userService" ref="userService"/>
        </bean>
       注:bean中的id值必须与xwork.xml中对应的class值一致。
        
   2、 基于Hibernate3的原生API实现DAO
        Hibernate 3.0.1引入了一个新的特性:“带上下文环境的Session”。 这一特性使得Hibernate自身具备了每个事务绑定当前 Session 对象的功能。
         这与Spring中每个Hibernate的 Session 与事务同步的功能大致相同。
   (1)、 为Dao创建基类BaseDao
         public class BaseDao {
         private SessionFactory  sessionFactory;

         public void setSessionFactory(SessionFactory sessionFactory) {
          this.sessionFactory = sessionFactory;
         }
 
         public Session getSession(){
                 Session session = this.sessionFactory.getCurrentSession();
                 return session;
         }
         }
   (2)、在子类Dao中实现具体持久化操作
        public class UserDao extends BaseDao implements IUserDao {
        public void saveUser(User user) throws HibernateException {
              getSession().save(user);
        }     
        }
   (3)、在上下文中配置
        <bean id="baseDao" class="cn.com.nawang.dao.BaseDao">
           <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
    
        <bean id="userDao" class="cn.com.nawang.dao.impl.UserDao" parent="baseDao"/>
       
        <bean id="userService" class="cn.com.nawang.service.impl.UserService">
           <property name="userDao" ref="userDao"/>
        </bean>
   
        <bean id="userAction" class="cn.com.nawang.action.UserAction" > 
          <property name="userService" ref="userService"/>
        </bean>    
     重启服务,在web页面上触发register的action,执行后,抛出下面的异常:
      Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    google了下,大概明白了是因为没有配置了事务导致的错误。在配置事务之前,查看了以前的一个采用HibernateDaoSupport实现的项目,记得
     当时并不需要配置事务就可以正常运行。于是,让UserDao继承于HibernateDaoSupport,修改后的代码如下:
         public class UserDao extends BaseDao implements IUserDao {
        public void saveUser(User user) throws HibernateException {
              getHibernateTemplate().save(user);
        }     
        }
     接下去,修改spring上下文中的相关配置,
       <!--
        <bean id="baseDao" class="cn.com.nawang.dao.BaseDao">
           <property name="sessionFactory" ref="sessionFactory"/>
        </bean>-->
    
        <bean id="userDao" class="cn.com.nawang.dao.impl.UserDao">
           <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
       
        <bean id="userService" class="cn.com.nawang.service.impl.UserService">
           <property name="userDao" ref="userDao"/>
        </bean>
   
        <bean id="userAction" class="cn.com.nawang.action.UserAction" > 
          <property name="userService" ref="userService"/>
        </bean> 
     保存修改后的,重启服务,再次触发register的action,用户信息成功保存。
    
     去掉HibernateDaoSupport的dao实现后,又换回基于hibernate3.0原生API的实现方式,根据之前google后的结果,给userService配置
     事务,拷贝了下之前项目中的配置,并做相应修改,修改后的内容如下:
       <bean id="baseTransaction"
       class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
        <property name="transactionManager" ref="transactionManager"/>
        <property name="proxyTargetClass" value="true"/>
        <property name="transactionAttributes">
           <props>                
               <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
               <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
               <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>                
               <prop key="save*">PROPAGATION_REQUIRED</prop>                
               <prop key="add*">PROPAGATION_REQUIRED</prop>                
               <prop key="update*">PROPAGATION_REQUIRED</prop>                
               <prop key="delete*">PROPAGATION_REQUIRED</prop>            
           </props>       
        </property>  
    </bean>
   
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>      
   
    <bean id="baseDao" class="cn.com.nawang.dao.BaseDao">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    
    <bean id="userDao" class="cn.com.nawang.dao.impl.UserDao" parent="baseDao"/>
   
    <bean id="userServiceTarget" class="cn.com.nawang.service.impl.UserService">
       <property name="userDao" ref="userDao"/>
    </bean>
   
    <bean id="userService" parent="baseTransaction">
       <property name="target" ref="userServiceTarget"/>
    </bean>
   
    <bean id="userAction" class="cn.com.nawang.action.UserAction" > 
       <property name="userService" ref="userService"/>
    </bean>
   
    保存修改内容,重启服务,重启中出现错误,查看了spring in action中的相关配置,发现baseTransaction这个bean的配置稍有不同,
    上面那个配置是参考springside的,当时那个项目赶,就直接拿过来用,也没出现问题,就不认真去考虑,现在拷贝到现有项目中,却出错了,
    于是先根据书上的介绍做相应修改,改后的内容如下:
       <bean id="baseTransaction"
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true">
        <property name="transactionManager" ref="transactionManager"/>
        <property name="transactionAttributes">
           <props>                
               <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
               <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
               <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>                
               <prop key="save*">PROPAGATION_REQUIRED</prop>                              
               <prop key="update*">PROPAGATION_REQUIRED</prop>                
               <prop key="delete*">PROPAGATION_REQUIRED</prop>            
           </props>       
        </property>  
    </bean>
    去掉了<property name="proxyTargetClass" value="true"/>的配置,将abstract="true"改为lazy-init="true",保存修改
    重启服务,并再次触发register的action,一切如所愿。 

posted @ 2010-03-15 10:00 webber 阅读(713) | 评论 (0)编辑 收藏

2010年2月11日 #

虽然大多数web文档的顶部都有doctype声明,但很多人都没有注意它。它是在你新建一个文档时,由web创作软件草率处理的众多细节之一。

虽然doctype被许多人忽视,但在遵循标准的任何web文档中,它都是一项必需的元素。doctype会影响代码验证,并决定了浏览器最终如何显示你的web文档。


doctype的作用
doctype声明指出阅读程序应该用什么规则集来解释文档中的标记。在web文档的情况下,“阅读程序”通常是浏览器或者校验器这样的一个程序,“规则”则是w3c所发布的一个文档类型定义(dtd)中包含的规则。

每个dtd都包括一系列标记、attributes和properties,它们用于标记web文档的内容;此外还包括一些规则,它们规定了哪些标记能出现在其他哪些标记中。每个web建议标准(比如html 4 frameset和xhtml 1.0 transitional)都有自己的dtd。

假如文档中的标记不遵循doctype声明所指定的dtd,这个文档除了不能通过代码校验之外,还有可能无法在浏览器中正确显示。对于标记不一致的问题,浏览器相较于校验器来说更宽容。但是,不正确的doctype声明经常导致网页不正确显示,或者导致它们根本不能显示。


选择正确的doctype
为了获得正确的doctype声明,关键就是让dtd与文档所遵循的标准对应。例如,假定文档遵循的是xhtml 1.0 strict标准,文档的doctype声明就应该引用相应的dtd。另一方面,如果doctype声明指定的是xhtml dtd,但文档包含的是旧式风格的html标记,就是不恰当的;类似地,如果doctype声明指定的是html dtd,但文档包含的是xhtml 1.0 strict标记,同样是不恰当的。

有的时候,也可以根本不使用一个doctype声明。如果没有指定有效的doctype声明,大多数浏览器都会使用一个内建的默认dtd。在这种情况下,浏览器会用内建的dtd来试着显示你所指定的标记。对于一些临时性的、匆忙拼凑的文档(这种文档有许多),你确实可以考虑省略doctype声明,并接受浏览器的默认显示。

完全可以从头编写一个doctype声明,并让它指向自己选择的一个dtd。然而,由于大多数web文档都需要遵循由w3c发布的某个国际公认的web标准,所以那些文档通常都要包含以下标准doctype声明之一:

html 2:

<!doctype html public "-/ietf/dtd html 2.0/en">

html 3.2:

<!doctype html public "-/w3c/dtd html 3.2 final/en">

html 4.01 strict:

<!doctype html public "-/w3c/dtd html 4.01/en"
"http://www.w3.org/tr/html4/strict.dtd">

html 4.01 transitional:

<!doctype html public "-/w3c/dtd html 4.01 transitional/en"
"http://www.w3.org/tr/html4/loose.dtd">

html 4.01 frameset:

<!doctype html public "-/w3c/dtd html 4.01 frameset/en"
"http://www.w3.org/tr/html4/frameset.dtd">

xhtml 1.0 strict:

<!doctype html public "-/w3c/dtd xhtml 1.0 strict/en"
"http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd">

xhtml 1.0 transitional:

<!doctype html public "-/w3c/dtd xhtml 1.0 transitional/en"
"http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd">

xhtml 1.0 frameset:

<!doctype html public "-/w3c/dtd xhtml 1.0 frameset/en"
"http://www.w3.org/tr/xhtml1/dtd/xhtml1-frameset.dtd">

xhtml 1.1:

<!doctype html public "-/w3c/dtd xhtml 1.1/en"
"http://www.w3.org/tr/xhtml11/dtd/xhtml11.dtd">

xhtml 1.1 plus mathml plus svg:

<!doctype html public
"-/w3c/dtd xhtml 1.1 plus mathml 2.0 plus svg 1.1/en"
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">

除了上面列出的doctype声明,具有特殊要求的一些文档还使用了其他几种声明。

doctype声明通常是文档的第一行,要在<html>标记以及其他文档内容之前。注意,在xhtml文档中,doctype的前面偶尔会出现一条xml处理指令(也称为xml prolog):

<@xml version="1.0" encoding="utf-8"@>

为了确保网页正确显示和顺利通过验证,使用正确的doctype是关键。与内容相反的、不正确的或者形式错误的doctype是大量问题的罪魁祸首。在未来的专栏文章中,我还会具体解释如何诊断及纠正这些问题。

用dw设计网页时,新建一个文件,看代码最前面总要出现一个下面的东东,
<!doctype html public "-/w3c/dtd html 4.01 transitional/en"
"http://www.w3.org/tr/html4/loose.dtd">
这个是dw自动在网页文件页增加了dtd信息.可以删.
删除后,浏览器会使用的默认dtd.
posted @ 2010-02-11 10:49 webber 阅读(315) | 评论 (0)编辑 收藏

2010年2月10日 #

webwork2.0配置详解

首先下载WebWork2 的最新版本(http://www.opensymphony.com/webwork/)。

这里我们所谈及的WebWork,实际上是Webwork+XWork的总集,Webwork1.x 版本中,
整个框架采用了紧耦合的设计(类似Struts),而2.0 之后,Webwork被拆分为两个部分,
即Webwork 2.x +XWork 1.x,设计上的改良带来了系统灵活性上的极大提升。

本例的部署如下图所示

webwork能为我们做什么:
1.将Web页面中的输入元素封装为一个(请求)数据对象。
2.根据请求的不同,调度相应的逻辑处理单元,并将(请求)数据对象作为参数传入。
3.逻辑处理单元完成运算后,返回一个结果数据对象。
4.将结果数据对象中的数据与预先设计的表现层相融合并展现给用户。
首先来看登录界面:
index.jsp
<html>
<body>
<form action="/login.action">
<p align="center">
登录<br> </p>
用户名:<input type="text" name="model.username" /><br>
密 码 :<input type="password" name="model.password" /><br>
<p align="center"><input type="submit" value="提交" name="B1"/><input type="reset" value="重置" name="B2"/></p>
</form>
</body>
</html>
 这里的index.jsp实际上是由纯html 组成,非常简单,其中包含一个表单:
<form action="/login.action">
这表明其提交对象为/login.action . 表单中同时包含两个文本输入框,
<input type="text" name="model.username" />
<input type="password" name="model.password" />
可以看到,两个输入框的名称均以“model”开头,这是因为在这里我们采用了WebWork
中Model-Driven的Action驱动模式
当表单被提交之时,浏览器会以两个文本框的值作为参数,向Web 请求以/login.action命名的服务。
标准HTTP协议中并没有.action结尾的服务资源。我们需要在web.xml中加以设定:
……
<servlet>
<servlet-name>webwork</servlet-name>
<servlet-class>
com.opensymphony.webwork.dispatcher.ServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>webwork</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>

 此后,所有以.action结尾的服务请求将由ServletDispatcher 接管。
ServletDispatcher 接受到Servlet Container 传递过来的请求,将进行一下几个动作:
1. 从请求的服务名(/login.action)中解析出对应的Action名称(login)
2. 遍历 HttpServletRequest、HttpSession、ServletContext 中的数据,并将其复制到
Webwork的Map实现中,至此之后,所有数据操作均在此Map结构中进行,从而将内部结构与Servlet API相分离。
至此,Webwork 的工作阶段结束,数据将传递给XWork 进行下一步处理。从这里也可以看
到Webwork和xwork之间的切分点,Webwork为xwork提供了一个面向Servlet 的协议转换
器,将Servlet 相关的数据转构转换成xwork所需要的通用数据格式,而xwork将完成实际的
服务调度和功能实现。
这样一来,以xwork为核心,只需替换外围的协议转换组件,即可实现不同技术平台之间的
切换(如将面向Servlet的Webwork替换为面向JMS的协议转换器实现,即可在保留应用逻
辑实现的情况下,实现不同外部技术平台之间的移植)。
3. 以上述信息作为参数,调用ActionProxyFactory创建对应的ActionProxy实例。
ActionProxyFactory 将根据Xwork 配置文件(xwork.xml)中的设定,创建
ActionProxy实例,ActionProxy中包含了Action的配置信息(包括Action名称,
对应实现类等等)。
4. ActionProxy创建对应的Action实例,并根据配置进行一系列的处理程序。包括
执行相应的预处理程序(如通过Interceptor 将Map 中的请求数据转换为Action
所需要的Java 输入数据对象等),以及对Action 运行结果进行后处理。
ActionInvocation 是这一过程的调度者。而com.opensymphony.xwork.
DefaultActionInvocation 则是XWork 中对ActionInvocation 接口的标准实现,如
果有精力可以对此类进行仔细研读,掌握了这里面的玄机,相信XWork的引擎
就不再神秘。

下面我们来看配置文件:
xwork.xml:
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-1.0.dtd">
<xwork>
<include file="webwork-default.xml" />

<package name="default" extends="webwork-default">

<action name="login" class="net.xiaxin.webwork.action.LoginAction">
<result name="success" type="dispatcher">

<param name="location">/main.jsp</param>
</result>
<result name="loginfail" type="dispatcher">

<param name="location">/index.jsp</param>
</result>
<interceptor-ref name="params" />
<interceptor-ref name="model-driven"/>
</action>
</package>
</xwork>
⑴ include
通过include 节点,我们可以将其他配置文件导入到默认配置文件xwork.xml 中。
从而实现良好的配置划分。
这里我们导入了Webwork 提供的默认配置webwork-default.xml(位于
webwork.jar 的根路径)。
⑵ package
XWork中,可以通过package对action进行分组。类似Java 中package和class的
关系。为可能出现的同名Action提供了命名空间上的隔离。
同时,package还支持继承关系。在这里的定义中,我们可以看到:
extends="webwork-default"
"webwork-default"是webwork-default.xml文件中定义的package,这里通
过继承,"default" package 自动拥有"webwork-default" package 中的所有
定义关系。
这个特性为我们的配置带来了极大便利。在实际开发过程中,我们可以根据自身
的应用特点,定义相应的package模板,并在各个项目中加以重用,无需再在重复
繁琐的配置过程中消耗精力和时间。
此外,我们还可以在Package节点中指定namespace,将我们的action分为若干个
逻辑区间。如:
<package name="default" namespace="/user"
extends="webwork-default">
就将此package中的action定义划归为/user 区间,之后在页面调用action的时候,
我们需要用/user/login.action 作为form action 的属性值。其中的/user/就指定了此
action的namespace,通过这样的机制,我们可以将系统内的action进行逻辑分类,
从而使得各模块之间的划分更加清晰。
⑶ action
Action配置节点,这里可以设定Action的名称和对应实现类。
⑷ result
通过result 节点,可以定义Action 返回语义,即根据返回值,决定处理模式以及
响应界面。
这里,返回值"success"(Action 调用返回值为String 类型)对应的处理模式为
"dispatcher"。
可选的处理模式还有:
1. dispatcher
本系统页面间转向。类似forward。
2. redirect
浏览器跳转。可转向其他系统页面。
3. chain
将处理结果转交给另外一个Action处理,以实现Action的链式处理。
4. velocity
将指定的velocity模板作为结果呈现界面。
5. xslt
将指定的XSLT 作为结果呈现界面。
随后的param节点则设定了相匹配的资源名称。
⑷ interceptor-ref
设定了施加于此Action的拦截器(interceptor)。关于拦截器,请参见稍后的“XWork
拦截器体系”部分。
interceptor-ref定义的是一个拦截器的应用,具体的拦截器设定,实际上是继
承于webwork-default package,我们可以在webwork-default.xml 中找到
对应的"params"和"model-driven"拦截器设置:
<interceptors>
……
<interceptor name="params"
class="com.opensymphony.xwork.interceptor.ParametersInt
erceptor" />
<interceptor name="model-driven"
class="com.opensymphony.xwork.interceptor.ModelDrivenIn
terceptor" />
……
</interceptors>
"params"大概是Webwork 中最重要、也最常用的一个Interceptor。上面曾经将
MVC工作流程划分为几个步骤,其中的第一步:
“将 Web 页面中的输入元素封装为一个(请求)数据对象”
就是通过"params"拦截器完成。Interceptor 将在Action 之前被调用,因而,
Interceptor 也成为将Webwork传来的MAP 格式的数据转换为强类型Java 对象的

最佳实现场所。
"model-driven"则是针对Action 的Model驱动模式的interceptor 实现。具体描
述请参见稍后的“Action驱动模式”部分
很可能我们的Action 都需要对这两个interceptor 进行引用。我们可以定义一个
interceptor-stack,将其作为一个interceptor 组合在所有Action 中引用。如,上面
的配置文件可修改为:
<xwork>
<include file="webwork-default.xml" />
<package name="default" extends="webwork-default">
<interceptors>
<interceptor-stack name="modelParamsStack">
<interceptor-ref name="params" />
<interceptor-ref name="model-driven" />
</interceptor-stack>
</interceptors>
<action name="login"
class="net.xiaxin.webwork.action.LoginAction">
<result name="success" type="dispatcher">
<param name="location">/main.jsp</param>
</result>
<result name="loginfail" type="dispatcher">
<param name="location">/index.jsp</param>
</result>
<interceptor-ref name="modelParamsStack" />
</action>
</package>
</xwork>
通过引入interceptor-stack,我们可以减少interceptor 的重复申明。
下面是我们的Model对象:
LoginInfo.java:
public class LoginInfo {
private String password;
private String username;
private List messages = new ArrayList();
private String errorMessage;

public List getMessages() {
return messages;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
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;
}
}
很简单,这只是一个纯粹的值对象(Value-Object)。这里,它扮演着模型(Model)的
角色,并与Action的输入输出密切相关。
与 SpringMVC中的Command对象不同,Webwork 中的Model对象,扮演着承上启下
的角色,它既是Action的输入参数,又包含了Action处理的结果数据。
换句话说,输入的Http请求参数,将被存储在Model对象传递给Action进行处理,Action
处理完毕之后,也将结果数据放置到Model 对象中,之后,Model 对象与返回界面融合生
成最后的反馈页面。
也正由于此,笔者建议在实际开发中采用Model-Driven 模式,而非Property-Driven 模
式(见稍后“Action驱动模式”部分),这将使得业务逻辑更加清晰可读。
对应的Action代码
public class LoginAction implements Action, ModelDriven {
private final static String LOGIN_FAIL="loginfail";
LoginInfo loginInfo = new LoginInfo();
public String execute() throws Exception {
if ("erica".equalsIgnoreCase(loginInfo.getUsername())
&& "mypass".equals(loginInfo.getPassword())) {
//将当前登录的用户名保存到Session
ActionContext ctx = ActionContext.getContext();
Map session = ctx.getSession();
session.put("username",loginInfo.getUsername());
//出于演示目的,通过硬编码增加通知消息以供显示
loginInfo.getMessages().add("message1");
loginInfo.getMessages().add("message2");
loginInfo.getMessages().add("message3");
return SUCCESS;
}else{
loginInfo.setErrorMessage("Username/Password Error!");
return LOGIN_FAIL;
}
}
public Object getModel() {
return loginInfo;
}
}

 

 LoginAction实现了两个接口:
1. Action
Action接口非常简单,它指定了Action的入口方法(execute),并定义了
几个默认的返回值常量:
public interface Action extends Serializable {
public static final String SUCCESS = "success";
public static final String NONE = "none";
public static final String ERROR = "error";
public static final String INPUT = "input";
public static final String LOGIN = "login";
public String execute() throws Exception;
}

SUCCESS、NONE、ERROR、INPUT、LOGIN 几个字符串常量定义了常用的
几类返回值。我们可以在Action 实现中定义自己的返回类型,如本例中的
LOGIN_FAIL定义。
而execute方法,则是Action的入口方法,XWork将调用每个Action的execute
方法以完成业务逻辑处理。
2. ModelDriven
ModelDriven接口更为简洁:
public interface ModelDriven {
Object getModel();
}
ModelDriven仅仅定义了一个getModel方法。XWork在调度Action时,将通
过此方法获取Model 对象实例,并根据请求参数为其设定属性值。而此后的
页面返回过程中,XWork 也将调用此方法获取Model 对象实例并将其与设定
的返回界面相融合。
注意这里与Spring MVC 不同,Spring MVC 会自动为逻辑处理单元创建
Command Class实例,但Webwork不会自动为Action创建Model对象实例,
Model 对象实例的创建需要我们在Action 代码中完成(如LoginAction 中
LoginInfo对象实例的创建)。
另外,如代码注释中所描述,登录成功之后,我们随即将username保存在Session之中,
这也是大多数登录操作必不可少的一个操作过程。
这里面牵涉到了Webwork中的一个重要组成部分:ActionContext。
ActionContext为Action提供了与容器交互的途径。对于Web 应用而言,与容器的交互
大多集中在Session、Parameter,通过ActionContext我们在代码中实现与Servlet API无关的
容器交互。
如上面代码中的:
ActionContext ctx = ActionContext.getContext();
Map session = ctx.getSession();
session.put("username",loginInfo.getUsername());
同样,我们也可以操作Parameter:
ActionContext ctx = ActionContext.getContext();
Map params = ctx.getParameters();
String username = ctx.getParameters("username");
上述的操作,将由XWork根据当前环境,调用容器相关的访问组件(Web 应用对应的
就是Webwork)完成。上面的ActionContext.getSession(),XWork 实际上将通过Webwork
提供的容器访问代码“HttpServletRequest.getSession()”完成。
注意到,ActionContext.getSession返回的是一个Map类型的数据对象,而非HttpSession。
这是由于WebWork对HttpSession进行了转换,使其转变为与Servlet API无关的Map对象。
通过这样的方式,保证了Xwork 所面向的是一个通用的开放结构。从而使得逻辑层与表现
层无关。增加了代码重用的可能。
此 外, 为了提供与Web 容器直接交互的可能。WebWork 还提供了一个
ServletActionContext实现。它扩展了ActionContext,提供了直接面向Servlet API的容器访
问机制。
我们可以直接通过ServletActionContext.getRequest 得到当前HttpServletRequest 对象的
引用,从而直接与Web 容器交互。
获得如此灵活性的代价就是,我们的代码从此与ServletAPI 紧密耦合,之后系统在不
同平台之间移植就将面临更多的挑战(同时单元测试也难于进行)。
平台移植的需求并不是每个应用都具备。大部分系统在设计阶段就已经确定其运行平
台,且无太多变更的可能。不过,如果条件允许,尽量通过ActionContext 与容器交互,而
不是平台相关的ServletActionContext,这样在顺利实现功能的同时,也获得了平台迁移上
的潜在优势,何乐而不为。
登录成功界面:
main.jsp:
<%@ taglib prefix="ww" uri="webwork"%>
<html>
<body>
<p align="center">Login Success!</p>
<p align="center">Welcome!
<ww:property value="#session['username']"/>
</p>
<p align="center">
<b>Messages:</b><br>
<ww:iterator value="messages" status="index">
<ww:if test="#index.odd == true">
!<ww:property/><br>
</ww:if>
<ww:else>
*<ww:property/><br>
</ww:else>
</ww:iterator>
</p>
</body>
</html>
这里我们引入了Webwork的taglib,如页面代码第一行的申明语句。
下面主要使用了三个tag:
 <ww:property value="#session['username']"/>
读取Model对象的属性填充到当前位置。
value指定了需要读取的Model对象的属性名。
这里我们引用了LoginAction在session中保存的’username’对象。
由于对应的Model(LoginInfo)中也保存了username 属性。下面的语句与之
效果相同:
<ww:property value="username"/>
与 JSP2中的EL类似,对于级联对象,这里我们也可以通过“.”操作符获得
其属性值,如value="user.username"将得到Model 对象中所引用的user
对象的username 属性(假设LoginInfo中包含一个User 对象,并拥有一个名
为“username”的属性)。
关于EL的内容比较简单,本文就不再单独开辟章节进行探讨。
Webwork中包括以下几种特殊的EL表达式:
parameter[‘username’] 相当于request.getParameter(“username”);
request[‘username’] 相当于request.getAttribute(“username”);
session[‘username’] 从session中取出以“username”为key的值
application[‘username’] 从ServletContext中取出以“username”为key
的值
注意需要用“#”操作符引用这些特殊表达式。
另外对于常量,需要用单引号包围,如#session['username'] 中的
'username'。
 <ww:iterator value="messages" status="index">
迭代器。用于对java.util.Collection、java.util.Iterator、java.util.Enumeration,、
java.util.Map、Array类型的数据集进行循环处理。
其中,value属性的语义与<ww:property>中一致。
而 status属性则指定了循环中的索引变量,在循环中,它将自动递增。
而在下面的<ww:if>中,我们通过“#”操作符引用这个索引变量的值。
索引变量提供了以下几个常用判定方法:
first 当前是否为首次迭代
last 当前是否为最后一次迭代
odd 当前迭代次数是否奇数
even 当前迭代次数是否偶数
<ww:if test="#index.odd == true">和<ww:else>
用于条件判定。
test属性指定了判定表达式。表达式中可通过“#”操作符对变量进行引用。
表达式的编写语法与java 表达式类似。
类似的,还有<ww:elseif test="……">。
登录失败界面
实际上,这个界面即登录界面index.jsp。只是由于之前出于避免干扰的考虑,隐藏了
index.jsp中显示错误信息的部分。
完整的index.jsp如下:
<%@ page pageEncoding="gb2312"
contentType="text/html;charset=gb2312"%>
<%@ taglib prefix="ww" uri="webwork"%>
<html>
<body>
<form action="/login.action">
<p align="center">
登录<br>
<ww:if test="errorMessage != null">
<font color="red">
<ww:property value="errorMessage"/>
</font>
</ww:if>
</p>
用户名:
<input type="text" name="model.username" />
<br>
密 码 :
<input type="password" name="model.password" />
<br>
<p align="center">
<input type="submit" value="提交" name="B1"/>
<input type="reset" value="重置" name="B2"/>
</p>
</form>
</body>
</html>
这里首先我们进行判断,如果Model中的errorMessage不为null,则显示错误信息。这
样,在用户第一次登录时,由于Model对象尚未创建,errorMessage自然为null,错误信息
不会显示,即得到了与之前的index.jsp同样的效果。
posted @ 2010-02-10 16:44 webber 阅读(1558) | 评论 (0)编辑 收藏

     摘要:   3.2.2  拦截器介绍 拦截器可以实现横切(crosscutting)功能并使这些实现相对action甚至Struts 2框架保持独立。这样可以使核心框架代码比以前更加简洁,且使开发人员更快捷地使用新框架的特性。实际上,松耦合机制意味着用户再也不用等着框架来实现他们所需的新特性;相反,它们可以实现和使用自己所需的特性且不用修改框架的底层源代码。 使用拦截器可以达到如...  阅读全文
posted @ 2010-02-10 16:36 webber 阅读(2430) | 评论 (0)编辑 收藏

document.form.action,表单分向提交,javascript提交表单
同一个表单可以根据用户的选择,提交给不同的后台处理程序。即,表单的分向提交。如,在编写论坛程序时,如果我们希望实现用户在发送贴子的时候,既发送提交功能又有预览功能时,就会遇到上述问题。即,当用户点击提交按钮时,我们希望表单提交给"提交"处理程序;而当用户点击预览按钮时,我们希望表单提交给"预览"处理程序。那么,如何实现上述功能呢?下面代码可以很好的解决这个问题。 
<form name="form" method="post">
测试表单:<input name="test"><br>
<input type="button" value="提交" onClick=send()>
<input type="button" value="预览" onClick=preview()>
</form>
<script language=javascript>
function send()
  {
    document.form.action="send.asp"
    document.form.submit()
   }
function preview()
   {
     document.form.action="preview.asp"
     document.form.submit()
   }
</script>

         关于上面实例的两点说明:
        1、在整个表单中,不应有名字为action或submit的标签,否则将会产生"对象不支持此属性和方法"的错误。如代码 "<input type='xxxx' name='action' >"在表单中是不允许出现的;
        2、在form标签中应该存在name属性。即,应该给表单取一个名字。语句document.form.action和document.form.submit中的"form"也就是表单的名字。
        表单的分向提交不仅仅使用在论坛的程序中,它还可以运用在许多场合下。恰当的运用表单的分向提交功能可以大大的增强网站的人性化程度。

        昨天,我调试程序就出现了这样的问题,就是出现了"对象不支持此属性和方法"的错误,一直无法定位出来,都快疯掉了,后来在发现一个button命名为submit了。

1.--------  
   
  <form   name="formname"   action="">  
  <input   name="inputname">  
  <input   type="submit"   onclick="document.formname.action='a.asp'"   value="button   a">  
  <input   type="submit"   onclick="document.formname.action='b.asp'"   value="button   b">  
  <input   type="submit"   onclick="document.formname.action='c.asp'"   value="button   c">  
  </form>      
  2.----------  
  <input   type=button   name="a"   value="a"   onclick="this.form.action='a.asp';this.form.submit();">  
  <input   type=button   name="b"   value="b"   onclick="this.form.action='b.asp';this.form.submit();">  
  <input   type=button   name="c"   value="c"   onclick="this.form.action='c.asp';this.form.submit();">  
  <input   type=button   name="d"   value="d"   onclick="this.form.action='d.asp';this.form.submit();">
posted @ 2010-02-10 16:35 webber 阅读(2542) | 评论 (1)编辑 收藏

用过办公软件word的人都知道,快捷键对我们的工作有多么重要。据说,真正的电脑高手们很少使用鼠标操作,绝大多数操作都直接通过键盘实现。但有些操作在软件本身并没有自带,这种情况下需要我们开动脑筋,自已动手创建快捷键。

下面我们以“粘贴”中的“选择性粘贴”为例,介绍下自己创建系统中所没有的快捷键的步骤。其它的快捷键以此类推。

用word时,复制粘贴是必不可少的操作步骤。以前在word 97 时代,能粘贴到Word中的只是纯文本的文字,现在更多的人使用的是2003、xp版本,粘贴结果就是有格式的文字,还包括图片、甚至表格。这个新功能当然有它的好处,可是如果我只需要源文件中的纯文字,不想要格式、图片,那就需要用到它所附带的选择性粘贴功能。目前word2007提供的方法有两个:

1. 在粘贴结束后,会自动出现一个浮动的“粘贴选项”按钮,用鼠标按这个按钮,选择“仅保留文本”即可。这个方法的缺点是,粘贴动作比较慢(因为无用的内容比较多)

2. 不是用一般的“粘贴”功能,而在菜单中选“编辑”--“选择性粘贴”--“无格式文本”即可。这个方法执行速度快,但操作步骤多,太麻烦。

下面就通过自己创建宏来解决这个问题,实现快捷键操作,就象按一下ctrl+V就可以实现粘贴一样。这个过程分两步:一是建立一个实现“选择性粘贴”的宏,二是给这个宏指定键盘快捷键。

一、创建“选择性粘贴”功能的宏

1、打开word 2007,选择开发工具下的“Visual Basic编辑器”;或者直接按Alt+F11也可以。这时会出现一个Visual Basic编辑窗口,现在看看你的窗口中左侧位置是否有某个模块存在?如果已经有了,你可以跳过建立新模块这一步。如果你的系统在Normal下面不存在任何模块,那么在Normal上,打开右键菜单,选插入模块。

2、现在模块已经有了,用鼠标点击使之高亮,然后按F7键(直接双击模块也可以)。这样右侧就会出现代码窗口。

3、将下面的代码粘贴到“代码窗口”中。关闭Visual Basic编辑窗口。这样,一个宏就建立好了。

Sub 无格式粘贴()

'无格式粘贴 Macro

'宏在 2005-9-22 由 SQ 录制

Selection.PasteSpecial Link:=False, DataType:=wdPasteText, Placement:=wdInLine, DisplayAsIcon:=False
End Sub

到这一步,可以自动运行选择性粘贴的宏就创建成功了,但这时候使用它还需要到“工具—宏—宏”,选择相应的宏名称来运行它,比较麻烦。想快捷使用,继续向下看。


二、将宏拖到工具栏,并指定键盘快捷键

在Word主窗口中,找到word选项,选择工具按钮中的“自定义”。

选“从下列位置选择命令”选项卡,选“宏”,会出现我们刚建立的宏。用鼠标将这个宏拖到右边快速访问工具栏位置,松开鼠标键。请单击修改按钮,在弹出菜单中作相应的图标、名称。这样工具栏中就有了刚才制作的选择性粘贴的宏的快捷按钮了。

再指定快捷键:此时“自定义”对话框依然打开着,请按“键盘快捷方式”按钮旁的“自定义”。“类别”选“宏”,在“宏”中选定EditPasteNoFormat。这时“请按新快捷键”应该是空白的,用鼠标点一下这里,然后按一下你想要的快捷键。这里,假设我们参考粘贴的快捷键ctrl+V,而使用alt+V,出现“未指定”,说明这是一个可用的快捷键,和其它功能不发生冲突。按“指定”按钮。现在alt+V被指定为这个宏的快捷键了。按“关闭”按钮,再 关闭“自定义”窗口。(上面的步骤中既设置了工具栏按钮,也设置了键盘快捷键,也可以只指定其中的一个,看各人使用习惯而定。)

以后再使用WORD时,我们可以按这个工具按钮,或者使用alt+v快捷键方便地实现“粘贴为纯文本”的功能了。其它功能的快捷键也可以照葫芦画瓢。

posted @ 2010-02-10 10:56 webber 阅读(5131) | 评论 (2)编辑 收藏

2010年1月13日 #

*****************web开发的问题汇总********************


2.      Hibernate优化问题。如何优化数据库访问,使程序访问数据库更优化。

 初用HIBERNATE的人也许都遇到过性能问题,实现同一功能,用HIBERNATE与用JDBC性能相差十几倍很正常,如果不及早调整,很可能影响整个项目的进度。

  大体上,对于HIBERNATE性能调优的主要考虑点如下:

  * 数据库设计调整

  * HQL优化

  *  API的正确使用(如根据不同的业务类型选用不同的集合及查询API)

  * 主配置参数(日志,查询缓存,fetch_size, batch_size等)

* 映射文件优化(ID生成策略,二级缓存,延迟加载,关联优化)

  * 一级缓存的管理

  * 针对二级缓存,还有许多特有的策略

  * 事务控制策略。

  1、 数据库设计

  a) 降低关联的复杂性

  b) 尽量不使用联合主键

  c) ID的生成机制,不同的数据库所提供的机制并不完全一样

  d) 适当的冗余数据,不过分追求高范式

  2、 HQL优化

  HQL如果抛开它同HIBERNATE本身一些缓存机制的关联,HQL的优化技巧同普通的SQL优化技巧一样,可以很容易在网上找到一些经验之谈。

  3、 主配置

  a) 查询缓存,同下面讲的缓存不太一样,它是针对HQL语句的缓存,即完全一样的语句再次执行时可以利用缓存数据。但是,查询缓存在一个交易系统(数据变更频繁,查询条件相同的机率并不大)中可能会起反作用:它会白白耗费大量的系统资源但却难以派上用场。

  b) fetch_size,同JDBC的相关参数作用类似,参数并不是越大越好,而应根据业务特征去设置

  c) batch_size同上。

  d) 生产系统中,切记要关掉SQL语句打印。

  4、 缓存

  a) 数据库级缓存:这级缓存是最高效和安全的,但不同的数据库可管理的层次并不一样,比如,在ORACLE中,可以在建表时指定将整个表置于缓存当中。

  b) SESSION缓存:在一个HIBERNATE SESSION有效,这级缓存的可干预性不强,大多于HIBERNATE自动管理,但它提供清除缓存的方法,这在大批量增加/更新操作是有效的。比如,同时增加十万条记录,按常规方式进行,很可能会发现OutofMemeroy的异常,这时可能需要手动清除这一级缓存:Session.evict以及Session.clear

  c) 应用缓存:在一个SESSIONFACTORY中有效,因此也是优化的重中之重,因此,各类策略也考虑的较多,在将数据放入这一级缓存之前,需要考虑一些前提条件:

  i. 数据不会被第三方修改(比如,是否有另一个应用也在修改这些数据?)

  ii. 数据不会太大

  iii. 数据不会频繁更新(否则使用CACHE可能适得其反)

  iv. 数据会被频繁查询

  v. 数据不是关键数据(如涉及钱,安全等方面的问题)。

  缓存有几种形式,可以在映射文件中配置:read-only(只读,适用于很少变更的静态数据/历史数据),nonstrict-read-write,read-write(比较普遍的形式,效率一般),transactional(JTA中,且支持的缓存产品较少)

  d) 分布式缓存:同c)的配置一样,只是缓存产品的选用不同,在目前的HIBERNATE中可供选择的不多,oscache, jboss cache,目前的大多数项目,对它们的用于集群的使用(特别是关键交易系统)都持保守态度。在集群环境中,只利用数据库级的缓存是最安全的。

  5、 延迟加载

  a) 实体延迟加载:通过使用动态代理实现

  b) 集合延迟加载:通过实现自有的SET/LIST,HIBERNATE提供了这方面的支持

  c) 属性延迟加载:

  6、 方法选用

  a) 完成同样一件事,HIBERNATE提供了可供选择的一些方式,但具体使用什么方式,可能用性能/代码都会有影响。显示,一次返回十万条记录(List/Set/Bag/Map等)进行处理,很可能导致内存不够的问题,而如果用基于游标(ScrollableResults)或Iterator的结果集,则不存在这样的问题。

  b) Session的load/get方法,前者会使用二级缓存,而后者则不使用。

  c) Query和list/iterator,如果去仔细研究一下它们,你可能会发现很多有意思的情况,二者主要区别(如果使用了Spring,在HibernateTemplate中对应find,iterator方法):

  i. list只能利用查询缓存(但在交易系统中查询缓存作用不大),无法利用二级缓存中的单个实体,但list查出的对象会写入二级缓存,但它一般只生成较少的执行SQL语句,很多情况就是一条(无关联)。

  ii. iterator则可以利用二级缓存,对于一条查询语句,它会先从数据库中找出所有符合条件的记录的ID,再通过ID去缓存找,对于缓存中没有的记录,再构造语句从数据库中查出,因此很容易知道,如果缓存中没有任何符合条件的记录,使用iterator会产生N+1条SQL语句(N为符合条件的记录数)

  iii. 通过iterator,配合缓存管理API,在海量数据查询中可以很好的解决内存问题,如:

  while(it.hasNext()){

  YouObject object = (YouObject)it.next();

  session.evict(youObject);

 sessionFactory.evice(YouObject.class, youObject.getId());

  }

  如果用list方法,很可能就出OutofMemory错误了。

  iv. 通过上面的说明,我想你应该知道如何去使用这两个方法了。

  7、 集合的选用

  在HIBERNATE 3.1文档的“19.5. Understanding Collection performance”中有详细的说明。

  8、 事务控制

  事务方面对性能有影响的主要包括:事务方式的选用,事务隔离级别以及锁的选用

  a) 事务方式选用:如果不涉及多个事务管理器事务的话,不需要使用JTA,只有JDBC的事务控制就可以。

  b) 事务隔离级别:参见标准的SQL事务隔离级别

  c) 锁的选用:悲观锁(一般由具体的事务管理器实现),对于长事务效率低,但安全。乐观锁(一般在应用级别实现),如在HIBERNATE中可以定义VERSION字段,显然,如果有多个应用操作数据,且这些应用不是用同一种乐观锁机制,则乐观锁会失效。因此,针对不同的数据应有不同的策略,同前面许多情况一样,很多时候我们是在效率与安全/准确性上找一个平衡点,无论如何,优化都不是一个纯技术的问题,你应该对你的应用和业务特征有足够的了解。

  9、 批量操作

  即使是使用JDBC,在进行大批数据更新时,BATCH与不使用BATCH有效率上也有很大的差别。我们可以通过设置batch_size来让其支持批量操作。

  举个例子,要批量删除某表中的对象,如“delete Account”,打出来的语句,会发现HIBERNATE找出了所有ACCOUNT的ID,再进行删除,这主要是为了维护二级缓存,这样效率肯定高不了,在后续的版本中增加了bulk delete/update,但这也无法解决缓存的维护问题。也就是说,由于有了二级缓存的维护问题,HIBERNATE的批量操作效率并不尽如人意!

3.      网站是动态的,需要频繁访问数据库,如何提高访问速度,减少服务器压力?

提供JDBC数据库链接池共享,大大降低数据库压力;

智能动态HTML,大大减少网络数据流量;

海量数据优化,大大提高登录和访问速度;

用户界面和数据缓存,大大提高登录速度;

多层次体系结构,支持多个应用程序服务器并行,大大提高系统伸缩性,并发访问用户数目和数据安全性

4.      搜索引擎优化,如何提高网站排名。优化有哪些具体技术措施?

   网站结构设计中面向搜索引擎的优化注意事项包括:

1)链接引用的重要性

a.以量取胜:不一定加入传统门户网站的分类目录才是网站推广,来自其他网站的任何反相链接都是有用的

b. 以质取胜:被PageRank高的网站引用能更快地提高PageRank

c. 了解搜索引擎的"价值观":不要通过Link Farm提高自身的站点排名:Google会惩罚那些主动链接到Link Farm站点以提高自身排名站点,相应站点的页面将不会被收入到索引中。但如果你的页面被别的Link Farm链接了也不必担心,因为这种被动的链接是不会被惩罚的。

d. 不要吝啬给其他网站的链接:如果一个网页只有大量的进入链接,而缺乏导出链接,也会被搜索引擎认为是没有价值的站点。

2)如何突出关键词:网页标题、主题的设计

a.Theme Engine正在逐步超过PR,成为结果排序中更主要的因素

b.不要空着标题:空着<title></title>无异于浪费了最有价值的一块阵地

c. 标题长度和内容:不要过长,一般在40个字(80个字节)以内,并充分突出关键词的比重

d. 如果网页很多的话,尽量使用不同的网页标题,争取让自己网站的内容更多的进入搜索引擎索引范围

e. 除了<title></title>外,还可以用<h1></h1>标题行突出内容主题, 加强标题的效果

3)页面及站点结构设计注意事项

a. 静态链接: 大部分搜索引擎都认为静态链接的网页是优质网页,Google在优先抓取索引的网页中70%以上是不带参数链接的静态网页。而且即使同样的内容,静态网页也 会比动态网页权重高

b. 能够进入Google索引的页面数量越多越好

c. 网站目录结构要扁平,因为每深一级目录,PAGERANK降低1-2个档次。假设首页是3,其子可能目录就是1了,更深可能就无法列入评级范围了。

d. 表现和内容的分离:“绿色”网页

网页中的javascript和css尽可能和网页分离,一方面提高代码重用度(也方便页面缓存),另外一方面,由于有效内容占网页长度的百分比高,也能提高相关关键词在页面中的比重也增加了。总之,应该鼓励遵循w3c的规范,使用更规范的XHTML和XML作为显示格式便于内容更长时间的保存。

e. 让所有的页面都有能够快速入口:站点地图, 方便网页爬虫(spider)快速遍历网站所有需要发布的内容。如果首页就是用Flash或图片进入的话,无异于将搜索引擎拒之门外,除了UI设计的用户 友好外,spider friendly也是非常重要的

f. 保持网站自身的健康:经常利用坏 链检查工具检查网站中是否有死链

g. 保持网页内容/链接的稳定性和持久性:在搜索引擎索引中网页存在的历史也是一个比较重要的因素,而且历史比较久的网页被链接的几率越高。为了 保证自己网页能够被比较持久的被其他网站的页面引用,如果自己网页中有链接更新时,最好能保留旧的页面并做好链接转向,以保持内容的连续性。

h. 文件类型因素:Google有对PDF, Word(Power Point, Excel), PS文档的索引能力,由于这种文档的内容比一般的HTML经过了更多的整理,学术价值一般比较高,所以这些类型的文档天生就比一般的HTML类型的文档 PageRank要高。因此,对于比较重要的文档:技术白皮书,FAQ,安装文档等建议使用PDF PS等高级格式存取,这样在搜索结果中也能获得比较靠前的位zhi点访问统计的重要性等;,的设计 

4)以及站点访问统计的重要性等

5)Google的站点设计指南

1.Make a site with a clear hierarchy and text links. Every page should be reachable from at least one static text link.  让网站有着清晰的结构和文本链接,所有的页面至少要有一个静态文本链接入口

批注:尽量不要用图片和JAVASCRIPT

2.Offer a site map to your users with links that point to the important parts of your site. If the site map is larger than 100 or so links, you may want to break the site map into separate pages.

为用户提供一个站点地图:转向网站的重要部分。如果站点地图页面超过100个链接,则需要将页面分成多个页面。

批注:索引页不要超过100个链接:SPIDER只考虑页面中头100个链接

3.Create a useful, information-rich site and write pages that clearly and accurately describe your content.

用一些有用的,信息量丰富的站点,清晰并正确的描述你的信息。

4.Think about the words users would type to find your pages, and make sure that your site actually includes those words within it.

想像用户可能用来找到你的关键词,并保证这些关键词在网站中出现。

批注:少用“最大”,“最好”之类的形容词,用用户最关心的词,比如:下载,歌星名字,而不是一些抽象名词。

5.Try to use text instead of images to display important names, content, or links. The Google crawler doesn't recognize text contained in images.

尽可能使用文本,而不是图片显示重要的名称,内容和链接。GOOGLE的机器人不认识图片中的文字。

6.Make sure that your TITLE and ALT tags are descriptive and accurate.

保证:页面的TITLE和ALT标记正确的精确描述

7.Check for broken links and correct HTML.

检查坏链并修正这些HTML错误。

8.If you decide to use dynamic pages (i.e., the URL contains a '?' character), be aware that not every search engine spider crawls dynamic pages as well as static pages. It helps to keep the parameters short and the number of them small.

如果你打算使用动态页面:链接中包含"?",必须了解:并非所有的搜索引擎的机器人能想对待静态页面一样对待动态页面,保持动态页面的参数尽可能的少也会 很有帮助。

9.Keep the links on a given page to a reasonable number (fewer than 100).

让一个页面中的链接少于100个。

批注:用lynx -dump http://www.chedong.com/ 可以模拟从robot角度看到的页面。其最后有链接统计

5.      hibernate对动态查询的理解,如何应用,并作应用示例。

定义:

?静态查询:在编程时已经确定要查询的字段,这时编写的HQL或QBC称为静态查询。

?动态查询:在编程时无法确定要查询的字段,这时编写的HQL或QBC称为动态查询。比如组合查询时,往往需要查询的项很多,但不是每个项都必需。

(HQL适用于静态查询,QBC适用于动态查询)

以下分别用HQL和QBC实现动态查询:

1)下面的程序通过对字符串的拼装使用HQL语句实现动态查询:

Public List findStu(String name, int age){

StringBuffer queryString= new StringBuffer();

Boolean conditionFound= false;

if(name!=null){

queryString.append(“lower(s.name) like :name”);

conditionFound= true;

}if(age!= 0){

if(conditionFound) queryString.append(“and”);

queryString.append(“s.age= :age”);

conditionFound=true;

}

String fromClause= conditionFound?”fromStudent s where” : ”fromStudent s”;

queryString.insert(0,fromClause).append(“order by s.name”);

Query query=getSession().createQuery(“queryString.toString()”);

if(name!=null)query.setString(“name”,’%’+name.toLowerCase()+’%’);

if(age!=0)query.setInteger(“age”,newInteger(age));

return query.list();

}

上面的代码虽然可以正常工作,但是把简单的功能实现的相当复杂,维护起来不方便。我们来看一下使用QBC查询。

Public List findStu(String name, int age){

Criteria crit= getSession().createCriteria(Student.class);

if(name!=null){

crit.add(Restrictions.like(“name”,name,MatchMode.ANYWHERE));

}if(age!=0){

crit.add(Restrictions.eq(“age”,newInteger(age)));

}

crit.addOrder(Order.asc(“name”));

return crit.list();

}

6.      Hibernate问题

●     a different object with the same identifier value was already associated with the session是什么原因导致的?

在hibernate中同一个session里面有了两个相同标识但是是不同实体,当这时运行saveOrUpdate(object)操作的时候就会报这个错误。种错误经常出现在一对多映射和多对多映射

在hibernate的开发中,HBM文件中会涉及到n2m的种种关系,但是,只有在出于必要性考虑的时候,才加上cascade="all" ,同时,需小心规避由此引起的程序Exception.

考虑不周,会导致可能出现的最常见Exception是:

net.sf.hibernate.NonUniqueObjectException

a different object with the same identifier value was already associated with the session:......

解决办法是:

1. 去掉引起Exception的associated Class对映HBM中的不必要cascade;

2. 检查Exception发生位置的session的使用情况;

●     Object references an unsaved transient instance-save the transient instance before flushing是什么原因导致的?

某个对象的某个属性是一个实体,在这个实体没有保存之前就保存这个对象而造成了这个错误。

以下举个例子说明以下

Session session = dao.getSession();

Transaction tx = session.beginTransaction();

Bo.setBman( form ,man,session);

Bo.saveChangeTable( man,session); // 把man当作属性赋给ChangeTable,并保存ChangeTable. 就是这出的错,

dao.save(man,session);  // 保存man

tx.commit();

==================== 应该这样写:===============

Session session = dao.getSession();

Transaction tx = session.beginTransaction();

Bo.setBman( form ,man,session);

dao.save(man,session);  // 保存man

Bo.saveChangeTable( man,session); // 把man当作属性赋给ChangeTable,并保存ChangeTable

tx.commit();

这样,问题就解决了。

 

●     如果修改一个列表中的一个数据,会发现这个数据不稳定,一会是修改后的,一会是修改前的,说出其原因

因为hibernate利用了缓存技术,sql适时提交,当数据修改以后,数据并不一定及时提交到数据库,而是放在hibernate的缓存中,当我们察看数据时,可能是提交完的,也可能是没有提交的,所以就会出现数据的脏读。

如何避免使用缓存技术所带来的脏数据问题呢?

在设计、实现和测试时,应该清晰定义缓存数据的更新:

i. 不考虑缓存数据的更新,重启软件系统是一种必要的方式;

ii. 不考虑缓存数据的更新,缓存数据不可能成为脏数据(但在软件系统中,往往“不可能”会在一次又一次的重构之后变为“可能”);

iii. 考虑缓存数据的更新,当源数据变化时,实时更新缓存数据。

●     对于数据库自增长来说,在映射文件中主键配置,用哪种配置方案最好,最不容易出现问题?

Hibernate标识生成策略:

标识符生成器 描述

increment 适用于代理主键。由Hibernate自动以递增方式生成。

identity     适用于代理主键。由底层数据库生成标识符。

sequence  适用于代理主键。Hibernate根据底层数据库的序列生成标识符,这要求底层数据库支持序列。

hilo       适用于代理主键。Hibernate分局high/low算法生成标识符。

seqhilo     适用于代理主键。使用一个高/低位算法来高效的生成long,short或者int类型的标识符。

native适用于代理主键。根据底层数据库对自动生成标识符的方式,自动选择identity、sequence或hilo。

uuid.hex   适用于代理主键。Hibernate采用128位的UUID算法生成标识符。

uuid.string       适用于代理主键。UUID被编码成一个16字符长的字符串。

assigned   适用于自然主键。由Java应用程序负责生成标识符。

foreign     适用于代理主键。使用另外一个相关联的对象的标识符。

就这个问题我认为应该用increment、native都可以

7.       概括你常用的框架的优缺点。

1. Struts的优缺点:

具体来讲,Struts的优点有:

1. 实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现.

2. 有丰富的tag可以用 ,Struts的标记库(Taglib),如能灵活动用,则能大大提高开发效率。另外,就目前国内的JSP开发者而言,除了使用JSP自带的常用标记外,很少开发自己的标记,或许Struts是一个很好的起点。

3. 页面导航.页面导航将是今后的一个发展方向,事实上,这样做,使系统的脉络更加清晰。通过一个配置文件,即可把握整个系统各部分之间的联系,这对于后期的维护有着莫大的好处。尤其是当另一批开发者接手这个项目时,这种优势体现得更加明显。

4. 提供Exception处理机制 .

5. 数据库链接池管理

6. 支持I18N

缺点:

一、 转到展示层时,需要配置forward,每一次转到展示层,相信大多数都是直接转到jsp,而涉及到转向,需要配置forward,如果有十个展示层的jsp,需要配置十次struts,而且还不包括有时候目录、文件变更,需要重新修改forward,注意,每次修改配置之后,要求重新部署整个项目,而tomcate这样的服务器,还必须重新启动服务器,如果业务变更复杂频繁的系统,这样的操作简单不可想象。现在就是这样,几十上百个人同时在线使用我们的系统,大家可以想象一下,我的烦恼有多大。

二、 Struts 的Action必需是thread-safe方式,它仅仅允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。

三、 测试不方便. Struts的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。

四、 类型的转换. Struts的FormBean把所有的数据都作为String类型,它可以使用工具Commons-Beanutils进行类型转化。但它的转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。

五、 对Servlet的依赖性过强. Struts处理Action时必需要依赖ServletRequest 和ServletResponse,所有它摆脱不了Servlet容器。

六、 前端表达式语言方面.Struts集成了JSTL,所以它主要使用JSTL的表达式语言来获取数据。可是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。

七、 对Action执行的控制困难. Struts创建一个Action,如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。

八、 对Action 执行前和后的处理. Struts处理Action的时候是基于class的hierarchies,很难在action处理前和后进行操作。

九、 对事件支持不够. 在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能对应一个事件,struts这种事件方式称为application event,application event和component event相比是一种粗粒度的事件。

Struts重要的表单对象ActionForm是一种对象,它代表了一种应用,这个对象中至少包含几个字段,这些字段是Jsp页面表单中的input字段,因为一个表单对应一个事件,所以,当我们需要将事件粒度细化到表单中这些字段时,也就是说,一个字段对应一个事件时,单纯使用Struts就不太可能,当然通过结合JavaScript也是可以转弯实现的。

 

       2.Hibernate的优缺点:

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。

Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序实用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

大多数开发机构经常采取创建各自独立的数据持久层。一旦底层的数据结构发生改变,那么修改应用的其余部分使之适应这种改变的代价将是十分巨大的。Hibernate适时的填补了这一空白,它为Java应用提供了一个易用的、高效率的对象关系映射框架。hibernate是个轻量级的持久性框架,功能却非常丰富。

优点:

a. Hibernate 使用 Java 反射机制而不是字节码增强程序来实现透明性。

b. Hibernate 的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。

c. 它支持各种关系数据库,从一对一到多对多的各种复杂关系。

缺点:它限制您所使用的对象模型。(例如,一个持久性类不能映射到多个表)其独有的界面和可怜的市场份额也让人不安,尽管如此,Hibernate 还是以其强大的发展动力减轻了这些风险。其他的开源持久性框架也有一些,不过都没有 Hibernate 这样有市场冲击力。

3. Spring框架的优缺点

它是一个开源的项目,而且目前非常活跃;它基于IoC(Inversion of Control,反向控制)和AOP的构架多层j2ee系统的框架,但它不强迫你必须在每一层中必须使用Spring,因为它模块化的很好,允许你根据自己的需要选择使用它的某一个模块;它实现了很优雅的MVC,对不同的数据访问技术提供了统一的接口,采用IoC使得可以很容易的实现bean的装配,提供了简洁的AOP并据此实现Transcation Managment,等等

优点

   a. Spring能有效地组织你的中间层对象,不管你是否选择使用了EJB。如果你仅仅使用了Struts或其他为J2EE的 API特制的framework,Spring致力于解决剩下的问题。

   b. Spring能消除在许多工程中常见的对Singleton的过多使用。根据我的经验,这是一个很大的问题,它降低了系统的可测试性和面向对象的程度。

   c. 通过一种在不同应用程序和项目间一致的方法来处理配置文件,Spring能消除各种各样自定义格式的属性文件的需要。曾经对某个类要寻找的是哪个魔法般的属性项或系统属性感到不解,为此不得不去读Javadoc甚至源编码?有了Spring,你仅仅需要看看类的JavaBean属性。Inversion of Control的使用(在下面讨论)帮助完成了这种简化。

  d.  通过把对接口编程而不是对类编程的代价几乎减少到没有,Spring能够促进养成好的编程习惯。

  e. Spring被设计为让使用它创建的应用尽可能少的依赖于他的APIs。在Spring应用中的大多数业务对象没有依赖于Spring。

  f. 使用Spring构建的应用程序易于单元测试。

  g.  Spring能使EJB的使用成为一个实现选择,而不是应用架构的必然选择。你能选择用POJOs或local EJBs来实现业务接口,却不会影响调用代码。

  h. Spring帮助你解决许多问题而无需使用EJB。Spring能提供一种EJB的替换物,它们适用于许多web应用。例如,Spring能使用AOP提供声明性事务管理而不通过EJB容器,如果你仅仅需要与单个数据库打交道,甚至不需要一个JTA实现。

  i.  Spring为数据存取提供了一个一致的框架,不论是使用的是JDBC还是O/R mapping产品

Spring确实使你能通过最简单可行的解决办法来解决你的问题。而这是有有很大价值的。

缺点:使用人数不多、jsp中要写很多代码、控制器过于灵活,缺少一个公用控制器

8.       是否了解设计模式,将几种常用的设计模式的思想、并举例。

创建模式:

Factory、Prototype(原型)、Builder、Singleton

结构模式:

Facade(外观)、Proxy(代理)、Adapter(适配器)、Composite(组合)、Decorator(油漆工)、Bridge、Flyweight(享元)

行为模式:

Template、Memento(备忘机制)、Observer、Chain of Responsibility(职责链)、Command、State、Strategy(策略)、Mediator(中介者)、Interdivter(解释器)、Visitor

9.       页面重复刷新,如何解决?客户的行为是无法控制的,那如何控制客户的重复刷新导致的重复提交。

Jsp防止页面刷新表单自提交重复提交思路:

1. 提交后禁用提交按钮(大部分人都是这样做的)

2. 用javascript实现

10.   如何精确记录同时在线人数

   我们可以利用Servlet规范中定义的事件监听器(Listener)来解决这个问题,实现更准确的在线人数统计功能。对每一个正在访问的用户,J2EE应用服务器会为其建立一个对应的HttpSession对象。当一个浏览器第一次访问网站的时候,J2EE应用服务器会新建一个HttpSession对象,并触发HttpSession创建事件,如果注册了HttpSessionListener事件监听器,则会调用HttpSessionListener事件监听器的sessionCreated方法。相反,当这个浏览器访问结束超时的时候,J2EE应用服务器会销毁相应的HttpSession对象,触发HttpSession销毁事件,同时调用所注册HttpSessionListener事件监听器的sessionDestroyed方法。

  可见,对应于一个用户访问的开始和结束,相应的有sessionCreated方法和sessionDestroyed方法执行。这样,我们只需要在HttpSessionListener实现类的sessionCreated方法中让计数器加1,在sessionDestroyed方法中让计数器减1,就轻松实现了网站在线人数的统计功能。

  下面就是利用HttpSessionListener实现在线人数统计的一个例子,这个例子已经在中创软件的J2EE应用服务器InforWeb中测试通过。

  首先,编写一个简单的计数器,代码如下:

package gongfei.cmc.articles.onlinecounter;

public class OnlineCounter {

    private static long online = 0;    

    public static long getOnline() {

        return online;

    }    

    public static void raise(){

        online++;

    } 

    public static void reduce(){

        online--;

   }

}

  然后,编写HttpSessionListener实现类,在这个实现类的sessionCreated方法中调用OnlineCounter的raise方法,在sessionDestroyed方法中调用OnlineCounter的reduce方法,代码如下:

package gongfei.cmc.articles.onlinecounter;

import javax.servlet.http.HttpSessionEvent;

import javax.servlet.http.HttpSessionListener;

public class OnlineCounterListener implements HttpSessionListener {

    public void sessionCreated(HttpSessionEvent hse) {

        OnlineCounter.raise();

    }

    public void sessionDestroyed(HttpSessionEvent hse) {

        OnlineCounter.reduce();

    }

}

  再然后,把这个HttpSessionListener实现类注册到网站应用中,也就是在网站应用的web.xml中加入如下内容:

<web-app>

    ……

    <listener>

        <listener-class>

            gongfei.cmc.articles.example.OnlineCounterListener

        </listener-class>

    </listener>

    ……

</web-app>

OK,在线人数统计功能已经实现,只要在JSP页面中加入下面这样的脚本就能显示但前在线人数了:

<%@ page language="java" pageEncoding="GB2312" %>

<%@ page language="java" pageEncoding="GB2312" %>

<%@ page import="gongfei.cmc.articles.onlinecounter.OnlineCounter" %>

<html>

    <head><title>On Line Counert</title></head>

    <body bgcolor="#FFFFFF">

        On line:<%=OnlineCounter.getOnline()%>

    </body>

</html>

11.   乱码解决方案。是否遇到过?有哪些?讲解具体遇到的情形,并说出你在具体的应用中的解决方案。

1.JSP输出中文的乱码问题

所谓在jsp输出中文,即直接在jsp中输出中文,或者给变量赋中文值再输出等,这种情况下的乱码问题往往是因为没有给jsp页面制定显示中文字符的编码方式,解决办法如下:

1)在jsp页面头部加上语句<%@ page contentType="text/html;charset=utf-8"%>(在Servlet中使用httpServletResponse.setContentType("text/html;charset=utf-8"),最好同时在jsp页面的head部分加上<meta http-equiv="Content-Type" content="text/html;charset="utf-8">

2)在每次要输出中文的地方主动转换编码方式,比如要在页面中输入“中文”二字,就可以用以下的方法:

<%

   String str="中文";

   byte[] tmpbyte=str.getBytes("ISO8859_1");

   str=new String(tmpbyte);

   out.println(str);

%>

对于以上这两种方法,显然第一种方法更通用一点,只需要在一个页面中添加一次代码即可;而对于第二种方法,在每个需要输出中文的地方都需要转码,如果这样的地方很多,这将是一个繁重的工作。

2.获取表单提交的数据时的中文乱码问题

在没有加任何其他处理之前,用request.getParameter("paramName")获取表单提交中的数据,且表单数据中含有中文时,返回的字符串会呈现乱码。出现这种问题的原因是Tomcat的j2ee实现对表单提交,即以POST方式提交的参数采用默认的ISO-8859-1来处理。

解决此问题的办法有两个:

1)不修改其他配置,只是在将表单中的中文数据区出来后再转换编码,方法如语句 String str=request.getParameter("chStr");String str = new String(str.getBytes("ISO-8859-1"),"UTF-8");但这种方法只是从一个局部来考虑问题,如果这样的情况很多,就要写很多次,势必加大工作量。

2)让对所有页面的请求都通过一个Filter,将处理字符集设置为utf-8(根据自己需要也可以设置成其他的,如gb2312,gbk)。具体做法参考Tomcat的webapps/servlet-exemples目录有一个完整的例子,也可以参考其中web.xml和SetCharacterEncodingFilter的配置.

3.URL中的中文问题

对于直接通过在url中传递中文参数,如http://localhost:8080/a.jsp?str="中文"这样的get请求,在服务器端用request.getParameter("name")时返回的往往是乱码。按照以上的做法设置Filter没有用,用request.setCharacterEncoding("utf-8")的方式,仍然不管用。造成这种结果的原因是Tomcat中以get方式提交的请求对query-string处理时采用了和post方法不一样的处理方式。

解决这个问题的方法是是打开Tomcat安装目录下的/conf/server.xml文件,找到Connector块,往其中添加URLEncoding="utf-8"/>

4.数据库访问时的乱码问题

数据库中所有表的编码方式和jsp中的使用的编码要保持一致,这样做的目的可以减少不必要的编码转换问题.另外,在使用jdbc连接MySQL数据库时,连接字符串写成如下形式可以避免一些中文问题:

jdbc://mysql://hostname:port/DBname?user=username&password=pwd&useUnicode=true&character Encoding=utf-8

如果是以数据源的方式连接数据库,配置文件中使用:

<parameter>

   <name>url</name>

   <value>jdbc:mysql://hostname:port/DBname?&useUnicode=true&characterEncoding=utf-8

   </value>

</parameter>

但是如果使用一个已经存在的数据库,数据库的编码方式为ISO-8859-1,而Web应用中使用的utf-8,且数据库已经有很多重要的信息,因此不能通过更改数据库的编码方式来解决。这个时候,在往数据库中写数据时,一定要在jdbc连接字符串中加入“useUnicode=true&characterEncoding=ISO-8859-1”,这样可以顺利的王数据库写入正常的数据。但是,在将数据读出数据库时,乱码又会出现,这个时候就应该在数据取出时对其转码,可以将转码功能写为一个函数,具体实现如下:

public String charConvert(String src){

String result=null;

if(src!=null){

  try{

   result=new String(src.getBytes("ISO-8859-1"),"UTF-8");

  }catch(Exception e){

   result=null;

  }

}

return result;

}

于是,在数据库读出数据过后调用charConvert(rs.getString("colName"));这样就可以正常的显示数据库中的中文数据了。

posted @ 2010-01-13 15:26 webber 阅读(350) | 评论 (0)编辑 收藏