• JDBC及开源连接池Druid和C3P0的使用


    1.JDBC

    前面我们讲解了如何创建我们的第一个JDBC程序,接下来就详细的介绍我们的第一个JDBC程序

    JDBC概述:JDBC是一种用于执行sql语句的java API,可以为多种关系数据库提供统一访问,它由一组java语言编写的类和接口组成,JDBC提供了一种基准,据此1就可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。

    什么是数据库驱动

    两个设备(应用)之间通信的桥梁

    为什么要学习JDBC

    没有JDBC的时候,如果现在要开发一套系统,使用java连接MySQL数据库,那么这时候java程序员需要了解MySQL驱动API,如果使用java连接Oracle数据库,那么这个时候java程序源需要了解Oracle数据库驱动API,后来,SUN公司就提供了一套统一的规范(接口),然后各个数据库生产商提供这套接口的实现,这套接口规范就是JDBC的规范了

    JDBC的环境部署(以下JDBC操作的数据库都是这个数据库)

    创建数据库:

    create database my;
    

    创建表:

    -- 用户表 
    
    CREATE TABLE user(
    	 nid int not null auto_increment PRIMARY KEY,
    	 username varchar(20),
    	 password varchar(20),
    	 role varchar(20)
    );
    
    -- 银行账户表
    
    create table account(
    nid int not null auto_increment PRIMARY key,
    name varchar(20),
    money int
    );
    

    添加数据:

    //往用户表添加数据
    
    insert into student(username, `password`, role) VALUES("luyi", "123", "student"), ("luer","321", "teacher")
    
    //往银行账户表添加数据
    insert into account(name, money) values("luyi", 1000), ("luer", 1000)
    

    JDBC的使用步骤

    1.加载驱动

    2.获得连接

    3.基本操作(增删改查)

    4.释放资源

    代码示例:

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;
    
    public class JDBCDemo {
    	public static void main(String[] args) throws Exception {
    		//加载驱动
    		Class.forName("com.mysql.cj.jdbc.Driver");
    		//获得连接
    		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/my?serverTimezone=UTC", "root", "123");
    		//获得执行sql的对象
    		Statement statement = conn.createStatement();
    		//编写sql语句
    		String sql = "select * from user";
    		//执行sql语句
    		ResultSet rs = statement.executeQuery(sql);
    		//遍及结果集
    		while(rs.next()){
    			System.out.println(rs.getInt("id") + " ");
    			System.out.println(rs.getString("username") + " ");
    			System.out.println(rs.getString("password") + " ");
    			System.out.println(rs.getString("role") + " ");
    		}
    		
    		//释放资源
    		rs.close();
    		statement.close();
    		conn.close();
    	}
    }
    

    2.JDBC的API详解

    DriverManager:驱动管理类

    • registerDriver(Driver driver):该方法可以完成驱动的注册,但是实际开发中一般不会使用这个方法完成驱动的注册,因为通过查看源代码我们可以知道,在代码中有一段静态代码块,静态代码块已经调用了注册驱动的方法,所以如果我们手动调用该方法注册驱动,就会导致驱动被注册两次,实际开发中我们使用Class.forName("com.mysql.cj.jdbc.Driver"),通过加载这个类,然后执行静态代码块的代码,注册了驱动
    • getConnection(url, user, password):这个方法就是用来获得与数据库连接的,这个方法的三个参数,返回值是一个Connection实现类对象:
      • url:与数据库连接的路径:格式为jdbc:mysql://localhost:3306/mydb,如果连接的是本机,则可简写为jdbc:mysql:///mydb
        • jdbc:连接数据库的协议
        • mysql:是jdbc的子协议,我们用的是mysql数据
        • localhost:连接的MySQL数据库服务区的主机地址(连接本机的数据所以写出localhost,如果不是本机的数据库,就需要写上连接主机的ip地址)
        • 3306:MySQL数据库服务器的端口号
        • mydb:数据库名称
      • user:与数据库连接的用户名
      • password:与数据库连接的密码

    Connection:与数据库连接的对象,是一个接口

    Connection的实现类对象可以干嘛呢?

    1.创建执行sql语句的对象:

    • createStatement():创建一个Statement对象,用于将sql语句发送到数据库
    • prepareCall(String sql):创建一个CallableStatement对象来调用数据库存储过程
    • prepareStatement(String sql):创建一个PreparedStatment对象来将参数化的sql语句发送到数据库,用于对sql进行预处理,解决sql注入漏洞

    2.管理事务

    • setAutoCommit(boolean autoCommit):用于设置是否自动提交事务
    • commit():用于提交事务
    • rollback():事务回滚的方法

    Statement:用于执行静态sql语句并返回它所生成结果的对象,一个接口

    Statement的实现类对象可以干嘛呢?

    1.执行sql:

    • boolean execute(String sql):执行查询,修改,添加,删除的sql语句,不经常使用
    • ResultSet executeQuery(String sql):执行查询语句,返回结果集ResultSet
    • int executeUpdate(String sql):执行修改,添加,删除的sql语句

    2.执行批处理

    • void addBatch(String sql):将给定的sql命令添加到此Statement对象的当前命令列表中(添加批处理)
    • void clearBatch():清空此Statement对象的当前sql命令列表(删除批处理)
    • int[] executeBatch():将命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组(执行批处理)

    ResultSet(结果集):表示的是数据库结果集的数据表,通常通过执行查询数据库的语句生成,是一个接口

    1.结果集的遍历和获取:

    • next():将光标从当前位置向前移动一行
    • getXxx(int columnIndex):根据第几列第几列来获取数据
    • getXxx(String columnName):根据列名来获取数据

    JDBC资源释放

    概述:JDBC程序执行结束后,将与数据库进行交互的对象释放掉,通常是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 JDBCDemo {
    	public static void main(String[] args) {
    		
    		Connection conn = null;
    		Statement statement = null;
    		ResultSet rs = null;
    		try{
    			//加载驱动
    			Class.forName("com.mysql.cj.jdbc.Driver");
    			//获得连接
    			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/my?serverTimezone=UTC", "root", "123");
    			//获得执行sql的对象
    			statement = conn.createStatement();
    			//编写sql语句
    			String sql = "select * from user";
    			//执行sql语句
    			rs = statement.executeQuery(sql);
    			//遍及结果集
    			while(rs.next()){
    				System.out.println(rs.getInt("id") + " ");
    				System.out.println(rs.getString("username") + " ");
    				System.out.println(rs.getString("password") + " ");
    				System.out.println(rs.getString("role") + " ");
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			//标准的释放资源
    			if(statement != null){
    				try {
    					statement.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				statement = null;
    			}
    			
    			if(conn != null){
    				try {
    					conn.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				conn = null;
    			}
    			if(rs != null){
    				try {
    					rs.close();
    				} catch (SQLException e1) {
    					// TODO Auto-generated catch block
    					e1.printStackTrace();
    				}
    				rs = null;
    			}		
    		}
    	}
    }
    

    3.JDBC的CRUD操作

    代码示例(对数据库进行增删改查):

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class JDBCDemo {
    	public static void main(String[] args) {
    		
    		Connection conn = null;
    		Statement statement = null;
    		ResultSet rs = null;
    		try{
    			//加载驱动
    			Class.forName("com.mysql.cj.jdbc.Driver");
    			//获得连接
    			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/my?serverTimezone=UTC", "root", "123");
    			//获得执行sql的对象
    			statement = conn.createStatement();
    			//编写sql语句
    			String sql1 = "insert into user(username, password, role) value('卢一', '123', 'doctor')";//添加操作
    			//String sql2 = "delete from user where username = 'luyi'";//删除操作
    			//String sql3 = "update user set password = '666' where username = 'luer'";//修改操作
    			String sql4 = "select * from user";//查询操作
    			
    			//增删改操作对应返回影响的行数
    			int column = statement.executeUpdate(sql1);
    			if(column > 0){
    				System.out.println("操作成功");
    			}
    			
    			/*//查询操作对应返回结果集
    			 * //执行sql语句
    			rs = statement.executeQuery(sql4);
    			//遍及结果集
    			while(rs.next()){
    				System.out.print(rs.getInt("id") + " ");
    				System.out.print(rs.getString("username") + " ");
    				System.out.print(rs.getString("password") + " ");
    				System.out.println(rs.getString("role"));
    			}*/
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			//标准的释放资源
    			if(statement != null){
    				try {
    					statement.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				statement = null;
    			}
    			
    			if(conn != null){
    				try {
    					conn.close();
    				} catch (SQLException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				conn = null;
    			}
    			if(rs != null){
    				try {
    					rs.close();
    				} catch (SQLException e1) {
    					// TODO Auto-generated catch block
    					e1.printStackTrace();
    				}
    				rs = null;
    			}		
    		}
    	}
    }
    

    4.对JDBC的工具类的抽取以及提取配置信息到配置文件

    从上面对JDBC的增删改查操作可以看出

    1.工具类:

    没有对象封装数据体现,只有方法,禁止了对象创建

    2.配置文件:

    • 属性文件
      • 格式:扩展名是.properties
      • 内容:key=value:键值对形式出现
    • XML文件

    示例代码:

    //db.properties属性配置文件
    
    driverClassName = com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/my?serverTimezone=UTC
    username=root
    password=123
    
    //JDBCUtils.java
    
    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;
    
    /*
     * JDBC的工具类
     */
    public class JDBCUtils {
    	/*
    	 * 注册驱动的方法
    	 */
    	private static final String driverClassName;
    	private static final String url;
    	private static final String username;
    	private static final String password;
    	
    	static{
    		//获取属性文件中的内容
    		Properties properties = new Properties();
    		try {
    			properties.load(new FileInputStream("src/db.properties"));
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		driverClassName = properties.getProperty("driverClassName");
    		url = properties.getProperty("url");
    		username = properties.getProperty("username");
    		password = properties.getProperty("password");
    	}
    	
    	public static void loadDriver(){
    		try {
    			Class.forName(driverClassName);
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/*
    	 * 获得连接的方法
    	 */
    	public static Connection getConnection(){
    		Connection conn = null;
    		try {
    			conn =  DriverManager.getConnection(url, username, password);
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    		return conn;
    	}
    	
    	/*
    	 * 释放资源的方法
    	 */
    	public static void release(Statement stmt, Connection conn){
    		if(stmt != null){
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			stmt = null;
    		}
    		
    		if(conn != null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			conn = null;
    		}
    	}
    	public static void release(ResultSet rs, Statement stmt, Connection conn){
    		if(stmt != null){
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			stmt = null;
    		}
    		
    		if(conn != null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			conn = null;
    		}
    		if(rs != null){
    			try {
    				rs.close();
    			} catch (SQLException e1) {
    				e1.printStackTrace();
    			}
    			rs = null;
    		}		
    	}
    }
    
    //JDBCUtilsText
    
    /*
     * JDBC工具类的测试类
     */
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.Statement;
    
    public class JDBCUtilsText {
    	public static void main(String[] args) {
    		Connection conn = null;
    		Statement stmt = null;
    		ResultSet rs = null;
    		try{
    			//加载驱动
    			JDBCUtils.loadDriver();
    			//获得连接
    			conn = JDBCUtils.getConnection();
    			//sql操作
    			stmt = conn.createStatement();
    			String sql = "select * from user";
    			rs = stmt.executeQuery(sql);
    			//遍历结果集
    			while(rs.next()){
    				System.out.print(rs.getString("username") + " ");
    				System.out.print(rs.getString("password") + " ");
    				System.out.println(rs.getString("role"));
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			JDBCUtils.release(rs, stmt, conn);
    		}
    		
    		
    	}
    }
    

    5.JDBC的SQL注入及解决方式

    sql注入的概述:SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库

    sql注入攻击实例:

    String sql = "select * from user_table where username=
    ' "+userName+" ' and password=' "+password+" '";
    
    --当输入了上面的用户名和密码,上面的SQL语句变成:
    SELECT * FROM user_table WHERE username=
    '’or 1 = 1 -- and password='’
    
    """
    --分析SQL语句:
    --条件后面username=”or 1=1 用户名等于 ” 或1=1 那么这个条件一定会成功;
    
    --然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用,这样语句永远都--能正确执行,用户轻易骗过系统,获取合法身份。
    --这还是比较温柔的,如果是执行
    SELECT * FROM user_table WHERE
    username='' ;DROP DATABASE (DB Name) --' and password=''
    --其后果可想而知…
    """
    

    JDBC的SQL注入漏洞解决

    需要采用PreparedStatement对象解决SQL注入漏洞,这个对象将SQL预先进行编译1,使用?作为占位符,?所代表的内容就是SQL所固定的,再次传入变量(包含SQL的关键字),这个时候也不会识别这些关键字

    使用PrepareStatement对象进行增删改查:

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    public class JDBCUtilsText {
    	public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		try{
    			//加载驱动
    			JDBCUtils.loadDriver();
    			//获得连接
    			conn = JDBCUtils.getConnection();
    			
    			//预编译sql操作
    			
    			//添加操作
    			/*String sql1 = "insert into user(username, password, role) value(?, ?, ?)";
    			pstmt = conn.prepareStatement(sql1);
    			pstmt.setString(1, "赵露思");
    			pstmt.setString(2, "888");
    			pstmt.setString(3, "actor");*/
    			
    			//删除操作
    			/*String sql2 = "delete from user where role = ?";
    			pstmt.setString(1, "doctor");*/
    			
    			//修改操作
    			String sql3 = "update user set password = ? where username = ?";
    			pstmt = conn.prepareStatement(sql3);
    			pstmt.setString(1, "000000");
    			pstmt.setString(2, "赵露思");
    			int column = pstmt.executeUpdate();
    			if(column > 0){
    				System.out.println("操作成功");
    			}
    			
    			//查询操作
    			/*String sql4 = "select * from user where username = ? and password = ?";
    			pstmt = conn.prepareStatement(sql4);
    			pstmt.setString(1, "luer");
    			pstmt.setString(2, "666");
    			rs = pstmt.executeQuery();
    			if(rs.next()){
    				System.out.println(rs.getString("username"));
    				System.out.println(rs.getString("password"));
    			}*/
    			
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			JDBCUtils.release(rs, pstmt, conn);
    		}
    	}
    }
    

    6.JDBC的批处理

    批量插入数据

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    
    public class JDBCDemo2 {
    	public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		try{
    			JDBCUtils.loadDriver();
    			conn = JDBCUtils.getConnection();
    			String sql = "insert into user(username, password, role) values(?, '123', 'student')";
    			pstmt = conn.prepareStatement(sql);
    			
    			for(int i = 1; i <= 10000; i ++){
    				pstmt.setString(1, "luyi" + i);
    				//添加到批处理
    				pstmt.addBatch();
    				//每添加1000条就执行一次批处理并释放
    				if(i % 1000 == 0){
    					//执行批处理
    					pstmt.executeBatch();
    					//清空批处理
    					pstmt.clearBatch();
    				}
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			JDBCUtils.release(pstmt, conn);
    		}
    		
    	}
    }
    
    //注意:db.properiter属性文件中url需要传递一个参数表示允许批处理,修改后如下:
    url=jdbc:mysql://localhost:3306/my?serverTimezone=UTC&rewriteBatchedStatements=true
    

    7.JDBC的事务管理

    事务的概念:事务是指逻辑上的一组操作,组成这组操作的各个逻辑单元要么全部成功,要么全部失败

    JDBC的事务管理案例(银行转账案例):

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    public class AffairDemo {
    	public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		
    		try{
    			//加载驱动
    			JDBCUtils.loadDriver();
    			//获得连接
    			conn = JDBCUtils.getConnection();
    			//开启事务
    			conn.setAutoCommit(false);
    			//编写sql语句,用luyi账户给luer账户转账
    			String sql = "update account set money = money + ? where name = ?";
    			pstmt = conn.prepareStatement(sql);
    			pstmt.setInt(1, -100);
    			pstmt.setString(2, "luyi");
    			//执行sql
    			pstmt.executeUpdate();
    			
    			//使其报错
    			int i = 1/0;
    			
    			//给luer账号加100
    			pstmt.setInt(1, 100);
    			pstmt.setString(2, "luer");
    			pstmt.executeUpdate();
    			//提交事务
    			conn.commit();
    		}catch(Exception e){
    			//事务回滚
    			try {
    				conn.rollback();
    			} catch (SQLException e1) {
    				e1.printStackTrace();
    			}
    			e.printStackTrace();
    		}finally{
    			JDBCUtils.release(pstmt, conn);
    		}
    	}
    }
    
    //如果没有添加事务,程序里面的这个报错就会使得luyi转账没了100,但是luer却没有收到这100块
    

    8.连接池

    概述:连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用;连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接,把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中,从内存中获取和归还的效率要远远高于创建和销毁的效率

    自定义连接池

    概述:通过实现DataSource接口实现自定义连接池

    代码示例:

    //MyDataSource.java
    
    package cn.luyi.demo1;
    
    /*
     * 自定义连接池
     */
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.logging.Logger;
    
    import javax.sql.DataSource;
    
    public class MyDataSource implements DataSource {
    	//将一些连接存入到内存中,可以定义一个集合,用于存储连接对象,这个集合就相当于连接池
    	private List<Connection> connList = new ArrayList<Connection>();
    	//在初始化的时候提供一些连接,添加连接到内存中
    	public MyDataSource(){
    		//初始化连接
    		for(int i = 1; i <= 3; i ++){
    			connList.add(JDBCUtils.getConnection());
    		}
    	}
    	//从连接池中获得连接的方法
    	@Override
    	public Connection getConnection() throws SQLException {
    		Connection conn  = connList.remove(0);
    		return conn;
    	}
    	/*
    	 * 将连接归还到连接池的方法
    	 */
    	public void addBack(Connection conn){
    		connList.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 { 
    		return null;
    	}
    
    
    }
    
    //MyDataSourceText.java
    
    package cn.luyi.demo1;
    
    /*
     * 连接池测试类
     */
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class MyDataSourceText {
    	public static void main(String[] args) {
    		Connection conn = null;
    		Statement stmt = null;
    		ResultSet rs = null;
    		MyDataSource mdb = null;
    		
    		try{
    			//加载驱动
    			JDBCUtils.loadDriver();
    			//从连接池获取连接
    			mdb = new MyDataSource();
    			conn = mdb.getConnection();
    			
    			stmt = conn.createStatement();
    			//编写sql语句
    			String sql = "select * from user";
    			//执行sql,获取结果集
    			rs = stmt.executeQuery(sql);
    			while(rs.next()){
    				System.out.print(rs.getString("username")  + " ");
    				System.out.print(rs.getString("password")  + " ");
    				System.out.println(rs.getString("role"));
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			//释放资源
    			if(rs != null){
    				try {
    					rs.close();
    				} catch (SQLException e) {
    					e.printStackTrace();
    				}
    			}
    			rs = null;
    			
    			if(stmt != null){
    				try {
    					stmt.close();
    				} catch (SQLException e) {
    					e.printStackTrace();
    				}
    			}
    			stmt = null;
    			
    			//归还连接到连接池
    			mdb.addBack(conn);
    		}	
    	}
    }
    

    以上是我们对自定义连接池的基本实现,但是我们的代码存在两点不合理的地方:

    • 使用接口的实现类完成构造,而不是接口来完成构造
    • 额外提供了连接归还连接池的方法,也正是因为有这个额外的方法,我们无法用接口来完成构造

    那么,我们怎么样才能不提供额外的方法,还能实现连接归还连接池呢?

    回想之前我们释放Connection时调用的是close方法,那我们是否有办法把close完成的释放资源的功能改为归还连接的功能呢?

    答案是肯定的,把原有的close方法的逻辑改为归还连接,也就是增强一个类中的方法,我们有三种方法:

    1.采用继承的方式,继承这种方法最简单,但是是有使用条件的:必须能够控制这个类的构造,也就是可以new这个类创建对象

    2.采用装饰者模式:使用条件

    • 增强的类和被增强的类实现相同的接口
    • 在增强的类中获得被增强的类的引用

    3.采用动态代理模式

    采用装饰者模式实现把原有的close方法的逻辑改为归还连接代码示例:

    	//ConnectionImpl.java
    	
    	package cn.luyi.demo1;
    	
    	/*
    	 * 模板类
    	 */
    	import java.sql.Array;
    	import java.sql.Blob;
    	import java.sql.CallableStatement;
    	import java.sql.Clob;
    	import java.sql.Connection;
    	import java.sql.DatabaseMetaData;
    	import java.sql.NClob;
    	import java.sql.PreparedStatement;
    	import java.sql.SQLClientInfoException;
    	import java.sql.SQLException;
    	import java.sql.SQLWarning;
    	import java.sql.SQLXML;
    	import java.sql.Savepoint;
    	import java.sql.Statement;
    	import java.sql.Struct;
    	import java.util.Map;
    	import java.util.Properties;
    	import java.util.concurrent.Executor;
    	
    	public class ConnectionImpl implements Connection{
    	
    		private Connection conn;
    		public ConnectionImpl(Connection conn){
    			super();
    			this.conn = conn;
    		}
    		@Override
    		public <T> T unwrap(Class<T> iface) throws SQLException {
    			return conn.unwrap(iface);
    		}
    	
    		@Override
    		public boolean isWrapperFor(Class<?> iface) throws SQLException {
    			return conn.isWrapperFor(iface);
    		}
    	
    		@Override
    		public Statement createStatement() throws SQLException {
    			return conn.createStatement();
    		}
    	
    		@Override
    		public PreparedStatement prepareStatement(String sql) throws SQLException {
    			return conn.prepareStatement(sql);
    		}
    	
    		@Override
    		public CallableStatement prepareCall(String sql) throws SQLException {
    			return conn.prepareCall(sql);
    		}
    	
    		@Override
    		public String nativeSQL(String sql) throws SQLException {
    			return nativeSQL(sql);
    		}
    	
    		@Override
    		public void setAutoCommit(boolean autoCommit) throws SQLException {
    			
    		}
    	
    		@Override
    		public boolean getAutoCommit() throws SQLException {
    			return conn.getAutoCommit();
    		}
    	
    		@Override
    		public void commit() throws SQLException {
    			conn.commit();
    		}
    	
    		@Override
    		public void rollback() throws SQLException {
    			conn.rollback();
    		}
    	
    		@Override
    		public void close() throws SQLException {
    			conn.close();
    		}
    	
    		@Override
    		public boolean isClosed() throws SQLException {
    			return conn.isClosed();
    		}
    	
    		@Override
    		public DatabaseMetaData getMetaData() throws SQLException {
    			return conn.getMetaData();
    		}
    	
    		@Override
    		public void setReadOnly(boolean readOnly) throws SQLException {
    			conn.setReadOnly(readOnly);
    		}
    	
    		@Override
    		public boolean isReadOnly() throws SQLException {
    			return conn.isReadOnly();
    		}
    	
    		@Override
    		public void setCatalog(String catalog) throws SQLException {
    			conn.setCatalog(catalog);
    		}
    	
    		@Override
    		public String getCatalog() throws SQLException {
    			return conn.getCatalog();
    		}
    	
    		@Override
    		public void setTransactionIsolation(int level) throws SQLException {
    			conn.setTransactionIsolation(level);
    		}
    	
    		@Override
    		public int getTransactionIsolation() throws SQLException {
    			return conn.getTransactionIsolation();
    		}
    	
    		@Override
    		public SQLWarning getWarnings() throws SQLException {
    			return conn.getWarnings();
    		}
    	
    		@Override
    		public void clearWarnings() throws SQLException {
    			conn.clearWarnings();
    		}
    	
    		@Override
    		public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
    			return conn.createStatement();
    		}
    	
    		@Override
    		public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
    				throws SQLException {
    			return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
    		}
    	
    		@Override
    		public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
    			return conn.prepareCall(sql, resultSetType, resultSetConcurrency);
    		}
    	
    		@Override
    		public Map<String, Class<?>> getTypeMap() throws SQLException {
    			return conn.getTypeMap();
    		}
    	
    		@Override
    		public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
    			conn.setTypeMap(map);
    		}
    	
    		@Override
    		public void setHoldability(int holdability) throws SQLException {
    			conn.setHoldability(holdability);
    		}
    	
    		@Override
    		public int getHoldability() throws SQLException {
    			return conn.getHoldability();
    		}
    	
    		@Override
    		public Savepoint setSavepoint() throws SQLException {
    			return conn.setSavepoint();
    		}
    	
    		@Override
    		public Savepoint setSavepoint(String name) throws SQLException {
    			return conn.setSavepoint();
    		}
    	
    		@Override
    		public void rollback(Savepoint savepoint) throws SQLException {
    			conn.rollback();
    		}
    	
    		@Override
    		public void releaseSavepoint(Savepoint savepoint) throws SQLException {
    			conn.releaseSavepoint(savepoint);
    		}
    	
    		@Override
    		public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
    				throws SQLException {
    			return conn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
    		}
    	
    		@Override
    		public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
    				int resultSetHoldability) throws SQLException {
    			return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
    		}
    	
    		@Override
    		public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
    				int resultSetHoldability) throws SQLException {
    			return conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    		}
    	
    		@Override
    		public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
    			return conn.prepareStatement(sql, autoGeneratedKeys);
    		}
    	
    		@Override
    		public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
    			return conn.prepareStatement(sql, columnIndexes);
    		}
    	
    		@Override
    		public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
    			return conn.prepareStatement(sql, columnNames);
    		}
    	
    		@Override
    		public Clob createClob() throws SQLException {
    			return conn.createClob();
    		}
    	
    		@Override
    		public Blob createBlob() throws SQLException {
    			return conn.createBlob();
    		}
    	
    		@Override
    		public NClob createNClob() throws SQLException {
    			return conn.createNClob();
    		}
    	
    		@Override
    		public SQLXML createSQLXML() throws SQLException {
    			return conn.createSQLXML();
    		}
    	
    		@Override
    		public boolean isValid(int timeout) throws SQLException {
    			return conn.isValid(timeout);
    		}
    	
    		@Override
    		public void setClientInfo(String name, String value) throws SQLClientInfoException {
    			conn.setClientInfo(name, value);
    		}
    	
    		@Override
    		public void setClientInfo(Properties properties) throws SQLClientInfoException {
    			conn.setClientInfo(properties);
    		}
    	
    		@Override
    		public String getClientInfo(String name) throws SQLException {
    			return getClientInfo(name);
    		}
    	
    		@Override
    		public Properties getClientInfo() throws SQLException {
    			return conn.getClientInfo();
    		}
    	
    		@Override
    		public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
    			return conn.createArrayOf(typeName, elements);
    		}
    	
    		@Override
    		public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
    			return conn.createStruct(typeName, attributes);
    		}
    	
    		@Override
    		public void setSchema(String schema) throws SQLException {
    			conn.setSchema(schema);
    		}
    	
    		@Override
    		public String getSchema() throws SQLException {
    			return conn.getSchema();
    		}
    	
    		@Override
    		public void abort(Executor executor) throws SQLException {
    			conn.abort(executor);
    		}
    	
    		@Override
    		public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
    			conn.setNetworkTimeout(executor, milliseconds);
    		}
    	
    		@Override
    		public int getNetworkTimeout() throws SQLException {
    			return conn.getNetworkTimeout();
    		}
    		
    	}
    	
    	//MyConnectionWrapper.java
    	package cn.luyi.demo1;
    	
    	/*
    	 * 装饰者模式增强类中的close方法
    	 */
    	import java.sql.Connection;
    	import java.sql.SQLException;
    	import java.util.List;
    	
    	public class MyConnectionWrapper extends ConnectionImpl {
    		private Connection conn;
    		private List<Connection> connList;
    		
    		public MyConnectionWrapper(Connection conn, List<Connection> connList) {
    			super(conn);
    			this.conn = conn;
    			this.connList = connList;
    		}
    		//增强了close方法:将释放资源功能改为归还连接功能
    		@Override
    		public void close() throws SQLException {
    			connList.add(conn); 
    		}
    	}
    	
    	//MyDataSource.java
    	
    	package cn.luyi.demo1;
    	
    	/*
    	 * 自定义连接池
    	 */
    	import java.io.PrintWriter;
    	import java.sql.Connection;
    	import java.sql.SQLException;
    	import java.sql.SQLFeatureNotSupportedException;
    	import java.util.ArrayList;
    	import java.util.List;
    	import java.util.logging.Logger;
    	
    	import javax.sql.DataSource;
    	
    	public class MyDataSource implements DataSource {
    		//将一些连接存入到内存中,可以定义一个集合,用于存储连接对象,这个集合就相当于连接池
    		private List<Connection> connList = new ArrayList<Connection>();
    		//在初始化的时候提供一些连接,添加连接到内存中
    		public MyDataSource(){
    			//初始化连接
    			for(int i = 1; i <= 3; i ++){
    				connList.add(JDBCUtils.getConnection());
    			}
    		}
    		//从连接池中获得连接的方法
    		@Override
    		public Connection getConnection() throws SQLException {
    			Connection conn  = connList.remove(0);
    			//使用装饰者模式把close重写为归还连接的方法
    			MyConnectionWrapper connWrapper = new MyConnectionWrapper(conn, connList);
    			return connWrapper;
    		}
    		
    		
    		@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 { 
    			return null;
    		}
    	
    	
    	}
    	
    	
    	
    	//MyDataSourceText.java
    	
    	package cn.luyi.demo1;
    	
    	/*
    	 * 连接池测试类
    	 */
    	import java.sql.Connection;
    	import java.sql.ResultSet;
    	import java.sql.SQLException;
    	import java.sql.Statement;
    	
    	import javax.sql.DataSource;
    	
    	public class MyDataSourceText {
    		public static void main(String[] args) {
    			Connection conn = null;
    			Statement stmt = null;
    			ResultSet rs = null;
    			DataSource mdb = null;
    			
    			try{
    				//加载驱动
    				JDBCUtils.loadDriver();
    				//从连接池获取连接
    				mdb = new MyDataSource();
    				conn = mdb.getConnection();
    				
    				stmt = conn.createStatement();
    				//编写sql语句
    				String sql = "select * from user";
    				//执行sql,获取结果集
    				rs = stmt.executeQuery(sql);
    				while(rs.next()){
    					System.out.print(rs.getString("username")  + " ");
    					System.out.print(rs.getString("password")  + " ");
    					System.out.println(rs.getString("role"));
    				}
    			}catch(Exception e){
    				e.printStackTrace();
    			}finally{
    				//释放资源
    				JDBCUtils.release(rs, stmt, conn);
    				
    			}	
    		}
    	}
    

    开源连接池Druid

    Druid概述:Druid是阿里旗下的开源连接池产品,使用非常简单,可以与spring框架进行快速整合,在很多的开源网站上都可以找到它,下载它的jar包就可以使用了

    Druid的使用(两种方式):

    //db.properties
    
    driverClassName = com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/my?serverTimezone=UTC&rewriteBatchedStatements=true
    username=root
    password=123
    
    //DruidDemo.java
    
    import java.io.FileInputStream;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.Statement;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    public class DruidDemo {
    	public static void main(String[] args) {
    		Connection conn = null;
    		Statement stmt = null;
    		ResultSet rs = null;
    		try{
    			//加载驱动
    			JDBCUtils.loadDriver();
    			
    			//使用Druid连接池获取Connection对象
    			DruidDataSource dataSource = new DruidDataSource();
    			/*//方式一:手动设置数据库连接的参数
    			dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    			dataSource.setUrl("jdbc:mysql://localhost:3306/my?serverTimezone=UTC");
    			dataSource.setUsername("root");
    			dataSource.setPassword("123");
    			conn = dataSource.getConnection();*/
    			//方式二:从配置文件里读取
    			Properties properties = new Properties();
    			properties.load(new FileInputStream("src/db.properties"));
    			DataSource datasource = DruidDataSourceFactory.createDataSource(properties);
    			conn = datasource.getConnection();
    			
    			stmt = conn.createStatement();
    			//编写sql语句
    			String sql = "select * from user";
    			//执行sql,获取结果集
    			rs = stmt.executeQuery(sql);
    			while(rs.next()){
    				System.out.print(rs.getString("username")  + " ");
    				System.out.print(rs.getString("password")  + " ");
    				System.out.println(rs.getString("role"));
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			//释放资源
    			JDBCUtils.release(rs, stmt, conn);
    			
    		}
    

    开源连接池C3P0

    概述:C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等,下载它的jar包就可以使用了,需要注意的是C3P0版本0.9.1之前是只有一个包的,而版本0.9.2之后就被分离了一个mchange-commons-java包出来,所以使用版本0.9.2以上就需要导入两个包

    C3P0的连接池使用:

    //c3p0-config.xml
    
    <?xml version="1.0" encoding="utf-8"?>
    <c3p0-config>
    	<!-- 默认配置 -->
      <default-config>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/my?serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123</property>
        
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">10</property>
        <property name="checkoutTimeout">3000</property>
      </default-config>
    
    	<!-- 设置其他的配置 -->
      <named-config name="otherc3p0"> 
      </named-config>
    </c3p0-config>
    
    //C3P0Demo.java
    
    package cn.luyi.demo1;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.Statement;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class C3P0Demo {
    	public static void main(String[] args) {
    		Connection conn = null;
    		Statement stmt = null;
    		ResultSet rs = null;
    		try{
    			//加载驱动
    			JDBCUtils.loadDriver();
    			
    			//使用C3P0连接池获取Connection对象
    			/*//方式一:手动设置参数
    			ComboPooledDataSource dataSource = new ComboPooledDataSource();
    			dataSource.setDriverClass("com.mysql.jdbc.Driver");
    			dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/my?serverTimezone=UTC");
    			dataSource.setUser("root");
    			dataSource.setPassword("123");
    			conn = dataSource.getConnection();*/
    			
    			//方式二:使用配置文件
    			ComboPooledDataSource dataSource = new ComboPooledDataSource();
    			conn = dataSource.getConnection();
    			
    			stmt = conn.createStatement();
    			//编写sql语句
    			String sql = "select * from user";
    			//执行sql,获取结果集
    			rs = stmt.executeQuery(sql);
    			while(rs.next()){
    				System.out.print(rs.getString("username")  + " ");
    				System.out.print(rs.getString("password")  + " ");
    				System.out.println(rs.getString("role"));
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			//释放资源
    			JDBCUtils.release(rs, stmt, conn);
    			
    		}
    	}
    }
    

    连接池版的工具类

    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    import javax.sql.DataSource;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class JDBCUtils2 {
    	//创建一个连接池,但是这个连接池只需要创建一次即可
    	private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();
    	
    	/*
    	 * 获得连接的方法
    	 */
    	public static Connection getConnection() throws SQLException{
    		return dataSource.getConnection();
    	}
    	/*
    	 * 获得连接池
    	 */
    	public static DataSource getDataSource(){
    		return dataSource;
    	}
    	/*
    	 * 释放资源的方法
    	 */
    	public static void release(Statement stmt, Connection conn){
    		if(stmt != null){
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			stmt = null;
    		}
    		
    		if(conn != null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			conn = null;
    		}
    	}
    	public static void release(ResultSet rs, Statement stmt, Connection conn){
    		if(stmt != null){
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			stmt = null;
    		}
    		
    		if(conn != null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			conn = null;
    		}
    		if(rs != null){
    			try {
    				rs.close();
    			} catch (SQLException e1) {
    				e1.printStackTrace();
    			}
    			rs = null;
    		}		
    	}
    }
    

    9.DBUtils

    概述:Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能,下载它的jar包即可使用

    为什么要学习DBUtils?

    因为JDBC手写比较麻烦,而且有非常多的代码是类似的,比如获得连接,预编译SQL,释放资源登,那么可以将这些代码抽取出来放到工具类中,大大简化JDBC的编程

    DBUtils核心类QueryRunner

    有两套常用方法:

    1.在一般情况下如果执行CRUD的操作:

    • QueryRunner(DataSource ds):带参构造方法
    • int update(String sql, Object...args):增删改操作
    • T query(String sql, ResultSetHandler rsh, Object...args):查询操作

    2.如果有事务管理的话则用这一套:

    • QuerryRunner():无参构造方法
    • int update(Connection conn, String sql, Object...args):增删改操作
    • T query(Connection conn, String sql, ResultSetHandler rsh, Object...args):查询操作

    批量处理方法:

    • int[] batch(Connection conn, String sql, Object[][] params)
    • int[] batch(String sql, Object[][] params)

    DbUtils类

    主要方法:

    • static void commitAndCloseQuietly(Connection conn):提交事务,会把异常也处理了
    • static void rollbackAndCloseQuiety(Connection conn):回滚事务,会把异常也处理了

    代码示例1(DBUtils的增删改):

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import org.apache.commons.dbutils.QueryRunner;
    
    public class DBUtilsDemo {
    	public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		try{
    			//insert();
    			//delete();
    			update();
    			
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			JDBCUtils2.release(rs, pstmt, conn);
    		}
    	}
    	/*
    	 *添加数据操作
    	 */
    	public static void insert() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		queryRunner.update("insert into user value(null, ?, ?, ?)", "黄伊", "123", "student");
    	}
    	/*
    	 * 删除操作
    	 */
    	public static void delete() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		queryRunner.update("delete from user where username = ?", "luer");
    	}
    	/*
    	 * 修改操作
    	 */
    	public static void update() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		queryRunner.update("update user set password = ? where username = ?", "520", "黄伊");
    	}
    }
    

    代码示例2(DBUtils的查询操作):

    //User.java
    package cn.luyi.demo1;
    
    public class User {
    	private String username;
    	private String password;
    	private String role;
    	public String getUsername() {
    		return username;
    	}
    	public void setUsername(String username) {
    		this.username = username;
    	}
    	public String getPassword() {
    		return password;
    	}
    	public void setPassword(String password) {
    		this.password = password;
    	}
    	public String getRole() {
    		return role;
    	}
    	public void setRole(String role) {
    		this.role = role;
    	}
    	@Override
    	public String toString() {
    		return "User [username=" + username + ", password=" + password + ", role=" + role + "]";
    	}
    	
    }
    
    // DBUtilsDemo2.java
    package cn.luyi.demo1;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.ResultSetHandler;
    
    
    public class DBUtilsDemo2 {
    	public static void main(String[] args) throws SQLException {
    		//querySingle();
    		queryMany();
    	}
    	
    	/*
    	 * 单条查询
    	 */
    	public static void querySingle() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		User user = queryRunner.query("select * from user where username = ?", new ResultSetHandler<User>(){
    
    			@Override
    			public User handle(ResultSet rs) throws SQLException {
    				User user = new User();
    				if(rs.next()){
    					user.setUsername(rs.getString("username"));
    					user.setPassword(rs.getString("password"));
    					user.setRole(rs.getString("role"));
    				}
    				return user;
    			}
    			
    		}, "lusan");
    		
    		System.out.println(user);
    	}
    	/*
    	 * 多条查询
    	 */
    	public static void queryMany() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		//用一个集合来装User对象
    		List<User> list = queryRunner.query("select * from user", new ResultSetHandler<List<User>>(){
    			
    			@Override
    			public List<User> handle(ResultSet rs) throws SQLException {
    				//创建一个集合用于封装数据
    				List<User> list = new ArrayList<User>();
    				while(rs.next()){
    					User user = new User();
    					user.setUsername(rs.getString("username"));
    					user.setPassword(rs.getString("password"));
    					user.setRole(rs.getString("role"));
    					list.add(user);
    				}
    				return list;
    			}
    		});
    		for(User user : list){
    			System.out.println(user);
    		}
    		
    		}
    }
    

    ResultHandler的实例

    前面我们通过使用DBUtils对数据库进行增删改查,可以看出确实省了很多代码,但是我们在使用查询时,仍然需要一个个手动封装数据到对象中,有没有什么办法可以帮我们封装好数据呢?答案是有的,ResultHandler的不同的实现类就帮我们做了这样的事情,接下来我们就来学习ResultHandler的实例吧

    1.ArrayHandler和ArrayListHandler

    • ArrayHandler将一条记录封装到一个数组当中,这个数组就是Object[]
    • ArrayListHandler:将多条数据封装到一个装有Object[]的List集合中

    代码示例:

    import java.sql.SQLException;
    import java.util.Arrays;
    import java.util.List;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.ArrayHandler;
    import org.apache.commons.dbutils.handlers.ArrayListHandler;
    
    public class DBUtilsDemo3 {
    	public static void main(String[] args) throws SQLException {
    		//method1();
    		method2();
    	}
    	/*
    	 * ArrayHandler:将一条记录封装到一个Object数组中
    	 */
    	public static void method1() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		Object[] objs = queryRunner.query("select * from user where id = ?", new ArrayHandler(), 3);
    		System.out.println(Arrays.toString(objs));
    	}
    	/*
    	 * ArrayListHandler:将多条数据封装到一个装有Object数组的list集合中
    	 */
    	public static void method2() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		List<Object[]> list = queryRunner.query("select * from user", new ArrayListHandler());
    		
    		for(Object[] obj:list){
    			System.out.println(Arrays.toString(obj));
    		}
    	}
    }
    

    2.BeanHandler和BeanListHandler

    • BeanHandler:将一条记录封装到一个javaBean中
    • BeanListHandler:将多条记录封装到一个装有javaBean的list集合中

    代码示例:

    //User.java
    
    public class User {
    	private String username;
    	private String password;
    	private String role;
    	public String getUsername() {
    		return username;
    	}
    	public void setUsername(String username) {
    		this.username = username;
    	}
    	public String getPassword() {
    		return password;
    	}
    	public void setPassword(String password) {
    		this.password = password;
    	}
    	public String getRole() {
    		return role;
    	}
    	public void setRole(String role) {
    		this.role = role;
    	}
    	@Override
    	public String toString() {
    		return "User [username=" + username + ", password=" + password + ", role=" + role + "]";
    	}
    	
    }
    
    
    //DBUtilsDemo4.java
    
    import java.sql.SQLException;
    import java.util.List;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    import org.apache.commons.dbutils.handlers.BeanListHandler;
    
    public class DBUtilsDemo4 {
    	public static void main(String[] args) throws SQLException {
    		//method1();
    		method2();
    	}
    	/*
    	 * BeanHandler的使用
    	 */
    	public static void method1() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		User user = queryRunner.query("select * from user where id = ?", new BeanHandler<User>(User.class), 3);
    		
    		System.out.println(user);
    	}
    	
    	/*
    	 * BeanListHandler的使用
    	 */
    	public static void method2() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		List<User> list = queryRunner.query("select * from user", new BeanListHandler<User>(User.class));
    		for(User user:list){
    			System.out.println(user);
    		}
    	}
    }
    

    3.MapHandler和MapListHandler

    • MapHandler:将一条记录封装到一个集合中,Map的key是列名,Map的value就是表中列的记录
    • MapListHandler:将多条记录封装到一个装有Map的List集合中

    代码示例:

    import java.sql.SQLException;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.MapHandler;
    import org.apache.commons.dbutils.handlers.MapListHandler;
    
    public class DBUtilsDemo5 {
    	public static void main(String[] args) throws SQLException {
    		//method1();
    		method2();
    	}
    	/*
    	 * MapHandler的使用
    	 */
    	public static void method1() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		Map<String, Object> map = queryRunner.query("select * from user where id = ?", new MapHandler(), 3);
    		
    		System.out.println(map);
    	}
    	/*
    	 * MapListHandler的使用
    	 */
    	public static void method2() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		List<Map<String, Object>> list = queryRunner.query("select * from user", new MapListHandler());
    		
    		for(Map<String, Object> map:list){
    			System.out.println(map);
    		}
    	}
    }
    

    4.ColumnListHandler、ScalarHandler、KeyedHandler(了解)

    • ColumnListHandler:将数据中的某列封装到List集合中
    • ScalarHandler:将单个值进行封装
    • KeyedHandler:将一条记录封装到一个Map集合中,将多条记录封装到一个装有Map集合的Map集合中去,而且外面的Map的key是可以指定的

    代码示例:

    import java.sql.SQLException;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.ColumnListHandler;
    import org.apache.commons.dbutils.handlers.KeyedHandler;
    import org.apache.commons.dbutils.handlers.ScalarHandler;
    
    public class DBUtilsDemo6 {
    	public static void main(String[] args) throws SQLException {
    		//method1();
    		//method2();
    		method3();
    	}
    
    	/*
    	 * ColumnListHandler的使用
    	 */
    	public static void method1() throws SQLException {
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		List<Object> list = queryRunner.query("select username, password  from user", new ColumnListHandler<Object>("username"));
    		for (Object obj : list) {
    			System.out.println(obj);
    		}
    	}
    	
    	/*
    	 * ScalarHandler的使用
    	 */
    	public static void method2() throws SQLException {
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		Object obj = queryRunner.query("select count(*) from user", new ScalarHandler<Object>());
    		System.out.println(obj);
    	}
    	/*
    	 * KeyedHandler的使用
    	 */
    	public static void method3() throws SQLException {
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		//指定键为username
    		Map<Object, Map<String, Object>> map = queryRunner.query("select * from user", new KeyedHandler<Object>("username"));
    		for(Object key: map.keySet()){
    			System.out.println(key + "---" + map.get(key));
    		}
    	}
    }
  • 相关阅读:
    生成指定范围的随机数
    sql
    map的使用
    基础03 JVM到底在哪里?
    Elasticsearch6.1.0 TransportClient聚合查询索引中所有数据
    Elasticsearch6.1.0 TransportClient滚动查询索引中所有数据写入文件中
    elasticsearch-java api中get() 和execute().actionGet()方法
    Elasticsearch6(Transport Client)常用操作
    Reflections反射获取注解下类
    Ambari2.6.0 安装HDP2.6.3: Python script has been killed due to timeout after waiting 300 secs
  • 原文地址:https://www.cnblogs.com/luyi001/p/13420171.html
Copyright © 2020-2023  润新知