JDBC、数据库连接池、JDBCTemplate、C3P0、Druid、Spring JDBC

JDBC:Java DataBase Connectivity(Java数据库连接)

概念:Java DataBase Connectivity(Java数据库连接),Java语言操作数据库。

JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

0. 前言

本文所用到的数据库有da3、db4。所用到的表有da3中的account表、emp表,db4中的user表
account表:
在这里插入图片描述
emp表:
在这里插入图片描述
user表:
在这里插入图片描述

1.快速入门

  • 步骤

    1. 导入驱动jar包

      • 复制相应的.jar包到项目的libs(也可以建立其他名字的文件夹,建立文件夹的目的是方便管理)
      • 然后右键libs文件夹–>Add As Library
    2. 注册驱动

       Class.forName("com.mysql.cj.jdbc.Driver");
      
    3. 获取数据库连接对象Connection

       Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/数据库名称?useSSL=false&serverTimezone=UTC", "root", "root");
      
    4. 定义sql语句

    5. 获取执行sql语句的对象 Statement

    6. 执行sql,接收返回结果

    7. 处理结果

    8. 释放资源

  • 代码示例:

      //这里我们用名为da3的数据库
    
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.Statement;
    
      public class Main {
          public static void main(String[] args) throws Exception {
              //1.导入驱动jar包
              //2.注册驱动
              Class.forName("com.mysql.cj.jdbc.Driver");//这一行可以省略
              //3.获取数据库连接对象
              Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/da3?useSSL=false&serverTimezone=UTC", "root", "root");
              //4、定义sql语句
              String sql = "update account set balance = 1000 where id = 1";
              //5、获取执行sql的对象Statement
              Statement stmt = connection.createStatement();
              //6、执行sql
              int count = stmt.executeUpdate(sql);
              //7.处理结果
              System.out.println(count);
              //8.释放资源
              stmt.close();
              connection.close();
          }
      }
    

2.详解各个对象

  1. DriverManager:驱动管理对象
  2. Connection:数据库连接对象
  3. Statement:执行sql的对象
  4. ResultSet:结果集对象
  5. PreparedStatement:执行sql的对象,是Statement的子接口

2.1 DriverManager:驱动管理对象

功能1.注册驱动

告诉程序该使用哪一个数据库驱动jar

在DriverManager中有一个静态方法static void registerDriver(Driver driver)可以注册与给定的驱动程序DriverManager。但是我们在写代码的时候却直接使用Class.forName("com.mysql.cj.jdbc.Driver")注册驱动。通过查看源码我们可以发现原因:在com.mysql.cj.jdbc.Driver类中存在静态代码块(如下),可以驱动程序。

static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }

注意:mysql5之后的驱动jar包可以省略注册驱动步骤。

功能2.获取数据库连接
  • 方法:static Connection getConnection(String url, String user, String password)

    • 参数:

        url:指定连接的对象
            语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?useSSL=false&serverTimezone=UTC
        user:用户名
        password:密码
      

2.2 Connection:数据库连接对象

功能1.获取执行sql的对象

方法1:Statement createStatement()

方法2:PreparedStatement PreparedStatement (String sql)

功能2.管理事务
开启事务

Connection.setAutoCommit(boolean autoCommit):调用该方法设置参乎上位false,即开启事务

提交事务

commit()

回滚事务

rollback()

案例

转账。这里我们用数据库da3中的account表。

代码实现:

import jdbc_utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Tran_Main {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement1 = null;
        PreparedStatement preparedStatement2 = null;
        try {
            //获取连接
            connection = JDBCUtils.getConnection();
            //开启事务
            connection.setAutoCommit(false);
            //定义sql语句
            String sql1 = "update account set balance = balance - ? where id = ?";
            String sql2 = "update account set balance = balance + ? where id = ?";
            //获取执行对象
            preparedStatement1 = connection.prepareStatement(sql1);
            preparedStatement2 = connection.prepareStatement(sql2);
            //设置参数
            preparedStatement1.setInt(1, 500);
            preparedStatement1.setInt(2, 1);
            preparedStatement2.setInt(1, 500);
            preparedStatement2.setInt(2, 2);
            //执行SQL
            preparedStatement1.executeUpdate();
            //测试:在下面手动制造异常
            //int i = 3 / 0;
            preparedStatement2.executeUpdate();
            //提交事务
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //如果在事务中遇到了异常,就回滚事务
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //释放资源
            JDBCUtils.close(preparedStatement1, connection);
            JDBCUtils.close(preparedStatement2, null);
        }
    }
}

2.3 Statement:执行sql的对象

相关方法
  1. boolean execute(String sql): 可以执行任意的sql,了解即可
  2. int executeUpdate(String sql): 执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句
    • 返回值:影响的行数,可以通过这个影响的行数判断DML是否执行成功,返回值>0则执行成功,反之,则失败
  3. ResultSet executeQuery(String sql): 执行DQL(select)语句
练习
  1. account表,添加一条记录
  2. account表,修改记录
  3. account表,删除一条记录
