JDBC的典型用法:
DriverManager
用于管理JDBC驱动的服务类。程序中使用该类主要功能是获取Connection对象,该类包含如下方法:
Public static synchronized connection getConnection(String url,String user,String pass)throws SQLException:该方法获得url对应数据库的连接。
Connection
代表数据库连接对象,每个Connection代表一个物理连接会话,想要访问数据库,必须先获得数据库连接,该接口常用的方法:
-
Statement createStatement()throws SQLException:该方法返回一个Statement对象
-
PreparedStatement prepareStatement(String sql)throws SQLException:该方法返回预编译的Statement对象,即将SQL语句提交到数据库中进行预编译。
-
CallableStatement prepareCall(String sql)throws SQLException:该方法返回CallableStatement对象,该对象用于调用存储过程。
Collection还有几个用于控制事务的方法: -
savepoint setSavepoint():创建一个保存点
-
Savepoint setSavepoint(String name):指定名字来创建一个保存点
-
Void setTransactionIsolation(int level):设置事务的隔离级别
-
Void rollback():回滚事务
-
Void rollback(Savepoint savepoint):将事务回滚到指定的保存点
-
Void setAutoCommit(Boolean autoCommit):关闭自动提交,打开事务
-
Void commit():提交事务
Statement
用于执行SQL语句的工具接口。该对象既可以用于执行DDL,DCL语句,也可用于执行DML语句,还可用于执行SQL查询,当执行SQL查询时,返回查询到结果集。常用方法:
1.ResultSet executeQuery(String sql)throws SQLException:该方法用于执行查询语句,并返回查询结果对应的ResultSet对象,该方法只能用于执行查询语句。
2.int executeUpdate(String sql)throws SQLException:该方法用于执行DML语句,并返回受影响的行数;该方法也可用于执行DDL,执行DDL将返回0.
3.Boolean execute(String sql)throws SQLException :该方法可用于执行任何SQL语句。如果执行后第一个结果为ResultSet对象,则返回true;如果执行后第一个结果为受影响的行数或没有任何结果,则返回false。
PreparedStatement
预编译的Statement对象,preparedStatement是Statement的子接口,他允许数据库预编译SQL(这些SQL语句通常带有参数)语句,以后每次只改变SQL命令的参数,避免数据库每次都需要编译SQL语句,因此性能好。方法:
Void setXXX(int para,emterIndex,XXX value):该方法根据传入参数值的类型不同,需要使用不同的方法。传入的值根据索引传给SQL语句中指定位置的参数。
PreparedStatenment同样有executeUpdate,executeQuery和execute三个方法,只是这三个方法无需接收SQL字符串,因为preparedStatement对象已经预编译了SQL命令
ReaultSet:结果集对象,该对象包含访问查询结果的方法,ResultSet可以通过列索引或列名获得列数据,它的常用方法如下: -
void close()throws SQLException:释放ResultSet对象
-
Boolean absolute(int row):将结果集的记录指针移到row行,如果row是负数,则移动到倒数第几行,如果移动后的记录指针指向一条有效记录,则该方法返回true
-
Void beforeFirst():将resultSet的记录指针定位到首行之前,这是resultSet结果集记录指针的初始状态:记录指针的起始位置位于第一行之前。
-
Boolean first():将ResultSet的记录指针定位到首行。如果移动后的记录指针指向一条有效记录,则方法返回true
-
Boolean previous():将resultSet的记录指针定位到上一行,如果移动后指向一条有效记录,则方法返回true
-
Boolean next():将resultSet的记录指针定位到下一行,如果移动后指向一条有效记录,则方法返回true
-
Boolean last():将ResultSet的记录指针定位到最后一行。如果移动后的记录指针指向一条有效记录,则方法返回true
-
Void afterLast():将ResultSet的记录指针定位到最后一行之后。
JDBC编程步骤: -
加载数据库驱动。通常我们使用Class类的forName静态方法来加载驱动,例如:
//加载驱动
Class.forName(driverClass)//driverClass就是数据库驱动类所对应的字符串
例如加载MySQL的驱动采用如下代码:
//加载MySQL驱动
Class.forName(“com.mysql.jdbc.Driver”);
加载Oracle驱动
Class.forName(“oracle.jdbc,driver.OracleDriver”);
加载驱动时并不是真正使用数据库的驱动类,只是使用数据库驱动类名。
2.通过DriverManager获取数据库连接,DriverManager提供了如下方法:
//获取数据库连接
DriverManager.getConnection(String url,String user,String pass)
通常需要传入三个参数:数据库URL,登录数据库的用户名和密码。这三个参数中用户名和密码
通常由DBA(数据库管理员)分配,而且该用户还应该具有相应的权限,才可执行相应的SQL语句
数据库URL通常遵守如下写法:
Jdbc:subprotocol:other stuff
UML 中jdbc是固定的,而subprotocol指定连接到特定数据库的驱动程序,而后面的other和stuff也是不固定的如MySQL数据库:
jdbc:mysql://hostname:port/databasename
Oracle数据库的URL写法如下:
jdbc:oracle:thin:@hostname:port:databasename
3. 通过Connection对象创建Statement对象,Connection创建statement的方法有如下三个:
CreateStatement():创建基本的Statement对象
PrepareStatement(String sql):根据传入的SQL语句创建预编译的Statement对象
PrepareCall(String sql):根据传入的SQL语句创建CallableStatement对象。
使用statement执行SQL语句,所有的statement都有如下三个方法来执行sql语句:
Execute:可以执行任何SQL语句。
ExecuteUpdate:主要用于执行DML和DDL语句。执行DML返回受SQL语句的影响的行数,执行DDL返回0
ExecuteQuery:只能执行查询语句,执行后返回代表查询结果的ResultSet对象
4.操作结果集。如果执行的SQl语句是查询语句,执行结果将返回一个ResultSet对象,该对象里保存了SQL语句查询的结果。程序可以通过操作该ResultSet对象来获取查询结果,ResultSet对象主要提供两类方法:
Next,previous,first,last,beforeFirst,afterLast,absolute等移动记录指针的方法
GetXXX获取记录指针指向行,特定列的值。该方法既可以使用列索引作为参数,也可以使用列名作为参数,使用列索引作为参数性能更好,使用列名作为参数可读性好
ResultSet的实质是一个查询结果集,在逻辑结构上非常类似于一个表。
6.回收数据库资源,包括关闭Result,Statement,和connection等资源
执行SQL语句的方式
使用executeUpdate执行DDL和DML语句:
public class Mysql {
private String driver;
private String url;
private String user;
private String pass;
Connection conn;
Statement stmt;
public void initParam(String paramFile) throws Exception, IOException
{
//使用properties类来加载文件
Properties props=new Properties();
props.load(new FileInputStream(paramFile));
driver=props.getProperty(“driver”);
url=props.getProperty(“url”);
user=props.getProperty(“user”);
pass=props.getProperty(“pass”);
}
public void createTable(String sql) throws Exception {try { //加载驱动 Class.forName(driver); //获取数据库连接 conn=DriverManager.getConnection(url, user, pass); //使用Connection来创建一个Statement对象 stmt=conn.createStatement(); //执行DDL,创建数据表 stmt.executeUpdate(sql); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if(stmt!=null) { stmt.close(); } if(conn!=null) { conn.close(); } }
}
public static void main(String[] args) throws Exception, Exception {
Mysql ed=new Mysql();
ed.initParam(“mysql.ini”);
ed.createTable(“create table jdbc_test”+"(jdbc_id int auto_increment primary key,"+“jdbc_name varchar(255),”+“jdbc_desc text);”);
System.out.println(“创建成功”);
}
使用execute方法执行SQL语句:
Statement的execute方法几乎可以执行任何SQL语句,但执行SQL语句时比较麻烦。
当使用Statement的execute执行了SQL语句后的返回值只是Boolean值。
如下两个方法:
getResultSet():获取该Statement执行查询语句返回的ResultSet对象
GetUpdateCount():获取该Statement执行了DML语句所影响的行记录
使用PreparedStatement执行SQL语句:
JDBC提供了PreparedStatement接口,它是Statement接口的子接口,他可以预编译SQL语句,预编译后的SQL语句被存储在PreparedStatement对象中(可以使用占位符)
创建preparedStatement对象使用Connection的PrepareStatement方法,该方法需要传入一个SQL字符串,该SQL字符串可以包含占位符。如:
创建一个对象
Pstmt=conn.preparedStatement(“insert into student_table values(null,?,1)”);
也有execute,executeUpdate,executeQuery三个方法,但这三个方法无需参数,因为PreparedStatement存储了预编译的SQL语句
使用preparedStatement预编译SQL语句时,该SQL语句可以带占位符参数,因此在执行SQL语句之前必须为这些参数传入参数值,preparedStatement提供了系列的setXXX(int index,XXX value)方法来传入参数值。
使用preparedStatement有一个很大的作用:用于防止SQL的注入
SQL注入是一个较常见的Cracker入侵方式,他利用SQL语句的漏洞来入侵
使用preparedStatement比用Statement的好处: -
PreparedStatement预编译SQL语句,性能更好
-
PreparedStatement无须“拼接”SQL语句,编程更简单
-
PreparedStatement可以防止SQL注入,安全性好
使用PreparedStatement执行带占位符的SQL语句时,SQL语句中的占位符参数只能代替普通值,不要使用占位符代替表名,列名等数据库对象。
使用CallableStatement调用存储过程:
调用存储过程使用CallableStatement,可以通过Connection的prepareCall方法来创建CallableStatement对象,创建该对象时需要传入调用存储过程的SQL语句,格式:{call 过程名(?,?,?…)},其中问号作为存储过程参数的占位符。如:
//使用Connection来创建一个对象
Cstmt=conn.prepareCall(“{call add_pro(?,?,?)}”);
可以通过CakkableStatement的setXXX方法为传入参数设置值,CallableStatement需要用registerOutParameter方法来注册该参数。如:
//注册CallableStatement的第三参数是int类型
Cstmt.registerOutParameter(3,Types.INTEGER);
管理结果集:
JDBC还允许通过ResultSet来更新记录,并提供了ResultSetMetaData来获得ResultSet对象的相关信息
可滚动,可更新的结果集
以默认方式打开的ResultSet是不可更新的,如果希望创建可更新的ResultSet,必须在创建Statement或PreparedStatement时传入额外的参数。Connection在创建Statement或preparedStatement时还可以额外的传入两个参数: -
resultSetType:控制ResultSet的类型,该参数可以是如下三个值:
ResultSet.TYPE_FORWARD_ONLY:该常量控制记录指针只能向前移动
ResultSet.TYPE_SCROLL_INSENSITIVE:该常量控制记录指针可以自由移动(可滚动结果集)
但底层数据的改变不会影响ResultSet的内容
ResultSet.TYPE_SCROLL_SENSITIVE:该常量控制记录指针可以自由移动(可滚动结果集)
但底层数据的改变会影响ResultSet的内容 -
resultSetConcurrency:控制ResultSet的并发类型,该参数可以接受如下两个值:
ResultSet.CONCUR_READ_ONLY:该常量指示ResultSet是只读的并发式模式(默认)
ResultSet.CONCUR_UPDATABLE:该常量指示resultSet是可更新的并发模式
如:
//创建
Pstmt=conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE)
程序可以调用ResultSet的updateXXX(int columnIndex,XXX value)来修改记录指针所指记录的,特定列的值,最后调用ResultSet的updateRow来提交修改。
如果要创建可更新的结果集,查询语句查询的数据通常只能来自一个数据表,而且查询结果集中的数据列必须包含主键列,否则会引起更新失败
处理Blob类型数据:
Blob(Binary Long Object):是二进制长对象的意思,Blob列通常用于存储大文件,典型Blob内容是一张图片或一个声音文件,由于他们的特殊性,必须使用特殊的方式来存储。使用Blob列看可以把图片,声音等文件的二进制数据保存在数据库,并可以从数据库里恢复指定文件。
如果需要将图片插入数据库,显然不能直接通过普通SQL语句来完成,因为有一个关键的问题:Blob常量无法表示。所以将Blob数据插入数据库需要使用PreparedStatement,该对象有一个方法:setBinaryStream(int parameterIndex,InputStream x),该方法可以为指定参数传入二进制输入流,从而可以实现将Blob数据保存到数据库的功能。
可以调用ResultSet的getBlob(int columnIndex)方法,该方法将返回一个Blob对象,
Blob对象提供了getbinaryStream方法来获取该Blob数据的输入流,也可使用Blob对象提供的getBytes方法直接取出该Blob对象封装的二进制数据。
使用ResultSetMetaDate分析结果集:
可以通过ResultSetMetaData来获取关于ResultSet的描述信息。
ResultSet里包含了一个getMetaDate()方法 ,该方法返回该ResultSet对应的ResultSetMetaData对象。一旦获得了ResultSet<etaData对象,就可以通过ResultSetMetaDate提供的方法返回ResultSet的描述信息,常用的方法有如下三个:
1:int getColumnCount():返回该ResultSet的列数量
2:String getColumnName(int column):返回指定索引的列名。
3:int getColumnType(int column):返回指定索引的列类型
事务处理:
事务概念和MySQL事务支持:
程序和事务是两个不同的概念,一般而言:一段程序中可能包含多个事务。
事务具备四个特性:原子性,一致性,隔离性和持续性。这四个特性也称为ACID性。
- 原子性:事务是应用中最小执行单位,具有不可再分割特性。事务时应用不可再分的最小逻辑执行体。
- 一致性:事务执行的结果,必须使数据库从一个一致状态,变到另一个一致性状态,当数据库只包含事务成功提交的结果时,数据库处于一致性状态,如果系统运行发生中断,而该未完成的事务对数据库所做的修改已经被写入数据库,此时,数据库处于一种不确定的状态,一致性是通过原子性来控制的。
- 隔离性:各个事物的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的,也就是说:并发执行的事务之间不受影响
- 持续性:持续性也称为持久性,指事务一旦提交,对数据所做的任何改变,都要记录到永久的存储器中,通常就是保存进物理数据库。
数据库的事务由下列语句组成: - 一组DML语句,经过这组DML修改后数据将保持较好的一致性。
- 一个DDL语句
- 一个DCL语句
DDL和DCL语句最多只能有一个,因为DDL和DCL语句都会导致事务立即提交
事务提交有两种方式:显式提交和自动提交 - 显式提交:使用Commit
- 自动提交:执行DDL或DCL,或者程序正常退出
当事务所包含的任意一个数据库操作执行失败后,应该回滚事务,使该事务中所做的修改全部失效。事务回滚有两种方式:显式回滚和自动回滚 - 显式回滚:使用rollback
- 系统错误或者强行退出
为了开启MySQL的事务支持,可以显式的调用如下命令:
SET AUTOCOMMIT={0|1}:0为关闭自动提交,即开启事务
自动提交和开启事务恰好相反,如果开启自动提交就是关闭事务;关闭自动提交就是开启事务
一旦在MySQL里开启了事务,该命令行里的所有DML语句都不会立即生效,上一个事务结束后第一条DML语句将开始一个新的事务,而后续执行的所有SQL语句都处于该事务中,除非显式使用Commit来提交事务,或者正常退出,或者运行了DDL,DCL导致事务隐式提交。使用rollback回滚来结束事务,使用rollback结束事务将导致本次事务中DML语句中所做的修改全部消失
一个MySQL命令行窗口代表一次连接Session,在窗口里设置Set autocommit=0
相当于关闭了该连接Session的自动提交,对其他连接不会有影响
除此之外,如果不想关闭整个命令行窗口的自动提交,只是想在暂时性的开始事务,则可以使用MySQL提供的start transation或begin两个命令,他们都表示临时性的开始一次事务,处于start transaction或begin后DML语句不会立即生效,除非使用commit显式提交任务,或执行DDL,DCL来隐式提交事务。
如:
–临时开始事务
begin;
–向student_table表中插入3条数据
insert into student_table
values(null,‘x’,1)
insert into student_table
values(null,‘y’,1)
insert into student_table
values(null,‘z’,1)
–查询student_table表的查询
select * from student_table;
–回滚事务
rollback;
–再次查询
select * from student_table;
MySQL还提供了savepoint来设置事务的中间点,通过使用savepoint设置事务中间点可以让事务回滚到指定中间点,而不是回滚全部事务。如:
Savepoint a;
一旦设置了中间点后,就可以使用rollback回滚到指定中间点,回滚到指定中间点的代码如下:
rollback to a;
回滚到指定中间点因为依旧处于事务之中;所以不会结束当前事务。
JDBC事务支持:
JDBC连接的事务支持由Connection提供支持,Connection默认打开自动提交,即关闭事务,在这种情况下,每条SQL语句一旦执行,便会立即提交到数据库,永久生效,无法对其回滚操作
可以调用Connection的setAutoCommit方法来关闭自动提交,开启事务
//关闭自动提交模式
Conn.setAutoCommit(false);
程序可以调用Connection提供的getAutoCommit方法来获得该连接的自动提交模式。
创建了Statement对象之后,可以执行任意多条DML语句,如:
Stmt.executeUpdate(…);
//提交事务
Conn.commit();
//如果任意一条SQL语句执行失败,我们应该用Connection的rollback来回滚事务,如:
//回滚事务
Conn.rollback();
实际上当Connection遇到一个未处理的SQLException异常时,系统将会非正常退出,系统会自动回滚该事务,但如果程序显式捕捉了该异常,则需要在异常处理块中显式回滚事务。
Connection也提供了设置中间点的方法,setSavepoint,Connection提供了两个方法来设置中间点: - Savepoint setSavepoint():在当前事务中创建一个未命名的中间点,并返回代表该中间点的Savepoint对象。
- Savepoint setSavepoint(String name):在当前事务中创建一个具有指定名称的中间点,并返回代表该中间点的Savepoint对象。
Connection提供了rollback(Savepoint savepoint)方法来回到指定中间点。
批量更新:
批量更新必须得到底层数据库的支持,可以通过调用DatabaseMetaData的supportsBatchUpdates方法来查看底层数据库是否支持批量更新。
如:
Statement stmt=conn.createStatement();
//使用Statement同时收集多条SQL语句
Stmt.addBatch(sql1);
Stmt.addBatch(sql2);
Stmt.addBatch(sql3);
…
//同时执行所有的SQL语句
Stmt.executeBatch();
执行executeBatch将返回一个int[]数组,因为使用Statement执行DDL,DML都将返回一个int值,而执行多条DDL,DML将会返回多个int的值,多个int值就组成了这个int[]数组,如果在批量更新的addBatch方法中添加了select查询语句,程序将直接出现错误。
为了让批量操作可以正确的处理错误,必须把批量执行的操作视为单个事务,如果批量更新在行过程失败,则让事务回滚到批量操作开始之前,程序应该在开始批量操作之前先关闭自动提交,然后开始收集更新语句,当批量操作执行结束后提交事务,并恢复之前的自动提交模式。如://保存当前自动模式
Boolean autoCommit=conn.getAutoCommit();
//关闭自动提交
Conn.setAutoCommit(false);
Statement stmt=conn.createStatement();
//使用Statement同时收集多条SQL语句
Stmt.addBatch(sql1);
Stmt.addBatch(sql2);
Stmt.addBatch(sql3);
…
//同时提交说有SQL语句
Stmt.executeBatch();
//提交修改
Conn.commit();
//恢复原有的自动提交模式
Conn.setAutoCommit(autoCommit)
分析数据库信息:
使用DatabaseMetaData分析数据库信息:
JDBC提供了DatabaseMetaData来封装数据库连接对应数据哭的信息,通过Connection提供的getMetaData()方法就可以获得到数据库对象的DatabaseMetaData对象。
DatabaseMetaData接口通常由驱动程序供应商提供实现,其目的是让用户了解底层数据库的相关信息。使用该接口的目的是发现如何处理底层数据库,尤其是对于试图与多个数据库一起使用的应用程序:因为应用程序需要在多个数据库之间进行切换,所以必须利用该接口来找出底层数据库的功能。
很多DatabaseMetaData方法都需要传入一个xxxPattern的模式字符串,这里的xxxpattern不是正则表达式,而是SQL里的模式字符串,即用%代表任意多个字符,使用下户线(_)代表一个字符。通常情况下,如果把该模式字符串参数值设为null,即表名该参数不作为过滤条件。
使用系统表分析数据库信息:
系统表又称为数据字典,数据字典表的数据通常由数据库系统负责维护,用户只能查询数据库字典,而不能修改数据字典内容。
MySQL和SQL Server数据库,他们提供一个系统表数据库来存储这些系统表。
MySQL的数据库系统表使用information_schema数据库来保存,在该数据库里包含了大量系统表;常用的系统表的简单介绍如下: - table:存放数据库里所有数据表信息。
- schemata:存放数据库里所有数据库(与MySQL的Schema对应)的所有信息。
- views:存放数据库里所有的视图的信息。
- columns:存放数据库里所有列的信息
- triggers:存放数据库里所有触发器的信息
- routines:存放数据库里所有存储过程和函数信息。
- key_column_usage:存放数据库里所有具备约束的键信息
- table_constraints:存放数据库全部约束的表信息
- statistics:存放数据库里全部索引的信息
选择合适的分析方式:
如果使用DatabaseMetaData来分析数据库信息,则具有更好的跨数据库特性,应用程序可以左到数据库无关,但使用DatabaseMetaData来分析数据库可能无法准确获得数据库更多细节。
使用数据库系统表来分析数据库信息会更加准确,但是使用系统表也有坏处:这种方法与底层数据库耦合太严重,采用这种方式将会导致程序只能运行特定的数据库之上
使用连接池管理连接:
一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完后立即关闭连接,频繁打开,关闭连接将造成系统性能下降。
数据库连接池的解决方案是:当应用程序启动时,系统主动建立足够的数据库连接,并将这些连接组成一个连接池,每次应用程序请求连接数据库时,无需重新打开连接,而是从池中取出已有的连接使用,使用完成后,不再关闭数据库连接,而是直接将连接归还给连接池,通过连接池将大大提高程序的运行效率。
对于共享资源的情况,有一个通用的设计模式:资源池,用于解决资源的频繁请求,释放所造成的性能下降。为了解决数据库连接的频繁请求,释放所造成的性能下降。为了解决数据库连接的频繁请求,释放。数据库连接池的常用参数: - 数据库初始连接数
- 连接池最大连接数
- 连接池最小连接数
- 连接池的每次增加的容量
JDBC的数据库连接池使用javax.sql.DataSourse来表示,DataSource只是一个接口,该接口通常
由商用服务器(如:webLogic,WebSphere)等提供实现,也有一些开源组织的提供实现(如:DBCP)
DataSource通常被称为数据源,他包含连接池和连接池管理两个部分。
主要介绍两种来源的数据源实现;
DBCP数据源
DBCP是Apache软件基金组织下的来源连接池实现,该连接池依赖该组织下的另一个来源系统:common-pool。如果需要使用该连接池的实现,应在系统中增加如下两个jar文件: - commons-bdcp.jar:连接池的实现
- commons-pool.jar:连接池实现的依赖库
Tomcat的连接池正是采用该连接池来实现的,数据库连接池既可以与应用服务器整合使用,也可由应用程序单独使用,如:
//创建数据源对象
BasicDataSource ds=new BasicDataSource();
//设置连接池所需要的驱动
ds.serDriverClassName(“com.mysql.jdbc.Driver”);
//设置连接数据库的url
ds.setUrl(“jdbc:mysql://localhost:3306/javaee”);.
//设置数据库的用户名
ds.setUsername(“用户名”);
//设置数据库的密码
ds.setPassword(“密码”);
//设置连接池的初始连接数
ds.setInitialSize(5);
//设置连接池最多可有多少个活动连接数
ds.setMaxActive(20)
//设置连接池中最少有两个空闲的连接
ds.setMinIdle(2);
//通过数据源获取连接
Connection conn=ds.getConnection();
数据源和数据库连接不同,数据源可以创建无数多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。建议把上面的ds设置成Static成员变量,并且在应用开始时立即初始化数据源对象,程序中所有需要获取数据库连接的地方直接访问该ds对象,并获取数据库连接即可,当数据访问结束后,程序还是关闭数据库连接。
//释放数据库连接
Conn.close();
并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给连接池,让其他客户端可以使用该连接。
C3P0数据源
Hibernate就使用的该连接池。C3P0连接池不仅可以自动清理不再使用的Connection,还可以自动清理Statement和ResultSet。
C3p0-0.9.1.2.jar:C3P0连接池的实现
代码通过C3p0连接池获得数据库的连接: