一、JDBC快速入门

1.概念

  • JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系型数据库提供统一访问,它是由一组用Java语言编写的类和接口组成的。

2.本质

  • 其实就是java官方提供的一套规范(接口)。用于帮助开发人员快速实现不同关系型数据库的连接!

4.快速入门

1)导入jia包

Maven

2)注册驱动
// MySQL 5
Class.forName("com.mysql.jdbc.Driver");
// MySQL 8
Class.forName("com.mysql.cj.jdbc.Driver");
3)获取连接
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "root");
4)获取执行者对象
Statement stat = con.createStatement();
5)执行sql语言,并接受返回结果
String sql = "SELECT * FROM user";
ResultSet rs = stat.executeQuery(sql);
6)处理结果
while(rs.next()) {
    System.out.println(rs.getInt("id") + "\t" + rs.getString("name"));
}
7)释放资源
con.close();
stat.close();
rs.close();
public class Test {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");

        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_db", "root", "");

        Statement statement = connection.createStatement();

        String sql = "Select * from account";
        ResultSet resultSet = statement.executeQuery(sql);

        while (resultSet.next()){
            System.out.println(
                    "id:"+resultSet.getInt("id") + "\t"
                    +"name:"+resultSet.getString("name") + "\t"
                    +"money:"+resultSet.getDouble("money")
            );
        }
        connection.close();
        statement.close();
        resultSet.close();
    }
}

二、API介绍

1.DriverManager

  • DriverManager:驱动管理对象

    • 注册驱动(告诉程序该使用哪一个数据库驱动)

      • static void registerDriver(Driver driver):注册与给定的驱动程序 DriverManager
      • 写代码使用:Class.forName("com.mysql.jdbc.Driver");
      • 通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
      static {
      	try {
      		java.sql.DriverManager.registerDriver(new Driver());
      	} catch (SQLException E) {
      		throw new RuntimeException("Can't register driver!");
      	}
      }
      
      • 注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。在jar包中,存在一个java.sql.Driver配置文件,文件中指定了com.mysql.jdbc.Driver
    • 获取数据库连接(获取到数据库的连接并返回连接对象)

      • static Connection getConnection(String url, String user, String password);
        • 返回值:Connection数据库连接对象
        • 参数
          • url:指定连接的路径。语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
          • user:用户名
          • password:密码

2.Connection

  • Connection:数据库连接对象
    • 获取执行者对象
      • 获取普通执行者对象:Statement createStatement();
      • 获取预编译执行者对象:PreparedStatement prepareStatement(String sql);
    • 管理事务
      • 开启事务:setAutoCommit(boolean autoCommit); 参数为false,则开启事务。
      • 提交事务:commit();
      • 回滚事务:rollback();
    • 释放资源
      • 立即将数据库连接对象释放:void close();

3.Statement

  • Statement:执行sql语句的对象
    • 执行DML语句:int executeUpdate(String sql);
      • 返回值int:返回影响的行数。
      • 参数sql:可以执行insert、update、delete语句。
    • 执行DQL语句:ResultSet executeQuery(String sql);
      • 返回值ResultSet:封装查询的结果。
      • 参数sql:可以执行select语句。
    • 释放资源
      • 立即将执行者对象释放:void close();

4.ResultSet

  • ResultSet:结果集对象
    • 判断结果集中是否还有数据:boolean next();
      • 有数据返回true,并将索引向下移动一行
      • 没有数据返回false
    • 获取结果集中的数据:XXX getXxx("列名");
      • XXX代表数据类型(要获取某列数据,这一列的数据类型)
      • 例如:String getString("name"); int getInt("age");
    • 释放资源
      • 立即将结果集对象释放:void close();

三、使用

1.数据准备

1)数据库
-- 创建student表
CREATE TABLE student(
	sid INT PRIMARY KEY AUTO_INCREMENT,	-- 学生id
	NAME VARCHAR(20),					-- 学生姓名
	age INT,							-- 学生年龄
	birthday DATE						-- 学生生日
);

-- 添加数据
INSERT INTO student VALUES (NULL,'张三',23,'1999-09-23'),(NULL,'李四',24,'1998-08-10'),(NULL,'王五',25,'1996-06-06'),(NULL,'赵六',26,'1994-10-20');

2)实体类

package com.itheima02.domain;

import java.util.Date;

public class Student {
    private Integer sid;
    private String name;
    private Integer age;
    private Date birthday;

    public Student() {
    }

    public Student(Integer sid, String name, Integer age, Date birthday) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }
}

2.查询全部

1)持久层
public class StudentDaoImpl implements StudentDao {
    @Override
    public ArrayList<Student> findAll() {
        ArrayList<Student> list = new ArrayList<>();
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            // 注册驱动  可省
            //Class.forName("com.mysql.cj.jdbc.Driver");
            // 获取连接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_db","root","");
            // 获取执行者对象
            statement = connection.createStatement();
            // 执行sql语句 接受返回的结果集
            String sql = "select * from student";
            resultSet = statement.executeQuery(sql);
            // 处理结果集
            while (resultSet.next()) {
                Integer id = resultSet.getInt("sid");
                String name = resultSet.getString("name");
                Integer age = resultSet.getInt("age");
                Date birthday = resultSet.getDate("birthday");

                Student student = new Student(id, name, age, birthday);
                list.add(student);
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放资源
            try {
                if (connection!=null){
                    connection.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

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

            if (resultSet!=null){
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

        }

        return list;
    }
}

2)服务层
public class StudentServiceImpl implements StudentService {
    private StudentDao studentDao = new StudentDaoImpl();
    @Override
    public ArrayList<Student> findAll() {
        ArrayList<Student> all = studentDao.findAll();
        return all;
    }
}
3)视图层
public class StudentController {
    private StudentService studentService = new StudentServiceImpl();

    @Test
    public void findAll(){
        ArrayList<Student> all = studentService.findAll();

        for (Student student : all) {
            System.out.println(student);
        }
    }

}

3. 条件查询

@Override
public Student findById(Integer sid) {
        Student student = null;
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            // 注册驱动  可省
            //Class.forName("com.mysql.cj.jdbc.Driver");
            // 获取连接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_db","root","");
            // 获取执行者对象
            statement = connection.createStatement();
            // 执行sql语句 接受返回的结果集
            String sql = "select * from student where sid ="+sid;
            resultSet = statement.executeQuery(sql);
            // 处理结果集
            while (resultSet.next()) {
                Integer id = resultSet.getInt("sid");
                String name = resultSet.getString("name");
                Integer age = resultSet.getInt("age");
                Date birthday = resultSet.getDate("birthday");

                student = new Student(id, name, age, birthday);

            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放资源
            try {
                if (connection!=null){
                    connection.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

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

            if (resultSet!=null){
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

        }

        return student;
    }

@Override
    public Student findById(Integer sid) {
        Student student = studentDao.findById(sid);
        return student;
    }

 @Test
    public void findById(){
        Student student = studentService.findById(1);
        System.out.println(student);
    }

4.添加信息

 int result = null;
 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String sql = "insert into student values ("
                    + student.getSid()+",'"
                    + student.getNAME()+"',"
                    + student.getAge()+",'"
                    + sdf.format(student.getBirthday())
                    +"')";
            result  = statement.executeUpdate(sql);

tips: 字符串和时间要用小括号包起来

5.修改信息

			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String sql = "update student set name = "+
                    "'"+student.getName()+ "',age=" +
                    student.getAge()+ ",birthday='"
                    +sdf.format(student.getBirthday())+
                    "' where sid ="+student.getSid();
            resultSet  = statement.executeUpdate(sql);

5.删除信息

String sql = "delete from student where sid = 6";
resultSet  = statement.executeUpdate(sql);

四、工具类

1.配置文件

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db14
username=root
password=itheima

2.工具类

package com.codeui.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JDBCUtils {
    // 私有化构造方法
    private JDBCUtils(){};

    // 声明配置信息
    private static String driverClass;
    private static String url;
    private static String username;
    private static String password;
    private static Connection connection;

    // 静态代码块实现加载配置文件和注册驱动
    static {
        try {
            // 通过类加载器返回配置文件的字节流
            InputStream resourceAsStream = JDBCUtils.class.getClassLoader().getResourceAsStream(
                    "config.properties");
            //创建properties集合,加载流对象的信息
            Properties properties = new Properties();
            properties.load(resourceAsStream);
            //获取信息为变量赋值
            driverClass = properties.getProperty("driverClass");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            Class.forName(driverClass);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //获取连接数据库的方法
    public static Connection getConnection(){
        try {
            connection = DriverManager.getConnection(url,username,password);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return connection;
    }

    //释放资源
    public static void close(Connection connection, Statement statement, ResultSet resultSet){
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

    public static void close(Connection connection,Statement statement){
        close(connection,statement,null);
    }
}

3.使用

@Override
    public ArrayList<Student> findAll() {
        ArrayList<Student> list = new ArrayList<>();
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {

            // 获取连接
            connection = JDBCUtils.getConnection();
            // 获取执行者对象
            statement = connection.createStatement();
            // 执行sql语句 接受返回的结果集
            String sql = "select * from student";
            resultSet = statement.executeQuery(sql);
            // 处理结果集
            while (resultSet.next()) {
                Integer id = resultSet.getInt("sid");
                String name = resultSet.getString("name");
                Integer age = resultSet.getInt("age");
                Date birthday = resultSet.getDate("birthday");

                Student student = new Student(id, name, age, birthday);
                list.add(student);
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
           JDBCUtils.close(connection,statement,resultSet);
        }

        return list;
    }

五、SQL注入攻击

1.演示

22091401

我们会发现即使密码错误,我们也可以登录,原因是Statement对象在执行sql语句时,将一部分内容当做查询条件来执行了。

2.PreparedStatement的介绍

  • 预编译sql语句的执行者对象。在执行sql语句之前,将sql语句进行提前编译。
    • 原本 sql语句 password = aaa or 1=1;
    • 现在 sql语句 password = "aaa or 1=1"
    • 会发现"aaa or 1=1"变成了参数而不是语句加参数
  • 为参数赋值的方法:setXxx(参数1,参数2);
    • 参数1:?的位置编号(编号从1开始)
    • 参数2:?的实际参数
  • 执行sql语句的方法
    • 执行insert、update、delete语句:int executeUpdate();
    • 执行select语句:ResultSet executeQuery();
 String sql = "SELECT * FROM user WHERE loginname=? AND password=?";
        pstm = conn.prepareStatement(sql);
        //设置参数
        pstm.setString(1,loginName);
        pstm.setString(2,password);
        //执行sql语句,获取结果集
        rs = pstm.executeQuery();

六、事务管理

事务管理一般在服务层实现

  • 管理事务的功能类 Connection
    • 开启事务:setAutoCommit(boolean autoCommit); 参数为false,则开启事务。
    • 提交事务:commit();
    • 回滚事务:rollback();

七、数据库连接池

1.数据库连接池的概念

  • 数据库连接背景
    • 数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。
  • 数据库连接池
    • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。这项技术能明显提高对数据库操作的性能。

2.自定义连接池

  • DataSource
    • 数据源(数据库连接池),java官方提供的数据库连接池规范接口
    • 核心功能 Connection getConnection();
    // 准备容器,用于保存多个连接对象
    private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>());
    // 定义静态代码块,通过工具类获得10个连接对象
    static {
        for (int i=0; i<10; i++){
            Connection connection = JDBCUtils.getConnection();
            pool.add(connection);
        }
    }
    // 重写getConnection(),用于获得;一个连接对象

    @Override
    public Connection getConnection() throws SQLException {
        if (pool.size()>0){
            Connection remove = pool.remove(0);
            return remove;
        } else {
            throw new RuntimeException("连接数量已用尽");
        }
    }

    public int getSize(){
        return pool.size();
    }
1)1.测试
public class MyDataSourceTest {

    public static void main(String[] args) throws SQLException {
        // 创建连接池对象
        MyDataSource dataSource = new MyDataSource();
        System.out.println("前:"+dataSource.getSize());
        // 通过连接池对象获取连接对象
        Connection connection = dataSource.getConnection();
        // 查询学生的全部信息
        String sql = "select * from student";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();

        while (resultSet.next()) {
            System.out.println(resultSet.getInt("sid"));
        }

        JDBCUtils.close(connection,preparedStatement,resultSet);
        System.out.println("后:"+dataSource.getSize());
    }
}

3.归还连接

  • 归还方法
    • 继承方式
    • 装饰设计模式
    • 适配器设计模式
    • 动态代理模式
1)继承方式(mysql 5)
  • 定义一个类继承JDBC4Connection

  • 定义Connection连接对象和连接容器对象的成员变量

  • 通过有参构造方法完成对变量赋值

  • 重写close方法,将对象添加到池中

/*
    自定义Connection类
 */
public class MyConnection1 extends JDBC4Connection {
    //声明连接对象和连接池集合对象
    private Connection con;
    private List<Connection> pool;

    //通过构造方法给成员变量赋值
    public MyConnection1(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url,Connection con,List<Connection> pool) throws SQLException {
        super(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url);
        this.con = con;
        this.pool = pool;
    }

    //重写close()方法,将连接归还给池中
    @Override
    public void close() throws SQLException {
        pool.add(con);
    }
}


//将之前的连接对象换成自定义的子类对象
private static MyConnection1 con;

//4.获取数据库连接的方法
public static Connection getConnection() {
    try {
        //等效于:MyConnection1 con = new JDBC4Connection();  语法错误!
        con = DriverManager.getConnection(url,username,password);
    } catch (SQLException e) {
        e.printStackTrace();
    }

    return con;
}

我们虽然自定义了一个子类,完成了归还连接的操作。但是DriverManager获取的还是JDBC4Connection这个对象,并不是我们的子类对象。而我们又不能整体去修改驱动包中类的功能!

2)装饰设计模式
  • 思想

    • 定义一个类,实现Connection接口,这样就具备了和JDBC4Connection相同的方法
    • 重写close方法,完成归还连接操作,其余功能还是用Mysql驱动包原有方法
  • 步骤

    • 定义一个类实现Connection接口

    • 定义Connection连接对象和连接容器对象的成员变量

    • 通过有参构造方法完成对变量赋值

    • 重写close方法,将对象添加到池中

    • 剩余方法,只需要调用mysql驱动包的的连接对象完成即可

    • 在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装

    package com.codeui.data;
    
    import com.codeui.utils.JDBCUtils;
    
    import javax.sql.DataSource;
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.logging.Logger;
    
    public class MyDataSource implements DataSource {
    
        // 准备容器,用于保存多个连接对象
        private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>());
        // 定义静态代码块,通过工具类获得10个连接对象
        static {
            for (int i=0; i<10; i++){
                Connection connection = JDBCUtils.getConnection();
                System.out.println("池"+connection);
                pool.add(connection);
            }
        }
        // 重写getConnection(),用于获得;一个连接对象
    
        @Override
        public Connection getConnection() throws SQLException {
            if (pool.size()>0){
                Connection connection = pool.remove(0);
                MyConnection myConnection = new MyConnection(connection,pool);
                return myConnection;
            } else {
                throw new RuntimeException("连接数量已用尽");
            }
        }
    
        public int getSize(){
            return pool.size();
        }
    
        @Override
        public Connection getConnection(String username, String password) throws SQLException {
            return null;
        }
    
        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return null;
        }
    
        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {
    
        }
    
        @Override
        public void setLoginTimeout(int seconds) throws SQLException {
    
        }
    
        @Override
        public int getLoginTimeout() throws SQLException {
            return 0;
        }
    
        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return null;
        }
    
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    }
    
    

    MyConnection

    package com.codeui.data;
    
    import com.mysql.cj.jdbc.ConnectionImpl;
    
    import java.sql.*;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.concurrent.Executor;
    
    /**
     * 定义一个类继承JDBC4Connection
     * 定义Connection连接对象和连接容器对象的成员变量
     * 通过有参构造方法完成对变量赋值
     * 重写close方法,将对象添加到池中
    * */
    public class MyConnection implements Connection {
    
        private Connection con;
        private List<Connection> pool;
    
        public MyConnection(Connection con, List<Connection> pool){
            this.con = con;
            this.pool = pool;
        }
    
        @Override
        public void close() throws SQLException {
            pool.add(con);
        }
    
        @Override
        public Statement createStatement() throws SQLException {
            return con.createStatement();
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return con.prepareStatement(sql);
        }
    
        @Override
        public CallableStatement prepareCall(String sql) throws SQLException {
            return con.prepareCall(sql);
        }
    
        @Override
        public String nativeSQL(String sql) throws SQLException {
            return con.nativeSQL(sql);
        }
    
        @Override
        public void setAutoCommit(boolean autoCommit) throws SQLException {
            con.setAutoCommit(autoCommit);
        }
    
        @Override
        public boolean getAutoCommit() throws SQLException {
            return con.getAutoCommit();
        }
    
        @Override
        public void commit() throws SQLException {
            con.commit();
        }
    
        @Override
        public void rollback() throws SQLException {
            con.rollback();
        }
    
        @Override
        public boolean isClosed() throws SQLException {
            return con.isClosed();
        }
    
        @Override
        public DatabaseMetaData getMetaData() throws SQLException {
            return con.getMetaData();
        }
    
        @Override
        public void setReadOnly(boolean readOnly) throws SQLException {
            con.setReadOnly(readOnly);
        }
    
        @Override
        public boolean isReadOnly() throws SQLException {
            return con.isReadOnly();
        }
    
        @Override
        public void setCatalog(String catalog) throws SQLException {
            con.setCatalog(catalog);
        }
    
        @Override
        public String getCatalog() throws SQLException {
            return con.getCatalog();
        }
    
        @Override
        public void setTransactionIsolation(int level) throws SQLException {
            con.setTransactionIsolation(level);
        }
    
        @Override
        public int getTransactionIsolation() throws SQLException {
            return con.getTransactionIsolation();
        }
    
        @Override
        public SQLWarning getWarnings() throws SQLException {
            return con.getWarnings();
        }
    
        @Override
        public void clearWarnings() throws SQLException {
            con.clearWarnings();
        }
    
        @Override
        public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
            return con.createStatement(resultSetType,resultSetConcurrency);
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
            return con.prepareStatement(sql,resultSetType,resultSetConcurrency);
        }
    
        @Override
        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
            return con.prepareCall(sql,resultSetType,resultSetConcurrency);
        }
    
