Struts2 DoubleSelect标签的使用

Struts2本身提供了级联下拉选择框标签,这对简化诸如省份/城市这样的典型需求的操作是大有好处的,同时也有利于架构的统一,没有必要引入一大堆的javascript代码,使用方法如下:

1、Action层

DoubleListAction.java

package com.mobilesoft.esales.webapp.action;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.mobilesoft.framework.tree.model.FileWrapper;
import com.mobilesoft.framework.tree.model.OssCategory;
import com.mobilesoft.framework.tree.service.TreeService;

public class DoubleListAction extends BaseAction {
    /**
     * Logger for this class
     */
    private static final Logger logger = Logger.getLogger(DoubleListAction.class);

    public String execute() {
        Map map=new HashMap();
        ArrayList list1=new ArrayList();
        list1.add("11");
        list1.add("12");
        list1.add("13");
        map.put("1", list1);
        ArrayList list2=new ArrayList();
        list2.add("21");
        list2.add("22");
        list2.add("23");
        map.put("2", list2);
        ArrayList list3=new ArrayList();
        list3.add("31");
        list3.add("32");
        list3.add("33");
        map.put("3", list3);
        getRequest().setAttribute("map", map);
        return SUCCESS;
    }

    public String doubleSelectTest(){
        Map map=new HashMap();
        ArrayList list1=new ArrayList();
        list1.add("11");
        list1.add("12");
        list1.add("13");
        map.put("1", list1);
        ArrayList list2=new ArrayList();
        list2.add("21");
        list2.add("22");
        list2.add("23");
        map.put("2", list2);
        ArrayList list3=new ArrayList();
        list3.add("31");
        list3.add("32");
        list3.add("33");
        map.put("3", list3);
        getRequest().setAttribute("map", map);
        return SUCCESS;
    }
    public String doubleSelectPost(){

        Map map=getRequest().getParameterMap();
        Set set=map.entrySet();
        Iterator iterator=set.iterator();
        while(iterator.hasNext()){
            Map.Entry mapEntry=(Map.Entry)iterator.next();
            logger.fatal("The key is: "+mapEntry.getKey()+",value is :"+mapEntry.getValue());
        }
        getRequest().setAttribute("map", map);
        return SUCCESS;
    }

}

2、页面

doubleslect.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page language="java" errorPage="/error.jsp" pageEncoding="GBK" contentType="text/html;charset=GBK" %>
<html>
<head>
    <title>Struts 2 Cool Tags - <s:doubeselect/ ></title>
    <s:head />
</head>
<body>
<h2>Doubleselect Tag Example</h2>
<s:form action="doubleSelectPost" name="form1">
Doubleselect演示1
<s:set name="technology"
     value="#{
         'Java': {'Spring', 'Hibernate', 'Struts 2'},
         '.Net': {'Linq', ' ASP.NET 2.0'},
         'Database': {'Oracle', 'SQL Server', 'DB2', 'MySQL'}
         }" />
<s:doubleselect label="技术选择"
list="#technology.keySet()"
name="doubleselect1"
doubleName="techdetail"
formName="form1"
doubleList="#technology[top]" />
<br/><br/>
Doubleselect演示数据演示2:
<s:set name="mymap" value="#request.map"/>
<s:doubleselect list="#request.map.keySet()"
                doubleName="mymap1"
                name="doubleselect2"
                formName="form1"
                doubleList="#request.map[top]"
                label="Map Test" />

<s:submit/>
</s:form>
</body>
</html>

doubleselectpost.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page language="java" errorPage="/error.jsp" pageEncoding="GBK" contentType="text/html;charset=GBK" %>
<html>
<head>
    <title>Struts 2 Cool Tags - <s:doubeselect/ ></title>
    <s:head />
</head>
<body>
<h2>Doubleselect Tag Example</h2>
<s:form name="form1">
<table border="0" cellspacing="0" cellpadding="1">
<tr>
  <th>参数值</th>
</tr>
<s:iterator value="#request.map.keySet()" status="keys">
    <tr>
        <td><s:property/></td>
    </tr>
</s:iterator>
</s:form>
</body>
</html>

3、struts.xml配置文件

