<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>guoli83945</title>
    <description></description>
    <link>http://guoli83945.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>JAVA 性能优化 “通用篇”</title>
        <author>guoli83945</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://guoli83945.javaeye.com">guoli83945</a>&nbsp;
          链接：<a href="http://guoli83945.javaeye.com/blog/198174" style="color:red;">http://guoli83945.javaeye.com/blog/198174</a>&nbsp;
          发表时间: 2008年05月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          “通用篇”讨论的问题适合于大多数Java应用。<br /><br />　　1.1 不用new关键词创建类的实例<br /><br />　　用new关键词创建类的实例时，构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口，我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。<br /><br />　　在使用设计模式（Design Pattern）的场合，如果用Factory模式创建对象，则改用clone()方法创建新的对象实例非常简单。例如，下面是Factory模式的一个典型实现：<br /><br /><pre name="code" class="java">public static Credit getNewCredit() 
{
　return new Credit();
} </pre><br />　　改进后的代码使用clone()方法，如下所示：<br /><br /><pre name="code" class="java">private static Credit BaseCredit = new Credit();
public static Credit getNewCredit() 
{
　return (Credit) BaseCredit.clone();
} </pre><br />　　上面的思路对于数组处理同样很有用。<br /><br />　　1.2 使用非阻塞I/O<br /><br />　　版本较低的JDK不支持非阻塞I/O API。为避免I/O阻塞，一些应用采用了创建大量线程的办法（在较好的情况下，会使用一个缓冲池）。这种技术可以在许多必须支持并发I/O流的应用中见到，如Web服务器、报价和拍卖应用等。然而，创建Java线程需要相当可观的开销。<br /><br />　　JDK 1.4引入了非阻塞的I/O库（java.nio）。如果应用要求使用版本较早的JDK，在这里有一个支持非阻塞I/O的软件包。<br /><br />　　1.3 慎用异常<br /><br />　　异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地（Native）方法，fillInStackTrace()方法检查堆栈，收集调用跟踪信息。只要有异常被抛出，VM就必须调整调用堆栈，因为在处理过程中创建了一个新的对象。<br /><br />　　异常只能用于错误处理，不应该用来控制程序流程。<br /><br />　　1.4 不要重复初始化变量<br /><br />　　默认情况下，调用类的构造函数时，Java会把变量初始化成确定的值：所有的对象被设置成null，整数变量（byte、short、int、long）设置成0，float和double变量设置成0.0，逻辑值设置成false。当一个类从另一个类派生时，这一点尤其应该注意，因为用new关键词创建一个对象时，构造函数链中的所有构造函数都会被自动调用。<br /><br />　　1.5 尽量指定类的final修饰符<br /><br />　　带有final修饰符的类是不可派生的。在Java核心API中，有许多应用final的例子，例如java.lang.String。为String类指定final防止了人们覆盖length()方法。<br /><br />　　另外，如果指定一个类为final，则该类所有的方法都是final。Java编译器会寻找机会内联（inline）所有的final方法（这和具体的编译器实现有关）。此举能够使性能平均提高50%。<br /><br />　　1.6 尽量使用局部变量<br /><br />　　调用方法时传递的参数以及在调用中创建的临时变量都保存在栈（Stack）中，速度较快。其他变量，如静态变量、实例变量等，都在堆（Heap）中创建，速度较慢。另外，依赖于具体的编译器/JVM，局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。<br /><br />　　1.7 乘法和除法<br /><br />　　考虑下面的代码：<br /><br /><pre name="code" class="java">for (val = 0; val &lt; 100000; val +=5) 
{
　alterX = val * 8; 
　myResult = val * 2; 
} </pre><br />　　用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码：<br /><br /><pre name="code" class="java">for (val = 0; val &lt; 100000; val += 5) 
{
　alterX = val &lt;&lt; 3;
　myResult = val &lt;&lt; 1;
} </pre><br /><br />　　修改后的代码不再做乘以8的操作，而是改用等价的左移3位操作，每左移1位相当于乘以2。相应地，右移1位操作相当于除以2。值得一提的是，虽然移位操作速度快，但可能使代码比较难于理解，所以最好加上一些注释。<br /><br />本贴来自ZDNetChina中文社区 http://bbs.zdnet.com.cn ,本贴地址：http://bbs.zdnet.com.cn/viewthread.php?tid=222399
          <br/>
          <span style="color:red;">
            <a href="http://guoli83945.javaeye.com/blog/198174#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 29 May 2008 11:32:51 +0800</pubDate>
        <link>http://guoli83945.javaeye.com/blog/198174</link>
        <guid>http://guoli83945.javaeye.com/blog/198174</guid>
      </item>
      <item>
        <title>通过JDBC连接oracle数据库的十大技巧</title>
        <author>guoli83945</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://guoli83945.javaeye.com">guoli83945</a>&nbsp;
          链接：<a href="http://guoli83945.javaeye.com/blog/195504" style="color:red;">http://guoli83945.javaeye.com/blog/195504</a>&nbsp;
          发表时间: 2008年05月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          　　Java数据库连接（JDBC）API是一系列能够让Java编程人员访问数据库的接口，各个开发商的接口并不完全相同。在使用多年的Oracle公司的JDBC后，我积累了许多技巧，这些技巧能够使我们更好地发挥系统的性能和实现更多的功能。<br /><br />　　<strong>1、在客户端软件开发中使用Thin驱动程序 </strong><br />　　在开发Java软件方面，Oracle的数据库提供了四种类型的驱动程序，二种用于应用软件、applets、servlets等客户端软件，另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中，我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口（JNI），通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序，它直接与数据库进行通讯。为了获得最高的性能，Oracle建议在客户端软件的开发中使用OCI驱动程序，这似乎是正确的。但我建议使用Thin驱动程序，因为通过多次测试发现，在通常情况下，Thin驱动程序的性能都超过了OCI驱动程序。 <br /><br />　　<strong>2、关闭自动提交功能，提高系统性能 </strong><br />　　在第一次建立与数据库的连接时，在缺省情况下，连接是在自动提交模式下的。为了获得更好的性能，可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能，如下所示： <br /><br /><br />　　conn.setAutoCommit(false);<br /><br />　　值得注意的是，一旦关闭了自动提交功能，我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。 <br /><br />　　<strong>3、在动态SQL或有时间限制的命令中使用Statement对象 </strong><br />　　在执行SQL命令时，我们有二种选择：可以使用PreparedStatement对象，也可以使用Statement对象。无论多少次地使用同一个SQL命令，PreparedStatement都只对它解析和编译一次。当使用Statement对象时，每次执行一个SQL命令时，都会对它进行解析和编译。这可能会使你认为，使用PreparedStatement对象比使用Statement对象的速度更快。然而，我进行的测试表明，在客户端软件中，情况并非如此。因此，在有时间限制的SQL操作中，除非成批地处理SQL命令，我们应当考虑使用Statement对象。 <br /><br />　　此外，使用Statement对象也使得编写动态SQL命令更加简单，因为我们可以将字符串连接在一起，建立一个有效的SQL命令。因此，我认为，Statement对象可以使动态SQL命令的创建和执行变得更加简单。 <br /><br />　　<strong>4、利用helper函数对动态SQL命令进行格式化 </strong><br />　　在创建使用Statement对象执行的动态SQL命令时，我们需要处理一些格式化方面的问题。例如，如果我们想创建一个将名字O'Reilly插入表中的SQL命令，则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法，然后在连接字符串心服用公式表达一个SQL命令时，使用创建的helper方法。与此类似的是，我们可以让helper方法接受一个Date型的值，然后让它输出基于Oracle的to_date()函数的字符串表达式。 <br /><br />　　<strong>5、利用PreparedStatement对象提高数据库的总体效率 </strong><br />　　在使用PreparedStatement对象执行SQL命令时，命令被数据库进行解析和编译，然后被放到命令缓冲区。然后，每当执行同一个PreparedStatement对象时，它就会被再解析一次，但不会被再次编译。在缓冲区中可以发现预编译的命令，并且可以重新使用。在有大量用户的企业级应用软件中，经常会重复执行相同的SQL命令，使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的时间长于Statement任务，我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。 <br /><br />　<strong>6、在成批处理重复的插入或更新操作中使用PreparedStatement对象 </strong><br /><br /><br />　　如果成批地处理插入和更新操作，就能够显著地减少它们所需要的时间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理，只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理，或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制，可以以如下所示的方式调用setExecuteBatch()： <br /><br />PreparedStatement pstmt3D null; <br />try { <br />((OraclePreparedStatement) <br />pstmt).setExecuteBatch(30); <br />... <br />pstmt.executeUpdate(); <br />}<br /><br />　　调用setExecuteBatch()时指定的值是一个上限，当达到该值时，就会自动地引发SQL命令执行，标准的executeUpdate()方法就会被作为批处理送到数据库中。我们可以通过调用PreparedStatement类的sendBatch()方法随时传输批处理任务。 <br /><br />　　<strong>7、使用Oracle locator方法插入、更新大对象（LOB） </strong><br />　　Oracle的PreparedStatement类不完全支持BLOB和CLOB等大对象的处理，尤其是Thin驱动程序不支持利用PreparedStatement对象的setObject()和setBinaryStream()方法设置BLOB的值，也不支持利用setCharacterStream()方法设置CLOB的值。只有locator本身中的方法才能够从数据库中获取LOB类型的值。可以使用PreparedStatement对象插入或更新LOB，但需要使用locator才能获取LOB的值。由于存在这二个问题，因此，我建议使用locator的方法来插入、更新或获取LOB的值。 <br /><br />　　<strong>8、使用SQL92语法调用存储过程 </strong><br />　　在调用存储过程时，我们可以使用SQL92或Oracle PL/SQL，由于使用Oracle PL/SQL并没有什么实际的好处，而且会给以后维护你的应用程序的开发人员带来麻烦，因此，我建议在调用存储过程时使用SQL92。 <br /><br />　　<strong>9、使用Object SQL将对象模式转移到数据库中 </strong><br />　　既然可以将Oracle的数据库作为一种面向对象的数据库来使用，就可以考虑将应用程序中的面向对象模式转到数据库中。目前的方法是创建Java bean作为伪装的数据库对象，将它们的属性映射到关系表中，然后在这些bean中添加方法。尽管这样作在Java中没有什么问题，但由于操作都是在数据库之外进行的，因此其他访问数据库的应用软件无法利用对象模式。如果利用Oracle的面向对象的技术，可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作，然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式，不但Java应用程序可以使用应用软件的对象模式，其他需要共享你的应用中的数据和操作的应用软件也可以使用应用软件中的对象模式。 <br /><br />　　<strong>10、利用SQL完成数据库内的操作 </strong><br />　　我要向大家介绍的最重要的经验是充分利用SQL的面向集合的方法来解决数据库处理需求，而不是使用Java等过程化的编程语言。 <br /><br />　　如果编程人员要在一个表中查找许多行，结果中的每个行都会查找其他表中的数据，最后，编程人员创建了独立的UPDATE命令来成批地更新第一个表中的数据。与此类似的任务可以通过在set子句中使用多列子查询而在一个UPDATE命令中完成。当能够在单一的SQL命令中完成任务，何必要让数据在网上流来流去的？我建议用户认真学习如何最大限度地发挥SQL的功能。
          <br/>
          <span style="color:red;">
            <a href="http://guoli83945.javaeye.com/blog/195504#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 22 May 2008 09:36:23 +0800</pubDate>
        <link>http://guoli83945.javaeye.com/blog/195504</link>
        <guid>http://guoli83945.javaeye.com/blog/195504</guid>
      </item>
      <item>
        <title>使用 Spring 2.5 TestContext 测试框架(转)</title>
        <author>guoli83945</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://guoli83945.javaeye.com">guoli83945</a>&nbsp;
          链接：<a href="http://guoli83945.javaeye.com/blog/183314" style="color:red;">http://guoli83945.javaeye.com/blog/183314</a>&nbsp;
          发表时间: 2008年04月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>概述</strong><br /><br />Spring 2.5 相比于 Spring 2.0 所新增的最重要的功能可以归结为以下 3 点：<br /><br />1.基于注解的 IoC 功能； <br />2.基于注解驱动的 Spring MVC 功能； <br />3.基于注解的 TestContext 测试框架。 <br /><br />Spring 推荐开发者使用新的基于注解的 TestContext 测试框架，本文我们将对此进行详细的讲述。<br /><br />低版本的 Spring 所提供的 Spring 测试框架构在 JUnit 3.8 基础上扩展而来，它提供了若干个测试基类。而 Spring 2.5 所新增的基于注解的 TestContext 测试框架和低版本的测试框架没有任何关系。它采用全新的注解技术可以让 POJO 成为 Spring 的测试用例，除了拥有旧测试框架所有功能外，TestContext 还添加了一些新的功能，TestContext 可以运行在 JUnit 3.8、JUnit 4.4、TestNG 等测试框架下。<br /><br /><strong>直接使用 JUnit 测试 Spring 程序存在的不足</strong><br /><br />在拙作《精通 Spring 2.x — 企业应用开发详解》一书中，笔者曾经指出如果直接使用 JUnit 测试基于 Spring 的程序，将存在以下 4 点明显的不足：<br /><br /><u>1.导致 Spring 容器多次初始化问题</u>：根据 JUnit 测试用例的调用流程，每执行一个测试方法都会重新创建一个测试用例实例并调用其 setUp() 方法。由于在一般情况下，我们都在 setUp() 方法中初始化 Spring 容器，这意味着测试用例中有多少个测试方法，Spring 容器就会被重复初始化多少次。 <br /><u>2.需要使用硬编码方式手工获取 Bean</u>：在测试用例中，我们需要通过 ApplicationContext.getBean() 的方法从 Spirng 容器中获取需要测试的目标 Bean，并且还要进行造型操作。 <br /><u>3.数据库现场容易遭受破坏</u>：测试方法可能会对数据库记录进行更改操作，破坏数据库现场。虽然是针对开发数据库进行测试工作的，但如果数据操作的影响是持久的，将会形成积累效应并影响到测试用例的再次执行。举个例子，假设在某个测试方法中往数据库插入一条 ID 为 1 的 t_user 记录，第一次运行不会有问题，第二次运行时，就会因为主键冲突而导致测试用例执行失败。所以测试用例应该既能够完成测试固件业务功能正确性的检查，又能够容易地在测试完成后恢复现场，做到踏雪无迹、雁过无痕。 <br /><u>4.不容易在同一事务下访问数据库以检验业务操作的正确性</u>：当测试固件操作数据库时，为了检测数据操作的正确性，需要通过一种方便途径在测试方法相同的事务环境下访问数据库，以检查测试固件数据操作的执行效果。如果直接使用 JUnit 进行测试，我们很难完成这项操作。 <br /><br />Spring 测试框架是专门为测试基于 Spring 框架应用程序而设计的，它能够让测试用例非常方便地和 Spring 框架结合起来，以上所有问题都将迎刃而解。<br /><br /><br /><strong>一个需要测试的 Spring 服务类</strong><br />在具体使用 TextContext 测试框架之前，我们先来认识一下需要测试的 UserService 服务类。UserService 服务类中拥有一个处理用户登录的服务方法，其代码如下所示：<br /><br />清单1. UserService.java 需要测试的服务类<br /><pre name="code" class="java">package com.baobaotao.service;

