科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件对J2EE中的DAO组件编写单元测试(3)

对J2EE中的DAO组件编写单元测试(3)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在JavaEE开发网的开发过程中,为了对DAO组件进行有效的单元测试,我们采用HSQLDB这一小巧的纯Java数据库作为测试时期的数据库环境,配合Ant,实现了自动生成数据库脚本,测试前自动初始化数据库,极大地简化了DAO组件的单元测试的编写。

作者:廖雪峰 来源:天极社区  2007年8月31日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

在编写测试类之前,我们首先准备了一个TransactionCallback抽象类,该类通过Template模式将DAO调用代码通过事务包装起来:

 public abstract class TransactionCallback {

public final Object  execute() throws Exception {

Transaction tx =  HibernateUtil.getCurrentSession().beginTransaction();

try {

Object r =  doInTransaction();

tx.commit();

return r;

}

catch(Exception e) {

tx.rollback();

throw e;

}

}

// 模板方法:

protected abstract Object  doInTransaction() throws Exception;

}

其原理是使用JDK提供的动态代理。由于JDK的动态代理只能对接口代理,因此,要求DAO组件必须实现接口。如果只有具体的实现类,则只能考虑CGLIB之类的第三方库,在此我们不作更多讨论。

下面我们需要编写DatabaseFixture,负责启动HSQLDB数据库,并在@Before方法中初始化数据库表。该DatabaseFixture可以在所有的DAO组件的单元测试类中复用:

 public class DatabaseFixture {

private static Server  server = null; // 持有HSQLDB的实例

private static final  String DATABASE_NAME = "javaeedev"; // 数据库名称

private static final  String SCHEMA_FILE = "schema.sql"; // 数据库初始化脚本

private static final  List initSqls = new ArrayList();

 @BeforeClass // 启动HSQLDB数据库

public static void  startDatabase() throws Exception {

if(server!=null)

return;

server = new  Server();

server.setDatabaseName(0,  DATABASE_NAME);

server.setDatabasePath(0, "mem:" + DATABASE_NAME);

server.setSilent(true);

server.start();

try {

Class.forName("org.hsqldb.jdbcDriver");

}

catch(ClassNotFoundException cnfe) {

throw new  RuntimeException(cnfe);

}

LineNumberReader  reader = null;

try {

reader = new  LineNumberReader(new   

InputStreamReader(DatabaseFixture.class.getClassLoader()

.getResourceAsStream(SCHEMA_FILE)));

for(;;) {

String line =  reader.readLine();

if(line==null) break;

// 将text类型的字段改为varchar(2000),因为HSQLDB不支持text:

line =  line.trim().replace(" text ", " varchar(2000)  ").replace("  

text,", " varchar(2000),");

if(!line.equals(""))

initSqls.add(line);

}

}

catch(IOException e)  {

throw new  RuntimeException(e);

}

finally {

if(reader!=null)  {

try {  reader.close(); } catch(IOException e) {}

}

}

}

 @Before // 执行初始化脚本

public void initTables()  {

for(String sql :  initSqls) {

executeSQL(sql);

}

}

static Connection  getConnection() throws SQLException {

return  DriverManager.getConnection("jdbc:hsqldb:mem:" + DATABASE_NAME, 

"sa", "");

}

static void  close(Statement stmt) {

if(stmt!=null) {

try {

stmt.close();

}

catch(SQLException e) {}

}

}

static void  close(Connection conn) {

if(conn!=null) {

try {

conn.close();

}

catch(SQLException e) {}

}

}

static void  executeSQL(String sql) {

Connection conn =  null;

Statement stmt =  null;

try {

conn =  getConnection();

boolean  autoCommit = conn.getAutoCommit();

conn.setAutoCommit(true);

stmt = conn.createStatement();

stmt.execute(sql);

conn.setAutoCommit(autoCommit);

}

catch(SQLException e)  {

log.warn("Execute failed: " + sql + "\nException: "  + e.getMessage());

}

finally {

close(stmt);

close(conn);

}

}

public static Object  createProxy(final Object target) {

return  Proxy.newProxyInstance(

target.getClass().getClassLoader(),

target.getClass().getInterfaces(),

new  InvocationHandler() {

public  Object invoke(Object proxy, final Method method, final Object[] args)

throws  Throwable {

return new TransactionCallback() {

@Override

protected Object doInTransaction() throws Exception {

return method.invoke(target, args);

}

}.execute();

}

}

);

}

}

注意DatabaseFixture的createProxy()方法,它将一个普通的DAO对象包装为在事务范围内执行的代理对象,即对于一个普通的DAO对象的方法调用前后,自动地开启事务并根据异常情况提交或回滚事务。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章