没有比人更高的山

Appfuse中文教程0

现在的Web应用开发人员是幸福的,有如此多的选择,Web层有Sturts2、JSF、Spring MVC、Tapestry等,持久层可以选择Hibernate、iBATIS或者JPA等,还有极为强大的Spring作为粘合剂,完美地运行时自动创 建所需的一切对象(依赖注入)。然而我们这些开发人员也是不幸的,因为要让一切完美的运行有太多太多的配置——XML、Annotation甚至是 Properties文件。当开始一个新项目时,要搭建起一个能正常运行、能真正简化开发、能真正实现模块化开发的Web应用开发骨架太难了,更不用说还 要加入DbUnit等自动测试框架。

但是有了Appfuse,一切变得简单起来,Appfuse是一个开源的Web应用骨架生成工具,支持上文提到的任何一种开发工具的组合,包括Web Services、工作流、任务调度等,能方便的生成Web开发中常用CRUD骨架,够诱人了吧。

吹了这么多,Appfuse其实很简单,你甚至不需要下载,因为她实质上是一个Maven插件,你运行mvn命令时,maven会自动帮你搞定任何事情,也就是说你只需要知道命令就行了,而且实际上你也不需要记住命令,http://appfuse.org/display/APF/AppFuse+QuickStart+-+Chinese 这个页面可以帮助你生成命令,而且是中文的,虽然翻译得不怎么样,不过看得懂。

选择好你需要的Web框架,将它帮你生成的mvn命令直接复制到命令行中,如果你已经按http://appfuse.org/display/APF/Development+Environment 配置好了环境变量的话,你可以看到maven开始下载一切需要的东西了。其实配置环境变量没有说得那么复杂,只需要将JDK、Maven、MySQL的bin目录都加入到path里面就行了,linux的用户请自己摸索一下,我不是很懂。

其实上面这些如果你是标准SSH(Struts2+Spring+Hibernate架构)还有一个选择就是Springside, 国人开发的,异常不错,强烈推荐,个人认为在SSH方面比Appfuse要更好一些。不过Appfuse还有个绝招,它还提供了一个VMWare 虚拟机镜像,可以直接下下来,里面已经装好了Subversion(版本控制), CruiseControl(持续集成工具) 和Trac(bug追踪工具以及wiki),不过我没有尝试,不知道具体使用起来怎么样。

第一篇就到这里,要知后事如何,且听下回分解。

VN:F [1.7.5_995]
Rating: 4.0/10 (2 votes cast)
VN:F [1.7.5_995]
Rating: -1 (from 1 vote)

DbUnit中文教程——核心组件0

注:本文基本翻译自DbUnit的文档,翻译不好朋友见谅

本文主要介绍在使用DBUnit编写测试用例的时候会常用的几个接口和类
IDatabaseConnection:接口,DbUnit的数据库连接对象,封装了java.sql.Connection
IDataSet:接口,DbUnit的数据集对象,里面可以放多个数据表
DataBaseOperation:抽象类,数据库操作类
 
IDatabaseConnection有两个子类:
DatabaseConnection:封装了一个普通的JDBC连接
DatabaseDataSourceConnection:封装了一个JDBC数据源连接
 
IDataSet是DbUnit用来存储数据的一种数据结构
FlatXmlDataSet:最普遍使用的数据集,每个XML元素代表数据表中的一行数据,XML元素的名称是表明,属性名是列名
XmlDataSet
StreamingDataSet
DatabaseDataSet
QueryDataSet
DefaultDataSet
CompositeDateSet
FilteredDataSet
XlsDataSet
ReplacementDataSet
 
