jdbc数据库连接池实现方法、编写自己的jdbc框架


JDBC JDBC JDBC JDBC 数据库连接池实现方法、 编写自己的JDBC JDBC JDBC JDBC 框架 作者:吕鹏 时间: 2011-08-062011-08-062011-08-062011-08-06 目录: 使用数据库连接池优化程序性能 (1)回 顾上 节课 内容 (2)实 现连 接池 的方 式( 自定 义、 动态 代理 ) (3)使 用开 源组 织提 供的 jar 实 现连 接池 ( DBCP,C3P0,Tomcat 等) 编写自己的 JDBC JDBC JDBC JDBC 框架 (1)为 什么 要编 写自 己的 框架 (2)元 数据 的知 识 (3)分 析相 同点 不同 点以 及实 现方 案 (4)CRUD 修改。。。。 (5)总 结反 思 一、 jdbc jdbc jdbc jdbc 数据库连接池实现方法 回 顾: 昨 天我 们讲 了分 页的 实现 ,分 页几 大要 素要 记住 ,分 页的 原理 ,将 page 信 息封 装获 得, 将 list 信 息封 装在 bean 中,传 递给 页面 显示 。。。讲 了对 大文 本的 读写 ,昨 天还 讲了 事务 ,事务 是针对多个业务需求而指定的,拿银行转账的例子来说明,事务的重要性,开启事务使用 connection。setAutoCommit(false)在数据库中相当于start stransaction 另外提交事务是 commit 回 滚事 务是 rollback(),另 外保 存点 的使 用可 以灵 活的 控制 事务 的提 交 ,Savepoint sp = conn.setSavepoint(); ;另 外事 务的 隔离 级别 也是 一个 重点 ,隔 离级 别出 现的 背景 是为 了解 决 脏读()重 复读 ()幻读()等 问题 的 ,这 三个 错误 要弄 出清 什么 概念 ,脏 读就 是指 一个 事 务读 取了 另外 一个 事务 未提 交的 数据 ;重 复读 就是 指一 个事 务读 到了 另外 一个 事务 已经 提 交 的数 据 ,导 致读 取数 据不 一致 现象 ;幻 读就 是指 一个 事务 读到 了另 外一 个事 务插 入 (insert) 的 数据 。另 外隔 离级 别有 四个 ,mysql 默 认级 别为 可重 复读 ,可 以避 免脏 读和 不可 重复 读等 问 题。 另外 几种 隔离 级别 自己 看看 。 今天我们将要学习的内容是使用数据库连接池优化程序的性能。前两天我们已经使用jdbc 做 完了 我们 的客 户管 理系 统 ,但 是呢 ,现 在我 们需 要优 化一 下 ,具 体优 化什 么呢 ?第 一数 据 库 连接 问题 ,我 们的 客户 管理 系统 是当 一个 连接 访问 服务 器后 就会 开启 一个 连接 ,带 操作 完 毕后连接返回数据库。。。这样的话当多个用户上万个用户访问我们的web 站点的时候就有 必 要为 每一 个客 户建 立一 个数 据库 连接 ,那 样数 据库 将承 担非 常大 的压 力 ,所 以我 们提 出来 连 接池 的目 的就 是为 了解 决直 接从 数据 库获 取连 接的 问题 。 连接池的原理,是指事先从数据库中获取一定数量的连接放在一个linkedlist 集合中,的那 个 用户 需要 创建 连接 的时 候从 连接 池( 链表 )中 拿出 连接 ,回 收连 接都 在连 接池 内完 成 ,与 数 据库 没有 关系 ,这 样就 大大 的缓 解了 数据 库的 压力 。请 看原 理图 : 既 然连 接池 作用 那么 大, 那 我们 就来 看一 下怎 么样 编写 我们 的数 据库 连接 池呢 ? 1111、编 写数 据库 连接 池 首先编写数据库连接池需要实现java,sql.DataSource 接口,DataSource 接口中定义了两个重 载的getConnection()方法,实现DataSource 接 口后 ,在 构造 函数 中批 量创 建与 数据 库的 连接 , 并 把创 建的 连接 加入 到 linkedList 对 象中 ,实现getConnection 方法,让 这个 方法 每次 调用 的 时 候从 linkedList 中 取出 来一 个连 接给 用户 ,当 用户 使用 完毕 connection 调用close 方 法时 , Collection 对 象要 保证 将自 己返 回到 链表 中而 不要 把连 接返 还给 数据 库。 首 先我 们来 看如 何获 取数 据库 的连 接并 且存 放在 链表 中: //初 始化 集合 staticstaticstaticstatic{ InputStream in = DataSourcePool.classclassclassclass.getClassLoader().getResourceAsStream("db.properties"); Properties prop = newnewnewnew Properties(); trytrytrytry{ prop.load(in); String driver = prop.getProperty("driver"); String url = prop.getProperty("url"); String user = prop.getProperty("user"); String password = prop.getProperty("password"); //注 册驱 动 Class.forName(driver); //通过DriverManager获 取一 批 Connection, 存入 集合 forforforfor(intintintint i=0;i<10;i++){ Connection conn = DriverManager.getConnection(url, user, password); //加 入集 合 list.add(conn); } }catchcatchcatchcatch (Exception e) { throwthrowthrowthrow newnewnewnew ExceptionInInitializerError(e); } } @Override publicpublicpublicpublic Connection getConnection() throwsthrowsthrowsthrows SQLException { ifififif(list.size() > 0){ //还 有连 接 Connection conn = list.removeFirst();//mysql的 一个 实现 System.out.println( "获 取连 接 :" + conn); MyConnection myconn = newnewnewnew MyConnection(conn,list); returnreturnreturnreturn myconn; }elseelseelseelse{ //没 有连 接 throwthrowthrowthrow newnewnewnew RuntimeException("系 统忙 ,请 稍后 ....."); } } 这 里有 问题 就是 如何 保证 当我 们回 收连 接时 候将 连接 放回 到我 们的 链表 中呢 ? 我 们之 前的 回收 连接 是返 还给 数据 库的 ,要 向实 现返 回连 接我 们必 须使 用包 装模 式了 ! 2222、包 装设 计模 式: 1111、自 定义 一个 类, 需要 实现 与被 增强 的对 象相 同的 接口 2222、在 类中 ,定 义被 增强 的实 例 3333、对 于感 兴趣 的方 法 closeclosecloseclose 进 行重 写 4444、对 于其 它方 法直 接让 实例 进行 完成 首先是第一步,自定义一个类,实现与被增强的对象(这里这个被增强的对象就是connconnconnconn , 连 接对 象, 因为 我们 要对 连接 进行 获取 和回 收 ), 相同 的接 口就 是 Connection!:Connection!:Connection!:Connection!: classclassclassclass MyConnection implementsimplementsimplementsimplements Connection{ 第 二步 ,定 义被 增强 的实 例即 connconnconnconn还 有要 回收 到链 表 listlistlistlist使 用构 造方 法初 始化 参数 : //被 增强 对象 的实 例 privateprivateprivateprivate Connection conn = nullnullnullnull; privateprivateprivateprivate LinkedList list = nullnullnullnull; publicpublicpublicpublic MyConnection(Connection conn,LinkedList list){ thisthisthisthis.conn = conn; thisthisthisthis.list = list; } 对 于感 兴趣 的方 法进 行重 写, 这里 我们 重写 closeclosecloseclose方 法: //需 要重 写该 方法 :将Connection还 入集 合 @Override publicpublicpublicpublic voidvoidvoidvoid close() throwsthrowsthrowsthrows SQLException { //自 己逻 辑 System.out.println("连 接还 池 :" + conn); list.add(conn); } 对 于其 它的 方法 我们 直接 使用 实例 完成 : @Override publicpublicpublicpublic voidvoidvoidvoid clearWarnings() throwsthrowsthrowsthrows SQLException { conn.clearWarnings(); } @Override publicpublicpublicpublic voidvoidvoidvoid commit() throwsthrowsthrowsthrows SQLException { conn.commit(); } 。。。。。。 如 此我 们说 我们 使用 这个 包装 类生 成一 个连 接返 还给 用户 : publicpublicpublicpublic Connection getConnection() throwsthrowsthrowsthrows SQLException { ifififif(list.size() > 0){ //还 有连 接 Connection conn = list.removeFirst();//mysql的 一个 实现 System.out.println( "获 取连 接 :" + conn); MyConnection myconn = newnewnewnew MyConnection(conn,list); returnreturnreturnreturn myconn; }elseelseelseelse{ //没 有连 接 throwthrowthrowthrow newnewnewnew RuntimeException("系 统忙 ,请 稍后 ....."); } } 同时又保证了这个close 方法是conn 的,即当回收连接的时候调用的是这个conn 的colose 方法。。。 这 样写 的确 可以 是使 用连 接池 完成 了对 连接 的获 取与 回收 ,但 是另 外一 方面 ,我 们看 到了 代 码的冗长性,当我们的包装类实现了与被增强对象同样的接口一样,也要实现其所有的45 个 方法 ,且 那些 方法 都没 有什 么实 际作 用。 所 以我 们必 须想 出一 套更 为完 善的 方法 来解 决连 接的 获取 与回 收 。于 是我 们使 用到 了动 态代 理:所 谓动 态代 理就 是指 在程 序运 行过 程中 ,生 成被 代理 对象 的代 理类 对象 ,让 这个 代理 类 对 象帮 助客 户获 取连 接 ,并 且释 放连 接 ,不 仅仅 如此 ,代 理类 可以 帮助 被代 理目 标做 任何 它 向 做的 事 ,包 括它 自己 不能 做的 事 ,而 我们 说如 何保 证我 们的 代理 类可 以代 理不 同的 角色 呢 , 我 们说 使用 静态 代理 是可 以代 理某 一个 具体 的类 ,而 动态 代理 接受 的是 一个 对象 ,这 个对 象 可以是任何类型对象,在调用proxy 的getProxyInstance 后,传递过去必要的参数就可以生 成 代理 类对 象了 。使 用代 理类 对象 将被 代理 对象 的方 法使 用反 射封 装成 了 Method 对象,所 以 避免 了冗 长的 代码 : 3333、使 用动 态代 理模 式优 化程 序代 码: 刚 才我 们大 体说 了下 动态 代理 的实 现过 程 ,现 在我 们具 体来 看如 何使 用动 态代 理模 式优 化我 们 的代 码 ,实 现连 接池 。前 面的 代码 都差 不多 ,都 是在 static 代 码块 中读 配置 文件 ,将 我们 的 连接 使用 for 循 环保 存在 链表 中, 然后 看我 们的 getConnection 方 法: @Override publicpublicpublicpublic Connection getConnection() throwsthrowsthrowsthrows SQLException { ifififif(list.size()>0){ finalfinalfinalfinal Connection conn = list.removeFirst(); /* * 使用Mysql 5.0驱动 * Proxy类: public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException ClassLoader loader----该类ClassLoader Class[] interfaces ----通过conn.getClass().getInterfaces()获取 InvocationHandler h---应用程序调用interfaces中方法时,使用handler处理;需 要 自己 实现 */ Object obj = Proxy.newProxyInstance(DataSourcePoolProxy.classclassclassclass.getClassLoader(), conn.getClass().getInterfaces(), newnewnewnew InvocationHandler(){ @Override publicpublicpublicpublic Object invoke(Object proxy, Method method,Object[] args) throwsthrowsthrowsthrows Throwable { //method:应 用程 序调 用代 理对 象中 的哪 个方 法 //关心close方法 ifififif(method.getName().equals("close")){ //调用close方法,需要connecion还池 System.out.println("动 态代 理, 连接 还池 :" + conn); list.add(conn); returnreturnreturnreturn nullnullnullnull; }elseelseelseelse{ //其 他方 法 ,直 接使 用被 增强 对象 完成 returnreturnreturnreturn method.invoke(conn, args); } } }); returnreturnreturnreturn (Connection)obj; }elseelseelseelse{ throwthrowthrowthrow newnewnewnew RuntimeException("系 统忙 ...."); } } 这 些代 码代 理了 包装 模式 的那 些代 码 ,使用proxy 这 个类 的动 态生 成代 理类 对象 的方 法获 取 一 个代 理类 对象 ,如 何获 得的 呢? ProxyProxyProxyProxy类:::: publicpublicpublicpublic staticstaticstaticstatic ObjectObjectObjectObject newProxyInstance(ClassLoadernewProxyInstance(ClassLoadernewProxyInstance(ClassLoadernewProxyInstance(ClassLoader loader,loader,loader,loader, Class[]Class[]Class[]Class[] interfaces,interfaces,interfaces,interfaces, InvocationHandlerInvocationHandlerInvocationHandlerInvocationHandler h)h)h)h) throwsthrowsthrowsthrows IllegalArgumentExceptionIllegalArgumentExceptionIllegalArgumentExceptionIllegalArgumentException ClassLoaderClassLoaderClassLoaderClassLoader loader----loader----loader----loader----该类ClassLoaderClassLoaderClassLoaderClassLoader Class[]Class[]Class[]Class[] interfacesinterfacesinterfacesinterfaces ----------------通过conn.getClass().getInterfaces()conn.getClass().getInterfaces()conn.getClass().getInterfaces()conn.getClass().getInterfaces()获取 InvocationHandlerInvocationHandlerInvocationHandlerInvocationHandler h---h---h---h---应用程序调用interfacesinterfacesinterfacesinterfaces中方法时,使用handlerhandlerhandlerhandler处理;;;; 需 要自 己实 现 HandlerHandlerHandlerHandler作为处理者,是真正的执行者,代替被代理对象执行closeclosecloseclose方法和其它方法。在这里 我们使用了内部类创建了这样一个处理者,当然我们也可以定义这样一个方法让它实现 InvocationHandlerInvocationHandlerInvocationHandlerInvocationHandler 接口,然后在这里使用它的对象,这种匿名内部类的方式比较简单,实 现invokeinvokeinvokeinvoke,在这个方法里面我们进行处理就OKOKOKOK了。但要注意,使用动态代理,我们要保证 我 们的 数据 库驱 动不 是 5.15.15.15.1的 ,因 为这 个驱 动有 点 bugbugbugbug。 刚 才我 们使 用了 自定 义连 接池 和动 态代 理的 方式 获取 和释 放数 据库 的连 接, 这两 种方 式 ,动 态 更加 完善 一些 ,但 是总 体来 看, 这两 种方 法都 是不 容易 想到 的, 且代 理很 多, 也很 复杂 , 但 是呢 ,数 据库 连接 池的 实现 又是 那么 有必 要 ,所 以开 源组 织为 此将 数据 库连 接池 的实 现技 术 封装 了起 来, 实现 数据 库连 接池 的方 式常 见有 以下 方式 : (1111)DBCPDBCPDBCPDBCP (2222)C3P0C3P0C3P0C3P0 (3333)TOMCATTOMCATTOMCATTOMCAT 4444、DBCPDBCPDBCPDBCP实 现数 据库 连接 池方 法 DBCP 是Apache 软 件基 金组 织下 的开 源连 接池 实现 ,使用DBCP数 据源 ,应 用程 序应 在系 统 中增 加如 下两 个 jar 文 件: Commons-dbcp.jarCommons-dbcp.jarCommons-dbcp.jarCommons-dbcp.jar: 连接 池的 实现 Commons-pool.jarCommons-pool.jarCommons-pool.jarCommons-pool.jar: 连接 池实 现的 依赖 库 Tomcat 的 连接 池正 是采 用该 连接 池来 实现 的。 该数 据库 连接 池既 可以 与应 用服 务器 整合 使 用 ,也 可由 应用 程序 独立 使用 。 示 例代 码: packagepackagepackagepackage cn.itcast.jdbc.pool.util; importimportimportimport java.io.InputStream; importimportimportimport java.sql.Connection; importimportimportimport java.sql.SQLException; importimportimportimport java.util.Properties; importimportimportimport javax.sql.DataSource; importimportimportimport org.apache.commons.dbcp.BasicDataSourceFactory; /* * 基于DBCP实 现连 接池 */ publicpublicpublicpublic classclassclassclass DataSourcePoolDBCP { //定 一个 变量 保存 datasource privateprivateprivateprivate staticstaticstaticstatic DataSource ds = nullnullnullnull; //初 始化 staticstaticstaticstatic{ InputStream in = DataSourcePoolDBCP.classclassclassclass.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties prop = newnewnewnew Properties(); trytrytrytry{ prop.load(in); //构造ds ds = BasicDataSourceFactory.createDataSource(prop); }catchcatchcatchcatch (Exception e) { throwthrowthrowthrow newnewnewnew ExceptionInInitializerError(e); } } //获 取连 接 publicpublicpublicpublic staticstaticstaticstatic Connection getConnection(){ trytrytrytry { returnreturnreturnreturn ds.getConnection(); } catchcatchcatchcatch (SQLException e) { throwthrowthrowthrow newnewnewnew RuntimeException(e); } } } (4444)使用 C3P0C3P0C3P0C3P0实现数据库连接池 使 用架 包如 下: 使用C3P0比 较简 单些 ,但 是呢 它的 配置 文件 使用 的是 xml格 式的 ,而 且命 名要 固定 统一 ,里 面 的内 容都 差不 多的 : com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/jdbc root password 50 100 50 1000 oracle.jdbc.OracleDriver jdbc:oracle:thin:@localhost:1521:orcl scott tiger 50 100 50 1000 针 对不 同的 数据 库有 不同 的连 接参 数 具 体代 码如 下: /* * 采用C3P0实 现数 据库 连接 池 */ publicpublicpublicpublic classclassclassclass DataSourcePoolC3P0 { //定 一个 C3P0数 据源 // private static ComboPooledDataSource ds = null; // static{ // try{ // ds = new ComboPooledDataSource(); // ds.setDriverClass("com.mysql.jdbc.Driver"); // ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc"); // ds.setUser("root"); // ds.setPassword("password"); // // ds.setMinPoolSize(5); // ds.setAcquireIncrement(5); // ds.setMaxPoolSize(20); //}catch(Exception ex){ // throw new ExceptionInInitializerError(ex); //} //} //使 用配 置文 件创 建 C3P0 privateprivateprivateprivate staticstaticstaticstatic ComboPooledDataSource ds = newnewnewnew ComboPooledDataSource(); publicpublicpublicpublic staticstaticstaticstatic Connection getConnection(){ trytrytrytry { returnreturnreturnreturn ds.getConnection(); } catchcatchcatchcatch (SQLException e) { throwthrowthrowthrow newnewnewnew RuntimeException(e); } } } (5555)使用 TOMCATTOMCATTOMCATTOMCAT实现数据库连接池( JAVAEEJAVAEEJAVAEEJAVAEE推荐) 其 配置 文件 必须 命名 为 content.xml:且在META-INF下 内 容为 : 实 现代 码: /* * 从web服 务器 中取 出 datasource */ public class JDBCUtil { private static DataSource ds = null; static { try{ //初 始化 Context initCtx = new InitialContext(); //得到JNDI的 容器 Context envCtx = (Context) initCtx.lookup("java:comp/env"); //在JNDI容 器中 查找 ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB"); }catch(Exception ex){ throw new ExceptionInInitializerError(ex); } } public static Connection getConnection(){ try { return ds.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } 注意:使用tomcattomcattomcattomcat创建连接池,是让服务器在启动的时候帮助你创建,那么你就必须让服 务器知道你连接数据库的驱动,但是很多同学不理解TOMCATTOMCATTOMCATTOMCAT的连接池的实现机制,把驱 动放在了webwebwebweb工程下,tomcattomcattomcattomcat注册驱动的时候是不会找webwebwebweb工程的,而是去服务器下找,所 以连接数据库的驱动应该放在TomcatTomcatTomcatTomcat的liblibliblib目录下,这样才能运行成功,得到连接,不然就 会 报错 说找 不到 驱动 。 以 上讲 的是 数据 库连 接池 的实 现方 式, 我们 简单 回顾 一下 : 首 先讲 了数 据库 连接 池出 现的 背景 是为 了解 决什 么问 题 ,后 来讲 了实 现数 据库 连接 池的 两种 方 式, 自定 义方 式和 动态 代理 ,后 来说 开源 组织 封装 好了 ,我 们讲 了他 们的 DBCP和C3P0。 TOMCAT。。。 接下 来我 们来 看下 一个 问题 : 二、编写自己的 JDBCJDBCJDBCJDBC框架: 为 什么 我们 要编 写自 己的 JDBC框 架? 当然 是为 了方 便我 们使 用 JDBC操 作我 们的 数据 库 ,那 之 前我 们的 代码 有什 么缺 点呢 ?我 们之 前的 代码 ,先 说增 删改 这三 个方 法 ,可 以看 到重 复了 很 多的 代码 ,我 们想 我们 能否 提炼 这三 个方 法使 其成 为一 个方 法 ,只 是根 据不 同的 参数 来确 定 去执 行什 么操 作? 实现 这一 设想 ,我 们首 先来 学习 一些 必备 的知 识 :就 是说 我们 如何 知道 我 们数 据库 中的 表的 信息 ,当 然如 果你 是表 的创 始人 你知 道, 但那 是现 在我 们是 框架 师 ,我 们对sql,对表一无所知,我们只是使用一些技术实现对数据库的操作,所以我们框架师是 如 何得 到数 据库 相关 信息 的呢 ? 通 过元 数据 ! 1111、元 数据 DataBaseMetaDataDataBaseMetaDataDataBaseMetaDataDataBaseMetaData 所谓元数据就是指数据库、表、列的定义信息,如何获得元数据呢?使用connection。 getDatabasemetaData()方 法, 那这 个 DataBaseMetaDataDataBaseMetaDataDataBaseMetaDataDataBaseMetaData对 象到 底有 什么 属性 或者 方法 呢? /* *数 据库 的元 数据 *getURL(): 返回 一个 String类 对象 ,代 表数 据库 的 URL。 getUserName(): 返回 连接 当前 数据 库管 理系 统的 用户 名。 getDatabaseProductName(): 返回 数据 库的 产品 名称 。 getDatabaseProductVersion(): 返回 数据 库的 版本 号。 getDriverName(): 返回 驱动 驱动 程序 的名 称。 getDriverVersion(): 返回 驱动 程序 的版 本号 。 isReadOnly(): 返回 一个 boolean值 ,指 示数 据库 是否 只允 许读 操作 。 */ 另外,元数据ParameterMetaDataParameterMetaDataParameterMetaDataParameterMetaData如何获得呢,使用PreparedStatement。 getParameterMetaData()方法获得代表PreparedStatement元数据的 ParameterMetaData对 象。 它 有两 个最 重要 的方 法: getParameterCount()getParameterCount()getParameterCount()getParameterCount() 获 得指 定参 数的 个数 getParameterType(intparam) 获 得指 定参 数的 sql类型(MySQL驱 动不 支持 该方 法 ) 被 标记 为红 色的 方法 有什 么作 业呢 ,比 如说 我们 可以 根据 指定 参数 的个 数为 参数 赋值 ,就要 使用到这个方法。那么我们如何为我们的参数赋值呢,使用pst.setObject(1,value) 就 可以 了, 那么 如果 是向 把返 回的 结果 遍历 一下 封装 到 bean当 中我 们如 何设 置呢 ,我 们又 不 知道 key和value, 这个 时候 我们 就需 要另 外一 个元 数据 了, ResultSetMetaDataResultSetMetaDataResultSetMetaDataResultSetMetaData它 可 以获 得待 用 ResultSet对 象元 数据 的对 象, 主要 方法 包括 : getColumnCount()getColumnCount()getColumnCount()getColumnCount() 返回resultsetresultsetresultsetresultset对 象的 列数 getColumnName(intgetColumnName(intgetColumnName(intgetColumnName(intcolumn)column)column)column) 获 得指 定列 的名 称 getColumnTypeName(intgetColumnTypeName(intgetColumnTypeName(intgetColumnTypeName(intcolumn)column)column)column) 获 得指 定列 的类 型 2222、 使用 元数 据简 化 JDBCJDBCJDBCJDBC代码 了 解了 上述 的相 关代 码以 后, 我们 就可 以对 我们 的 JDBC的 代码 进行 完善 了, 系统 中所 有的 实体对象都涉及到了基本的CRUD操作:他们的区别在于所发送的sqlsqlsqlsql不一样,以及sqlsqlsqlsql当 中相关参数不一样而已,因为我们可以把CUD中所有相同的代码抽取到JDBCUtils类中 update方 法中 ,并 定义 参数 接受 变化 的 sql语 句: 首先实体对象依旧少不了,我们先定义一个简单的实体对象,在数据库中创建一个personpersonpersonperson 表 ,用 来做 测试 : packagepackagepackagepackage cn.itcast.domain; publicpublicpublicpublic classclassclassclass Person { privateprivateprivateprivate intintintint id; privateprivateprivateprivate String name; privateprivateprivateprivate intintintint age; publicpublicpublicpublic intintintint getId() { returnreturnreturnreturn id; } publicpublicpublicpublic voidvoidvoidvoid setId(intintintint id) { thisthisthisthis.id = id; } publicpublicpublicpublic String getName() { returnreturnreturnreturn name; } publicpublicpublicpublic voidvoidvoidvoid setName(String name) { thisthisthisthis.name = name; } publicpublicpublicpublic intintintint getAge() { returnreturnreturnreturn age; } publicpublicpublicpublic voidvoidvoidvoid setAge(intintintint age) { thisthisthisthis.age = age; } } 获 取连 接的 方式 ,既 然我 们已 经学 习了 连接 池方 式 ,那 么我 们就 使用 连接 池吧 在这 里我 们使 用C3P0, 因为 代码 比较 少, 配置 文件 直接 拷贝 ,修 改一 下密 码即 可。 //c3p0连 接池 publicpublicpublicpublic staticstaticstaticstatic ComboPooledDataSource ds = newnewnewnew ComboPooledDataSource(); /** * 获 取数 据库 连接 *@return@return@return@return 数 据库 连接 连 接方 式( 连接 池 错c3p0) */ publicpublicpublicpublic staticstaticstaticstatic Connection getConnection(){ trytrytrytry { returnreturnreturnreturn ds.getConnection(); } catchcatchcatchcatch (Exception e) { throwthrowthrowthrow newnewnewnew RuntimeException(); } } /** * 释 放资 源 *@param@param@param@param conn 释放Connection *@param@param@param@param st 释放Statement *@param@param@param@param rs 释放ResultSet */ publicpublicpublicpublic staticstaticstaticstatic voidvoidvoidvoid release(Connection conn,Statement st,ResultSet rs){ ifififif(rs!=nullnullnullnull){ trytrytrytry { rs.close(); } catchcatchcatchcatch (SQLException e) { throwthrowthrowthrow newnewnewnew RuntimeException(e); }finallyfinallyfinallyfinally{ rs = nullnullnullnull; } } ifififif(st != nullnullnullnull){ trytrytrytry { st.close(); } catchcatchcatchcatch (SQLException e) { throwthrowthrowthrow newnewnewnew RuntimeException(e); }finallyfinallyfinallyfinally{ st = nullnullnullnull; } } ifififif(conn != nullnullnullnull){ trytrytrytry { conn.close(); } catchcatchcatchcatch (SQLException e) { throwthrowthrowthrow newnewnewnew RuntimeException(e); }finallyfinallyfinallyfinally{ conn = nullnullnullnull; } } } Update Update Update Update 方 法的 修改 : 首 先我 们想 我们 之前 的删 除 ,修 改方 法还 有增 加方 法他 们的 相同 点和 不同 点是 什么 ?相 同点 都 要获 取数 据库 连接 ,都 要创 建 preparedStatement对象,都 要使 用 pst对 象给 ?号 赋值 ,都要 去 执行 这个 sql语句,都 要关 闭连 接 。。。。不 同的 是 sql语 句不 同 ,以及sql语 句中 ?的 个数 和 什 么不 同 ,,, 简单 而言 就是 参数 不同 ,那 么我 们可 以定 义这 样一 个方 法, 接受 两个 参数 ,一 个是sql语句,一个是参数集合,在这个方法里面我们必须想到一种公有的方法让我们的pst 把这些参数值赋值给?,这个方法我们可以使用元数据ParameterMetaData对象的 getParameterCount()getParameterCount()getParameterCount()getParameterCount()方 法完 成, 代码 如下 : /** * 用 于更 新( 代替 了之 前的 增加 修改删除) *@param@param@param@param sql 执 行这 个 sql语句 *@param@param@param@param params sql语 句中 必要 的参 数 */ publicpublicpublicpublic staticstaticstaticstatic voidvoidvoidvoid update(String sql,Object[] params){ Connection conn = nullnullnullnull; PreparedStatement pst = nullnullnullnull; trytrytrytry{ conn = getConnection(); pst = conn.prepareStatement(sql); //赋值 forforforfor(intintintint i=0;i clazz; publicpublicpublicpublic BeanHander(Class clazz) { thisthisthisthis.clazz = clazz; } /** * 将ResultSet的 结果 集打 包成 对象 返回 */ publicpublicpublicpublic Object beanHander(ResultSet rs) { Object instance = nullnullnullnull;// 声 明一 个要 返回 的对 象 trytrytrytry { // 判 断如 果结 果集 不存 在的 话 就 返回 null ifififif (rs == nullnullnullnull || !rs.next()) { returnreturnreturnreturn nullnullnullnull; } instance = clazz.newInstance(); // 使 用反 射创 建实 例对 象 ResultSetMetaData meta = rs.getMetaData();// 得 到元 数据 forforforfor (intintintint i = 0; i < meta.getColumnCount(); i++) {// 遍 历元 数据 String name = meta.getColumnName(i+1);// 获 取列 名 Object value = rs.getObject(name);// 根 据列 名获 取值 Field field = clazz.getDeclaredField(name);// 使 用反 射获 取属 性字 节码 field.setAccessible(truetruetruetrue);// 打 破封 装型 field.set(instance, value);// 设 置属 性值 } } catchcatchcatchcatch (Exception e) { throwthrowthrowthrow newnewnewnew RuntimeException(e.getMessage(), e); } returnreturnreturnreturn instance; } } 如 果是 List的 话, 方法 如下 : packagepackagepackagepackage cn.itcast.myframe; importimportimportimport java.lang.reflect.Field; importimportimportimport java.sql.ResultSet; importimportimportimport java.sql.ResultSetMetaData; importimportimportimport java.util.ArrayList; importimportimportimport java.util.List; /** * 用 以将 返回 结果 封装 成一 个 List集合 *@author@author@author@author 吕鹏 * */ publicpublicpublicpublic classclassclassclass ListHander implementsimplementsimplementsimplements ResultSetHander { publicpublicpublicpublic Class clazz; publicpublicpublicpublic ListHander(Class clazz){ thisthisthisthis.clazz = clazz; } /** * 将 返回 结果 集打 包为 一个 list返回 */ publicpublicpublicpublic Object beanHander(ResultSet rs) { List list = newnewnewnew ArrayList();//声 明创 建要 返回 的 List trytrytrytry { // 判 断如 果结 果集 不存 在的 话 就 返回 null ifififif (rs == nullnullnullnull){//为 什么 ? || !rs.next()就 不出 第一 条? ? returnreturnreturnreturn nullnullnullnull; } whilewhilewhilewhile(rs.next()){ Object instance = clazz.newInstance(); // 使 用反 射创 建实 例对 象 ResultSetMetaData meta = rs.getMetaData();// 得 到元 数据 forforforfor (intintintint i = 0; i < meta.getColumnCount(); i++) {// 遍 历元 数据 String name = meta.getColumnName(i+1);// 获 取列 名 Object value = rs.getObject(name);// 根 据列 名获 取值 Field field = clazz.getDeclaredField(name);// 使用反射获取属性字节 码 field.setAccessible(truetruetruetrue);// 打 破封 装型 field.set(instance, value);// 设 置属 性值 } list.add(instance); System.out.println("list.size:"+list.size()); } } catchcatchcatchcatch (Exception e) { throwthrowthrowthrow newnewnewnew RuntimeException(e.getMessage(), e); } returnreturnreturnreturn list.size()>0?list:nullnullnullnull; } } 最后要注意的是,当调用这些方法的时候,只需要指定好sql 语句和参数数组,然后调用 update 方 法就 可以 了 ,那对query 方 法调 用 ,除了sql 和 数组 以外 ,我 们还 要传 递处 理 者,但 是我 们不 知道 处理 者是 谁啊 ,没 关系 ,我 们有 接口 ,那 如果 让使 用者 知道 我们 的接 口 有 他们 需要 的方 法呢 ,这 个时 候我 们就 需要 编写 我们 的帮 助文 档了 ,当 文档 ,和 这些 类打 包 在 一起 ,我 们也 可以 向他 们一 样发 布我 们自 己写 的框 架了 。 总 结反 思: 接 下来 ,希 望你 可以 对这 个框 架进 一步 的完 善, 争取 下次 将 sql 语 句也 分离 出程 序当 中 ,怎 么做呢,ibatis 就这么实现了,你可以把sql 语句放在xml 文件当中,执行相关参数,使用 dom4j 读取xml 文件,这样使用者把相关sql 写在xml 文件中,在调用方法的时候,将sql 语 句在 xml 文 件中 的 id 找到,在 你的 方法 中找 这个 id 所 代表 的 sql,然 后执 行 ,当然,xml 文 件除 了执 行 sql 以外,还 要执 行返 回类 型 ,返 回类 型就 要写 全名 ,因 为要 使用 反射 创建 实 例。。。 先想 好, 再去 实践 。。。。
还剩19页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 5 金币 [ 分享pdf获得金币 ] 6 人已下载

下载pdf

pdf贡献者

mousefat

贡献于2012-07-20

下载需要 5 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf