• 一个应用程序新发版后,无法正常使用,从系统日志中查看下面的错误:

    Caused by: org.xml.sax.SAXException: Fatal Error: URI=null Line=5: 未找到外部实体“http://ibatis.apache.org/dtd/sql-map-2.dtd”。 

    根据发版前后更改内容的对比,很快排查出原因可能出在新加的iBatis配置文件,原来的配置文件中DOCTYPE部分对应的:http://www.ibatis.com/dtd/sql-map-2.dtd,但具体原因是什么?

    在使用SAX解析XML文件的时候,会根据DOCTYPE中的定义的DTD验证XML文件的合法性,但是iBatis的XML配置文件中DOCTYPE中DTD文件地址是个公网地址,那么对不能上互联网的情况下如何处理呢?

    在IBM DW的这篇文章中有一个解决方案,可以在解析XML的时候设定自己的EntityResolver,将公网地址映射到本地地址。下面是iBatis 2.3中实现类:

    public class SqlMapClasspathEntityResolver implements EntityResolver {

      private static final String SQL_MAP_CONFIG_DTD = "com/ibatis/sqlmap/engine/builder/xml/sql-map-config-2.dtd";
      private static final String SQL_MAP_DTD = "com/ibatis/sqlmap/engine/builder/xml/sql-map-2.dtd";

      private static final Map doctypeMap = new HashMap();

      static {
        doctypeMap.put("http://www.ibatis.com/dtd/sql-map-config-2.dtd".toUpperCase(), SQL_MAP_CONFIG_DTD);
        doctypeMap.put("http://ibatis.apache.org/dtd/sql-map-config-2.dtd".toUpperCase(), SQL_MAP_CONFIG_DTD);
        doctypeMap.put("-//iBATIS.com//DTD SQL Map Config 2.0//EN".toUpperCase(), SQL_MAP_CONFIG_DTD);
        doctypeMap.put("-//ibatis.apache.org//DTD SQL Map Config 2.0//EN".toUpperCase(), SQL_MAP_CONFIG_DTD);

        doctypeMap.put("http://www.ibatis.com/dtd/sql-map-2.dtd".toUpperCase(), SQL_MAP_DTD);
        doctypeMap.put("http://ibatis.apache.org/dtd/sql-map-2.dtd".toUpperCase(), SQL_MAP_DTD);
        doctypeMap.put("-//iBATIS.com//DTD SQL Map 2.0//EN".toUpperCase(), SQL_MAP_DTD);
        doctypeMap.put("-//ibatis.apache.org//DTD SQL Map 2.0//EN".toUpperCase(), SQL_MAP_DTD);
      }


      /**
       * Converts a public DTD into a local one
       *
       * @param publicId Unused but required by EntityResolver interface
       * @param systemId The DTD that is being requested
       * @return The InputSource for the DTD
       * @throws SAXException If anything goes wrong
       */
      public InputSource resolveEntity(String publicId, String systemId)
          throws SAXException {

        if (publicId != null) publicId = publicId.toUpperCase();
        if (systemId != null) systemId = systemId.toUpperCase();

        InputSource source = null;
        try {
          String path = (String) doctypeMap.get(publicId);
          source = getInputSource(path, source);     
          if (source == null) {
            path = (String) doctypeMap.get(systemId);
            source = getInputSource(path, source);
          }
        } catch (Exception e) {
          throw new SAXException(e.toString());
        }
        return source;
      }

      private InputSource getInputSource(String path, InputSource source) {
        if (path != null) {
          InputStream in = null;
          try {
            in = Resources.getResourceAsStream(path);
            source = new InputSource(in);
          } catch (IOException e) {
            // ignore, null is ok
          }
        }
        return source;
      }}

    我们项目中iBatis用的是2.0的版本,是在iBatis加入Apache之前的版本,SqlMapClasspathEntityResolver里面只有对“www.ibatis.com”相关两个地址的映射,因此解析DOCTYPE里面为“http://ibatis.apache.org/dtd/sql-map-2.dtd”的XML文件时,就出现了上面提到的SAXException异常。

    回想在开发环境上没有出现这个问题,原因是开发环境可以通过互联网直接下载http://ibatis.apache.org/dtd/sql-map-2.dtd这个文件。

  •   if (cell.getType() == CellType.DATE) {
       DateCell dateCell = (DateCell) cell;
       Date date = dateCell.getDate();

       System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(date));
      }

  • 奇怪了,功能这么强大的IDE居然没有这个功能,还要安装插件,地址为http://ahtik.com/eclipse-update/,没想到啊。

    相关链接:Eclipse中的换行

     

  • 原文地址:基于Converter解决Struts无法处理日期类型的问题

    造成Struts处理日期类型数据的问题,根本原因是BeanUtils里面,只内置了对java.sql.Date的转换器,而没有对java.util.Date的转换器。使用Converter,还是需要对Apahce的BeanUtils的类库做比较多的了解,一般人图省事,就把变量类型都换成String了。

    下面是DateConverter的一个例子,把它注册到ConvertUtils里面就行了:ConvertUtils.register(new DateConverter(), Date.class)

    package zizz.struts;

    import java.text.SimpleDateFormat;
    import org.apache.commons.beanutils.Converter;
    import java.util.Set;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.text.ParseException;

    /**
    *
    * 日期转换对象,使用该转换器,在BaseForm当中做一下注册,系统自动地帮助字符的日期表示转换为java.util.Date对象.
    * @author zizz.
    * Create Time:2006-9-11 19:11:47.
    */

    public class DateConverter implements Converter {
        /**
         * 日期格式化对象.
         */

        private static SimpleDateFormat df = new SimpleDateFormat();
        
        /**
         * 模式集合.
         */

        private static Set<String> patterns = new HashSet<String>();
        //注册一下日期的转换格式
        static{
            DateConverter.patterns.add("yyyy-MM-dd");
            DateConverter.patterns.add("yyyy-MM-dd HH:mm");
            DateConverter.patterns.add("yyyy-MM-dd HH:mm:ss");
            DateConverter.patterns.add("yyyy/MM/dd HH:mm:ss");
        }
        
        /**
         * 日期转换器.
         * @param type Class
         * @param value Object
         * return Date Object.
         */

        public Object convert(Class type,Object value){
            if(value == null){
                return null;
            }else if(value instanceof String){
                Object dateObj = null;
                Iterator it = patterns.iterator();
                while(it.hasNext()){
                    try{
                        String pattern = (String)it.next();
                        df.applyPattern(pattern);
                        dateObj = df.parse((String)value);
                        break;
                    }catch(ParseException ex){
                        //do iterator continue
                    }
                }
                return dateObj;
            }else{
                return null;
            }
        }
    }
  • PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
    PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
    PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
    PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
    PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
    PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

    对照一下,自己以前的理解确实有错误,结果就是单独定义几个没有事务的Service,调用的时候需要嵌入到其他Service方法中,认为这样才能能保证两个Service方法在一个事务里面执行。实际上声明为PROPAGATION_REQUIRED就可以了。

    详细的说明请参见下面的文章:解惑Spring嵌套事务