如何简化JDBC代码

转载 2004年08月19日 10:21:00

--------这是网络上看到的好资源,收藏之--------

问题的提出

在一个应用程序中,处理JDBC的操作是一个重复率较高的工作。当你在一个JDBC数据源上执行SQL查询时,你通常需要执行下面几个步骤:

1.生成SQL语句

2.获得连接

3.获得一个PreparedStatement对象

4.在PreparedStatement对象中设定需要送入到数据库的值

5.执行SQL语句

6.处理查询结果

除此之外,你还需要处理SQLException异常。如果上面列出的这些步骤分散在程序的各个部分的话,程序中需要多个try/catch块来处理异常。

如果我们仔细看看上面列出的步骤,就会发现在执行不同的SQL语句时,上面这些步骤中涉及到的程序代码变化不会很大:我们使用同样的方法获得数据库连接和PreperedStatement对象;使用setXXX方法来设定PreperedStatement对象中的值;处理SQL查询结果的过程也是基本不变的。在这篇文章中,我们通过定义三个JDBC模型,去除了上述六个步骤中的三个步骤,这样使得整个过程更加简单,而且具有更好的通用性。

查询模型

我们定义了一个名叫SQLProcessor的类,在该类中定义了一个executeQuery()方法来执行SQL语句,我们在实现这个方法的时候尽量保持代码的简洁性,并且传递尽可能少的参数给该方法。下面是该方法的定义:



我们知道在执行SQL语句的JDBC过程中,变化的因素有三个:SQL语句,PreparedStatement对象和如何解释和处理查询结果。在上面的方法定义中,sql中保存的就是SQL语句;pStmntValues对象数组保存的是需要放入preparedStatement对象中的值;processor参数是一个能够处理查询结果的对象,于是我们把JDBC程序涉及到的对象分成了三个部分。下面让我们来看一下executeQuery()和与它相关的一些方法的实现:



程序中有两个方法需要说明:PreparedStatementFactory.buildStatement()和processor.process()。buildStatement()方法把在pStmntValues对象数组中的所有对象送到prepareStatement对象中的相应位置。例如:

因为stmnt.setOject(int index, Object value)方法不能接受一个空对象作为参数。为了使程序能够处理空值,我们使用了自己设计的NullSQLType类。当一个NullSQLType对象被初始化的时候,将会保存数据库表中相应列的SQL类型。在上面的例子中我们可以看到,NULLSQLType对象的属性中保存了一个SQL NULL实际对应的SQL类型。我们用NULLSQLType对象的getFieldType()方法来向preparedStatement对象填入空值。

下面让我们来看一看processor.process()方法。Processor类实现了ResultProcessor接口,该接口是用来处理SQL查询结果的,它只有一个方法process(),该方法返回了处理SQL查询结果后生成的对象数组。



process()的典型实现方法是遍历查询后返回的ResultSet对象,将保存在ResultSet对象中的值转化为相应的对象放入对象数组。下面我们通过一个例子来说明如何使用这些类和接口。例如当我们需要从数据库的一张用户信息表中取出用户信息,表名称为User:

列名 数据类型
ID NUMBER
UserName VARCHAR2
Email VARCHAR2



我们需要在程序中定义一个类User来映射上面的表:



如果我们使用常规方法来读取User表中的数据,我们需要一个方法来从数据库表中读取数据,然后将数据送入User对象中。而且一旦查询语句发生变化,我们需要修改大量的代码。让我们看一看采用本文描述的解决方案情况会如何。

首先构造一个SQL语句。



然后创建一个ResultProcessor接口的实例类,通过它我们可以从查询结果中获得一个User对象。



最后,将执行SQL查询和返回User对象的指令放入getUser()方法中。

这就是我们需要做的全部工作:只需要实现一个processor类和一个getUser()方法。与传统的JDBC程序相比,在本文描述的模型中,我们不需要处理数据库连接操作,生成prepareStatement对象和异常处理部分的代码。如果需要在同一张表中根据用户名查用户ID,我们只需要在代码中申明新的查询语句,然后重用UserResultProcessor类中的大部分代码。

更新模型

如果SQL语句中涉及到更新,情况又会怎样呢?我们可以用类似于设计查询模型的方法来设计更新模型,我们需要向SQLProcessor类中增加一些新的方法。这些方法同executeQuery()和handleQuery()方法有相似之处,只是我们需要改变一下处理ResultSet对象的代码,并且把更新的行数作为方法的返回值。



上面的两个方法和处理查询的方法不同之处在于他们如何处理返回值。由于更新语句只需要返回被更新了的行数,所以我们不需要处理SQL操作返回的结果。实际上有些情况下连被更新了的行数都不需要返回,我们这样做的原因是在某些情况下需要确认更新操作已经完成。

我们设计了UpdateProcessor接口来处理Update操作返回的更新行数。



例如在程序中需要保证更新操作,更新表中的至少一条记录。在UpdateProcessor接口的实现类中就可以加入对修改行数的检测,当没有记录被更新时,processor()方法可以抛出自定义的异常;也可以将更新的行数记录到日志文件中;或者激发一个自定义的更新事件。总而言之,你可以在其中做任何事。

下面是一个使用更新模型的例子:

首先生成SQL语句



实现UpdateProcessor接口。在Processor()方法中,检查Update操作是否更新了数据。如果没有,抛出IllegalStateException异常。



最后在updateUser()方法中执行Update操作并处理结果。

?

事务模型