//1.account表,添加一条记录

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCDemo01 {
    public static void main(String[] args) {
        Statement statement = null;
        Connection connection = null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取数据库连接对象
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/da3?useSSL=false&serverTimezone=UTC", "root", "root");
            //3.定义sql语句
            String sql = "insert into account values(null, '王五', 3000 )";
            //4.获取执行sql语句的对象 Statement
            statement = connection.createStatement();
            //5.执行sql,接收返回结果
            int i = statement.executeUpdate(sql);
            //6.处理结果
            System.out.println(i);
            if(i > 0){
                System.out.println("添加成功");
            }else{
                System.out.println("添加失败");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if(connection != null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
//2.account表,修改记录

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCDemo02 {
    public static void main(String[] args) {
        Statement statement = null;
        Connection connection = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/da3?useSSL=false&serverTimezone=UTC", "root", "root");
            statement = connection.createStatement();
            String sql = "update account set balance = 10000 where id = 3";
            int i = statement.executeUpdate(sql);
            System.out.println(i);
            if(i > 0){
                System.out.println("修改成功");
            }else{
                System.out.println("修改失败");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if(connection != null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
//3.account表,删除一条记录

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCDemo03 {
    public static void main(String[] args) {
        Statement statement = null;
        Connection connection = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/da3?useSSL=false&serverTimezone=UTC", "root", "root");
            statement = connection.createStatement();
            String sql = "delete from account where id = 3";
            int i = statement.executeUpdate(sql);
            System.out.println(i);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

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

2.4 ResultSet:结果集对象

封装查询的结果。

相关方法
  1. boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true

  2. getXxx(参数): 获取数据

    • Xxx: 如 int getInt(参数) String getString(参数)
    • 参数:
      • int : 代表列的编号,从1开始,如:getString(1)
      • String : 代表列名称。如getDouble("列名")
  3. 使用步骤

    1. 游标向下移动一行
    2. 判断是否有数据
    3. 获取数据
  4. 代码示例:

     import java.sql.*;
    
     public class JDBCDemo04 {
         public static void main(String[] args) {
             Statement statement = null;
             Connection connection = null;
             ResultSet resultSet = null;
             try {
                 Class.forName("com.mysql.cj.jdbc.Driver");
                 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/da3?useSSL=false&serverTimezone=UTC", "root", "root");
                 statement = connection.createStatement();
                 String sql = "select * from account";
                 resultSet = statement.executeQuery(sql);
                 //获取数据
                 while (resultSet.next()){
                     int anInt = resultSet.getInt(1);
                     String name = resultSet.getString("name");
                     double aDouble = resultSet.getDouble(3);
                     System.out.println(anInt + "---" + name + "---" + aDouble);
                 }
             } catch (ClassNotFoundException e) {
                 e.printStackTrace();
             } catch (SQLException e) {
                 e.printStackTrace();
             }finally {
                 if(resultSet != null){
                     try {
                         resultSet.close();
                     } catch (SQLException e) {
                         e.printStackTrace();
                     }
                 }
                 if(statement != null){
                     try {
                         statement.close();
                     } catch (SQLException e) {
                         e.printStackTrace();
                     }
                 }
    
                 if(connection != null){
                     try {
                         connection.close();
                     } catch (SQLException e) {
                         e.printStackTrace();
                     }
                 }
             }
         }
     }
    
练习

查询da3中的emp表

  1. 创建emp类:
import java.util.Date;

public class Emp {
    private int id;
    private String ename;
    private int job_id;
    private int mgr;
    private Date joindate;
    private double salary;
    private double bonus;
    private int dept_id;

    public Emp() {
    }

    public Emp(int id, String ename, int job_id, int mgr, Date joindate, double salary, double bonus, int dept_id) {
        this.id = id;
        this.ename = ename;
        this.job_id = job_id;
        this.mgr = mgr;
        this.joindate = joindate;
        this.salary = salary;
        this.bonus = bonus;
        this.dept_id = dept_id;
    }

    @Override
    public String toString() {
        return "emp{" +
                "id=" + id +
                ", ename='" + ename + '\'' +
                ", job_id=" + job_id +
                ", mgr=" + mgr +
                ", joindate=" + joindate +
                ", salary=" + salary +
                ", bonus=" + bonus +
                ", dept_id=" + dept_id +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public int getJob_id() {
        return job_id;
    }

    public void setJob_id(int job_id) {
        this.job_id = job_id;
    }

    public int getMgr() {
        return mgr;
    }

    public void setMgr(int mgr) {
        this.mgr = mgr;
    }

    public Date getJoindate() {
        return joindate;
    }

    public void setJoindate(Date joindate) {
        this.joindate = joindate;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public int getDept_id() {
        return dept_id;
    }

    public void setDept_id(int dept_id) {
        this.dept_id = dept_id;
    }
}
  1. 实现查询
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class empMain {
    public static void main(String[] args) {
        List<Emp> all = getAll();
        for(Emp e : all){
            System.out.println(e);
        }
    }

    public static List<Emp> getAll(){
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //获取连接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/da3?useSSL=false&serverTimezone=UTC", "root", "root");
            //定义sql语句
            String sql = "select * from emp";
            //获取执行对象
            statement = connection.createStatement();
            //获取结果集
            resultSet = statement.executeQuery(sql);
            //把结果集中的数据依次存入emp对象中,并把每个对象封装成集合
            Emp emp = null;
            List<Emp> list = new ArrayList<>();
            while(resultSet.next()){
                int id = resultSet.getInt("id");
                String ename = resultSet.getString("ename");
                int job_id = resultSet.getInt("job_id");
                int mgr = resultSet.getInt("mgr");
                Date joindate = resultSet.getDate("joindate");
                double salary = resultSet.getDouble("salary");
                double bonus = resultSet.getDouble("bonus");
                int dept_id = resultSet.getInt("dept_id");

                //存入emp对象中并返回
                emp = new Emp(id, ename, job_id, mgr, joindate, salary, bonus, dept_id);
                list.add(emp);
            }
            return list;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
        }finally {
            if(resultSet != null){
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

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

            if(connection != null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;//这里的返回值表名如果try中的代码出现异常,就不会返回list集合,但是这个方法是需要有返回值的,程序就会直接执行finally中的代码,返回null
    }
}
  1. 查询结果:
emp{id=1001, ename='孙悟空', job_id=4, mgr=1004, joindate=2000-12-17, salary=8000.0, bonus=0.0, dept_id=20}
emp{id=1002, ename='卢俊义', job_id=3, mgr=1006, joindate=2001-02-20, salary=16000.0, bonus=3000.0, dept_id=30}
emp{id=1003, ename='林冲', job_id=3, mgr=1006, joindate=2001-02-22, salary=12500.0, bonus=5000.0, dept_id=30}
emp{id=1004, ename='唐僧', job_id=2, mgr=1009, joindate=2001-04-02, salary=29750.0, bonus=0.0, dept_id=20}
emp{id=1005, ename='李逵', job_id=4, mgr=1006, joindate=2001-09-28, salary=12500.0, bonus=14000.0, dept_id=30}
emp{id=1006, ename='宋江', job_id=2, mgr=1009, joindate=2001-05-01, salary=28500.0, bonus=0.0, dept_id=30}
emp{id=1007, ename='刘备', job_id=2, mgr=1009, joindate=2001-09-01, salary=24500.0, bonus=0.0, dept_id=10}
emp{id=1008, ename='猪八戒', job_id=4, mgr=1004, joindate=2007-04-19, salary=30000.0, bonus=0.0, dept_id=20}
emp{id=1009, ename='罗贯中', job_id=1, mgr=0, joindate=2001-11-17, salary=50000.0, bonus=0.0, dept_id=10}
emp{id=1010, ename='吴用', job_id=3, mgr=1006, joindate=2001-09-08, salary=15000.0, bonus=0.0, dept_id=30}
emp{id=1011, ename='沙僧', job_id=4, mgr=1004, joindate=2007-05-23, salary=11000.0, bonus=0.0, dept_id=20}
emp{id=1012, ename='李逵', job_id=4, mgr=1006, joindate=2001-12-03, salary=9500.0, bonus=0.0, dept_id=30}
emp{id=1013, ename='小白龙', job_id=4, mgr=1004, joindate=2001-12-03, salary=30000.0, bonus=0.0, dept_id=20}
emp{id=1014, ename='关羽', job_id=4, mgr=1007, joindate=2002-01-23, salary=13000.0, bonus=0.0, dept_id=10}

2.5 抽取JDBC工具类:JDBCUtils

目的:简化书写。

分析:

1. 抽取注册驱动的相关代码
 2. 抽取一个方法用来获取连接对象
* 需求:不想传递参数,而且必须保证工具类的通用性
* 解决方案:用配置文件来结局
3. 抽取一个方法用来释放资源
1.定义配置文件

定义配置文件jdbc.proprities(名字可随意,此处只是例子)

Url=jdbc:mysql://localhost:3306/da3?useSSL=false&serverTimezone=UTC
user=root
password=root
driver=com.mysql.cj.jdbc.Driver
2.创建工具类JDBCUtils
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;

public class JDBCUtils {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;

    //用类加载器获取配置文件的路径
    //注册代码
    static {
        try {
            Properties properties = new Properties();
            //获取src路径下的文件的方式---->ClassLoader: 类加载器
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();
            URL resource = classLoader.getResource("jdbc.proprities");
            String path = resource.getPath();
            properties.load(new FileReader(path));

            url = properties.getProperty("Url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            driver = properties.getProperty("driver");

            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }


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

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

    public static void close(ResultSet resultSet, Statement statement, Connection connection){
        if(resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

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

        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
3.测试

以上一节查询emp表为例进行测试

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class empMain {
    public static void main(String[] args) {
        List<Emp> all = getAll();
        for(Emp e : all){
            System.out.println(e);
        }
    }

    public static List<Emp> getAll(){
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCUtils.getConnection();
            //定义sql语句
            String sql = "select * from emp";
            //获取执行对象
            statement = connection.createStatement();
            //获取结果集
            resultSet = statement.executeQuery(sql);
            //把结果集中的数据依次存入emp对象中,并把每个对象封装成集合
            Emp emp = null;
            List<Emp> list = new ArrayList<>();
            while(resultSet.next()){
                int id = resultSet.getInt("id");
                String ename = resultSet.getString("ename");
                int job_id = resultSet.getInt("job_id");
                int mgr = resultSet.getInt("mgr");
                Date joindate = resultSet.getDate("joindate");
                double salary = resultSet.getDouble("salary");
                double bonus = resultSet.getDouble("bonus");
                int dept_id = resultSet.getInt("dept_id");

                //存入emp对象中并返回
                emp = new Emp(id, ename, job_id, mgr, joindate, salary, bonus, dept_id);
                list.add(emp);
            }
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
           JDBCUtils.close(resultSet, statement, connection);
        }
        return null;//这里的返回值表名如果try中的代码出现异常,就不会返回list集合,程序就会直接执行finally中的代码,
    }
}
练习
  • 需求:

    • 通过键盘录入用户名和密码
    • 判断用户是否登录成功
  • 代码实现:
    数据库部分:

      create database db4;
    
      use db4;
    
      CREATE table user(
          id int primary key auto_increment,
          username varchar(32),
          password varchar(32)
      );
    
      insert into user values(null, 'zhangsan', '123');
      insert into user values(null, 'lisi', '234');
    
      select * from user;
    

    Java部分:

      import jdbc_utils.JDBCUtils;
    
      import java.sql.Connection;
      import java.sql.ResultSet;
      import java.sql.SQLException;
      import java.sql.Statement;
      import java.util.Scanner;
    
      public class Login_Main {
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);
              System.out.println("请输入用户名:");
              String username = sc.nextLine();
              System.out.println("请输入密码:");
              String password = sc.nextLine();
              boolean flag = flag(username, password);
              if(flag){
                  System.out.println("登录成功!");
              }else{
                  System.out.println("登录失败!");
              }
          }
    
          //判断用户名和密码是否匹配,如果匹配则返回true,否则返回false
          public static boolean flag(String username, String password){
              if(username == null || password == null){
                  return false;
              }
    
              Connection connection = null;
              Statement statement = null;
              ResultSet resultSet = null;
              try {
                  connection = JDBCUtils.getConnection();
                  String sql = "select * from user where username = '" + username + "'and password = '" + password + "'";
                  statement = connection.createStatement();
                  resultSet = statement.executeQuery(sql);
                  return  resultSet.next();
              } catch (SQLException e) {
                  e.printStackTrace();
              }finally {
                  JDBCUtils.close(resultSet, statement, connection);
              }
    
              return false;
          }
      }
    

2.6 PreparedStatement:执行sql的对象

是Statement的子接口。

  • 对于上一节练习中的代码会出现如下情况:

      请输入用户名:
      dsvbjhbadvsjd
      请输入密码:
      a' or 'a' = 'a
      登录成功!
    
      Process finished with exit code 0
    

    我们随便输入了一个用户名和密码a' or 'a' = 'a,竟然登录成功了。

    这就是sql注入问题。

  • SQL注入问题:

    在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题

      随便输入一个用户名,输入密码:a' or 'a' = 'a
      这时执行的SQL语句是:select * from user where username = '随便输入的用户名' and password = 'a' or 'a' = 'a' 
      这条语句是可以正确执行的。
    
  • 解决SQL注入问题:使用PreparedStatement对象来解决

  • 预编译SQL:参数使用?作为占位符

  • 步骤:

    1. 导入jar包
    2. 注册驱动
    3. 获取数据库连接对象Connection
    4. 定义sql
      • 注意:sql的参数使用?作为占位符。如:select * from user where username = ? and password = ?
    5. 获取执行sql的对象 PreparedStatement COnnection.prepareStatement(String sql)
    6. 给?(占位符)复制
      • 方法:setXxx(参数1,参数2)
        • 参数1:?的位置编号 从1开始
        • 参数2:?的值
    7. 执行sql,接收返回结果,不需要传递sql语句
    8. 处理结果
    9. 释放资源
  • 注意:后期都会使用PreparedStatement来完成增删改查的所有操作

    • 可以防止SQL注入
    • 效率更高
  • 应用上述步骤解决上一节的问题

      import jdbc_utils.JDBCUtils;
    
      import java.sql.*;
      import java.util.Scanner;
    
      public class Login_Main {
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);
              System.out.println("请输入用户名:");
              String username = sc.nextLine();
              System.out.println("请输入密码:");
              String password = sc.nextLine();
              boolean flag = flag(username, password);
              if(flag){
                  System.out.println("登录成功!");
              }else{
                  System.out.println("登录失败!");
              }
          }
    
          //判断用户名和密码是否匹配,如果匹配则返回true,否则返回false
          public static boolean flag(String username, String password){
              if(username == null || password == null){
                  return false;
              }
    
              Connection connection = null;
              PreparedStatement preparedStatement = null;
              ResultSet resultSet = null;
              try {
                  connection = JDBCUtils.getConnection();
                  String sql = "select * from user where username = ? and password = ?";
                  preparedStatement = connection.prepareStatement(sql);
                  preparedStatement.setString(1, username);
                  preparedStatement.setString(2, password);
                  resultSet = preparedStatement.executeQuery();
                  return  resultSet.next();
              } catch (SQLException e) {
                  e.printStackTrace();
              }finally {
                  JDBCUtils.close(resultSet, preparedStatement, connection);
              }
    
              return false;
          }
      }
    

    此时,就不会出现sql注入问题了。

      请输入用户名:
      adishjvchsakjds
      请输入密码:
      a' or 'a' = 'a
      登录失败!
    
      Process finished with exit code 0
    

数据库连接池

  • 概念:其实就是一个容器(集合),存放数据库连接的容器。

    当系统初始化好后,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

  • 好处:

    1. 节约资源
    2. 用户访问高效
  • 实现:

    • 标准接口:DateSource javax.sql 包下的
      • 该接口中常用的方法:
        1. 获取连接:getConnection()
        2. 归还连接:COnnection.close();如果连接对象Connection是从连接池中获取的,那么调用COnnection.close()方法就不会再关闭连接了,而是归还连接。
    • 常用的数据库连接池技术如下(一般我们不去实现它,由数据库厂商来实现)
      1. C3P0:数据库连接池技术
      2. Druid:数据库连接池实现技术,由阿里巴巴提供

1. C3P0

1.1 使用步骤:

  1. 导入jar包(两个):c3p0-0.9.5.2. jar mchange - commons-java-0.2.12. jar

    不要忘记导入数据库驱动jar包

  2. 定义配置文件

    • 名称:c3p0. properties或者c3p0- config . xml
    • 路径:直接将文件放在src目录下即可
  3. 创建核心对象:数据库连接池对象 ComboPooledDataSource

  4. 获取连接:getConnection

1.2 配置文件详解

c3p0- config . xml文件详解(这里我们使用了db4数据库):

<c3p0-config>
  <!-- 使用默认的配置读取连接池对象 -->
  <default-config>
      <!--  连接参数 -->
    <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db4?useSSL=false&amp;serverTimezone=UTC</property>
    <property name="user">root</property>
    <property name="password">root</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.cj.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db4?useSSL=false&amp;serverTimezone=UTC</property>
    <property name="user">root</property>
    <property name="password">root</property>

    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">8</property>
    <property name="checkoutTimeout">1000</property>
  </named-config>
</c3p0-config>

1.3 代码测试

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class C3P0Demo {
    public static void main(String[] args) throws SQLException {
        //1.创建数据库连接池对象,使用默认配置,还可以指定名称配置
        DataSource dataSource = new ComboPooledDataSource();
        /*
            如果使用指定名称的配置,则可以进行如下操作
            DataSource dataSource = new ComboPooledDataSource("oteherc3p0");
        */
        //2.获取连接对象
        Connection connection = dataSource.getConnection();
        //3.打印,如果能打印出地址值则代表获取成功
        System.out.println(connection);
    }
}

--------------------------------------------------
//输出结果
com.mchange.v2.c3p0.impl.NewProxyConnection@1e802ef9 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@2b6faea6]

2. Druid

2.1 使用步骤:

  1. 导入jar包 druid-1.0.9.jar
  2. 定义配置文件:
    • 这个配置文件是properties形式的
    • 可以叫任意名称,可以放在任意目录下,需要我们手动导入
  3. 加载配置文件
  4. 获取数据库连接池对象:通过工厂来获取:DruidDatasourceFactory
  5. 获取连接:getConnection

2.2 配置文件详解

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/da3?useSSL=false&serverTimezone=UTC
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000

2.3 代码测试

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

public class DruidDemo {
    public static void main(String[] args) throws Exception {
        //1.导入jar包
        //2.定义配置文件
        //3.加载配置文件
        ClassLoader classLoader = DruidDemo.class.getClassLoader();
        Properties properties = new Properties();
        InputStream resourceAsStream = classLoader.getResourceAsStream("druid.properties");
        properties.load(resourceAsStream);
        //4.获取数据库连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //5.获取连接
        Connection connection = dataSource.getConnection();
        //6.打印,如果能获取到地址,则代表获取连接成功
        System.out.println(connection);
    }
}

2.4 定义工具类

  • JDBCUtils.java
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {
    private static DataSource dataSource;
    static {
        try {
            //1.加载配置文件
            Properties properties = new Properties();
            properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            //获取DataSource
            dataSource = DruidDataSourceFactory.createDataSource(properties);

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

    //获取连接的方法
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    //三个参数的释放资源(归还连接)方法
    public static void close(ResultSet resultSet, Statement statement, Connection connection){
        if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    //两个参数的释放资源(归还连接)方法
    public static void close(Statement statement, Connection connection){
        close(null, statement, connection);
    }

    //获取连接池的方法
    public static DataSource getDataSource(){
        return  dataSource;
    }
}
  • 测试工具类
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class utils_test {
    public static void main(String[] args) {
        //完成一个添加数据的操作

        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            //1.获取连接
            connection = JDBCUtils.getConnection();
            //2.定义sql
            String sql = "insert into account values(null, ?, ?)";
            //3.获取PreparedStatement对象
            preparedStatement = connection.prepareStatement(sql);
            //4.给?赋值
            preparedStatement.setString(1, "wangwu");
            preparedStatement.setInt(2, 3000);
            //5.执行sql
            int count = preparedStatement.executeUpdate();
            System.out.println(count);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //6.释放资源(归还连接)
            JDBCUtils.close(preparedStatement, connection);
        }
    }
}

Spring JDBC

Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发。

1.使用步骤

  1. 导入jar包
  2. 创建JDBCTemplate对象(依赖于数据源DataSource)
    • JDBCTemplate t = new JDBCTemplate(ds);
  3. 调用JDBCTemplate的相关方法来完成CURD的操作
    • update():执行DML语句(增删改语句)
    • queryForMap():查询结果,将结果集风封装为map集合,将列名作为key,将值作为value,将这条记录封装为一个map集合
      • 注意:这个方法的查询长度只能是1
    • queryForList():查询结果,将结果集风封装为list集合
      • 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
    • query():查询结果,将结果封装为JavaBean对象
      • query的参数
        • 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
        • new BeanPropertyRowMapper<类型名>(类型名.class)
    • queryForObject():查询结果,将结果集风封装为对象
      • 一般用于聚合函数的查询

2.入门

这里我们更新da3数据库中的account表中的一条数据

import org.springframework.jdbc.core.JdbcTemplate;
import utils.JDBCUtils;

import javax.sql.DataSource;

public class Temp_test {
    public static void main(String[] args) {
        //获取连接池
        DataSource dataSource = JDBCUtils.getDataSource();
        //创建JdbcTemplate对象
        JdbcTemplate template = new JdbcTemplate(dataSource);
        //定义sql语句
        String sql = "update account set balance = 60000 where id = ?";
        //执行sql,第二个参数为占位符
        int updateLineNum = template.update(sql, 2);//影响的行数
        System.out.println(updateLineNum);
    }
}

3.练习

使用da3数据库中的emp表。

需求:

  1. 修改1号数据的salary为10000
  2. 添加一条记录
  3. 删除刚才添加的记录
  4. 查询id为1的记录,将其封装为Map集合
  5. 查询所有记录,将其封装为List
  6. 查询所有记录,将其封装为Emp对象的List集合
  7. 查询总记录数

代码实现:

import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import utils.JDBCUtils;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class Emp_Test {
    //获取JdbcTemplate对象
    JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());

    //1.修改1号数据的salary为10000
    @Test
    public void test1(){
        //定义sql语句
        String sql = "update emp set salary = ? where id = ?";
        //执行sql
        int update = template.update(sql, 10000, 1001);
        //打印执行结果
        System.out.println(update);
    }

    //2.添加一条记录
    @Test
    public void test2(){
        //定义sql语句
        String sql = "insert into emp(id, ename) values(?, ?) ";
        //执行sql
        int update = template.update(sql, 1015, "哈哈哈");
        //打印执行结果
        System.out.println(update);
    }

    //3.删除刚才添加的记录
    @Test
    public void test3(){
        //定义sql语句
        String sql = "delete from emp where id = ?";
        //执行sql
        int update = template.update(sql, 1015);
        //打印执行结果
        System.out.println(update);
    }

    //4.查询id为1001的记录,将其封装为Map集合
    @Test
    public void test4(){
        //定义sql语句
        String sql = "select * from emp where id = ?";
        //执行sql
        Map<String, Object> stringObjectMap = template.queryForMap(sql, 1001);
        //打印执行结果
        System.out.println(stringObjectMap);
    }

    //5.查询所有记录,将其封装为Map集合
    @Test
    public void test5(){
        //定义sql语句
        String sql = "select * from emp";
        //执行sql
        List<Map<String, Object>> maps = template.queryForList(sql);
        //打印执行结果
        for(Map<String, Object> m : maps){
            System.out.println(m);
        }
    }

    //6_1.查询所有记录,将其封装为Emp对象的List集合
    @Test
    public void test6_1(){
        //定义sql语句
        String sql = "select * from emp";
        //执行sql
        List<Emp> query = template.query(sql, new RowMapper<Emp>() {
            @Override
            public Emp mapRow(ResultSet resultSet, int i) throws SQLException {
                int id = resultSet.getInt("id");
                String ename = resultSet.getString("ename");
                int job_id = resultSet.getInt("job_id");
                int mgr = resultSet.getInt("mgr");
                Date joindate = resultSet.getDate("joindate");
                double salary = resultSet.getDouble("salary");
                double bonus = resultSet.getDouble("bonus");
                int dept_id = resultSet.getInt("dept_id");

                Emp emp = new Emp();
                emp.setId(id);
                emp.setEname(ename);
                emp.setJob_id(job_id);
                emp.setMgr(mgr);
                emp.setJoindate(joindate);
                emp.setSalary(salary);
                emp.setBonus(bonus);
                emp.setDept_id(dept_id);

                return emp;
            }
        });
        //打印执行结果
        for(Emp m : query){
            System.out.println(m);
        }
    }

    //6_1.查询所有记录,将其封装为Emp对象的List集合
    @Test
    public void test6_2(){
        //定义sql语句
        String sql = "select * from emp";
        //执行sql
        List<Emp> query = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
        //打印执行结果
        for(Emp m : query){
            System.out.println(m);
        }
    }

    //7.查询所有记录,将其封装为Emp对象的List集合
    @Test
    public void test7(){
        //定义sql语句
        String sql = "select count(id) from emp";
        //执行sql
        Integer integer = template.queryForObject(sql, Integer.class);
        //打印执行结果
        System.out.println(integer);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值