import com.baobaotao.domain.LoginLog;
import com.baobaotao.domain.User;
import com.baobaotao.dao.UserDao;
import com.baobaotao.dao.LoginLogDao;

public class UserService{

    private UserDao userDao;
    private LoginLogDao loginLogDao;

    public void handleUserLogin(User user) {
        user.setCredits( 5 + user.getCredits());
        LoginLog loginLog = new LoginLog();
        loginLog.setUserId(user.getUserId());
        loginLog.setIp(user.getLastIp());
        loginLog.setLoginTime(user.getLastVisit());
        userDao.updateLoginInfo(user);
        loginLogDao.insertLoginLog(loginLog);
    }
    //省略get/setter方法
}</pre><br /><br />UserService 需要调用 DAO 层的 UserDao 和 LoginLogDao 以及 User 和 LoginLog 这两个 PO 完成业务逻辑，User 和 LoginLog分别对应 t_user 和 t_login_log 这两张数据库表。<br /><br />在用户登录成功后调用 UserService 中的 handleUserLogin() 方法执行用户登录成功后的业务逻辑：<br /><br />登录用户添加 5 个积分（t_user.credits）； <br />登录用户的最后访问时间（t_user.last_visit）和 IP（t_user.last_ip）更新为当前值； <br />在日志表中（t_login_log）中为用户添加一条登录日志。 <br />这是一个需要访问数据库并存在数据更改操作的业务方法，它工作在事务环境下。下面是装配该服务类 Bean 的 Spring 配置文件：<br /><br /><br />清单2. applicationContext.xml：Spring 配置文件，放在类路径下<br /><pre name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8" ?>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
<a href="http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans-2.5.xsd</a>
     http://www.springframework.org/schema/tx 