        @Override
        public Map<String, Class<?>> getTypeMap() throws SQLException {
            return con.getTypeMap();
        }
    
        @Override
        public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
            con.setTypeMap(map);
        }
    
        @Override
        public void setHoldability(int holdability) throws SQLException {
            con.setHoldability(holdability);
        }
    
        @Override
        public int getHoldability() throws SQLException {
            return con.getHoldability();
        }
    
        @Override
        public Savepoint setSavepoint() throws SQLException {
            return con.setSavepoint();
        }
    
        @Override
        public Savepoint setSavepoint(String name) throws SQLException {
            return con.setSavepoint(name);
        }
    
        @Override
        public void rollback(Savepoint savepoint) throws SQLException {
            con.rollback(savepoint);
        }
    
        @Override
        public void releaseSavepoint(Savepoint savepoint) throws SQLException {
            con.releaseSavepoint(savepoint);
        }
    
        @Override
        public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
            return con.createStatement(resultSetType,resultSetConcurrency,resultSetHoldability);
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
            return con.prepareStatement(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
        }
    
        @Override
        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
            return con.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
            return con.prepareStatement(sql,autoGeneratedKeys);
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
            return con.prepareStatement(sql,columnIndexes);
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
            return con.prepareStatement(sql,columnNames);
        }
    
        @Override
        public Clob createClob() throws SQLException {
            return con.createClob();
        }
    
        @Override
        public Blob createBlob() throws SQLException {
            return con.createBlob();
        }
    
        @Override
        public NClob createNClob() throws SQLException {
            return con.createNClob();
        }
    
        @Override
        public SQLXML createSQLXML() throws SQLException {
            return con.createSQLXML();
        }
    
        @Override
        public boolean isValid(int timeout) throws SQLException {
            return con.isValid(timeout);
        }
    
        @Override
        public void setClientInfo(String name, String value) throws SQLClientInfoException {
            con.setClientInfo(name,value);
        }
    
        @Override
        public void setClientInfo(Properties properties) throws SQLClientInfoException {
            con.setClientInfo(properties);
        }
    
        @Override
        public String getClientInfo(String name) throws SQLException {
            return con.getClientInfo(name);
        }
    
        @Override
        public Properties getClientInfo() throws SQLException {
            return con.getClientInfo();
        }
    
        @Override
        public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
            return con.createArrayOf(typeName,elements);
        }
    
        @Override
        public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
            return con.createStruct(typeName,attributes);
        }
    
        @Override
        public void setSchema(String schema) throws SQLException {
            con.setSchema(schema);
        }
    
        @Override
        public String getSchema() throws SQLException {
            return con.getSchema();
        }
    
        @Override
        public void abort(Executor executor) throws SQLException {
            con.abort(executor);
        }
    
        @Override
        public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
            con.setNetworkTimeout(executor,milliseconds);
        }
    
        @Override
        public int getNetworkTimeout() throws SQLException {
            return con.getNetworkTimeout();
        }
    
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return con.unwrap(iface);
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return con.isWrapperFor(iface);
        }
    }
    