在数据库中,事务和独立的SQL语句的区别在于事务在生命期内使用一个数据库连接,并且AutoCommit属性必须被设为False。因此我们需要指定事务何时开始,何时结束,并且在事务结束时提交事务。我们可以重用SQLProcessor中的大部分代码来处理事务。也许在最开始读者会问为什么要把执行更新和处理更新的工作放在 executeUpdate()和handleUpdate()两个函数中完成--实际上它们是可以被合并到同一个函数中的。这样做的原因是把处理数据库连接的代码和处理SQL操作的代码分离开来。对于需要在多个SQL操作间共用数据库连接的事务模型来说,这种方案便于编码。

在事务中,我们需要保存事务的状态,特别是数据库连接的状态。前面的SQLProcessor中没有保存状态的属性,为了保证对SQLProcessor类的重用,我们设计了一个包装类,该类包装了SQLProcessor类,并且可以维护事务在生命周期内的状态。



SQLTransaction中出现了一些新方法,这些方法主要是用来处理数据库连接和进行事务管理的。当一个事务开始时,SQLTransaction对象获得一个新的数据库连接,并将连接的AutoCommit设定为False,随后的所有SQL语句都是用同一个连接。

只有当commitTransaction()被调用时,事务才会被提交。如果执行SQL语句的过程中发生了异常,程序会自动发出一个回滚申请,以恢复程序对数据库所作的改变。对于开发人员来说,不需要担心在出现异常后处理回滚或关闭连接的工作。下面是一个使用事务模型的例子。



在例子中我们只使用了更新语句(在大多数情况下事务都是由更新操作构成的),查询语句的实现方法和更新语句类似。

问题

在实际使用上面提到的这些模型时,我遇到了一些问题,下面是这些问题的小结,希望对大家有所帮助。

自定义数据库连接

在事务处理的时候,有可能发生在多个事务并存的情况下,它们使用的数据库连接不同的情况。ConnectionManager需要知道它应该从数据库连接池中取出哪一个连接。你可以简单修改一下模型来满足上面的要求。例如在executeQuery()和executeUpdate()方法中,你可以把数据库连接作为参数,然后将它们传送给ConnectionManager对象。请记住所有的连接管理都应该放在executeXXX()方法中。另外一种解决方案,也是一种更面向对象化的解决方案,是将一个连接工厂作为参数传递给SQLProcessor的构造函数。对于不同的连接工厂类型,我们需要不同的SQLProcessor对象。

ResultProcessor类的返回值:对象数组还是List?

为什么ResultProcessor接口中的process()方法返回的是对象数组呢?怎么不使用List类呢?这是由于在很多实际的应用中,SQL查询在大多数情况下值返回一行数据,在这种情况下,使用List对象会有些多余了。但是如果你确信SQL查询将返回多行结果,你可以使用List对象。

数据库操作异常

我们可以用多个自定义的数据库操作异常类来替代运行时发生的SQLException异常。最好在这些自定义的异常类时继承RuntimeException类,这样可以将这些异常进行集中处理。也许你会认为因该将异常处理放在发生异常的地方。但是我们设计这个的模型的目的之一是在JDBC应用程序开发中去掉或弱化异常处理的部分,只有使用RuntimeException我们才可能达到这个目的。


?

?

?

?

?

JDBC连接池的简单实现

先说明一下,我本身是做android开发的,java web是我的弱项,只是近来京东云免费,于是去折腾了几下,有了些许经验,特作分享。如果文章中内容有误,还请各高手指正。 我在web端,需要连接数据库...
  • maosidiaoxian
  • maosidiaoxian
  • 2014年07月02日 21:51
  • 7980

使用spring jdbc template简化jdbc数据库操作实例代码

使用spring jdbc template简化jdbc数据库操作实例代码,简化操作,包括详细测试例子。...
  • 5iasp
  • 5iasp
  • 2013年09月30日 21:51
  • 17439

Java 代码简化系列 (一)

Java趣味短码 - (第一节) 今天跟公司的童鞋聊天的时候,谈到了关于短码和代码的精简的方式,所以整理出来。 需求很简单。 首先定义一个类 class Item{ public in...
  • ttch
  • ttch
  • 2013年01月23日 23:11
  • 1839

深入体验JavaWeb开发内幕——使用元数据简化JDBC代码

业务背景:系统中所有实体对象都涉及到基本的CRUD操作: •        所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一...
  • lijizh1013
  • lijizh1013
  • 2012年11月27日 16:55
  • 872

使用元数据简化jdbc代码

使用元数据简化jdbc代码 一  简化的原因        在daoImp层中,在写增删改查的时候,每次都要重写一次封装数据库对象,创建连接对象,定义sql语句,释放资源,在这些方法中,我们可以提...
  • xiaolang827
  • xiaolang827
  • 2011年11月22日 22:28
  • 138

JDBC之使用SimpleJdbcTemplate和泛型技术简化代码

Spring的SimpleJdbcTemplate: ①SimpleJdbcTemplate内部包含了一个NamedParameterJdbcTemplate;所以NamedParameterJdb...
  • BruceLeeNumberOne
  • BruceLeeNumberOne
  • 2017年07月30日 19:05
  • 162

使用spring jdbc template简化jdbc数据库操作实例代码

  • 2013年09月30日 21:54
  • 15KB
  • 下载

JDBC的连接代码

  • 2014年09月26日 08:34
  • 698B
  • 下载

jdbc连接代码

  • 2016年09月06日 16:07
  • 2KB
  • 下载

韩顺平第70讲学生管理系统jdbc完整代码

  • 2017年11月28日 14:23
  • 8KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:如何简化JDBC代码
举报原因:
原因补充:

(最多只允许输入30个字)