<a href="http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" target="_blank">http://www.springframework.org/schema/tx/spring-tx-2.5.xsd</a>
     http://www.springframework.org/schema/aop 
<a href="http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" target="_blank">http://www.springframework.org/schema/aop/spring-aop-2.5.xsd</a>">
    &lt;!-- 配置数据源  -->
&lt;bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost/sampledb"
        p:username="root"
        p:password="1234"/>
    
    &lt;!-- 配置Jdbc模板  -->
&lt;bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
            p:dataSource-ref="dataSource"/>

    &lt;!-- 配置dao  -->
&lt;bean id="loginLogDao"class="com.baobaotao.dao.LoginLogDao"
           p:jdbcTemplate-ref="jdbcTemplate"/>
&lt;bean id="userDao" class="com.baobaotao.dao.UserDao" 
p:jdbcTemplate-ref="jdbcTemplate"/>

&lt;!-- 事务管理器 -->
&lt;bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
           p:dataSource-ref="dataSource"/>

&lt;bean id="userService" class="com.baobaotao.service.UserService"
             p:userDao-ref="userDao" p:loginLogDao-ref="loginLogDao"/>

    &lt;!-- 使用aop/tx命名空间配置事务管理,这里对service包下的服务类方法提供事务-->
    &lt;aop:config>
&lt;aop:pointcut id="jdbcServiceMethod"
expression= "within(com.baobaotao.service..*)" />
&lt;aop:advisor pointcut-ref="jdbcServiceMethod" advice-ref="jdbcTxAdvice" />
    &lt;/aop:config>
    &lt;tx:advice id="jdbcTxAdvice" transaction-manager="transactionManager">
&lt;tx:attributes>
            &lt;tx:method name="*"/>
        &lt;/tx:attributes>
&lt;/tx:advice>
&lt;/beans></pre><br /><br />UserService 所关联的 DAO 类和 PO 类都比较简单，请参看本文附件的程序代码。在着手测试 UserSerivce 之前，需要将创建数据库表，你可以在附件的 schema 目录下找到相应的 SQL 脚本文件。<br /><br /><br /><strong>编写 UserService 的测试用例</strong><br />下面我们为 UserService 编写一个简单的测试用例类，此时的目标是让这个基于 TestContext 测试框架的测试类运行起来，我们将在后面逐步完善这个测试用例。<br /><br />清单3.TestUserService.java: 基于注解的测试用例<br /><pre name="code" class="java">package com.baobaotao.service;

import org.springframework.test.context.junit4.
    AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.junit.Test;
import com.baobaotao.domain.User;

import java.util.Date;