3)适配器设计模式
  • 提供一个适配器类,实现Connection接口,将所有功能进行实现(除了close方法)。自定义连接类只需要继承这个适配器类,重写需要改进的close()方法即可!
  • 步骤
    • 定义一个适配器类实现Connection接口
    • 定义Connection连接对象的成员变量
    • 通过有参构造方法完成对成员变量的赋值
    • 重写所有方法(除了close()),调用mysql驱动包的连接对象完成即可
    • 定义一个连接类,继承适配器类
    • 定义Connection连接对象和连接池容器的成员变量,并通过有参构造方法进行赋值
    • 重写close()方法,完成归还链接
    • 在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装。
/*
    适配器抽象类。实现Connection接口。
    实现所有的方法,调用mysql驱动包中Connection连接对象的方法
 */
public abstract class MyAdapter implements Connection {

    // 定义数据库连接对象的变量
    private Connection con;

    // 通过构造方法赋值
    public MyAdapter(Connection con) {
        this.con = con;
    }

    // 所有的方法,均调用mysql的连接对象实现
    @Override
    public Statement createStatement() throws SQLException {
        return con.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return con.prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return con.prepareCall(sql);
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
        return con.nativeSQL(sql);
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        con.setAutoCommit(autoCommit);
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return con.getAutoCommit();
    }

    @Override
    public void commit() throws SQLException {
        con.commit();
    }

    @Override
    public void rollback() throws SQLException {
        con.rollback();
    }

