JDBC-Java DateBase Connectivity(Java 数据库连接)

  1. 概念:Java语言操作数据库

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

    • 步骤:

      1. 导入驱动jar包 mysql-connector-java-8.0.26.jar
        1. 复制mysql-connector-java-8.0.26.jar到项目的libs目录下
        2. 右键–>Add as Library
      2. 注册驱动
      3. 获取数据库连接对象 Connection
      4. 定义sql
      5. 获取执行sql语句的对象 Statement
      6. 执行sql,接受返回结果
      7. 处理结果
      8. 释放资源
      //1. 导入驱动jar包
              //2. 注册驱动
              Class.forName("com.mysql.cj.jdbc.Driver");
              //3. 获取数据库连接对象
              Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "qin");
              //4. 定义sql语句
              String sql="update emp set salary = 1 where dept_id=1";
              //5. 获取执行sql的对象 Statement
              Statement stmt =con.createStatement();
              //6. 执行sql
              int c=stmt.executeUpdate(sql);
              //7. 执行结果
              System.out.println(c);
              //8. 释放资源
              stmt.close();
              con.close();
      
  3. 详解各个对象

    1. DriverManager对象:驱动管理对象

      • 功能:

        1. 注册驱动:告诉程序该使用哪一个数据库驱动jar

          static void registerDriver(Driver driver):注册与给定的驱动程序DriverManager;

          写代码使用:Class.forName(“com.mysql.cj.jdbc.Driver”);ctrl+n:直接跳到相应的类

          通过查看源码发现:在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(Strng url, String user, String password);
          • 参数:
            • url:指定连接的路径
              • 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
              • 例子:jdbc:mysql://localhost:3306/db1
              • 细节:如果连接的是本机的mysql服务器,并且mysql服务默认的端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
            • user:用户名
            • password:密码
    2. Connection对象:数据库连接对象

      • 功能:
        1. 获取执行mysql的对象
          • Statement createStatement()
          • PreparedStatement prepareStatement(String sql)
        2. 管理事务:
          • 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
          • 提交事务:commit()
          • 回滚事务:rollback()
    3. Statement对象:执行sql的对象

      1. 执行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)语句
      2. 练习:

        1. 表 添加一条记录
        2. 表 修改记录
        3. 表 删除一条记录
        Statement stm=null;
                Connection con =null;
                try {
                 
                    //1. 注册驱动
                    Class.forName("com.mysql.cj.jdbc.Driver");
                    //2. 定义sql
                    String sql="insert into emp values(7,'王五','男',9999,'2001-08-08',2)";
                    //3. 获取Connection对象
                    con = DriverManager.getConnection("jdbc:mysql:///db1", "root", "qin");
                    //4. 获取执行sql的对象Statement
                    stm = con.createStatement();
                    //5. 执行sql
                    int account=stm.executeUpdate(sql);//影响的行数
                    //6. 处理结果
                    System.out.println(account);
                    if (account>0){
                 
                        System.out.println("添加成功!");
                    }else{
                 
                        System.out.println("添加失败!");
                    }
        
                } catch (ClassNotFoundException | SQLException e) {
                 
                    e.printStackTrace();
                }finally {
                 
                    //7. 释放资源
                    //避免空指针异常
                    if (stm != null){
                 
                        try {
                 
                            stm.close();
                        } catch (SQLException e) {
                 
                            e.printStackTrace();
                        }
                    }
                    if (con != null){
                 
                        try {
                 
                            con.close();
                        } catch (SQLException e) {
                 
                            e.printStackTrace();
                        }
                    }
        
                }
        
    4. ResultSet对象:结果集对象,封装查询结果的对象

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

      • getXxx(参数):获取数据

        • Xxx:代表数据类型 如:int getInt();
        • 参数:
          1. int:代表列的编号,从1开始的,如:getString(1)
          2. String:代表列的名称。如:getDouble(“balance”);
      • 注意:

        • 使用步骤:

          1. 游标向下移动一行
          2. 判断是否有数据
          3. 获取数据
          
          // 循环获取
          while(res.next()) {
                     
              //2. 获取数据
              int id = res.getInt(1);
              String name = res.getString("name1");
              double salary = res.getDouble("salary");
              System.out.println(id + "---" + name + "---" + salary);
          }
          
          
      • 练习:

        • 查询emp表的数据将其封装为对象,然后装载集合,返回。
          1. 定义Emp的类
          2. 定义方法 public List<Emp> findAll(){}
          3. 实现方法 select * from emp;
    5. PreparedStatement对象:执行sql的对象

      1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,会造成安全问题
        1. 输入用户随便,输入密码:a’ or ‘a’=‘a’
        2. sql:select * from user where username = ‘failjdd’ and password = ‘a’ or ‘a’=‘a’;
      2. 解决sql注入问题:使用PreparedStatement对象来解决
      3. 预编译的sql:参数使用”?“作为占位符
      4. 步骤:
        1. 导入驱动jar包
        2. 注册驱动
        3. 获取数据库连接对象 Connction
        4. 定义sql
          • 注意:sql的参数使用?作为占位符。如:select * from user where username = ? and password = ?;
        5. 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
        6. 给?赋值:
          • 方法:setXxx(参数1(问号位置编号1从开始),参数2(问号值));
        7. 执行sql,接收返回结果,不需要传递sql语句
        8. 处理结果
        9. 释放资源
      5. 注意:后期都会使用PreparedStatement来完成增删改查的所有操作
        1. 可以防止SQL注入
        2. 效率高