@ContextConfiguration  //①
public class TestUserService extends 
    AbstractTransactionalJUnit4SpringContextTests {
   
   @Autowired  //②
   private UserService userService;

   @Test  //③
   public void handleUserLogin(){
       User user = new User();
       user.setUserId(1);
       user.setLastIp("127.0.0.1");
       Date now = new Date();
       user.setLastVisit(now.getTime());
       userService.handleUserLogin(user);
   }
}</pre><br /><br />这里，我们让 TestUserService 直接继承于 Spring 所提供的 AbstractTransactionalJUnit4SpringContextTests 的抽象测试类，稍后本文将对这个抽象测试类进行剖析，这里你仅须知道该抽象测试类的作用是让 TestContext 测试框架可以在 JUnit 4.4 测试框架基础上运行起来就可以了。<br /><br />在 ① 处，标注了一个类级的 @ContextConfiguration 注解，这里 Spring 将按 TestContext 契约查找 classpath:/com/baobaotao/service/TestUserService-context.xml 的 Spring 配置文件，并使用该配置文件启动 Spring 容器。@ContextConfiguration 注解有以下两个常用的属性：<br /><br />1. locations：可以通过该属性手工指定 Spring 配置文件所在的位置，可以指定一个或多个 Spring 配置文件。如下所示：<br /><pre name="code" class="java">@ContextConfiguration(locations={“xx/yy/beans1.xml”,"xx/yy/beans2.xml”}) </pre><br /><br />2. inheritLocations：是否要继承父测试用例类中的 Spring 配置文件，默认为 true。如下面的例子：<br /><br /><pre name="code" class="java">@ContextConfiguration(locations={"base-context.xml"})
 public class BaseTest {
     // ...
 }
 @ContextConfiguration(locations={"extended-context.xml"})
 public class ExtendedTest extends BaseTest {
     // ...
 }</pre><br /><br />如果 inheritLocations 设置为 false，则 ExtendedTest 仅会使用 extended-context.xml 配置文件，否则将使用 base-context.xml 和 extended-context.xml 这两个配置文件。<br /><br />② 处的 @Autowired 注解让 Spring 容器自动注入 UserService 类型的 Bean。而在 ③ 处标注的 @Test 注解则让 handleUserLogin() 方法成为一个 JUnit 4.4 标准的测试方法， @Test 是 JUnit 4.4 所定义的注解。<br /><br />在运行 TestUserService 测试类之前，让我们先看一下 TestUserService-context.xml 配置文件的内容：<br /><br />清单 4.TestUserService 所引用的 Spring 配置文件<br /><pre name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8" ?>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
<a href="http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans-2.5.xsd</a>">

&lt;!-- ① 引入清单1定义的Spring配置文件 -->
&lt;import resource="classpath:/applicationContext.xml"/>

&lt;/beans></pre><br /><br />在 ① 处引入了清单 1 中定义的 Spring 配置文件，这样我们就可以将其中定义的 UserService Bean 作为测试固件注入到 TestUserService 中了。<br /><br />在你的 IDE 中（Eclipse、JBuilder、Idea 等），将 JUnit 4.4 类包引入到项目工程中后，在 TestUserService 类中点击右键运行该测试类，将发现 TestUserService 已经可以成功运行了，如 图 1 所示：<br /><br />图 1. 在 Eclipse 6.0 中运行 TestUserService<br /><img src="http://www.ibm.com/developerworks/cn/java/j-lo-spring25-test/fig001.jpg" /><br />TestUserService 可以正确运行，说明其 userService 这个测试固件已经享受了 Spring 自动注入的功能。在运行该测试用例后，到数据库中查看 t_user 表和 t_login_log 表，你会发现表数据和测试前是一样的！这说明虽然我们在清单 3 的 handleUserLogin() 测试方法中执行了 userService.handleUserLogin(user) 的操作，但它并没有对数据库现场造成破坏：这是因为 Spring 的在测试方法返回前进行了事务回滚操作。<br /><br />虽然 TestUserService.handleUserLogin() 测试方法已经可以成功运行，但是它在测试功能上是不完善的，读者朋友可以已经发现了它存在以下两个问题：<br /><br />1. 我们仅仅执行了 UserService#handleUserLogin(user) 方法，但验证该方法执行结果的正确性。 <br />2. 在测试方法中直接使用 ID 为 1 的 User 对象进行测试，这相当于要求在数据库 t_user 表必须已经存在 ID 为 1 的记录，如果 t_user 中不存在这条记录，将导致测试方法执行失败。 <br /><br /><strong>准备测试数据并检测运行结果</strong><br /><br />在这节里，我们将着手解决上面所提出的两个问题，在测试用例中准备测试数据并到数据库中检测业务执行结果的正确性。<br /><br /><strong>准备测试数据</strong><br /><br />相比于在测试方法中直接访问预定的数据记录，在测试方法执行前通过程序准备一些测试数据，然后在此基础上运行测试方法是比较好的策略，因为后者不需要对数据库的状态做假设。在 TestContext 中，你可以通过使用 JUnit 4.4 的 @Before 注解达到这个目的，请看下面的代码：<br /><br /><br />清单5. 为测试方法准备数据<br /><pre name="code" class="java">package com.baobaotao.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;

import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.
AbstractTransactionalJUnit4SpringContextTests;

import com.baobaotao.dao.UserDao;
import com.baobaotao.domain.User;

@ContextConfiguration
public class TestUserService 
extends AbstractTransactionalJUnit4SpringContextTests {
    @Autowired
    private UserService userService;
   
    @Autowired
    private UserDao userDao;
   
    private int userId;
   
    @Before //① 准备测试数据
    public void prepareTestData() {
        final String  sql = "insert into t_user(user_name,password) values('tom','1234')";
        simpleJdbcTemplate.update(sql);
        KeyHolder keyHolder = new GeneratedKeyHolder();
        simpleJdbcTemplate.getJdbcOperations().update(
            new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection conn)
                    throws SQLException {
                    PreparedStatement ps = conn.prepareStatement(sql);
                    return ps;
                }
            }, keyHolder);
        userId = keyHolder.getKey().intValue();//①－1 记录测试数据的id
    }
   
    @Test
    public void handleUserLogin(){
        User user = userDao.getUserById(userId); //② 获取测试数据
        user.setLastIp("127.0.0.1");
        Date now = new Date();
        user.setLastVisit(now.getTime());
        userService.handleUserLogin(user);
    }
}</pre><br /><br /><br />JUnit 4.4 允许通过注解指定某些方法在测试方法执行前后进行调用，即是 @Before 和 @After 注解。在 Spring TestContext 中，标注 @Before 和 @After 的方法会在测试用例中每个测试方法运行前后执行，并和测试方法运行于同一个事务中。在 清单 5 中 ① 处，我们给 prepareTestData() 标注上了 @Before 注解，在该方法中准备一些测试数据，以供 TestUserService 中所有测试方法使用（这里仅有一个 handleUserLogin() 测试方法）。由于测试方法运行后，整个事务会被回滚，在 prepareTestData() 中插入的测试数据也不会持久化到数据库中，因此我们无须手工删除这条记录。<br /><br />标注 @Before 或 @After 注解的方法和测试方法运行在同一个事务中，但有时我们希望在测试方法的事务开始之前或完成之后执行某些方法以便获取数据库现场的一些情况。这时，可以使用 Spring TestContext 的 @BeforeTransaction 和 @AfterTransaction 注解来达到目录（这两个注解位于 org.springframework.test.context.transaction 包中）。<br /><br />虽然大多数业务方法都会访问数据库，但也并非所有需要测试的业务方法都需要和数据库打交道。而在默认情况下，继承于 AbstractTransactionalJUnit4SpringContextTests 测试用例的所有测试方法都将工作于事务环境下，你可以显式地通过 @NotTransactional 注解，让测试方法不工作于事务环境下。<br /><br />prepareTestData() 方法中使用到了 simpleJdbcTemplate 对象访问操作数据库，该对象在 AbstractTransactionalJUnit4SpringContextTests 抽象类中定义，只要 Spring 容器有配置数据源，simpleJdbcTemplate 就会被自动创建。同时该抽象类中还拥有一个 Spring 容器引用：applicationContext，你可以借助该成员变量访问 Spring 容器，执行获取 Bean，发布事件等操作。<br /><br />此外，AbstractTransactionalJUnit4SpringContextTests 还提供了若干个访问数据库的便捷方法，说明如下：<br /><br />protected int countRowsInTable(String tableName) ：计算数据表的记录数。 <br />protected int deleteFromTables(String... names)：删除表中的记录，可以指定多张表。 <br />protected void executeSqlScript(String sqlResourcePath, boolean continueOnError)：执行 SQL 脚本文件，在脚本文件中，其格式必须一个 SQL 语句一行。 <br /><br />在测试方法 handleUserLogin() 的 ② 处，我们通过 userDao 获取 prepareTestData() 添加的测试数据，测试方法在测试数据的基础上执行业务逻辑。使用这种测试方式后，在任何情况下运行 TestUserService 都不会发生业务逻辑之外的问题。<br /><br /><strong>检验业务逻辑的正确性</strong><br /><br />到目前为此，TestUserService 的 handleUserLogin() 测试方法仅是简单地执行 UserService#handleUserLogin() 业务方法，但并没有在业务方法执行后检查执行结果的正确性，因此这个测试是不到位的。也就是说，我们必须访问数据库以检查业务方法对数据更改是否成功：这包括积分(credits)、最后登录时间(last_visit)、最后登录 IP(last_ip)以及登录日志表中的登录日志记录(t_login_log)。下面，我们补充这项重要的检查数据正确性的工作：<br /><br /><br />清单5. 检验业务方法执行结果的正确性<br /><pre name="code" class="java">@Test
public void handleUserLogin(){
    User user = userDao.getUserById(userId);
    user.setLastIp("127.0.0.1");
    Date now = new Date();
    user.setLastVisit(now.getTime());
    userService.handleUserLogin(user);

    //------------------以下为业务执行结果检查的代码---------------------
    User newUser = userDao.getUserById(userId);
    Assert.assertEquals(5, newUser.getCredits()); //①检测积分
    //①检测最后登录时间和IP
    Assert.assertEquals(now.getTime(), newUser.getLastVisit());
    Assert.assertEquals("127.0.0.1",newUser.getLastIp());
       
    // ③检测登录记录
    String sql = "select count(1) from t_login_log where user_id=? "+
        “ and login_datetime=? and ip=?";
    int logCount =simpleJdbcTemplate.queryForInt(sql, user.getUserId(),
        user.getLastVisit(),user.getLastIp());
    Assert.assertEquals(1, logCount); 
   }</pre><br /><br />在业务方法执行后，我们查询数据库中相应记录以检查是否和期望的效果一致，如 ① 和 ② 所示。在 ③ 处，我们使用 SimpleJdbcTemplate 查询 t_login_log，以检查该表中是否已经添加了一条用户登录日志。<br /><br />注意：由于我们的 DAO 层采用 Spring JDBC 框架，它没有采用服务层缓存技术，所以可以使用 DAO 类返回数据库中的数据。如果采用 Hibernate 等 ORM 框架，由于它们采用了服务层缓存的技术，为了获取数据库中的相应数据，需要在业务方法执行后调用 HibernateTemplate.flush() 方法，将缓存中的对象同步到数据库中，这时才可以通过 SimpleJdbcTemplate 在数据库中访问业务方法的执行情况。<br /><br /><strong>小结</strong><br /><br />我们通过对一个典型的涉及数据库访问操作的 UserService 服务类的测试，讲述了使用 Spring 2.5 TestContext 测试框架进行集成测试的各项问题，这包括测试固件的自动注入、事务自动回滚、通过 SimpleJdbcTemplate 直接访问数据库以及测试数据准备等问题。<br /><br />在通过一个实际例子的学习后，我们对如何使用 TestContext 测试框架有了一个具体的认识，在此基础上我们对 Spring TestContext 测试框架体系结构进行了分析，然后剖析了 Spring 为 TestContext 嫁接到 JUnit 4.4 测试框架上所提供的两个抽象测试用例类。<br /><br />Spring 的 TestContext 测试框架不但可以整合到 JUnit 4.4 测试框架上，而且还可以整合到 JUnit 3.8 以及 TestNG 等测试框架上。目前已经提供了对 JUnit 3.8 以及 TestNG 的支持，你可以分别在 org.springframework.test.context.junit38 和 org.springframework.test.context.testng 包下找到整合的帮助类。<br /><br />转自（http://www.ibm.com/developerworks/cn/java/j-lo-spring25-test/?S_TACT=105AGX52&S_CMP=techcsdn）
          <br/>
          <span style="color:red;">
            <a href="http://guoli83945.javaeye.com/blog/183314#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 16 Apr 2008 11:43:34 +0800</pubDate>
        <link>http://guoli83945.javaeye.com/blog/183314</link>
        <guid>http://guoli83945.javaeye.com/blog/183314</guid>
      </item>
      <item>
        <title>使用 Spring 2.5 注释驱动的 IoC 功能(转)</title>
        <author>guoli83945</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://guoli83945.javaeye.com">guoli83945</a>&nbsp;
          链接：<a href="http://guoli83945.javaeye.com/blog/183310" style="color:red;">http://guoli83945.javaeye.com/blog/183310</a>&nbsp;
          发表时间: 2008年04月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          概述<br /><br />注释配置相对于 XML 配置具有很多的优势：<br /><br />它可以充分利用 Java 的反射机制获取类结构信息，这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时，我们就不需要指定 PO 的属性名、类型等信息，如果关系表字段和 PO 属性名、类型都一致，您甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java 反射机制获取。 <br />注释和 Java 代码位于一个文件中，而 XML 配置采用独立的配置文件，大多数配置信息在程序开发完成后都不会调整，如果配置信息和 Java 代码放在一起，有助于增强程序的内聚性。而采用独立的 XML 配置文件，程序员在编写一个功能时，往往需要在程序文件和配置文件中不停切换，这种思维上的不连贯会降低开发效率。 <br />因此在很多情况下，注释配置比 XML 配置更受欢迎，注释配置有进一步流行的趋势。Spring 2.5 的一大增强就是引入了很多注释类，现在您已经可以使用注释配置完成大部分 XML 配置的功能。在这篇文章里，我们将向您讲述使用注释进行 Bean 定义和依赖注入的内容。<br /><br /><br /><br /><br /> <br /><br /><br /><br />原来我们是怎么做的<br /><br />在使用注释配置之前，先来回顾一下传统上是如何配置 Bean 并完成 Bean 之间依赖关系的建立。下面是 3 个类，它们分别是 Office、Car 和 Boss，这 3 个类需要在 Spring 容器中配置为 Bean：<br /><br />Office 仅有一个属性：<br /><br /><br />清单 1. Office.java<br />                <br /><pre name="code" class="java">package com.baobaotao;
