详细说明关于Java的数据库连接(JDBC)

目录
  • Java的数据库连接(JDBC)
    • 1、什么是JDBC
    • 2、JDBC的原理
    • 3、演示JDBC的使用
    • 4、数据库连接方式
    • 5、JDBC的查询
    • 6、SQL注入
    • 7、预处理查询
      • 7.1 查询 已解决注入问题
      • 7.2 插入,更新,删除
    • 8、工具类开发
      • 9、JDBC事务
        • 10、批处理
          • 11、数据库连接池
            • 11.1 数据库连接池基本介绍
            • 11.2 JDBC的数据库连接池使用
            • 11.3 数据库连接池的种类
            • 11.4 C3P0连接池
            • 11.5 Druid连接池
          • 12、Apache-DBUtils
            • 12.1 ArrayList模拟
            • 12.2 Apache-DBUtils 
          • 13、BasicDao
            • 13.1 BasicDAO类
            • 13.2 domain中的类
            • 13.3 测试类

        Java的数据库连接(JDBC)

        1、什么是JDBC

        JDBC(JavaDataBase Connectivity) 就是Java数据库连接,说白了就是用Java语言来操作数据库。原来我们操作数据库是在控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句。

        2、JDBC的原理

        早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动!JDBC是接口,而JDBC驱动才是接口的实现,没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。

        3、演示JDBC的使用

        通过下载MySQL的驱动jar文件,将其添加到项目中间,在注册驱动时要指定为已经下载好的驱动。

        package jdbc;
        
        import com.mysql.jdbc.Driver;  //这是我们驱动的路径
        
        import java.sql.Connection;
        import java.sql.SQLException;
        import java.sql.Statement;
        import java.util.Properties;
        public class Jdbc01 {
            public static void main(String[] args) throws SQLException {
        
                //1.注册驱动
                Driver driver = new Driver();
        
                //2.得到连接
                //jdbc:mysql:// 规定好的协议  localhost 连接的地址  3306 监听的端口 test_table 连接的数据库
                String url = "jdbc:mysql://localhost:3306/test_table";
                Properties properties = new Properties();
                //user和password 规定好的不能随意改
                properties.setProperty("user", "root");//
                properties.setProperty("password", "161142");
                Connection connect = driver.connect(url, properties); //相当于网络连接
        
                //3.执行sql语句
                //String sql = "insert into actor values(null,'syj','女','2000-05-26','110')";
                String sql = "update actor set name = 'xhj' where id =  2";
                Statement statement = connect.createStatement();
                int rows = statement.executeUpdate(sql);    //返回影响的行数
                if (rows > 0) System.out.println("添加成功");
                else System.out.println("添加失败");
        
                //4.关闭连接资源
                statement.close();
                connect.close();
            }
        }
        
        

        4、数据库连接方式

        public class JdbcConn {
            @Test    /* 第一种 */
            public void testConn01() throws SQLException {
                //获取Driver实现类对象
                Driver driver = new Driver();
        
                String url = "jdbc:mysql://localhost:3306/test_table";
                Properties properties = new Properties();
                properties.setProperty("user", "root");
                properties.setProperty("password", "161142");
                Connection connect = driver.connect(url, properties);
                System.out.println(connect);
            }
            
            @Test    /* 第二种 */
            public void testConn02() throws Exception{
                //使用反射加载Driver类,动态加载,可以通过配置文件灵活使用各种数据库
                Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
                Driver driver = (Driver) aClass.getDeclaredConstructor().newInstance();
        
                String url = "jdbc:mysql://localhost:3306/test_table";
                Properties properties = new Properties();
                properties.setProperty("user", "root");
                properties.setProperty("password", "161142");
                Connection connect = driver.connect(url, properties);
                System.out.println(connect);
            }
            
            @Test    /* 第三种 */
            //DriverManager统一来管理Driver
            public void testConn03() throws Exception{
                //使用反射加载Driver类
                Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
                Driver driver = (Driver) aClass.getDeclaredConstructor().newInstance();
        
                //创建url和user和password
                String url = "jdbc:mysql://localhost:3306/test_table";
                String user = "root";
                String password = "161142";
        
                DriverManager.registerDriver(driver);   //注册Driver驱动
        
                Connection connection = DriverManager.getConnection(url, user, password);
                System.out.println(connection);
            }
            
            @Test    /* 第四种 */
            public void testConn04() throws Exception{
                //使用反射加载Driver类
                Class.forName("com.mysql.jdbc.Driver");
                /* Class.forName("com.mysql.jdbc.Driver")在底层加载Driver时自动完成注册驱动,简化代码
                    //在底层加载Driver时会自动加载静态代码块
                    static {
                        try {
                            DriverManager.registerDriver(new Driver());
                        } catch (SQLException var1) {
                            throw new RuntimeException("Can't register driver!");
                        }
                    }
                */
                //Class.forName("com.mysql.jdbc.Driver");
                /*  Class.forName("com.mysql.jdbc.Driver");这句话也可以去掉
                    mysql驱动5.1.6可以无需CLass.forName(“com.mysql.jdbc.Driver");
                    从jdk1.5以后使用了jdbc4,不再需要显示调用class.forName()注册驱动而是自动调用驱动
                    jar包下META-INF\services\java.sqI.Driver文本中的类名称去注册
                    建议还是写上,更加明确
                */
                //创建url和user和password
                String url = "jdbc:mysql://localhost:3306/test_table";
                String user = "root";
                String password = "161142";
        
                Connection connection = DriverManager.getConnection(url, user, password);
                System.out.println(connection);
            }
            
            @Test    /* 第五种(推荐使用) */
            public void testConn05() throws Exception{
                //在方式4的情况下,将信息放到配置文件里,利于后续可持续操作
                //获取配置文件信息
                Properties properties = new Properties();
                properties.load(new FileInputStream("src\\mysql.properties"));
                //获取相关信息
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
                String url = properties.getProperty("url");
                String driver = properties.getProperty("driver");
        
                Class.forName(driver);  //加载Driver类,建议加上
        
                Connection connection = DriverManager.getConnection(url, user, password);
                System.out.println(connection);
            }
        }
        
        

        5、JDBC的查询

        使用ResultSet 记录查询结果
        ResultSet: 底层使用ArrayList 存放每一行数据(二维字节数组,每一维表示一行中的一个数据)
        Resultment: 用于执行静态SQL语句并返回其生成的结果的对象,是一个接口,需要各个数据库厂家来实现。(实际中我们一般不用这个)

        public class jdbc03 {
            public static void main(String[] args) throws Exception {
                Properties properties = new Properties();
                properties.load(new FileInputStream("src\\mysql.properties"));
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
                String url = properties.getProperty("url");
                String driver = properties.getProperty("driver");
                Class.forName(driver);
                Connection connection = DriverManager.getConnection(url, user, password);
                Statement statement = connection.createStatement();
        
                ResultSet resultSet = statement.executeQuery("select id,`name`,sex,borndate from actor;");
        
                while (resultSet.next()){ //resultSet.previous();//向上移动一行
                    int id = resultSet.getInt(1);
                    //int id = resultSet.getInt("id"); //也可以按照列明来获取
                    String name = resultSet.getString(2);
                    String sex = resultSet.getString(3);
                    Date date = resultSet.getDate(4);
                    //Object object = resultSet.getObject(索引|列明); //对象形式操作(分情况考虑)
                    System.out.println(id + "\t" + name + "\t" + sex + "\t" + date);
                }
        
                statement.close();
                connection.close();
            }
        }
        
        

        6、SQL注入

        SQL注入: 是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库。
        例如下列代码实现了注入问题(而Statement就存在这个问题,所以实际开发过程中不用它)

        create table admit(name varchar(32),password varchar(32));
        insert into admit values('tom','1234');
        select * from admit where name = 'tom' and password = '1234'; # 输出 tom 1234
        # 如果有人输入 name 为 1' or   password 为  or '1' = '1
        # 那么select 就变成
        select * from admit where name = '1' or ' and password = ' or '1' = '1'; # 其中'1' = '1'永远成立
        
        

        7、预处理查询

        使用PreparedStatement代替Statement就避免了注入问题,通过传入**?** 代替拼接 (PreparedStatement接口继承了Statement接口)

        PreparedStatement的好处

        • 不再使用+拼接sql语句,减少语法错误
        • 有效的解决了sql注入问题!
        • 大大减少了编译次数,效率较高

        7.1 查询 已解决注入问题

        public class jdbc04 {
            public static void main(String[] args) throws Exception {
                Scanner scanner = new Scanner(System.in);
                System.out.print("请输入用户名:");
                String name = scanner.nextLine();
                System.out.print("请输入密码:");
                String pwd = scanner.nextLine();
        
                Properties properties = new Properties();
                properties.load(new FileInputStream("src\\mysql.properties"));
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
                String url = properties.getProperty("url");
                String driver = properties.getProperty("driver");
                Class.forName(driver);
                Connection connection = DriverManager.getConnection(url, user, password);
        
                //Statement statement = connection.createStatement();
                //preparedStatement是PreparedStatement实现类的对象
                PreparedStatement preparedStatement = connection.prepareStatement("select `name` ,`password` " +
                        "from admit where name = ? and password = ?");
                preparedStatement.setString(1,name);    //?号下标从1开始
                preparedStatement.setString(2,pwd);
        
                ResultSet resultSet = preparedStatement.executeQuery();
                if (resultSet.next()) System.out.println("登录成功");
                else System.out.println("登陆失败");
        
                preparedStatement.close();
                connection.close();
            }
        }
        
        

        7.2 插入,更新,删除

        public class jdbc05 {
            public static void main(String[] args) throws Exception {
                Scanner scanner = new Scanner(System.in);
                System.out.print("请输入用户名:");
                String name = scanner.nextLine();
                System.out.print("请输入密码:");
                String pwd = scanner.nextLine();
        
                Properties properties = new Properties();
                properties.load(new FileInputStream("src\\mysql.properties"));
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
                String url = properties.getProperty("url");
                String driver = properties.getProperty("driver");
                Class.forName(driver);
                Connection connection = DriverManager.getConnection(url, user, password);
        
                //添加
                String sql1 = "insert into admit values(?,?)";
                //修改
                String sql2 = "update admit set name = ? where name = ? and password = ?";
                //删除
                String sql3 = "delete from admit where name = ? and password = ?";
                PreparedStatement preparedStatement = connection.prepareStatement(sql3);
                //preparedStatement.setString(1,name+"plas");    //?号下标从1开始
                //preparedStatement.setString(2,name);
                //preparedStatement.setString(3,pwd);
        
                preparedStatement.setString(1,name);
                preparedStatement.setString(2,pwd);
        
                int rows = preparedStatement.executeUpdate();
                if (rows > 0) System.out.println("操作成功");
                else System.out.println("操作失败");
        
                preparedStatement.close();
                connection.close();
            }
        }
        
        

        8、工具类开发

        由于在进行数据库操作时,有些步骤是重复的,如连接,关闭资源等操作。
        工具类

        package utils;
        import java.sql.*;
        import java.io.FileInputStream;
        import java.util.Properties;
        public class JDBCUtils {
            private static String user;     //用户名
            private static String password; //密码
            private static String url;      //连接数据库的url
            private static String driver;   //驱动
            
            //静态代码块进行行初始化
            static {
                try {
                    Properties properties = new Properties();
                    properties.load(new FileInputStream("src\\mysql.properties"));
                    user = properties.getProperty("user");
                    password = properties.getProperty("password");
                    url = properties.getProperty("url");
                    driver = properties.getProperty("driver");
                } catch (Exception e) {
                    //实际开发过程中(将编译异常,改成运行异常,用户可以捕获异常,也可以默认处理该异常)
                    throw new RuntimeException(e);
                }
            }
            //连接
            public static Connection getConnection(){
                try {
                    return DriverManager.getConnection(url,user,password);
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            //关闭资源
            public static void close(ResultSet set, Statement statement,Connection connection){
                try {
                    if (set != null) set.close();
                    if (statement != null)statement.close();
                    if (connection != null)connection.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        应用:

        public class JdbcUtilsTest {
            @Test  //测试select操作
            public void testSelect() {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
                ResultSet resultSet = null;
        
                try {
                    //得到连接
                    connection = JDBCUtils.getConnection();
        
                    //设置sql
                    String sql = "select * from actor where id = ?";
        
                    //创建PreparedStatement
                    preparedStatement = connection.prepareStatement(sql);
        
                    //占位赋值
                    preparedStatement.setInt(1,2);
        
                    //执行
                    resultSet = preparedStatement.executeQuery();
                    while (resultSet.next()) {
                        /* 也可以这样写
                        int id = resultSet.getInt("id");
                        String name = resultSet.getString("name");
                        String sex = resultSet.getString("sex");
                        Date date = resultSet.getDate("borndate");
                        String phone = resultSet.getString("phone");
                         */
                        int id = resultSet.getInt(1);
                        String name = resultSet.getString(2);
                        String sex = resultSet.getString(3);
                        Date date = resultSet.getDate(4);
                        String phone = resultSet.getString(5);
                        System.out.println(id + "\t" + name + "\t" + sex + "\t" + date + "\t" + phone);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    JDBCUtils.close(resultSet, preparedStatement, connection);
                }
            }
        
            @Test  //测试DML操作
            public void testDML() {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
        
                try {
                    //得到连接
                    connection = JDBCUtils.getConnection();
        
                    //设置sql
                    String sql = "update actor set name = ?,sex = ? where id = ?";
        
                    //创建PreparedStatement
                    preparedStatement = connection.prepareStatement(sql);
        
                    //占位符赋值
                    preparedStatement.setString(1, "sxy");
                    preparedStatement.setString(2, "男");
                    preparedStatement.setInt(3, 2);
        
                    //执行
                    preparedStatement.executeUpdate();
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    JDBCUtils.close(null, preparedStatement, connection);
                }
            }
        }
        
        

        9、JDBC事务

        public class Jdbc06 {
            public static void main(String[] args) {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
                try {
                    connection = JDBCUtils.getConnection();
                    connection.setAutoCommit(false);//关闭自动提交(开启事务)
        
                    //第一个动作
                    String sql = "update actor set phone = phone - 10 where id = 2";
                    preparedStatement = connection.prepareStatement(sql);
                    preparedStatement.executeUpdate();
        
                    //int i = 1/0;  异常
        
                    //第二个动作
                    sql = "update actor set phone = phone + 10 where id = 1";
                    preparedStatement = connection.prepareStatement(sql);
                    preparedStatement.executeUpdate();
        
                    //提交事务
                    connection.commit();
        
                } catch (Exception e) {
                    System.out.println("有异常存在,撤销sql服务");
                    try {
                        connection.rollback();  //回滚到事务开始的地方
                    } catch (SQLException throwables) {
                        throwables.printStackTrace();
                    }
                    e.printStackTrace();
                } finally {
                    JDBCUtils.close(null, preparedStatement, connection);
                }
        
            }
        }
        
        

        10、批处理

        public class Jdbc07 {
            @Test   //普通处理5000条插入数据   执行时间169839
            public void test01() {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
                try {
                    connection = JDBCUtils.getConnection();
                    String sql = "insert into actor(id,`name`,sex) values (?,?,'男')";
                    preparedStatement = connection.prepareStatement(sql);
                    long begin = System.currentTimeMillis();
                    for (int i = 0; i < 5000; i++) {
                        preparedStatement.setString(1, 3 + i + "");
                        preparedStatement.setString(2, "sxy" + (i + 1));
                        preparedStatement.executeUpdate();
                    }
                    long end = System.currentTimeMillis();
                    System.out.println(end - begin);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    JDBCUtils.close(null, preparedStatement, connection);
                }
            }
        
            @Test   //批处理   执行时间429
            public void test02() {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
                try {
                    connection = JDBCUtils.getConnection();
                    String sql = "insert into actor(id,`name`,sex) values (?,?,'男')";
                    preparedStatement = connection.prepareStatement(sql);
                    long begin = System.currentTimeMillis();
                    for (int i = 0; i < 5000; i++) {
                        preparedStatement.setString(1, 3 + i + "");
                        preparedStatement.setString(2, "sxy" + (i + 1));
                        
                        //将sql语句加入批处理包中
                        preparedStatement.addBatch();
                        /*
                            preparedStatement.addBatch()在底层把每一条数据加入到ArrayList
                            执行过程:检查本条sql中的语法问题 -> 把本条sql语句加入到ArrayList -> 每1000条执行一次
                            批处理优点:减少了编译次数,又减少了运行次数,效率大大提高
                            还需要在properties配置文件中将url加上?rewriteBatchedStatements=true
                            url=jdbc:mysql://localhost:3306/test_table?rewriteBatchedStatements=true
                        */
                        
                        //当有1000条时,在进行处理
                        if ((i + 1) % 1000 == 0) {
                            preparedStatement.executeBatch();
                            //清空批处理包
                            preparedStatement.clearBatch();
                        }
                    }
                    long end = System.currentTimeMillis();
                    System.out.println(end - begin);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    JDBCUtils.close(null, preparedStatement, connection);
                }
            }
        }
        
        

        11、数据库连接池

        由于有很多用户连接数据库,而数据库连接数量又是有限制的,而且就算连接并且关闭也是很耗时,所以就有了引入了数据库连接池可以很好的来解决这个问题。下面是普通连接数据库连接并且关闭5000次所耗时间6249毫秒,可以发下时间相对很长。

        public class ConQuestion {
            public static void main(String[] args) {
                //看看连接-关闭 connection 会耗用多久
                long start = System.currentTimeMillis();
                System.out.println("开始连接.....");
                for (int i = 0; i < 5000; i++) {
                    //使用传统的jdbc方式,得到连接
                    Connection connection = JDBCUtils.getConnection();
                    //做一些工作,比如得到PreparedStatement ,发送sql
                    //..........
                    //关闭
                    JDBCUtils.close(null, null, connection);
        
                }
                long end = System.currentTimeMillis();
                System.out.println("传统方式5000次 耗时=" + (end - start));//传统方式5000次 耗时=6249
            }
        }
        

        11.1 数据库连接池基本介绍

        • 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
        • 数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
        • 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

        11.2 JDBC的数据库连接池使用

        JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现。

        11.3 数据库连接池的种类

        • C3P0 数据库连接池,速度相对较慢,稳定性不错(hibernate,spring)。(用的较多)
        • DBCP数据库连接池,速度相对c3p0较快,但不稳定。
        • Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点。
        • BoneCP 数据库连接池,速度快。
        • Druid (德鲁伊)是阿里提供的数据库连接池,集DBCP,C3P0,Proxool优点于身的数据库连接池。(应用最广)

        11.4 C3P0连接池

        利用C3P0连接池再次尝试连接5000次数据库 可以发现耗时方式一仅仅花了456毫秒,第二种通过配置文件操作也是花了419毫秒差不多的时间,值得说的是这个连接池连接配置文件不能是我们自己写,官方有给定的模板(c3p0.config.xml)。

        public class C3P0_ {
            @Test   //方式一: 相关参数,在程序中指定user,url,password等
            public void testC3P0_1() throws Exception {
                //创建一个数据源对象
                ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        
                //通过配合文件获取相关连接信息
                Properties properties = new Properties();
                properties.load(new FileInputStream("src\\mysql.properties"));
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
                String url = properties.getProperty("url");
                String driver = properties.getProperty("driver");
        
                //给数据源(comboPooledDataSource)设置相关参数
                //连接管理是由comboPooledDataSource(连接池)来管理的
                comboPooledDataSource.setDriverClass(driver);   //设置驱动
                comboPooledDataSource.setJdbcUrl(url);
                comboPooledDataSource.setUser(user);
                comboPooledDataSource.setPassword(password);
        
                //初始化数据源的连接数
                comboPooledDataSource.setInitialPoolSize(10);
                //数据库连接池最大容量,如果还有连接请求,那么就会将该请求放入等待队列中
                comboPooledDataSource.setMaxPoolSize(50);
        
                //测试连接池的效率, 测试对mysql 5000次操作
                long start = System.currentTimeMillis();
                for (int i = 0; i < 5000; i++) {
                    //getConnection()这个方法就是重写了DataSource接口的方法
                    Connection connection = comboPooledDataSource.getConnection();
                    connection.close();
                }
                long end = System.currentTimeMillis();
                //c3p0 5000连接mysql 耗时=456
                System.out.println("c3p0 5000连接mysql 耗时=" + (end - start));
                comboPooledDataSource.close();
            }
            
            
            //第二种方式 使用配置文件模板来完成
            //将C3P0 提供的 c3p0.config.xml 拷贝到 src目录下
            //该文件指定了连接数据库和连接池的相关参数
            @Test
            public void testC3P0_02() throws SQLException {
                ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("sxy");
                //测试5000次连接mysql
                long start = System.currentTimeMillis();
                for (int i = 0; i < 5000; i++) {
                    Connection connection = comboPooledDataSource.getConnection();
                    connection.close();
                }
                long end = System.currentTimeMillis();
                //c3p0的第二种方式(5000) 耗时=419
                System.out.println("c3p0的第二种方式(5000) 耗时=" + (end - start));
        
            }
        }
        
        

        11.5 Druid连接池

        在使用Druid连接池连接数据库500000次耗时643毫秒,而C3P0500000次连接耗时2373毫秒,很显然Druid连接速度更快。

        public class Druid_ {
            @Test
            public void testDruid() throws Exception {
                //1.加入Druid jar包
                //2.加入 配置文件 druid.properties 放到src目录下
                //3.创建Properties对象
                Properties properties = new Properties();
                properties.load(new FileInputStream("src\\druid.properties"));
        
                //4.创建一个指定参数的数据库连接池
                DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        
                long start = System.currentTimeMillis();
                for (int i = 0; i < 500000; i++) {
                    Connection connection = dataSource.getConnection();
                    connection.close();
                }
                long end = System.currentTimeMillis();
                //Druid的500000次创建 耗时=643
                System.out.println("Druid的500000次创建 耗时=" + (end - start));
            }
        }
        
        

        对应的工具类

        public class JDBCUtilsByDruid {
            private static DataSource ds;
        
            //在静态代码块完成 ds初始化
            static {
                Properties properties = new Properties();
                try {
                    properties.load(new FileInputStream("src\\druid.properties"));
                    ds = DruidDataSourceFactory.createDataSource(properties);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        
            //编写getConnection方法
            public static Connection getConnection() throws SQLException {
                return ds.getConnection();
            }
        
            //关闭连接:在数据库连接池技术中,close不是真的断掉连接,而是把使用的Connection对象放回连接池
            public static void close(ResultSet resultSet, Statement statement, Connection connection) {
                try {
                    if (resultSet != null) resultSet.close();
                    if (statement != null) statement.close();
                    if (connection != null) connection.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        
        

        使用工具类:

        public class TestUtilsByDruid {
            @Test
            public void testSelect() {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
                ResultSet resultSet = null;
                try {
                    //得到连接
                    connection = JDBCUtilsByDruid.getConnection();
                    System.out.println(connection.getClass());
                    //connection 的运行类型 class com.alibaba.druid.pool.DruidPooledConnection
                    //设置sql
                    String sql = "select * from actor where id = ?";
                    //创建PreparedStatement
                    preparedStatement = connection.prepareStatement(sql);
                    //占位赋值
                    preparedStatement.setInt(1, 2);
                    //执行
                    resultSet = preparedStatement.executeQuery();
                    while (resultSet.next()) {
                        int id = resultSet.getInt(1);
                        String name = resultSet.getString(2);
                        String sex = resultSet.getString(3);
                        Date date = resultSet.getDate(4);
                        String phone = resultSet.getString(5);
                        System.out.println(id + "\t" + name + "\t" + sex + "\t" + date + "\t" + phone);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    JDBCUtilsByDruid.close(resultSet, preparedStatement, connection);
                }
            }
        }
        
        

        12、Apache-DBUtils

        由于resultSet存放数据集合,在connection关闭时,resultSet结果集无法使用。所以为了使用这些数据,也有JDBC官方提供的文件Apache-DBUtils来存放数据。

        12.1 ArrayList模拟

        ArrayList模拟Apache-DBUtils

        Actor类 用来保存Actor表中的数据用的。

        public class Actor { //Javabean, POJO, Domain对象
            private Integer id;
            private String name;
            private String sex;
            private Date borndate;
            private String phone;
            public Actor() { //一定要给一个无参构造器[反射需要]
            }
        
            public Actor(Integer id, String name, String sex, Date borndate, String phone) {
                this.id = id;
                this.name = name;
                this.sex = sex;
                this.borndate = borndate;
                this.phone = phone;
            }
        
            public Integer getId() { return id; }
            public void setId(Integer id) { this.id = id; }
            public String getName() { return name; }
            public void setName(String name) { this.name = name; }
            public String getSex() { return sex; }
            public void setSex(String sex) { this.sex = sex; }
            public Date getBorndate() { return borndate; }
            public void setBorndate(Date borndate) { this.borndate = borndate; }
            public String getPhone() { return phone; }
            public void setPhone(String phone) { this.phone = phone; }
        
            @Override
            public String toString() {
                return "\nActor{" + "id=" + id + ", name='" + name + '\'' +
                        ", sex='" + sex + '\'' + ", borndate=" + borndate +
                        ", phone='" + phone + '\'' + '}';
            }
        }
        
        

        用ArrayList来存放数据

        public class LikeApDB {
        
            @Test
            public /*也可以返回ArrayList<Actor>*/void testSelectToArrayList() {
                Connection connection = null;
                String sql = "select * from actor where id >= ?";
                PreparedStatement preparedStatement = null;
                ResultSet resultSet = null;
                ArrayList<Actor> list = new ArrayList<>();
                try {
                    connection = JDBCUtilsByDruid.getConnection();
                    System.out.println(connection.getClass());
                    preparedStatement = connection.prepareStatement(sql);
                    preparedStatement.setInt(1, 1);
                    resultSet = preparedStatement.executeQuery();
                    while (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String name = resultSet.getString("name");//getName()
                        String sex = resultSet.getString("sex");//getSex()
                        Date borndate = resultSet.getDate("borndate");
                        String phone = resultSet.getString("phone");
                        //把得到的 resultSet 的记录,封装到 Actor对象,放入到list集合
                        list.add(new Actor(id, name, sex, borndate, phone));
                    }
        
                    System.out.println("list集合数据=" + list);
                    for(Actor actor : list) {
                        System.out.println("id=" + actor.getId() + "\t" + actor.getName());
                    }
        
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    //关闭资源
                    JDBCUtilsByDruid.close(resultSet, preparedStatement, connection);
                }
                //因为ArrayList 和 connection 没有任何关联,所以该集合可以复用.
                //return  list;
            }
        }
        
        

        12.2 Apache-DBUtils 

         基本介绍

        commons-dbutils是 Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量。

        DbUtils类

        • QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增,删,改,查,批处理
        • 使用QueryRunner类实现查询。
        • ResultSetHandler接口:该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式

        应用实例

        使用Apache-DBUtils工具+数据库连接池(Druid)方式,完成对一个表的增删改查。

        package datasourse;
        
        import ApDB.Actor;
        import org.apache.commons.dbutils.QueryRunner;
        import org.apache.commons.dbutils.handlers.BeanHandler;
        import org.apache.commons.dbutils.handlers.BeanListHandler;
        import org.apache.commons.dbutils.handlers.ScalarHandler;
        import org.junit.jupiter.api.Test;
        
        import java.sql.Connection;
        import java.sql.SQLException;
        import java.util.List;
        public class DBUtils_Use {
            @Test       //查询多条数据
            public void testQueryMany() throws Exception {
                //1.得到连接(Druid)
                Connection connection = JDBCUtilsByDruid.getConnection();
                //2.使用 DBUtils 类和接口,先引入 DBUtils jar文件 ,放到src目录下
                //3.创建QueryRunner
                QueryRunner queryRunner = new QueryRunner();
                //4.执行相应的方法,返回ArrayList结果集
                String sql = "select * from actor where id >= ?";
                //String sql = "select id,`name` from actor where id >= ?";
                /*
                (1) query 方法就是执行sql 语句,得到resultSet ---封装到 --> ArrayList 集合中
                (2) 返回集合
                (3) connection: 连接
                (4) sql : 执行的sql语句
                (5) new BeanListHandler<>(Actor.class): 在将resultSet -> Actor 对象 -> 封装到 ArrayList
                    底层使用反射机制 去获取Actor 类的属性,然后进行封装
                (6) 1 就是给 sql 语句中的? 赋值,可以有多个值,因为是可变参数Object... params
                (7) 底层得到的resultSet ,会在query 关闭, 关闭PreparedStatement
                 */
                List<Actor> query =
                        queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
                /**
                 * 分析 queryRunner.query方法源码分析
                 * public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
                 *         PreparedStatement stmt = null;//定义PreparedStatement
                 *         ResultSet rs = null;//接收返回的 ResultSet
                 *         Object result = null;//返回ArrayList
                 *
                 *         try {
                 *             stmt = this.prepareStatement(conn, sql);//创建PreparedStatement
                 *             this.fillStatement(stmt, params);//对sql 进行 ? 赋值
                 *             rs = this.wrap(stmt.executeQuery());//执行sql,返回resultset
                 *             result = rsh.handle(rs);//返回的resultset --> arrayList[result] [使用到反射,对传入class对象处理]
                 *         } catch (SQLException var33) {
                 *             this.rethrow(var33, sql, params);
                 *         } finally {
                 *             try {
                 *                 this.close(rs);//关闭resultset
                 *             } finally {
                 *                 this.close((Statement)stmt);//关闭preparedstatement对象
                 *             }
                 *         }
                 *
                 *         return result;
                 *     }
                 */
                for (Actor actor : query) {
                    System.out.print(actor);
                }
                JDBCUtilsByDruid.close(null,null,connection);
            }
        
            @Test               //查询单条记录
            public void testQuerySingle() throws SQLException {
                Connection connection = JDBCUtilsByDruid.getConnection();
                QueryRunner queryRunner = new QueryRunner();
                String sql = "select * from actor where id = ?";
                //已知查询的是单行,所以就用BeanHandler,返回一个对应的对象
                Actor query = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 2);
                System.out.print(query);
                JDBCUtilsByDruid.close(null,null,connection);
            }
        
            @Test               //查询单行单列(某个信息)  返回一个Object对象
            public void testQuerySingleObject() throws SQLException {
                Connection connection = JDBCUtilsByDruid.getConnection();
                QueryRunner queryRunner = new QueryRunner();
                String sql = "select `name` from actor where id = ?";
                //已知查询的是单行单列,所以就用BeanHandler,返回一个Object
                Object query = queryRunner.query(connection, sql, new ScalarHandler(), 1);
                System.out.println(query);
                JDBCUtilsByDruid.close(null,null,connection);
            }
        
            @Test       //演示DML操作(insert,update,delete)
            public void testDML() throws SQLException {
                Connection connection = JDBCUtilsByDruid.getConnection();
                QueryRunner queryRunner = new QueryRunner();
        
                //String sql = "update actor set phone = ? where id = ?";
                //int affectedRow = queryRunner.update(connection, sql, "110", 2);
        
                String sql = "insert into actor values(?,?,?,?,?)";
                int affectedRow = queryRunner.update(connection, sql, 3, "xhj", "女", "2000-05-26", "110");
        
                //String sql = "delete from actor where id = ?";
                //int affectedRow = queryRunner.update(connection, sql, 5004);
        
                System.out.println(affectedRow > 0 ? "OK" : "NO");
                JDBCUtilsByDruid.close(null,null,connection);
            }
        }
        
        

        13、BasicDao

        引入问题

        • SQL语句是固定,不能通过参数传入,通用性不好,需要进行改进,更方便执行增删改查
        • 对于select 操作,如果有返回值,返回类型不能固定,需要使用泛型
        • 将来的表很多,业务需求复杂,不可能只靠一个JAVA类完成。

        所以在实际开发中,也有解决办法 —BasicDao

        13.1 BasicDAO类

        public class BasicDAO<T> {  //泛型指定具体的类型
            private QueryRunner queryRunner = new QueryRunner();
        
            //开发通用的DML,针对任意表
            public int update(String sql,Object... parameter){
                Connection connection = null;
                try {
                    connection = JDBCUtilsByDruid.getConnection();
                    return queryRunner.update(connection, sql, parameter);
                } catch (SQLException e) {
                    throw new RuntimeException(e);//将编译异常转化成运行异常,可以被捕获,也可以被抛出
                }finally {
                    JDBCUtilsByDruid.close(null,null,connection);
                }
            }
        
            /**                     返回多个对象(多行结果)
             *
             * @param sql       sql语句,可以存在?
             * @param clazz     传入一个类的class对象   例如Actor.class
             * @param parameter 传入?号具体的值,可以有多个
             * @return          根据类似Actor.class类型,返回对应的ArrayList集合
             */
            public List<T> QueryMultiply(String sql,Class<T> clazz, Object... parameter){
                Connection connection = null;
                try {
                    connection = JDBCUtilsByDruid.getConnection();
                    return queryRunner.query(connection,sql,new BeanListHandler<T>(clazz),parameter);
                } catch (SQLException e) {
                    throw new RuntimeException(e);//将编译异常转化成运行异常,可以被捕获,也可以被抛出
                }finally {
                    JDBCUtilsByDruid.close(null,null,connection);
                }
            }
            //返回单个对象(单行数据)
            public T querySingle(String sql,Class<T> clazz,Object... parameter){
                Connection connection = null;
                try {
                    connection = JDBCUtilsByDruid.getConnection();
                    return queryRunner.query(connection,sql,new BeanHandler<T>(clazz),parameter);
                } catch (SQLException e) {
                    throw new RuntimeException(e);//将编译异常转化成运行异常,可以被捕获,也可以被抛出
                }finally {
                    JDBCUtilsByDruid.close(null,null,connection);
                }
            }
            //返回单个对象的单个属性(单行中的单列)
            public Object queryScalar(String sql,Object... parameter){
                Connection connection = null;
                try {
                    connection = JDBCUtilsByDruid.getConnection();
                    return queryRunner.query(connection,sql,new ScalarHandler(),parameter);
                } catch (SQLException e) {
                    throw new RuntimeException(e);//将编译异常转化成运行异常,可以被捕获,也可以被抛出
                }finally {
                    JDBCUtilsByDruid.close(null,null,connection);
                }
            }
        }
        
        

        13.2 domain中的类

        public class Actor { //Javabean, POJO, Domain对象
            private Integer id;
            private String name;
            private String sex;
            private Date borndate;
            private String phone;
            public Actor() { //一定要给一个无参构造器[反射需要]
            }
        
            public Actor(Integer id, String name, String sex, Date borndate, String phone) {
                this.id = id;
                this.name = name;
                this.sex = sex;
                this.borndate = borndate;
                this.phone = phone;
            }
        
            public Integer getId() { return id; }
            public void setId(Integer id) { this.id = id; }
            public String getName() { return name; }
            public void setName(String name) { this.name = name; }
            public String getSex() { return sex; }
            public void setSex(String sex) { this.sex = sex; }
            public Date getBorndate() { return borndate; }
            public void setBorndate(Date borndate) { this.borndate = borndate; }
            public String getPhone() { return phone; }
            public void setPhone(String phone) { this.phone = phone; }
        
            @Override
            public String toString() {
                return "\nActor{" + "id=" + id + ", name='" + name + '\'' +
                        ", sex='" + sex + '\'' + ", borndate=" + borndate +
                        ", phone='" + phone + '\'' + '}';
            }
        }
        
        

        ActorDAO类继承BasicDAO类,这样的类可以有很多。

        public class ActorDAO extends BasicDAO<Actor> {
        }
        
        

        13.3 测试类

        public class TestDAO {
            @Test//测试ActorDAO对actor表的操作
            public void testActorDAO() {
                ActorDAO actorDAO = new ActorDAO();
                //1.查询多行
                List<Actor> actors = actorDAO.QueryMultiply("select * from actor where id >= ?", Actor.class, 1);
                System.out.println(actors);
        
                //2.查询单行
                Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 1);
                System.out.println(actor);
        
                //3.查询单行单个数据
                Object o = actorDAO.queryScalar("select name from actor where id = ?", 1);
                System.out.println(o);
        
                //4.DML操作   当前演示update
                int affectedRow = actorDAO.update("update actor set phone = ? where id = ?", "120", 3);
                System.out.println(affectedRow > 0 ? "OK" : "NO");
            }
        }
        
        

        本文转自网络,如有侵权请联系客服删除。