    @Override
    public boolean isClosed() throws SQLException {
        return con.isClosed();
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        return con.getMetaData();
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {
        con.setReadOnly(readOnly);
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        return con.isReadOnly();
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {
        con.setCatalog(catalog);
    }

    @Override
    public String getCatalog() throws SQLException {
        return con.getCatalog();
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        con.setTransactionIsolation(level);
    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return con.getTransactionIsolation();
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return con.getWarnings();
    }

    @Override
    public void clearWarnings() throws SQLException {
        con.clearWarnings();
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return con.createStatement(resultSetType,resultSetConcurrency);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return con.prepareStatement(sql,resultSetType,resultSetConcurrency);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return con.prepareCall(sql,resultSetType,resultSetConcurrency);
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return con.getTypeMap();
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        con.setTypeMap(map);
    }

    @Override
    public void setHoldability(int holdability) throws SQLException {
        con.setHoldability(holdability);
    }

    @Override
    public int getHoldability() throws SQLException {
        return con.getHoldability();
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        return con.setSavepoint();
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        return con.setSavepoint(name);
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {
        con.rollback(savepoint);
    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        con.releaseSavepoint(savepoint);
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return con.createStatement(resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return con.prepareStatement(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return con.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        return con.prepareStatement(sql,autoGeneratedKeys);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        return con.prepareStatement(sql,columnIndexes);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        return con.prepareStatement(sql,columnNames);
    }

    @Override
    public Clob createClob() throws SQLException {
        return con.createClob();
    }

    @Override
    public Blob createBlob() throws SQLException {
        return con.createBlob();
    }

    @Override
    public NClob createNClob() throws SQLException {
        return con.createNClob();
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        return con.createSQLXML();
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
        return con.isValid(timeout);
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        con.setClientInfo(name,value);
    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        con.setClientInfo(properties);
    }

    @Override
    public String getClientInfo(String name) throws SQLException {
        return con.getClientInfo(name);
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        return con.getClientInfo();
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        return con.createArrayOf(typeName,elements);
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        return con.createStruct(typeName,attributes);
    }

    @Override
    public void setSchema(String schema) throws SQLException {
        con.setSchema(schema);
    }

    @Override
    public String getSchema() throws SQLException {
        return con.getSchema();
    }

    @Override
    public void abort(Executor executor) throws SQLException {
        con.abort(executor);
    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        con.setNetworkTimeout(executor,milliseconds);
    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        return con.getNetworkTimeout();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return con.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return con.isWrapperFor(iface);
    }
}
public class MyConnection extends MyAdapter {

    private Connection con;
    private List<Connection> pool;

    public MyConnection(Connection connection,List<Connection> pool){
        super(connection);
        this.con = connection;
        this.pool = pool;
    }

    @Override
    public void close() throws SQLException {
        pool.add(con);
    }
}
4)动态代理
1.快速入门

在不改变目标对象方法的情况下对方法进行增强

  • 组成

    • 被代理对象:真实的对象
    • 代理对象:内存中的一个对象
  • 要求

    • 代理对象和被代理对象要实现相同的接口
  • 实现

    • newProxyInstance(ClassLoader loader,interfaces,InvocationHandler h)
  • 参数

    • ClassLoader loader 和被代理对象使用相同的类加载器
    • Class<?>[] interfaces 接口类型Class数组,和被代理对象使用相同接口
    • InvocationHandler h 代理规则 完成代理增强的功能
public interface StudentInterface {

    void eat(String name);

    void study();
}

public class Student implements StudentInterface{
    @Override
    public void eat(String name) {
        System.out.println(name+"在家吃");
    }

    @Override
    public void study() {
        System.out.println("在家学");
    }
}

public class Test {

    public static void main(String[] args) {
        Student student = new Student();

        StudentInterface proxyInstance = (StudentInterface) Proxy.newProxyInstance(student.getClass().getClassLoader(), new Class[]{StudentInterface.class}, new InvocationHandler() {
            /*
             * 执行Student类中所有方法都会经过invoke方法
             * 对method方法进行判断
             *   如果是study方法,则对其增强
             *   如果不是,就调用原方法即可
             * */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("study")) {
                    System.out.println("来codeui学习");
                    return null;
                } else {
                    return method.invoke(student, args);
                }
            }
        });

        proxyInstance.study();
    }
}
2.动态代理方式归还连接池
  • 步骤
    • 定义一个类实现DataSource接口
    • 定义一个容器,用于保存多个Connection连接对象
    • 定义静态代码块,通过jdbc工具类获取101个对象保存到容器中
    • 重写getConnection方法,从容器中获取一个连接
    • 通过Poxy代理,如果是close()方法,就将连接归还池中,如果是其他方法则调用原有功能
    • 定义getSise方法,用于获取容器的大小并返回
    @Override
    public Connection getConnection() throws SQLException {
        if (pool.size()>0){
            Connection connection = pool.remove(0);
            Connection con = (Connection) Proxy.newProxyInstance(connection.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
                /*
                执行Connection实现类对象所有方法都会经过invoke
                * */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if (method.getName().equals("close")){
                        pool.add(connection);
                        return null;
                    }else {
                        return method.invoke(connection,args);
                    }
                }
            });
            return con;
        } else {
            throw new RuntimeException("连接数量已用尽");
        }
    }

4.了解C3P0连接池

  • 需要导入jar包

    • c3p0-0.9.5.2.jar
    • mchange-commons-java-0.2.12.jar
  • c3p0的配置文件会自动加载,但是必须c3p0-config.xml或c3p0-config.properties

<c3p0-config>
  <!-- 使用默认的配置读取连接池对象 -->
  <default-config>
  	<!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/test_db</property>
    <property name="user">root</property>
    <property name="password">itheima</property>
    
    <!-- 连接池参数 -->
<!--    初始化连接数量-->
    <property name="initialPoolSize">5</property>
<!--    最大连接数量-->
    <property name="maxPoolSize">10</property>