public class Office {
    private String officeNo =”001”;

    //省略 get/setter

    @Override
    public String toString() {
        return "officeNo:" + officeNo;
    }
}</pre><br /> <br /><br /><br />Car 拥有两个属性：<br /><br /><br />清单 2. Car.java<br />                <br /><pre name="code" class="java">package com.baobaotao;

public class Car {
    private String brand;
    private double price;

    // 省略 get/setter

    @Override
    public String toString() {
        return "brand:" + brand + "," + "price:" + price;
    }
}</pre><br /> <br /><br /><br />Boss 拥有 Office 和 Car 类型的两个属性：<br /><br /><br />清单 3. Boss.java<br />                <br /><pre name="code" class="java">package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;

    // 省略 get/setter

    @Override
    public String toString() {
        return "car:" + car + "\n" + "office:" + office;
    }
}</pre><br /> <br /><br /><br />我们在 Spring 容器中将 Office 和 Car 声明为 Bean，并注入到 Boss Bean 中：下面是使用传统 XML 完成这个工作的配置文件 beans.xml：<br /><br /><br />清单 4. beans.xml 将以上三个类配置成 Bean<br />                <br /><pre name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8" ?>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    &lt;bean id="boss" class="com.baobaotao.Boss">
        &lt;property name="car" ref="car"/>
        &lt;property name="office" ref="office" />
    &lt;/bean>
    &lt;bean id="office" class="com.baobaotao.Office">
        &lt;property name="officeNo" value="002"/>
    &lt;/bean>
    &lt;bean id="car" class="com.baobaotao.Car" scope="singleton">
        &lt;property name="brand" value=" 红旗 CA72"/>
        &lt;property name="price" value="2000"/>
    &lt;/bean>
&lt;/beans></pre> <br /><br /><br />当我们运行以下代码时，控制台将正确打出 boss 的信息：<br /><br /><br />清单 5. 测试类：AnnoIoCTest.java<br />                <br /><pre name="code" class="java">import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnoIoCTest {

    public static void main(String[] args) {
        String[] locations = {"beans.xml"};
        ApplicationContext ctx = 
		    new ClassPathXmlApplicationContext(locations);
        Boss boss = (Boss) ctx.getBean("boss");
        System.out.println(boss);
    }
}
 </pre><br /><br />这说明 Spring 容器已经正确完成了 Bean 创建和装配的工作。<br /><br /><br /><br /><br /><br /><br />使用 @Autowired 注释<br /><br />Spring 2.5 引入了 @Autowired 注释，它可以对类成员变量、方法及构造函数进行标注，完成自动装配的工作。来看一下使用 @Autowired 进行成员变量自动注入的代码：<br /><br /><br />清单 6. 使用 @Autowired 注释的 Boss.java<br />                <br /><pre name="code" class="java">package com.baobaotao;
import org.springframework.beans.factory.annotation.Autowired;

public class Boss {

    @Autowired
    private Car car;

    @Autowired
    private Office office;

    …
}</pre> <br /><br /><br />Spring 通过一个 BeanPostProcessor 对 @Autowired 进行解析，所以要让 @Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。<br /><br /><br />清单 7. 让 @Autowired 注释工作起来<br />                <br /><pre name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8" ?>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    &lt;!-- 该 BeanPostProcessor 将自动起作用，对标注 @Autowired 的 Bean 进行自动注入 -->
    &lt;bean class="org.springframework.beans.factory.annotation.
        AutowiredAnnotationBeanPostProcessor"/>

    &lt;!-- 移除 boss Bean 的属性注入配置的信息 -->
    &lt;bean id="boss" class="com.baobaotao.Boss"/>
 
    &lt;bean id="office" class="com.baobaotao.Office">
        &lt;property name="officeNo" value="001"/>
    &lt;/bean>
    &lt;bean id="car" class="com.baobaotao.Car" scope="singleton">
        &lt;property name="brand" value=" 红旗 CA72"/>
        &lt;property name="price" value="2000"/>
    &lt;/bean>
&lt;/beans></pre> <br /><br /><br />这样，当 Spring 容器启动时，AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean，当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配（默认按类型匹配）的 Bean，并注入到对应的地方中去。<br /><br />按照上面的配置，Spring 将直接采用 Java 反射机制对 Boss 中的 car 和 office 这两个私有成员变量进行自动注入。所以对成员变量使用 @Autowired 后，您大可将它们的 setter 方法（setCar() 和 setOffice()）从 Boss 中删除。<br /><br />当然，您也可以通过 @Autowired 对方法或构造函数进行标注，来看下面的代码：<br /><br /><br />清单 8. 将 @Autowired 注释标注在 Setter 方法上<br />                <br /><pre name="code" class="java">package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;

     @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
 
    @Autowired
    public void setOffice(Office office) {
        this.office = office;
    }
    …
}</pre> <br /><br /><br />这时，@Autowired 将查找被标注的方法的入参类型的 Bean，并调用方法自动注入这些 Bean。而下面的使用方法则对构造函数进行标注：<br /><br /><br />清单 9. 将 @Autowired 注释标注在构造函数上<br />                <br /><pre name="code" class="java">package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;
 
    @Autowired
    public Boss(Car car ,Office office){
        this.car = car;
        this.office = office ;
    }
 
    …
}</pre> <br /><br /><br />由于 Boss() 构造函数有两个入参，分别是 car 和 office，@Autowired 将分别寻找和它们类型匹配的 Bean，将它们作为 Boss(Car car ,Office office) 的入参来创建 Boss Bean。<br /><br /><br /><br /><br /><br /><br />当候选 Bean 数目不为 1 时的应对方法<br /><br />在默认情况下使用 @Autowired 注释进行自动注入时，Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时，Spring 容器将抛出 BeanCreationException 异常，并指出必须至少拥有一个匹配的 Bean。我们可以来做一个实验：<br /><br /><br />清单 10. 候选 Bean 数目为 0 时<br />                <br /><pre name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8" ?>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">
 
    &lt;bean class="org.springframework.beans.factory.annotation.
        AutowiredAnnotationBeanPostProcessor"/> 

    &lt;bean id="boss" class="com.baobaotao.Boss"/>

    &lt;!-- 将 office Bean 注释掉 -->
    &lt;!-- &lt;bean id="office" class="com.baobaotao.Office">
    &lt;property name="officeNo" value="001"/>
    &lt;/bean>-->

    &lt;bean id="car" class="com.baobaotao.Car" scope="singleton">
        &lt;property name="brand" value=" 红旗 CA72"/>
        &lt;property name="price" value="2000"/>
    &lt;/bean>
&lt;/beans></pre> <br /><br /><br />由于 office Bean 被注释掉了，所以 Spring 容器中将没有类型为 Office 的 Bean 了，而 Boss 的 office 属性标注了 @Autowired，当启动 Spring 容器时，异常就产生了。<br /><br />当不能确定 Spring 容器中一定拥有某个类的 Bean 时，可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false)，这等于告诉 Spring：在找不到匹配 Bean 时也不报错。来看一下具体的例子：<br /><br /><br />清单 11. 使用 @Autowired(required = false)<br />                <br /><pre name="code" class="java">package com.baobaotao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;

public class Boss {

    private Car car;
    private Office office;

