2008年1月10日

MyEclipse使用技巧

程序代码自动排版:Ctrl+Shift+F,会自动把代码进行格式化的排版,非常方便
快速执行程序:Ctrl + F11第一次执行时,它会询问您执行模式,设置好后,以后只要按这个热键,它就会快速执行。

Ctrl+Shift+/ 加上段注释/**/

Ctrl+Shift+\ 取消段注释/**/

Ctrl+/ 加上行注释或取消行注释

自动汇入所需要的类别:Ctrl+Shift+O

取消自动validation:
取消方法: windows-->perferences-->myeclipse-->validation 
除开Manual下面的复选框全部选中之外,其他全部不选 
手工验证方法: 
在要验证的文件上,单击鼠标右键-->myeclipse-->run validation 

按new Remote Site,Name填 svn , URL填http://subclipse.tigris.org/update,一直next到finished为止

posted @ 2008-02-18 21:09 灵! 阅读(849) | 评论 (0)编辑 收藏

几个提高代码质量,检查代码规范的工具

1.FindBugs:查错
 目前版本0.9.1,有for eclipse的插件. 网址是http://findbugs.sourceforge.net.
 
 工作原理:检查程序生成的class的工具.
 
 界面:独立运行的提供图形界面,很友好,有bug报告.
 
 可用性:大多数提示有用,值得改
 
 插件:
  可以设置基本和检查的错误类别.
  插件保存设置有问题,我是关闭项目后台修改了配置文件,在装入才成功改了配置的.
  bug临时解决: 使用独立的findbugs设置规则,然后到C:\Documents and Settings\XXX\下找.Findbugs_prefs,然后改名覆盖eclipse project下的.fbprefs (先关闭你的project)
 
 配置没有查找功能,不过缩写能让我们很快找到某个规则
 
2.PMD:主要是查错
 目前版本3.2,有for eclipse以及其他ide的插件.网址是http://pmd.sourceforge.net
 工作原理:检查源码
 可用性:一部分值得修改,有些过于严格
 界面:独立运行的是命令行界面,命令比较简单.
 插件:可以配置规则,有一个独立的窗口显示提示,分5级提示,很友好
 
 使用:建立自己的规范,然后用于实际使用中.
 
3.CheckStyle:主要查代码规范
 目前版本4.0 beta 5,有for eclipse的插件.网址是http://checkstyle.sourceforge.net.
 工作原理:检查源码,对javadoc,书写格式等进行检查.
 规则定义:默认的规则是sun的编码规范.不过按照sun的规则则过于严格,而且每个公司也有自己的规范,和sun的不同,所以需要自定义规范. 

4.JTest 重量级的商业工具
 目前版本7.0.7,有for eclipse的插件.网址是http://www.parasoft.com/
 
 不推荐使用,不过功能强大,可以进行代码检查,可以自动生成单元测试和进行单元测试.(不过就是太慢了,而且生成的单元测试没太大用途)
 
 
 

使用感觉:

 安装上插件后,对自己的项目进行检查,发现警告太多了,有点发蒙的感觉.不过把警告看一遍,觉得都很有道理,有些也确实是一些错误.
 当然PMD和CheckStyle的规范太严格,最后还是配置了一下.
 
 通过改正警告,感觉还是不错,至少可以说自己的代码可以通过工具的检测了.
 
 当然基础代码和项目代码还是不一样的,基础代码往往比较复杂,所以和普通项目代码的规范应该有所不同.有些规则只能用在普通代码上,用在基础类代码上往往没法处理.
 
其他

代码查错推荐使用Findbugs和PMD,代码书写规范推荐使用CheckStyle进行检查.这样不仅能查出一些基本的错误,也能提高项目的代码质量.对提高自己的代码水平也是非常好.

推荐项目组建立统一的规则,代码复查的时候就使用这些工具,省时省力.

实乃居家旅行,杀人越货必备之工具也.(因为肯定有人要骂你,呵呵,也是你找"差"的工具)

posted @ 2008-01-11 09:48 灵! 阅读(888) | 评论 (0)编辑 收藏

