JDBC基础自查手册

本文最后更新于:2020年12月30日 下午

一、概述

远古技术了,没人带就一点一点来吧

二、入门

  • 创建数据库和表

    create database web_test3;
    use web_test3;
    create table user(
    	id int primary key auto_increment,
    	username varchar(20),
    	password varchar(20),
    	nickname varchar(20),
    	age int
    );
    insert into user values (null,'aaa','123','xiaoli',34);
    insert into user values (null,'bbb','123','dawang',32);
    insert into user values (null,'ccc','123','xiaoming',28);
    insert into user values (null,'ddd','123','dahuang',21);
  • 创建项目,引入mysql-connector-java的jar包,如将mysql-connector-java-5.0.8-bin.jar放在lib文件夹下

  • 开发JDBC

    • 加载驱动
    • 获得连接
    • 基本操作
    • 释放资源
    import org.junit.Test;
    
    /**
     * JDBC的入门程序
     * @author jt
     *
     */
    public class JDBCDemo1 {
    
    	@Test
    	public void demo1() throws Exception{
    		// 1.加载驱动
    		Class.forName("com.mysql.jdbc.Driver");
            
    		// 2.获得JDBC连接(url,username,pwd)
    		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web_test3", "root", "abc");
            
    		// 3.基本操作:执行SQL
    		// 3.1获得执行SQL语句的对象
    		Statement statement = conn.createStatement();
    		// 3.2编写SQL语句:
    		String sql = "select * from user";
    		// 3.3执行SQL:
    		ResultSet rs = statement.executeQuery(sql);
    		// 3.4遍历结果集:
    		while(rs.next()){
    			System.out.print(rs.getInt("id")+" ");
    			System.out.print(rs.getString("username")+" ");
    			System.out.print(rs.getString("password")+" ");
    			System.out.print(rs.getString("nickname")+" ");
    			System.out.print(rs.getInt("age"));
    			System.out.println();
    		}
            
    		// 4.释放资源
    		rs.close();
    		statement.close();
    		conn.close();
    	}
    }

三、常用API

1)DriverManager 驱动管理类

  • 注册驱动

    • DriverManager.registerDriver(new Driver());一般不会用,更推荐Class.forName(“com.mysql.jdbc.Driver”);
  • 获得连接:

    • DriverManager.getConnection(String url, String user, String password)

      • “jdbc:mysql://localhost:3306/web_test3”, “root”, “abc”
      • jdbc:连接数据库的协议,mysql:是jdbc的子协议
      • localhost:连接的MySQL数据库服务器的主机地址。(连接是本机就可以写成localhost),如果连接不是本机的,就需要写上连接主机的IP地址。
      • 3306:MySQL数据库服务器的端口号,web_test3:数据库名称
    • url如果连接的是本机的路径,可以简化为:jdbc:mysql:///web_test3

2)Connection 与数据库连接对象

  • 创建执行SQL语句的对象
    • Connection.createStatement():创建一个Statement对象,来执行SQL
    • Connection.prepareCall(String sql):创建一个CallableStatement对象,来执行数据库中存储过程
    • Connection.PreparedStatement:创建一个PreparedStatement对象,来讲参数化的SQL语句发送到数据库。对SQL进行预处理,解决SQL注入漏洞
  • 管理事务
    • setAutoCommit:设置自动提交
    • commit():提交
    • rollback():回滚,取消在当前事务中进行的所有更改

3)Statement

  • 执行SQL语句:
    • statement.execute(String sql):执行增删改查等SQL语句
    • statement.executeQuery(String sql):执行查询(select语句)
    • statement.executeUpate(String sql):执行修改,添加,删除的SQL语句
  • 执行批处理:
    • statement.addBatch(String sql):将给定的SQL命令添加到此statement对象的当前命令列表中
    • statement.clearBatch():清空此statement对象的当前SQL命令列表
    • statement.executeBatch():将一批SQL命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数 组成的数组

4)ResultSet 结果集