DatabaseOperation
DatabaseOperation.UPDATE:这个操作将从测试数据源中读取的数据集的内容更新到数据库中,注意这个操作正确执行的前提是测试数据表已经存在,如果不存在这个测试用例将会失败
DatabaseOperation.INSERT:这个操作把从测试数据源中读取的数据集的内容插入到数据库中,注意这个操作正确执行的前提是测试数据表不存在,这个操作将新建数据表。如果测试数据表已经存在这个测试用例将会失败。另外,在执行这个操作的时候要特别注意数据集中数据表的顺序,否则可能会因为违反外键约束而造成测试用例失败
DatabaseOperation.DELETE:这个操作会从数据库中删除数据,注意,这个操作只删除数据集中存在的数据行而不是整个数据表中的数据
DatabaseOperation.DELETE_ALL:这个操作删除数据表中的所有行,注意,这个操作也只影响数据集中声明了的数据表,数据集中没有涉及到的数据表中的数据不会删除
DatabaseOperation.TRUNCATE:这个操作将删除数据集中声明的数据表,如果数据中有些表并没有在预定义的数据集中提到,这个数据表将不会被影响。注意,这个操作是按照相反的顺序执行的
DatabaseOperation.REFRESH:顾名思义,这个操作将把预定义数据集中的数据同步到数据库中,也就是说这个操作将更新数据数中已有的数据、插入数据库中没有的数据。数据库中已有的、但是数据集中没有的行将不会被影响。我们用一个产品数据库的拷贝进行测试的时候可以使用这个操作将预定义数据同步到产品数据库中
DatabaseOperation.CLEAN_INSERT:删除所有的数据表中的数据,然后插入数据集中定义的数据,如果你想保证数据库是受控的,这个你会比较喜欢。
DatabaseOperation.NONE:不执行任何操作
CompositeOperation:将多个操作组合成一个,便以维护和重用
TransactionOperation:在一个事物内执行多个操作
IdentityInsertOperation:在使用MSSQL的时候,插入数据时IDENTITY列我们是没有办法控制的,用这个就可以控制了,只有在使用MSSQL的时候才会用得到
VN:F [1.7.5_995]
Rating: 10.0/10 (3 votes cast)
VN:F [1.7.5_995]
Rating: +3 (from 3 votes)

DbUnit中文教程——基本原理和简单开始0

DBUnit是JUnit的一个扩展,对于数据库驱动的项目而言(基本上所有的Web项目都是数据库驱动的),对于服务层的单元测试非常麻烦,因为不能保证每次测试时数据库都是同一个状态,所以开发者不敢写断言(assertEquals())。我个人也是因为这个原因所以对驱动测试开发总是敬而远之。

有了DBUnit,一切都变了,DBUnit的目的就是在每个单元测试运行之前将数据库初始化成一个预定义的状态,以保证单元测试时的断言不会因为数据库状态发生了变化而失败,同时可以解决前一个单元测试失败导致对数据库的操作未按照测试用例执行而影响后一个单元测试的问题。

DBUnit可以将数据库中的数据导出成XML数据集,也可以将XML数据集重新导入到数据库中,这也是DBUnit实现测试前初始化数据库状态的实现方法,而且,DBUnit还可以验证数据库中的模式和数据是不是和XML数据集中的模式和数据是否一样,也就是真正意义上的“数据库测试”,不过DBUnit貌似还不支持数据库存储过程、触发器、视图等高级功能的测试。DBUnit2.0增强了对于流模式下巨型XML数据集的支持。

相信信仰单元测试驱动开发或者有意向在开发中加入单元测试的开发者很容易理解上面的叙述,下面用一个示例演示DBUnit的功能。

假设我们开发中已经建立了一个数据库,里面只有一个数据表user:

create table user
(
id bigint auto_increament primary key,
username char(48) not null,
password char(48) not null,
name varchar(48) not null
);

其中有一行数据:
insert into user(id, username, password, name) values(1, 'admin', '123', 'Administrator');

第一步是根据测试数据建立XML数据集,DBUnit用下面的格式来表示一行数据:
<user id="1" username="admin" password="123" name="Administrator" />

一个XML数据集应该具有如下格式:

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <user id="1" username="admin1" password="123" name="Administrator1" />
    <user id="2" username="admin2" password="123" name="Administrator2" />
    <user id="3" username="admin3" password="123" name="Administrator3" />
</dataset>

 

一般,最好对每个需要数据库驱动的测试用例都建立一个XML数据集,一方面可以保证单元测试之间完全的数据独立,另一方面可以只设置测试需要的数据,尽量使XML数据集保持简单可控。

第二步就可以开始编码了,DBUnit提供了一个抽象类DatabaseTestCase,这个类继承了JUnit的TestCase类,其中定义了两个抽象方法: 
/**
 * 返回测试用的数据库连接对象
 */
protected abstract IDatabaseConnection getConnection() throws Exception;

/**
 * 返回测试用XML数据集对象
 */
protected abstract IDataSet getDataSet() throws Exception; 

getConnection()的返回值类型为IDatabaseConnection,对java.sql.Connection进行了简单的封装,可以简单的实现如下:
 protected IDatabaseConnection getConnection() throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/dbunitdemo", "root", "admin");
    return new MySqlConnection(conn, "dbunitdemo");//本人用的是MySQL
}

getDataSet()的返回值是IDataSet类型,即从XML数据集中读取数据和结构,实例化为IDataSet对象,可以简单实现如下:
protected IDataSet getDataSet() throws Exception {
    InputStream is = new FileInputStream("dbunitdemo-seed.xml");
    return new FlatXmlDataSet(is);
}

