JDBC学习1

一.JDBC基本概念

​ JDBC,全拼为:Java DataBase Connectivity ,是一种Java程序访问数据库的标准接口。

​ 当使用Java程序访问数据库时,Java代码并不是直接就去操作数据库的,而是通过JDBC接口去访问,JDBC接口再通过JDBC驱动来实现真正的数据库访问。

​ 在Java中,JDBC接口是JDK自带的,可以直接调用,但是具体的JDBC驱动是根据不同的数据库厂商来提供的,其实也是java语言编写的,是一个jar包,在我们使用的时候导入相关的数据库驱动jar包就好了。(目的就在于同一套Java代码处理访问数据库的代码,但可以访问各种不同的数据库,根据数据库驱动去实现)

​ Java与数据库驱动和数据库之间的关系,如下:

JDBC.png

好处:

  1. 使用同一套java代码,针对不同的数据库,不需要重新开发。
  2. 不受限于底层数据库,即使换一个其他数据库,java代码基本不变。

总结:

​ 在开发时,只需完成对JDBC接口的调用,底层的数据库具体操作不需要我们去实现,由驱动完成。

二.JDBC开发主要步骤

先记录开发步骤,后面再详细学习各个对象。

  1. 导入驱动jar抱

    比如mysql,需要导入mysql-connector-java-5.1.37-bin.jar

    在idea中的导入的步骤:

    • 复制jar包到项目的lib文件夹下
    • 右键lib文件夹 --> Add As Library
  2. 注册驱动

  3. 获取数据库连接对象 Connection

  4. 定义sql语句

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

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

  7. 处理结果

  8. 释放资源

有两种执行sql语句的对象:Statement和PreparedStatement版本(以后都是用PreparedStatement,区别下面再解释)

Statement版本:

public class JDBCDemo2 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement state = null;
        try {
            // 1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 获取数据库连接对象Conn
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");
            // 3. 定义sql语句
            String sql = "insert into student3 values(15,'亚瑟',28,'男','深圳',80,85)";
            // 4. 获取执行sql对象
            state = conn.createStatement();
            // 5. 执行sql
            int count = state.executeUpdate(sql);
            if (count > 0) {
                System.out.println("添加成功");
            } else {
                System.out.println("执行失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6. 释放资源
            if (state != null) {
                try {
                    state.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

PreparedStatement版本

public class JDBCDemo12 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstate = null;
        ResultSet rs = null;

        try {
            // 1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 获取数据库连接对象Connection
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");
            // 3. 定义sql语句
            String sql = "select * from user where id = ?";
            // 4. 获取sql执行对象
            pstate = conn.prepareStatement(sql);
            pstate.setInt(1,2);
            // 5. 执行sql
            rs = pstate.executeQuery();
            // 6. 处理结果
            if (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String password = rs.getString("password");

                System.out.println("id = " + id + ",name = " + name + ",password = " + password);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 7.释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (pstate != null) {
                try {
                    pstate.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

三.JDBC各个对象的详解

  • DriverManager:驱动管理对象

    作用:

    1. 用于管理和注册数据库驱动。

      使用方法:Class.forName("com.mysql.jdbc.Driver")

      查看Driver源码,发现在com.mysql.jdbc.Driver类中存在静态代码块

      public class Driver extends NonRegisteringDriver implements java.sql.Driver {
          public Driver() throws SQLException {
          }
      
          static {
              try {
                  DriverManager.registerDriver(new Driver());
              } catch (SQLException var1) {
                  throw new RuntimeException("Can't register driver!");
              }
          }
      }

      代码块里调用了DriverManager.registerDriver(new Driver())去注册给定的驱动程序

      注:mysql5之后可以省略注册驱动的步骤。也就是可以省略Class.forName("com.mysql.jdbc.Driver")

    2. 获取数据库连接对象。

      代码中使用方式:
      conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");

      使用方法:

      static Connection getConnection(String url, String user, String password):通过连接字符串,用户名和密码来获取数据库的连接对象。

      参数:

      • url:指定连接的路径,不同的数据库url是不同的。

        mysql的是:dbc:mysql://localhost:3306/数据库名字

      • user:数据库用户名

      • password:数据库密码

  • Connection:数据库连接对象

    Connection接口的具体实现是由数据库厂商实现的,代表了一个连接对象。

    作用:

    1. 获取执行sql的对象

      • Statement createStatement()
      • PreparedStatement prepareStatement(String sql)
    2. 管理事务

      事务管理就是指针对一系列的操作,这些操作要么同时成功,要么同时失败。

      • setAutoCommit(boolean autoCommit):开启事务,设置参数为false,即开启事务
      • commit():提交事务
      • rollback():回滚事务
  • Statement:执行sql语句的对象

    Statement是一个sql语句对象,用于发送sql给服务器,执行静态sql语句,并返回相关的结果对象。

    作用:

    1. 执行sql
      • int executeUpdate(String sql):用于执行增删改的操作(insert,update,delete),返回值是对数据库影响的行数。
      • ResultSet executeQuery(String sql):用于执行查询的操作(select),返回值为一个ResultSet结果集。
  • ResultSet:查询结果对象,封装了查询结果

    ResultSet是执行了查询操作executeQuery(String sql)后返回的一个对象,封装的是数据库返回的数据,把每一条记录封装成一个ResultSet对象。

    ResultSet提供了一些方法方便获取具体的数据:

    • getXxxx(参数):获取数据。

      提供了一系列的方法来获取不同类型的数据,比如:int getInt()String getString()

      数据类型依据于要获取的数据的那一列的数据类型。

      这个方法有两种传参方式:一种是传递int值,代表编号(编号从1开始),一种是传递String,代表列名称。

      比如:

      String getSrting(int columnIndex):传递参数为列的编号,从1开始的。

      String getString(String columnIndex):传递参数为列的名称。

    • boolean next():游标向下移动一行,判断当前行是否是最后一行(是否有数据),如果是最后一行,没有数据,则返回false,否则就返回true。

    基于ResultSet提供的方法,可以通过以下操作获取数据库返回的所有查询结果:

    // 循环判断是不是最后一行,如果是最后一行会返回false
    while (rs.next()) {
        // 获取这一条记录的每一列的数据
        int id = rs.getInt("id");
        String name = rs.getString(2);
        ...
    }
  • PreparedStatement:执行sql语句的对象

    1. 使用Statement存在问题:SQL注入问题

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

      举例:让用户输入用户名和密码,正确即可登录数据库。

      public class JDBCDemo7 {
          public static void main(String[] args) {
              String name;
              String pw;
              Connection conn = null;
              Statement state = null;
              ResultSet rs = null;
      
              // 1. 获取用户输入
              Scanner sc = new Scanner(System.in);
              System.out.print("请输入用户名:");
              name = sc.nextLine();
              System.out.print("请输入密码:");
              pw = sc.nextLine();
      
              // 2. 连接数据库
              try {
                  conn = JDBCUtil.getConnection();
                  // 定义sql
                  String sql = "select * from user where name='" + name + "'and password='" + pw + "'";
                  state = conn.createStatement();
                  rs = state.executeQuery(sql);
                  if (rs.next()) {
                      System.out.println("登录成功");
                  } else {
                      System.out.println("登录失败");
                  }
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  JDBCUtil.close(rs,state,conn);
              }
          }
      }

      当输入随意用户名,密码为:a' or 'a' = 'a时,会发现居然登录成功了!!!

      PreparedStatement.png

      这就是Statement带来的一个问题。

    2. 解决方法就是使用PreparedStatement(以后都是使用PreparedStatement来完成操作)

    3. PreparedStatement使用方法

      PreparedStatement使用?作为占位符,然后再调用方法填充数据,避免了字符串拼接的方式

      具体步骤是这样的(前面几个步骤和之前一样,这里从定义sql开始)

      • 定义sql,使用?作为占位符。

        比如:String sql = select * from user where username = ? and password = ?;

      • 获取执行sql的对象PreparedStatement

        pstate = conn.prepareStatement(sql);

      • 给sql语句的占位符?赋值

        使用PreparedStatement提供的方法:

        setXxxx(参数1,参数2):这个方法和ResultSet中的方法类似,也是根据数据类型调用对应的方法。

        • 参数1:占位符?的位置编号,从1开始。
        • 参数2:占位符?的值。

        比如:pstate.setInt(1,2);

      • 执行sql,获取返回结果,不需要再传递sql了(在获取PreparedStatement对象时候已经传递了)

        rs = pstate.executeQuery();

      • 处理结果

      • 释放资源

四.实现自己的JDBC工具类:JDBCUtils

​ 在真正编写代码之后,我们发现每一次操作数据库,都要经历上述的至少7,8个步骤,步骤很繁琐,而且重复度很高,所以为了方便使用以及简化书写,可以把JDBC的相关操作抽取出来,作为一个工具类,提供相关方法供调用。

​ 步骤:

  1. 整合注册驱动部分,驱动的注册只需要注册一次即可,不需要每一次都注册。

    可以使用静态代码块,在第一次加载类时调用一次。

  2. 整合一个获取连接对象的方法

    数据库用户名,密码等信息可以通过配置文件获取,使用Properties集合类。

  3. 整合一个释放资源的方法

    针对不同操作,查询操作需要释放ResultSet,可以使用重载方法的方式提供同名的方法。

public class JDBCUtil {
    private static String url;
    private static String root;
    private static String password;
    private static String driver;

    /*配置文件的读取,只需要读取一次即可,使用静态代码块*/
    static {
        // 读取资源文件,获取值

        // 1. 创建Properties集合类
        Properties pro = new Properties();
        // 获取src路径下的文件的方式 -- ClassLoader 类加载器
        ClassLoader classLoader = JDBCUtil.class.getClassLoader();
        URL res = classLoader.getResource("jdbc.properties");
        String path = res.getPath();
        // 2. 加载文件
        try {
            pro.load(new FileInputStream(path));
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 3. 获取数据,赋值
        url = pro.getProperty("url");
        root = pro.getProperty("user");
        password = pro.getProperty("password");
        driver = pro.getProperty("driver");
        // 4. 注册驱动
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /* 获取连接对象 */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, root, password);
    }

    public static void close(ResultSet rs, Statement state, Connection conn) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (state != null) {
            try {
                state.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Statement state, Connection conn) {
        if (state != null) {
            try {
                state.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/LucasBlog/p/12382535.html