使用Lucene进行全文检索(一)---处理索引

 Lucene是一个全文检索的引擎,目前有Java和.Net 等几个版本.Java版本的网址是http://lucene.apache.org.相关的一个项目是车东的WebLucene: http://sourceforge.net/projects/weblucene.

 首先,基于一个简单的新闻系统,要想做全文检索.新闻系统的管理等在这里不在具体提出,下面列出新闻对象的类:
 
 注:程序用会到一些工具类,不在此列出,用户可以自己实现.
 
 

  package com.jscud.website.newsinfo.bean;
  
  
  import java.sql.Timestamp;
  
  import com.jscud.util.DateTime;
  import com.jscud.util.StringFunc;
  import com.jscud.website.newsinfo.NewsConst;
  
  
  /**
   * 一个新闻.
   *
   * @author scud(飞云小侠) http://www.jscud.com
   * 
   */
  public class NewsItem
  {
  
      private int nid; //新闻编号
  
      private int cid; //类别编号
  
      private String title;//标题
  
      private int showtype; //内容类型:目前支持url和html
  
      private String content;//内容
  
      private String url;//对应网址,如果内容类型是url的话
  
      private Timestamp addtime; //增加时间
  
      private int click; //点击数
     
      //对应的get,set函数,较多不在列出,可以使用工具生成
      //......
  
     
      /**
       * 按照类型格式化
       */
      public String getShowContent()
      {
          String sRes = content;
          if(showtype == NewsConst.ShowType_HTML)
          {
          }  
          return sRes;
      }
     
      public String getTarget()
      {
          if(showtype == NewsConst.ShowType_URL)
          {
              return "_blank";
          }
          else
              return "";       
      }
     
      /**
       * 静态Html文件的路径及其名字
       */
      public String getHtmlFileName()
      {
          int nYear = DateTime.getYear_Date(getAddtime());
          int nMonth =  DateTime.getMonth_Date(getAddtime());
             
          String sGeneFileName =
             "/news/" + getCid() + "/" + nYear + "/" + nMonth +"/" + getNid() + ".htm";
         
          return sGeneFileName;
      }
     
      /**
       * 静态Html文件的路径
       */
      public String getHtmlFilePath()
      {
          int nYear = DateTime.getYear_Date(getAddtime());
          int nMonth =  DateTime.getMonth_Date(getAddtime());
             
          String sGeneFilePath =
             getCid() + "_" + nYear + "_" + nMonth;
         
          return sGeneFilePath;
      }     
  } 


 
 可以看到,我们需要对标题和内容进行检索,为了这个目的,我们首先需要来研究一下lucene.
 
 在Lucene中,如果要进行全文检索,必须要先建立索引然后才能进行检索,当然实际工作中还会有删除索引和更新索引的工作.
 
 在此之前,介绍一个最基本的类(摘抄自http://www.blogjava.net/cap/archive/2005/07/17/7849.html):
 
 Analyzer 文件的分析器(听起来别扭,还是叫Analyzer好了)的抽象,这个类用来处理分词(对中文尤其重要,转换大小写(Computer->computer,实现查询大小写无关),转换词根(computers->computer),消除stop words等,还负责把其他格式文档转换为纯文本等.
 
 在lucene中,一般会使用StandardAnalyzer来分析内容,它支持中文等多字节语言,当然可以自己实现特殊的解析器.StandardAnalyzer目前对中文的处理是按照单字来处理的,这是最简单的办法,但是也有缺点,会组合出一些没有意义的结果来. 
 
 
 首先我们来了解建立索引,建立索引包含2种情况,一种是给一条新闻建立索引,另外的情况是在开始或者一定的时间给批量的新闻建立索引,所以为了通用,我们写一个通用的建立索引的函数:
 
 (一般一类的索引都放在一个目录下,这个配置可以在函数中定义,也可以写在配置文件中,通过参数传递给函数.)

    /**
     * 生成索引.
     *
     * @param doc 目标文档
     * @param indexDir 索引目录
     */
    public static void makeIndex(Document doc, String indexDir)
    {
        List aList = new ArrayList();
        aList.add(doc);
        makeIndex(aList, indexDir);
    }
 
    /**
     * 生成索引.
     *
     * @param doc 生成的document.
     * @param indexDir 索引目录
     */
    public static void makeIndex(List docs, String indexDir)
    {
        if (null == docs)
        {
            return;
        }       
        boolean indexExist = indexExist(indexDir);

        IndexWriter writer = null;
        try
        {
            StandardAnalyzer analyzer = new StandardAnalyzer();
           
            //如果索引存在,就追加.如果不存在,就建立新的索引.lucene要是自动判决就好了.
            if(indexExist)
            {
                writer = new IndexWriter(indexDir, analyzer, false);
            }
            else
            {
                writer = new IndexWriter(indexDir, analyzer, true);
            }

            //添加一条文档
            for (int i = 0; i < docs.size(); i++)
            {
                Document doc = (Document) docs.get(i);
                if (null != doc)
                {
                    writer.addDocument(doc);
                }
            }

            //索引完成后的处理
            writer.optimize();
        }
        catch (IOException e)
        {
            LogMan.warn("Error in Make Index", e);
        }
        finally
        {
            try
            {
                if (null != writer)
                {
                    writer.close();
                }
            }
            catch (IOException e)
            {
                LogMan.warn("Close writer Error");
            }
        }
    }



 可以看到,建立索引用到类是IndexWrite,它可以新建索引或者追加索引,但是需要自己判断.判断是通过IndexReader这个类来实现的,函数如下:

 

  /**
     * 检查索引是否存在.
     * @param indexDir
     * @return
     */
    public static boolean indexExist(String indexDir)
    {
        return IndexReader.indexExists(indexDir);
    }
 


 如果每次都是新建索引的话,会把原来的记录删除,我在使用的时候一开始就没有注意到,后来观察了一下索引文件,才发现这个问题.
 
 
 还可以看到,建立索引是给用户的Document对象建立索引,Document表示索引中的一条文档记录.那么我们如何建立一个文档那?以新闻系统为例,代码如下:
 

     /**
      * 生成新闻的Document.
      *
      * @param aNews 一条新闻.
      *
      * @return lucene的文档对象
      */
     public static Document makeNewsSearchDocument(NewsItem aNews)
     {
         Document doc = new Document();
 
         doc.add(Field.Keyword("nid", String.valueOf(aNews.getNid())));
 
         doc.add(Field.Text("title", aNews.getTitle()));
        
         //对Html进行解析,如果不是html,则不需要解析.或者根据格式调用自己的解析方法
         String content = parseHtmlContent(aNews.getContent());
 
         doc.add(Field.UnStored("content", content));
 
         doc.add(Field.Keyword("addtime", aNews.getAddtime()));
 
         //可以加入其他的内容:例如新闻的评论等
         doc.add(Field.UnStored("other", ""));
 
         //访问url
         String newsUrl = "/srun/news/viewhtml/" + aNews.getHtmlFilePath() + "/" + aNews.getNid()
                         + ".htm";
 
         doc.add(Field.UnIndexed("visiturl", newsUrl));
 
         return doc;
     }


 
 通过上面的代码,我们把一条新闻转换为lucene的Document对象,从而进行索引工作.在上面的代码中,我们又引入了lucene中的Field(字段)类.Document文档就像数据库中的一条记录,它有很多字段,每个字段是一个Field对象.
 
 从别的文章摘抄一段关于Field的说明(摘抄自http://www.blogjava.net/cap/archive/2005/07/17/7849.html):
 [quote]
    类型                               Analyzed Indexed Stored 说明
    Field.Keyword(String,String/Date)  N Y Y                    这个Field用来储存会直接用来检索的比如(编号,姓名,日期等)
    Field.UnIndexed(String,String)     N N Y                    不会用来检索的信息,但是检索后需要显示的,比如,硬件序列号,文档的url地址
    Field.UnStored(String,String)      Y Y N                    大段文本内容,会用来检索,但是检索后不需要从index中取内容,可以根据url去load真实的内容
    Field.Text(String,String)          Y Y Y                    检索,获取都需要的内容,直接放index中,不过这样会增大index
    Field.Text(String,Reader)          Y Y N                    如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略.
 [/quote]
 
 我们可以看到新闻的编号是直接用来检索的,所以是Keyword类型的字段,新闻的标题是需要检索和显示用的,所以是Text类型,而新闻的内容因为是Html格式的,所以在经过解析器的处理用,使用的UnStored的格式,而新闻的时间是直接用来检索的,所以是KeyWord类型.为了在新闻索引后用户可以访问到完整的新闻页面,还设置了一个UnIndexed类型的访问地址字段.
 
 (对Html进行解析的处理稍后在进行讲解)
 
 为一条新闻建立索引需要两个步骤:获取Document,传给makeIndex函数,代码如下:

    public static void makeNewsInfoIndex(NewsItem aNews)
    {
        if (null == aNews)
        {
            return;
        }
        makeIndex(makeNewsSearchDocument(aNews),indexDir);
    }  


 

 
 
 建立索引的工作就进行完了,只要在增加新闻后调用 makeNewsInfoIndex(newsitem); 就可以建立索引了.
 
 如果需要删除新闻,那么也要删除对应的索引,删除索引是通过IndexReader类来完成的:
 


    /**
     * 删除索引.
     * @param aTerm 索引删除条件
     * @param indexDir 索引目录
     */
    public static void deleteIndex(Term aTerm, String indexDir)
    {
        List aList = new ArrayList();
        aList.add(aTerm);
        deleteIndex(aList, indexDir);
    }

    /**
     * 删除索引.
     *
     * @param aTerm 索引删除条件.
     * @param indexDir 索引目录
     * 
     */
    public static void deleteIndex(List terms, String indexDir)
    {
        if (null == terms)
        {
            return;
        }
       
        if(!indexExist(indexDir)) { return; }

        IndexReader reader = null;
        try
        {
            reader = IndexReader.open(indexDir);
            for (int i = 0; i < terms.size(); i++)
            {
                Term aTerm = (Term) terms.get(i);
                if (null != aTerm)
                {
                    reader.delete(aTerm);
                }
            }
        }
        catch (IOException e)
        {
            LogMan.warn("Error in Delete Index", e);
        }
        finally
        {
            try
            {
                if (null != reader)
                {
                    reader.close();
                }
            }
            catch (IOException e)
            {
                LogMan.warn("Close reader Error");
            }
        }
    } 


 
 删除索引需要一个条件,类似数据库中的字段条件,例如删除一条新闻的代码如下:
 

     public static void deleteNewsInfoIndex(int nid)
     {
         Term aTerm = new Term("nid", String.valueOf(nid));
         deleteIndex(aTerm,indexDir);
     }   




 通过新闻的ID,就可以删除一条新闻.
 
 如果需要更新新闻,如何更新索引哪? 更新索引需要先删除索引然后新建索引2个步骤,其实就是把上面的代码组合起来,例如更新一条新闻:

     public static void updateNewsInfoIndex(NewsItem aNews)
     {
         if (null == aNews)
         {
             return;
         }
         deleteNewsInfoIndex(aNews.getNid());
         makeNewsInfoIndex(aNews);
     } 
 



 
 至此,索引的建立更新和删除就告一段落了.其中批量更新新闻的代码如下:
 (批量更新应该在访问人数少或者后台程序在夜间执行)

    public static void makeAllNewsInfoIndex(List newsList)
    {
        List terms = new ArrayList();
        List docs = new ArrayList();

        for (int i = 0; i < newsList.size(); i++)
        {
            NewsItem aitem = (NewsItem) newsList.get(i);
            if (null != aitem)
            {
                terms.add(new Term("nid", String.valueOf(aitem.getNid())));
                docs.add(makeNewsSearchDocument(aitem));
            }
        }

        deleteIndex(terms,indexDir);
        makeIndex(docs,indexDir);
    } 

posted @ 2008-01-11 09:47 灵! 阅读(356) | 评论 (0)编辑 收藏

分析/解析Html页面:HTML Parser的试用

最近在研究lucene的全文检索,在很多地方需要解析或者说分析Html内容或者Html页面,Lucene本身的演示程序中也提供了一个Html Parser,但是不是纯Java的解决方案.于是到处搜索,在网上找到了一个"HTMLParser".

网址是: http://htmlparser.sourceforge.net ,当前版本为1.5.

下载下来,试用一番,感觉不错,完全能满足lucene解析Html的需求.

过几天贴出lucene进行全文检索的代码.(检索本站的文章等).

试用代码如下,供大家参考:

package com.jscud.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.nodes.TextNode;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.htmlparser.visitors.HtmlPage;
import org.htmlparser.visitors.TextExtractingVisitor;

import com.jscud.util.LogMan; //一个日志记录类

/**
 * 演示了Html Parse的应用.
 *
 * @author scud http://www.jscud.com
 */

public class ParseHtmlTest
{

    public static void main(String[] args) throws Exception
    {
        String aFile = "e:/jscud/temp/test.htm";

        String content = readTextFile(aFile, "GBK");

        test1(content);
        System.out.println("====================================");

        test2(content);
        System.out.println("====================================");

        test3(content);
        System.out.println("====================================");

        test4(content);
        System.out.println("====================================");

        test5(aFile);
        System.out.println("====================================");

        //访问外部资源,相对慢
        test5("http://www.jscud.com");
        System.out.println("====================================");

    }

    /**
     * 读取文件的方式来分析内容.
     * filePath也可以是一个Url.
     *
     * @param resource 文件/Url
     */
    public static void test5(String resource) throws Exception
    {
        Parser myParser = new Parser(resource);

        //设置编码
        myParser.setEncoding("GBK");

        HtmlPage visitor = new HtmlPage(myParser);

        myParser.visitAllNodesWith(visitor);

        String textInPage = visitor.getTitle();

        System.out.println(textInPage);
    }

    /**
     * 按页面方式处理.对一个标准的Html页面,推荐使用此种方式.
     */
    public static void test4(String content) throws Exception
    {
        Parser myParser;
        myParser = Parser.createParser(content, "GBK");

        HtmlPage visitor = new HtmlPage(myParser);

        myParser.visitAllNodesWith(visitor);

        String textInPage = visitor.getTitle();

        System.out.println(textInPage);
    }

    /**
     * 利用Visitor模式解析html页面.
     *
     * 小优点:翻译了<>等符号
     * 缺点:好多空格,无法提取link
     *  
     */
    public static void test3(String content) throws Exception
    {
        Parser myParser;
        myParser = Parser.createParser(content, "GBK");

        TextExtractingVisitor visitor = new TextExtractingVisitor();

        myParser.visitAllNodesWith(visitor);

        String textInPage = visitor.getExtractedText();

        System.out.println(textInPage);
    }

    /**
     * 得到普通文本和链接的内容.
     *
     * 使用了过滤条件.
     */
    public static void test2(String content) throws ParserException
    {
        Parser myParser;
        NodeList nodeList = null;

        myParser = Parser.createParser(content, "GBK");

        NodeFilter textFilter = new NodeClassFilter(TextNode.class);
        NodeFilter linkFilter = new NodeClassFilter(LinkTag.class);

        //暂时不处理 meta
        //NodeFilter metaFilter = new NodeClassFilter(MetaTag.class);

        OrFilter lastFilter = new OrFilter();
        lastFilter.setPredicates(new NodeFilter[] { textFilter, linkFilter });

        nodeList = myParser.parse(lastFilter);

        Node[] nodes = nodeList.toNodeArray();

        for (int i = 0; i < nodes.length; i++)
        {
            Node anode = (Node) nodes[i];

            String line = "";
            if (anode instanceof TextNode)
            {
                TextNode textnode = (TextNode) anode;
                //line = textnode.toPlainTextString().trim();
                line = textnode.getText();
            }
            else if (anode instanceof LinkTag)
            {
                LinkTag linknode = (LinkTag) anode;

                line = linknode.getLink();
                //@todo 过滤jsp标签:可以自己实现这个函数
                //line = StringFunc.replace(line, "<%.*%>", "");
            }

            if (isTrimEmpty(line))
                continue;

            System.out.println(line);
        }
    }

    /**
     * 解析普通文本节点.
     *
     * @param content
     * @throws ParserException
     */
    public static void test1(String content) throws ParserException
    {
        Parser myParser;
        Node[] nodes = null;

        myParser = Parser.createParser(content, null);

        nodes = myParser.extractAllNodesThatAre(TextNode.class); //exception could be thrown here

        for (int i = 0; i < nodes.length; i++)
        {
            TextNode textnode = (TextNode) nodes[i];
            String line = textnode.toPlainTextString().trim();
            if (line.equals(""))
                continue;
            System.out.println(line);
        }

    }

    /**
     * 读取一个文件到字符串里.
     *
     * @param sFileName  文件名
     * @param sEncode   String
     * @return 文件内容
     */
    public static String readTextFile(String sFileName, String sEncode)
    {
        StringBuffer sbStr = new StringBuffer();

        try
        {
            File ff = new File(sFileName);
            InputStreamReader read = new InputStreamReader(new FileInputStream(ff),
                    sEncode);
            BufferedReader ins = new BufferedReader(read);

            String dataLine = "";
            while (null != (dataLine = ins.readLine()))
            {
                sbStr.append(dataLine);
                sbStr.append("\r\n");
            }

            ins.close();
        }
        catch (Exception e)
        {
            LogMan.error("read Text File Error", e);
        }

        return sbStr.toString();
    }

    /**
     * 去掉左右空格后字符串是否为空
     * @param astr String
     * @return boolean
     */
    public static boolean isTrimEmpty(String astr)
    {
        if ((null == astr) || (astr.length() == 0))
        {
            return true;
        }
        if (isBlank(astr.trim()))
        {
            return true;
        }
        return false;
    }

    /**
     * 字符串是否为空:null或者长度为0.
     * @param astr 源字符串.
     * @return boolean
     */
    public static boolean isBlank(String astr)
    {
        if ((null == astr) || (astr.length() == 0))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

}

 


posted @ 2008-01-11 09:45 灵! 阅读(322) | 评论 (0)编辑 收藏

JavaEE事务扫盲笔记之一扫扫到尾

1.资料

2.本地事务与分布式事务

  • 本地事务
    完全依赖于DB、JMS自身,,如直接调用jdbc中的conn.commit();这里没应用服务器什么事,所以也不支持多数据源的全局事务。
  • 分布式事务
    在JavaEE世界的事务在JTA、JTS规范和XA Sources之上实现。
    JTA是用户编程接口,JTS是服务器底层服务,两者一般由应用服务器自带实现,而atomikos 、JOTM 和JBoss Transaction 是专门搞局抢生意的。
    XA Sources其实先于JavaEE而存在,JDBC driver必须有javax.sql.XADataSource接口的实现类,否则所谓二阶段提交就是个伪能力。
    JavaEE除了支持JDBC和JMS外,还引入了JCA模型。JCA可以说是目前唯一可移植的插入JavaEE事务的资源模型,因此像JDO这类框架/Server就是靠乖乖出自己的JCA连接器来参与JavaEE事务的。

3.编程式模型

    手工调用jdbc的connection事务方法和使用JTA接口都属于编程式开发,在EJB中叫BMT(Bean管理事务)。
    JTA最重要的接口就是UserTransaction和它的六个方法-begin,commit,rollback,getStatus,setRollbackonly,setTransactionTimeout。
    程序需要UserTransaction时可以从JNDI领取,不过JNDI名随应用服务器不同而不同。EJB3里可以直接用个@Resource注入。

4.宣告式模型

    前面都是铺垫,这个才是主打的事务模型,如EJB的CMT(容器管理事务)和Sprin。

    其中EJB2.0,Spring1.0在部署描述符和applicationContext.xml中定义,而EJB3.0和Spring2.0则采用annotation。

4.1 事务类型

     这里JavaEE与Spring的定义基本相同:

  • Required:如果Context中有事务就加入,没有就自己创建一个。(最常用设置)
  • Mandatory:永远加入一个事务。如果当前Context没有事务,抛出异常。(那些不打算自己负责rollback事务的方法,必须加入到别人的事务,由别人来控制rollback)
  • RequiresNew:永远新建一个事务。(那些不管别人如何,自己必须提交事务的方法,比如审计信息是一定要写的)
  • Supports:如果有事务就加入,如果没有就算了。永远不会创建新事务。(一般用于只读方法,不会主动创建事务,但如果当前有事务就加入,以读到事务中未提交的数据)
  • NotSupported:永远不使用事务,如果当前有事务,挂起事务。(那些有可能抛异常但异常并不影响全局的方法)
  • Never:不能在有当前事务的情况下调用本方法。(生人勿近?)

      可见,Required是默认的设置,Supports是只读方法的最佳选择。

4.2 事务隔离级别

  • ReadUncommited:本事务可以看到另一事务未提交的数据。脏读。
  • ReadCommited:本事务只可以看到另一事务已提交的数据。不可重复读。
  • RepeatableRead:可重复读。在一个事务内,第一次读到的数据,在本事务没有提交前,无论另一个事务如何提交数据,本事务读到的数据都是不变的。
  • Serializable:串行化,同时只有一个事务能读相同的数据。

    级别越低越安全效率也越低。隔离级别需要相关资源支持,如重复读在Oracle里会降级为ReadCommited。Spring里默认的Default级别完全看数据源的脸色行事。

4.3 关于Rollback

    EJB里,想rollback只能sessionContext.setRollbackOnly(),或者抛出EJBException。(EJB3还可以annotation设置某些自定义Exception可以触发rollback)

    在Spring里,同样只会rollback unchecked exception(RuntimeExcption及子类),而checked exception(Exception及子类)是不会rollback的,除非你特别声明。

   @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW,rollbackFor = {MyException1.class,MyException2.class})

    因此所有在service层方法中用throws定义的Exception,都必须在事务定义中进行rollback设定。(请勿善忘)

    所有在service层方法中c被atch处理了的异常,又希望容器辅助rollback的话,必须重抛一个预定义的RuntimeException的子类。(请勿回望)

4.4 关于Spring

    Spring不希望编程式事务管理。
    Spring也不希望使用EJB CMT--CMT依赖于EJB而无法用于POJO,依赖于JTA全局事务对单数据源场景造成了浪费,而且rollback机制比较麻烦(必须为EJBException或手工setRollbackOnly())。
    因此Spring通过AOP实现了对POJO的整套宣告式事务体系;对jdbc,hibernate,jpa,jms等local数据源和JTA实现了统一的事务管理机制,而且支持本地资源与JTA在配置文件级的切换,而且改进了rollback机制。

   1)一个本地事务管理器:

<bean id="transactionManager"  class="org.springframework.orm.jpa.JpaTransactionManager">  <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean>

   2)Spring就会把请求都转发到应用服务器的JTA对象上(注意此时数据源也需要改为用JNDI从应用服务器获取)。

<bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

   3)应用服务器专有的类型的JTA事务管理器:

<bean id="myTxManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager"/>

posted @ 2008-01-11 09:44 灵! 阅读(343) | 评论 (0)编辑 收藏