定义了以上两个方法后,DBUnit就可以工作了,但是不少人会就会想到一个问题,如果数据库中存在以前的数据,而我需要将这些数据删除,DBUnit会自动的帮我做这些事情么?事实上,很显然DBUnit会,其默认操作就是删除数据,同时将XML数据集中的数据插入到数据库中,不过DBUnit也允许我们通过覆盖DatabaseTestCase中的getSetUpOperation()方法来实现自己的动作,在DatabaseTestCase中默认实现是这样的:
protected DatabaseOperation getSetUpOperation() throws Exception
{
    return DatabaseOperation.CLEAN_INSERT;
}

DatabaseOperation是DBUnit定义的数据库操作对象,CLEAN_INSERT代表清空数据、插入XML数据集数据的操作。 

与SetUpOperation对象就有TearDownOperation,表示结束测试的时候做的事情,DatabaseTestCase的默认实现是什么都不做:
protected DatabaseOperation getTearDownOperation() throws Exception
{
    return DatabaseOperation.NONE;
}
同样,开发者可以根据自己的需求来改变默认操作。

基本原理就已经讲解完了,我写了一个例子程序,实现了一个领域模型类(User),一个Dao接口(IUserDao),定义了list\save\get\delete四个基本方法,一个用JDBC实现的Dao接口实现类(UserDaoImpl),建立一个UserDaoTest对JBDC版本的Dao实现类进行测试。

例子程序下载地址:dbunitdemo_jdbc_1.0.zip

另外这个工程使用maven进行build,如果你对maven还是不很了解,请查考《Maven权威指南》

下载工程后,在项目根目录下执行mvn sql:execute,将使用Maven Sql插件导入src/main/sql/dbunitdemo.sql中的数据库脚本到数据库中。

然后可以直接运行mvn test进行单元测试,测试报告会在命令行输出,同时在target\surefire-reports下也会生成DBUnit报告

VN:F [1.7.5_995]
Rating: 3.3/10 (3 votes cast)
VN:F [1.7.5_995]
Rating: 0 (from 2 votes)

maven中文教程——一个简单的应用实例0

我突然有个想法,我们使用hibernate search来构建一个全文检索(本文的内容不会重点放在hibernate search,对此感兴趣可以下载本文的代码)。假设我们的数据库中有一些数码相机的数据,对于这些数据的维护采用hibernate,这样我们可以使用hibernate search架设一个全文检索系统,以提供更好的产品检索服务。

心动不如行动,马上动手吧。

第一步,在命令行中敲入“mvn archetype:generate”,这个命令好处是可以不用我们记那么多的参数,在前面的文章中我已经说明了。

第二步,选择 “internal -> maven-archetype-quickstart ()”这一项,自需要选择编号,我这里的编号是17,这个号码在每个电脑上可能有不同。

第三部,按照提示输入相关信息,之后,maven即生成了项目的结构,如下:
Define value for groupId: : com.zhlwish
Define value for artifactId: : jpastudy
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  com.zhlwish: : com.zhlwish.jpastudy
Confirm properties configuration:
groupId: com.zhlwish
artifactId: jpastudy
version: 1.0-SNAPSHOT
package: com.zhlwish.jpastudy
 Y: : y