    @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
    @Autowired(required = false)
    public void setOffice(Office office) {
        this.office = office;
    }
    …
}</pre> <br /><br /><br />当然，一般情况下，使用 @Autowired 的地方都是需要注入 Bean 的，使用了自动注入而又允许不注入的情况一般仅会在开发期或测试期碰到（如为了快速启动 Spring 容器，仅引入一些模块的 Spring 配置文件），所以 @Autowired(required = false) 会很少用到。<br /><br />和找不到一个类型匹配 Bean 相反的一个错误是：如果 Spring 容器中拥有多个候选 Bean，Spring 容器在启动时也会抛出 BeanCreationException 异常。来看下面的例子：<br /><br /><br />清单 12. 在 beans.xml 中配置两个 Office 类型的 Bean<br />                <br />… <br /><pre name="code" class="xml">&lt;bean id="office" class="com.baobaotao.Office">
    &lt;property name="officeNo" value="001"/>
&lt;/bean>
&lt;bean id="office2" class="com.baobaotao.Office">
    &lt;property name="officeNo" value="001"/>
&lt;/bean></pre>…<br /> <br /><br /><br />我们在 Spring 容器中配置了两个类型为 Office 类型的 Bean，当对 Boss 的 office 成员变量进行自动注入时，Spring 容器将无法确定到底要用哪一个 Bean，因此异常发生了。<br /><br />Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名称，这样歧义就消除了，可以通过下面的方法解决异常：<br /><br /><br />清单 13. 使用 @Qualifier 注释指定注入 Bean 的名称<br /><pre name="code" class="java">                
@Autowired
public void setOffice(@Qualifier("office")Office office) {
    this.office = office;
}
 </pre><br /><br /><br />@Qualifier("office") 中的 office 是 Bean 的名称，所以 @Autowired 和 @Qualifier 结合使用时，自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释，而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同，所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。下面是对成员变量和构造函数入参进行注释的代码：<br /><br />对成员变量进行注释：<br /><br /><br />清单 14. 对成员变量使用 @Qualifier 注释<br />                <br /><pre name="code" class="java">public class Boss {
    @Autowired
    private Car car;
 
    @Autowired
    @Qualifier("office")
    private Office office;
    …
}</pre><br /> <br /><br /><br />对构造函数入参进行注释：<br /><br /><br />清单 15. 对构造函数变量使用 @Qualifier 注释<br /><pre name="code" class="java">                
public class Boss {
    private Car car;
    private Office office;

    @Autowired
    public Boss(Car car , @Qualifier("office")Office office){
        this.car = car;
        this.office = office ;
	}
}
 </pre><br /><br /><br />@Qualifier 只能和 @Autowired 结合使用，是对 @Autowired 有益的补充。一般来讲，@Qualifier 对方法签名中入参进行注释会降低代码的可读性，而对成员变量注释则相对好一些。<br /><br /><br /><br /><br /><br />使用 JSR-250 的注释<br /><br />Spring 不但支持自己定义的 @Autowired 的注释，还支持几个由 JSR-250 规范定义的注释，它们分别是 @Resource、@PostConstruct 以及 @PreDestroy。<br /><br />@Resource<br /><br />@Resource 的作用相当于 @Autowired，只不过 @Autowired 按 byType 自动注入，面 @Resource 默认按 byName 自动注入罢了。@Resource 有两个属性是比较重要的，分别是 name 和 type，Spring 将 @Resource 注释的 name 属性解析为 Bean 的名字，而 type 属性则解析为 Bean 的类型。所以如果使用 name 属性，则使用 byName 的自动注入策略，而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性，这时将通过反射机制使用 byName 自动注入策略。<br /><br />Resource 注释类位于 Spring 发布包的 lib/j2ee/common-annotations.jar 类包中，因此在使用之前必须将其加入到项目的类库中。来看一个使用 @Resource 的例子：<br /><br /><br />清单 16. 使用 @Resource 注释的 Boss.java<br />                <br /><pre name="code" class="java">package com.baobaotao;

import javax.annotation.Resource;

public class Boss {
    // 自动注入类型为 Car 的 Bean
    @Resource
    private Car car;

    // 自动注入 bean 名称为 office 的 Bean
    @Resource(name = "office")
    private Office office;
}
 </pre><br /><br /><br />一般情况下，我们无需使用类似于 @Resource(type=Car.class) 的注释方式，因为 Bean 的类型信息可以通过 Java 反射从代码中获取。<br /><br />要让 JSR-250 的注释生效，除了在 Bean 类中标注这些注释外，还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor：<br /><br /><pre name="code" class="xml">&lt;bean 
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
 </pre><br /><br />CommonAnnotationBeanPostProcessor 实现了 BeanPostProcessor 接口，它负责扫描使用了 JSR-250 注释的 Bean，并对它们进行相应的操作。<br /><br />@PostConstruct 和 @PreDestroy<br /><br />Spring 容器中的 Bean 是有生命周期的，Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作，您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法，也可以通过 &lt;bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。关于 Spring 的生命周期，笔者在《精通 Spring 2.x—企业应用开发精解》第 3 章进行了详细的描述，有兴趣的读者可以查阅。<br /><br />JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类，分别是 @PostConstruct 和 @PreDestroy，这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用，而标注了 @PreDestroy 的方法将在类销毁之前调用。<br /><br /><br />清单 17. 使用 @PostConstruct 和 @PreDestroy 注释的 Boss.java<br />                <br /><pre name="code" class="java">package com.baobaotao;

import javax.annotation.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class Boss {
    @Resource
    private Car car;

    @Resource(name = "office")
    private Office office;

    @PostConstruct
    public void postConstruct1(){
        System.out.println("postConstruct1");
    }

    @PreDestroy
    public void preDestroy1(){
        System.out.println("preDestroy1"); 
    }
    …
}</pre><br /> <br /><br /><br />您只需要在方法前标注 @PostConstruct 或 @PreDestroy，这些方法就会在 Bean 初始化后或销毁之前被 Spring 容器执行了。<br /><br />我们知道，不管是通过实现 InitializingBean/DisposableBean 接口，还是通过 &lt;bean> 元素的 init-method/destroy-method 属性进行配置，都只能为 Bean 指定一个初始化 / 销毁的方法。但是使用 @PostConstruct 和 @PreDestroy 注释却可以指定多个初始化 / 销毁方法，那些被标注 @PostConstruct 或 @PreDestroy 注释的方法都会在初始化 / 销毁时被执行。<br /><br />通过以下的测试代码，您将可以看到 Bean 的初始化 / 销毁方法是如何被执行的：<br /><br /><br />清单 18. 测试类代码<br />                <br /><pre name="code" class="java">package com.baobaotao;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnoIoCTest {

    public static void main(String[] args) {
        String[] locations = {"beans.xml"};
        ClassPathXmlApplicationContext ctx = 
            new ClassPathXmlApplicationContext(locations);
        Boss boss = (Boss) ctx.getBean("boss");
        System.out.println(boss);
        ctx.destroy();// 关闭 Spring 容器，以触发 Bean 销毁方法的执行
    }
}</pre> <br /><br /><br />这时，您将看到标注了 @PostConstruct 的 postConstruct1() 方法将在 Spring 容器启动时，创建 Boss Bean 的时候被触发执行，而标注了 @PreDestroy 注释的 preDestroy1() 方法将在 Spring 容器关闭前销毁 Boss Bean 的时候被触发执行。<br /><br /><br /><br /><br /><br />使用 &lt;context:annotation-config/> 简化配置<br /><br />Spring 2.1 添加了一个新的 context 的 Schema 命名空间，该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的，它仅提供元数据信息。要使元数据信息真正起作用，必须让负责处理这些元数据的处理器工作起来。 <br /><br />而我们前面所介绍的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是处理这些注释元数据的处理器。但是直接在 Spring 配置文件中定义这些 Bean 显得比较笨拙。Spring 为我们提供了一种方便的注册这些 BeanPostProcessor 的方式，这就是 &lt;context:annotation-config/>。请看下面的配置：<br /><br /><br />清单 19. 调整 beans.xml 配置文件<br />                <br /><pre name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8" ?>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">
 
    &lt;context:annotation-config/> 

    &lt;bean id="boss" class="com.baobaotao.Boss"/>
    &lt;bean id="office" class="com.baobaotao.Office">
        &lt;property name="officeNo" value="001"/>
    &lt;/bean>
    &lt;bean id="car" class="com.baobaotao.Car" scope="singleton">
        &lt;property name="brand" value=" 红旗 CA72"/>
        &lt;property name="price" value="2000"/>
    &lt;/bean>