例子:

package cn.itcast.jdbc;

import cn.itcast.util.JDBCUtils;

import java.sql.*;
import java.util.Scanner;

/**
 * 需求:
 * 1. 通过键盘录入用户名和密码
 * 2. 判断用户是否登录成功
 */
public class JBDCDemo9 {

    public static void main(String[] args) {
        //1. 键盘录入,接收用户名和密码
        Scanner r = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = r.nextLine();
        System.out.println("请输入密码:");
        String password=r.nextLine();
        //2. 调用方法
        boolean f = new JBDCDemo9().login2(username, password);
        //3. 判断结果,输出不同语句
        System.out.println(username);
        if (f){
            //成功
            System.out.println("登录成功!");
        }else{
            System.out.println("登录失败!");
        }
    }

    /**
     * 登录方法
     * @return返回成功或者失败
     */
    /*
    public boolean login(String username,String password){
        if (username==null||password==null){
            return false;
        }
        //连接数据库,判断是否登录成功
        Connection con=null;
        Statement stmt=null;
        ResultSet resultSet=null;
        //1. 获取连接
        try {
            con= JDBCUtils.getConnection();
            //2. 定义sql
            String sql="select * from user where username = '"+username+"' and password = '"+password+"'";
            //3.获取执行sql的对象
            stmt=con.createStatement();
            //4. 执行查询
            resultSet = stmt.executeQuery(sql);
            //5. 判断结果集是否有数据
//            if (resultSet.next()){//如果有下一行
//                return true;
//            }else{
//                return false;
//            }
            return resultSet.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(resultSet,stmt,con);
        }


        return true;
    }
*/
    /**
     * 登录方法,使用PreparedStatement实现
     * @return返回成功或者失败
     */
    public boolean login2(String username,String password){
        if (username==null||password==null){
            return false;
        }
        //连接数据库,判断是否登录成功
        Connection con=null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        //1. 获取连接
        try {
            con= JDBCUtils.getConnection();
            //2. 定义sql
            String sql="select * from user where username = ? and password = ?";
            //3.获取执行sql的对象
            preparedStatement = con.prepareStatement(sql);
            //给?问号赋值
            preparedStatement.setString(1,username);
            preparedStatement.setString(2,password);
            //4. 执行查询,不需要传递sql
            resultSet = preparedStatement.executeQuery();
            //5. 判断结果集是否有数据
//            if (resultSet.next()){//如果有下一行
//                return true;
//            }else{
//                return false;
//            }
            return resultSet.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(resultSet,preparedStatement,con);
        }


        return true;
    }
}

抽取JDBC工具类:JDBCUtils

  • 目的:简化书写

  • 分析:

    1. 注册驱动也抽取

    2. 抽取一个方法获取连接对象

      • 需求:不想传递参数(麻烦),还得保证工具类的通用性。

      • 解决方案:配置文件(配置文件一定要在src下面,不能套一层文件夹!!!血流量教训)

        jdbc.properties

        url=

        user=

        password=

    3. 抽取一个方法释放资源

  • 代码实现:

package cn.itcast.util;

import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;

/** * JDBC工具类 */
public class JDBCUtils {
   

    private static String url;
    private static String user;
    private static String password;
    private static String driver1;

    static{
   
        //读取资源文件,获取值
          try {
   
            //1. 创建Properties集合类
            Properties pro = new Properties();

            //获取src路径下的文件的方式--->ClassLoader类加载器
            ClassLoader classLoader=JDBCUtils.class.getClassLoader();
            URL re = classLoader.getResource("jdbc.properties");
            String path = re.getPath();
            System.out.println(path);

            //2. 加载文件
            pro.load(new FileInputStream(path));
// pro.load(JDBCUtils.class.getResourceAsStream("/jdbc.properties"));
            //3. 获取数据
            url=pro.getProperty("url");
            user=pro.getProperty("user");
            password=pro.getProperty("password");
            driver1 = pro.getProperty("driver");
            //4. 注册驱动
            Class.forName(driver1);
        } catch (IOException | ClassNotFoundException e) {
   
            e.printStackTrace();
        }


    }


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

    /** * 释放资源 */
    public static void close(ResultSet res,Statement stmt, Connection con){
   
        if (stmt!=null){
   
            try {
   
                stmt.close();
            } catch (SQLException e) {
   
                e.printStackTrace();
            }
        }

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

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

    }


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

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

    }


}
  • 练习:

    • 需求:

      1. 通过键盘录入用户名和密码
      2. 判断用户是否登录成功
        • select * from user where username = “” and password = “”;
        • 如果这个sql有查询结果则成功,否则失败
    • 步骤:

      1. 创建数据库表 user

        CREATE TABLE USER(
        	id INT PRIMARY KEY AUTO_INCREMENT,
        	username VARCHAR(32),
        	PASSWORD VARCHAR(32)
        );
        
        SELECT * FROM USER;
        
        INSERT INTO USER VALUES(NULL,'zhnagsan','123');
        INSERT INTO USER VALUES(NULL,'lisi','234');
        
        
        

JDBC控制事务

  1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
  2. 操作
    1. 开启事务
    2. 提交事务
    3. 回滚事务
  3. 使用Connction对象来管理事务
    • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
      • 执行sql之前开启事务
    • 提交事务:commit()
      • 所有sql都执行完提交事务
    • 回滚事务:rollback()
      • catch中回滚事务