第四步,修改pom.xml文件,增加依赖项,我们使用mysql数据,那么需要增加mysql的jdbc驱动依赖,我们在mvn的仓库(如:http://mvnrepository.com/)找到对应的版本。同样找到hibernate-core、hibernate-search,因为hibernate-search还依赖于hibernate-annotation,因此我们也要把这些项目加入到依赖中。整个文件变成如下:
<dependencies>
  <!– 测试 –>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>

  <!– MySQL 驱动 –>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
  </dependency>

  <!– Hibernate –>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>3.3.1.GA</version>
  </dependency>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-annotations</artifactId>
    <version>3.4.0.GA</version>
  </dependency>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-search</artifactId>
    <version>3.1.0.GA</version>
  </dependency>

  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.4.2</version>
  </dependency>

  <dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.4.GA</version>
  </dependency>

  <!– lucene –>
  <dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-snowball</artifactId>
    <version>2.4.0</version>
  </dependency>
  <dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers</artifactId>
    <version>2.4.0</version>
  </dependency>
</dependencies>

第五步,加入代码,然后运行“mvn compile”编译程序,结果发现出现错误“(请使用 -source 5 或更高版本以启用泛型)”,因为我们用到了泛型和annotation,这些都是jdk5才具有的新特性,我们需要设置compile插件,具体可以参考Setting the -source and -target of the Java Compiler,根据说明,我们继续向pom文件中加入:
<build>
   <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>5</source>
          <target>5</target>
        </configuration>
      </plugin>
   </plugins>
</build>

第六步,运行“mvn complie”,这次编译成功过了哦。运行“mvn package”,在target目录下生成了jpastudy-1.0-SNAPSHOT.jar,但是我们还不能直接运行这个jar,因为所有的运行时依赖没有加入到classpath中。

第七步,我们想将所有的依赖库都打包,直接交给用户,这样用户不需要在做其他设置了,这里需要使用Assembly插件了,其说明参考Pre-defined Descriptor Files,这个参考文件也说明了有四种默认定义的打包方式,我们选择jar-with-dependencies,继续添加pom文件,在<plugins>节点下添加如下内容:
<plugin>
   <artifactId>maven-assembly-plugin</artifactId>
   <configuration>
      <descriptorRefs>
         <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
   </configuration>
</plugin>

第八步,首先清理一下前面生成的一些东西,运行“mvn clean”,然后运行“mvn assembly:assembly”(具体请参见本系列文章第一篇),在target目录中生成了jpastudy-1.0-SNAPSHOT-jar-with-dependencies.jar。好!

第九步,可以来看我们的成果了,打开MySQL数据库,运行“java -cp target/jpastudy-1.0-SNAPSHOT-jar-with-dependencies.jar com.zhlwish.jpastudy.App”,输出结果了吧:
一共检索到3条数据:
产品名称 | 产品型号
Cannon   | 1000D
Cannon   | 450D
Cannon   | 500D

我的工作完成了,大家只需要从google code的svn上checkout代码,然后运行第八步和第九步的命令就行了,把包的依赖关系和生命周期交给maven吧。本文代码svn地址:http://zhlwish.googlecode.com/svn/trunk/

VN:F [1.7.5_995]
Rating: 8.0/10 (1 vote cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)

maven中文教程——坐标与包依赖管理0

maven用“坐标(Coordinate)”这个词来唯一标识一个库文件,所谓“坐标”就是库文件的“主键”(借用一下数据库的概念),而且这个“主键”是个复合组件,包含四个部分:groupId, artifactId, version和packaging,分别对应pom文件中的下面四个结点:

<groupId>com.zhlwish</groupId>   项目开发组织标识
<artifactId>simple</artifactId>  项目标识(一般是项目名称)
<packaging>jar</packaging>       打包类型(可能是jar、war、ear)
<version>1.0-SNAPSHOT</version>  版本(按照apache版本命名规则来命名,一般为“主版本.次版本.增量版本-限定版本号”)

snapshot、release、latest是maven中常用的三个“限定版本号”,snapshot表示项目正处于活动的开发状态;latest是指项目的最新的发布版,或者最近被发布到仓库的快照版(snapshot);release是指仓库中的最后一个非快照版本。

maven最强大的地方就是能够对库文件(俗称“包、package”)的依赖进行管理。对于一个应用项目而言,不可避免的会引入很多第三方的类库。如一个项目采用struts2开发,使用struts2的2.1.6版本,而这个struts2又依赖于apache commons的某些包,如果我们不使用maven开发的话,需要下载这么多库文件,然后全部copy到项目的classpath路径下面,当依赖的第三方库越来越多,而且很多还有传递依赖,每当Java虚拟机抛出class not found exceptions时头都大了,而且一旦库升级,随之而来的又是一大堆问题。另外,如果我们将库文件纳入到版本库的管理中,也会造成版本库的急剧增大。

maven的出现解决了这个问题。她可以很好的管理这些问题,包括传递依赖。我们在pom文件里面加入:

<dependencies>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
    </dependency>
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
<dependencies>

上面代码的意思是我们为项目添加了log4j和dom4j以及junit的依赖,我们不用去找这些jar文件然后复制到classpath里面了,maven会自动去找这些包(本地或者远程仓库),每个dependency结点下面就是一个库文件的“主键”。

对于最后一个还有一个scope结点,标识了这个库文件的使用范围,有如下五种,其中compile是默认的:
compile:编译需要,在pachakge生命周期阶段如果使用”mvn assembly:assembly”命令,所有的包都会一起导出
provided:编译期间需要,但是由于部署环境中已经存在,打包时就不用输出了,如servlet.jar,在开发时编译需要的,但是部署到servlet容器中后,servlet容器肯定有这个库文件,所以就不用打包输出。
test:编译测试代码和运行测试期间需要
runtime:运行期间需要,但是编译期间不需要
system:表示这个包由jvm或者jdk提供

关于scope的详细说明请参考《Maven dependency and repository》

VN:F [1.7.5_995]
Rating: 0.0/10 (0 votes cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)


Switch to our mobile site