&lt;/beans></pre> <br /><br /><br />&lt;context:annotationconfig/> 将隐式地向 Spring 容器注册 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。<br /><br />在配置文件中使用 context 命名空间之前，必须在 &lt;beans> 元素中声明 context 命名空间。<br /><br /><br /><br /> <br /><br />使用 @Component<br /><br />虽然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能，但是 Bean 还是在 XML 文件中通过 &lt;bean> 进行定义 —— 也就是说，在 XML 配置文件中定义 Bean，通过 @Autowired 或 @Resource 为 Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 Bean，从 XML 配置文件中完全移除 Bean 定义的配置呢？答案是肯定的，我们通过 Spring 2.5 提供的 @Component 注释就可以达到这个目标了。<br /><br />下面，我们完全使用注释定义 Bean 并完成 Bean 之间装配：<br /><br /><br />清单 20. 使用 @Component 注释的 Car.java<br />                <br /><pre name="code" class="java">package com.baobaotao;

import org.springframework.stereotype.Component;

@Component
public class Car {
    …
}</pre><br /> <br /><br /><br />仅需要在类定义处，使用 @Component 注释就可以将一个类定义了 Spring 容器中的 Bean。下面的代码将 Office 定义为一个 Bean：<br /><br /><br />清单 21. 使用 @Component 注释的 Office.java<br />                <br /><pre name="code" class="java">package com.baobaotao;

import org.springframework.stereotype.Component;

@Component
public class Office {
    private String officeNo = "001";
    …
}
 </pre><br /><br /><br />这样，我们就可以在 Boss 类中通过 @Autowired 注入前面定义的 Car 和 Office Bean 了。<br /><br /><br />清单 22. 使用 @Component 注释的 Boss.java<br />                <br /><pre name="code" class="java">package com.baobaotao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("boss")
public class Boss {
    @Autowired
    private Car car;

    @Autowired
    private Office office;
    …
}</pre> <br /><br /><br />@Component 有一个可选的入参，用于指定 Bean 的名称，在 Boss 中，我们就将 Bean 名称定义为“boss”。一般情况下，Bean 都是 singleton 的，需要注入 Bean 的地方仅需要通过 byType 策略就可以自动注入了，所以大可不必指定 Bean 的名称。<br /><br />在使用 @Component 注释后，Spring 容器必须启用类扫描机制以启用注释驱动 Bean 定义和注释驱动 Bean 自动注入的策略。Spring 2.5 对 context 命名空间进行了扩展，提供了这一功能，请看下面的配置：<br /><br /><br />清单 23. 简化版的 beans.xml<br />                <br /><pre name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8" ?>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    &lt;context:component-scan base-package="com.baobaotao"/>
&lt;/beans>
 </pre><br /><br />这里，所有通过 &lt;bean> 元素定义 Bean 的配置内容已经被移除，仅需要添加一行 &lt;context:component-scan/> 配置就解决所有问题了——Spring XML 配置文件得到了极致的简化（当然配置元数据还是需要的，只不过以注释形式存在罢了）。&lt;context:component-scan/> 的 base-package 属性指定了需要扫描的类包，类包及其递归子包中所有的类都会被处理。<br /><br />&lt;context:component-scan/> 还允许定义过滤器将基包下的某些类纳入或排除。Spring 支持以下 4 种类型的过滤方式，通过下表说明：<br /><br /><br />表 1. 扫描过滤方式<br />过滤器类型 说明 <br />注释 假如 com.baobaotao.SomeAnnotation 是一个注释类，我们可以将使用该注释的类过滤出来。 <br />类名指定 通过全限定类名进行过滤，如您可以指定将 com.baobaotao.Boss 纳入扫描，而将 com.baobaotao.Car 排除在外。 <br />正则表达式 通过正则表达式定义过滤的类，如下所示： com\.baobaotao\.Default.* <br />AspectJ 表达式 通过 AspectJ 表达式定义过滤的类，如下所示： com. baobaotao..*Service+ <br /><br />下面是一个简单的例子：<br /><br /><pre name="code" class="xml">&lt;context:component-scan base-package="com.baobaotao">
    &lt;context:include-filter type="regex" 
        expression="com\.baobaotao\.service\..*"/>
    &lt;context:exclude-filter type="aspectj" 
        expression="com.baobaotao.util..*"/>
&lt;/context:component-scan></pre> <br /><br /><br />值得注意的是 &lt;context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能，同时还启用了注释驱动自动注入的功能（即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor），因此当使用 &lt;context:component-scan/> 后，就可以将 &lt;context:annotation-config/> 移除了。<br /><br />默认情况下通过 @Component 定义的 Bean 都是 singleton 的，如果需要使用其它作用范围的 Bean，可以通过 @Scope 注释来达到目标，如以下代码所示：<br /><br /><br />清单 24. 通过 @Scope 指定 Bean 的作用范围<br />                <br /><pre name="code" class="java">package com.baobaotao;
import org.springframework.context.annotation.Scope;
…
@Scope("prototype")
@Component("boss")
public class Boss {
    …
}
 </pre><br /><br /><br />这样，当从 Spring 容器中获取 boss Bean 时，每次返回的都是新的实例了。<br /><br /><br /><br /><br /><br /><br />采用具有特殊语义的注释<br /><br />Spring 2.5 中除了提供 @Component 注释外，还定义了几个拥有特殊语义的注释，它们分别是：@Repository、@Service 和 @Controller。在目前的 Spring 版本中，这 3 个注释和 @Component 是等效的，但是从注释类的命名上，很容易看出这 3 个注释分别和持久层、业务层和控制层（Web 层）相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意，但 Spring 将在以后的版本中为它们添加特殊的功能。所以，如果 Web 应用程序采用了经典的三层分层结构的话，最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释，而用 @Component 对那些比较中立的类进行注释。<br /><br /><br /><br /><br /><br /><br />注释配置和 XML 配置的适用场合<br /><br />是否有了这些 IOC 注释，我们就可以完全摒除原来 XML 配置的方式呢？答案是否定的。有以下几点原因：<br /><br />注释配置不一定在先天上优于 XML 配置。如果 Bean 的依赖关系是固定的，（如 Service 使用了哪几个 DAO 类），这种配置信息不会在部署时发生调整，那么注释配置优于 XML 配置；反之如果这种依赖关系会在部署时发生调整，XML 配置显然又优于注释配置，因为注释是对 Java 源代码的调整，您需要重新改写源代码并重新编译才可以实施调整。 <br />如果 Bean 不是自己编写的类（如 JdbcTemplate、SessionFactoryBean 等），注释配置将无法实施，此时 XML 配置是唯一可用的方式。 <br />注释配置往往是类级别的，而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction 事务注释，使用 aop/tx 命名空间的事务配置更加灵活和简单。 <br />所以在实现应用中，我们往往需要同时使用注释配置和 XML 配置，对于类级别且不会发生变动的配置可以优先考虑注释配置；而对于那些第三方类以及容易发生调整的配置则应优先考虑使用 XML 配置。Spring 会在具体实施 Bean 创建和 Bean 注入之前将这两种配置方式的元信息融合在一起。<br /><br /><br /><br /><br /><br />小结<br /><br />Spring 在 2.1 以后对注释配置提供了强力的支持，注释配置功能成为 Spring 2.5 的最大的亮点之一。合理地使用 Spring 2.5 的注释配置，可以有效减少配置的工作量，提高程序的内聚性。但是这并不意味着传统 XML 配置将走向消亡，在第三方类 Bean 的配置，以及那些诸如数据源、缓存池、持久层操作模板类、事务管理等内容的配置上，XML 配置依然拥有不可替代的地位。
          <br/>
          <span style="color:red;">
            <a href="http://guoli83945.javaeye.com/blog/183310#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 16 Apr 2008 11:37:32 +0800</pubDate>
        <link>http://guoli83945.javaeye.com/blog/183310</link>
        <guid>http://guoli83945.javaeye.com/blog/183310</guid>
      </item>
      <item>
        <title>请教高手关于事务隔离的问题</title>
        <author>guoli83945</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://guoli83945.javaeye.com">guoli83945</a>&nbsp;
          链接：<a href="http://guoli83945.javaeye.com/blog/173053" style="color:red;">http://guoli83945.javaeye.com/blog/173053</a>&nbsp;
          发表时间: 2008年03月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">private static String url = "jdbc:mysql://localhost:3306/test";
	private static String username = "root";
	private static String password = "";
	
	
	
	public static Connection  getConn() throws Exception {
		Class.forName("org.gjt.mm.mysql.Driver").newInstance();
		Connection con =  DriverManager.getConnection(url , username , password);
		return con;
	}