<action name="doubleSelectTest" method="doubleSelectTest" class="com.mobilesoft.esales.webapp.action.DoubleListAction">
    <result name="success">test/doubleselect.jsp</result>
</action>
<action name="doubleSelectPost" method="doubleSelectPost" class="com.mobilesoft.esales.webapp.action.DoubleListAction">
    <result name="success">test/doubleselectpost.jsp</result>
</action>

4、级联下拉框间的换行<br >问题

缺省情况下,两个级联下拉框间是换行的,Struts2采用Freemarker来做模板,可以通过修改模板来定制标签缺省的树形。参看“Struts2中Datetimepicker控件的中文问题”中的方法,也即:

  • 修改struts.mxl,增加如下内容。

<constant name=”struts.serve.static” value=”false” />

<constant name=”struts.ui.templateDir” value=”struts” />

注意在struts2-core-2.0.11.jar/org/apache/struts2/default.properties中有几个与Freemarker对应的配置参数:

struts.ui.theme=xhtml
struts.ui.templateDir=template
#sets the default template type. Either ftl, vm, or jsp
struts.ui.templateSuffix=ftl

  • 覆盖缺省的静态文件

在resource目录(与WEB-INF同级或WEB-INF下)创建struts目录,并:

解压struts2-core-2.0.11.jar:/org/apache/struts2/static/ to /struts/

解压struts2-core-2.0.11.jar:/template/simple/ to /struts/simple/

解压struts2-core-2.0.11.jar:/template/xhtml/ to /struts/xhtml/

解压struts2-core-2.0.11.jar:/template/css_xhtml/ to /struts/css_xhtml/

解压struts2-core-2.0.11.jar:/template/ajax/ to /struts/ajax/

解压struts2-core-2.0.11.jar:/template/archive/ to /struts/archive

最后的目录结构如下:

struts/
- ajax/
- archive/
- css_xhtml/
- dojo/
- nls/
- src/
- animation/
- …
- xml/
- struts/
- widget/
- widgets/
- niftycorners/
- simple/
- xhtml/

  • 修改doubleselect的模板doubleselect.ftl

修改struts/simple/doubleselect.ftl去除其中的<br />

Technorati 标签: ,,,

Struts2 Tree标签的使用

  在项目中有很多地方使用了树形结构,主要有几个地方:素材和软件等软件资源表,人员权限的权限资源表、人员权限的角色,套餐及产品包,现以类别表为例,说明树形结构的数据库存储、利用Struts2 中dojo的Tree标签来完成前端展现。

1、 数据库结构