通过select语句得到的查询结果

  • 结果集遍历:ResultSet.next()
  • 结果集获取:ResultSet.getXXX(int columnIndex)或者 ResultSet.getXXX(String columnName)

四、资源释放

JDBC程序执行结束后,将与数据库进行交互的对象释放掉,通常是ConnectionStatementResultSet

这几个对象中尤其是Connection对象是非常稀有的。这个对象一定要做到尽量晚创建,尽早释放掉。

if(rs != null){
    try {
        rs.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }

    rs = null;
}
			
if(statement !=null){
    try {
        statement.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }

    statement = null;
}


if(conn != null){
    try {
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }

    conn = null;
}

五、JDBC的CRUD操作

Create增加、Retrieve查询、Update更新、Delete删除

  • 通过Statement实现

    @Test
    /**
     * 保存操作的代码实现
     */
    public void demo1(){
        Connection conn = null;
        Statement stmt = null;
        try{
            // 注册驱动:
            Class.forName("com.mysql.jdbc.Driver");
            // 获得连接:
            conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
            // 执行操作:
            // 创建执行SQL语句对象:
            stmt = conn.createStatement();
            // 编写SQL语句:
            String sql = "insert into user values (null,'eee','123','阿黄',21)";
            // 执行SQL语句:
            int num = stmt.executeUpdate(sql);
            if(num > 0){
                System.out.println("保存用户成功!!!");
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            // 资源释放:
            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;
            }
        }
    }
    
    
    
    @Test
    /**
     * 更新操作(update)的代码实现
     */
    public void demo2(){
        Connection conn = null;
        Statement stmt  = null;
        try{
            // 注册驱动:
            Class.forName("com.mysql.jdbc.Driver");
            // 获得连接
            conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
            // 执行操作:
            // 创建执行SQL语句的对象:
            stmt = conn.createStatement();
            // 编写SQL语句:
            String sql = "update user set password='abc',nickname='旺财' where id = 5";
            // 执行SQL语句:
            int num = stmt.executeUpdate(sql);
            if(num > 0){
                System.out.println("修改用户成功!!!");
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            // 资源释放:
            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;
            }
        }
    }
    
    
    @Test
    /**
     * 查询多条记录
     */
    public void demo4(){
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 获得连接
            conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
            // 执行操作
            // 创建执行SQL语句的对象:
            stmt = conn.createStatement();
            // 编写SQL:
            String sql = "select * from user";
            // 执行SQL:
            rs = stmt.executeQuery(sql);
            // 遍历结果集:
            while(rs.next()){
                System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
            }
        }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;
            }
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
    
    
    @Test
    /**
     * 查询一条记录
     */
    public void demo5(){
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 获得连接
            conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
            // 执行SQL
            // 创建执行SQL语句对象:
            stmt = conn.createStatement();
            // 编写SQL:
            String sql = "select * from user where id = 4";
            rs = stmt.executeQuery(sql);
            // 判断就可以:
            if(rs.next()){
                System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
            }
        }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;
            }
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
    
    
    @Test
    /**
     * 删除操作的代码实现
     */
    public void demo3(){
        Connection conn = null;
        Statement stmt = null;
        try{
            // 注册驱动:
            Class.forName("com.mysql.jdbc.Driver");
            // 获得连接:
            conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
            // 创建执行SQL语句对象:
            stmt = conn.createStatement();
            // 编写SQL:
            String sql = "delete from user where id = 5";
            // 执行SQL:
            int num = stmt.executeUpdate(sql);
            if(num > 0){
                System.out.println("删除用户成功!!!");
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            // 资源释放:
            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;
            }
        }
    }
  • 通过PreparedStatement实现

    @Test
    /**
     * 保存操作
     */
    public void demo1(){
    	Connection conn = null;
    	PreparedStatement pstmt = null;
    	try{
               
    		// 获得连接:
    		conn = JDBCUtils.getConnection();
    		// 编写SQL语句:
    		String sql = "insert into user values (null,?,?,?,?)";
    		// 预编译SQL:
    		pstmt = conn.prepareStatement(sql);
    		// 设置参数:
    		pstmt.setString(1, "eee");
    		pstmt.setString(2, "abc");
    		pstmt.setString(3, "旺财");
    		pstmt.setInt(4, 32);
    		// 执行SQL
    		int num = pstmt.executeUpdate();
    		if(num > 0){
    			System.out.println("保存成功!");
    		}
               
    	}catch(Exception e){
    		e.printStackTrace();
    	}finally{
    		JDBCUtils.release(pstmt, conn);
    	}
    }
      
      
    @Test
    /**
     * 修改操作
     */
    public void demo2(){
    	Connection conn = null;
    	PreparedStatement pstmt = null;
    	try{
    		// 获得连接:
    		conn = JDBCUtils.getConnection();
    		// 编写SQL语句:
    		String sql = "update user set username = ?,password =?,nickname=?,age = ? where id = ?";
    		// 预编译SQL:
    		pstmt = conn.prepareStatement(sql);
    		// 设置参数:
    		pstmt.setString(1, "abc");
    		pstmt.setString(2, "1234");
    		pstmt.setString(3, "旺旺");
    		pstmt.setInt(4, 23);
    		pstmt.setInt(5, 6);
    		// 执行SQL:
    		int num = pstmt.executeUpdate();
    		if(num > 0){
    			System.out.println("修改成功!");
    		}
    	}catch(Exception e){
    		e.printStackTrace();
    	}finally{
    		JDBCUtils.release(pstmt, conn);
    	}
    }
      
      
       @Test
    /**
     * 删除操作
     */
    public void demo3(){
    	Connection conn = null;
    	PreparedStatement pstmt  = null;
    	try{
    		// 获得连接:
    		conn = JDBCUtils.getConnection();
    		// 编写SQL语句:
    		String sql = "delete from user where id = ?";
    		// 预编译SQL
    		pstmt = conn.prepareStatement(sql);
    		// 设置参数:
    		pstmt.setInt(1, 4);
    		// 执行SQL:
    		int num = pstmt.executeUpdate();
    		if(num > 0){
    			System.out.println("删除成功!");
    		}
    	}catch(Exception e){
    		e.printStackTrace();
    	}finally{
    		JDBCUtils.release(pstmt, conn);
    	}
    }
      
    @Test
    /**
     * 查询操作
     */
    public void demo4(){
    	Connection conn = null;
    	PreparedStatement pstmt = null;
    	ResultSet rs = null;
    	try{
    		// 获得连接:
    		conn = JDBCUtils.getConnection();
    		// 编写SQL:
    		String sql = "select * from user";
    		// 预编译SQL:
    		pstmt = conn.prepareStatement(sql);
    		// 设置参数:
    		// 执行SQL:
    		rs = pstmt.executeQuery();
    		// 遍历结果集:
    		while(rs.next()){
    			System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("nickname"));
    		}
    	}catch(Exception e){
    		e.printStackTrace();
    	}finally{
    		JDBCUtils.release(rs, pstmt, conn);
    	}
    }
      
      
      

