JDBC入门
Posted youngchaolin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC入门相关的知识,希望对你有一定的参考价值。
接下来记录一下,JDBC的相关知识点
为什么使用JDBC
不同的数据库有不同的驱动,为了使用这些数据库如果没有JDBC就需要每个驱动都需要了解,但是有了JDBC后,就只需要了解JDBC的API就可以了。JDBC是数据库驱动的上层,里面主要包含一些接口,而各个数据库厂商需遵循这个接口来开发驱动。
组成JDBC需要2个包的支持,其为java.sql包和javax.sql包,另外使用哪个数据库还需要对应数据库的驱动包,如mysql选择mysql的驱动包,可以理解为驱动包为具体接口实现,而JDBC可以理解为包含这些实现的接口。
如何实现JDBC
step1 注册数据库驱动
step2 获取数据库连接Connection
step3 创建传输器Statement或PreparedStatement
step4 传输sql并返回结果集ResultSet
step5 遍历结果集
step6 关闭资源
实现JDBC最原始方式
接下来使用最原始的方式实现以上步骤,参考如下代码。
1 package com.boe.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 import com.mysql.jdbc.Driver; 10 11 public class ConnectionDemo 12 13 public static void main(String[] args) throws SQLException 14 //1 注册数据库驱动 15 DriverManager.registerDriver(new Driver()); 16 //2 获取数据库连接 17 Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2", "root", "2688"); 18 //3 创建传输器 19 Statement stmt=conn.createStatement(); 20 //4 创建sql会返回结果 21 ResultSet rs=stmt.executeQuery("SELECT *FROM dept"); 22 //5 遍历结果 23 while(rs.next())//next()会移动行的光标,如果移动到的行有数据返回true,否则返回false,初始化位置在第一行前面 24 //获取表字段数据 25 int id=rs.getInt("id"); 26 String name=rs.getString("name"); 27 System.out.println("id="+id+",name="+name); 28 29 //6 关闭资源 后创建的先关闭 30 rs.close();//不关闭,数据库数据会留在内存 31 stmt.close();//这个关闭会自动关闭rs 32 conn.close(); 33 34 35 36
控制台输出情况。
优化JDBC连接方式
以上为最原始的方式,但是存在问题,即DriverManager会注册两次,因为底层Driver有一个静态方法也会注册一次,因此这里采用反射加载驱动类来实现注册,具体参考代码。
底层静态代码块加载后会注册一次。
1 package com.boe.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 import com.mysql.jdbc.Driver; 10 11 public class ConnectionDemo1 12 13 public static void main(String[] args) 14 //1 注册数据库驱动 15 //问题1 手动注册了一次,底层代码也注册了一次,注册了两次驱动实际上,只需要注册一次即可 16 //问题2 包名和代码绑定死,如果更换数据库,需要修改包名 17 //DriverManager.registerDriver(new Driver()); 18 //定义成员变量 19 Connection conn=null; 20 Statement stmt=null; 21 ResultSet rs=null; 22 try 23 Class.forName("com.mysql.jdbc.Driver"); 24 //利用反射API获取一次Driver类,会自动执行一次Driver类中的静态方法 25 //2 获取数据库连接 26 //Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2", "root", "2688"); 27 //下面的连接方式写法也可以,参考文档26连接器 28 conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?user=root&password=2688"); 29 //3 创建传输器 30 stmt=conn.createStatement(); 31 //4 创建sql会返回结果 32 rs=stmt.executeQuery("SELECT * FROM dept"); 33 //5 遍历结果 34 while(rs.next())//next()会移动行的光标,如果移动到的行有数据返回true,否则返回false,初始化位置在第一行前面 35 //获取表字段数据 36 int id=rs.getInt("id"); 37 String name=rs.getString("name"); 38 System.out.println("id="+id+",name="+name); 39 40 catch (Exception e) 41 e.printStackTrace(); 42 throw new RuntimeException(e); 43 finally 44 //6 关闭资源 后创建的先关闭 45 try 46 if(rs!=null) 47 rs.close();//不关闭,数据库数据会留在内存 48 49 catch (SQLException e) 50 e.printStackTrace(); 51 //可以选择抛出运行时异常,不会打断程序 52 throw new RuntimeException(e); 53 finally 54 rs=null; 55 56 try 57 if(stmt!=null) 58 stmt.close();//这个关闭会自动关闭rs 59 60 catch (SQLException e) 61 e.printStackTrace(); 62 throw new RuntimeException(e); 63 finally 64 stmt=null; 65 66 try 67 if(conn!=null) 68 conn.close(); 69 70 catch (SQLException e) 71 e.printStackTrace(); 72 throw new RuntimeException(e); 73 finally 74 conn=null; 75 76 77 78
使用优化后的JDBC实现CRUB操作
以下简单的实现了CRUB操作,使用优化后注册方式。
1 package com.boe.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 import org.junit.Test; 10 11 import com.boe.utils.JDBCUtils; 12 13 public class ConnectionDemo3 14 //添加操作 15 @Test 16 public void add() 17 Connection conn=null; 18 Statement stmt=null; 19 try 20 //1 注册数据库驱动 21 Class.forName("com.mysql.jdbc.Driver"); 22 //2 创建数据库连接 23 conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?user=root&password=2688"); 24 //3 获取传输器 25 stmt=conn.createStatement(); 26 //4 输出sql 获取结果集 27 int i=stmt.executeUpdate("INSERT INTO dept Values(null,‘文娱部‘)"); 28 if(i>0) 29 System.out.println("插入成功,受到影响的行数为:"+i+"行"); 30 31 catch (Exception e) 32 e.printStackTrace(); 33 finally 34 //后创建的先关闭 35 try 36 if(stmt!=null) 37 stmt.close(); 38 39 catch (SQLException e) 40 e.printStackTrace(); 41 finally 42 stmt=null; 43 44 try 45 if(conn!=null) 46 conn.close(); 47 48 catch (SQLException e) 49 e.printStackTrace(); 50 finally 51 conn=null; 52 53 54 55 56 //更新数据的方法 57 @Test 58 public void update() 59 Connection conn=null; 60 Statement stmt=null; 61 try 62 Class.forName("com.mysql.jdbc.Driver"); 63 conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?user=root&password=2688"); 64 stmt=conn.createStatement(); 65 int i=stmt.executeUpdate("UPDATE dept SET name=‘IT部门‘ WHERE id=6"); 66 if(i>0) 67 System.out.println("更新成功,受到影响的行数为:"+i+"行"); 68 69 catch(Exception e) 70 e.printStackTrace(); 71 finally 72 //后创建的先关闭 73 try 74 if(stmt!=null) 75 stmt.close(); 76 77 catch (SQLException e) 78 e.printStackTrace(); 79 finally 80 stmt=null; 81 82 try 83 if(conn!=null) 84 conn.close(); 85 86 catch (SQLException e) 87 e.printStackTrace(); 88 finally 89 conn=null; 90 91 92 93 94 //删除数据 95 @Test 96 public void delete() 97 Connection conn=null; 98 Statement stmt=null; 99 try 100 conn=JDBCUtils.getConnection(); 101 stmt=conn.createStatement(); 102 int i=stmt.executeUpdate("DELETE FROM dept WHERE id=8"); 103 if(i>0) 104 System.out.println("删除成功,受到影响的行数为:"+i+"行"); 105 106 catch(Exception e) 107 e.printStackTrace(); 108 finally 109 JDBCUtils.closeConnection(conn, stmt, null); 110 111 112
生成工具类DBUtils
在使用的过程中发现很多代码需要重复写,如类加载和获取连接,以及关闭资源等,这些反复书写的代码可以提取出来,写到一个工具类里面,这里将创建连接需要的驱动类名,url,用户名和密码都保存在了一个properties文件里面,这样如果需要更改数据库就只需要修改配置文件即可。
1 package com.boe.utils; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.IOException; 7 import java.sql.Connection; 8 import java.sql.DriverManager; 9 import java.sql.ResultSet; 10 import java.sql.SQLException; 11 import java.sql.Statement; 12 import java.util.Properties; 13 14 //工厂类:不允许创建对象,方法为静态方法 15 public class JDBCUtils 16 //不允许创建对象,构造方法私有化 17 private JDBCUtils() 18 19 20 //prop变成静态,代表只创建一次 21 private static Properties prop=new Properties(); 22 static 23 try 24 prop.load(new FileInputStream(new File(JDBCUtils.class.getClassLoader().getResource("conf.properties").getPath()))); 25 catch (FileNotFoundException e) 26 e.printStackTrace(); 27 catch (IOException e) 28 e.printStackTrace(); 29 30 31 //写静态方法 32 public static Connection getConnection() throws Exception 33 //将配置信息加载prop 34 //Properties prop=new Properties(); 35 //prop.load(new FileInputStream(new File(JDBCUtils.class.getClassLoader().getResource("conf.properties").getPath()))); 36 //获取prop里的内容 37 String driver=prop.getProperty("driver"); 38 String url=prop.getProperty("url"); 39 String user=prop.getProperty("user"); 40 String pwd=prop.getProperty("password"); 41 //Class.forName("com.mysql.jdbc.Driver"); 42 Class.forName(driver); 43 //2 创建数据库连接 44 //return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2?user=root&password=2688"); 45 return DriverManager.getConnection(url,user,pwd); 46 47 public static void closeConnection(Connection conn,Statement stmt,ResultSet rs) 48 //后创建的先关闭 49 try 50 if(rs!=null) 51 rs.close(); 52 53 catch (SQLException e) 54 e.printStackTrace(); 55 finally 56 rs=null; 57 58 try 59 if(stmt!=null) 60 stmt.close(); 61 62 catch (SQLException e) 63 e.printStackTrace(); 64 finally 65 stmt=null; 66 67 try 68 if(conn!=null) 69 conn.close(); 70 71 catch (SQLException e) 72 e.printStackTrace(); 73 finally 74 conn=null; 75 76 77
使用工具类完成登录模拟
在完成工具类的泛化后,接下来使用它可以完成一个简单的登录案例,其中引出PreparedStatement和SQL注入,
如果使用Statement,会有SQL注入的风险,即SQL后台拼接后如果参数里有SQL关键字,可能导致整条SQL语义的变化导致不需要输入密码也可以登录的情况,如用户名后加上‘ #,或者密码里加上‘OR ‘ 1=1。这样的情况下,PreparedStatement就应用而生,它是Statement的一个子接口,使用它将先将SQL进行预编译,参数只是用?占位符来代替,后面传入参数也只是将其作为文本来处理,不会导致语义的变化。
1 package com.boe.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.Statement; 7 import java.util.Scanner; 8 9 import com.boe.utils.JDBCUtils; 10 11 //登录 12 public class Login 13 //用户输入用户名和密码,和数据库中的进行比较,如果匹配则提示登录成功,否则提示失败 14 public static void main(String[] args) 15 //获取控制台输入的用户名和密码 16 Scanner scan=new Scanner(System.in); 17 System.out.println("请输入用户名:"); 18 String user=scan.nextLine(); 19 System.out.println("请输入密码:"); 20 String pwd=scan.nextLine(); 21 //测试是否正确 22 testLogin( user,pwd); 23 //testPreparedStatementLogin(user,pwd); 24 25 26 /** 27 * 访问数据库,使用Preparedstatement根据用户名和密码进行访问 28 * @param user 29 * @param pwd 30 */ 31 private static void testPreparedStatementLogin(String user, String pwd) 32 Connection conn=null; 33 PreparedStatement ps=null; 34 ResultSet rs=null; 35 try 36 conn=JDBCUtils.getConnection(); 37 String sql="SELECT * FROM user WHERE username=? AND password=? "; 38 ps=conn.prepareStatement(sql); 39 ps.setString(1, user); 40 ps.setString(2, pwd); 41 rs=ps.executeQuery(); 42 if(rs.next()) 43 System.out.println("登录成功"); 44 else 45 System.out.println("登录失败"); 46 47 catch (Exception e) 48 e.printStackTrace(); 49 finally 50 //PreparedStatement继承自Statement接口,所以可以直接用 51 JDBCUtils.closeConnection(conn, ps, rs); 52 53 54 55 /** 56 * 访问数据库,根据用户名和密码进行访问 57 * @param user 58 * @param pwd 59 */ 60 private static void testLogin(String user, String pwd) 61 Connection conn=null; 62 Statement stmt=null; 63 ResultSet rs=null; 64 try 65 conn=JDBCUtils.getConnection(); 66 stmt=conn.createStatement(); 67 String sql="SELECT * FROM user WHERE username=‘"+user+"‘ AND password=‘"+pwd+"‘"; 68 rs=stmt.executeQuery(sql); 69 //打印sql,查看sql注入攻击 70 System.out.println(sql); 71 if(rs.next()) 72 System.out.println("登录成功"); 73 else 74 System.out.println("登录失败"); 75 76 catch (Exception e) 77 e.printStackTrace(); 78 finally 79 JDBCUtils.closeConnection(conn, stmt, rs); 80 81 82 83
不使用预编译的结果,可以看到可以sql注入。
批量处理
在介绍完上面的基本操作后,JDBC还提供批量处理的API,可以实现SQL批量处理,其有两种选择,一个是使用Statement的批处理,一个是使用PreparedStatement的批处理,两者各有优缺点。前者的话就是可以放入不同结构的SQL,但是没有预编译效率低,后者是有预编译效率高,但是更适合有相同结构但是参数不同的SQL。
Statement实现批处理
1 package com.boe.batch; 2 3 import java.sql.Connection; 4 import java.sql.Statement; 5 6 import com.boe.utils.JDBCUtils; 7 8 //批处理 9 /* 10 * create table t1(id int primary key auto_increment,name varchar(20)); 11 * insert into t1 values(null,‘鸣人‘); 12 * insert into t1 values(null,‘向日‘); 13 * insert into t1 values(null,‘卡卡西‘); 14 * insert into t1 values(null,‘大蛇‘); 15 * 16 * */ 17 public class StatementBatch 18 19 public static void main(String[] args) 20 Connection conn=null; 21 Statement stmt=null; 22 try 23 conn=JDBCUtils.getConnection(); 24 stmt=conn.createStatement(); 25 stmt.addBatch("create table t1(id int primary key auto_increment,name varchar(20))"); 26 stmt.addBatch(" insert into t1 values(null,‘鸣人‘)"); 27 stmt.addBatch(" insert into t1 values(null,‘向日‘)"); 28 stmt.addBatch(" insert into t1 values(null,‘卡卡西‘)"); 29 stmt.addBatch(" insert into t1 values(null,‘大蛇‘)"); 30 int[] arr=stmt.executeBatch(); 31 System.out.println(arr.toString()); 32 System.out.println("批处理执行完成"); 33 catch(Exception e) 34 e.printStackTrace(); 35 finally 36 JDBCUtils.closeConnection(conn, stmt, null); 37 38 39 40
PreparedStatement实现批处理
1 package com.boe.batch; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 6 import com.boe.utils.JDBCUtils; 7 8 /* 9 * create table t1(id int primary key auto_increment,name varchar(20)); 10 * insert into t1 values(null,‘鸣人‘); 11 * insert into t1 values(null,‘向日‘); 12 * insert into t1 values(null,‘卡卡西‘); 13 * insert into t1 values(null,‘大蛇‘); 14 * 15 * */ 16 public class PrepareBatch 17 18 public static void main(String[] args) 19 //PrepareStatement比较适合sql主干一致的 20 Connection conn=null; 21 PreparedStatement ps=null; 22 try 23 conn=JDBCUtils.getConnection(); 24 ps=conn.prepareStatement("INSERT INTO t1 values(null,?)"); 25 //添加批处理 26 for(int i=1;i<1000;i++) 27 ps.setString(1, "小明"+i); 28 ps.addBatch(); 29 if(i%100==0) 30 //每隔1000条执行一次 31 ps.executeBatch(); 32 //执行完的批处理可以清空 33 ps.clearBatch(); 34 System.out.println("第"+i/100+"批处理完成"); 35 36 37 //批量处理,收尾 38 ps.executeBatch(); 39 System.out.println("全部处理完成"); 40 catch(Exception e) 41 e.printStackTrace(); 42 finally 43 JDBCUtils.closeConnection(conn, ps, null); 44 45 46 47
数据库连接池
在大量数据库连接的情况下,显然普通的连接会给数据库造成巨大的压力,因为会频繁的创建连接删除连接,这种操作特别的消耗资源,在这样的情况下数据库连接池就应用而生,它会将获取到的数据库连接保存到一个池子中,需要连接就取不需要了就归还连接。下面实现一个简单的连接池,只包含获取归还连接,并且还将连接包装了close方法,这样实现调用连接的close方法只是将连接归还到连接池,具体参考代码。
手写数据库简单连接池
(1)连接池类需要实现DataSource接口,并写一个包装类包装Connection。
1 package com.boe.pool; 2 3 import java.io.PrintWriter; 4 import java.sql.Connection; 5 import java.sql.DriverManager; 6 import java.sql.SQLException; 7 import java.sql.SQLFeatureNotSupportedException; 8 import java.util.LinkedList; 9 import java.util.List; 10 import java.util.logging.Logger; 11 12 import javax.sql.DataSource; 13 14 public class MyConnectionPool implements DataSource 15 //在MyConnectionPool加载的时候初始化一批连接 16 public static List<Connection> pool=new LinkedList<Connection>(); 17 static 18 try 19 Class.forName("com.mysql.jdbc.Driver"); 20 for(int i=0;i<5;i++) 21 Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2", "root", "2688"); 22 pool.add(conn); 23 24 catch (Exception e) 25 e.printStackTrace(); 26 27 28 29 @Override 30 public PrintWriter getLogWriter() throws SQLException 31 // TODO Auto-generated method stub 32 return null; 33 34 35 @Override 36 public void setLogWriter(PrintWriter out) throws SQLException 37 // TODO Auto-generated method stub 38 39 40 41 @Override 42 public void setLoginTimeout(int seconds) throws SQLException 43 // TODO Auto-generated method stub 44 45 46 47 @Override 48 public int getLoginTimeout() throws SQLException 49 // TODO Auto-generated method stub 50 return 0; 51 52 53 @Override 54 public Logger getParentLogger() throws SQLFeatureNotSupportedException 55 // TODO Auto-generated method stub 56 return null; 57 58 59 @Override 60 public <T> T unwrap(Class<T> iface) throws SQLException 61 // TODO Auto-generated method stub 62 return null; 63 64 65 @Override 66 public boolean isWrapperFor(Class<?> iface) throws SQLException 67 // TODO Auto-generated method stub 68 return false; 69 70 71 //取出连接 72 @Override 73 public Connection getConnection() throws SQLException 74 //取出连接前先判断 75 if(pool.size()>0) 76 Connection conn=pool.remove(0);//移除一个连接,并将它返回 77 System.out.println("还有"+pool.size()+"条连接"); 78 //返回之前先包装连接 79 DecorateConnection connDeco=new DecorateConnection(conn, this); 80 return connDeco; 81 else 82 //如果没有就初始化一批连接 83 for(int i=0;i<5;i++) 84 Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb2", "root", "2688"); 85 pool.add(conn); 86 87 Connection conn=pool.remove(0);//移除一个连接,并将它返回 88 System.out.println("还有"+pool.size()+"条连接"); 89 //返回之前先包装连接 90 DecorateConnection connDeco=new DecorateConnection(conn, this); 91 return conn; 92 93 94 //归还连接 95 public void returnConnection(Connection conn) throws Exception 96 //判断连接还在,并且没有关闭,再归还连接 97 if(conn!=null&&!conn.isClosed()) 98 pool.add(conn); 99 System.out.println("还有"+pool.size()+"条连接"); 100 101 102 103 @Override 104 public Connection getConnection(String username, String password) 105 throws SQLException 106 // TODO Auto-generated method stub 107 return null; 108 109 110
包装类,实现部分方法。
1 package com.boe.pool; 2 3 import java.sql.Array; 4 import java.sql.Blob; 5 import java.sql.CallableStatement; 6 import java.sql.Clob; 7 import java.sql.Connection; 8 import java.sql.DatabaseMetaData; 9 import java.sql.NClob; 10 import java.sql.PreparedStatement; 11 import java.sql.SQLClientInfoException; 12 import java.sql.SQLException; 13 import java.sql.SQLWarning; 14 import java.sql.SQLXML; 15 import java.sql.Savepoint; 16 import java.sql.Statement; 17 import java.sql.Struct; 18 import java.util.Map; 19 import java.util.Properties; 20 import java.util.concurrent.Executor; 21 22 //Connection的包装类,重写close方法 23 public class DecorateConnection implements Connection 24 //属性 25 private Connection conn; 26 private MyConnectionPool pool; 27 //构造方法将自己写的连接池导入,可以调用里面的方法,进行装饰 28 public DecorateConnection(Connection conn,MyConnectionPool pool) 29 this.conn=conn; 30 this.pool=pool; 31 32 //重写close方法,这里实现将连接归还到连接池 33 @Override 34 public void close() throws SQLException 35 try 36 pool.returnConnection(conn); 37 catch (Exception e) 38 e.printStackTrace(); 39 finally 40 conn=null; 41 42 43 //重写下,因为包装类测试暂时用的方法不多,先重写它 44 @Override 45 public Statement createStatement() throws SQLException 46 System.out.println("包装类的createStatement方法执行了"); 47 return conn.createStatement(); 48 49 50 ......省略其他重写方法 51 52
测试一下连接池,是否销毁连接后有归还连接。
1 package com.boe.pool; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 import org.junit.Test; 10 11 import com.boe.utils.JDBCUtils; 12 //测试连接池 13 public class TestPool 14 //添加操作 15 @Test 16 public void add() 17 Connection conn=null; 18 Statement stmt=null; 19 MyConnectionPool pool=new MyConnectionPool(); 20 try 21 //此时得到的连接是包装好的 22 conn=pool.getConnection(); 23 //3 获取传输器 24 stmt=conn.createStatement(); 25 //4 输出sql 获取结果集 26 int i=stmt.executeUpdate("INSERT INTO dept VALUES(null,‘文娱部‘)"); 27 if(i>0) 28 System.out.println("插入成功,受到影响的行数为:"+i+"行"); 29 30 catch (Exception e) 31 e.printStackTrace(); 32 finally 33 //后创建的先关闭 34 try 35 if(stmt!=null) 36 stmt.close(); 37 38 catch (SQLException e) 39 e.printStackTrace(); 40 finally 41 stmt=null; 42 43 try 44 if(conn!=null) 45 //归还连接 46 //pool.returnConnection(conn); 47 //使用了包装类后,直接关闭连接 48 conn.close(); 49 50 catch (Exception e) 51 e.printStackTrace(); 52 finally 53 conn=null; 54 55 56 57
这是一个简单的实现,可以看到刚开始使用一条后还有4条连接,执行完插入数据库操作后,使用close方法后连接池又变回了5条连接,并没有销毁连接。但是实际上一般不使用自己写的,而使用厂商提供的连接池,比较常见的就是DBCP和C3P0,其中后者性能上要好一些。
DHCP连接池的使用
DHCP是Apache下的,使用需要导两个包,包commons-dbcp.jar是用于连接池实现,commons-pool.jar是提供连接池资源库,其使用有如下两种常见方法,但是建议使用配置文件的方式来初始化连接池,如下是直接在代码中初始化连接池,使用DataSource的实现类BasicDataSource。
1 package com.boe.pool; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 8 import org.apache.commons.dbcp.BasicDataSource; 9 10 //测试DBCP连接池 11 public class DPCPDemo1 12 13 public static void main(String[] args) 14 Connection conn=null; 15 PreparedStatement ps=null; 16 ResultSet rs=null; 17 //使用dbcp连接池获取连接 18 BasicDataSource bs=new BasicDataSource(); 19 //设置连接池属性,driver,url,name,password 20 bs.setDriverClassName("com.mysql.jdbc.Driver"); 21 bs.setUrl("jdbc:mysql://localhost:3306/mydb2");//也可以不写localhost:3306 22 bs.setUsername("root"); 23 bs.setPassword("2688"); 24 //获取连接 25 try 26 conn=bs.getConnection(); 27 ps=conn.prepareStatement("SELECT * FROM exam"); 28 rs=ps.executeQuery(); 29 while(rs.next()) 30 int id=rs.getInt("id"); 31 String name=rs.getString("name"); 32 System.out.println(id+":"+name); 33 34 catch (SQLException e) 35 e.printStackTrace(); 36 finally 37 //不是正的关闭,而是归还连接 38 try 39 conn.close(); 40 catch (SQLException e) 41 e.printStackTrace(); 42 43 44 45
如果使用配置文件,需要注意用户名需要修改为username,普通mysql连接写的是user,写好配置文件后,加载资源,然后使用工厂类来创建BasicDataSource。
1 package com.boe.pool; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.sql.Connection; 6 import java.sql.PreparedStatement; 7 import java.sql.ResultSet; 8 import java.sql.SQLException; 9 import java.util.Properties; 10 11 import javax.sql.DataSource; 12 13 import org.apache.commons.dbcp.BasicDataSourceFactory; 14 15 //测试DBCP连接池 16 public class DPCPDemo2 17 18 public static void main(String[] args) 19 Connection conn=null; 20 PreparedStatement ps=null; 21 ResultSet rs=null; 22 //获取连接 23 try 24 //使用工厂类 25 BasicDataSourceFactory factory=new BasicDataSourceFactory(); 26 //需要传入properties类型参数 27 Properties prop=new Properties(); 28 prop.load(new FileInputStream(new File(DPCPDemo2.class.getClassLoader().getResource("dbcp.properties").getPath()))); 29 //使用工厂方法,将有数据库连接信息的prop作为参数传入,得到连接池,这里使用接口引用指向 30 DataSource ds=factory.createDataSource(prop); 31 conn=ds.getConnection(); 32 ps=conn.prepareStatement("SELECT * FROM exam"); 33 rs=ps.executeQuery(); 34 while(rs.next()) 35 int id=rs.getInt("id"); 36 String name=rs.getString("name"); 37 System.out.println(id+":"+name); 38 39 catch (Exception e) 40 e.printStackTrace(); 41 finally 42 //不是正的关闭,而是归还连接 43 try 44 conn.close(); 45 catch (SQLException e) 46 e.printStackTrace(); 47 48 49 50
properties文件配置如下,可以参考。
1 # 设置mysql驱动,DBCP下的配置 2 driverClassName=com.mysql.jdbc.Driver 3 # 设置url 4 url=jdbc:mysql://localhost:3306/数据库名 5 # user 6 username=数据库用户名 7 # password 8 password=数据库密码
C3P0连接池的使用
C3P0是另外一款数据库连接池,使用也需要导包,这里使用版本为c3p0-0.9.1.2.jar,性能有博客说上要优于DBCP,它也可以直接代码传入连接参数使用,也可以使用配置文件,建议后者。
1 package com.boe.pool; 2 3 import java.beans.PropertyVetoException; 4 import java.sql.Connection; 5 import java.sql.PreparedStatement; 6 import java.sql.ResultSet; 7 8 import com.boe.utils.JDBCUtils; 9 import com.mchange.v2.c3p0.ComboPooledDataSource; 10 //c3p0连接池的普通使用 11 public class C3P0Demo 12 13 public static void main(String[] args) 14 Connection conn=null; 15 PreparedStatement ps=null; 16 ResultSet rs=null; 17 ComboPooledDataSource source=new ComboPooledDataSource(); 18 try 19 source.setDriverClass("com.mysql.jdbc.Driver"); 20 source.setJdbcUrl("jdbc:mysql:///mydb2"); 21 source.setUser("root"); 22 source.setPassword("2688"); 23 conn=source.getConnection(); 24 ps=conn.prepareStatement("SELECT * FROM exam"); 25 rs=ps.executeQuery(); 26 while(rs.next()) 27 int id=rs.getInt("id"); 28 String name=rs.getString("name"); 29 System.out.println(id+":"+name); 30 31 catch (Exception e) 32 e.printStackTrace(); 33 finally 34 JDBCUtils.closeConnection(conn, ps, rs); 35 36 37 38
如果使用配置文件,可以参考C3P0的文档来配置,本文是将配置文件放到src目录下使用。以下为文档内容,可以用来参考。
Named and Per-User configuration: Overriding c3p0 defaults via c3p0-config.xml
As of c3p0-0.9.1, you can define multiple configurations in an XML configuration file, and specify in your code which configuration to use. For any configurations (including the unnamed default configuration), you can define overrides for a particular database user. For example, if several applications access your database under different authentication credentials, you might define maxPoolSizeto be 100 for user highVolumeApp, but only 10 for user lowLoadApp. (Recall that Connections associated with different authentication credentials are of necessity separated into separate pools, so it makes sense that these could be configured separately.)
You can use the XML config file for all c3p0 configuration, including configuration of defaults. However, for users who don‘t want or need the extra complexity, the c3p0.properties file will continue to be supported.
By default, c3p0 will look for an XML configuration file in its classloader‘s resource path under the name "/c3p0-config.xml". That means the XML file should be placed in a directly or jar file directly named in your applications CLASSPATH, in WEB-INF/classes, or some similar location.
If you prefer not to bundle your configuration with your code, you can specify an ordinary filesystem location for c3p0‘s configuration file via the system property com.mchange.v2.c3p0.cfg.xml.
Here is an example c3p0-config.xml file:
1 <c3p0-config> 2 <default-config> 3 <property name="automaticTestTable">con_test</property> 4 <property name="checkoutTimeout">30000</property> 5 <property name="idleConnectionTestPeriod">30</property> 6 <property name="initialPoolSize">10</property> 7 <property name="maxIdleTime">30</property> 8 <property name="maxPoolSize">100</property> 9 <property name="minPoolSize">10</property> 10 <property name="maxStatements">200</property> 11 12 <user-overrides user="test-user"> 13 <property name="maxPoolSize">10</property> 14 <property name="minPoolSize">1</property> 15 <property name="maxStatements">0</property> 16 </user-overrides> 17 18 </default-config> 19 20 <!-- This app is massive! --> 21 <named-config name="intergalactoApp"> 22 <property name="acquireIncrement">50</property> 23 <property name="initialPoolSize">100</property> 24 <property name="minPoolSize">50</property> 25 <property name="maxPoolSize">1000</property> 26 27 <!-- intergalactoApp adopts a different approach to configuring statement caching --> 28 <property name="maxStatements">0</property> 29 <property name="maxStatementsPerConnection">5</property> 30 31 <!-- he‘s important, but there‘s only one of him --> 32 <user-overrides user="master-of-the-universe"> 33 <property name="acquireIncrement">1</property> 34 <property name="initialPoolSize">1</property> 35 <property name="minPoolSize">1</property> 36 <property name="maxPoolSize">5</property> 37 <property name="maxStatementsPerConnection">50</property> 38 </user-overrides> 39 </named-config> 40 </c3p0-config>
1 package com.boe.pool; 2 3 import java.beans.PropertyVetoException; 4 import java.sql.Connection; 5 import java.sql.PreparedStatement; 6 import java.sql.ResultSet; 7 8 import com.boe.utils.JDBCUtils; 9 import com.mchange.v2.c3p0.ComboPooledDataSource; 10 //c3p0连接池的使用xml配置文件 11 public class C3P0Demo1 12 13 public static void main(String[] args) 14 Connection conn=null; 15 PreparedStatement ps=null; 16 ResultSet rs=null; 17 ComboPooledDataSource source=new ComboPooledDataSource(); 18 try 19 /*source.setDriverClass("com.mysql.jdbc.Driver"); 20 source.setJdbcUrl("jdbc:mysql:///mydb2"); 21 source.setUser("root"); 22 source.setPassword("2688");*/ 23 conn=source.getConnection(); 24 ps=conn.prepareStatement("SELECT * FROM exam"); 25 rs=ps.executeQuery(); 26 while(rs.next()) 27 int id=rs.getInt("id"); 28 String name=rs.getString("name"); 29 System.out.println(id+":"+name); 30 31 catch (Exception e) 32 e.printStackTrace(); 33 finally 34 JDBCUtils.closeConnection(conn, ps, rs); 35 36 37 38
c3p0的配置文件名字使用c3p0-config.xml,如果上面ComboPooled里没有传入参数,读取的是配置在default-config里的信息,如果传入参数则读取named-config里的配置信息。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <c3p0-config> 3 <default-config> 4 <property name="driverClass">com.mysql.jdbc.Driver</property> 5 <property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb2</property> 6 <property name="user">root</property> 7 <property name="password">2688</property> 8 </default-config> 9 10 <!-- 可以配置很多,name中自己写名字,根据名字可以切换数据源,在ComboPooledDataSource()作为参数传入 --> 11 <named-config name="intergalactoApp"> 12 13 </named-config> 14 </c3p0-config>
以上即为JDBC的基础知识。
参考博文:
(1)https://www.jianshu.com/p/1564631ed0d4 C3P0
以上是关于JDBC入门的主要内容,如果未能解决你的问题,请参考以下文章