CREATE TABLE `oss_category` (
  `category_id` int(11) NOT NULL auto_increment,
  `parent_id` int(11) default '-1',
  `level` smallint(6) default NULL ,
  `is_leaf` tinyint(1) default NULL,
  `category_title` varchar(100) default NULL,
  `category_name` varchar(100) default NULL,
  `category_code` varchar(100) default NULL,
  `category_type` varchar(30) default NULL,
  `image` varchar(255) default NULL,
  `status` varchar(20) default NULL,
  `creator` varchar(50) default NULL,
  `create_date` datetime default NULL,
  `modify_user` varchar(50) default NULL,
  `modify_date` datetime default NULL,
  `description` text,
  PRIMARY KEY  (`category_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=gbk;

测试数据:

INSERT INTO `oss_category` VALUES ('1', '-1', '1', '0', 'root', 'root', 'root', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('2', '1', '2', '0', '<input type=\'checkbox\' name=\'product_type1\'  id=\'product_type1\'  /><b>product_type1</b>', 'product_type1', 'product_type1', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('3', '1', '2', '0', '<input type=\'checkbox\' name=\'product_type2\' id=\'product_type2\'  class=\'treeCheckBox\'/><b>product_typ', 'product_type2', 'product_type2', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('4', '3', '3', '0', '<input type=\'checkbox\' name=\'product_type21\'  id=\'product_type21\'  /><b>product_type21</b>', 'product_type21', 'product_type21', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('5', '2', '3', '0', '<input type=\'checkbox\' name=\'product_type11\'  id=\'product_type11\'  /><b>product_type11</b>', 'product_type11', 'product_type11', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('6', '5', '4', '1', '<input type=\'checkbox\' name=\'product_type111\'  id=\'product_type111\'  /><b>product_type111</b>', 'product_type111', 'product_type111', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('7', '3', '2', '0', '<input type=\'checkbox\' name=\'product_type22\'  id=\'product_type22\'  /><b>product_type22</b>', 'product_type22', 'product_type22', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('8', '2', '3', '0', '<input type=\'checkbox\' name=\'product_type12\'  id=\'product_type12\'  /><b>product_type12</b>', 'product_type12', 'product_type12', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('9', '4', '4', '1', '<input type=\'checkbox\' name=\'product_type211\'  id=\'product_type211\'  /><b>product_type211</b>', 'product_type211', 'product_type211', 'product_type', null, '1', null, null, null, null, null);
INSERT INTO `oss_category` VALUES ('10', '7', '4', '1', '<input type=\'checkbox\' name=\'product_type221\'  id=\'product_type221\'  /><b>product_type221</b>', 'product_type221', 'product_type221', 'product_type', null, '1', null, null, null, null, null);

 

注意:

  • 为了支持在树形结构的节点前有checkbox框,需要在树形结构的title(对应s:tree中的nodeTitleProperty)前增加checkbox树形,因此在数据库中category_title字段的格式采用如上的形式,例如:

<input type=’checkbox’ name=’product_type1′  id=’product_type1′  /><b>product_type1</b>

  • 对于有序的树形结构,为了标识left和right,可以增加ordering字段,或者采用类似嵌套树的结构,例如
CREATE TABLE nested_category (
 category_id INT AUTO_INCREMENT PRIMARY KEY,
 name VARCHAR(20) NOT NULL,
 lft INT NOT NULL,
 rgt INT NOT NULL
);
但采用嵌套树的方式,在变动一个节点时候需要对相关的节点进行更新,不是很适合。但效率较高。可以参看:
http://dev.mysql.com/tech-resources/articles/hierarchical-data.html
http://www.sitepoint.com/print/hierarchical-data-database
  • mysql对树形结构没有原生的支持,Oracle和DB2可以支持,Oracle通过CONNECT BY,DB2通过With方式。

2、Model层

OssCategory.java

直接用myeclipse生成后,增加了如下内容:

    private OssCategory[] childCategories;
    private OssCategory parentCategory;

    public OssCategory[] getChildCategories() {
        return childCategories;
    }

    public void setChildCategories(OssCategory[] childCategories) {
        this.childCategories = childCategories;
    }

    public OssCategory getParentCategory() {
        return parentCategory;
    }

    public void setParentCategory(OssCategory parentCategory) {
        this.parentCategory = parentCategory;
    }

3、DAO层

OssCategoryDAO.java

直接用myeclipse生成,没有调优,增加如下内容:

    public OssCategory[] getAllCategory() {
        ArrayList<OssCategory> resultList=new ArrayList<OssCategory>();
        String queryString="from OssCategory as category where category.parentId=1";
        List<OssCategory> queryList =getHibernateTemplate().find(queryString);
        Iterator iterator=queryList.iterator();
        while(iterator.hasNext()){
            OssCategory ossCategory=(OssCategory)iterator.next();
            OssCategory[] childrenArray=getChildCategoriesById(ossCategory.getCategoryId());
            logger.debug("getAllCategory() - OssCategory[] childrenList=" + childrenArray.length);
            ossCategory.setChildCategories(childrenArray);
            resultList.add(ossCategory);

        }

        OssCategory[] resultArray=(OssCategory[])resultList.toArray(new OssCategory[resultList.size()]);
        return resultArray;

    }

    public OssCategory[] getChildCategoriesById(int categoryId) {
        String queryString="from OssCategory as category where category.parentId="+categoryId;
        List<OssCategory> queryList =getHibernateTemplate().find(queryString);
        logger.debug("getChildCategoriesById(int) - List<OssCategory> queryList=" + queryList.size());
        ArrayList<OssCategory> resultList=new ArrayList<OssCategory>();
        Iterator iterator=queryList.iterator();
        while(iterator.hasNext()){
            OssCategory ossCategory=(OssCategory)iterator.next();
            OssCategory[] childrenList=getChildCategoriesById(ossCategory.getCategoryId());
            logger.debug("getChildCategoriesById(int) - OssCategory[] childrenList=" + childrenList+"parentid is "+categoryId); //$NON-NLS-1$
            ossCategory.setChildCategories(childrenList);
            resultList.add(ossCategory);

        }
        OssCategory[] resultArray=(OssCategory[])resultList.toArray(new OssCategory[resultList.size()]);
        return resultArray;
    }
注意:
  •  getAllCategory和getChildCategoriesById采用了递归调用方式,应该可以一次性取出指定节点的所有子节点到结果集合List,然后对List在内存中进行遍历,但比较麻烦,先采用此种方式。
  •  struts2的tree应该支持List,而不一定要采用数组方式,只不过没有测试
 

4、Service层

TreeService.java

有些方法尚未实现

package com.mobilesoft.framework.tree.service;

import java.util.List;
import java.util.Set;

import com.mobilesoft.framework.tree.model.OssCategory;

public interface TreeService {

    /**
     * @return 获取下级子节点
     */
  //  public Set getChildren(int rootId);

    /**
     * @return 递归指定级别的所有子节点
     *      
     */
  //  public Set getChildrenByLevel(int rootId,int level);

    /**
     * @return 获取指定类型的所有子节点
     * @param type:节点的类型
     * @param levle:节点相对于所属类型的节点的级别 -1表示获取所属类型的根节点的所有级别的子节点
     */
 //   public Set getChildrenByType( String type,int level);

    /**
     * @return 获取指定根节点的所有子节点
     */
    public OssCategory[]  getAllCategory();

}

TreeServiceImpl.java

 

package com.mobilesoft.framework.tree.service.impl;

import java.util.List;
import java.util.Set;

import com.mobilesoft.framework.tree.dao.hibernate.OssCategoryDAO;
import com.mobilesoft.framework.tree.model.OssCategory;
import com.mobilesoft.framework.tree.service.TreeService;

public class TreeServiceImpl implements TreeService {
    OssCategoryDAO ossCategoryDAO;
    public Set getChildren(int rootId) {
        // TODO Auto-generated method stub
        return null;
    }

    public Set getLevelChildren(int rootId, int level) {
        // TODO Auto-generated method stub
        return null;
    }

    public OssCategory[]   getAllCategory(){
        return ossCategoryDAO.getAllCategory();
    }
    public OssCategoryDAO getOssCategoryDAO() {
        return ossCategoryDAO;
    }

    public void setOssCategoryDAO(OssCategoryDAO ossCategoryDAO) {
        this.ossCategoryDAO = ossCategoryDAO;
    }

}

5、Action层

package com.mobilesoft.esales.webapp.action;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.mobilesoft.framework.tree.model.FileWrapper;
import com.mobilesoft.framework.tree.model.OssCategory;
import com.mobilesoft.framework.tree.service.TreeService;

public class TreeAction extends BaseAction {
    /**
     * Logger for this class
     */
    private static final Logger logger = Logger.getLogger(TreeAction.class);

    private OssCategory root;
    private TreeService treeService;
    private FileWrapper fileroot;

    public String execute() {
        root = new OssCategory();
        root.setCategoryId(new Integer(1));
        root.setParentId(-1);
        root.setCategoryTitle("Root");
        root.setCategoryName("Root");
        OssCategory[] childCategories = treeService.getAllCategory();

        root.setChildCategories(childCategories);
        visitTree(childCategories);
        getRequest().setAttribute("root", root);
        // fileroot = new FileWrapper(getSession().getServletContext().getRealPath("/"));
        return SUCCESS;
    }

    public String treeWithCheckbox() {
        Map map = getRequest().getParameterMap();
        logger.fatal("the tree map");
        Set set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();

            logger.fatal("the tree key is: " + entry.getKey() + " ,and value is: "
                    + entry.getValue());
        }

        return SUCCESS;
    }

    public TreeService getTreeService() {
        return treeService;
    }

    public void setTreeService(TreeService treeService) {
        this.treeService = treeService;
    }

    public OssCategory getRoot() {
        return root;
    }

    public void setRoot(OssCategory root) {
        this.root = root;
    }

    public void visitTree(OssCategory[] tree) {

        for (int i = 0; i < tree.length; i++) {
            OssCategory node = (OssCategory) tree[i];
            logger.fatal("visitTree:The treenode parentid is :"
                    + node.getParentId() + " ,treenode id is "
                    + node.getCategoryId() + ",and the treenode name is "
                    + node.getCategoryName());
            visitTree(node.getChildCategories());
        }

    }
}

6、页面

treetest.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page language="java" errorPage="/error.jsp" pageEncoding="GBK" contentType="text/html;charset=GBK" %>
<html>
<head>
    <title>tag list</title>
    <%@ include file="/common/meta.jsp" %>

<style>
    .treeCheckBox {
        height: 14px;
        margin: 0px;
        padding: 0px;
        border: 1px;
        vertical-align: middle;
    }
</style>
<s:head theme="ajax" debug="true" />
</head>
<body >

<script type="text/javascript">
function treeNodeSelected(arg) {
    //alert("id["+arg.source.widgetId+"], name["+ arg.source.title+ "] selected");
}

function treeNodeExpanded(arg) {
    //alert("id["+arg.source.widgetId+"], name["+ arg.source.title+ "] expanded");
}

function treeNodeCollapsed(arg) {
    //alert("id["+arg.source.widgetId+"], name["+ arg.source.title+ "] collapsed");
}

dojo.addOnLoad(function() {
    var t = dojo.widget.byId('root');
    dojo.event.topic.subscribe(t.eventNames.expand, treeNodeExpanded);
    dojo.event.topic.subscribe(t.eventNames.collapse, treeNodeCollapsed);

    var s = t.selector;   

});

</script>

<s:form name="form1" action="treeWithCheckbox">
<div style="float:left; margin-right: 50px;" >
<s:tree
    theme="ajax"
    id="root"
    rootNode="#request.root"
    childCollectionProperty="childCategories"
    nodeIdProperty="categoryId"
    nodeTitleProperty="categoryTitle"
    treeSelectedTopic="treeSelected">
</s:tree>
</div>     

<div style="float:left; margin-right: 50px;">
<s:submit/>
</div>
</s:form>

</body>
</html>
 

注意:

  • 在s:tree中不能使用label标签,使用后树形结构出不来。
  • childCollectionProperty=”childCategories”对应Category.java中的
    private OssCategory[] childCategories;
    
    public OssCategory[] getChildCategories() {
        return childCategories;
    } 
    
    public void setChildCategories(OssCategory[] childCategories) {
        this.childCategories = childCategories;
    }
    
  • nodeIdProperty=”categoryId”和nodeTitleProperty=”categoryTitle” 对应OssCategory[] childCategories中每一个Category的categoryId和categoryTitle

  • rootNode=”#request.root”,采用rootNode=”root”方式好像取不到数据,按理说应该可以通过ognl自动调用getRoot方法得到

  • 结果提交后,根据对应checkbox的name的on属性来判断是否选中,因此在数据库中对节点的命名需要有一定规则,以便方便选择

尚需要完善的地方:

  • 增加对下拉框树形结构实现的支持,以处理项目中大量诸如产品类型、资源类型等类型。

在实现上,为了避免由于浏览器兼容性问题,在形式上是树形结构,实际上就是从字典表(目前树形结构仍然采用oss_category作为字典表)动态取出数据,然后拼凑成字符串形式展现,例如:

<select>
    <option value="1">1</option>
    <option value="11">&nbsp;&nbsp;|-11</option>
    <option value="12">&nbsp;&nbsp;|-12</option>
    <option value="2">2</option>
    <option value="21">&nbsp;&nbsp;|-21</option>
    <option value="22">&nbsp;&nbsp;|-22</option>
</select>
  • 进一步优化递归算法,一次性从数据库取出需要的数据,在内存中完成树形结构的构造,避免对数据库的查询,在目前情况下,采用递归方式问题也不大。
  • 由于Struts2.0采用dojo来实现其标签,尽管dojo与jquery相比较很重,但由于与Struts2.0较好支持,在架构统一上和使用上还是较为方便的,因此有空需要研究一下dojo库。

7、参考资料

http://www.ajaxtree.com/

http://struts.apache.org/2.x/docs/tree.html

http://ait.web.psi.ch/js/dojo/tests/widget/tree/

http://exjava.spaces.live.com/blog/cns!A43FBDAF9245A0BC!199.entry

http://www.stack.be/~roel/blog/archives/the-dojo-tree-control-for-beginners-part-1/

http://www.stack.be/~roel/blog/archives/the-dojo-tree-control-for-beginners-part-2

http://www.codepencil.com/index.php/struts2-dojo-dynamic-tree/

http://dojotoolkit.org/book/dojo-book-0-9/part-2-dijit/advanced-editing-and-display/tree

 

Technorati 标签: ,,,,

Struts2中Datetimepicker控件的中文问题

为了与现有数据库字符集GBK统一,在Struts2中采用GBK作为缺省的编码方式,与此对应的编码相关的配置:

1、JVM中缺省字符集

    或者在环境变量中配置JAVA_OPTS=%JAVA_OPT%;-Dfile.encoding=GBK -Duser.language=zh_CN
    或者在Tomcat的启动脚本中配置JAVA_OPTS=%JAVA_OPT%;-Dfile.encoding=GBK -Duser.language=zh_CN

2、Tomcat的URIEncoding ,处理GET方式的乱码问题

在server.xml中配置:

<Connector port="8080" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true"
               URIEncoding="GBK"

3、Struts2的Locale配置

在Struts.xml中配置
    <constant name=”struts.locale” value=”zh_CN” />
    <constant name=”struts.i18n.encoding” value=”GBK” />

4、采用Spring的过滤器对POST页面编码

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>
        org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>GBK</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

5、页面统一编码为GBK

在common/meta.jsp中

<meta http-equiv=”Cache-Control” content=”no-store”/>
<!– HTTP 1.0 –>
<meta http-equiv=”Pragma” content=”no-cache”/>
<!– Prevents caching at the Proxy Server –>
<meta http-equiv=”Expires” content=”0″/>
<meta http-equiv=”Content-Type” content=”text/html; charset=GBK”/>
<meta name=”generator” content=”Mobile-soft.cn” />
<meta http-equiv=”keywords” content=”mobile,payment,telecommunication,internet”>
<meta http-equiv=”description” content=”mobile-soft”>
同时在各页面中包含meta.jsp页面及设定pageEncoding:

<%@ include file=”/common/meta.jsp” %>

<%@ page language=”java” errorPage=”/error.jsp” pageEncoding=”GBK” contentType=”text/html;charset=GBK” %>

6、数据库编码

数据库建库时候字符集编码采用GBK

在applicationContext-resources.xml中,mysql的配置

    <bean id=”dataSource” class=”org.apache.commons.dbcp.BasicDataSource”
        destroy-method=”close”>
        <property name=”driverClassName” value=”com.mysql.jdbc.Driver”/>
        <property name=”url”
            value=”jdbc:mysql://localhost/mysql?useUnicode=true&amp;characterEncoding=gbk”/>
        <property name=”username” value=”root”/>
        <property name=”password” value=”"/>
        <property name=”maxActive” value=”100″/>
        <property name=”maxIdle” value=”30″/>
        <property name=”maxWait” value=”1000″/>
        <property name=”defaultAutoCommit” value=”true”/>
        <property name=”removeAbandoned” value=”true”/>
        <property name=”removeAbandonedTimeout” value=”60″/>
        <property name=”validationQuery” value=”SELECT 1″/>
        <property name=”testOnBorrow” value=”true”/>
    </bean>

    采用以上步骤基本上搞定了乱码问题,但在使用Struts2的datetimepicker控件时候出现了乱码问题,Struts2的javascript标签库缺省是采用庞大的dojo库(为何不采用jquery这样清爽的javascript库),因此怀疑是dojo的i18n问题,解决步骤如下:

1、修改struts.serve.static的缺省配置

修改struts.mxl,增加如下内容。

<constant name=”struts.serve.static” value=”false” />

在struts2-core-2.0.11.jar/org/apache/struts2/default.properties中,struts.serve.static缺省值为true,也即从struts2-core-2.0.11.jar查找Struts2的静态文件,这参数的命名太晦涩。

### Used by FilterDispatcher
### If true then Struts serves static content from inside its jar.
### If false then the static content must be available at <context_path>/struts
struts.serve.static=true

注意这里的<context_path>/struts实际指的就是jsp页面所在目录。

2、覆盖缺省的静态文件

    在resource目录(与WEB-INF同级或WEB-INF下)创建struts目录,并:

    解压struts2-core-2.0.11.jar:/org/apache/struts2/static/ to /struts/

    解压struts2-core-2.0.11.jar:/template/simple/dojoRequire.js to /struts/simple/

    解压struts2-core-2.0.11.jar:/template/xhtml/styles.css to /struts/xhtml/

    解压struts2-core-2.0.11.jar:/template/xhtml/validation.js to /struts/xhtml/

    解压struts2-core-2.0.11.jar:/template/css_xhtml/styles.css to /struts/css_xhtml/

    解压struts2-core-2.0.11.jar:/template/css_xhtml/validation.js to /struts/css_xhtml/

    解压struts2-core-2.0.11.jar:/template/ajax/dojoRequire.js to /struts/ajax/

    解压struts2-core-2.0.11.jar:/template/ajax/validation.js to /struts/ajax/

    注意:以上所说的解压并不是把整个包都解压,只是把需要的内容勇winrar直接拽出来,对于struts2-core-2.0.11.jar包还是保持原状,不要做任何改动,以方便后期的升级。

3、修改js文件的编码方式

    用记事本打开struts/dojo/src/i18n/calendar/nls/zh/gregorian.js并以ASSI格式另存为gregorian.js

    用记事本打开struts/dojo/src/i18n/calendar/nls/zh/gregorianExtras.js并以ASSI格式另存为gregorianExtras.js

    用记事本打开struts/dojo/src/i18n/calendar/nls/zh-cn/gregorian.js并以ASSI格式另存为gregorian.js

  以上几个文件,原来的格式是UTF-8的

4、页面实例

<%@ taglib prefix=”s” uri=”/struts-tags” %>
<%@ page language=”java” errorPage=”/error.jsp” pageEncoding=”GBK” contentType=”text/html;charset=GBK” %>
<html>
<head>

 <title>tag list</title>

 <%@ include file=”/common/meta.jsp” %>

 <s:head/>

</head>
<body>
 <s:form name=”form1″ action=”test2″>
 <s:datetimepicker name=”start_date” language=”zh-cn”  label=”计费开始时间” displayFormat=”yyyy-MM-dd” />
 <s:submit/>
</s:form>

</body>
</html>

注意这里language为zh-cn(也可以为zh),而不是zh_CN,这与java中不同。dojo官方在Internationalization (i18n)文档中强调:

Locale

dojo.locale

The locale is a short string, defined by the host environment, which conforms to RFC 3066 used in the HTML specification. It consists of short identifiers, typically two characters long which are case-insensitive. Note that Dojo uses dash separators, not underscores like Java (e.g. “en-us”, not “en_US”). Typically country codes are used in the optional second identifier, and additional variants may be specified. For example, Japanese is “ja”; Japanese in Japan is “ja-jp”. Notice that the lower case is intentional — while Dojo will often convert all locales to lowercase to normalize them, it is the lowercase that must be used when defining your resources.

The locale in the browser is typically set during install and is not easily configurable. Note that this is not the same locale in the preferences dialog which can be used to accompany HTTP requests; there is unfortunately no way to access that locale from the client without a server round-trip.

The locale Dojo uses on a page may be overridden by setting djConfig.locale. This may be done to accomodate applications with a known user profile or server pages which do manual assembly and assume a certain locale. You may also set djConfig.extraLocale to load localizations in addition to your own, in case you want to specify a particular translation or have multiple languages appear on your page.

5、参考文档

http://cwiki.apache.org/S2WIKI/creating-a-custom-dojo-profile-for-struts-20x.html

http://dojo.jot.com/InternationalizationOverview

http://cwiki.apache.org/WW/datetimepicker.html