六、工具类的抽取

因为传统JDBC的开发,注册驱动获得连接释放资源这些代码都是重复编写的。所以可以将重复的代码提取到一个类中来完成。

/**
 * JDBC的工具类
 * @author jt
 *
 */
public class JDBCUtils {
	private static final String driverClassName;
	private static final String url;
	private static final String username;
	private static final String password;
	
	static{
		driverClassName="com.mysql.jdbc.Driver";
		url="jdbc:mysql:///web_test3";
		username="root";
		password="abc";
	}

	/**
	 * 1.注册驱动
	 */
	public static void loadDriver(){
		try {
			Class.forName(driverClassName);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
    
	/**
	 * 2.获得连接
	 */
	public static Connection getConnection(){
		Connection conn = null;
		try{
			// 将驱动一并注册:
			loadDriver();
			// 获得连接
			conn = DriverManager.getConnection(url,username, password);
		}catch(Exception e){
			e.printStackTrace();
		}
		return conn;
	}
	
    
	/**
	 * 3.释放资源
	 */
	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(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;
		}
		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}
}

测试下工具类,具体如下:

@Test
/**
 * 查询操作:使用工具类
 */
public void demo1(){
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    
    try{
        // 获得连接:
        conn = JDBCUtils.getConnection();
        // 创建执行SQL语句的对象:
        stmt = conn.createStatement();
        // 编写SQL:
        String sql = "select * from user";
        // 执行查询:
        rs = stmt.executeQuery(sql);
        // 遍历结果集:
        while(rs.next()){
            System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        // 释放资源:
        JDBCUtils.release(rs, stmt, conn);
    }
}

七、配置文件

属性文件db.properties,其由key=value组成

  • 定义配置文件

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://web_test3
    username=root
    password=abc
  • 在工具类中解析属性文件

    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")
    }

八、SQL注入漏洞

只知道用户名,无需密码即可完成登录。

原因分析:当采取用户名aaa’ or ‘1=1 时,select * from user where username = ‘“+username”’ and password = ‘“+password”’变成了select * from user where username = ‘aaa’ or ‘1=1’and password = ‘“+password”’,有一个or产生了,只要用户名存在即可通过。

解决方法:使用PreparedStatement对象替换掉Statement对象,将SQL预先进行编译,使用作为占位符,之后再传入变量。

public class UserDao {
	
