1. 简介与基本代码实现
1. 简介:jdbc即java数据库连接,是sun公司用于统一数据库操作代码而制定的一套规范(提供了一套接口由数据库厂商实现,开发者只需加载相应的数据库驱动并调用这些接口即可操作数据库),简称为JDBC。
2. 基本代码实现:
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;//这个就是mysql实现的Driver接口实现类 /** * @ClassName:JDBCDemo1 * @Description:JDBC基础开发过程 * 首先需要引入需要连接的对应数据库的jar包,比如mysql为mysql-connector-java */ public class JDBCDemo1 { public static void main(String[] args) { try { //1. 注册mysql数据库连接的驱动类 DriverManager.registerDriver(new Driver()); // 2. 获取到数据库的连接对象,指定数据库的url,用户名和密码 Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/studytest", "root", "123456"); //3. 编写SQL语句 String sql="select * from employee"; // 4. 执行SQL语句,创建能执行SQL语句的Statement对象,然后通过这个对象执行 Statement statement=con.createStatement(); // 5. 获取执行SQL语句的结果 ResultSet rs=statement.executeQuery(sql); // 6. 遍历rs中的结果 while(rs.next()){ // 获取某个字段的数据,可以通过字段名,也可以通过查询的所有字段中目标字段在第几个位置的下标 System.out.println(rs.getInt("id"));//可以通过字段名 System.out.println(rs.getString(2));;//也就是获取第二个字段username下的数据 System.out.println(rs.getFloat("salary")); System.out.println(); //注意获取的字段值的类型必须要对应get 数据类型()的方法 } // 7. 释放资源 rs.close(); statement.close(); con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
3. 上面所写的基本JDBC实现代码的缺点:
- java.sql.DriverManager类过于依赖某一个数据库驱动类的Driver对象
- 在创建mysql数据库驱动类的Driver对象时,com.mysql.jdbc.Driver类内部实际上已经进行了一次数据库驱动注册,而在使用DriverManager时又进行了一次注册,所以实际上总共进行了两次数据库连接驱动注册行为。
所以,可以使用Class.forName()来改善上面两个缺点。改善代码如下
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCDemo2 { public static void main(String[] args) { try { //加载com.mysql.jdbc.Driver类,直接进行数据库驱动注册 Class.forName("com.mysql.jdbc.Driver"); // 2. 获取到数据库的连接对象,指定数据库的url,用户名和密码 Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/studytest", "root", "123456"); //3. 编写SQL语句 String sql="select * from employee"; // 4. 执行SQL语句,创建能执行SQL语句的Statement对象,然后通过这个对象执行 Statement statement=con.createStatement(); // 5. 获取执行SQL语句的结果 ResultSet rs=statement.executeQuery(sql); // 6. 遍历rs中的结果 while(rs.next()){ // 获取某个字段的数据,可以通过字段名,也可以通过查询的所有字段中目标字段在第几个位置的下标 System.out.println(rs.getInt("id"));//可以通过字段名 System.out.println(rs.getString(2));;//也就是获取第二个字段username下的数据 System.out.println(rs.getFloat("salary")); System.out.println(); //注意获取的字段值的类型必须要对应get 数据类型()的方法 } // 7. 释放资源 rs.close(); statement.close(); con.close(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
4. Connection对象:代表一个与数据库之间的连接对象。其作用有
- 创建执行SQL语句的对象:
- createStatement():创建向数据库发送SQL语句的Statement对象
- prepareStatement(String sql):创建向数据库发送预编译SQL语句的PreparedStatement对象,预编译SQL可以防止SQL注入漏洞
- prepareCall(String sql):创建执行存储过程的CallableStatement 对象
- 管理事务:
- void setAutoCommit(boolean autocommit):设置事务是否自动提交
- void commit():在当前连接上提交事务
- void rollback():在当前连接上回滚事务
5. Statement对象:用于向数据库发送要执行的SQL语句,常用执行方法
- ResultSet executeQuery(String sql):专门用于执行查询SQL语句,执行后会返回查询结果集ResultSet 对象
- int executeUpdate(String sql):专门用于执行数据更新语句,比如insert、update、delete等,执行完毕后会返回改变了几行数据
- boolean execute(String sql):执行任何SQL语句,如果执行的是查询语句且有查询结果则返回true,否则为false
- void addBatch( String sql ):该方法用于将一个SQL语句添加至一批等待执行的SQL语句中,比如增删改数据的语句
- int[] executeBatch():批处理执行SQL语句,将一批SQL语句全部执行,返回每个SQL语句执行所改变的数据行数所组成的数组
- void clearBatch():清除当前这一批SQL语句
使用批处理执行大量SQL语句肯定要比单条SQL语句执行更快。
6. ResultSet 对象:用于保存查询语句返回的结果集,只有查询语句的执行才会返回该对象。其封装数据采用类似与表格的数据结构,其内部维护了一个指向表格数据行的游标,游标初始时会指向第一行数据,当执行ResultSet 对象的 next()方法时,游标便会下移一行,可以通过get 指定数据类型()的方法来获取指定字段下的当前行数据。比如
- String getString(String columnLabel):获取某一个字段下的某一行数据,并且转为String类型返回
- int getInt(String columnLabel):获取某一个字段下的某一行数据,并且转为int类型返回
- Object getObject(String columnLabel):获取某一个字段下的某一行数据,并且转为Object类型返回
还有其余获取数据方法,其方法名形式都为get 指定数据类型;每个方法都有两个版本,一个版本的参数为字段名字符串,另一个则是字段在查询得到的字段集中按照书写顺序的下标。通常都使用字段名字符串来取值。
7. 资源对象的释放:ResultSet对象、Statement对象和Connection对象在使用结束后都必须立即释放,尤其是Connection对象,而资源的释放代码一般都放在finally块里,保证资源释放代码一定会执行。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCDemo2 { public static void main(String[] args) { Connection con=null; Statement statement=null; ResultSet rs=null; try { //加载com.mysql.jdbc.Driver类,直接进行数据库驱动注册 Class.forName("com.mysql.jdbc.Driver"); // 2. 获取到数据库的连接对象,指定数据库的url,用户名和密码 con=DriverManager.getConnection("jdbc:mysql://localhost:3306/studytest", "root", "123456"); //3. 编写SQL语句 String sql="select * from employee"; // 4. 执行SQL语句,创建能执行SQL语句的Statement对象,然后通过这个对象执行 statement=con.createStatement(); // 5. 获取执行SQL语句的结果 rs=statement.executeQuery(sql); // 6. 遍历rs中的结果 while(rs.next()){ // 获取某个字段的数据,可以通过字段名,也可以通过查询的所有字段中目标字段在第几个位置的下标 System.out.println(rs.getInt("id"));//可以通过字段名 System.out.println(rs.getString(2));;//也就是获取第二个字段username下的数据 System.out.println(rs.getFloat("salary")); System.out.println(); //注意获取的字段值的类型必须要对应get 数据类型()的方法 } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { // 7. 释放资源 try { if(rs!=null){ rs.close(); } rs=null; } catch (SQLException e) { e.printStackTrace(); } try { if(statement!=null){ statement.close(); } statement=null; } catch (SQLException e) { e.printStackTrace(); } try { if(con!=null){ con.close(); } con=null; } catch (SQLException e) { e.printStackTrace(); } } } }
8. 修改连接数据库的几个必需字符串,改为通过配置文件的方式来获取:先添加一个db.properties文件,并填写对应数据
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 JDBCDemo3 { public static void main(String[] args) { Connection con=null; Statement statement=null; ResultSet rs=null; try { Properties pro=new Properties(); pro.load(JDBCDemo3.class.getClassLoader().getResourceAsStream("jdbc/db.properties")); String driver=pro.getProperty("driver"); String user=pro.getProperty("user"); String url=pro.getProperty("url"); String password=pro.getProperty("password"); Class.forName(driver); con=DriverManager.getConnection(url, user, password); String sql="select * from employee"; statement=con.createStatement(); rs=statement.executeQuery(sql); while(rs.next()){ System.out.println(rs.getInt("id")); System.out.println(rs.getString(2)); System.out.println(rs.getFloat("salary")); System.out.println(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { if(rs!=null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); } rs=null; try { if(statement!=null){ statement.close(); } } catch (SQLException e) { e.printStackTrace(); } statement=null; try { if(con!=null){ con.close(); } } catch (SQLException e) { e.printStackTrace(); } con=null; } } }
2. SQL注入漏洞
1. SQL语句是字符串类型,而且其通常都由多个字符串拼接而成,以用户登录为例
String username; String password; String sql="select * from user where username=" username "and password=" password;
username和password的值通常都是由用户输入,由前端传入后台进行验证,如果username输入为" 'sdfds' or 1=1",而password的值任意那么SQL语句就变成:
select * from user where username='sdfds' or 1=1 and password='任意值';
如果username正确,那么 username='sdfds' 得到true,而密码是错误的,而且and的优先级更高,所以 1=1 and password='任意值' 先进行逻辑运算 得到false,然后 true or false 得到true,所以会判断密码正确,进而就造成了不安全的SQL注入漏洞。
2. 解决方法:SQL语句的执行对象使用PreparedStatement对象,因为该对象会预编译SQL,可以防止SQL注入漏洞;该对象所执行的SQL语句,不需要通过字符串拼接来指定SQL语句中需要手动填入的字符串符合,而是替换为 ? 占位符的形式,然后将该SQL语句的格式进行预编译,当编译完成后SQL语句的格式就会被固定,通过PreparedStatement对象向 ? 处的数据需要填充的参数,填充在占位符的数据全部都会成为指定的数据类型形式,而不会出现上面拼接字符串中出现的情况(也就是说会把or、=等关键字当做普通字符串处理),所以就避免了SQL注入漏洞。
PreparedStatement对象通过
import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; public class JDBCDemo4 { public static void main(String[] args) { Connection con=null; PreparedStatement statement=null; ResultSet rs=null; try { Properties pro=new Properties(); pro.load(JDBCDemo3.class.getClassLoader().getResourceAsStream("jdbc/db.properties")); String driver=pro.getProperty("driver"); String user=pro.getProperty("user"); String url=pro.getProperty("url"); String password=pro.getProperty("password"); Class.forName(driver); con=DriverManager.getConnection(url, user, password); String id="1"; //String sql="select * from employee where id=" id; String sql="select * from employee where id=?";//将拼接字符串改为占位符形式 statement=con.prepareStatement(sql);//进行预编译,固定SQL语句格式 //设置占位符处参数的值,填充数据的参数类型为int型,设置在第一个 ? 占位符处,填充数据为2 //对于or、=等数据库关键字是不被识别的,也是会报错的,无法在这里设置字符串类型的数据,只能是int值 statement.setInt(1,2); //执行时不需要传入sql语句作为参数,因为在预编译时就已经传入,只需直接执行即可 rs=statement.executeQuery(); while(rs.next()){ System.out.println(rs.getInt("id")); System.out.println(rs.getString(2)); System.out.println(rs.getFloat("salary")); System.out.println(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { if(rs!=null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); } rs=null; try { if(statement!=null){ statement.close(); } } catch (SQLException e) { e.printStackTrace(); } statement=null; try { if(con!=null){ con.close(); } } catch (SQLException e) { e.printStackTrace(); } con=null; } } }
3. 事务
1. 简介:事务表示一组逻辑上的操作,要么全部成功要么全部失败。其有四个特性,原子性、一致性、隔离性和持续性。通常用于一组数据更新操作。
2. MySQL中事务相关的命令:
- start transaction; 使用该命令来开启一个mysql事务。
- commit; 使用该命令提交事务
- rollback; 使用该命令事务回滚
- set autocommit=off或0; 设置其后执行的SQL语句执行后不自动提交,执行该行命令后,表示在此命令之后执行的所有SQL语句都必须输入 commit 命令来提交SQL语句的执行结果,永久存储到数据库中,也就相当于每条SQL语句都会开启一个事务,执行完毕后需要输入提交或回滚命令。
- show variables like '%commit%'; 通过该语句查看当前数据库是否是自动提交事务的
3. 开启事务的两种方法:在事务中执行的SQL语句,操作的是临时表,更改的数据也是临时表的数据,提交之后才会更改数据库表中的数据
- 通过 使用该命令开启事务后,就可以执行多条SQL语句,当执行完毕后,如果过确认无误则输入命令进行事务提交,此时SQL语句执行的结果就会真正存储到数据库中,如果SQL语句执行有误,则输入事务回滚命令,就会初始化此次事务所执行的所有SQL语句的结果,也就是使数据库中的数据返回到本次事务中所有的SQL语句未执行前的状态。比如
start transaction;//开启事务 //执行多个SQL语句 sql1; sql2; ... commit;//提交SQL语句执行结果 或 rollback;//回滚SQL语句执行结果
- MySQL中的SQL语句执行完毕后,是默认会自动提交的,通过 set autocommit=off或0; 命令设置其后执行的SQL语句执行后不自动提交,执行该行命令后,表示在此命令之后执行的所有SQL语句都必须输入 commit 命令来提交SQL语句的执行结果,永久存储到数据库中,也就是相当于每条SQL语句都会开启一个事务,执行完毕后需要输入提交或回滚命令。比如
show variables like '%commit%';//查看当前数据库是否自动提交SQL执行结果的,如果是 set autocommit = off或0; sql1; sql2; ... commit;//提交 或 rollback;//回滚
4. JDBC代码中使用事务:通过java.sql.Connection对象来操作事务
- 开启事务:调用 void setAutoCommit(boolean autoCommit)方法,将参数值设置为false,即设置数据库取消自动提交事务。
- 提交事务:调用 void commit()
- 事务回滚:调用 void rollback()
import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; /** * @ClassName:JDBCDemo5 * @Description:JDBC代码中使用事务 */ public class JDBCDemo5 { public static void main(String[] args) { Connection con=null; PreparedStatement statement=null; ResultSet rs=null; try { Properties pro=new Properties(); pro.load(JDBCDemo3.class.getClassLoader().getResourceAsStream("jdbc/db.properties")); String driver=pro.getProperty("driver"); String user=pro.getProperty("user"); String url=pro.getProperty("url"); String password=pro.getProperty("password"); Class.forName(driver); con=DriverManager.getConnection(url, user, password); //开启事务 con.setAutoCommit(false); String sql="insert into employee values(null,'vn',20234,1)"; PreparedStatement ps=con.prepareStatement(sql); ps.executeUpdate(sql);//执行SQL语句 //提交事务 con.commit(); } catch (ClassNotFoundException e) { //若发生异常,则事务回滚 try { con.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } catch (SQLException e) { //若发生异常,则事务回滚 try { con.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } catch (IOException e) { //若发生异常,则事务回滚 try { con.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); }finally { try { if(rs!=null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); } rs=null; try { if(statement!=null){ statement.close(); } } catch (SQLException e) { e.printStackTrace(); } statement=null; try { if(con!=null){ con.close(); } } catch (SQLException e) { e.printStackTrace(); } con=null; } } }
5. 事务的4大特性:原子性、一致性、隔离性和持续性
- 原子性:指事务是一个不可分割的工作单位,其中的操作要么全部发生,要么全不发生。
- 一致性:事务前后的数据完整性必须保持一致,比如A表的某个字段依赖B表的主键,那么如果要删除B表中的主键数据时,必须先把A表中外键对应B表的数据先行删除。
- 隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间的数据要互相隔离。在并发访问时,如果不考虑隔离性,则会产生以下问题
- 数据脏读:指一个事务读取了另一个未提交的事务的数据。
- 不可重复读:在A事务中,多次重新执行一个查询,在B事务中进行数据修改并先行提交,当A事务返回查询的数据结果后,会发现获取的数据会被另一个事务修改过。正常来说,同一个事务内部,多次
- 虚读:在A事务中,多次重新执行一个查询,在B事务中进行数据插入并先行提交,当A事务返回查询的数据结果后,会发现获取的数据会被另一个事务修改过。
- 持续性:是指一个事务一旦被提交,就会对数据库中的数据进行的改变是永久性的。
6. 数据库事务隔离设置:有四个隔离级别,分别是
- serializable:可避免脏读、不可重复读、以及虚读情况,相当于将所有的事务进行串行化执行,效率最低,安全性最高
- repeatable read:避免不可重复读、脏读情况,但不能避免虚读。效率大于上一个,安全性小于上一个
- read committed:可以避免脏读情况(读已提交),效率大于上一个,安全性小于上一个
- read uncommitted:最低级别,以上情况均不能保证(未提交读),效率最高,安全性最低
MySQL数据库中可通过以下命令语句设置事务隔离级别(默认级别为repeatable read)
- set session transaction isolation level xx; (xx代表隔离级别)设置事务隔离级别
- select @@tx_isolation; 查询当前事务的隔离级别
7. JDBC代码中设置事务隔离级别:四个级别分别对应Connection对象中的四个静态变量
- static int TRANSACTION_READ_UNCOMMITTED = 1;
- static int TRANSACTION_READ_COMMITTED = 2;
- static int TRANSACTION_REPEATABLE_READ = 4;
- static int TRANSACTION_SERIALIZABLE = 8;
- void setTransactionIsolation(int level):Connection对象中通过该方法设置隔离级别,参数就是以上四个int变量
import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; public class JDBCDemo4 { public static void main(String[] args) { Connection con=null; PreparedStatement statement=null; ResultSet rs=null; try { Properties pro=new Properties(); pro.load(JDBCDemo3.class.getClassLoader().getResourceAsStream("jdbc/db.properties")); String driver=pro.getProperty("driver"); String user=pro.getProperty("user"); String url=pro.getProperty("url"); String password=pro.getProperty("password"); Class.forName(driver); con=DriverManager.getConnection(url, user, password); String id="1"; //String sql="select * from employee where id=" id; String sql="select * from employee where id=?";//将拼接字符串改为占位符形式 statement=con.prepareStatement(sql);//进行预编译,固定SQL语句格式 //设置占位符处参数的值,填充数据的参数类型为int型,设置在第一个 ? 占位符处,填充数据为2 //对于or、=等数据库关键字是不被识别的,也是会报错的,无法在这里设置字符串类型的数据,只能是int值 statement.setInt(1,2); rs=statement.executeQuery(); while(rs.next()){ System.out.println(rs.getInt("id")); System.out.println(rs.getString(2)); System.out.println(rs.getFloat("salary")); System.out.println(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { if(rs!=null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); } rs=null; try { if(statement!=null){ statement.close(); } } catch (SQLException e) { e.printStackTrace(); } statement=null; try { if(con!=null){ con.close(); } } catch (SQLException e) { e.printStackTrace(); } con=null; } } }
4. 数据库连接池技术
1. 在上面的jdbc代码,都是简单的实现代码,在代码中每开启一个Connection连接对象,都需要关闭释放,而获取和销毁连接操作都会消耗一些时间,而当有大量的开启关闭Connection动作时,就会使性能降低,效率低下,而且可能会造成内存溢出,而使用连接池技术就可以很好的避免这个问题。
2. 连接池技术:连接池技术类似于线程池,提前创建多个连接对象并放在连接池(也就是内存中,从内存取肯定比创建快)中,并且使用完后也不需要关闭销毁,当需要使用时就从连接池中获取,使用完后归还即可。
3. 连接池配置参数:
有默认值,但一般需要根据需求和条件来配置性能最高的参数
- 初始连接个数大小:连接池中的初始连接数
- 最小空闲连接数:当空闲连接数小于该值时,就会创建新连接放入连接池
- 增量:当需要创建新连接时,一次性创建的最小连接数
- 最大空闲连接数:当空闲的连接数超出这个数值时,就会把多余的连接销毁,仅剩下数量等于这个数值的连接
- 最大连接数:所能创建的最大连接数
- 最大等待时间:当最大连接数目的连接全都处于占用时,其他新的连接请求的最大等待连接时间,单位为毫秒。
无默认值,必须配置的参数:
- DriverClassName:要连接的数据库的驱动类的全限定名
- Url:数据库的地址
- Username:登录数据库的用户名
- Password:登录数据库的用户密码
4. 数据库连接池中利用装饰者模式增强的Connection对象:从数据库连接池中获取到的Connection对象并不是普通的由数据库厂商实现的Connection实现类的对象,而是将由数据库厂商实现的Connection实现类增强,将Connection对象方法中的close方法修改为不销毁连接,而是返还给数据库连接池。演示简单原理如下
//首先是sun公司提供的java.sql.Connection接口 //然后是数据库厂商实现sun公司提供的java.sql.Connection接口的实现类,以MySQL为例是com.mysql.jdbc.Connection //然后是数据库连接池中利用装饰者模式实现的com.mysql.jdbc.Connection类的增强类 /** * @ClassName:JDBCDemo7 * @Description:自定义数据库连接池 */ public class MyDataSource implements Connection{ public Connection conn;//该Connection对象指向的是一个由数据库厂商实现的Connection类 public List<Connection> list;//该集合表示数据库连接池中维护的Connection对象集合 //该构造方法会由数据库连接池来执行,当执行getConnection()方法时就会执行该方法并返回这个增强的Connection对象, //并传入对应参数,Connection conn参数就是数据库连接池中创建的com.mysql.jdbc.Connection对象 public MyDataSource(Connection conn,List<Connection> list){ this.conn=conn; this.list=list; } // 增强该方法,或者说修改该方法,不销毁连接对象,而是改为归还连接对象 public void close() throws SQLException { list.add(conn); } //还有其他许多要实现的方法,但都可以使用conn的相对应的方法,主要是close方法 }
所以,对于那些数据连接池的开源项目,其中获取的Connection对象的close方法都是返还连接,而不是销毁连接,所以只需使用该方法即可。
5. 常用的几个数据库开源项目:dbcp连接池、c3p0连接池、druid连接池(阿里提供,性能最好的),使用方式为
- dbcp连接池:需要导入名为commons-dbcp的jar包,使用时通过org.apache.commons.dbcp.BasicDataSource类来使用连接池的功能
import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import org.apache.commons.dbcp.BasicDataSource; /** * @ClassName:JDBCDemo7 * @Description:使用DBCP连接池,建立一个工厂类获取Connection对象 */ public class JDBCDemo7 { private static BasicDataSource bds; private static String driver; private static String user; private static String url; private static String password; private static String initsize; private static String maxsize; static{ //通过配置文件来读取数据库连接池的相应配置 Properties p=new Properties(); try { p.load(test1.class.getClassLoader().getResourceAsStream("jdbc/db.properties")); driver=p.getProperty("driver"); user=p.getProperty("user"); url=p.getProperty("url"); password=p.getProperty("password"); initsize=p.getProperty("initsize"); maxsize=p.getProperty("maxsize"); bds=new BasicDataSource(); bds.setDriverClassName(driver); bds.setUrl(url); bds.setPassword(password); bds.setUsername(user); bds.setInitialSize(Integer.parseInt(initsize)); bds.setMaxActive(Integer.parseInt(maxsize)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("配置文件读取失败",e); } } public Connection getConnection() throws SQLException{ return bds.getConnection(); } public void close(Connection con){ if(con!=null) try { con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("连接关闭异常",e); } } public static void main(String[] args) { JDBCDemo7 t=new JDBCDemo7(); Connection con=null; try { con=t.getConnection(); System.out.println(con.toString()); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { try { con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
- c3p0连接池:需要导入名为c3p0的jar包,使用时通过com.mchange.v2.c3p0.ComboPooledDataSource类来使用连接池的功能,具体实现遇上面dbcp连接池的使用方式基本类似
- druid连接池:需要导入名为druid的jar包,使用时通过com.alibaba.druid.pool.DruidDataSource类来使用连接池的功能
5. JDBC查询数据分页
1. 简介:在开发中,常常会将从数据库中查询到的数据,分为多个部分,在客户端使用多个页面来向用户展示,比如百度搜索出来的结果。分页分为物理分页和逻辑分页,
- 物理分页:指利用MySQL等数据库中的SQL语句中的分页关键字来实现分页查询,比如MySQL数据库中的分页关键字为limit,使用形式比如 select * from mytable limit m,n; 表示检索第m 1条到第m n条数据,m<n。
- 逻辑分页:表示先用SQL语句查询到需要的数据结果,然后将这个数据结果通过逻辑代码进行按页分割,将分割后指定部分的数据返回。主要应用于没有分页关键字的数据库,比如Oracle数据库。