day29_JDBC

百般思念 提交于 2020-02-14 08:58:41

JDBC

概念

Java DataBase Connectivity  Java 数据库连接, Java语言操作数据库。JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。JDBC 规范定义接口,具体的实现由各大数据库厂商来实现。JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。

JDBC 的好处:
  • 程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
  • 使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库

使用 JDBC 开发使用到的包: 

JDBC 的核心 API

JDBC 访问数据库的步骤

1:注册和加载驱动

导入驱动 Jar 包

2:获取连接使用DriverManager类中的getConnection方法,获取连接对象

3:Connection连接对象 获取 Statement/getConnection 执行SQL 对象

4:使用Statement/getConnection 执行SQL 对象执行 SQL 语句

5:返回结果集

6: 释放资源

详解JDBC核心API

DriverManager类:驱动管理对象

DriverManager 作用
  • 管理和注册驱动 告诉程序该使用哪一个数据库驱动jar

详解:

我们通过下面语句注册驱动

查看com.mysql.jdbc.Driver 类 源代码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.mysql.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;
// 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!");
        }
    }
}

结论:

我们写代码使用:  Class.forName("com.mysql.jdbc.Driver");注册驱动。底层是通过静态代码块调用 DriverManager 类中的static void registerDriver(Driver driver) 方法加载驱动。从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。Class.forName 这句话可以省略。不建议省略,因为不会向下兼容。

  • 创建数据库的连接
我们通过DriverManager类中的方法获取数据库连接,具体如下:
使用 JDBC 连接数据库的四个参数:

 

连接数据库的 URL 地址格式:
  • 协议名:子协议://服务器名或 IP 地址:端口号/数据库名?参数=参数值
MySQL 写法:

小技巧:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称

示例:

package jdbc;

import java.sql.Connection;
import java.sql.DriverManager;

public class Demo2 {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql://localhost:3306/day24";
        //1) 使用用户名、密码、URL 得到连接对象
        Connection connection = DriverManager.getConnection(url, "root", "root");
        //com.mysql.jdbc.JDBC4Connection@68de145
        System.out.println(connection);
    }
}

Connection接口:数据库连接对象

Connection 作用:Connection 接口,具体的实现类由数据库的厂商实现,代表一个连接对象。
获取执行sql 的对象的方法:
  • Statement createStatement() 
  • PreparedStatement prepareStatement(String sql)

管理事务:

  • 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
  • 提交事务:commit()
  • 回滚事务:rollback()

Statement接口:执行sql的对象

Statement 作用:代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
不常用方法:
  • boolean execute(String sql) :可以执行任意的sql 

常用方法

需求

执行DDL语句,创建一个student表

代码实现

package jdbc;


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

/**
 * 执行DDL语句,创建一个student表
 */
public class Demo1 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        try {
            //1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///one", "root", "root");
            //3.定义sql
            String sql = "create table student (id int , name varchar(20))";
            //4.获取执行sql对象
            stmt = conn.createStatement();
            //5.执行sql
            int count = stmt.executeUpdate(sql);
            //6.处理结果
            System.out.println(count);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //7.释放资源

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

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

}

student表 添加一条记录 insert 语句

package jdbc;


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

/**
 * student 表 添加一条记录 insert 语句
 */