<!--    超时时间-->
    <property name="checkoutTimeout">3000</property>
  </default-config>
 <!--    可以选择默认配置,也可以自己设置-->
  <named-config name="otherc3p0"> 
    <!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/test_db</property>
    <property name="user">root</property>
    <property name="password"></property>
    
    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">8</property>
    <property name="checkoutTimeout">1000</property>
  </named-config>
</c3p0-config>
public class C3P0Test {

    public static void main(String[] args) throws SQLException {
        // 创建c3p0连接池对象
        DataSource dataSource = new ComboPooledDataSource();
        // 通过连接池对象获取连接
        Connection connection = dataSource.getConnection();
        // 获取执行者对象
        Statement statement = connection.createStatement();
        // 执行sql语句 接受返回的结果集
        String sql = "select * from student";
        ResultSet resultSet = statement.executeQuery(sql);
        // 处理结果集
        while (resultSet.next()) {
            Integer id = resultSet.getInt("sid");
            String name = resultSet.getString("name");
            Integer age = resultSet.getInt("age");
            Date birthday = resultSet.getDate("birthday");

            Student student = new Student(id, name, age, birthday);
            System.out.println(student);
        }
        //
    }
}

5.了解Druid连接池

  • 导入jar包
  • 编写配置文件,src目录下
  • 通过Properties集合加载配置文件
  • 通过Druid连接池工厂类获取数据库连接池对象
  • 通过连接池对象获取连接
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test_db
username=root
password=
initialSize=5
maxActive=10
maxWait=3000
public class DruidTest {

        public static void main(String[] args) throws Exception {
            // 获取配置文件流对象,加载配置文件
            InputStream resourceAsStream = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
            Properties properties = new Properties();
            properties.load(resourceAsStream);
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
            // 通过连接池对象获取连接
            Connection connection = dataSource.getConnection();
            // 获取执行者对象
            Statement statement = connection.createStatement();
            // 执行sql语句 接受返回的结果集
            String sql = "select * from student";
            ResultSet resultSet = statement.executeQuery(sql);
            // 处理结果集
            while (resultSet.next()) {
                Integer id = resultSet.getInt("sid");
                String name = resultSet.getString("name");
                Integer age = resultSet.getInt("age");
                Date birthday = resultSet.getDate("birthday");

                Student student = new Student(id, name, age, birthday);
                System.out.println(student);
            }
        }
    
}

6.抽取工具类

/*
    数据库连接池工具类
 */
public class DataSourceUtils {
    //1.私有构造方法
    private DataSourceUtils(){}

    //2.定义DataSource数据源变量
    private static DataSource dataSource;