	public boolean login(String username,String password){
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
        
		// 定义一个变量:
		boolean flag = false;
		try{
			// 获得连接:
			conn = JDBCUtils.getConnection();
			// 编写SQL语句:
			String sql = "select * from user where username = ? and password = ?";
			// 预编译SQL
			pstmt = conn.prepareStatement(sql);
			// 设置参数:
			pstmt.setString(1, username);
			pstmt.setString(2, password);
			// 执行SQL语句:
			rs = pstmt.executeQuery();
			if(rs.next()){
				// 说明根据用户名和密码可以查询到这条记录
				flag = true;
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			JDBCUtils.release(rs, pstmt, conn);
		}
		return flag;
	}
}

/**
* SQL 注入漏洞
*/
public class JDBCDemo4{
    public void demo1(){
        UserDao userDao = new UserDao();
        boolean flag = userDao.login("aaa' or '1=1","qweerttyu");
        boolean flag = userDao.login("aaa' -- ","qweerttyu");
        if (flag){
            System.out.println("登陆成功");
        } else{
            System.out.println("登陆失败");
        }
    }
}

九、批处理

批量执行SQL语句

@Test
/**
 * 批处理基本操作
 */
public void demo1(){
	Connection conn = null;
	Statement stmt = null;
	try{
		// 获得连接:
		conn = JDBCUtils.getConnection();
		// 创建执行批处理对象:
		stmt = conn.createStatement();
           
		// 编写一批SQL语句:
		String sql1 = "create database test1";
		String sql2 = "use test1";
		String sql3 = "create table user(id int primary key auto_increment,name varchar(20))";
		String sql4 = "insert into user values (null,'aaa')";
		String sql5 = "insert into user values (null,'bbb')";
		String sql6 = "insert into user values (null,'ccc')";
		String sql7 = "update user set name = 'mmm' where id = 2";
		String sql8 = "delete from user where id = 1";
		// 添加到批处理
		stmt.addBatch(sql1);
		stmt.addBatch(sql2);
		stmt.addBatch(sql3);
		stmt.addBatch(sql4);
		stmt.addBatch(sql5);
		stmt.addBatch(sql6);
		stmt.addBatch(sql7);
		stmt.addBatch(sql8);
		// 执行批处理:
		stmt.executeBatch();
           
	}catch(Exception e){
		e.printStackTrace();
	}finally{
		JDBCUtils.release(stmt, conn);
	}
}



@Test
/**
 * 批量插入记录:
 * * 默认情况下MySQL批处理没有开启的,需要在url后面拼接一个参数即可。
 */
public void demo2(){
	// 记录开始时间:
	long begin = System.currentTimeMillis();
       
	Connection conn = null;
	PreparedStatement pstmt = null;
       
	try{
		// 获得连接:
		conn = JDBCUtils.getConnection();
           
		// 编写SQL语句:
		String sql = "insert into user values (null,?)";
           
		// 预编译SQL:
		pstmt = conn.prepareStatement(sql);
           
		for(int i=1;i<=10000;i++){
               
			pstmt.setString(1, "name"+i);
               
			// 添加到批处理
			pstmt.addBatch();
			// 注意问题:
			// 一段时间执行一次批处理,避免内存溢出
			if(i % 1000 == 0){
				// 执行批处理:
				pstmt.executeBatch();
				// 清空批处理:
				pstmt.clearBatch();
			}
		}
           
	}catch(Exception e){
		e.printStackTrace();
	}finally{
		JDBCUtils.release(pstmt, conn);
	}
       
	long end = System.currentTimeMillis();
	System.out.println((end-begin));
}

十、事务

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

环境准备:

