1、JDBC学习完本章后你应该知道并掌握驱动管理创建连接创建执行JDBC学习完本章后,你应该知道并掌握: 驱动管理 创建连接 创建执行者 结果集处理 缓冲结果集 批处理 预处理 调用存储过程 事务处理 锁机制 工具类15.1 概述数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它现在已经成为企业、部门乃至个人日常工作、生产和生活的基础设施,可以说现在的各种信息管理系统已经离不开各种数据库。目前常用的数据库有多种,如:甲骨文公司的Oracle、IBM公司的DB2、微软公司的SQL Server和Access、Sybase公司的Sybase SQLServer以及开源数据库MyS
2、QL、PostgreSQL等。用户可以根据自己的实际情况选择合适的数据库。随着软件应用技术的发展,数据库的应用方式也在不断改进。在早期,对数据库的访问、使用是通过编写相关的应用程序来调用目标数据库的访问接口,采用这种方法的结果是已经编写好的数据库访问应用程序无法在不同类型的数据库之间通用,同时它也增加了开发人员的学习时间。后来,为了解决数据库访问应用程序无法在不同类型的数据库之间通用的问题,微软公司制定了ODBC(Open Database Connectivity)标准,建立了一组规范,并提供了一组对数据库访问的标准API(应用程序编程接口)。这些API利用SQL(结构化查询语言)来完成大部
3、分数据库访问与使用任务。JDBC(Java Database Connectivity, Java数据库连接)是一种用于连接数据库和执行SQL语句的Java API,可以为多种关系数据库提供统一访问数据库的方法,它由一组使用Java语言编写的类和接口组成。使用JDBC提供的数据库编程统一接口规范,开发人员能够很容易地编写面向各种关系数据库的应用程序。因为JDBC接口规范是针对所有数据库而言,开发人员不论是编写Oracle数据库应用程序,还是SQL Server数据库应用程序,其调用的接口都是相同的,这样既便于开发人员学习掌握,也便于在开发中更换数据库类型时,不对数据库应用程序做大的改变。同时,
4、将Java语言和JDBC结合起来可以使程序员不必为不同的软件运行平台编写不同的应用程序,只须写一遍程序就可以让它在任何平台上实现对不同类型数据库的访问与使用,这也体现了Java语言“编写一次,处处运行”的优势。本章主要介绍在Java开发环境下如何进行数据库的应用开发,重点讲述基于JDBC开发数据库访问组件的步骤、重点及注意事项。15.2 任务分析 在应用软件中,当有业务逻辑涉及到对数据表的CRUD(Create、Retrieve、Update、Delete)操作时,通常都需要通过数据访问层来进行和实现。在这种情况下,开发者在开发业务逻辑之前,首先要进行数据访问层的设计与开发。数据访问层既可以借
5、助于象Hibernate、Ibatis这样的ORM组件来实现,也可以是开发者自己封装数据访问组件来实现。无论是Hibernate、Ibatis这样的数据访问组件或者是开发者自己开发的数据组件,其实都是对JDBC的封装,只是封装的程度有所区别。本章所介绍的就是开发者自己封装一个功能比较简单的Oracle数据访问组件来完成对数据表的CRUD操作。这主要涉及对java.sql包中Connection、Statement、ResultSet接口的使用,对Sun公司提供的CachedRowSet的使用,以及基于组件的设计思想。要完成Oracle数据组件的开发,首先需要在开发环境中导入Oracle的JDB
6、C驱动和Sun公司提供的CachedRowSet包。至于访问数据库的URL地址、用户名、密码,开发者可以将其置于配置文件中,并在程序中动态获取。时间:6课时15.3 相关知识 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一的访问方法。JDBC由一组用Java语言编写的类和接口组成,它提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序 Connection是与数据库的一次会话,SQL语句的执行和返回的结果都包含在一次会话的上下文中。通过Connect
7、ion提供的getMetaData方法可以获取表、存储过程等相关描述信息。Connection默认为自动提交,这就意味着一旦语句执行后Connection将自动提交改变。如果设置Connection自动提交失效,必须显示调用commit方法以提交变化,否则数据库数据不会发生改变 Statement用于执行静态SQL语句并返回它所生成结果的对象。默认情况下,同一时间每个Statement对象只能打开一个ResultSet对象。因此,不能用Statement交叉的打开ResultSet对象,否则会有异常发生 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。此对象具有指向其当前数据行的指
8、针。最初,指针被置于第一行之前,next方法调用将指针移动到下一行。因为该方法在ResultSet对象中没有下一行时返回false,所以可以在while循环中使用它来迭代结果集。默认的ResultSet对象不能更新,仅有一个向前移动的指针,因此只能迭代一次,且只能按从每一行到最后一行的顺序进行。可以生成可滚动或可更新的ResultSet对象。以下代码片码演示了如何生成可滚动且不受其他更新影响的、可更新的结果集。Statement stmt=con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATA
9、BLE);ResultSet接口提供用于从当前行检索列值的方法(getString、getInt等),可以使用列的索引编号或列的名称检索值。一般情况下,使用列索引较为高效,列从开始编号,列名称不区分大小写,如果多个列具有同一名称,则返回第一个匹配列的值。以下两种情况使用更新方法:1. 更新当前行中的列值。在可滚动的ResultSet对象中,移动指针到要更新的行,以下代码片段更新ResultSet对象的第五行中的name列,然后使用方法updateRow更新数据源表:rs.absolute(5);rs.updateString(“name”,”china”);rs.updateRow();2.
10、将列值插入到目标行中。可列新的ResultSet对象具有一个与其关联的特列行,该行用作构建要插入的目标行的暂存区域,以下代码片段将指针移动到插入行,构建一个三列的行,并使用方法insertRow将其插入到rs和数据源表中:rs.moveToInsertRow();rs.updateString(1,”china”);rs.updateString(2,”sichuan”);rs.updateString(3,”chengdu);rs.insertRow();rs.moveTocurrentRow();当生成ResultSet对象的Statement对象关闭、重新执行或用来从多个结果的序列检索下
11、一个结果时,ResultSet会自动关闭。ResultSet对象的列的编号、类型和属性由ResultSet.getMetaData方法返回的ResultSetMetaData对象提供。 CachedRowSet对象每次用完连接后都需要调用close方法,以释放连接资源。但是连接一旦释放后,与连接关联的结果集就没有办法使用了。CachedRowSet是存放数据行的容器,可用来在内存中存放需要使用的数据。可以把CachedRowSet想像成是在数据源之外存放数据的Cache(高速缓存),通过它可以对没有带连接的数据源进行操作。CachedRowSet具有可滚动、可更新、序列化的特点,CachedR
12、owSet对象是一个无连接的(disconnected)的RowSet,这就意味着开发者可以在Connection关闭以后对结果集数据进行操作。以下代码片段是将rs中的数据放入CachedRowSet中。CachedRowSetImpl crs = new CachedRowSetImpl();crs. populate(rs);开发者也可以通过调用setPageSize设置CachedRowSet每次包含的数据行,调用setMaxRows设置包含的最大行数,以下代码片段将从rsHandle的第10行开始的4行数据填充crs。CachedRowSet crs = new CachedRowSe
13、tImpl();crs.setMaxRows(20);crs.setPageSize(4);crs.populate(rsHandle, 10);CachedRowSet crs = new CachedRowSetImpl();crs.setPageSize(4);crs.populate(conHandle);以下代码片段演示了如何用execute方法填充CachedRowSet,该方法接收一个连接参数。更新CachedRowSet和更新ResultSet类似,但是因为rowset没有连接到数据源,所以需要在更新之后加入以下方法调用:crs.acceptChanges();15.4 工作任
14、务15.4.1 驱动管理java.sql.DriverManager管理一组 JDBC 驱动程序的基本服务,可以调用DriverManager.registerDriver(Driver driver)注册数据库的驱动,也可以用Class.forName(“驱动类名”);只有注册驱动之后,才能进行数据库的连接操作。15.4.2 创建连接Connection是和数据库的会话,是应用程序和数据库之间的桥梁,通过注册的驱动程序可以建立起连接对象,用完连接后需要把连接关闭,以释放掉相关联的资源。Class.forName(com.mysql.jdbc.Driver);/注册String url = j
15、dbc:mysql:/localhost:3306/example;String username=”root”;String pwd=”1234”;Connection conn = DriverManager.getConnection(url, username,pwd);System.out.println(conn);conn.close();15.4.3 创建执行者Statement的作用是发送SQL语句给数据库,也可以从数据库中接收相关的数据信息。Statement stmt = conn.createStatement();String sql = insert into te
16、acher(name,age)values(zhang,20);int row = stmt.executeUpdate(sql);/返回值表示影响的行数15.4.4 结果集处理ResultSet中提供了一个指针,用于指向数据行,可以通过next()、absolute(row)等方法定位行指针,一旦定位行后,可以利用ResultSet中提供的一系列get方法来获取所在行的列的值。ResultSet rs = stmt.executeQuery(select * from teacher);while(rs.next() int t_id = rs.getInt(teacher_id); Str
17、ing name = rs.getString(name); int age = rs.getInt(age); System.out.print(t_id+ +name+ +age); System.out.println();15.4.5 缓冲结果集当数据库连接关闭后,ResultSet结果集不能用,解决的办法是在关闭之前把数据放在CachedRowSet中,然后把CachedRowSet返回,这样就可以把连接关闭了。ResultSet rs = stmt.executeQuery(String sql);CachedRowSet crs = new CachedRowSet();crs.
18、 populate(rs)/缓冲结果集15.4.6 批处理当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率for (int i = 1; i = 100; i+) String sql = insert into teacher(name,age)values(wang+i+,30);stmt.addBatch(sql);if (i%10 =0) /每10条语句提交一次stmt.executeBatch();stmt.clearBatch();15.4.7 预处理PreparedStatement扩展自Statement,Pr
19、eparedStatement是用于执行预编译的SQL语句,在提高性能方面有很多优点;预处理可以处理一些特殊符号,解决Statement不能解决的问题,比如单引号、日期赋值等。预处理中要注意参数的序号是从1开始的。String sql = insert into teacher(name,age)values(?,?);PreparedStatement stmt = conn.prepareStatement(sql);stmt.setString(1, wang);stmt.setInt(2, 30);stmt.executeUpdate();15.4.8 调用存储过程请事先确定mysql
20、的驱动是否技持存储过程调用,例子中采用的驱动程序版本是mysql-connector-java-5.1.22.jarCallableStatement cs = conn. prepareCall(“call proc_add(?,?,?)”);cs.setInt(1,1);cs.setInt(2,2);cs.registerOutParameter (3, java.sql.Types.INTEGER);cs.execute();/执行存储过程intresult=stmt.getInt(3)15.4.9 事务处理事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作。这些操作作
21、为一个整体一起向系统提交,要么都执行、要么都不执行 。事务是一个不可分割的工作逻辑单元。 事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态。 原子性(Atomic):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行 一致性(Consistent):事务在完成时,必须是所有的数据都保持一致状态 隔离性(Isolated):一个事务的执行不能被
22、其他事务所影响 持久性(Durable):一个事务一旦提交,事物的操作便永久性的保存在DB中try conn.setAutoCommit(false); /将自动提交设置为false stmt.executeUpdate(修改SQL); /执行修改操作 stmt.executeQuery(查询SQL); /执行查询操作 mit(); /当两个操作成功后手动提交 catch (Exception e) conn.rollback(); /一旦其中一个操作出错都将回滚,使两个操作都不成功 15.4.10 锁机制 共享锁select * from teacher Lock in share mode
23、,针对mysql来讲必须放在事务中才有效,一旦锁定这些被锁定的数据行是不能进行修改、删除的。Mysql front中默认是自动提交的,如果想改变提交模式,可以在“数据库”莱单中取消自动提交,变为手动提交,这样每次执行完语句(insert、update、delete)后,需要选择提交才生效。表15.4.10 使用排它锁和不使用的线程对操作记录的影响线程读取操作写入操作共享锁申请排他锁申请使用共享锁可读可写可申请可申请不使用共享锁可读不可写(阻塞)可申请不可申请(阻塞) 排它锁select * from teacher for update,下表对比了使用排它锁和不使用的线程区别:表15.4.10
24、 使用排它锁和不使用的线程对操作记录的影响线程读取操作写入操作共享锁申请排他锁申请使用排他锁可读(新版本)可写可申请可申请不使用排他锁可读(旧版本)不可写(阻塞)不可申请(阻塞)不可申请(阻塞)15.4.11 工具类public class DatabaseHelper public static ResultSet executeQuery(String sql) public static void executeUpdate(String sql) 15.5 归纳总结数据库驱动URLOracle oracle.jdbc.driver.OracleDriverjdbc:oracle:thi
25、n:localhost:1521:pubsSQLServercom.microsoft.jdbc.sqlserver.SQLServerDriverjdbc:microsoft:sqlserver:/localhost:1433;DatabaseName=pubsMySQLorg.gjt.mm.mysql.Driverjdbc:mysql:/localhost/pubs? user=sa&password= &use15.6 拓展提高连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用,当一个线程需要用 JDBC 对一个数据库操作时,它从池中请求一个连接。当这个线程
26、使用完了这个连接,将它返回到连接池中,这样这就可以被其它想使用它的线程使用。Java中有较多的第三方连接池实现:dbcp、c3p0、proxool,在并发与访问量比较在的系统中采用连接池技术是比较好的选择。下面采用了proxool连接池实现: 下载组件proxool-0.9.1.zip,解压后将lib proxool-0.9.1.jar、lib proxool-cglib.jar放在执行路径中 编写conf.propertiesjdbc-0.proxool.alias=property-testjdbc-0.proxool.driver-url=jdbc:mysql:/localhost:33
27、06/examplejdbc-0.proxool.driver-class=com.mysql.jdbc.Driverjdbc-0.user=rootjdbc-0.password=1234jdbc-0.proxool.maximum-connection-count=10jdbc-0.proxool.house-keeping-test-sql=select CURRENT_DATE 测试类import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import org.logic
28、alcobwebs.proxool.ProxoolDataSource;import org.logicalcobwebs.proxool.configuration.PropertyConfigurator;public class Test Pool public static void main(String args) throws Exception PropertyConfigurator.configure(src/conf.properties); ProxoolDataSource ds=new ProxoolDataSource(property-test); Connec
29、tion connection = ds.getConnection(); System.out.println(connection); 15.7 练习与实训练习:1. 什么是Connection?2. 什么是Statement?3. 什么是ResultSet?4. CachedRowSet作用是什么?5. 打开结果集的方式有哪些?6. 用完Connection后必须关闭吗?7. ResultSet和Statement能否不关闭?8. 如何加载数据库驱动?9. 连接Oracle数据库的URL是什么?10. 连接Mysql数据库的URL是什么?实训: 实现一个通用的Mysql数据库访问组件类的开发,提供新增、修改、删除、查询功能。其他模块在涉及到数据库操作部分,可以通过这个类来完成。要求:1、数据库的连接参数要和代码分离,可以用配置文件;2、返回的结果集要用缓冲技术;