JDBC的开发

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时又进行了一次注册,所以实际上总共进行了两次数据库连接驱动注册行为。

c518a79dd9b265eff0359d022a3630fcfae.jpg

所以,可以使用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=off0;  设置其后执行的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=off0;  命令设置其后执行的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事务返回查询的数据结果后,会发现获取的数据会被另一个事务修改过。指一个线程中的事务读取到了另外一个线程中提交的update的数据。
    • 幻读:指一个线程中的事务读取到了另外一个线程中提交的insert的数据。
  • 持续性:是指一个事务一旦被提交,就会对数据库中的数据进行的改变是永久性的。

    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数据库。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/ProgramerLife/blog/2222149

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值