  • 创建数据库和表

    create database web_test4;
    use web_test4;
    create table account(
    	id int primary key auto_increment,
    	name varchar(20),
    	money double
    );
    insert into account values (null,'aaa',10000);
    insert into account values (null,'bbb',10000);
    insert into account values (null,'ccc',10000);
  • 转账案例代码

    @Test
    /**
     * 完成转账的案例
     */
    public void demo1(){
    	Connection conn = null;
    	PreparedStatement pstmt  = null;
    	try{
    		/**
    		 * 完成转账代码:
    		 * * 扣除某个账号的钱
    		 * * 给另外一个账号加钱
    		 */
               
    		// 获得连接:
    		conn = JDBCUtils.getConnection();
               
    		// 编写SQL语句:
    		String sql = "update account set money = money + ? where name = ?";
               
    		// 预编译SQL:
    		pstmt = conn.prepareStatement(sql);
               
    		// 设置参数:
    		// 用aaa账号给bbb账号转1000元
    		pstmt.setDouble(1, -1000);
    		pstmt.setString(2, "aaa");
               
    		// 执行SQL:扣除aaa账号1000元
    		pstmt.executeUpdate();
    		
    		// int i = 1 / 0;
             // 在转账中没有添加事务的管理,出现aaa账号的钱被转丢了,但是bbb账号的钱没有任何变化。需要给转账的功能添加事务的管理。
    		 
    		// 给bbb账号加1000
    		pstmt.setDouble(1, 1000);
    		pstmt.setString(2, "bbb");
    		pstmt.executeUpdate();
               
    	}catch(Exception e){
    		e.printStackTrace();
    	}finally{
    		JDBCUtils.release(pstmt, conn);
    	}
    }
  • 事务管理API

    • setAutoCommit:此连接自动提交事务
    • commit:提交事务
    • rollback:回滚
    @Test
    /**
     * 完成转账的案例
     */
    public void demo1(){
        Connection conn = null;
        PreparedStatement pstmt  = null;
        try{
            /**
             * 完成转账代码:
             * * 扣除某个账号的钱
             * * 给另外一个账号加钱
             */
    
            // 获得连接:
            conn = JDBCUtils.getConnection();
            // 开启事务
            conn.setAutoCommit(false);
    
            // 编写SQL语句:
            String sql = "update account set money = money + ? where name = ?";
    
            // 预编译SQL:
            pstmt = conn.prepareStatement(sql);
    
            // 设置参数:
            // 用aaa账号给bbb账号转1000元
            pstmt.setDouble(1, -1000);
            pstmt.setString(2, "aaa");
            // 执行SQL:扣除aaa账号1000元
            pstmt.executeUpdate();
    
            int i = 1 / 0;
    
            // 给bbb账号加1000
            pstmt.setDouble(1, 1000);
            pstmt.setString(2, "bbb");
            pstmt.executeUpdate();
    
            // 提交事务:
            conn.commit();
    
        }catch(Exception e){
            // 回滚事务,避免钱中途飞了
            try {
                conn.rollback();
    
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }finally{
            JDBCUtils.release(pstmt, conn);
        }
    }

十一、连接池

1)概述

连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。

提升性能:连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。

2)Druid 开源连接池

Druid是阿里旗下的开源连接池产品,使用非常简单,可以与Spring框架进行快速整合。

@Test
/**
 * Druid的使用:
 * * 手动设置参数的方式
 */
public void demo1(){
       
	Connection conn = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;
       
	try{
		// 使用连接池:
		DruidDataSource dataSource = new DruidDataSource();
           
		// 手动设置数据库连接的参数:
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql:///web_test4");
		dataSource.setUsername("root");
		dataSource.setPassword("abc");
           
		// 获得连接:
		// conn = JDBCUtils.getConnection();
		conn = dataSource.getConnection();
           
		// 编写SQL:
		String sql = "select * from account";
           
		// 预编译SQL:
		pstmt = conn.prepareStatement(sql);
           
		// 设置参数:
		// 执行SQL:
		rs = pstmt.executeQuery();
		while(rs.next()){
			System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
		}
	}catch(Exception e){
		e.printStackTrace();
	}finally{
		JDBCUtils.release(rs, pstmt, conn);
	}
}

也可以采用配置文件设置参数,从而使用Druid连接池