包装你的Session,使Session对象化

 在Web开发中,经常需要使用Session来保存特定用户的信息,在我们的程序中很多地方散落着类似下面的语句:
 int userAge = (int)this.Session["UserAge"];
    
    我们知道,Session中存放的是键值对,键是string类型的,如果我们一不小心把上面的语句写成这样:
  int userAge = (int)this.Session["UseAge"];
    编译期不会发现这个错误,但运行时一定会抛出异常,这是在程序中直接操作Session可能引发的问题之一。另外,每次获取userAge的时候都要写代码进行强制转换,感觉很繁琐。我们需要一个解决方案来解决这些问题。我的做法是引入一个Session的包装,使之对象化、强类型化。就像接下来的例子一样:
public class SessionHelper
{
    
private HttpSessionState curSession;

    
public SessionHelper(HttpSessionState session)
    {
        
this.curSession = session;
    }

    
public static SessionHelper CreateInstance(HttpSessionState session)
    {        
        
return new SessionHelper(session);
    }


    
public string UserID
    {
        
get
        {
            
return this.curSession["UserID"].ToString();
        }
        
set
        {
            
this.curSession["UserID"= value ;
        }
    }

    
public int UserAge
    {
        
get
        {
            
return (int)this.curSession["UserAge"];
        }
        
set
        {
            
this.curSession["UserAge"= value ;
        }
    }

    
//某用户上传的所有图片
    public ArrayList PicList
    {
        
get
        {
            
if (this.curSession["PicList"== null)
            {
                
this.curSession["PicList"= new ArrayList();
            }

            
return (ArraayList)this.curSession["PicList"];
        }       
    }

    
//清空图片列表
    public void ClearAllPics()
    {
        
this.PicList.Clear();
    }  
}

    这样,我们用起来就非常方便了:
       SessionHelper sessionHelper = SessionHelper.CreateInstance(this.Session);
        ArrayList picList 
= sessionHelper.PicList;
        
//    处理picList中的图片
        sessionHelper.ClearAllPics();  

    引入这一层包装,可以使我们的程序的可读性、可维护性更好,而且将原来的一些运行期的错误提前到了编译期,这也是强类型带来的好处。

posted @ 2008-01-11 09:36 灵! 阅读(247) | 评论 (0)编辑 收藏

Java -- 在Eclipse上使用Hibernate

  最近一个项目要用Java做,一点都不熟啊。没办法,只好硬着头皮啃了,花了大半天的时间,终于在Eclipse上完成了第一个Hibernate例子。下面记录关键的步骤,权作笔记,以备日后查看。

 (1)下载Hibernate,并向项目中导入Hibernate。
     Project->Properies->Java Build Path->Libraries->Add External JARs...,选择Hibernate根目录下的hibernate3.jar,添加到项目中。
    接着,要将Hibernate下的lib文件夹下的所有文件都作为一个User Library添加到项目中,否则,如果仅仅添加hibernate3.jar,编译可以通过,运行却会抛出ClassNotDef的异常,因为hibernate3.jar依赖于Hibernate下的lib文件夹下的文件。

 2)我们的应用的后台数据库使用的是Oracle,所以首先要在例子项目中引入含有Oracle jdbc driver的包,classes12.jar。该jar文件位于oracle安装目录的jdbc\lib目录下。
    在Eclipse中,Project->Properies->Java Build Path->Libraries->Add External JARs...,选择classes12.jar,将其添加到项目中。
    
(3)生成hibernate.cfg.xml文件。
    通常Hibernate的配置文件和.hbm.xml文件都可以自动生成,这种自动生成的工具很多,我使用的是HibernateSynchronizer,它可以作为一个插件添加到Eclipse中。当HibernateSynchronizer插件正确加载后,我们可以向当前项目中添加Hibernate配置文件:File->New->Other->Hibernate->Hibernate Configuration File,出现如下界面:


  注意,Driver Class要选择针对Oracle的oracle.jdbc.driver.OracleDriver,而且Database URL的格式也要正确,如:
jdbc:oracle:thin:@10.8.8.221:1521:ORCL

    最好将hibernate.cfg.xml文件存放于项目的根目录下。

 4)生成.hbm.xml文件。File->New->Other->Hibernate->Hibernate Mapping File,出现如下界面:

     
    在填写完Password后,点击Refresh按钮,就会在Tables中列出所有可以访问的数据库表,然后选中要为其生成.hbm.xml文件的表,点击Finish,即会生成对应的.hbm.xml文件,比如我上面选择的是Mobileuser表,就会生成Mobileuser.hbm.xml文件。

(5)从.hbm.xml文件自动生成实体类。
    在Package Explorer中选中Mobileuser.hbm.xml文件,右键->Hibernate Synchronizer->Synchronize Files ,即可生成对应的实体类和DAO类。如果你仅仅想要实体类,那么可以在Project->Properies->Hibernate Synchronizer->Data Access Objects ,将“I would like to have DAOs created for me”的钩选项去掉即可。

(6)在hibernate.cfg.xml文件中添加对应的mapping resource。
    在Package Explorer中选中Mobileuser.hbm.xml文件,右键->Hibernate Synchronizer->Add Mapping Reference,即会在
hibernate.cfg.xml中自动生成如下配置:

<mapping resource="HibernateTest/Mobileuser.hbm.xml" />

(7)修改自动生成的hibernate.cfg.xml文件。需要在hibernate.cfg.xml文件的首部添加:

<!DOCTYPE hibernate-configuration PUBLIC
        
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    比较繁琐的是,每次自动修改hibernate.cfg.xml文件后,都要重新添加这个xml片断。

    万事具备,现在可以写个测试来检验一下了:

    //仅仅作为示例,没有进行异常处理
    public static void main(String[] args)
    {
        Configuration cfg 
= new Configuration().configure() ;        
        SessionFactory  sFactory 
= cfg.buildSessionFactory() ;        
        
        Session session 
= sFactory.openSession() ;
        Transaction tx 
= session.beginTransaction();
        Mobileuser user 
= (Mobileuser)session.load(Mobileuser.class , new Integer(2)) ;
        String age 
= user.getMobilenumber() ;
        
        System.
out.println(age) ;
        tx.commit();
        session.close() ;
    }

posted @ 2008-01-11 09:34 灵! 阅读(582) | 评论 (0)编辑 收藏

Java -- 在Eclipse上使用Spring

   在.NET上用的VS.NET+Spring.net+Nhibernate,到了Java平台上,自然对应着Eclipse+Spring+Hibernate。上一篇文章介绍了如何在Eclipse上使用Hibernate的入门,本文就简单介绍一下如何在Eclipse使用Spring。

    (1)首先,是下载Spring,可以从sourceforge上下载,http://sourceforge.net/projects/springframework。目前的最新的可以下载 spring-framework-1.2.8-with-dependencies.zip 。

    (2)然后,可以将Spring引入到你的项目中。
    先将spring-framework-1.2.8-with-dependencies.zip解压,将其中的spring.jar(dist目录中)、commons-logging.jar(lib\jakarta-commons目录)、log4j-1.2.13.jar(lib\log4j目录)这三个文件复制到的”D:\java\Spring\lib" 目录中,然后在Eclipse中建立一个“Spring”库,将那三个文件添加进“Spring”库中。

    (3)测试一下:
    新建两个类,Student和Book。
public class Book 
{
    
private int id = 0 ;
    
private String bookName ;
    
public String getBookName() {
        
return bookName;
    }
    
public void setBookName(String bookName) {
        
this.bookName = bookName;
    }
    
public int getId() {
        
return id;
    }
    
public void setId(int id) {
        
this.id = id;
    }
}

public class Student 
{
    
private int age = 0;    
    
private String name ;
    
private Book book ;

    
public int getAge() {
        
return age;
    }

    
public void setAge(int age) {
        
this.age = age;
    }
    
    
public String getName() {
        
return name;
    }

    
public void setName(String name) {
        
this.name = name;
    }

    
public Book getBook() {
        
return book;
    }

    
public void setBook(Book book) {
        
this.book = book;
    }
    
    
public String GetBookName()
    {
        
return this.book.getBookName() ;
    }    
}

    然后添加Spring配置文件bean.xml(bean.xml必须在CLASSPATH可以存取到的目录中):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" 
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    
<bean id="student" class="com.springTest.Student">
        
<property name="age">
            
<value>22</value>
        
</property>
        
<property name="name">
            
<value>Sky</value>
        
</property>
        
<property name="book" ref="book">            
        
</property>
    
</bean>
    
    
<bean id="book" class="com.springTest.Book">
         
<property name="id">
            
<value>1000</value>
        
</property>
        
<property name="bookName">
            
<value>战争与和平</value>
        
</property>
    
</bean>
</beans>

    最后的主程序:
    public static void main(String[] args) 
    {
        Resource res 
= new ClassPathResource("bean.xml");
        BeanFactory factory 
= new XmlBeanFactory(res);

        Student stu 
= (Student) factory.getBean("student");
        System.
out.println(stu.GetBookName());
    }
    运行后可以看到控制台输出--“战争与和平”。

    与Spring.net的使用基本完全一致(包括配置文件、BeanFactory的获取等),所以熟悉Spring.net的你过渡到Spring是非常平滑的。
    最后,Java中的属性实在是没有C#中的简洁,呵呵。

posted @ 2008-01-11 09:33 灵! 阅读(324) | 评论 (0)编辑 收藏

Java -- 在Eclipse上使用XFire开发WebService

   终于,使用Java完成了一个WebService的例子,其中的一个非常小的问题,折腾了我将近一天的时间。下面给出步骤,说明在Java平台上如何开发WebService。

    采用的工具:Eclipse3.1.2 + Tomcat5.5 + XFire1.1 。使用XFire开发WebService应该说非常的容易,只需要按照下面例子的步骤来做:

(1)在Eclipse中新建一个dynamic Web Project ,假设名为XFireZhuweiTest。

(2)导入XFire用户库。该库中应包含xfire-1.1目录下的xfire-all-1.1.jar文件,以及xfire-1.1\lib目录下的所有文件。

(3)将上述的XFire用户库中的所有文件拷贝到XFireZhuweiTest项目的WebContent\WEB-INF\lib目录下。

(4)修改WebContent\WEB-INF\web.xml配置文件的内容,下面是修改后web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>
    XFireZhuweiTest</display-name>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    
    <servlet>
         <servlet-name>XFireServlet</servlet-name>
         <servlet-class>
                 org.codehaus.xfire.transport.http.XFireConfigurableServlet
         </servlet-class>
     </servlet>
     
     <servlet-mapping>
         <servlet-name>XFireServlet</servlet-name>
         <url-pattern>/servlet/XFireServlet/*</url-pattern>
     </servlet-mapping>
 
     <servlet-mapping>
         <servlet-name>XFireServlet</servlet-name>
          <url-pattern>/services/*</url-pattern>
     </servlet-mapping>
    
</web-app>

    web.xml中添加的servlet映射表明,所有匹配“/services/*”的url请求全部交给org.codehaus.xfire.transport.http.XFireConfigurableServlet来处理。

(5)编写需要发布为WebService的Java类,这个例子中是一个非常简单的MathService.java。

package com.zhuweisky.xfireDemo;
public class MathService 
{
    
public int Add(int a ,int b)
    {
        
return a+b ;
    }
}

(6)在WebContent\META-INF目录下新建xfire文件夹,然后在xfire目录下添加一个XFire使用的配置文件services.xml,该配置文件中的内容反映了要将哪些java类发布为web服务。本例中的services.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xfire.codehaus.org/config/1.0">
    
<service>
      
<name>MathService</name>
      
<namespace>http://com.zhuweisky.xfireDemo/MathService</namespace>
      <serviceClass>com.zhuweisky.xfireDemo.MathService</serviceClass>
    
</service>
</beans>

    XFire会借助Spring来解析services.xml,从中提取需要发布为WebService的配置信息。

    很多文章介绍到这里就完了,然而当我按照他们所说的启动WebService ,然后通过http://localhost:8080/XFireZhuweiTest/services/MathService?wsdl 来访问服务描述时,却抛出了异常,说services.xml文件不存在--
“org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [META-INF/xfire/services.xml]; nested exception is java.io.FileNotFoundException: class path resource [META-INF/xfire/services.xml] cannot be opened because it does not exist”。  

(7)非常关键的一点,就是这个小难题花费了我将近一天的时间。
    在WebContent\WEB-INF目录下新建classes文件夹,然后需要将WebContent下的整个META-INF文件夹剪切到新建的classes文件夹下。
    到这里,项目的完整目录结构如下:



(8)在Package Explorer中选中XFireZhuweiTest项目,右键->Run As ->Run On Server,关联到你机器上的TomCat,然后会启动Tomcat,以启动web服务。(注意,在进行此步骤之前,请先停止TomCat) 

(9)在IE中输入 http://localhost:8080/XFireZhuweiTest/services/MathService?wsdl 会得到正确的web服务描述文档。

(10)测试刚发布的webService。我使用C#动态调用Web服务:

                //C#
                string url = "http://localhost:8080/XFireZhuweiTest/services/MathService" ;
                
object[] args ={1,2} ;
                
object result = ESFramework.WebService.WebServiceHelper.InvokeWebService(url ,"Add" ,args) ;
                MessageBox.Show(result.ToString());

    (关于C#动态调用Web服务,请参见这里

    执行后,弹出对话框,显示结果是3。

posted @ 2008-01-11 09:31 灵! 阅读(1779) | 评论 (0)编辑 收藏

通用分页存储过程(原创)

 这是我项目中使用的一个分页存储过程,具有很强的通用性。配合前台ASP.NET使用50万条数据基本感不到延迟。数据库为SQLServer2000。

1.分页存储过程

CREATE   procedure pagination

 @str_sql           varchar(1000) = '*',     -- 执行的SQL 不含Order by 内容  
 @str_orderfield    varchar(255)='''',       -- 排序的字段名 
 @page_size         int = 10,                     -- 页大小 
 @page_index        int = 0,                      -- 页码
 @order_type        int,                           -- 设置排序类型, 非 -1 值则降序 
 @total_count       int   output                 -- 返回记录总数, 非 0 值则返回 
as

---------------------
-- 获取指定页的数据--
---------------------

declare @strsql   varchar(5000)              -- 主语句
declare @strtmp   varchar(5000)             -- 临时变量
declare @strorder varchar(400)              -- 排序字串
declare @cruRow   int                            -- 当前行号
 

--执行总数统计
exec getRowCount @str_sql,@total_count output

set @strtmp =  ' select * from ' +
        '      (select top ' + convert(varchar(10),@page_size) + ' * from ' +
        '         (select top ' + convert(varchar(10),(@page_index + 1) * @page_size)  +' * from '+        -- N+1页
        '            ('+ @str_sql +') Src '

--排序方向
if @order_type !=0
 begin
 set @strsql= @strtmp +
       '          order by @str_orderfield asc) a ' +
       '       order by @str_orderfield desc)b' +
              ' order by @str_orderfield asc'
 end
else
 begin
 set @strsql= @strtmp +
       '          order by @str_orderfield desc) a ' +
       '       order by  @str_orderfieldasc)b' +
              ' order by  @str_orderfield desc'
 end

exec (@strsql)

GO

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

2.分页存储过程执行中用到的行数统计

create  procedure getRowCount
       @sql    nvarchar(2000),
       @count  int output
as
begin

--------------------
-- 获取数据总行数 --
--------------------

  declare @tmpsql nvarchar(2000)
  set @tmpsql='select @count=count(*)  from ('+ @sql +') a'

  execute sp_executesql @tmpsql,N'@count int output',@count output
 
end

GO

posted @ 2008-01-10 17:08 灵! 阅读(484) | 评论 (0)编辑 收藏

JAVA面试题汇总 四

31 构造器Constructor是否可被override?

  构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载Overloading。

32 是否可以继承String类?

  String类是final类故不可以继承。

33 当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

  不能,一个对象的一个synchronized方法只能由一个线程访问。

33  try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不

会被执行,什么时候被执行,在return前还是后? 
  会执行,在return前执行。

34 编程题: 用最有效率的方法算出2乘以8等於几? 

  2 << 3 

35 两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?

  不对,有相同的hash code。

36  当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

  是值传递。Java编程语言只由值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。

37  swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?

  switch(expr1)中,expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。 
38 Hashtable和HashMap

  Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现 
  HashMap允许将null作为一个entry的key或者value,而Hashtable不允许
还有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
  最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在
多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap就必须为之提供外同步。 
Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

posted @ 2008-01-10 17:04 灵! 阅读(210) | 评论 (0)编辑 收藏

JAVA面试题汇总 三

21 数组有没有length()这个方法? String有没有length()这个方法?

  数组没有length()这个方法,有length的属性。 
  String有有length()这个方法。

22 Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?

  方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写(Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。 
Overloaded的方法是可以改变返回值的类型。

23 Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?

  Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。 equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。 

24最常见到的runtime exception。

ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException,EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException,ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFORMatException, Secur 
ityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException 

25 error和exception有什么区别?

  error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 
  exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。

26 List, Set, Map是否继承自Collection接口?

     List,Set是 
     Map不是
    
27 abstract class和interface有什么区别?

  声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。 
  接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。 

28 abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?

  都不能

29 接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?

  接口可以继承接口。抽象类可以实现(implements)接口,抽象类可继承实体类,但前提是实体类必须有明确的构造函数。

30 启动一个线程是用run()还是start()?

  启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。

posted @ 2008-01-10 17:03 灵! 阅读(168) | 评论 (0)编辑 收藏

JAVA面试题汇总 二

11   &和&&的区别。 
  &是位运算符。&&是布尔逻辑运算符。 

12  HashMap和Hashtable的区别。

  都属于Map接口的类,实现了将惟一键映射到特定的值上。 
  HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。 


  Hashtable 类似于 HashMap,但是不允许 null 键和 null 值。它也比 HashMap 慢,因为它是同步的。

13 Collection 和 Collections的区别。

    Collection是个java.util下的接口,它是各种集合结构的父接口。
  Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。

14 什么时候用assert。

  断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为 true。 
如果表达式计算为 false,那么系统会报告一个 Assertionerror。它用于调试目的: 
assert(a > 0); // throws an Assertionerror if a <= 0 
断言可以有两种形式: 
assert Expression1 ; 
assert Expression1 : Expression2 ; 
  Expression1 应该总是产生一个布尔值。 
  Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试 
信息的 String 消息。 
  断言在默认情况下是禁用的。要在编译时启用断言,需要使用 source 1.4 标记:
  javac -source 1.4 Test.java 
  要在运行时启用断言,可使用 -enableassertions 或者 -ea 标记。 
  要在运行时选择禁用断言,可使用 -da 或者 -disableassertions 标记。 
  要系统类中启用断言,可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。 
  可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过,断言不应该用于验证传递给公有方法的参数,因为不管是否启用了断言,公有方法都必须检查其参数。不过,既可以在公有方法中,也可以在非公有方法中利用断言测试后置条件。另外,断言不应该以任何方式改变程序的状态。 

15 GC是什么? 为什么要有GC? (基础)。

  GC是垃圾收集器。Java 程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:
  System.gc() 
  Runtime.getRuntime().gc()

16 String s = new String("xyz");创建了几个String Object?

  两个对象,一个是“xyz”,一个是指向“xyz”的引用对象s。

17 Math.round(11.5)等於多少? Math.round(-11.5)等於多少?

  Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11;

18 short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?

  short s1 = 1; s1 = s1 + 1;有错,s1是short型,s1+1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正确。

19 sleep() 和 wait() 有什么区别? 搞线程的最爱

  sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级 (b)正在运行的线程因为其它原因而阻塞。 
  wait()是线程交互时,如果线程对一个同步对象x 发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。 

20 Java有没有goto? 
  Goto—java中的保留字,现在没有在java中使用。

posted @ 2008-01-10 17:02 灵! 阅读(180) | 评论 (0)编辑 收藏

JAVA面试题汇总 一

1. Java中的异常处理机制的简单原理和应用。 

当Java程序违反了Java的语义规则时,Java虚拟机就会将发生的错误表示为一个异常。违反语义规则包括2种情况。一种是Java类库内置的语义检查。例如数组下标越界,会引发IndexOutOfBoundsException;访问null的对象时会引发NullPointerException。另一种情况就是Java允许程序员扩展这种语义检查,程序员可以创建自己的异常,并自由选择在何时用throw关键字引发异常。所有的异常都是java.lang.Thowable的子类。 

2. Java的接口和C++的虚类的相同和不同处。 

由于Java不支持多继承,而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性,现有的单继承机制就不能满足要求。与继承相比,接口有更高的灵活性,因为接口中没有任何实现代码。当一个类实现了接口以后,该类要实现接口里面所有的方法和属性,并且接口里面的属性在默认状态下面都是public static,所有方法默认情况下是public.一个类可以实现多个接口。 

3. 垃圾回收的优点和原理。并考虑2种回收机制。 

Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。 

4.线程同步的方法。 

wait():使一个线程处于等待状态,并且释放所持有的对象的lock。 

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。 

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切地唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 

Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。 

5. Error与Exception有什么区别? 

Error表示系统级的错误和程序不必处理的异常, 

Exception表示需要捕捉或者需要程序进行处理的异常。 

6. 在java中一个类被声明为final类型,表示了什么意思? 

   表示该类不能被继承,是顶级类。
  
7 heap和stack有什么区别。

   栈是一种线形集合,其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。堆是栈的一个组成元素。

8谈谈final, finally, finalize的区别。

  final—修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。 
  finally—异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。 
  finalize—方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。

9  Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?

  匿名的内部类是没有名字的内部类。不能extends(继承) 其它类,但一个内部类可以作为一个接口,由另一个内部类实现。

10  Static Nested Class 和 Inner Class的不同
Nested Class (一般是C++的说法),Inner Class (一般是JAVA的说法)。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。
  注: 静态内部类(Inner Class)意味着1创建一个static内部类的对象,不需要一个外部类对象,2不能从一个static内部类的一个对象访问一个外部类对象  .

posted @ 2008-01-10 17:02 灵! 阅读(207) | 评论 (0)编辑 收藏

汉字编码转换

汉字编码转换

相关:

UNICODE 是为了处理包括中文,日文等字符而提出的一种通用的字符集。最初的UNICODE为双字节字符集,即16位编码,能够包括65,536个字符。但这样的容量并不能满足所有需要,因此,现在的UNICODE已经扩展到4个字节,能够容纳1,112,064 个字符,而这些在16位之后的扩展背称为增补字符。

UTF-32 UTF-16 UTF-8 Unicode 标准的编码字符集的字符编码方案。

UTF-8 使用一至四个字节的序列对编码 Unicode 代码点进行编码

UTF-8 使用一至四个字节的序列对编码 Unicode 代码点进行编码。U+0000 至 U+007F 使用一个字节编码,U+0080 至 U+07FF 使用两个字节,U+0800 至 U+FFFF 使用三个字节,而 U+10000 至 U+10FFFF 使用四个字节。UTF-8 设计原理为:字节值 0x00 至 0x7F 始终表示代码点 U+0000 至 U+007F(Basic Latin 字符子集,它对应 ASCII 字符集)。这些字节值永远不会表示其他代码点,这一特性使 UTF-8 可以很方便地在软件中将特殊的含义赋予某些 ASCII 字符。

  GB2312(1980 ) 一共收录了 7445 个字符,包括 6763 个汉字和 682 个其它符号。汉字区的内码范围高字节从 B0-F7 ,低字节从 A1-FE ,占用的码位是 72*94=6768 。其中有 5 个空位是 D7FA-D7FE 。当然也可以表示数字和字符(一个字节,与 ASCII 表示相同)。

要读取一个以 GB2312 编码的包含汉字、数字、字母的二进制文件。

String strName =Encoding.GetEncoding("gb2312").GetString(name,0,i) ;

// name 是读取的二进制数组。

这样就能将二进制数组转换为 汉字、数字或字母

同样:也可以将包含汉字、数字、字母的字符串转换为 二进制数组保存到 二进制文件。

String unicodeString =   " 备用43E";

Byte[] encodedBytes = Encoding.GetEncoding("gb2312").GetBytes(unicodeString);

当然也可以进行二进制数组与UNICODE,UTF-8等编码方式的转换

Byte[] encodedBytes = utf8.GetBytes(unicodeString);

String decodedString = utf8.GetString(encodedBytes);

UnicodeEncoding unicode = new UnicodeEncoding();

Byte[] encodedBytes = unicode.GetBytes(unicodeString);

String decodedString = unicode.GetString(encodedBytes);

posted @ 2008-01-10 11:46 灵! 阅读(550) | 评论 (0)编辑 收藏

JBuilderX快捷键

F1 Help
F3 查找下一个

shift + F3 反向查找下一个

ctrl + F 查找

ctrl + p 路径查找

ctrl + F4 运行到当前位置
ctrl+F6在jbuilder中,切换不同的文件
Ctrl+F4关闭正在编辑的文件,
Ctrl + F5切换工程


F5 设置断点

F7 跟入

F8 单步

F9 运行

ctrl + F9 编译工程

shift + F9 调试模式运行

ctrl + shift + F9 编译当前类

ctrl + H 显示本类成员

ctrl + J 显示模板
ctrl + shift + J 模板编辑

ctrl + shift + c 自动完成错误捕捉代码

ctrl + Enter /mouseClick 当前关键字追踪

ctrl + shit + 数字(0-9) 设置/去除标签

ctrl + 数字(0-9) 返回标签位置

ctrl + alt + -> / <- 返回最近访问点

ctrl + -> / <- 光标跳过当前单位词

ctrl + shift + -> / <- 选择单位词

ctrl + 上下方向键 滚动屏幕

ctrl + home/end/pageup/pagedown

shift +盘方向键/home/end/pageup/pagedown 选择

ctrl + e 增量式查找

ctrl + w 捕捉离光标最近的单词

ctrl + shift + h 参数查找

ctrl + alt + space class insight

ctrl + F4 关闭当前类

ctrl + shift + F4 关闭(显示选择)

ctrl + B 切换窗体

ctrl + F6 切换窗体

ctrl + alt + p/c/m/z/s 视图开关

ctrl + o/n/c/v/x/p/a

ctrl + g 到指定行

Tab 格式化宿进

ctrl + / 注释/去除注释选择行

ctrl + shift + I 宿进

ctrl + shift + U 反向宿进

posted @ 2008-01-10 11:45 灵! 阅读(185) | 评论 (0)编辑 收藏

常用到的Eclipse快捷键

常用到的Eclipse快捷键

Ctrl+s                 存盘

Ctrl+/                  注释(取消)代码

Ctrl+shift+/       注释代码块

Ctrl+shift+\       取消代码块

Alt+/                  代码辅助/调出IF语句等程序模板:使用方法:打出if,按ALT+/

Ctrl+D             删除一行 

Ctrl+Shift+D   debug 模式里显示变量值

Ctrl+1               快速修复  

Ctrl+Shift+f     代码格式化

Ctrl+Shift+o    整理导入  

Ctrl+f6             切换窗口  

ctrl+shift+M   导入未引用的包

ctrl+w             关闭单个窗口

F3                      跳转到类、变量的声明

F11                   运行上次程序

Ctrl + F11        调试上次程序

Alt +                 回下一个编辑点

ctrl+shift+T   查找工程中的类

Alt-left arrow :    在导航历史记录(Navigation History)中后退。就像Web浏览器的后退按钮一样,在利用F3跳转之后,特别有用。(用来返回原先编译的地方)

Alt+right arrow :   导航历史记录中向前。

Control+Q :         回到最后依次编辑的地方。这个快捷键也是当你在代码中跳转后用的。特别是当你钻的过深,忘记你最初在做什么的时候。

ctrl+Alt+down     复制鼠标所在行到下一行

Alt+down arrow :    将一行或多行向下移动。

Alt+up arrow      将一行或多行 向上移动。

posted @ 2008-01-10 11:44 灵! 阅读(252) | 评论 (0)编辑 收藏

迭代器

迭代这个名词对于熟悉Java的人来说绝对不陌生。我们常常使用JDK提供的迭代接口进行java collection的遍历:

Iterator it = list.iterator();
while(it.hasNext()){
 //using “it.next();”do some businesss logic
}

  而这就是关于迭代器模式应用很好的例子。

  二、 定义与结构

  迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。

  从定义可见,迭代器模式是为容器而生。很明显,对容器对象的访问必然涉及到遍历算法。你可以一股脑的将遍历方法塞到容器对象中去;或者根本不去提供什么遍历算法,让使用容器的人自己去实现去吧。这两种情况好像都能够解决问题。

  然而在前一种情况,容器承受了过多的功能,它不仅要负责自己“容器”内的元素维护(添加、删除等等),而且还要提供遍历自身的接口;而且由于遍历状态保存的问题,不能对同一个容器对象同时进行多个遍历。第二种方式倒是省事,却又将容器的内部细节暴露无遗。

  而迭代器模式的出现,很好的解决了上面两种情况的弊端。先来看下迭代器模式的真面目吧。

  迭代器模式由以下角色组成:

  1) 迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。

  2) 具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。

  3) 容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。

  4) 具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。

  迭代器模式的类图如下:


  从结构上可以看出,迭代器模式在客户与容器之间加入了迭代器角色。迭代器角色的加入,就可以很好的避免容器内部细节的暴露,而且也使得设计符号“单一职责原则”。

  注意,在迭代器模式中,具体迭代器角色和具体容器角色是耦合在一起的——遍历算法是与容器的内部细节紧密相关的。为了使客户程序从与具体迭代器角色耦合的困境中脱离出来,避免具体迭代器角色的更换给客户程序带来的修改,迭代器模式抽象了具体迭代器角色,使得客户程序更具一般性和重用性。这被称为多态迭代。

  三、 举例

  由于迭代器模式本身的规定比较松散,所以具体实现也就五花八门。我们在此仅举一例,根本不能将实现方式一一呈现。因此在举例前,我们先来列举下迭代器模式的实现方式。

  1.迭代器角色定义了遍历的接口,但是没有规定由谁来控制迭代。在Java collection的应用中,是由客户程序来控制遍历的进程,被称为外部迭代器;还有一种实现方式便是由迭代器自身来控制迭代,被称为内部迭代器。外部迭代器要比内部迭代器灵活、强大,而且内部迭代器在java语言环境中,可用性很弱。

  2.在迭代器模式中没有规定谁来实现遍历算法。好像理所当然的要在迭代器角色中实现。因为既便于一个容器上使用不同的遍历算法,也便于将一种遍历算法应用于不同的容器。但是这样就破坏掉了容器的封装——容器角色就要公开自己的私有属性,在java中便意味着向其他类公开了自己的私有属性。

  那我们把它放到容器角色里来实现好了。这样迭代器角色就被架空为仅仅存放一个遍历当前位置的功能。但是遍历算法便和特定的容器紧紧绑在一起了。

  而在Java Collection的应用中,提供的具体迭代器角色是定义在容器角色中的内部类。这样便保护了容器的封装。但是同时容器也提供了遍历算法接口,你可以扩展自己的迭代器。

  好了,我们来看下Java Collection中的迭代器是怎么实现的吧。

//迭代器角色,仅仅定义了遍历接口

public interface Iterator {
 boolean hasNext();
 Object next();
 void remove();
}

//容器角色,这里以List为例。它也仅仅是一个接口,就不罗列出来了
//具体容器角色,便是实现了List接口的ArrayList等类。为了突出重点这里指罗列和迭代器相关的内容
//具体迭代器角色,它是以内部类的形式出来的。AbstractList是为了将各个具体容器角色的公共部分提取出来而存在的。

public abstract class AbstractList extends AbstractCollection implements List {
……
//这个便是负责创建具体迭代器角色的工厂方法
public Iterator iterator() {
 return new Itr();
}

//作为内部类的具体迭代器角色

private class Itr implements Iterator {
 int cursor = 0;
 int lastRet = -1;
 int expectedModCount = modCount;

 public boolean hasNext() {
  return cursor != size();
 }

 public Object next() {
  checkForComodification();
  try {
   Object next = get(cursor);
   lastRet = cursor++;
   return next;
  } catch(IndexOutOfBoundsException e) {
   checkForComodification();
   throw new NoSuchElementException();
  }
 }

 public void remove() {
  if (lastRet == -1)
   throw new IllegalStateException();
   checkForComodification();

  try {
   AbstractList.this.remove(lastRet);
   if (lastRet < cursor)
    cursor--;
   lastRet = -1;
   expectedModCount = modCount;
  } catch(IndexOutOfBoundsException e) {
   throw new ConcurrentModificationException();
  }
 }

 final void checkForComodification() {
  if (modCount != expectedModCount)
   throw new ConcurrentModificationException();
 }
}

  至于迭代器模式的使用。正如引言中所列那样,客户程序要先得到具体容器角色,然后再通过具体容器角色得到具体迭代器角色。这样便可以使用具体迭代器角色来遍历容器了……

  四、 实现自己的迭代器

  在实现自己的迭代器的时候,一般要操作的容器有支持的接口才可以。而且我们还要注意以下问题:

  在迭代器遍历的过程中,通过该迭代器进行容器元素的增减操作是否安全呢?

  在容器中存在复合对象的情况,迭代器怎样才能支持深层遍历和多种遍历呢?

  以上两个问题对于不同结构的容器角色,各不相同,值得考虑。

  五、 适用情况

  由上面的讲述,我们可以看出迭代器模式给容器的应用带来以下好处:

  1) 支持以不同的方式遍历一个容器角色。根据实现方式的不同,效果上会有差别。

  2) 简化了容器的接口。但是在java Collection中为了提高可扩展性,容器还是提供了遍历的接口。

  3) 对同一个容器对象,可以同时进行多个遍历。因为遍历状态是保存在每一个迭代器对象中的。

  由此也能得出迭代器模式的适用范围:

  1) 访问一个容器对象的内容而无需暴露它的内部表示。

  2) 支持对容器对象的多种遍历。

  3) 为遍历不同的容器结构提供一个统一的接口(多态迭代)。

  六、 总结

  迭代器模式在我们的应用中很广泛,希望本文能帮助你理解它。如有不对之处,还请不吝指正。

posted @ 2008-01-10 11:42 灵! 阅读(196) | 评论 (0)编辑 收藏

Hibernate 3新增XML关系持久性介绍(转)

非常感谢最近发布的Hibernate 3中的XML持久性特性,Java开发者现在拥有了一个框架组件,它为易于实现的对象关系(OR)和XML持久性提供了高效的和一致的方法。

  Hibernate的易用性、高性能和对象关系持久性等高级特性给IT界带来了很大的惊喜。 Hibernate的最新版本(版本3,3月29日发布的)给产品API带来了一个重要的新特性:XML持久性。有了Hibernate 3之后,Java应用程序开发者可以轻易地把XML文档合并到关系型数据库中。

  这个新特性应该明确地告诉已有的Hibernate开发者,因为它也遵循POJO(纯的旧Java对象)相同的一致性方法,需要学习的知识最少。XML持久性的优点也应该介绍给新用户。本文讲解的是Hibernate 3持久性方法。

  XML持久性为什么重要

  大多数大型商业数据库都支持某种形式的本地XML持久性。由于XML持久性是一个相对较新的机制--即使对大型厂商也是如此,这个领域中的标准还在不断地浮现。其结果是,为了把无处不在的关系型持久性机制与日益增长的XML解决方案集成在一起,架构师必须依赖厂商特定的特性或者实现定制的XML持久性框架组件。这两个选择都没有太大的吸引力。厂商特定的特性不是普及的,因为可能产生厂商封锁(lock-in),而定制的框架组件实现可能耗费大量的时间和财力,导致代码难于维护。

  在OR(对象关系)持久性方面,Hibernate XML持久性是一个自然而然的解决方案。它可以跨越Hibernate支持的所有关系型平台(如虚拟的或真实的关系型平台)移动,允许自由的迁移对象、基于XML的应用程序和集成解决方案而不用担心下层的关系型实现方法。

  体系结构的细节信息

  Hibernate是一个良好架构的框架组件,它无缝地利用了本地的环境,不需要用户进行任何特殊的干涉或安装操作。从一个数据库切换到另外一个数据库通常只需要改变驱动程序,并配置Hibernate(在线配置设置信息)来使用另外一种数据库语言。

  Hibernate利用dom4j框架组件进行XML的分析和维护。如果需要完全利用Hibernate的XML特性,你就必须对dom4j非常熟悉。一般来说,你会发现dom4j比Java提供的JAXP或与JAXP兼容的XML分析器要容易使用一些。它要求我们学习的相关知识较少,并且利用最少的dom4j知识你就能够高效率地使用Hibernate XML持久性。

实际例子:价格目录同步

  通用的电子商务案例可以演示XML关系持久性机制的作用。我们来考虑一个示例,在这个例子中XML集成了在线零售商和供应商之间的产品标价目录。

  该电子目录包含了已标价的产品列表。在线商店销售产品,通过自己的存货清单来管理(类似于Amazon与Toys-R-Us和运动产品商店之间的关系)。为了精确地和有效地反映价格的变化,在线零售商必须频繁地接收产品价格信息。它把这些信息存放为XML文档,如下所示:

<products>
<product prod_id="3" sku="100101">
<description>Athlete mode body fat scale</description>
<list_price>100.00</list_price>
<drop_price>60.00</drop_price>
</product>
<product prod_id="4" sku="100102">
<description>Thermometer</description>
<list_price>20.00</list_price>
<drop_price>11.00</drop_price>
</product>
</products>

  全面的主要的产品价格列表存储在数据库中,如下所示:

CREATE TABLE PRODUCT
(
id INT UNIQUE NOT NULL,
description VARCHAR(45) NOT NULL,
sku VARCHAR(45) UNIQUE NOT NULL,
list_price FLOAT,
base_price FLOAT,
order_price FLOAT,
CONSTRAINT PK_PRODUCT PRIMARY KEY (id )
)

  在线零售商通过已有的OR映射提供定价目录的Web表现形式,定价产品都表现为demo.Product Java对象:

/** Product对象表现了定价目录项*/
public class Product {
int id;
String sku;
String description;
Double listPrice;
Double basePrice;
Double orderPrice;

  这些对象按照下面的方式映射(为了清楚,我们列出了列名,尽管在属性和列名相匹配的时候Hibernate可以自动地把属性映射为列名):

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="demo">
<class name="Product"
table="product"
node="product">
<id name="id"
type="int"
node="@prod_id"
column="id">
</id>
<property name="sku" node="@sku" column="sku" not-null="true"/>
<property name="description" node="description" column="description" not-null="true"/>
<property name="listPrice" node="list_price" column="list_price" />
<property name="basePrice" node="drop_price" column="base_price"/>
<property name="orderPrice" column="order_price"/>
</class>
</hibernate-mapping>

  在这种情况下,Hibernate的XML关系持久性就非常方便了。由于该电子商务应用程序接收了包含产品价格更新的XML,它就利用Hibernate的XML持久性机制把这些XML写入到产品数据库中。Hibernate提供了几种XML持久性选择,包括Hibernate的saveOrUpdate方法:

document = saxReader.read(inputXML);
List users = document.selectNodes("//product");
try {
 Session session = ibernateUtil.sessionFactory.openSession();
 Transaction transaction = session.beginTransaction();
 Session dom4jSession = session.openSession(EntityMode.DOM4J);
 Iterator iter = users.iterator();
 while (iter.hasNext()) {
  Object next = iter.next();
  dom4jSession.saveOrUpdate("demo.Product", next );
 }// end while
transaction.commit();
session.close(); 

  XML映射语法

  上面的例子中使用的映射文件不用于Hibernate 2的映射文件。Hibernate 3引入了几种专门用于XML持久性的新映射类型。

  主要的新映射属性是节点(node),它与被映射的XML文档中的一个元素或文档中的属性相关联。

  一个"节点"可能表现为下面的某种映射:

  · "element-name(元素名)":在例子中,<product></product>元素会被表示为node="product"。

  · "@attribute-name(属性名)":在例子中,node="@sku"会被映射为XML属性<product sku="1001001">。

  · ".(句点)":映射为元素的父元素(例如<products>就<product>是的父元素)。

  · "element-name/@attribute-name(元素名/属性名)":映射为命名元素的属性(product/@sku)。

  XML持久性并非Hibernate的主要任务

  Hibernate 3框架组件高效率地实现了目前最通用的一些方法(除了LDAP之外)。Java社团现在拥有了一套框架组件,它为易于实现的OR和XML持久性提供了高效率的和一致性的方法。

  在我们知道上面一些内容之后,了解Hibernate项目的任务是很重要的。尽管Hibernate 3的XML特性非常有用、有吸引力,但是它们并不是用来代替最流行的XML编组(marshalling)和转换(transformation)框架组件的。不管它的OR映射解决方案多么完善,我们都不应该期待Hibernate成为主流的XML维护框架组件(根据Hibernate的作者Gavin King在TheServerSide Java Symposium 2005上的发言)。

  由于这个原因,你应该把XML持久性特性看作是已有的强大的Hibernate框架组件的有用的扩展,它允许你轻易地把现在流行的其它的数据表现机制合并到自己的应用程序中。但是,如果你必须处理复杂的集成和转换情况,最好去寻找其它的XML专用的框架组件。

posted @ 2008-01-10 11:41 灵! 阅读(250) | 评论 (0)编辑 收藏

hibernate入门

一、首先学习hibernate.cfg.xml配置文件的具体配置
<?xml version="1.0" encoding="UTF-8"?>

<!--指定该文件的官方dtd-->
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" >
<hibernate-configuration>
  <session-factory>
    <!-- 显示sql语言 -->
    <property name="show_sql">true</property>
    <!-- sql语言 -->
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    <!-- jdbc驱动程式 -->
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <!-- jdbc url -->
    <property name="connection.url">jdbc:mysql://localhost:3306/test</property>
    <!-- 数据库用户名 -->
    <property name="connection.username">root</property>
    <!-- 数据库密码 -->
    <property name="connection.password">wyq</property>
    <!-- C3P0连接池设定 -->
    <!--最小连接数-->
    <property name="c3p0.min_size">5</property>
    <!--最大连接数-->
    <property name="c3p0.max_size">20</property>
   <!--延迟所允许的时间-->
    <property name="c3p0.timeout">1800</property>
   <!--缓存所允许的最大连接数-->
    <property name="c3p0.max_statements">50</property>
    <!-- 每隔100笔资料送入资料库,清除缓存(定期清除缓存,减小压力) -->
    <property name="hibernate.jdbc.batch_size">100</property>
    <!-- 设定事务管理的工厂类 -->
    <property name="hibernate.transaction.factiory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
   <mapping resource="com/wyq/hibernate/pojo/User.hbm.xml"/>
   <mapping resource="com/wyq/hibernate/pojo/TUser.hbm.xml"/>
   <mapping resource="com/wyq/hibernate/pojo/Room.hbm.xml"/>
  </session-factory>
</hibernate-configuration>
需要的jar包有c3p0.jar,hibernate3.jar,数据库.jar,log4j.jar


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

1、读取配置文件获得连接
   读取hibernate.cfg.xml配置文件,hibernate.cfg.xml文件放在Classpath下,使用下面的方式读入该文件
      //Configuration 负责管理hibernate配置信息
      Configuration config=new Configuration().configure();
      //根据config建立SessionFactory
      //SessionFactory用于建立Session
      SessionFactory sessionFactory=config.buildSessionFactory();
      //开启session,相当于jdbc的Connection
      session = sessionFactory.openSession();

2、Criteria 基本资料查询
(1)标准查询:
      //创建查询标准
      Criteria criteria=session.creteCriteria(User.class);
      //查询条件
      criteria.add(Expression.eq("name","caterpillar"));
************************************************************************************
Expression.eq(String s1,String s2)---------->相等s1=s2
Expression.allEq(Map map)    --------------->多个属性-值对应关系,多个Expression.eq叠加
Expression.gt(String s1,String s2)----------->大于s1>s2
Expression.ge(String s1,String s2)----------->大于等于s1>=s2
Expression.lt(String s1,String s2)------------>小于s1<s2
Expression.le(String s1,String s2)------------>小于等于s1<=s2
Expression.between(String s1,int s2,int s3)--->s2<s1<s3
Expression.like(String s1,String s2)------------>s1 like s2
比较2个属性
Expression.eqProperty(String s1,String s2)--->s1=s2
Expression.gtProperty(String s1,String s2)---->s1>s2
Expression.geProperty(String s1,String s2)---->s1>=s2
Expression.ltProperty(String s1,String s2)----->s1<s2
Expression.leProperty(String s1,String s2)----->s1<=s2
Expression.and()----->Expression.and(Expression.eq("String s1,String s2"),Expression.eq(String s3,String s4))
Expression.or()
************************************************************************************
(2)高级查询
一、可以使用Criteria进行查询,并用order对结果进行排序。
//设置从第几条开始取的记录
criteria.setFirstResult(100);
//最多取的几条记录
criteria.setMaxResults(20);
//对结果进行排序
criteria.addOrder(Order.asc(String s1));
criteria.addOrder(Order.desc(String s2));

二、可以对查询结果进行统计操作,使用Projections的rowCount(),count(),max(),min(),countDistinct()等方法:
例如:criteria.setProjection(Projections.max("age"));

三、还可以用Projections的groupProperty()来对结果进行分组
例如:criteria.setProjection(Projections.groupProperty("age"));

(***)四、结合统计与分组的功能,可以用ProjectionList
例如:ProjectionList projectionList =Projections.projectionList();
            projectionList.add(Projections.groupProperty("age"));
            projectionList.add(Projections.rowCount());
            criteria.setProjection(projectionList);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      //查询所有记录
      List users=criteria.list();
      Iterator iterator=users.iterator();
      while(iterator.hasNext()){
         User user=(User)iterator.next();
         System.out.println(user.getId()+"\t"+user.getName()+"/"+user.getAge());
}
3、criteria的增、删、改(还不完善)
在用到增、删、改时,必须先声明事务
增加:
  Transaction tx = session.beginTransaction();//Transaction表示一组会话操作
  session.save(user);//将事物映射到数据库进行存储
  tx.commit();
  session.close();
删除:
  Session session=this.getSession();
  User user=(User)session.get(User.class, new Integer(1));
  Transaction tx = session.beginTransaction();//Transaction表示一组会话操作
  session.delete(user);//将事物映射到数据库进行存储
  tx.commit();
  session.close();

修改:
  Session session=this.getSession();
  User user =(User)session.get(User.class,new Integer(2));//创建持久化的事物
  user.setName("wyqqqqqqqqqq");
  user.setAge(new Integer(30));
  Transaction tx = session.beginTransaction();//Transaction表示一组会话操作
  session.update(user);//将事物映射到数据库进行存储
  tx.commit();
  session.close();
----------------------------------------------------------------------------------------------------------------------
一、Query查询可以先设定查询参数,之后通过set等方法,将指定的参数值添入.还可以使用命名参数
Session session = sessionFactory.openSession();
Query query = session.createQuery("select user.name from User as user where user.age>?(
:minAge )");
query.setInteger(0,25);
query.setInteger("minAge",25);
List names=query.list();
Iterator iterator = names.iterator();
while(iterator.hasNext()){
      System.out.println(iterator.next());
}
session.close();

二、如果查询整个表直接使用from User如果针对某个属性使用select user.name from User as user
使用hql可以更接近我们平时的jdbc编程,和把sql语句写在程序中差不多,另外,也可以将sql语句写在配置文件中。



--------------------------------------------------------------------------------------------------------------------------
   多表关联
一、多对一进行关联(多个学生对应同一间宿舍)---学生是主体,宿舍是附体,关联关系<many-to-one>在主体学生中设置,在学生类中设置宿舍类,由于宿舍类只有一个可以直接用类来设置,在映射学生类(User)中包含宿舍这个类(Room),在映射配置文件(User.hbm.xml)中定义

<many-to-one name="room" column="room_id" cascade="save-update" class="com.wyq.hibernate2.Room"></many-to-one>

哪个是主体类就在哪个配置文件定义关联关系.

cascade属性:表示关联对象的持久化,该属性也要设置在主体中,作用就是当主控方执行操作时,关联对象(被动方)是否同步执行同一操作.
cascade的值:all:表示所有情况下都进行级联操作.
                        none:所有情况下都不进行级联操作
                        save-update:在执行save-update时进行级联操作.
                        delete:在执行delete时进行级联操作.

注意:使用cascade自動持久化時,會先檢查被關聯物件的id屬性,未被持久化的物件之id屬性是由unsaved-value決定,預設是null,如果您使用long這樣的原生型態(primitive type)時,則必須自行指定預設值.

例如:<id name="id" column="ROOM_ID" unsaved-value="0">
            <generator class="increment"/>
        </id>

如果您不想額外設定unsaved-value資訊,則可以將long改為Long,這可以符合預設的unsaved-value為null的設定 .

二、一对多进行关联(一间宿舍对应多个学生)---宿舍是主体,学生是附体,关联关系<one-to-many>在主体宿舍中设置,由于要在宿舍类中设置学生类,一个宿舍包含多个学生,所以在宿舍类中要用Set类来进行设置,用set类(private Set users = new HashSet();)来存储多个学生类,在映射宿舍类(Room)中要包含<set>这个节点,用来与user相关联

例如:<set name="users" table="USER">
            <key column="ROOM_ID"/>
            <one-to-many class="onlyfun.caterpillar.User"/>
        </set>

name:表示属性,table:表示关联的表名,key:表示通过什么字段进行关联,<one-to-many>:表示关联类。这里也可以使用cascade属性。

三、在表关联的设计中,不论是一对多还是多对一,都要将关联字段设置在多的那一方。
例如:user表格和room表格,要将关联字段room_id设置在user表格中。

四、一对一进行关联(一个人只有一个房间,一个房间也只有一个人)。
可以通过2中方式进行关联:

(1)、通过外键进行关联:在多对一的例子中就是通过外键进行关联的.
在user-room的设置中(user.hbm.xml):
<many-to-one name="room"
                     column="ROOM_ID"
                     class="onlyfun.caterpillar.Room"
                     cascade="all"
                     unique="true"/>

其中unique表示限制一個User有一獨有的 Room,这只是单向的,说明一个user只有一个room.
在room-user的设置中(room.hbm.xml):
<one-to-one name="user"
                    class="onlyfun.caterpillar.User"
                    property-ref="room"/>
这样就完成了双向的一对一关联,property-ref告诉hibernate,查询出user并将其参考至room。
(2)、通过主键进行关联:限制兩個資料表的主鍵使用相同的值,如此一個User與Room就是一對一關係
user.hbm.xml:
<one-to-one name="room"
                    class="onlyfun.caterpillar.Room"
                    cascade="all"/>
room.hbm.xml:
<one-to-one name="user"
                    class="onlyfun.caterpillar.User"
                    constrained="true"/>

使用constrained="true"告訴Hibernate參考至User的主鍵

五、双向关联,就是将一和二结合起来,如果將關聯的維護交給User的話會比較容易,因為每個User都對應至一個Room,在儲存時並用像Room一樣必須對Set中的每個物件作檢查,為了將關聯的維護交給User,我們可以在Room.hbm.xml中的<set>修改,加上inverse="true",表示將關聯的維護「反過來」交給User作

 例如:<set name="users" table="users" iinverse="true" cascade="all">
              <key  column="room_id"/>
              <one-to-many class="onlyfun.caterpillar.User"/>

在設立雙向關聯時,關聯由多對一中「多」的哪一方維護,會比由「一」的哪一方維護來的方便,在Hibernate可以藉由inverse來設定,不設定inverse基本上也可以運行,但是效能會較差。


------------------------------------------------------------------------------------------------------------------------
      在Hibernate中,集合類的映射可以延遲初始(Lazy Initialization),在多对一或者一对多中,都可以使用延遲初始,例如:一个用户(user对应user表)有多个email地址(address对应address表),也就是在真正索取該物件的資料時,才向資料庫查詢,就上次例子來說,就是我們在讀取User時,先不取得其中的 addrs屬性中之物件資料,由於只需要讀取User的name屬性,此時我們只要執行一次select即可,真正需要addrs的資料時,才向資料庫要求。在含有集合类的user.hbm.xml中要如下设置:

  <set name="addrs" table="ADDRS" lazy="true">
            <key column="USER_ID"/>
            <element type="string" column="ADDRESS" not-null="true"/>
        </set>

--------------------------------------------------------------------------------------------------------------------------
      session是hibernate运做的核心,是有SessionFactory所创建,sessionFactory是线程安全的,你可以让多个线程同时存取SessionFactory,而不会有资源共用的问题,然而session不是设计为线程安全的,所以让多个线程共用一个session,将发生资料共用而发生混乱的问题.下面是一个标准类.

import java.io.Serializable;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
public class HibernateSessionUtil implements Serializable
{
     //创建线程局部变量 tLocalsess 
    public static final ThreadLocal tLocalsess = new ThreadLocal();
   //创建线程局部变量 tLocaltx
public static final ThreadLocal tLocaltx = new ThreadLocal();
      //取得session
    public static Session currentSession(){
         //从线程变量tLocalsess中,取得当前session
Session session = (Session) tLocalsess.get();
//判断session是否为空,如果为空,将创建一个session,并付给线程变量tLocalsess
    try{
if (session == null){
session = openSession();
tLocalsess.set(session);
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
return session;
}
//关闭当前session
    public static void closeSession(){
         //从线程变量tLocalsess中,取得当前session
Session session = (Session) tLocalsess.get();
         //设置线程变量tLocalsess为空
tLocalsess.set(null);
try{
            //关闭session
if (session != null && session.isOpen()){
session.close();
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//事物处理
    public static void beginTransaction(){
      //从线程变量tLocaltx中取得事物管理对象Transaction
        Transaction tx = (Transaction) tLocaltx.get();
try{
            //如果为空就从session中创建一个tx
if (tx == null){
tx = currentSession().beginTransaction();
tLocaltx.set(tx);
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//提交事物
    public static void commitTransaction(){
      //取得事物
Transaction tx = (Transaction) tLocaltx.get();
try{
            //如果不为空就提交
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack())
tx.commit();
tLocaltx.set(null);
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
    //事物回滚    
    public static void rollbackTransaction(){
         //取得tx事物
Transaction tx = (Transaction) tLocaltx.get();
try{
            //将变量清空
tLocaltx.set(null);
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()){
                //事物回滚
tx.rollback();
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
   //取得session
private static Session openSession() throws HibernateException{
return getSessionFactory().openSession();
}
   //取得sessionFactory
private static SessionFactory getSessionFactory() throws HibernateException{
return SingletonSessionFactory.getInstance();
}
}
filter的代码:
public class HibernateSessionCloser implements Filter{
protected FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig)throws ServletException{
this.filterConfig = filterConfig;
}
public void destroy(){
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
try{
chain.doFilter(request, response);
}
finally{
try{
HibernateSessionUtil.commitTransaction();
}catch (InfrastructureException e){
HibernateSessionUtil.rollbackTransaction();
}finally{
HibernateSessionUtil.closeSession();
}
}
}
}
---------------------------------------------------------------------------------------

      (1)、悲觀鎖定(Pessimistic Locking)一如其名稱所示,悲觀的認定每次資料存取時,其它的客戶端也會存取同一筆資料,因此對該筆資料進行鎖定,直到自己操作完成後解除鎖定。 

      悲觀鎖定通常透過系統或資料庫本身的功能來實現,依賴系統或資料庫本身提供的鎖定機制,Hibernate即是如此,可以利用Query或 Criteria的setLockMode()方法來設定要鎖定的表或列(Row)及其鎖定模式,可設定的鎖定模式有以下的幾個:
  • LockMode.WRITE:在insert或update時進行鎖定,Hibernate會在save()方法時自動獲得鎖定。
  • LockMode.UPGRADE:利用SELECT ... FOR UPDATE進行鎖定。
  • LockMode.UPGRADE_NOWAIT:利用SELECT ... FOR UPDATE NOWAIT進行鎖定,在Oracle環境下使用。
  • LockMode.READ:在讀取記錄時Hibernate會自動獲得鎖定。
  • LockMode.NONE:沒有鎖定。

          (2)、樂觀鎖定(Optimistic locking)則樂觀的認為資料的存取很少發生同時存取的問題,因而不作資料庫層次上的鎖定,為了維護正確的資料,樂觀鎖定使用應用程式上的邏輯實現版本控制的解決。 

          在不實行悲觀鎖定策略的情況下,資料不一致的情況一但發生,有幾個解決的方法,一種是先更新為主,一種是後更新的為主,比較複雜的就是檢查發生變動的資料來實現,或是檢查所有屬性來實現樂觀鎖定。
          要注意的是,由於樂觀鎖定是使用系統中的程式來控制,而不是使用資料庫中的鎖定機制,因而如果有人特意自行更新版本訊息來越過檢查,則鎖定機制就會無效,例如在上例中自行更改userV2的version屬性,使之與資料庫中的版本號相同的話就不會有錯誤,像這樣版本號被更改,或是由於資料是由外部系統而來,因而版本資訊不受控制時,鎖定機制將會有問題,設計時必須注意。

  • posted @ 2008-01-10 11:40 灵! 阅读(251) | 评论 (0)编辑 收藏

    持久层的组成(转)

    持久层的组成
        这一节的名字应该换成“基于Hibernate的持久层的组成”更合适一点,可是它太长了。既然Hibernate是用来开发持久层,那么我先介绍一下这个持久层中的各个元素。
    1.    POJO:Plain Old Java Object,你可以把它看作是简单的JavaBean。一般说来,一张数据库表对应一个POJO,也就是对象/关系的一一映射。
    2.    DAO:对于每一个POJO,一般都有一个DAO与之对应,承担所有关于该POJO的访问控制。实际上也就是控制了对数据库中一张表的访问控制。
    3.    *.hbm.xml文件:这个文件定义了POJO和数据库中的表是如何映射的,比如POJO中的字段对应数据库表中的哪个字段等等。一般每个映射都用单独的文件来描述,也就是有一个POJO就有一个*.hbm.xml文件。
    4.    *.cfg.xml文件:这个文件定义了Hibernate的基本信息,比如数据库驱动,用户名,密码等等连接信息,也包括了所有要用的*.hbm.xml文件,在初始化的时候,Hibernate会读取这个文件来找相应的映射文件完成对象/关系。
    我们还是以上文的例子来详细描述一下这里提到的各个元素的内容。
    1.    Student.java:
    代码片段4:
    1. public class Student  implements java.io.Serializable 
    2. {
    3.     private String id;
    4.     private String name;
    5.     private Set courseSelections = new HashSet(0);
    6.     public Student() 
    7.     {
    8.     }
    9.     public String getId() 
    10.     {
    11.         return this.id;
    12.     }
    13.     
    14.     public void setId(String id) 
    15.     {
    16.         this.id = id;
    17.     }
    18.     public String getName() 
    19.     {
    20.         return this.name;
    21.     }
    22.     
    23.     public void setName(String name) 
    24.     {
    25.         this.name = name;
    26.     }
    27.     public Set getCourseSelections() 
    28.     {
    29.         return this.courseSelections;
    30.     }
    31.     
    32.     public void setCourseSelections(Set courseSelections) 
    33.     {
    34.         this.courseSelections = courseSelections;
    35.     }
    36. }

    这个类就是一个POJO,你可以很明显的看出来它就是一个JavaBean。我想解释它的courseSelection字段。很显然,在数据库表student中,没有这个字段。这里的这个字段是因为一个外键引用,course_selection的student_id是一个外键,引用了student表中的id字段。那么在Student类中courseSelection来记录这样的外键关系,也就是说,当我们获取了Student对象以后,就可以直接获取他的选课记录,这样就为上层的调用提供了很大的方便。这里有点模糊没关系,我在介绍映射定义文件(*.hbm.xml)的时候还会提到这个问题。
    2.    StudentDAO.java
    代码片段5:
    1. public class StudentDAO 
    2. {
    3.     Session session;
    4.     public StudentDAO()
    5.     {
    6.         Configuration cfg = new Configuration();
    7.         cfg.configure("/hibernate.cfg.xml");
    8.         SessionFactory sessionFactory = cfg.buildSessionFactory();
    9.         session = sessionFactory.openSession();
    10.     }
    11.     
    12.     public void save(Student transientInstance) 
    13.     {
    14.         session.save(transientInstance);
    15.     }
    16.     
    17.     public void delete(Student persistentInstance) 
    18.     {
    19.         session.delete(persistentInstance);
    20.     }
    21.     
    22.     public Student findById(java.lang.String id) 
    23.     {
    24.         List list = session.createCriteria(Student.class).add(
    25. Expression.eq("id", id)).list();
    26.         if (list.size() > 0) 
    27.         {
    28.             return (Student)list.get(0);
    29.         }
    30.         return null;
    31.     }
    32. }

    这里的构造函数是用来启动Hibernate,并获取session。打开一个session就相当于打开了一个数据库连接,然后我们就可以对这个session进行操作,完成数据库操作,完全不用写SQL语句。我这里Hibernate的启动方式写的很不规范,系统应该只需要完成一次Hibernate启动就可以在不同的DAO中使用,我把它写在构造函数里面纯粹是为了简化演示代码。
    你可以看到save和delete方法都很简单直接对对象操作,而findById就有些麻烦,因为这里有一个查询过程在里面。Hibernate里面查询可以用Criteria这个类来完成,我们也常用Hibernate独有的HQL(Hibernate Query Language)来完成查询。当然Hibernate也是支持原生SQL的。关于查询的详细信息请参考其他文章或书籍,我只是演示一个流程,介绍一些概念。
    3.    Student.hbm.xml
    代码片段6:
    1. <hibernate-mapping>
    2.     <class name="Student" table="STUDENT">
    3.         <id name="id" type="string">
    4.             <column name="ID" length="10" />
    5.             <generator class="assigned" />
    6.         </id>
    7.         <property name="name" type="string">
    8.             <column name="NAME" not-null="true" />
    9.         </property>
    10.         <set name="courseSelections" inverse="true">
    11.             <key>
    12.                 <column name="STUDENT_ID" length="10" 
    13. not-null="true" />
    14.             </key>
    15.             <one-to-many class="CourseSelection" />
    16.         </set>
    17.     </class>
    18. </hibernate-mapping>

    这个文件定义了Student类和Student表是如何映射的。class元素定义了Sudent类和STUDENT表映射,然后就定义了各个属性是如何映射的。如果一个属性是数据库的key,那么会用id标签来定义,column定义了当前类的属性和数据库中的哪个字段对应,generator是id特有的。一般来说id是自增的,由于我的数据库是用的Oracle,它没有自增字段,要实现自增必须用Sequence,这超出了本文的范围,所以我就用assigned来简化示例代码。assigned表示id是用户给定的。
    有一个比较特别的标签是set,它对应着数据库中的外键关系,上文我提到的通过Student对象可以获得所有相关的选课记录就是通过这里的定义实现的。name属性对应了Student类中的字段名,key表示哪个字段是外键,one-to-many表示Student和CourseSelection是一对多关系,这和事实相符。类似的还有many-to-one,many-to-many,不过这些都不常用,我不介绍了。Hibernate根据这个映射定义文件,在实例化一个POJO(比如Student)的时候,会自动的把定义过映射的属性用数据库中的数据填充,set也包括在内。
    4.    hibernate.cfg.xml
    代码片段7:
    1. <hibernate-configuration>
    2.     <session-factory>
    3.         <property name="connection.username">test</property>
    4.         <property name="connection.url">
    5. jdbc:oracle:thin:@10.85.33.199:1521:glee</property>
    6.         <property name="dialect">
    7. org.hibernate.dialect.Oracle9Dialect</property>
    8.         <property name="connection.password">test</property>
    9.         <property name="connection.driver_class">
    10. oracle.jdbc.OracleDriver</property>
    11.         <mapping resource="Student.hbm.xml"></mapping>
    12.         <mapping resource="CourseSelection.hbm.xml"></mapping>
    13.         <mapping resource="Course.hbm.xml"></mapping>
    14.     </session-factory>
    15. </hibernate-configuration>

    这个文件我不解释了,自己看吧。结合上文StudentDAO的例子,我想你应该能看明白。
    看了这么多,或许你会有点头皮发麻,POJO,DAO,配置文件...好像要写的东西还是很多。值得庆幸的是现在Hibernate已经发展的比较成熟了,有很多工具来帮助我们完成这些工作,比如MiddleGen,Hibernate Synchronizer等等。我使用的开发工具是Eclipse+MyEclipse,我所要做的只是把数据库表建好,然后MyEclipse提供的工具会自动根据数据库表生成POJO,DAO,*.hbm.xml,甚至hibernate.cfg.xml都是自动完成的(前提是MyEclipse知道你的数据库连接信息)。我并不打算介绍如何用IDE来开发Hibernate,你可以参考IDE的帮助文档。
    到这里为止,使用Hibernate进行开发的基本组成元素我都介绍好了,强烈建议你马上实践一遍,即使有些不理解,也先依葫芦画瓢一个。对了,别忘了把Hibernate的包down下来放到classpath里面。

    三、Session与SessionFactory
    Session可以说是Hibernate的核心,Hibernate对外暴露的接口就是Session。所以我这里讲一下有关Session的常用函数和特性。
    在讲Session之前,我想先提一下SessionFactory,这个东西不复杂,只要配置好就行了。顾名思义,SessionFactory就是用来创建Session的。SessionFactory是线程安全的,也就是说对于同一个数据库的所有操作共享一个SessionFactory就行了。回头看代码片段5,我们可以看到SessionFactory的常用配置方式。
    代码片段8:
    1. Configuration cfg = new Configuration();
    2. cfg.configure("/hibernate.cfg.xml");
    3. SessionFactory sessionFactory = cfg.buildSessionFactory();

    我们通过Configuration来读取配置文件,然后就可以创建SessionFactory,这段代码在    所有系统中都大同小异,一般就是xml配置文件的名字不一样,所以也没什么好说的。
    当我们有了SessionFactory以后就可以获取Session了。调用SessionFactory.openSession()就会返回一个Session实例,然后我们操作这个Session来访问数据库。值得一提的是Session并不是线程安全的,也就是每一个线程都必须有自己的Session。所以我们一般通过以下方法来获取和关闭Session:
    代码片段9:
    1. public static Session currentSession() throws HibernateException 
    2.     {
    3.         Session session = (Session) threadLocal.get();
    4.         if (session == null || !session.isOpen()) 
    5.         {
    6.             if (sessionFactory == null
    7.             {
    8.                 try 
    9.                 {
    10.                     cfg.configure(CONFIG_FILE_LOCATION);
    11.                     sessionFactory = cfg.buildSessionFactory();
    12.                 } 
    13.                 catch (Exception e) 
    14.                 {
    15.                     e.printStackTrace();
    16.                 }
    17.             }
    18.             session = (sessionFactory != null) ?
    19.                  sessionFactory.openSession(): null;
    20.             threadLocal.set(session);
    21.         }
    22.         return session;
    23. }
    24. public static void closeSession() throws HibernateException 
    25.     {
    26.         Session session = (Session) threadLocal.get();
    27.         threadLocal.set(null);
    28.         if (session != null
    29.         {
    30.             session.close();
    31.         }
    32. }

    可以看到,我们通过threadLocal来保存每个线程的session,这样就保证了各个线程之    间的互不干扰,也保证了系统只有一个SessionFactory实例(对于大多数应用来说已经    足够了)。如果你使用MyEclipse进行开发的话,它会自动生成一个    HibernateSessionFactory.java,其中就包含了以上代码。
    好了,现在我们已经获得了Session,下面我来介绍以下Session的常用函数,这些函数都有很多重载函数,我只介绍以下大概是干嘛的,不一一解释,详细信息你可以查看Hibernate的API。
    1.Session.get(),获取某个类的实例,一般都是通过id来获取比如
    Session.get(Student.class, "0361095");
    这句话的意思就是获取id(primary key)为“0361095”的Student对象。这里要注    意的是第二个参数必须是Object,也就是说,如果是long类型的1,那么必须转换    成new Long(1)再传入。
    2.Session.load(),用法和意义都和get一样,不过它们还是有点区别,我稍后解释。
    3.Session.save(),将某个实例保存到数据库中去(往往在数据库中形成一条新的记录)。
    4.Session.update(),更新某个实例,这个实例必须和数据库中有对应,否则会报错。
    5.Session.delete(),删除某个实例,也就是删除这个实例对应的数据表中的数据。
    6.Session.saveOrUpdate(),保存或者更新某个实例,调用这个方法你就不用去关心到        底是save还是update了,它会自己判断,然后调用相应的函数。其实save和update        涉及到实体对象生命周期中的三种状态,这个比较重要,我在后面会单独讲的。
    对于get和load的区别,很难讲清楚,这里涉及到Hibernate的缓存机制,是一个非常    复杂的话题,我不打算深入讨论这个内容。简单的来讲,Session实现了Hibernate的一    级缓存,SessionFactory实现了Hibernate的二级缓存。load方法会先查找一级缓存再查    找二级缓存,最后再去数据库中找,而get只会查找一级缓存,然后就去数据库中找了。    这只是是get和load的一个区别,另外的区别如果你有兴趣的话自己去google吧。关于Hibernate的缓存机制,如果你只是一般用用Hibernate的话没有必要深入研究,就当它不存在好了,get和load到底用哪个也不用非常讲究,如果你用工具生成DAO的话,    生成的代码用什么就用什么吧。

    四、关键概念的理解
    在使用Hibernate进行开发的时候有几个概念在我看来是必须理解的,否则在开发的时候会遇到很多问题而摸不着头脑。
    1.Lazy loading,懒加载(延迟加载)
    这个技术在很多地方被应用,比如Eclipse的插件管理也是用的延迟加载。在Hibernate中,所谓延迟加载就是返回一个POJO但是某些数据(往往是实体类型或者Set类型)并没有被真正的被填充,直到POJO的某个字段真正被引用的时候才从数据库中读取相应的数据来填充POJO中的字段。这样就能有效的避免很多不必要的数据库操作,因为POJO的有些数据我们并不需要,而且数据库操作是很费时间的。在Hibernate2中,默认是非延迟加载的,而在Hibernate3中,默认就是延迟加载了。
    如果使用了延迟加载,那么在读取数据的时候有一个问题必须注意,那就是在数据真正被加载之前,Session不能被关闭。你可以回头看一下代码片段5,我在构造函数里面open了session以后就没有关闭这个session,所以我在使用的时候没有什么问题,但这样总占着数据库连接也不好,用好了应该及时关闭给别人用。我上文给的例子中没有关闭Session的代码,要加的话给DAO加一个方法调用Session.close(),然后在代码片段1中的return之前调用这个方法就行了。
    Hibernate的延迟加载机制远不是这么简单,但是普通的应用没有必要去深究这些东西,了解这么多就够了。

    2. Object lifecycle,对象生命周期
    在Hibernate中,对象分为三种状态,Transient(自由状态)、Persistent(持久状态),Detached(游离状态),下面我分别解释一下。
    1、自由状态:所谓自由状态就是说这个对象是自由的,与Hibernate无关,比       如:
    Student student = new Student();
        student.setId("0361095");
                    这里的student就是一个普通的对象与hibernate无关,称为自由状态。

    2、持久状态:所谓持久状态就是指对象和数据库中的数据(持久状态的数据)       有关联,也就是说对象被Hibernate所管理了,比如:
    session.save(student);
       这样student对象就从自由状态变为持久状态了。持久状态的对象在Session       与数据库中的数据进行同步时(比如commit)会把数据更新到数据库。而         其他状态的则不会。我觉得可以这样来理解持久状态,可以看成Hibernate       也拥有一份对象的引用,那么如果你对持久状态对象的属性进行更改的话,       Hibernate看到的对象的状态也更改了,而Hibernate所看到的对象和数据       库中的数据是等价的。也正是这样,Hibernate才实现了Object/Relation       的映射。类似的,load和get方法也一样会获取Persistent状态的对象。

    3、游离状态:每次调用Session.close以后,所有跟这个session有关的处于
    Persistant的对象就变成了游离状态。也许你要问Detached和Transient有什么区别。其实从我给的例子来说看不出什么区别,因为我这里ID是给定的,而真正开发的时候ID往往是自增的,那么Transient的对象是没有ID    的,当save了以后就有了,显而易见Detached的对象也是有ID,只不过这个对象已经和Hibernate脱离了关系。但是游离状态的对象仍然和数据库    中的记录有一定联系,至少游离状态的对象知道数据库中有条记录的ID为xxx。从这一点上来讲,游离状态是可以自己创造出来的,只要你知道数据库中的主键信息。
    在使用Hibernate开发的时候要分清楚这三种状态,否则很容易出错。比如不能去save一个游离状态的对象,不能去update一个自由状态的对象等等。

    五、结束语
    我要讲的就这么多,这篇文章不是详细介绍Hibernate,我只是总结了自己学习和使用Hibernate的一些感受和经验,希望能给没有用过Hibernate的开发者一个上手的指引。如果你看完了这篇文章仍然满头雾水,无从下手的话,我只能向你表示歉意,浪费你的时间了。不管怎样,我强烈推荐一本书《深入浅出Hibernate》,这本书既能作为学习的教程,也能作为日后的查询用书,相当实用。

    posted @ 2008-01-10 11:39 灵! 阅读(285) | 评论 (0)编辑 收藏

    JAVA数据类型转换

    基本类型有以下四种:
    int长度数据类型有:byte(8bits)、short(16bits)、int(32bits)、long(64bits)、
    float长度数据类型有:单精度(32bits float)、双精度(64bits double)
    boolean类型变量的取值有:ture、false
    char数据类型有:unicode字符,16位
    对应的类类型:
    Integer、Float、Boolean、Character、Double、Short、Byte、Long


    转换原则:
    从低精度向高精度转换
    byte 、short、int、long、float、double、char
    注:两个char型运算时,自动转换为int型;当char与别的类型运算时,也会先自动转换为int型的,再做其它类型的自动转换

    基本类型向类类型转换

    正向转换:
    通过类包装器来new出一个新的类类型的变量
    Integer a= new Integer(2);

    反向转换:
    通过类包装器来转换
    int b=a.intValue();
    类类型向字符串转换


    正向转换:
    因为每个类都是object类的子类,而所有的object类都有一个toString()函数,所以通过toString()函数来转换即可

    反向转换:
    通过类包装器new出一个新的类类型的变量
    eg1: int i=Integer.valueOf(“123”).intValue()
    说明:上例是将一个字符串转化成一个Integer对象,然后再调用这个对象的intValue()方法返回其对应的int数值。
    eg2: float f=Float.valueOf(“123”).floatValue()
    说明:上例是将一个字符串转化成一个Float对象,然后再调用这个对象的floatValue()方法返回其对应的float数值。
    eg3: boolean b=Boolean.valueOf(“123”).booleanValue()
    说明:上例是将一个字符串转化成一个Boolean对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。
    eg4:Double d=Double.valueOf(“123”).doubleValue()
    说明:上例是将一个字符串转化成一个Double对象,然后再调用这个对象的doubleValue()方法返回其对应的double数值。
    eg5: long l=Long.valueOf(“123”).longValue()
    说明:上例是将一个字符串转化成一个Long对象,然后再调用这个对象的longValue()方法返回其对应的long数值。
    eg6: char=Character.valueOf(“123”).charValue()
    说明:上例是将一个字符串转化成一个Character对象,然后再调用这个对象的charValue()方法返回其对应的char数值。


    基本类型向字符串的转换

    正向转换:
    如:int a=12;
    String b;
    b=a+””;

    反向转换:
    通过类包装器
    eg1:
    int i=Integer.parseInt(“123”)
    说明:此方法只能适用于字符串转化成整型变量
    eg2: float f=Float.valueOf(“123”).floatValue()
    说明:上例是将一个字符串转化成一个Float对象,然后再调用这个对象的floatValue()方法返回其对应的float数值。
    eg3: boolean b=Boolean.valueOf(“123”).booleanValue()
    说明:上例是将一个字符串转化成一个Boolean对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。
    eg4:Double d=Double.valueOf(“123”).doubleValue()
    说明:上例是将一个字符串转化成一个Double对象,然后再调用这个对象的doubleValue()方法返回其对应的double数值。
    eg5: long l=Long.valueOf(“123”).longValue()
    说明:上例是将一个字符串转化成一个Long对象,然后再调用这个对象的longValue()方法返回其对应的long数值。
    eg6: char=Character.valueOf(“123”).charValue()
    说明:上例是将一个字符串转化成一个Character对象,然后再调用这个对象的charValue()方法返回其对应的char数值。

    posted @ 2008-01-10 11:38 灵! 阅读(231) | 评论 (0)编辑 收藏

    java 数据库基本操作

    java 数据库基本操作

      1、java数据库操作基本流程

      2、几个常用的重要技巧:

      ·可滚动、更新的记录集

      ·批量更新

      ·事务处理

      java数据库操作基本流程:取得数据库连接 - 执行sql语句 - 处理执行结果 - 释放数据库连接

      1、取得数据库连接

      1)用DriverManager取数据库连接

      例子

      String className,url,uid,pwd;

      className = "oracle.jdbc.driver.OracleDriver";

      url = "jdbc:oracle:thin:@127.0.0.1:1521:orasvr;

      uid = "system";

      pwd = "manager";

      Class.forName(className);

      Connection cn = DriverManager.getConnection(url,uid,pwd);

      2)用jndi(java的命名和目录服务)方式

      例子

      String jndi = "jdbc/db";

      Context ctx = (Context) new InitialContext().lookup("java:comp/env");

      DataSource ds = (DataSource) ctx.lookup(jndi);

      Connection cn = ds.getConnection();

      多用于jsp中

      2、执行sql语句

      1)用Statement来执行sql语句

      String sql;

      Statement sm = cn.createStatement();

      sm.executeQuery(sql); // 执行数据查询语句(select)

      sm.executeUpdate(sql); // 执行数据更新语句(delete、update、insert、drop等)statement.close();

      2)用PreparedStatement来执行sql语句

      String sql;

      sql = "insert into user (id,name) values (?,?)";

      PreparedStatement ps = cn.prepareStatement(sql);

      ps.setInt(1,xxx);

      ps.setString(2,xxx);

      ...

      ResultSet rs = ps.executeQuery(); // 查询

      int c = ps.executeUpdate(); // 更新

      3、处理执行结果

      查询语句,返回记录集ResultSet

      更新语句,返回数字,表示该更新影响的记录数

      ResultSet的方法

      1、next(),将游标往后移动一行,如果成功返回true;否则返回false

      2、getInt("id")或getSting("name"),返回当前游标下某个字段的值

      4、释放连接

      cn.close();

      一般,先关闭ResultSet,然后关闭Statement(或者PreparedStatement);最后关闭Connection

      可滚动、更新的记录集

      1、创建可滚动、更新的Statement

      Statement sm = cn.createStatement(ResultSet.TYPE_SCROLL_ENSITIVE,ResultSet.CONCUR_READ_ONLY);

      该Statement取得的ResultSet就是可滚动的

      2、创建PreparedStatement时指定参数

      PreparedStatemet ps = cn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

      ResultSet.absolute(9000);

      ·批量更新

      1、Statement

      Statement sm = cn.createStatement();

      sm.addBatch(sql1);

      sm.addBatch(sql2);

      ...

      sm.executeBatch()

      一个Statement对象,可以执行多个sql语句以后,批量更新。这多个语句可以是delete、update、insert等或兼有

      2、PreparedStatement

      PreparedStatement ps = cn.preparedStatement(sql);

      {

      ps.setXXX(1,xxx);

      ...

      ps.addBatch();

      }

      ps.executeBatch();

      一个PreparedStatement,可以把一个sql语句,变换参数多次执行,一次更新。

      ·事务的处理

      1、关闭Connection的自动提交

      cn.setAutoCommit(false);

      2、执行一系列sql语句

      要点:执行每一个新的sql语句前,上一次执行sql语句的Statement(或者PreparedStatemet)必须先close

      Statement sm ;

      sm = cn.createStatement(insert into user...);

      sm.executeUpdate();

      sm.close();

      sm = cn.createStatement("insert into corp...);

      sm.executeUpdate();

      sm.close();

      3、提交

      cn.commit();

      4、如果发生异常,那么回滚

      cn.rollback();

    posted @ 2008-01-10 11:37 灵! 阅读(210) | 评论 (0)编辑 收藏

    <2008年1月>
    303112345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789

    导航

    统计

    随笔分类

    随笔档案

    最新评论

    阅读排行榜

    评论排行榜