    //3.提供静态代码块,完成配置文件的加载和获取连接池对象
    static {
        try{
            //加载配置文件
            InputStream is = DruidDemo1.class.getClassLoader().getResourceAsStream("druid.properties");
            Properties prop = new Properties();
            prop.load(is);

            //获取数据库连接池对象
            dataSource = DruidDataSourceFactory.createDataSource(prop);

        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    //4.提供获取数据库连接的方法
    public static Connection getConnection() {
        Connection con = null;
        try {
            con = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    //5.提供获取数据库连接池的方法
    public static DataSource getDataSource() {
        return dataSource;
    }

    //6.提供释放资源的方法
    public static void close(Connection con, Statement stat, ResultSet rs) {
        if(con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

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

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

    public static void close(Connection con, Statement stat) {
        close(con,stat,null);
    }

}

八、JDBCTemplate

  • 目的
    • 抽取出一个JDBC模板类,来封装一些方法(update、query),专门执行增删改查的sql语句,简化使用步骤

1.数据库的源信息

  • DataBaseMetaData(了解):数据库的源信息
    • java.sql.DataBaseMetaData:封装了整个数据库的综合信息
    • 例如:
      • String getDatabaseProductName():获取数据库产品的名称
      • int getDatabaseProductVersion():获取数据库产品的版本号
  • ParameterMetaData:参数的源信息
    • java.sql.ParameterMetaData:封装的是预编译执行者对象中每个参数的类型和属性
    • 这个对象可以通过预编译执行者对象中的getParameterMetaData()方法来获取
    • 核心功能:
      • int getParameterCount():获取sql语句中参数的个数
  • ResultSetMetaData:结果集的源信息
    • java.sql.ResultSetMetaData:封装的是结果集对象中列的类型和属性
    • 这个对象可以通过结果集对象中的getMetaData()方法来获取
    • 核心功能:
      • int getColumnCount():获取列的总数
      • String getColumnName(int i):获取列名

2.update()方法

  • 定义参数变量(数据源,连接对象,执行者对象,结果集对象)

  • 通过有参构造方法给数据源赋值

  • 定义updata方法,参数:sql语句,sql参数

  • 定义int类型变量,用于接受增删改后影响大的行数

  • 通过数据源获取一个数据库连接

  • 通过数据库连接对象获取执行者对象,并对sql语句进行预编译

  • 通过执行者对象获取参数的源信息对象

  • 通过通过参数源信息对象获取参数个数

  • 判断参数数量是否一致

  • 为占位符赋值

  • 执行sql语句并接受结果

  • 释放资源

  • 返回结果

package com.codeui.jdbc;

import com.codeui.utils.DataSourceUtils;

import javax.sql.DataSource;
import java.sql.*;

public class JDBCTemplate {
    //定义参数变量(数据源,连接对象,执行者对象,结果集对象)
    private DataSource dataSource;
    private Connection connection;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;

    //通过有参构造方法给数据源赋值
    public JDBCTemplate(DataSource dataSource){
        this.dataSource = dataSource;
    }
    //定义updata方法,参数:sql语句,sql参数
    public int update(String sql, Object...objects){
        //定义int类型变量,用于接受增删改后影响大的行数
        int result = 0;

        try {
            //通过数据源获取一个数据库连接
            connection = dataSource.getConnection();
            //通过数据库连接对象获取执行者对象,并对sql语句进行预编译
            preparedStatement = connection.prepareStatement(sql);
            // 通过执行者对象获取参数的源信息对象
            ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
            //通过通过参数源信息对象获取参数个数
            int parameterCount = parameterMetaData.getParameterCount();
            //判断参数数量是否一致
            if(parameterCount != objects.length){
                throw new RuntimeException("参数个数不匹配");
            }
            //为占位符赋值
            for (int i=0; i<objects.length; i++){
                preparedStatement.setObject(i+1,objects[i]);
            }
            //执行sql语句并接受结果
            result = preparedStatement.executeUpdate();


        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //释放资源
            DataSourceUtils.close(connection,preparedStatement);
        }
        //返回结果
        return result;

    }

}
private JDBCTemplate jdbcTemplate = new JDBCTemplate(DataSourceUtils.getDataSource());
    @Test
    public void update(){
        String sql = "update student set name = ? where sid = ?";
        int update = jdbcTemplate.update(sql,"丽丽",1);
        System.out.println(update);
    }

3.查询方法

  • 查询一条记录并封装对象的方法:querforObject()
  • 查询多条记录并封装集合的方法:querForList()
  • 查询聚合函数并返回单条数据的方法:queryForScalar()
1)处理结果集的接口
1.定义泛型接口ResultSetHandler< T >
/*
* 用于处理结果集的规范接口
* */
public interface ResultSetHandler<T> {
    <T> T handler(ResultSet resultSet);
}
  • 定义一个类实现RestltSerHandler接口
  • 定义Class对象类型变量
  • 通过有参构造为变量赋值
  • 重写headler方法,用于将一条记录封装到自定义对象中
  • 声明自定义对象类型
  • 创建传递参数的对象,为自定义对象赋值
  • 判断结果集中是否有数据
  • 通过结果集对象获取结果集源信息的对象
  • 通过结果集源信息对象获取列数
  • 通过循环遍历列数
  • 通过结果集信息源获取列名
  • 通过列名获取数据
  • 创建属性描述器对象,将获取的值通过该对象的set方法进行赋值
  • 返回封装好的对象
package com.codeui.jdbc;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.List;

//         * 定义一个类实现RestltSerHandler接口
public class BeanHandler<T> implements ResultSetHandler<T>{
//         * 定义Class对象类型变量
    private Class<T> beanClass;
//         * 通过有参构造为变量赋值
    public BeanHandler(Class<T> beanClass){
        this.beanClass = beanClass;
    }
//         * 重写headler方法,用于将一条记录封装到自定义对象中
    @Override
    public <T> T handler(ResultSet resultSet) {
//         * 声明自定义对象类型
        T bean = null;
        try {
//         * 创建传递参数的对象,为自定义对象赋值
            bean = (T) beanClass.getDeclaredConstructor().newInstance();

//         * 判断结果集中是否有数据
            if ( resultSet.next()){
//         * 通过结果集对象获取结果集源信息的对象
                ResultSetMetaData metaData = resultSet.getMetaData();
//         * 通过结果集源信息对象获取列数
                int columnCount = metaData.getColumnCount();
//         * 通过循环遍历列数
                for (int i=1; i<=columnCount; i++){
//         * 通过结果集信息源获取列名
                    String columnName = metaData.getColumnName(i);
//         * 通过列名获取数据
                    Object value = resultSet.getObject(columnName);
//         * 创建属性描述器对象,将获取的值通过该对象的set方法进行赋值
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName.toLowerCase(),beanClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(bean,value);
                }
//         * 返回封装好的对象
                return bean;
            };

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


}

2.定义用于处理结果集的泛型方法< T >Thandler(ResultSet resultSet)
public interface ResultSetHandler<T> {
    <T> T handler(ResultSet resultSet);

}
2)queryForObject() 返回一个对象
  • 定义queryForObject方法,参数 sql、 处理结果集接口、sql中的参数

  • 声明自定义类型对象

  • 通过数据源获取数据库连接对象

  • 通过连接对象获取执行者对象并对sql语句进行预编译

  • 通过执行者对象获取参数源信息的对象

  • 通过参数源信息对象获取参数的个数

  • 判断参数数量是否一致

  • 为sql语句中?占位符赋值

  • 执行sql语句并接收结果集

  • 释放资源

  • 返回结果

     public <T> T  queryForObject(String sql,ResultSetHandler resultSetHandler ,Object... objects){
            T object = null;
    
            try {
                //通过数据源获取一个数据库连接
                connection = dataSource.getConnection();
                //通过数据库连接对象获取执行者对象,并对sql语句进行预编译
                preparedStatement = connection.prepareStatement(sql);
                // 通过执行者对象获取参数的源信息对象
                ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
                //通过通过参数源信息对象获取参数个数
                int parameterCount = parameterMetaData.getParameterCount();
                //判断参数数量是否一致
                if(parameterCount != objects.length){
                    throw new RuntimeException("参数个数不匹配");
                }
                //为占位符赋值
                for (int i=0; i<objects.length; i++){
                    preparedStatement.setObject(i+1,objects[i]);
                }
                //执行sql语句并接受结果
                resultSet = preparedStatement.executeQuery();
    
                // 通过BeanHandler方法对结果集进行处理
                object = (T) resultSetHandler.handler(resultSet);
    
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }finally {
                //释放资源
                DataSourceUtils.close(connection,preparedStatement,resultSet);
            }
            //返回结果
            return object;
        }
    
    @Test
    public void queryForObject(){
        String sql = "select * from student where sid = ?";
        Student student = jdbcTemplate.queryForObject(sql, new BeanHandler(Student.class), 1);
        System.out.println(student);
    }
3)queryForList 返回一个集合
  • 定义BeanListHandler< T>类实现 ResultSetHandler< T>接口
  • 定义Class对象类型的变量
  • 定义有参构造为变量赋值
  • 重写handler方法,用于将结果集中的所有记录封装到集合中并返回
  • 创建List集合对象
  • 遍历结果集对象
  • 创建传递参数的对象
  • 通过结果集对象获取结果集的源信息对象
  • 通过结果集原信息对象获取列数
  • 同过循环遍历列数
  • 通过结果集源信息获取列名
  • 通过列名获取该列的数据
  • 创建属性描述器对象,将获取到的值通过对象的set()方法进行赋值
  • 将封装好的对象添加到集合中
  • 返回集合对象
1.BeanListHandler
package com.codeui.jdbc;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

//定义BeanListHandler< T>类实现 ResultSetHandler< T>接口
public class BeanListHandler<T> implements ResultSetHandler<T> {
    //* 定义Class对象类型的变量
    private Class<T> beanClass;

    //* 定义有参构造为变量赋值
    public BeanListHandler(Class<T> beanClass) {
        this.beanClass = beanClass;
    }

    //* 重写handler方法,用于将结果集中的所有记录封装到集合中并返回
    @Override
    public List<T> handler(ResultSet resultSet) {
        //创建List集合对象
        List<T> list = new ArrayList<>();

        try {
            //遍历结果集对象
            while (!resultSet.next()) {
                //* 创建传递参数的对象
                T bean = beanClass.getDeclaredConstructor().newInstance();
                //* 通过结果集对象获取结果集的源信息对象
                ResultSetMetaData metaData = resultSet.getMetaData();
                // * 通过结果集原信息对象获取列数
                int columnCount = metaData.getColumnCount();
                //* 通过循环遍历列数
                for (int i = 1; i <= columnCount; i++) {
                    // * 通过结果集源信息获取列名
                    String columnName = metaData.getColumnName(i);
                    // 通过列名获取该列数据
                    Object value = resultSet.getObject(columnName);
                    //* 创建属性描述器对象,将获取到的值通过对象的set()方法进行赋值
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName.toLowerCase(), beanClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(bean, value);
                }
                //* 将封装好的对象添加到集合中
                list.add(bean);
            }
            // * 返回集合对象

        } catch (Exception throwables) {
            throwables.printStackTrace();
        }


        return list;
    }
}
2.queryForList
public <T> List<T> queryForList(String sql, ResultSetHandler<T> resultSetHandler , Object... objects){
        // 创建list集合
        List<T> list = new ArrayList<>();

        try {
            //通过数据源获取一个数据库连接
            connection = dataSource.getConnection();
            //通过数据库连接对象获取执行者对象,并对sql语句进行预编译
            preparedStatement = connection.prepareStatement(sql);
            // 通过执行者对象获取参数的源信息对象
            ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
            //通过通过参数源信息对象获取参数个数
            int parameterCount = parameterMetaData.getParameterCount();
            //判断参数数量是否一致
            if(parameterCount != objects.length){
                throw new RuntimeException("参数个数不匹配");
            }
            //为占位符赋值
            for (int i=0; i<objects.length; i++){
                preparedStatement.setObject(i+1,objects[i]);
            }
            //执行sql语句并接受结果
            resultSet = preparedStatement.executeQuery();

            // 通过BeanListHandler方法对结果集进行处理
            list = (List<T>) resultSetHandler.handler(resultSet);

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            //释放资源
            DataSourceUtils.close(connection,preparedStatement,resultSet);
        }
        //返回结果
        return list;
    }
4)queryForScalar 返回集合函数结果
1.ScalarHandler
  • 定义ScalarHandler< T > 实现ResultSetHandler< T>接口

  • 重写handler()方法,用于返回一个聚合函数的查询结果

  • 定义Long类型变量

  • 判断结果集是否有数据

  • 通过结果集对象获取结果集源信息的对象

  • 通过结果集源信息对象获取第一列的列名

  • 通过列名获取该列的数据

  • 将结果集返回

    //* 定义ScalarHandler< T > 实现ResultSetHandler< T>接口
    public class ScalarHandler<T> implements ResultSetHandler<T>{
        //* 重写handler()方法,用于返回一个聚合函数的查询结果
        @Override
        public Long handler(ResultSet resultSet) {
            //* 定义Long类型变量
            Long value = null;
    
            try {
                //* 判断结果集是否有数据
                if (resultSet.next()){
                    //* 通过结果集对象获取结果集源信息的对象
                    ResultSetMetaData metaData = resultSet.getMetaData();
                    //* 通过结果集源信息对象获取第一列的列名
                    String columnName = metaData.getColumnName(1);
                    //* 通过列名获取该列的数据
                    value = resultSet.getLong(columnName);
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            //* 将结果集返回
            return value;
        }
    }
    
    2.queryForScalar
        public Long queryForScalar(String sql, ResultSetHandler<Long> resultSetHandler , Object... objects){
            // 创建Long类型变量
            Long value = null;
    
            try {
                //通过数据源获取一个数据库连接
                connection = dataSource.getConnection();
                //通过数据库连接对象获取执行者对象,并对sql语句进行预编译
                preparedStatement = connection.prepareStatement(sql);
                // 通过执行者对象获取参数的源信息对象
                ParameterMetaData parameterMetaData = preparedStatement.getParameterMetaData();
                //通过通过参数源信息对象获取参数个数
                int parameterCount = parameterMetaData.getParameterCount();
                //判断参数数量是否一致
                if(parameterCount != objects.length){
                    throw new RuntimeException("参数个数不匹配");
                }
                //为占位符赋值
                for (int i=0; i<objects.length; i++){
                    preparedStatement.setObject(i+1,objects[i]);
                }
                //执行sql语句并接受结果
                resultSet = preparedStatement.executeQuery();
    
                // 通过BeanListHandler方法对结果集进行处理
                value = resultSetHandler.handler(resultSet);
    
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }finally {
                //释放资源
                DataSourceUtils.close(connection,preparedStatement,resultSet);
            }
            //返回结果
            return value;
        }