	@Test
	/**
	 * Druid的使用:
	 * * 配置方式设置参数
	 * Druid配置方式可以使用属性文件配置的。
	 * * 文件名称没有规定但是属性文件中的key要一定的。
	 */
	public void demo2(){
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try{
			// 使用连接池:
			// 从属性文件中获取:
			Properties properties = new Properties();
			properties.load(new FileInputStream("src/druid.properties"));
			DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
            
			// 获得连接:
//			conn = JDBCUtils.getConnection();
			conn = dataSource.getConnection();
            
			// 编写SQL:
			String sql = "select * from account";
            
			// 预编译SQL:
			pstmt = conn.prepareStatement(sql);
            
			// 设置参数:
			// 执行SQL:
			rs = pstmt.executeQuery();
			while(rs.next()){
				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			JDBCUtils.release(rs, pstmt, conn);
		}
	}

3)C3P0 连接池

C3P0也是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准拓展。目前使用它的开源项目有Hibernate和Spring等。

  • 手动设置参数

    @Test
    	/**
    	 * 手动设置参数的方式:
    	 */
    	public void demo1(){
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		ResultSet rs = null;
    		try{
    			// 获得连接:从连接池中获取:
    			// 创建连接池:
    			ComboPooledDataSource dataSource = new ComboPooledDataSource();
                
    			// 设置连接参数:
    			dataSource.setDriverClass("com.mysql.jdbc.Driver");
    			dataSource.setJdbcUrl("jdbc:mysql:///web_test4");
    			dataSource.setUser("root");
    			dataSource.setPassword("abc");
                
    			// 从连接池中获得连接:
    			conn = dataSource.getConnection();
                
    			// 编写SQL:
    			String sql = "select * from account";
                
    			// 预编译SQL:
    			pstmt = conn.prepareStatement(sql);
                
    			// 执行SQL:
    			rs = pstmt.executeQuery();
                
    			while(rs.next()){
    				System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
                    
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			JDBCUtils.release(rs, pstmt, conn);
    		}
    	}
  • 采用配置文件的方式:

    • 配置连接池

    • 使用连接池

      @Test
      /**
       * 采用配置文件的方式:
       */
      public void demo2(){
             
      	Connection conn = null;
      	PreparedStatement pstmt = null;
      	ResultSet rs = null;
             
      	try{
      		// 获得连接:从连接池中获取:
      		// 创建连接池:创建连接池默认去类路径下查找c3p0-config.xml
      		ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 可以指定名称,找不到时会使用默认的
                 
      		// 从连接池中获得连接:
      		conn = dataSource.getConnection();
                 
      		// 编写SQL:
      		String sql = "select * from account";
                 
      		// 预编译SQL:
      		pstmt = conn.prepareStatement(sql);
                 
      		// 执行SQL:
      		rs = pstmt.executeQuery();
                 
      		while(rs.next()){
      			System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
      		}
                 
      	}catch(Exception e){
      		e.printStackTrace();
      	}finally{
      		JDBCUtils.release(rs, pstmt, conn);
      	}
      }

4)改写工具类

连接池对象应该是一个应用只创建一次就可以的,不需要每次使用均创建一个新的连接池

十二、DBUtils

DBUtils是一个对JDBC进行简单封装的开源工具类库,将重复的代码(获得连接、预编译SQL、释放资源等)抽取出来放到工具类中,简化JDBC的编程。

1)API概述

  • 核心运行类QueryRunner

    • 有参、无参构造均可

    • 常用方法有update、query、batch等

      // 一般情况下执行Crud操作
      // 1.构造
      QueryRunner(DataSource ds);
      // 2.方法
      int update(String sql,Object… args);
      T query(String sql,ResultSetHandler rsh,Object… args);  // 执行SQL语句,返回结果对象
      
      
      // 当有事务管理,且要执行CRUD时,
      // 1.构造(不传入连接池):
      QueryRunner();
      // 2.方法(参数包含连接):
      int update(Connection conn,String sql,Object… args);
      T query(Connection conn,String sql,ResultSetHandler rsh,Object… args);
  • DbUtils类:

    • commitAndCloseQuietly(connection conn):提交连接然后关闭它
    • rollbackAndCloseQuietly(connection conn):回滚连接,然后关闭

