1.JDBC概述
1.驱动
- 为了能让程序员利用java程序操作数据库,数据库厂商提供了一套jar包,通过导入这个jar包就可以直接调用其中的方法操作数据库,这个jar包称之为驱动。两个数据库驱动互不兼容。
- 行业中有很多种的数据库,要使用这么多数据库需要学习很多数据库驱动,对于程序员来说学习成本非常高。
- 想要让java程序兼容数据库,所有的数据库驱动都实现了jdbc这套接口。
2.JDBC简介
JDBC全称为:Java Data Base Connectivity(java数据库连接),它主要由接口组成。
组成JDBC的2个包:java.sql包 javax.sql包
开发JDBC应用需要以上2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动)。
不仅需要jdbc接口,还需要驱动这个实现,驱动中就是对jdbc接口的一些实现。
有时候会发生classnotfoundExcption异常,主要原因如下:
确认包已经被导入web工程目录。
原来是tomcat找不到MYSQL JAR包的问题。后来又把mysql-connector-java-5.1.7-bin.jar导入到tomcat的lib目录下面就ok了,嘿……
在java项目中,只需要引入mysql-connector-java-5.1.7-bin.jar就可以运行java项目。
在web项目中,当Class.forName("om.mysql.jdbc.Driver");时myeclipse是不会去查找字符串,不会去查找驱动的。所以只需要把mysql-connector-java-5.1.7-bin.jar拷贝到tomcat下lib目录就可以了。
3. 6步实现jdbc
- 注册数据库驱动
- 获取数据库连接
- 创建传输器
- 传输sql并返回结果
- 遍历结果
- 关闭资源
在使用的时候一般导入的是接口而不是具体类,因为JDBC是对应不同的数据库驱动,所以如果采用的是具体类的话,更改了数据库代码就全要大改,而采用接口就不会了,因为所以的数据库都要实现JDBC接口。
package cn.tedu.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import com.mysql.jdbc.Driver; public class Demo1 { public static void main(String[] args) { Connection conn=null; ResultSet rs=null; Statement state=null; //获取数据库驱动 //手动注册一次,底层注册一次,总共两次注册驱动,应该一次就可以啊? //代码与mysql驱动包包名绑死,如果切换数据库则要修改导入包名 //DriverManager.registerDriver(new Driver()); try { Class.forName("com.mysql.jdbc.Driver"); //创建数据库连接 //Connection conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/lx", "root", "root"); //jdbc是主协议 :mysql是子协议 conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/lx?&user=root&password=root"); //获取传输器 state=conn.createStatement(); //利用传输器传输sql,并获取返回结果 rs=state.executeQuery("select * from exam"); //增删改用excuteUpdate() //遍历结果 while(rs.next()) { int id= rs.getInt("id"); String name= rs.getString("name"); System.out.println(id+""+name); } } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } finally { //关闭资源 后创建的先关闭 if(rs!=null) { try { rs.close();//关闭resultSet防止返回的结果堆积在内存 } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e); } finally { rs=null; //置空交给GC处理 } } if(state!=null) { try { state.close();//关闭掉传输器 } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e); } finally { state=null; } } if(conn!=null) { try { conn.close();//关闭掉连接,mysql有最大连接数的,不关闭就一直占用着连接数 } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e); } finally { conn=null; } } } } }
二程序详解
1. 程序详解—DriverManager
• Jdbc程序中的DriverManager用于加载驱动,并创建与数据库的链接,这个API的常用方法:
DriverManager.registerDriver(new Driver())
DriverManager.getConnection(url, user, password),
• 注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:
o 查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
o 程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
• 推荐方式:Class.forName(“com.mysql.jdbc.Driver”);
o 采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
o 同样,在开发中也不建议采用具体的驱动类型指向getConnection方法返回的connection对象。
2. 数据库URL
URL用于标识数据库的位置,程序员通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:
jdbc:mysql://localhost:3306/test ?参数名=参数值
3. 常用数据库URL地址的写法:sid是库名
Oracle写法:jdbc:oracle:thin:@localhost:1521:sid
SqlServer—jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
MySql—jdbc:mysql://localhost:3306/sid
Mysql的url地址的简写形式: jdbc:mysql:///sid 这个简写的前提是跟得地址名和服务器就是localhost:3306
常用属性:useUnicode=true&characterEncoding=UTF-8
4. 程序详解—Connection
o Jdbc程序中的Connection,它用于代表数据库的链接,Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法:
createStatement():创建向数据库发送sql的statement对象。
prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
prepareCall(sql):创建执行存储过程的callableStatement对象。
setAutoCommit(boolean autoCommit):设置事务是否自动提交。
commit() :在链接上提交事务。
rollback() :在此链接上回滚事务。
5. 程序详解—Statement
o Jdbc程序中的Statement对象用于向数据库发送SQL语句, Statement对象常用方法:
executeQuery(String sql) :用于向数据发送查询语句。
executeUpdate(String sql):用于向数据库发送insert、update或delete语句
execute(String sql):用于向数据库发送任意sql语句
addBatch(String sql) :把多条sql语句放到一个批处理中。
executeBatch():向数据库发送一批sql语句执行。
6. 程序详解—ResultSet
o Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
o ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:
o 获取任意类型的数据
getObject(int index)
getObject(string columnName)
o 获取指定类型的数据,例如:
getString(int index)
getString(String columnName)
o 提问:数据库中列的类型是varchar,获取该列的数据调用什么方法?Int类型呢?bigInt类型呢?Boolean类型?
o 常用数据类型转换表
8. ResultSet中的api
o ResultSet还提供了对结果集进行滚动的方法:
next():移动到下一行
Previous():移动到前一行
absolute(int row):移动到指定行
beforeFirst():移动resultSet的最前面。
afterLast() :移动到resultSet的最后面。
9. 程序详解—释放资源
o 为什么要关闭资源?
在安装数据库的时候,设置过最大连接数量,如果用了不还连接,别人就无法使用了。
rs对象中可能包含很大的一个数据,对象保存在内存中,这样就十分占用内存。需要将他关闭。
最晚创建的对象,最先关闭。
o Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
o 特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
o 为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
o 释放资源
o 在关闭过程中可能会出现异常,为了能够关闭资源,需要将资源在finally中关闭。
o 如果在finally中关闭资源则需要将conn,stat,rs三个对象定义成全局的变量。
o 在conn,stat,rs三个变量出现异常的时候可能会关闭不成功,我们需要将他们在finally中置为null。conn,stat,rs这三个对象是引用,将引用置为null,它引用的对象就会被JVM回收,也能保证资源的释放。
三.JDBC工具类
配置文件放置在src文件夹下
- 提出冗余代码原因:
在程序中,大量代码重复导致代码复用性低,工作效率低下,不美观。为了提供代码复用性,我们将创建连接和关闭连接提取到工具类中,方便以后调用,提升工作效率,增强代码复用性。
package cn.tedu.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; //工具类 (工厂模式) public class JDBCUtils { //类中的方法只能通过类名.的方式来调用 private JDBCUtils(){ } public static Properties prop = new Properties(); static{ try { prop.load(new FileInputStream(new File( //获取类加载器,JDBCUtils.class.getClassLoader() //通过类加载器获取src目录,getResource() //getResource()会得到从盘符到src目录的路径, //直接在括号中书写文件名称即可得到文件路径。 //getPath()是为了将url转换为String类型的数据 JDBCUtils.class.getClassLoader().getResource("conf.properties").getPath()))); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //创建连接 public static Connection getConnection() throws Exception{ Class.forName(prop.getProperty("driver")); return DriverManager.getConnection(prop.getProperty("url") , prop.getProperty("user"), prop.getProperty("password")); } //关闭资源 public static void close(Connection conn,Statement stat,ResultSet rs){ if(rs !=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ rs = null; } } if(stat != null){ try { stat.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ stat = null; } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ conn = null; } } } }
四.登录案例练习
1.Login程序需求:
编写一个Java程序,要求在控制台输入用户名和密码,如果用户名和密码与数据库中的内容匹配,则提示登录成功,如果不匹配则提示登录失败。
2.代码实现
package cn.tedu.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.Scanner; import cn.tedu.utils.JDBCUtils; //登录功能 public class Login { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String username = sc.nextLine(); System.out.println("请输入密码:"); String password = sc.nextLine(); //testLogin(username,password); PreparedtestLogin(username,password); } private static void PreparedtestLogin(String username, String password) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); //发送sql主干 ps = conn.prepareStatement("select * from user where username=? and password=?"); //发送参数 ps.setString(1, username); ps.setString(2, password); //通知数据库服务器执行sql rs = ps.executeQuery(); if(rs.next()){ System.out.println("登录成功"); }else{ System.out.println("登录失败"); } } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtils.close(conn, ps, rs); } } private static void testLogin(String username, String password) { //这个是statement的 Connection conn = null; Statement stat = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); stat = conn.createStatement(); rs = stat.executeQuery("select * from user where username= '"+username+"' and password='"+password+"'"); if(rs.next()){//如果为true,则证明能够查询到用户名,可以登录 System.out.println("登录成功"); }else{//其他情况都不能登录。 System.out.println("登录失败"); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JDBCUtils.close(conn, stat, rs); } } }
5sql注入
1. sql注入攻击
在网页中输入'#,或者' or '1=1 就能够直接登录。
select * from user where name = 'name' and password = 'password';
select * from user where name='name'#' and password='password' //#后买你的都是注释
由于执行的sql语句是在后台拼接出来的,其中有一部分内容是由用户从客户端传入,所以当用户传入数据中包含sql关键字时,就有可能通过这些关键字改变sql语句的语义,从而执行一些特殊的操作,这样的攻击方式就叫做sql注入攻击。
2. 解决方案 采用Statement子接口PreparedStatement可以有效防止sql注入
PreparedStatement是Statement的一个子接口。
具有预编译功能。
a. PreparedStatement发送sql 的步骤:
i. 先将sql语句的主干部分发送到数据库服务器中,参数位置使用"?"预留,sql语句到达服务器中时,会立刻变成一段机器码(二进制数据)这个机器码不能被操作。
ii. 再讲sql语句中的参数发送到数据库服务器,这些参数到达数据库服务器中时只会作为纯文本内容使用。
b. PreparedStatement优势:
参数可以单独传入,避免sql语句的拼接错误。
拥有预编译功能,可以防止sql注入攻击。
c. 拓展:
PreparedStatement 和 statement 谁能传入参数?
两者都可以。
六.批处理
1. 批处理机制
a. 在sql语句执行过程中,每个JDBC六步仅操作一个语句,如果有多个sql要执行,则在成很大的代码冗余,书写不便利。
b. 可以将这些sql语句放入一个JDBC的批处理中,一同发送的数据库服务器执行。
2. Statement批处理和PreparedStatement批处理
a. Statement批处理:
stat.addBatch(String sql); 添加sql 到批处理中
stat.addBatch(String sql);
stat.addBatch(String sql);
stat.executeBatch(); 执行批处理
b.代码实现
package cn.tedu.batch; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import cn.tedu.utils.JDBCUtils; //Statement批处理 /* create table t1(id int,name varchar(20)) insert into t1 values(1,'鸣人') insert into t1 values(2,'佐助') insert into t1 values(3,'小樱') insert into t1 values(4,'蝎子') Statement特点: 优点:1.可以执行不同语义的sql 缺点:1.没有预编译功能 2.每次都会将sql语句完整的发送到数据库服务器。 3.无法预留sql语句在服务器中,执行效率较低。 * */ public class StateBatchDemo1 { public static void main(String[] args) { Connection conn = null; Statement stat = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); stat = conn.createStatement(); stat.addBatch("create table t1(id int,name varchar(20))"); stat.addBatch("insert into t1 values(1,'鸣人')"); stat.addBatch("insert into t1 values(2,'佐助')"); stat.addBatch("insert into t1 values(3,'小樱')"); stat.addBatch("insert into t1 values(4,'蝎子')"); //通知数据库服务器 stat.executeBatch(); System.out.println("Statement批处理执行成功"); } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtils.close(conn, stat, rs); } } }
PreparedStatement批处理:
ps = conn.preparedStatement(String sql);
ps.setString(1,5);
ps.addBatch() | 添加sql参数 到批处理中 |
ps.executeBatch() | 执行批处理 |
ps.clearBatch(); | 清空批处理 |
代码实现
package cn.tedu.batch; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import cn.tedu.utils.JDBCUtils; //PreparedStatement批处理 /* * PreparedStatement特点: 优点:1.有预编译功能。 2.将sql主干预留在数据库服务器中,不必重复发送sql语句。 3.每次仅发送sql参数部分,执行效率较高。 缺点:1.只能执行同一语义的sql语句。 * * */ public class PreparedBatchDemo1 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement("insert into t1 values(?,?)"); for(int i=0;i<100000;i++){ ps.setInt(1, i); ps.setString(2, "name"+i); ps.addBatch(); if(i%1000 ==0){ ps.executeBatch(); ps.clearBatch(); System.out.println("执行完毕,当前批次数为:"+i/1000); } } //循环可能有不满一千的数据,通过本句来执行。 ps.executeBatch(); System.out.println("PreparedStatement执行完毕"); } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtils.close(conn, ps, rs); } } }
八连接池 连接池就是数据源
a. 为什么使用连接池?
o 缺点:用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、宕机。
o 它是将那些已连接的数据库连接存放在一个容器里(连接池),这样以后别人要连接数据库的时候,将不会重新建立数据库连接,会直接从连接池里取出可用的连接,用户使用完毕后,连接又重新还回到连接池中。
o 注意:连接池里的连接将会一直保存在内存里,即使你没用也是一样。所以这个时候你得权衡一下连接池的连接数量了。
2. 实现:
o 编写连接池需实现javax.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:
Connection getConnection()
Connection getConnection(String username, String password)
o 实现DataSource接口,并实现连接池功能的步骤:
• 在DataSource构造函数中批量创建与数据库的连接,并把创建的连接保存到一个集合对象中。
• 实现getConnection方法,让getConnection方法每次调用时,从集合对象中取一个Connection返回给用户。
• 当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到连接池的集合对象中,而不要把conn还给数据库。
• 扩展Connection的close方法。
• 在关闭数据库连接时,将connection存回连接池中,而并非真正的关闭。
已开启服务器启动的时候就有一定的连接池数量,当你的服务器访问量达到了就会增加连接数量,所以是一点点增加连接数量的
package cn.tedu.pool; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.LinkedList; import java.util.List; import javax.sql.DataSource; /** * 手写连接池 * @author 16 * */ public class MyPool implements DataSource{ //定义一个能够存储连接的数据结构,由于经常使用插入和删除操作,所以List较好。 private static List<Connection> pool = new LinkedList<Connection>(); static{//在程序之后立刻创建一批连接以备使用 try{ Class.forName("com.mysql.jdbc.Driver"); for(int i=0;i<5;i++){ //每次都创建一个新的连接对象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?user=root&password=root"); //将创建好的每一个连接对象添加到List中,模拟将连接加入连接池 pool.add(conn); } }catch(Exception e){ e.printStackTrace(); throw new RuntimeException(e); } } //创建连接(从连接池中取出一个连接) @Override public Connection getConnection() throws SQLException { if(pool.size()==0){//取出连接之前首先判断当前连接池中是否还有连接,如果没有则重新创建一批连接 for(int i=0;i<5;i++){ Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?user=root&password=root"); pool.add(conn); } } //从List中取出一个连接对象 //此处不能使用get(),get()方法只能读取对应下标的元素,没有将读取到的元素移除,如果是取出连接对象,应将对象移除。 Connection conn = pool.remove(0); ConnDecorate connpool = new ConnDecorate(conn,this); System.out.println("成功获取一个连接,池中还剩:"+pool.size()+"个连接"); return connpool; } //返还连接 //手写一个返还连接的方法 public void retConn(Connection conn){ try { //归还的连接如果已经关闭或者为空,则不允许放入池中。 if(conn!=null&&!conn.isClosed()){ pool.add(conn); System.out.println("成功还回一个连接,池中还剩:"+pool.size()+"个连接"); } } catch (SQLException e) { e.printStackTrace(); } } @Override public PrintWriter getLogWriter() throws SQLException { 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 <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; } }
c3p0的连接池技术比DBCP要好 ,推荐使用c3p0
开源连接池:
现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:
DBCP 数据库连接池
C3P0 数据库连接池
实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。
DBCP
DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:
Commons-dbcp.jar:连接池的实现
Commons-pool.jar:连接池实现的依赖库
DBCP示例代码:
static{
InputStream in = JdbcUtil.class.getClassLoader().
getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory(); //生成一个数据源工厂,通过工厂生产数据源
dataSource = factory.createDataSource(prop);
}
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
下面的橘色代码和红色代码都是配置数据库连接参数,橘色的是程序内设置,红色的是配置文件设置
package cn.tedu.pool; import java.io.File; import java.io.FileInputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; import cn.tedu.utils.JDBCUtils; //DBCP连接池测试使用 public class DBCPDemo1 { public static void main(String[] args) { Connection conn = null; Statement stat = null; ResultSet rs = null; BasicDataSource source = new BasicDataSource(); //basicDataSource是Dbcp 数据源的类型 ,这句话的意思是直接生成数据源 但是这个是没有配置信息的所以在下main我们要自己设置 source.setDriverClassName("com.mysql.jdbc.Driver"); source.setUrl("jdbc:mysql://localhost:3306/mydb1"); source.setUsername("root"); source.setPassword("root"); //利用工厂生产一个DBCP数据源对象 try { /*Properties prop = new Properties(); prop.load(new FileInputStream(new File(DBCPDemo1.class.getClassLoader().getResource("dbcp.properties").getPath())));*/ /*BasicDataSourceFactory factory = new BasicDataSourceFactory(); DataSource source = factory.createDataSource(prop);*/ conn = source.getConnection(); stat = conn.createStatement(); rs = stat.executeQuery("select * from exam"); while(rs.next()){ int id = rs.getInt(1); String name = rs.getString(2); System.out.println("id:"+id+">>name:"+name); } } catch (Exception e) { throw new RuntimeException(e); }finally{ if(rs !=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ rs = null; } } if(stat != null){ try { stat.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ stat = null; } } if(conn != null){ try { //归还连接 conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ conn = null; } } } } }
2.c3p0开源连接库
1.导入c3p0的jar包
配置文件的键一定要和api文档一致并且首字母小写
package cn.tedu.pool; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import com.mchange.v2.c3p0.ComboPooledDataSource; //C3P0连接池测试使用 public class C3P0Demo1 { public static void main(String[] args) { Connection conn = null; Statement stat = null; ResultSet rs = null; ComboPooledDataSource source = new ComboPooledDataSource(); try { /*source.setDriverClass("com.mysql.jdbc.Driver"); source.setJdbcUrl("jdbc:mysql://localhost:3306/mydb1"); source.setUser("root"); source.setPassword("root");*/ //这个是自己写的配置 conn = source.getConnection(); //如果配置文件的设置完全参照c3p0的要求,那么c3p0会自动获取配置文件的信息 stat = conn.createStatement(); rs = stat.executeQuery("select * from exam"); while(rs.next()){ String name = rs.getString("name"); System.out.println("name:"+name); } } catch (Exception e) { e.printStackTrace(); }finally{ if(rs !=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ rs = null; } } if(stat != null){ try { stat.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ stat = null; } } if(conn != null){ try { //归还连接 conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); }finally{ conn = null; } } } } }