一、是为何物
1、概念
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。简言之,JDBC就是Java用于执行SQL语句实现数据库操作的API。
如果不好理解我们可以先看一下驱动程序的概念:
驱动程序:指的是设备驱动程序(Device Driver),是一种可以使计算机和设备通信的特殊程序。相当于硬件的接口,操作系统只有通过这个接口,才能控制硬件设备的工作,假如某设备的驱动程序未能正确安装,便不能正常工作。简言之就是操作系统通过一段代码来操作设备,而这段代码就称为该设备的驱动程序,是操作系统与硬件之间的桥梁。
同理,数据库驱动程序就是提供用于操作数据库的一段代码。我们现在想要使用纯Java来操作数据库,肯定不能直接调用这段代码,为了更便捷和灵活地使用高级语言直接对数据库进行管理,JDBC因此而产生,JDBC是一套协议,是JAVA开发人员和数据库厂商达成的协议,也就是由Sun定义一组接口,由数据库厂商来实现,并规定了JAVA开发人员访问数据库所使用的方法的调用规范。
2、分类
据访问数据库的技术不同, JDBC 驱动程序相应地分为四种类型。不同类型的驱动程序有着不一样的特性和使用方法。
JDBC-ODBC桥驱动程序(JDBC-ODBC Bridge Driver)
此类驱动程序由JDBC-ODBC桥和一个ODBC驱动程序组成。其工作原理是,通过一段本地C代码将JDBC调用转化成ODBC调用。这一类型必须在本地计算机上先安装好ODBC驱动程序,然后通过JDBC-ODBCBridge的转换,将Java程序中使用的JDBCAPI访问指令转化成ODBCAPI指令,进而通过ODBC驱动程序调用本地数据库驱动代码完成对数据库的访问。
部分Java的本地JDBCAPI驱动程序
JDBC API驱动程序(Anative API partly Java technology-enabled Driver)此类驱动程序也必须在本地计算机上先安装好特定的驱动程序(类似ODBC),然后通过PartialJavaJDBCDriver的转换,把Java程序中使用的JDBC API转换成NativeAPI,进而存取数据库。
纯Java的数据库中间件驱动程序(目前主流驱动)
纯Java的数据库中间件驱动程序(Pure Java Driver for Database Middleware)使用这类驱动程序时,不需要在本地计算机上安装任何附加软件,但是必须在安装数据库管理系统的服务器端加装中间件(Middleware),这个中间件负责所有存取数据库时必要的转换。其工作原理是:驱动程序将JDBC访问转换成与数据库无关的标准网络协议(通常是HTTP或HTTPS)送出,然后再由中间件服务器将其转换成数据库专用的访问指令,完成对数据库的操作。中间件服务器能支持对多种数据库的访问。
纯Java的JDBC驱动程序
纯Java的JDBC驱动程序(Direct-to-DatabasePureJavaDriver)这类驱动程序是直接面向数据库的纯Java驱动程序,即所谓的"瘦"驱动程序。使用这类驱动程序时无需安装任何附加的软件(无论是本地计算机或是数据库服务器端),所有存取数据库的操作都直接由JDBC驱动程序来完成,此类驱动程序能将JDBC调用转换成DBMS专用的网络协议,能够自动识别网络协议下的特殊数据库并能直接创建数据连接。
第一类最节省投资。但执行效率比较低,不适合对大数据量存取的应用;第二种方式具有开放性,但不够兼容使用较麻烦;
第三类驱动程序是由纯Java语言开发而成的,并且中间件也仅需要在服务器上安装,不再需要客户端的本机代码,这类驱动程序的体积最小,效率较高,具有最大的灵活性。而且,此类驱动采用标准的网络协议,可以被防火墙支持,是开发Applet程序理想的选择(其实这些驱动是为Applet特别编写的),是Internet应用理想的解决方案。另外,开发者还可以利用单一的驱动程序连接到多种数据库。由于此种JDBC驱动程序提供了许多企业级的特征,因而非常适合用户的特殊用途,如:SSL安全、分布式事务处理等。如果用户未来扩张应用需要多个数据库,则选择这种方式是最理想的。由于它具有较好的性能和安全性,广泛应用于基于Web的应用系统的开发。其不足之处是:需要在服务器端安装中间件,这适当影响了驱动程序的效率。
第四类驱动程序可能是最佳的JDBC驱动程序类型。这是一类新型的驱动程序,它由数据库厂商提供,能够实现对于本公司数据库系统的最优化访问。这种驱动程序的效率最高,拥有最佳的兼容性。然而,这种驱动由于使用DBMS专用的网络协议,可能不被防火墙支持,在Internet中会存在潜在安全隐患,成为这类驱动最大的缺陷。其次,不同DBMS的驱动程序不同,在一个异构计算环境中,驱动程序的数量可能会比较多。
由于我们主流选择的JDBC驱动为第三类,下面给出第三类的JDBC驱动程序结构图:
从图可见,JDBC驱动程序为两层结构,它由驱动程序客户端程序和驱动程序服务器端程序组成。客户端直接与用户交互,它为用户提供符合JDBC规范的数据库统一编程接口,客户端将数据库请求通过特定的网络协议传送给服务器。服务器充当中间件的角色,它负责接收和处理用户的请求以及支持对多种数据库的操作。JDBC驱动程序自身并不直接与数据库交互,而是借助于其他已实现的数据库驱动程序,成为“雇主”。可以想象,它“雇佣”的数据库驱动程序越多,则可支持的数据库就越多,“雇佣”的数据库驱动程序越强大,则类型3JDBC驱动程序的功能也越强大。除此之外,“雇佣”的数据库驱动程序的数量和成员可以动态改变,以满足系统扩展的需求。第三类 JDBC驱动程序的这一特性正是它强大的根源所在。
二、动手测一测(以MySQL为例)
了解了JDBC的大致原理,我们学会使用才是关键。
1、环境
既然要使用API,我们就需要导入JDBC运行环境所需要的jar包,而不同的数据库jar包也是不同的,下面以MySQL数据库为例,导入下列jar包(百度一堆或者去官网下载):
2、一般步骤
注册驱动
获取连接对象(url、username、password)
编写SQL语句
获取语句执行对象
(给占位符设置参数)
执行SQL语句
获得执行后的结果集
处理结果集
3、Java代码
下面看具体实现代码:
public void JDBCDemo(String pname,int price) throws ClassNotFoundException, Exception{ //1、注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2、获取连接对象 Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/web08?useUnicode=true&characterEncoding=utf-8&useSSL=false", "root", "password"); //sql语句 String sql = "select * from product where pname=? and price=?"; //3、获得sql语句执行对象 PreparedStatement ps = conn.prepareStatement(sql); //4、设置参数给占位符 ps.setString(1, pname); ps.setInt(2, price); //5、执行并保存结果集 ResultSet rs = ps.executeQuery(); //6、处理结果集 if(rs.next()){ System.out.println(rs.getString(2)+":"+rs.getInt(3)); }else System.out.println("查询失败"); if(rs!=null){ rs.close(); } if(ps!=null){ ps.close(); } if(conn!=null){ conn.close(); } } }
程序员在使用Java操作数据库时只需要按照给定的规则进行编程即可操作数据库。下面是常用api的详细讲解。
三、API详解
1、注册驱动程序
Class.forName(),是最常见的注册JDBC驱动程序的方法,注册某数据库就将该数据库驱动的名称以字符串的形式作为参数:
例如我们需要注册MySQL的数据库驱动,则使用代码:
1 Class.froName("com.mysql.jdbc.Driver");
另外还可以使用DriverManage的静态方法:DriverManager.registerDriver()
来注册驱动,参数为对应的数据库驱动对象。
2、获得连接对象Connection
可以使用 DriverManager.getConnection()
方法建立连接。根据传入参数的不同,有三种重载的DriverManager.getConnection()
方法:
getConnection(String url)
getConnection(String url, Properties prop)
getConnection(String url, String user, String password)
这里每个格式都需要一个数据库URL。 数据库URL是指向数据库的地址。制定数据库URL是建立连接相关联的大多数错误问题发生的地方。各数据库对应的URL如下所示:
假设我们现在需要连接MySQL数据库,格式为:jdbc:mysql://hostname:port/datebaseName。我们需要的信息是hostname主机名和端口号,一般默认为localHost:3306;还需要datebaseName数据库名,假设为freeDB;当然还有URL格式未包含的也是必须的信息:连接数据库的用户名和密码,假设为Leslie和030401。那么就有URL:
1 String url = "jdbc:mysql//localhost:3306/freeDB";
下面分别使用三种方法来实现:
使用一个URL作为参数的方式:需要将username+password以参数的形式放到URL中,但是每种数据库的放置都不太相同
//连接mysql的纯URLString url = "jdbc:mysql//localhost:3306/freeDB?username=leslie&password=030401"; Connection conn = DriverManager.getConnection(url,p); //连接Oracle的纯URL String url = "jdbc:oracle:thin:leslie/030401@192.0.0.10:1521:freeDB"; Connection conn = DriverManager.getConnection(url);
使用URL、properties作为参数的方式:即需要将username和password以键值对形式存放在properties对象中作为参数
//MySql String url = "jdbc:mysql//localhost:3306/freeDB"; Properties p = new Properties(); p.put("username","leslie"); p.put("password","030401"); Connection conn = DriverManager.getConnection(url,p);
使用URL、username、password三个参数分开的方式(推荐)
1 String url = "jdbc:mysql//localhost:3306/freeDB"; 2 String username = "leslie"; 3 String password = "030401"; 4 Connection conn = DriverManager.getConnection(url,username,password);
3、获得SQL语句执行对象
SQL语句的执行对象按理说有Statement和PreparedStatement两个,但我们一般都不会去使用Statement,先看下两者的基本描述:
Statement 是 Java 执行数据库操作的一个重要接口,用于在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句。Statement对象,用于执行不带参数的简单SQL语句,即静态SQL语句。
PreparedStatement 继承于Statement。实例包含已编译的 SQL 语句,这就是使语句“准备好”。包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN参数的值在 SQL 语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX() 方法来提供。
简言之,Statement执行静态SQL语句,而它的子类PreparedStatement执行预编译SQL,即可传入参数。两者相比之下,PreparedStatement有以下优势:
预编译处理,可动态执行SQL语句。很明显,SQL语句的预编译,使用占位符?去代替未知数据,因而一个句子可以执行多种不同的SQL,而Statement需要重新书写SQL语句,笨重。
速度快,执行效率高。SQL语句会预编译在数据库系统中。执行计划同样会被缓存起来,它允许数据库做参数化查询。使用预处理语句比普通的查询更快,因为它做的工作更少(数据库对SQL语句的分析,编译,优化已经在第一次查询前完成了)。我们要利用预编译的特性,比如第一种查询和第二种查询,第二种才是预编译形式,而第一种其实是恢复了父类Statement的做法:
//第一种方式,追加字符串:没有进行预编译,所以效率低 String loanType = getLoanType(); PreparedStatement prestmt = conn.prepareStatement("select banks from loan where loan_type=" + loanType);//第二种方式,使用占位符,进行预编译,效率高(推荐)PreparedStatement prestmt = conn.prepareStatement("select banks from loan where loan_type=?");prestmt.setString(1,loanType);
可防止SQL注入式攻击。静态SQL语句说到底还是字符串,所以存在拼字符串的而带来的注入式SQL攻击风险。比如某个网站的登录验证SQL查询代码为:
1 String sql = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"
验证需要用户输入用户名和密码,正确则执行查询语句(登录),但如果这样输入:
userName = "1' OR '1'='1"; passWord = "1' OR '1'='1";
那么执行语句就变成了:
1 String sql = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"
这样,where语句恒为真,就能实现无账号登录。此外便可能被恶意修改甚至删除数据表。然而使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。在使用参数化查询的情况下,数据库系统(eg:MySQL)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,且占位符?不允许多值,只能填入一个值,因此就算参数中含有破坏性的指令,也不会被数据库所运行。
说了那么多,PreparedStatement如何获取呢?PreparedStatement是和连接对象Connection关联的,所以我们需要使用Connection对象的PreparedStatement(sql)方法进行获取:
1 while(rs.next()){ 2 system.out.println(rs.getString(1)); 3 system.out.println(rs.getInt(2)); 4 }
4、给占位符设置参数
根据需要的参数类型,使用setXXX()的方法即可。(注:从第一个问好起是从1开始编号)
1 ps.setString(1,"tom");2 ps.setInt(2,18);
5、执行SQL语句
sql语句有增删查改等几种类型,所以执行方法有以下三种:
execute():执行SQL语句,可以是任何种类的 SQL 语句。返回值是boolean类型。
executeQuery()
:至少SQL语句查询,查询结果返回为ResultSet
对象。executeUpdate()
:执行更新语句。该语句必须是一个 SQL 数据操作语言(Data Manipulation Language,DML)语句,比如INSERT
、UPDATE
或DELETE
语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。返回值是int。
例如本例中的语句是查询语句,所以执行代码为:
1 ResultSet rs = ps.executeQuery();
6、处理结果或结果集
如果返回值是boolean或者int很好处理,但如果是查询结果集ResultSet对象,一般使用while循环来处理:
ResultSet
对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next()
方法将光标移动到下一行;因为该方法在 ResultSet
对象没有下一行时返回 false
,所以可以在 while
循环中使用它来迭代结果集。另外,可以使用ResultSet对象的getXXX(int columnIndex)获得游标所在行指定列的值。原理如下图所示:
所以,本例的结果集处理如下:
1 while(rs.next()){ 2 system.out.println(rs.getString(1)); 3 system.out.println(rs.getInt(2)); 4 }
7、关闭连接
在JDBC程序结束之后,显式地需要关闭与数据库的所有连接以结束每个数据库会话。 但是,如果在编写程序中忘记了关闭也没有关系,Java的垃圾收集器将在清除过时的对象时也会关闭这些连接。
依靠垃圾收集,特别是数据库编程,是一个非常差的编程实践。所以应该要使用与连接对象关联的close()
方法关闭连接。要确保连接已关闭,可以将关闭连接的代码中编写在“finally
”块中。 一个finally
块总是会被执行,不管是否发生异常。
conn.close();
四、工具类JDBCUtils的使用
在使用JDBC连接数据库的过程中,我们发现大多数步骤都是一样的,而我们平常使用数据库的频率如果很高所以我们需要对JDBC的通用代码进行提炼,提高代码复用率,提炼出来的工具类我们一般称为JDBCUtils,工具类中包含了我们常用的很多方法,比如连接数据库和断开连接就是常用的方法,我们只要掌握了JDBC原理,就可以自己设计满足需求工具类或参考以下工具类(后面我们会说到DBUtils工具类,这是Apache组织提供的JDBC工具类,比较全面,基本能够满足我们的需求,本文最后一节会进行详解):
package cn.jdbc;import java.io.IOException;import java.io.InputStream;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Properties;/** * @author Fzz * @date 2017年11月12日 * @Description TODO:提供获取连接和释放资源的方法 */public class jdbcUtil{ /* * 获取Properties有两种方法: * 一是使用io流获取文件内容, * 二是使用jdk提供的专门获取Properties配置文件的工具类:ResourceBundle */ private static String driver; private static String url; private static String username; private static String password; static{ //1、通过本类获取类加载器 ClassLoader classLoader = jdbcUtil_v3.class.getClassLoader(); //2、使用类加载器获得输入流 InputStream is = classLoader.getResourceAsStream("jdbc.properties"); //3、Properties工具类 Properties prop = new Properties(); //4、加载流 try { prop.load(is); } catch (IOException e) { e.printStackTrace(); } //5、 driver = prop.getProperty("driver"); url = prop.getProperty("url"); username = prop.getProperty("username"); password = prop.getProperty("password"); } /** * @Title: getConnection * @Description: TODO(获取mySql连接 ) * @return: Connection */ public static Connection getConnection(){ Connection conn = null; try { //注册驱动 Class.forName(driver); //获取连接 conn = DriverManager.getConnection(url,username,password); } catch (Exception e) { e.printStackTrace(); } return conn; } /** * @Title: release * @Description: TODO:释放连接mySql的资源 * @return: void */ public static void release(Connection conn,PreparedStatement ps,ResultSet rs){ try{ if(rs != null){ rs.close(); } if(ps != null){ ps.close(); } if(conn != null){ conn.close(); } }catch(SQLException e){ e.printStackTrace(); } }}
五、JDBC共享连接池的使用
对于频繁使用数据库或使用人数较多的项目,连接对象Connection的开开关关也是不好的吖,我们就可以设置JDBC连接池,连接池就相当于一个存放若干连接对象的池子,由于用户人数多,每次访问数据库需要一个连接对象,那么就从池子里取出一个,用完放回即可,而不是销毁。这样,就可以实现连接池共享的目的,减少系统资源消耗。
Java提供了一个公共接口:Javax.sql.DataSource。此接口提供了 DataSource
对象所表示的物理数据源的连接。作为 DriverManager
工具的替代项,DataSource
对象是获取连接的首选方法。
简单来说,就是DateSource接口是Drivermanager的替代项,提供了getConnection()方法并生产标准的Connection对象,那么要实现连接池,就需要实现该接口和该方法。(所以我们常说的数据源也就是连接池)连接池处理自定义的方式,目前主要的连接池工具有C3P0(主流)、DBCP。
1、自定义共享连接池
一般步骤:
实现数据源DataSource,即实现Javax.sql.DataSource接口;由于只是简单的演示,我们只实现其中的getConnection()方法即可。
创建一个LinkList容器。既然是“池子”,就需要保存东西,即存储连接池对象,而连接池涉及移除/添加连接池对象,优先考虑使用LinkList来存储。
使用静态代码块初始化若干个连接池对象。由于只是测试,我们初始化3个就行。
实现getConnection()方法。注意,为了保证连接对象只提供给一个线程(一个用户)使用,我们需要先将连接对象从池子中取出来。
用完的连接对象不需要执行close()而是放回池子去。
package cn.jdbcUtils;import java.io.PrintWriter;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.LinkedList;import java.util.logging.Logger;import javax.sql.DataSource;import cn.day09.jdbc.jdbcUtil;/** * @author Fzz * @date 2017年11月12日 * @Description TODO:自定义JDBC连接池 */public class MyDataSource implements DataSource{//[1]实现接口 //[2]创建一个容器存储连接池里的Connection对象。 private static LinkedList<Connection> pool = new LinkedList<Connection>(); //[3]初始化3个Connection对象放进池子。 static{ Connection conn = null; for (int i = 0; i < 3; i++) { conn = jdbcUtil.getConnection();//这里我们使用上面创建的jdbcUtils来获取连接 pool.add(conn); } } @Override /** * [4]从池子里取连接对象 */ public Connection getConnection() throws SQLException { //使用前先判断连接池是否有连接对象,没有则添加 Connection conn = null; if(pool.size() == 0){ for (int i = 0; i < 3; i++) { conn = jdbcUtil.getConnection(); pool.add(conn); } } conn = pool.removeFirst();//取出来 return conn; } /** * [5]用完归还连接到连接池 */ public boolean returnConnToPool(Connection conn){ return pool.add(conn); } //下面是未实现的方法。 @Override public PrintWriter getLogWriter() throws SQLException { // TODO Auto-generated method stub return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { // TODO Auto-generated method stub } @Override public void setLoginTimeout(int seconds) throws SQLException { // TODO Auto-generated method stub } @Override public int getLoginTimeout() throws SQLException { // TODO Auto-generated method stub return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { // TODO Auto-generated method stub return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { // TODO Auto-generated method stub return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { // TODO Auto-generated method stub return false; } @Override public Connection getConnection(String username, String password) throws SQLException { // TODO Auto-generated method stub return null; }}
【扩展:方法的增强】
自定义共享连接池到了这里基本已经完成,但美中不足的是:之前我们使用连接对象是这样的,用getConnection()方法获取,用完再用close()关闭。现在我们通过共享连接池来使用连接对象,是用getConnection()取出连接对象,使用returnConnToPool()归还连接对象。而美中不足的就是,我们希望通过共享连接池使用连接对象是仍然使用getConnection()和close()这两个方法,以保持前后一致的美感(呵呵),但我们是实现DataSource接口,接口中没有close()方法。可能你会说这是多余,就直接使用returnConnToPool()不挺好的吗,还能加深理解。
其实,这里我只是作为另一个知识点的引申——方法的增强,以此找了个小小的借口。(碧池)
目前有四种方法可以进行某个方法的增强,但各有优缺点:
使用继承增强方法。
使用前提是需要实现继承关系,子类通过继承父类方法,进而改进方法。使用装饰者设计模式增强方法。
装饰者设计模式是专门用来增强方法的设计模式,使用前提是需要装饰者实现被装饰对象(即被增强对象)相同接口,以实现交互。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。装饰者模式比继承灵活,但程序也相对复杂。使用动态代理增强方法。(难点)
动态代理与装饰者模式类似,且需要使用反射技术。使用字节码增强方法。(难点)
Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。Java字节码增强的应用场景主要是减少冗余代码,对开发人员屏蔽底层的实现细节。字节码增强技术主要有两种实现机制:一种是通过创建原始类的一个子类;另一种是直接去修改原先的class字节码。
目前有cglib、Javassist等框架提供使用。
上面只是简单的介绍,有意者可以自行搜索。此处不是本文重点故不再赘述,其实是笔者自己也没学。(只想对笔者说一句:“多捞啊”)
2、C3P0连接池的使用
C3P0是开源的共享连接池,比如Sring、Hibernate等开源项目都有使用C3P0。C3P0是第三方工具,除了需要导出相应jar包,还需要编写配置文件 c3p0-config.xml。
导包
我们首先去下载一个C3P0压缩包(自行百度)。然后解压找到lib目录,导入第一个包即可:编写配置文件:c3p0-config.xml
其实不一定非要配置文件,但相关设置比较麻烦建议编写。在压缩包的doc目录下有 index.html是c3p0的帮助文档(英文版):
帮助文档中就有配置文件相关格式的说明,这里总结如下:1 <?xml version="1.0" encoding="UTF-8"?> 2 <c3p0-config> 3 4 <!-- 默认配置1--> 5 <default-config> 6 <连接数据库的一些基本参数> 7 <property name="driverClass">com.mysql.jdbc.Driver</property> 8 <property name="jdbcUrl">jdbc:mysql:///FreeDB</property> 9 <property name="user">leslie</property>10 <property name="password">030401</property>11 <property name="initialPoolSize">5</property>12 <property name="maxPoolSize">20</property>13 </default-config>14 15 <!-- 配置2命名 -->16 <named-config name="mysql_web09"> 17 <property name="driverClass">数据库驱动</property>18 <property name="jdbcUrl">数据库url</property>19 <property name="user">用户名</property>20 <property name="password">密码</property>21 <property name="initialPoolSize">最小连接数</property>22 <property name="maxPoolSize">最大连接数</property>23 </named-config>24 25 </c3p0-config>
C3P0Utils
c3p0提供了一个工具类可以方便我们的使用,免去了多余类和方法的学习和直接使用:1 package cn.c3p0Utils; 2 3 import java.sql.Connection; 4 import java.sql.SQLException; 5 import com.mchange.v2.c3p0.ComboPooledDataSource; 6 7 /** 8 * C3P0工具类 9 * @author Fzz10 * @date 2017年11月12日12 */13 public class C3P0Utils {14 //创建c3p0的连接池对象(注:ComboPooledDataSource空参数时使用默认配置,否则传入需要使用的配置名字即可)15 private static ComboPooledDataSource datasource = new ComboPooledDataSource();16 /**17 * 返回DataSource18 * @Title: getDataSource19 * @Description: TODO:获得连接池对象20 * @return: ComboPooledDataSource21 */22 public static ComboPooledDataSource getDataSource(){23 return datasource;//将连接池对象返回即可24 }25 /**26 * 返回Connection27 * @Title: getConnection28 * @Description: TODO:获得连接对象(从池子中取)29 * @return: Connection30 */31 public static Connection getConnection(){32 try {33 return datasource.getConnection();34 } catch (SQLException e) {35 throw new RuntimeException(e);36 }37 }38 }
注:这里C3P0Utils提供没有进行连接对象的归还方法,这是因为一我们在使用完连接对象conn后直接使用conn.close()即可归还。(c3p0使用代理已经增强了close()方法不是关闭是归还连接池,和上文拓展说的一个意思),二是后面我们要使用DBUtils工具类进行数据库操作,而DBUtils底层会自动维护连接对象,固此处不再提供归还方法。
六、使用DBUtils工具类进行数据库测CRUD(增查改删)
上文我们也说到JDBC工具类对提高代码复用率的重要性,DBUtils是Apache大佬提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时又不会影响程序的性能。
1、准备工作
导包:既然是第三方工具,就需要导包,包括数据库驱动包、DBUtils包和连接池包(这里我们使用C3P0连接池):
准备连接池:比如编写c3p0工具类和配置文件。
JavaBean类的编写。JavaBean是一种JAVA语言规范写成可重用组件的类。写成JavaBean的类,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取。众所周知,属性名称符合这种模式,其他Java 类可以通过自省机制(反射机制)发现和操作这些JavaBean 的属性。比如我的数据库中有一个category表,那么我就可以设计一个JavaBean类。
1 package cn.domain; 2 3 public class category { 4 private String cid; 5 private String cname; 6 public category(){ 7 8 } 9 public String getCid() {10 return cid;11 }12 public void setCid(String cid) {13 this.cid = cid;14 }15 public String getCname() {16 return cname;17 }18 public void setCname(String cname) {19 this.cname = cname;20 }21 22 }
2、三个核心类
在使用之前我们先学习DBUtils的三个核心类:提供SQL语句操作API的QueryRunner类、用于执行select语句后进行结果集封装的ResultSetHandler接口、提供资源关闭、处理事务等方法的DbUtils工具类。
QueryRunner类:
new QueryRunner(DateSource ds):提供连接数据源(连接池)的方式。
update(String sql,Object...params):执行更新操作。
query(String sql,ResultSetHandler<T> rs,Object...paramas):执行查询操作。ResultSetHandler接口:(重点常用的三个已标红)
DbUtils工具类:
closeQuietly(Connection conn):关闭连接,如果有异常try后不抛。
commitAndCloseQuietly(Connection conn):提交事务并关闭连接。
rollbackAndCloseQuietly(Connection conn):回滚事务并关闭连接。
3、实现CRUD操作
这里举一个例子作为参考,其他步骤基本一致:
1 //使用DBUtils查询数据库 2 @Test 3 public void queryAll(){ 4 try { 5 //1、使用QueryRunner获得数据源(连接池) 6 QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); 7 //2、编写sql语句 8 String sql = "select * from category"; 9 //3、执行sql语句10 List<category> query = qr.query(sql,new BeanListHandler<category>(category.class));11 //4、处理结果12 for(category c:query){13 System.out.println(c.getCid()+":"+c.getCname());14 }15 } catch (SQLException e) {16 throw new RuntimeException(e);17 }18 }
作者:风之之