`
ihuashao
  • 浏览: 4552333 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

第二十章 JDBC DAO设计

阅读更多

第二十章 JDBC DAO设计


Connection的含义

Connection表示了一个和数据库的链接,底层需要有操作系统的Socket支持,所以Connection是一种资源,既然是一种资源,就需要按照建立,打开,使用,关闭的顺序合理的使用。
Connection是Java数据库操作的基础,是进行一系列操作的基础,所有的派生的操作,例如Statement,PreparedStatement,ResultSet等都由Connection直接或者间接的衍生。

如何获得Connection呢?
方法一,使用DriverManager类来获取,前提条件是数据库驱动程序需要在classpath下(即使用数据库链接的程序按照Java的方式可以访问到)。
Connection conn = DriverManager.getConnection( "jdbc:oracle:thin:@192.168.0.1:1521:ORCL", user, pwd );
方法二,使用数据库连接池来获取
什么是数据库连接池呢,数据库连接池是标准JavaEE容器的一种服务,例如Webspher,Weblogic,Tomcat等,容器预先建立一些数据 库链接,以便应用程序使用的时候从中借取,注意有借有还,当应用程序使用完了之后会将数据库链接还回连接池。(数据源配置请参考其他文档)
使用连接池的好处是,可以预先建立链接,减小在数据库获取上的相对时间。
使用连接池获取数据库链接的方式为:
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/DataSource");
Connection conn = ds.getConnection();
由于在配置数据库连接池的时候已经定义了URL,用户名,密码等信息,所以在程序中使用的时候不需要传入这些信息。

ConnectionManager定义
Connection用来专门管理数据库链接,通常情况下ConnectionManager只有一个方法,调用这个方法将返回一个Connection 的实例。通过ConnectionManager可以封装Connection的获取方式(例如开发的时候使用DriverManager,运用的时候使 用DataSource的方式,但是不需要修改ConnectionManager之外的其他代码)和追加Connection获取之前之后的操作(例如 针对Connection的属性的设置)。
下面的代码是一个ConnectionManager的代码示例:

package com.jpleasure.jdbc.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionManager {

public static Connection getConnection() throws DaoException {
Connection conn = null;
try {
conn = DriverManager.getConnection("", "", "");
} catch (SQLException e) {
throw new DaoException("can not get database connection", e);
}
return conn;
}
}

如果需要从开发模式变为运用模式,只需要将上述代码修改为:
package com.jpleasure.jdbc.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionManager {

public static Connection getConnection() throws DaoException {
Connection conn = null;
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/dsname");
conn = ds.getConnection();
} catch(NamingException e) {
throw new DaoException("can not find datasource", e);
}catch (SQLException e) {
throw new DaoException("can not get database connection", e);
}
return conn;
}
}

如果需要预先设定Connection的一些属性,也可以在上述代码中设定,例如:
package com.jpleasure.jdbc.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionManager {

public static Connection getConnection() throws DaoException {
Connection conn = null;
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/dsname");
conn = ds.getConnection();
conn.setAutoCommit(false);
} catch(NamingException e) {
throw new DaoException("can not find datasource", e);
}catch (SQLException e) {
throw new DaoException("can not get database connection", e);
}
return conn;
}
}

CommonDao定义
属性和构造方法
通常情况下,CommonDao要有一个Connection的引用。所有一个CommonDao的实例的所有方法的调用都需要依赖于这个 Connection。需要一个Connection的另外一个原因是如果各个方法需要保证在一个事务环境中(上下文中),必须保证所有的操作都在一个 Connection上。
构造方法通常需要将类型为Connection的属性实例化,例如:

package com.jpleasure.jdbc.dao;

import java.sql.Connection;

public class CommonDao {

private Connection conn;

public CommonDao() throws DaoException {
this.conn = ConnectionManager.getConnection();
}
}

事务方法
begin()
开始一个事务,调用CommonDao的begin方法之后,所以的后续操作将会在一个事务环境内,要么全部提交,要么全部回滚。

commit()
提交一个事务,必须在begin调用之后调用。且和rollback方法互斥。

rollback()
回滚一个事务,必须在begin方法调用之后调用。且和commit方法互斥。

事务的实现有两种方法,一种是使用基于单一Connection的事务,另外一种方法是使用容器的JTA(Java Transaction API)。需要注意的是第一种方法可以在任何环境下使用,但是只能是针对单一的数据库链接。第二种方法智能在支持JTA的Java EE容器中使用(例如Websphere,Weblogic等,Tomcat默认不支持),但是支持多个Connection实例。
第一种方法代码为:
package com.jpleasure.jdbc.dao;

import java.sql.Connection;
import java.sql.SQLException;

public class CommonDao {

private Connection conn;

public CommonDao() throws DaoException {
this.conn = ConnectionManager.getConnection();
}

public void begin() throws DaoException{
if(conn != null) {
try {
conn.setAutoCommit(false);
} catch (SQLException e) {
throw new DaoException("can not begin transaction", e);
}
} else {
throw new DaoException("connection not opened!");
}
}

public void commit() throws DaoException {
try {
if (conn != null && !conn.getAutoCommit()) {
conn.commit();
conn.setAutoCommit(true);
} else {
if (conn == null) {
throw new DaoException("connection not opened!");
} else {
throw new DaoException("first begin then commit please!");
}
}
} catch (SQLException e) {
throw new DaoException("can not commit transaction!", e);
}
}

public void rollback() throws DaoException {
try {
if (conn != null && !conn.getAutoCommit()) {
conn.rollback();
conn.setAutoCommit(true);
} else {
if (conn == null) {
throw new DaoException("connection not opened!");
} else {
throw new DaoException("first begin then rollback please!");
}
}
} catch (SQLException e) {
throw new DaoException("can not rollback transaction!", e);
}
}


}

第二种我们在使用DAO的实例中介绍如何使用(@TODO)
新建两个DAO,做不同的操作,使用JTA保证事务完整。

查询方法
查询方法也许是CommonDao最常用的方法,查询方法需要将数据库的结果返回给画面。返回值我们一般不使用ResultSet,因为 ResultSet依赖于Connection,如果Connection关闭,ResultSet将不再有效,所以我们通常将ResultSet转变为 一个List之后返回。
在说明查询方法之前,我们先说说如何将数据库中的内容放在List中,我们使用一个List表示一个查询结果集合,使用一个Map表示集合中的一行,Map的key表示数据库表的字段名字,Value表示数据库字段的内容。代码为:
private List convert(ResultSet rs) throws DaoException {

// record list
List retList = new ArrayList();

try {
ResultSetMetaData meta = rs.getMetaData();

// column count
int colCount = meta.getColumnCount();

// each record
while (rs.next()) {

Map recordMap = new HashMap();

// each column
for (int i = 1; i <= colCount; i++) {
// column name
String name = meta.getColumnName(i);
// column value
Object value = rs.getObject(i);
// add column to record
recordMap.put(name, value);
}
// ad record to list
retList.add(recordMap);
}
} catch (SQLException ex) {
throw new DaoException("can not convert result set to list of map", ex);
}
return retList;
}

为了避免Sql注入的安全问题,我们通常使用PreparedStatement,在使用PreparedStatement的时候涉及到如何将传入参数设置到PreparedStatement上面,参看以下的共通方法:
private void apply(PreparedStatement pstmt, List params) throws DaoException {
try {
// if params exist
if (params != null && params.size() > 0) {
// parameters iterator
Iterator it = params.iterator();

// parameter index
int index = 1;
while(it.hasNext()) {

Object obj = it.next();
// if null set ""
if (obj == null) {
pstmt.setObject(index, "");
} else {
// else set object
pstmt.setObject(index, obj);
}

//next index
index++;
}
}
} catch (SQLException ex) {
throw new DaoException("can not apply parameter", ex);
}
}

接着我们继续说我们的查询方法,有了上述两个方法,我们的查询方法就非常简单了:
public List query(String sql, List params) throws DaoException {
List result = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = conn.prepareStatement(sql);
this.apply(pstmt, params);
rs = pstmt.executeQuery();
result = this.convert(rs);
} catch (SQLException ex) {
throw new DaoException("can not execute query", ex);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// nothing
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// nothing
}
}
}

return result;
}

特殊的查询方法(返回单值)
有时候为了方便使用,我们需要返回单值的产寻方法,例如 select max(id) from table_a, select count(id) from table_b等。以下的代码使用了上述通用的查询方法,代码为:
public Object queryOne(String sql, List params) throws DaoException {
List list = this.query(sql, params);

if(list == null || list.size() == 0) {
throw new DaoException("data not exist");
} else {
Map record = (Map)list.get(0);
if(record == null || record.size() == 0 ) {
throw new DaoException("data not exist");
} else {
return record.values().toArray()[0];
}
}
}

更新,删除,插入方法
由于在JDBC中这三个方法都是用了一个execute完成,所以这里我们也使用一个方法来完成这些功能。代码为:
public int execute(String sql, List params) throws DaoException {
int ret = 0;
PreparedStatement pstmt = null;
try {
pstmt = conn.prepareStatement(sql);
this.apply(pstmt, params);
ret = pstmt.executeUpdate();
}catch(SQLException ex) {
throw new DaoException("", ex);
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// nothing.
}
}
}

return ret;
}

批处理方法(查询)
有些时候为了便于操作,需要一次查询多条SQL语句,我们称之为批处理,实现参看以下方法,其中为了和query方法做区分,将参数和返回值都改为了数组形式。

public List[] queryBatch(String[] sqlArray, List[] paramArray) throws DaoException {
List rets = new ArrayList();
if(sqlArray.length != paramArray.length) {
throw new DaoException("sql size not equal parameter size");
} else {
for(int i = 0; i < sqlArray.length; i++) {
String sql = sqlArray[i];
List param = paramArray[i];
List ret = this.query(sql, param);
rets.add(ret);
}
return (List[])rets.toArray();
}
}


批处理方法(更新)
有些时候需要一次更新多条Sql语句,为了便于操作,添加了批处理更新操作,参看以下代码,为了和更新方法区分,将参数和返回值都改为了数组形式。
public int[] executeBatch(String[] sqlArray, List[] paramArray) throws DaoException {
List rets = new ArrayList();
if(sqlArray.length != paramArray.length) {
throw new DaoException("sql size not equal parameter size");
} else {
for(int i = 0; i < sqlArray.length; i++) {
int ret = this.execute(sqlArray[i], paramArray[i]);
rets.add(new Integer(ret));
}

int[] retArray = new int[rets.size()];
for(int i = 0; i < retArray.length; i++) {
retArray[i] = ((Integer)rets.get(i)).intValue();
}

return retArray;
}
}


资源释放
由于CommonDao有一个Connection的属性,且Connection属于稀缺资源,所以在CommonDao不需要在使用的时候需要显示的关闭Connection。代码如下:
public void close() throws DaoException{
try {
if (conn != null && conn.getAutoCommit()) {
conn.close();
} else {
if(conn == null) {
throw new DaoException("can not close null connection, first new then close");
} else {
throw new DaoException("transaction is running, rollbakc or commit befor close please.");
}
}
} catch (SQLException ex) {
throw new DaoException("Can not close common dao");
}
}

JDBC工具类(JDBCUtil Class)
在上述的代码中我们看到有很多的无用的处理,例如:
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// nothing.
}
}
为什么要有这些处理呢?说先这些处理发生的位置都是在正常处理完成之后,这些处理(例如pstmt.close())即使失败也没有影响,这个时候我们需 要做上述的无用处理,这正是JDBC API的一个小小的瑕疵。我们通常使用一个特殊的静态工具来来做补充,例如:

package com.jpleasure.jdbc.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JDBCUtil {
public void safelyClose(Connection conn) {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
//
}
}
}
public void safelyClose(PreparedStatement pstmt) {
if(pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
//
}
}
}
public void safelyClose(ResultSet rs) {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
//
}
}
}
}

异常处理
也许细心的你已经发现了一个问题,为什么所有抛出异常的地方我们都是将SQLException包装在了DaoException之内抛出呢,为什么不直 接抛出SQLException呢?有两个原因,第一,可以细化,分类Exception抛出合适的异常,添加合适的消息,第二,隔离和Dao和业务逻辑 的耦合,可以方便的修改Dao层而不会影响到业务逻辑层。另外需要注意,DaoExcetion中可以包含SQLException,这个时候可以为客户 提供更详细的错误信息,例如ORA-12524等内容,但是很少见到。

package com.jpleasure.jdbc.dao;

public class DaoException extends Exception {

public DaoException() {
super();
}

public DaoException(String message, Throwable cause) {
super(message, cause);
}

public DaoException(String message) {
super(message);
}

public DaoException(Throwable cause) {
super(cause);
}

}

分享到:
评论

相关推荐

    JAVA2核心技术(第二版中文PDF).part2.rar

    JAVA2核心技术(第二版中文PDF)本书一共17章。 目录见下: 第一章 java简介 第二章 java编程环境 第三章 java基本编程结构 第四章 对象与类 第五章 进一步介绍OOP:继承 第六章 用AWT进行图形程序设计 第七章 用AWT...

    JAVA2核心技术(第二版中文PDF).part1.rar

    JAVA2核心技术(第二版中文PDF)本书一共17章。 目录见下: 第一章 java简介 第二章 java编程环境 第三章 java基本编程结构 第四章 对象与类 第五章 进一步介绍OOP:继承 第六章 用AWT进行图形程序设计 第七章 用AWT...

    软件设计基础(从事软件开发设计必备的基础知识)

    计算机的一些语言的简介(主要以Java为主)简单易懂,非常适合初学者,学校的课程资源,制作成PDF格式,分十二个章节:第一章 JAVA语言简介、第二章 Java的图形界面、第三章 流与Java中的文件操作、第四章 数据库...

    J2EE应用与BEA.WebLogic.Server第2版.pdf

    第二章 表示逻辑 第三章 高级servlet 技术 第四章 使用 WebLogic Server JSP 第五章 WebLogic Server JDBC 和 JTA 第六章 远程方法调用和分布式命名 第七章 使用 Java 消息服务进行企业消息传递 第八章 使用会话企业...

    JAVA2核心技术(第二版中文PDF).part3.rar

    JAVA2核心技术(第二版中文PDF)本书一共17章。 目录见下: 第一章 java简介 第二章 java编程环境 第三章 java基本编程结构 第四章 对象与类 第五章 进一步介绍OOP:继承 第六章 用AWT进行图形程序设计 第七章 用AWT...

    完整版 杭州电子科技大学JAVA语言程序设计 JAVA_13 JDBC(共15页).ppt

    JAVA_2面向对象(OO)程序设计概念\第二章上机练习题.doc JAVA_3JAVA语言基础\StringTest.java JAVA_3JAVA语言基础\Test.java JAVA_3JAVA语言基础\Welcome.java JAVA_3JAVA语言基础\第三章上机练习题.doc JAVA_4Java...

    JSP+JDBC实现手机销售系统(论文+系统+开题+封面+扉页+需求分析+任务书)

    第二章 任务概述 11 2.1 系统目标 11 2.2 系统特点 11 2.3基本流程 11 2.4 费用 12 第三章 详细设计 14 3.1 程序系统的结构 14 3.2 子功能模块图 14 3.2.1 会员功能 14 3.2.2 后台管理 15 3.2.3 前台信息 15 3.2.4 ...

    基于JavaBean+JSP+JDBC+SQL实现的网上书店售书系统(源代码+数据库+论文+答辩PPT)

    第二章 开发工具和环境简介2 2.1 Java Server Page 简介2 2.2 Microsoft SQLServer2000 简介2 2.3 JDBC 驱动程序简介-4 2.4 JavaBeans 简介5 2.5 JAVA 简介-5 2.6 电子商务简介8 第三章 书店售书系统功能分析9 3.1...

    oracle database 10g 完整参考手册part1

    第20章 使用表空间 第21章 用SQL*Loader加载数据 第22章 使用Data Pump导入和导出 第23章 访问远程数据 第24章 使用物化视图 第25章 使用Oracle Text进行文本搜索 第26章 使用外部表 第27章 使用回闪查询 第28章 回...

    oracle database 11g 完整参考手册中文高清完整版part3

     第20章 高级安全性——虚拟专用数据库  第21章 高级安全性:透明数据加密  第22章 使用表空间  第23章 用sql*loader 加载数据  第24章 使用data pump export 和data pump import  第25章 访问远程数据  第...

    oracle database 11g 高清完整中文版part2

     第20章 高级安全性——虚拟专用数据库  第21章 高级安全性:透明数据加密  第22章 使用表空间  第23章 用sql*loader 加载数据  第24章 使用data pump export 和data pump import  第25章 访问远程数据  第...

    oracle database 11g完全参考手册 高清完整版part1 共3部分

     第20章 高级安全性——虚拟专用数据库  第21章 高级安全性:透明数据加密  第22章 使用表空间  第23章 用sql*loader 加载数据  第24章 使用data pump export 和data pump import  第25章 访问远程数据  第...

    基于JSP酒店管理系统的设计与实现带论文程序及演时视频

    第二章 编程环境基础知识 10 2.1 JSP介绍 10 2.2 JSP优点 10 2.3 SQL Server简介 11 2.4 “结构化查询语言”(SQL)简介 12 2.5 JDBC 13 第三章 系统分析设计 14 3.1 可行性分析 14 3.2 设计目标 15 3.3总体功能设计...

    JSP+MySql基于BBS管理系统设计与实现(源代码+论文+中英资料+开题报告+答辩PPT)

    第二章 需求分析 5 2.1需求规格 5 2.1.1系统组成 5 2.1.2功能性需求 5 2.1.3非功能性需求 7 2.2开发环境的选择 7 第三章 概要设计 8 3.1总体设计 8 3.2系统结构与程序的关系 10 3.3运行模式 10 3.4接口设计 10 3.4.1...

    JSP基于WEB网上论坛设计与实现论文

    第二章 需求分析 5 2.1需求规格 5 2.1.1系统组成 5 2.1.2功能性需求 5 2.1.3非功能性需求 7 2.2开发环境的选择 7 第三章 概要设计 8 3.1总体设计 8 3.2系统结构与程序的关系 10 3.3运行模式 10 3.4接口设计 10 3.4.1...

    基于BBS管理系统设计与实现毕业论文

    第二章 需求分析 5 2.1需求规格 5 2.1.1系统组成 5 2.1.2功能性需求 5 2.1.3非功能性需求 7 2.2开发环境的选择 7 第三章 概要设计 8 3.1总体设计 8 3.2系统结构与程序的关系 10 3.3运行模式 10 3.4接口设计 10 3.4.1...

    JAVA程序设计基础

    第二章 Java的开发环境 第三章 Java语言基础 第四章 Java面向对象的程序设计 第五章 异常处理 第六章 Java的可重用类 第七章 Java小应用程序Applet 第八章 用户界面 第九章 Java的多线程机制 第十章 Java网络编程...

    《Java程序设计自学手册》源代码

    第3篇为实例篇 第15章 第20章 主要介绍JSP环境基础 JSP语法基础 JSP的内置对象 JavaBean技术和Servlet技术等Java Web领域的基础知识 最后通过一个博客网站介绍Java Web基础知识的综合应用 本资料为书本例子的源代码...

Global site tag (gtag.js) - Google Analytics