2)增删查改示例

	@Test
	/**
	 * 添加、删除、修改操作,具体的操作由对应的sql语句区分,其他都一样
	 */
	public void demo1() throws SQLException{
		// 创建核心类:QueryRunner:
		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
       // 调用update方法
		queryRunner.update("insert into account values (null,?,?)", "ddd",10000);
		queryRunner.update("delete from account where id = ?", 3);
		queryRunner.update("update account set name=?,money=? where id =?", "eee",20000,4);
	}


// 查询
// 1.查询一条记录
@Test
public class demo1 {
    // 创建核心类
    QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    // 执行查询
    Account account = queryRunner.query("select * from account where id = ?", new ResultSetHandler<account>(){
        @Override
        public Account handle(ResultSet rs){
            Account account = new Account();
            if(rs.next()){
                account.setId(rs.getInt("id"));
                account.setName(rs.getString("name"));
                account.setMoney(rs.getDouble("money"));
            }
            return account;
        }
    },1);
    System.out.println(account);
}

// 2.查询多条记录
@Test
public class demo2 {
    // 创建核心类
    QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());

    // 执行查询
    Account account = queryRunner.query("select * from account", new ResultSetHandler<list<account>>(){
        @Override
        public List<Account> handle(ResultSet rs){
            // 创建集合来封装数据
            List<Account> list = new ArrayList<Account>();
            while(rs.next()){
                // 封装数据
                Account account = new Account();

                account.setId(rs.getInt("id"));
                account.setName(rs.getString("name"));
                account.setMoney(rs.getDouble("money"));

                list.add(account);
            }
            return list;
        }
    });

    // 显示
    for(Account account : list){
        System.out.println(account);
    }
}