public class Demo2 {
    public static void main(String[] args) {
        Statement stmt = null;
        Connection conn = null;
        try {
            //1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2. 定义sql
            String sql = "insert into student values(null,'王五')";
            //3.获取Connection对象
            conn = DriverManager.getConnection("jdbc:mysql:///one", "root", "root");
            //4.获取执行sql的对象 Statement
            stmt = conn.createStatement();
            //5.执行sql
            int count = stmt.executeUpdate(sql);//影响的行数
            //6.处理结果
            System.out.println(count);//1
            if (count > 0) {
                System.out.println("添加成功!");
            } else {
                System.out.println("添加失败!");//添加成功!
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //stmt.close();
            //7. 释放资源
            //避免空指针异常
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

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


    }
}

ResultSet:结果集对象

作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。
接口中的方法:

常用数据类型转换表

java.sql.Date、Time、Timestamp(时间戳),三个共同父类是:java.util.Date

ResultSet 接口中的注意事项:
  • 如果光标在第一行之前,使用 rs.getXX()获取列值,报错:Before start of result set
  • 如果光标在最后一行之后,使用 rs.getXX()获取列值,报错:After end of result set
  • 使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection

需求

查询student类中的所有数据,并且对结果集遍历

package jdbc;


import java.sql.*;

/**
 * 执行DDL语句
 */
public class Demo3 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            //1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///one", "root", "root");
            //3.定义sql
            String sql = "select * from student";
            //4.获取执行sql对象
            stmt = conn.createStatement();
            //5.执行sql
            rs = stmt.executeQuery(sql);
            //6.处理结果
            //循环判断游标是否是最后一行末尾。
            while (rs.next()) {

                //获取数据
                //6.2 获取数据
                int id = rs.getInt("id");
                String name = rs.getString("name");
                System.out.println(id + "---" + name);//0---王五
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //7.释放资源

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

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

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

}

通过上述代码,我们发现冗余的代码太多怎么办?解决办法:定义工具类

数据库工具类 JdbcUtils

如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用。上面写的代码中出现了很多重复的代码,可以把这些公共代码抽取出来。

创建类 JdbcUtil 包含 3 个方法:

  • 可以把几个字符串定义成常量:用户名,密码,URL,驱动类(写成配置文件)
  • 得到数据库的连接:getConnection() (通过读取配置文件,与不同的数据库连接)
  • 关闭所有打开的资源:

步骤1:

在src文件下 新建配置文件,输入配置信息如下:

url=jdbc:mysql:///数据库名称
user=root 
password=root
driver=com.mysql.jdbc.Driver

步骤2:

新建一个util包,存放我们写的配置JDBCUtils类,代码如下:

package util;


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 driver;

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

        try {
            //1. 创建Properties集合类。
            Properties pro = new Properties();

            //获取src路径下的文件的方式--->ClassLoader 类加载器
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();
            URL res = classLoader.getResource("jdbc.properties");
            String path = res.getPath();
            // System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties
            //2. 加载文件
            // pro.load(new FileReader("D:\\IdeaProjects\\itcast\\day04_jdbc\\src\\jdbc.properties"));
            pro.load(new FileReader(path));

            //3. 获取数据,赋值
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
            //4. 注册驱动
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取连接
     *
     * @return 连接对象
     */
    public static Connection getConnection() throws SQLException {

        return DriverManager.getConnection(url, user, password);
    }

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

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


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

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

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

}

需求

1. 通过键盘录入用户名和密码,判断用户是否登录成功

MySQL数据库中准备数据

 2.编写登录类

package jdbc;

import util.JDBCUtils;

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

public class Demo4 {


    /**
     * 练习:
     * * 需求:
     * 1. 通过键盘录入用户名和密码
     * 2. 判断用户是否登录成功
     */
    public static void main(String[] args) {
        //1.键盘录入,接受用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        //2.调用方法
        boolean flag = new Demo4().login(username, password);
        //3.判断结果,输出不同语句
        if (flag) {
            //登录成功
            System.out.println("登录成功!");
        } else {
            System.out.println("用户名或密码错误!");
        }

    }

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

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(rs, stmt, conn);
        }

        return false;
    }


}

SQL 注入问题 

我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进行简单的字符串拼接。
解决sql注入问题方法:
  • 使用PreparedStatement对象来解决

PreparedStatement 接口 

PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句。因为有预先编译的功能,提高 SQL 的执行效率。 可以有效的防止 SQL 注入的问题,安全性更高。

Connection 创建 PreparedStatement 对象

PreparedStatement 接口中的方法: 

reparedSatement 的好处
  1. prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
  2. 安全性更高,没有 SQL 注入的隐患。
  3. 提高了程序的可读性

使用 PreparedStatement 的步骤:

  1. 编写 SQL 语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND password=?";
  2. 获得 PreparedStatement 对象
  3. 设置实际参数:setXxx(占位符的位置, 真实的值)
  4. 执行参数化 SQL 语句
  5. 关闭资源

使用 PreparedStatement 改写上面的登录程序,解决 SQL 注入的情况 
package jdbc;

import util.JDBCUtils;

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

public class Demo4 {

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

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

    }

    /**
     * 登录方法,使用PreparedStatement实现
     */
    public boolean login2(String username, String password) {
        if (username == null || password == null) {
            return false;
        }
        //连接数据库判断是否登录成功
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        //1.获取连接
        try {
            conn = JDBCUtils.getConnection();
            //2.定义sql
            String sql = "select * from user where name = ? and password = ?";
            //3.获取执行sql的对象
            pstmt = conn.prepareStatement(sql);
            //给?赋值
            pstmt.setString(1, username);
            pstmt.setString(2, password);
            //4.执行查询,不需要传递sql
            rs = pstmt.executeQuery();
            //5.判断

            return rs.next();//如果有下一行,则返回true

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(rs, pstmt, conn);
        }

        return false;
    }

}

JDBC控制事务:

事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

操作:

  • 开启事务
  • 提交事务
  • 回滚事务

使用Connection对象来管理事务

  • 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务我们一般在执行sql之前开启事务。
  • 提交事务:commit()  我们一般当所有sql都执行完提交事务
  • 回滚事务:rollback()  我们一般在catch中回滚事务

 



 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!