public static void demo3() throws Exception{ 
		
		Connection con1  = getConn();
		con1.setAutoCommit(false);
		con1.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);//设置事务的隔离级别
		String sql1 = "Select * from employee where salary=1000";
		Statement stmt1 = con1.createStatement();
		ResultSet rs1 = stmt1.executeQuery(sql1);
		while(rs1.next()){
			System.out.print(rs1.getInt(1) + " ");
			System.out.print(rs1.getString(2) + " ");
			System.out.println(rs1.getString(3));
		}
		stmt1.close();
		
		Connection con2  = getConn();
		con2.setAutoCommit(false);
		String sql2 = "insert into employee(name,salary) values(\"gigi\",1000)";
		Statement stmt2 = con2.createStatement();
		stmt2.executeUpdate(sql2);	
		con2.commit();
		stmt2.close();
		con2.close();
		
		System.out.println("===========================");
		Statement stmt11 = con1.createStatement();
		ResultSet rs11 = stmt11.executeQuery(sql1);
		while(rs11.next()){
			System.out.print(rs11.getInt(1) + " ");
			System.out.print(rs11.getString(2) + " ");
			System.out.println(rs11.getString(3));
		}
		stmt11.close();
		con1.commit();
		con1.close();
	}

	public static void main(String[] args) throws Exception {
		
		//demo1();
		//demo2();
		demo3();
	}</pre><br /><br />实际打印结果为:<br />1 gigi 1000<br />===========================<br />1 gigi 1000<br /><br />预期的结果应该是:<br />1 gigi 1000<br />===========================<br />1 gigi 1000<br />2 gigi 1000<br /><br />请高手 帮我看看这个代码.为啥隔离级别设置成ISOLATION_REPEATABLE_READ了,却不能幻像读<br />按照文档<br />ISOLATION_READ_UNCOMMITTED<br />ISOLATION_READ_COMMITTED<br />ISOLATION_REPEATABLE_READ 都是应该允许幻像读的<br />我用的是 mysql 的 InnoDB 存储引擎 .文档上说支持4个标准的事务隔离
          <br/>
          <span style="color:red;">
            <a href="http://guoli83945.javaeye.com/blog/173053#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 17 Mar 2008 16:12:38 +0800</pubDate>
        <link>http://guoli83945.javaeye.com/blog/173053</link>
        <guid>http://guoli83945.javaeye.com/blog/173053</guid>
      </item>
      <item>
        <title>使用maven自动创建数据库,导入导出数据 </title>
        <author>guoli83945</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://guoli83945.javaeye.com">guoli83945</a>&nbsp;
          链接：<a href="http://guoli83945.javaeye.com/blog/156879" style="color:red;">http://guoli83945.javaeye.com/blog/156879</a>&nbsp;
          发表时间: 2008年01月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          使用maven自动创建数据库,导入导出数据需要用到两个插件hibernate3-maven-plugin,dbunit-maven-plugin<br /> <br /><strong>hibernate3-maven-plugin</strong>,有7大功能,(这里我们只使用hbm2ddl,自动生成数据库)<br />The Maven Hibernate3 Plugin has seven goals.<br /><br />hibernate3:hbm2cfgxml: Generates hibernate.cfg.xml <br />hibernate3:hbm2ddl: Generates database schema. <br />hibernate3:hbm2doc: Generates HTML documentation for the database schema. <br />hibernate3:hbm2hbmxml: Generates a set of hbm.xml files <br />hibernate3:hbm2java: Generates Java classes from set of *.hbm.xml files <br />hibernate3:schema-export: Creates SQL DDL file and generates the database schema from set of *.hbm.xml files <br />hibernate3:schema-update: Updates the database schema based on the set of *.hbm.xml files <br /><br /><strong>dbunit-maven-plugin</strong>,可以实现数据库中数据的导入导出<br /><br />dbunit:operation: Execute a database operation using an external dataset file. <br />dbunit:export: Export database tables into a dataset file. <br />dbunit:compare: Compare a dataset with database. <br /> <br /><br />具体配置,(MYSQL为例)<br /><br />在pom.xml中添加下面的代码<br /><br /><pre name="code" class="xml">&lt;build>

  &lt;plugins>

 

     &lt;plugin>
        &lt;groupId>org.codehaus.mojo&lt;/groupId>
        &lt;artifactId>hibernate3-maven-plugin&lt;/artifactId>
        &lt;version>2.0-alpha-1&lt;/version>
        &lt;dependencies>
           &lt;dependency>
             &lt;groupId>mysql&lt;/groupId>
             &lt;artifactId>mysql-connector-java&lt;/artifactId>
             &lt;version>5.0.4&lt;/version>
          &lt;/dependency>
        &lt;/dependencies>
        &lt;configuration>
          &lt;components>
            &lt;component>
              &lt;name>hbm2ddl&lt;/name>
              &lt;implementation>
                 annotationconfiguration
              &lt;/implementation>
            &lt;/component>
          &lt;/components>
          &lt;componentProperties>
            &lt;drop>true&lt;/drop>
            &lt;jdk5>true&lt;/jdk5>
            &lt;propertyfile>
               /src/main/resources/jdbc.properties
            &lt;/propertyfile>
            &lt;configurationfile>
              /src/main/resources/hibernate.cfg.xml
            &lt;/configurationfile>
          &lt;/componentProperties>
        &lt;/configuration>
        &lt;executions>
          &lt;execution>
            &lt;phase>process-test-resources&lt;/phase>
            &lt;goals>
               &lt;goal>hbm2ddl&lt;/goal>
            &lt;/goals>
         &lt;/execution>
       &lt;/executions>
   &lt;/plugin>
 
    &lt;plugin>
       &lt;groupId>org.codehaus.mojo&lt;/groupId>
       &lt;artifactId>dbunit-maven-plugin&lt;/artifactId>
       &lt;dependencies>
          &lt;dependency>
            &lt;groupId>mysql&lt;/groupId>
            &lt;artifactId>mysql-connector-java&lt;/artifactId>
            &lt;version>5.0.4&lt;/version>
          &lt;/dependency>
        &lt;/dependencies>
        &lt;configuration>
          &lt;dataTypeFactoryName>
              org.dbunit.dataset.datatype.DefaultDataTypeFactory
          &lt;/dataTypeFactoryName>
          &lt;driver>com.mysql.jdbc.Driver&lt;/driver>
          &lt;url>
             jdbc:mysql://localhost/projectStencil?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8&sessionVariables=FOREIGN_KEY_CHECKS=0
         &lt;/url>
         &lt;username>root&lt;/username>
         &lt;password>&lt;/password>
         &lt;src>src/test/resources/sample-data.xml&lt;/src>
         &lt;type>CLEAN_INSERT&lt;/type>
       &lt;/configuration>
       &lt;executions>
          &lt;execution>
             &lt;id>test-compile&lt;/id>
             &lt;phase>test-compile&lt;/phase>
             &lt;goals>
               &lt;goal>operation&lt;/goal>
             &lt;/goals>
          &lt;/execution>
          &lt;execution>
             &lt;id>test&lt;/id>
             &lt;phase>test&lt;/phase>
             &lt;goals>
                &lt;goal>operation&lt;/goal>
            &lt;/goals>
          &lt;/execution>
       &lt;/executions>
   &lt;/plugin>
  
 

&lt;/plugins>

&lt;/build></pre><br /> <br /><br />其中jdbc.properties 内容为<br /><br />hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect<br />hibernate.connection.username=root<br />hibernate.connection.password=<br />hibernate.connection.url=jdbc:mysql://localhost/projectStencil?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8<br />hibernate.connection.driver_class=com.mysql.jdbc.Driver<br /><br /> <br /><br />hibernate.cfg.xml 举例如下<br /><br /><br /><pre name="code" class="xml">&lt;hibernate-configuration>
 &lt;session-factory>
       &lt;mapping class="com...dao.model.User" />

        ...

  &lt;/session-factory>
&lt;/hibernate-configuration></pre><br /> <br /><br />sample-data.xml 为要导入数据库的数据<br /><br />可以使用 mvn dbunit:export -Ddest=sample-data.xml 将数据库的数据导出来
          <br/>
          <span style="color:red;">
            <a href="http://guoli83945.javaeye.com/blog/156879#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Jan 2008 13:48:23 +0800</pubDate>
        <link>http://guoli83945.javaeye.com/blog/156879</link>
        <guid>http://guoli83945.javaeye.com/blog/156879</guid>
      </item>
  </channel>
</rss>