3)ResultSetHandler实现类

  • ArrayHandler:将一条记录封装到一个Object数组中
  • ArrayListHandler:将多条记录封装到一个装有Object数组的集合中
  • BeanHandler:将一条记录封装到一个JavaBean中
  • BeanListHandler:将多条记录封装到一个装有JavaBean的List集合中

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

  • MapListHandler:将多条记录封装到一个装有Map的List集合中。

  • ColumnListHandler:将数据中的某列封装到List集合中

  • ScalarHandler:封装单个值

  • KeyedHandler:将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的。

    package com.itheima.dbutils.demo3;
    
    import java.sql.SQLException;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.ArrayHandler;
    import org.apache.commons.dbutils.handlers.ArrayListHandler;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    import org.apache.commons.dbutils.handlers.BeanListHandler;
    import org.apache.commons.dbutils.handlers.ColumnListHandler;
    import org.apache.commons.dbutils.handlers.KeyedHandler;
    import org.apache.commons.dbutils.handlers.MapHandler;
    import org.apache.commons.dbutils.handlers.MapListHandler;
    import org.apache.commons.dbutils.handlers.ScalarHandler;
    import org.junit.Test;
    
    import com.itheima.dbutils.domain.Account;
    import com.itheima.jdbc.utils.JDBCUtils2;
    
    /**
     * ResultSetHandler的实现类
     */
    public class DBUtilsDemo3 {
    
    	@Test
    	/**
    	 * ArrayHandler:将一条记录封装到一个Object数组中
    	 */
    	public void demo1() throws SQLException {
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		Object[] objs = queryRunner.query("select * from account where id = ?", new ArrayHandler(), 1);
    		System.out.println(Arrays.toString(objs));
    	}
    
    	@Test
    	/**
    	 * ArrayListHandler:将多条记录封装到一个装有Object数组的List集合中 *
    	 * 一条记录封装到Objecct[]数组中,多条记录就是多个Object[],那么多个Object数组就将其装入List集合中即可。
    	 */
    	public void demo2() throws SQLException {
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		List<Object[]> list = queryRunner.query("select * from account", new ArrayListHandler());
    		for (Object[] objects : list) {
    			System.out.println(Arrays.toString(objects));
    		}
    	}
    
    	@Test
    	/**
    	 * BeanHandler:将一条记录封装到一个JavaBean中
    	 */
    	public void demo3() throws SQLException {
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		Account account = queryRunner.query("select * from account where id = ?",
    				new BeanHandler<Account>(Account.class), 2);
    		System.out.println(account);
    	}
    
    	@Test
    	/**
    	 * BeanListHandler:将多条记录封装到一个装有JavaBean的List集合中。 
    	 * *一条记录就是一个Java的对象(JavaBean),如果多条记录(多个Java的对象),将多个Java对象装到一个List集合中。
    	 */
    	public void demo4() throws SQLException {
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		List<Account> list = queryRunner.query("select * from account ", new BeanListHandler<Account>(Account.class));
    		for (Account account : list) {
    			System.out.println(account);
    		}
    	}
    	
        
        
        
    	@Test
    	/**
    	 * MapHandler:将一条记录封装到一个Map集合中,Map的key是列名,Map的value就是表中列的记录值。
    	 */
    	public void demo5() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		Map<String,Object> map = queryRunner.query("select * from account where id = ?", new MapHandler(), 4);
    		System.out.println(map);
    	}
    	
    	@Test
    	/**
    	 * MapListHandler:将多条记录封装到一个装有Map的List集合中。
    	 */
    	public void demo6() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		List<Map<String,Object>> list = queryRunner.query("select * from account", new MapListHandler());
    		for (Map<String, Object> map : list) {
    			System.out.println(map);
    		}
    	}
        
        
        
    	
    	@Test
    	/**
    	 * ColumnListHandler:将某列的值封装到List集合中
    	 */
    	public void demo7() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		List<Object> list = queryRunner.query("select name,money from account", new ColumnListHandler("name"));
    		for (Object object : list) {
    			System.out.println(object);
    		}
    	}
    	
    	@Test
    	/**
    	 * ScalarHandler:单值封装
    	 */
    	public void demo8() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		Object obj = queryRunner.query("select count(*) from account", new ScalarHandler());
    		System.out.println(obj);
    	}
    	
    	@Test
    	/**
    	 * KeyedHandler:将一条记录封装到一个Map集合中。将多条记录封装到一个装有Map集合的Map集合中。而且外面的Map的key是可以指定的。
    	 */
    	public void demo9() throws SQLException{
    		QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    		Map<Object,Map<String,Object>> map = queryRunner.query("select * from account", new KeyedHandler("id"));
    		for (Object key : map.keySet()) {
    			System.out.println(key+" "+map.get(key));
    		}
    	}
    }

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!