Java的数据库连接接口:JDBC

JDBC简介

  • 概念

    • Java DataBase Connectivity,Java 数据库连接。

    • JDBC是sun公司定义的一套操作所有关系型数据库的规则,即接口。

    • 各个数据库厂商去编写这套接口的实现类,包装成数据库驱动jar包。通过导入不同数据库厂商编写的数据库驱动jar包,来对JBDC接口进行实现。

快速入门

  • 导入MySQL数据库驱动jar包
    • mysql-connector-java-5.1.37-bin.jar
      • 复制到项目的libs目录下
      • 右键 --> Add As Library
  • 注册驱动 driver
    • “com.mysql.jdbc.Driver”
  • 获取数据库连接对象 Connection
    • 第一个参数:URL(jdbc:mysql://连接IP地址:端口号/数据库名)
    • 第二个参数:数据库账号
    • 第三个参数:数据库密码
  • 定义SQL语句
  • 获取执行SQL语句的对象 Statement
  • 执行SQL语句,接受返回结果
  • 处理结果
  • 释放资源
# 创建数据库
CREATE DATABASE jdbc;
# 使用数据库
USE jdbc;
# 创建表
CREATE TABLE account(
	id INT PRIMARY KEY  AUTO_INCREMENT ,
	NAME VARCHAR(20),
	balance INT
);
# 插入数据
INSERT INTO account VALUES(1,'Cat',1000),(2,'Dog',1000);
//1.导入驱动jar包
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接对象Connection
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "root");
//4.定义SQL语句
String s = "UPDATE account SET balance = 1500 WHERE id =1";
//5.获取执行SQL的对象Statement
Statement statement = connection.createStatement();
//6.执行SQL
int count = statement.executeUpdate(s);
//7.处理结果
System.out.println(count);
//8.释放资源
statement.close();
connection.close();

详解对象

DriverManager

  • DriverManager驱动管理对象:用于管理一组JDBC驱动程序的基本服务。
    • 新增的DataSource接口提供了另一种连接到数据源的方法。 使用DataSource对象是连接到数据源的首选方法。

注册驱动

  • 注册驱动:其实就是一个指定数据库驱动jar包的过程。
  • 我们在快速入门的时候,使用的注册驱动方式是加载文件:
    • 加载MySQL数据库驱动jar包中的com.mysql.jdbc.Driver类
      • Class.forName(“com.mysql.jdbc.Driver”);
  • 那么为什么加载com.mysql.jdbc.Driver类就能够注册驱动呢?我们来探究一下。
    • 其实真正注册驱动调用的是DriverManager类中的registerDriver静态方法
      • 注册与给定的驱动程序 DriverManager :static void registerDriver(Driver driver)
    • 而由于com.mysql.jdbc.Driver类在其静态代码块调用了registerDriver静态方法,所以当我们去加载这个类时,它会帮我们自动的注册驱动,简便了我们的操作。
  • MySQL5之后的驱动jar包可以省略注册驱动的步骤,因为它自动加载了com.mysql.jdbc.Driver类。
/* com.mysql.jdbc.Driver类中的静态代码块 */
static {
    try {
    	java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
    	throw new RuntimeException("Can't register driver!");
    }
}

获取数据库连接

  • static Connection getConnection(String url, String user, String password)
    • url:指定连接的路径
      • 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
        • 例子:jdbc:mysql://localhost:3306/db3
        • 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
    • user:用户名
    • password:密码

Connection

  • Connection数据库连接对象:与特定数据库的连接。

获取执行SQL语句的对象

  • Statement createStatement() :创建一个 Statement对象,用于将SQL语句发送到数据库。
  • PreparedStatement prepareStatement(String sql) :创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。

管理事务

  • void setAutoCommit(boolean autoCommit) :(开启事务)将此连接的自动提交模式设置为给定状态。
    • true(自动提交)
    • false(手动提交,开启事务)
  • void commit() :(提交事务)使自上次提交/回滚以来所做的所有更改都将永久性,并释放此 Connection对象当前持有的任何数据库锁。
  • void rollback() :(回滚事务)撤消在当前事务中所做的所有更改,并释放此 Connection对象当前持有的任何数据库锁。

Statement

  • Statement执行静态SQL语句的对象:用于执行静态SQL语句并返回其生成的结果的对象。

执行sql

  • boolean execute(String sql) :可以执行任意的SQL语句
  • int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
    • 返回值:影响的行数
    • 通过影响的行数可以判断语句是否执行成功。返回值>0的则执行成功,反之,则失败。
  • ResultSet executeQuery(String sql) :执行DQL(select)语句

练习:增删改

添加记录

注意:添加记录调用的是executeUpdate方法

public static void main(String[] args) {

Connection conn = null;
Statement stat = null;

try {
    //1.注册驱动 抓取异常
    Class.forName("com.mysql.jdbc.Driver");
    //2.定义SQL语句
    String s = "INSERT INTO account VALUES (NULL,'Rat',2000);";
    //3.获取数据库连接对象 Connection
    conn = DriverManager.getConnection("jdbc:mysql:///jdbc", "root", "root");
    //4.获取执行SQL的对象 Statement
    stat = conn.createStatement();
    //5.执行SQL语句
    int i = stat.executeUpdate(s);
    //6.处理结果
    if (i>0) {
        System.out.println("操作成功");
    }else {
        System.out.println("操作失败");
    }
} catch (ClassNotFoundException e) {
	e.printStackTrace();
} catch (SQLException e) {
	e.printStackTrace();
} finally {
//7.释放资源
/* 因为Statement和Connection已经在trycatch语句外声明了一个NULL值
由于trycatch语句的特性,可能会造成在执行新的赋值语句之前就被中断了运行,所以Statement和Connection可能会是空值。
于是这里需要采用if语句来避免空指针异常 */
    if (stat != null) {
        try {
        	stat.close();
        } catch (SQLException e) {
        	e.printStackTrace();
        }
    }
    if (conn != null) {
        try {
        	conn.close();
        } catch (SQLException e) {
        	e.printStackTrace();
        }
    }
}
}

修改记录

除了SQL语句,所有的代码都跟添加记录的练习一模一样。
注意:修改记录执行SQL语句的是executeUpdate方法

// .....
String s = "UPDATE account SET balance =1000 WHERE name = 'rat'";
// .....

删除记录

除了SQL语句,所有的代码都跟添加记录的练习一模一样。
注意:删除记录执行SQL语句的是executeUpdate方法

// .....
String s = "DELETE FROM account WHERE name = 'rat'";
// .....

ResultSet

  • ResultSet结果集对象:表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。

封装查询结果

  • boolean next()::游标向下移动一行,并判断当前行是否是最后一行的末尾(是否有数据)。
    • 如果是最后一行的末尾,则返回false;如果不是,则返回true。
  • getTYPE(参数):获取数据
    • TYPE:代表数据类型
      • 如: int getInt() ,String getString()
    • 参数:
      - int:代表列的编号,从1开始
      - 如: getString(1)
      - String:代表列名称。
      - 如: getDouble(“balance”)
  • 使用步骤:
    • 游标向下移动一行
    • 判断是否有数据
    • 获取数据

练习:查询

查询记录

除了SQL语句、执行SQL语句和处理结果不一样,所有的代码都跟添加记录的练习一模一样。另外,你要在trycatch语句外声明一个ResultSet对象为NULL,并在finally语句中释放ResultSet资源。
注意:查询记录执行SQL语句的是executeQuery方法

// .....
//2.定义SQL语句
String s = "SELECT * FROM account";
//3.获取数据库连接对象
conn = DriverManager.getConnection("jdbc:mysql:///jdbc", "root", "root");
//4.获取执行SQL的对象 statement
stat = conn.createStatement();
//5.执行SQL,获取结果集
reSet = stat.executeQuery(s);
//6.处理结果
//	1.让游标向下移动一行
//	2.并判断是否是最后一行
	while (reSet.next()) {
//		3.获取数据
		int anInt = reSet.getInt(1);
		String string = reSet.getString(2);
		double aDouble = reSet.getDouble("balance");
//		4.输出数据
		System.out.println(anInt + "---" + string + "---" + aDouble);
	}
// ....

查询记录(实体类)

代码

  • 查询数据库中emp表的数据,并将其封装为Employee对象,然后将其封装进List集合。
# MySQL数据库

-- 创建表
CREATE TABLE emp (
  id INT PRIMARY KEY, -- 员工id
  ename VARCHAR(50), -- 员工姓名
  job_id INT, -- 职务id
  mgr INT , -- 上级领导
  joindate DATE, -- 入职日期
  salary DECIMAL(7,2), -- 工资
  bonus DECIMAL(7,2), -- 奖金
  dept_id INT -- 所在部门编号
);

-- 添加员工
INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
(1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
(1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
(1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
(1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
(1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
(1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
(1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
(1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
(1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);
// 实体类

public class Employee {
	// 成员变量(根据数据库的列来设置)
    private int id;
    private String ename;
    private int job_id;
    private int mgr;
    private Date join_date;
    private double salary;
    private double bonus;
    private int dept_id;
	// 无参构造
    public Employee() {
    }
	// 带参构造(按照数据库表中列的顺序)
    public Employee(int id, String ename, int job_id, int mgr, Date join_date, double salary, double bonus, int dept_id) {
        this.id = id;
        this.ename = ename;
        this.job_id = job_id;
        this.mgr = mgr;
        this.join_date = join_date;
        this.salary = salary;
        this.bonus = bonus;
        this.dept_id = dept_id;
    }
    // toString方法
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", ename='" + ename + '\'' +
                ", job_id=" + job_id +
                ", mgr=" + mgr +
                ", join_date=" + join_date +
                ", salary=" + salary +
                ", bonus=" + bonus +
                ", dept_id=" + dept_id +
                '}';
    }
}

// 测试类

// mian方法
public static void main(String[] args) {
	//调用方法,接收list集合
	List<Employee> emps = returnTable();
	//遍历list集合
	for (Employee record : emps) {
    	System.out.println(record);
    }
}
// 成员方法(返回查询结果)
public static List<Employee> returnTable(){
Connection conn = null;
Statement stat = null;
ResultSet reSet = null;
List<Employee> tableList = new ArrayList<>();
try {
    //1.注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2.定义SQL语句
    String s = "SELECT * FROM emp";
    //3.获取数据库连接对象
    conn = DriverManager.getConnection("jdbc:mysql:///jdbc", "root", "root");
    //4.获取执行SQL的对象 statement
    stat = conn.createStatement();
    //5.执行SQL
    reSet = stat.executeQuery(s);
    //6.处理结果
        //6.1 让游标向下移动一行
        //6.2 并判断是否是最后一行
        while (reSet.next()) {
        //6.3 获取数据
        int id = reSet.getInt(1);
        String ename = reSet.getString(2);
        int job_id = reSet.getInt(3);
        int mgr = reSet.getInt(4);
        Date join_date = reSet.getDate(5);
        double salary = reSet.getDouble(6);
        double bonus = reSet.getDouble(7);
        int dept_id = reSet.getInt(8);
		//6.4 创建 Employee对象
Employee emp = new Employee(id,ename,job_id,mgr,join_date,salary,bonus,dept_id);
		//6.5 将Employee对象添加进List集合
                tableList.add(emp);
            }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
	//7.释放资源
		/* 避免空指针异常,使用if语句判断 */
        if (reSet != null) {
            try {
                reSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stat != null) {
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
	}
    //8. 返回List集合
	return tableList;
}

比较

将数据库、实体类和测试类进行比较。

# MySQL数据库

-- 创建表
CREATE TABLE emp (
  id INT PRIMARY KEY, -- 员工id
  ename VARCHAR(50), -- 员工姓名
  job_id INT, -- 职务id
  mgr INT , -- 上级领导
  joindate DATE, -- 入职日期
  salary DECIMAL(7,2), -- 工资
  bonus DECIMAL(7,2), -- 奖金
  dept_id INT -- 所在部门编号
);
// 实体类

	// 成员变量
    private int id;
    private String ename;
    private int job_id;
    private int mgr;
    private Date join_date;
    private double salary;
    private double bonus;
    private int dept_id;
// 测试类
    //6.3 获取数据
    int id = reSet.getInt(1);
    String ename = reSet.getString(2);
    int job_id = reSet.getInt(3);
    int mgr = reSet.getInt(4);
    Date join_date = reSet.getDate(5);
    double salary = reSet.getDouble(6);
    double bonus = reSet.getDouble(7);
    int dept_id = reSet.getInt(8);

PreparedStatement

  • PreparedStatement执行动态SQL语句的对象:表示预编译(动态)的SQL语句的对象。

SQL注入问题

  • 指在拼接SQL时,有一些特殊的SQL关键字参与,从而造成了安全性问题。

用户输入前

// Java语句
String sql = "SELECT * FROM user WHERE username = '"+username+"' AND password = '"+password+"';";

用户输入后, 账户:随便,密码:a’ or ‘a’='a

# SQL语句
SELECT * FROM JDBC_user WHERE username = '随便' AND password ='a' OR 'a'='a';

# 语句永远成立,轻松登录成功。

解决SQL注入问题

  • 使用PreparedStatement对象来解决SQL注入问题。
    • 预编译(动态)的SQL:参数使用?作为占位符
  • 使用步骤:
    1. 导入驱动jar包
    2. 注册驱动
    3. 获取数据库连接对象 Connection
    4. 定义sql
      • 注意:sql的参数使用?作为占位符。
      • 如:select * from user where username = ? and password = ?;
    5. 传递SQL语句,获取执行动态SQL语句的对象 PreparedStatement
    6. 给?占位符赋值:
      • 方法: setXxx(参数1,参数2)
        • 参数1:?占位符的位置编号,从1 开始
        • 参数2:?占位符的值
    7. 执行sql,接受返回结果,不需要传递sql语句
    8. 处理结果
    9. 释放资源

登录案例

  • 需求
    • 用户通过键盘录入用户名和密码;
    • 查询数据库后,判断用户是否登录成功。

使用静态SQL

# 创建表
CREATE TABLE USER(
	id INT PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(30),
	PASSWORD VARCHAR(30)
);
# 插入数据
INSERT INTO USER VALUES(1,'cat',111),(2,'dog',222);
// [main方法]
public static void main(String[] args) {
//用户输入
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账户:");
String username = scanner.next();
System.out.println("请输入密码:");
String password = scanner.next();
//调用login方法
boolean flg = login(username, password);
//输入
if (flg){
	System.out.println("登录成功");
}else {
	System.out.println("登录失败");
}
}

//[login方法]
public static boolean login(String username, String password) {
    //防止输入空值
    if (username == null) {
        return false;
    }
    if (password == null) {
        return false;
    }
    //声明变量
    Connection connection = null;
    Statement statement = null;
    ResultSet resultSet = null;
	try {
        //获取Connection对象
        connection = JDBCUtils.getConnection();
        //编写SQL语句
        String sql = "SELECT * FROM JDBC_user WHERE username = '"+username+"' AND password = '"+password+"'";
        //获取Statement对象
        statement = connection.createStatement();
		//执行SQL语句,并获取ResultSet对象
        resultSet = statement.executeQuery(sql);
        //返回
        return resultSet.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    	//返回
        return false;
    }

使用动态SQL

//  .....
    try {
    //获取Connection对象
    connection = JDBCUtils.getConnection();
    //编写SQL语句
    String sql = "SELECT * FROM user WHERE username = ? AND password = ? ";
    //传递SQL语句,获取preparedStatement对象
    preparedStatement = connection.prepareStatement(sql);
    //通过PreparedStatement类的set方法,给?占位符赋值
    preparedStatement.setString(1,username);
    preparedStatement.setString(2,password);
    //通过PreparedStatement类的executeQuery方法,并获取ResultSet对象
    resultSet = preparedStatement.executeQuery();//不需要传参
    //返回
    return resultSet.next();
} catch (SQLException e) {
//  .....

JDBC工具类

  • 经过几个JDBC的案例之后,我们不难发现JDBC的编写中存在大量复杂的相同的代码,为了简化书写,我们引入了JDBC工具类:JDBCUtils。
  • 但仅仅使用JDBC工具类,仍然不能使得获取数据库连接对象的代码变得简洁,而我们每次在获取数据库连接对象时,输入的三个参数是很少会变化的。于是,为了更加简化书写,我们引入了JDBC配置文件:jdbc.properties。
  • 为了达到最终的目的,我们需要在JDBC工具类中来编写以下内容:
    • 静态变量
    • 静态代码块
    • 获取数据库连接对象的静态方法
    • 增删改操作释放资源的静态方法
    • 查询操作释放资源的静态方法
//J DBC工具类:JDBCUtils

public class JDBCUtils {
//    [静态变量]
    private static String url;
    private static String user;
    private static String password;
    private static String driver;
    
//    [静态代码块:读取配置文件的数据]
    static {
        try {
//            1.创建properties对象
            Properties properties = new Properties();
//            1.1 获取 字节码文件
            Class jdbcClass = JDBCUtils.class;
//            1.2 获取 类加载器
            ClassLoader classLoader = jdbcClass.getClassLoader();
//            1.3 获取 统一资源定位符
            URL resource = classLoader.getResource("jdbc.properties");
//            1.4 获取 路径
            String path = resource.getPath();
//            2.加载配置文件
            properties.load(new FileReader(path));
//            3.获取数据
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            driver = properties.getProperty("driver");
//            4.注册驱动
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
//[成员方法:获取Connection对象]
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }

//[成员方法:增删改操作释放Connection对象、Statement对象]
    public static void close(Statement stat, Connection conn) {
        if (stat != null) {
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
//[成员方法:查询操作释放Connection对象、Statement对象、ResultSet对象]
    public static void close(Statement stat, Connection conn, ResultSet reSet) {
        if (reSet != null) {
            try {
                reSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stat != null) {
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

# properties提示配置文件
url = jdbc:mysql:///jdbc
user = root
password = root
driver = com.mysql.jdbc.Driver

查询练习

// 使用JDBC工具类
public static void main(String[] args) {
    Connection conn = null;
    Statement stat = null;
    ResultSet reSet = null;
	try {
        //1.注册驱动
        //2.获取数据库连接对象
		conn = JDBCUtils.getConnection();
		//3.定义SQL语句
		String s = "SELECT * FROM account";
        //4.获取执行SQL的对象 statement
        stat = conn.createStatement();
        //5.执行SQL
        reSet = stat.executeQuery(s);
        //6.处理结果
            //1.让游标向下移动一行
            //2.并判断是否是最后一行
            while (reSet.next()) {
				//3.获取数据
                int anInt = reSet.getInt(1);
                String string = reSet.getString(2);
                double aDouble = reSet.getDouble(3);
				//4.输出数据
                System.out.println(anInt + "---" + string + "---" + aDouble);
            }
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
        //7.释放资源
        JDBCUtils.close(stat,conn,reSet);
	}
}

JDBC控制事务

  • 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
  • JDBC使用Connection对象来管理事务
    • 开启事务:setAutoCommit(boolean autoCommit) (false为开启事务)

      • 在执行sql之前开启事务
    • 提交事务:commit()

      • 当所有sql都执行完提交事务
    • 回滚事务:rollback()

      • 在catch中回滚事务
  • 我在代码中写入了一句异常 ,当程序运行到异常时,异常后的提交事务不会进行,程序运行catch语句,进行事务回滚,账户会回滚到之前的状态。当然,如果去除异常,交易会正常进行,账户会发生变化。
public static void main(String[] args) {

    Connection connection = null;
    PreparedStatement preparedStatement = null;

try {
	//获取Connection对象
	connection = JDBCUtils.getConnection();
    //【开启事务】
    connection.setAutoCommit(false);
	//定义SQL语句  [Cat -500]***
	String sql1 = "UPDATE account SET balance = balance - ? WHERE id = ?";
	//传递SQL语句,获取preparedStatement对象
	preparedStatement = connection.prepareStatement(sql1);
	//通过PreparedStatement类的set方法,给?赋值
    preparedStatement.setInt(1,500);
    preparedStatement.setInt(2,1);
    //执行SQL语句
    int i = preparedStatement.executeUpdate();
System.out.println(i);
	//定义SQL语句  [Dog +500]***
	String sql2 = "UPDATE account SET balance = balance + ? WHERE id = ?";
	//传递SQL语句,获取preparedStatement对象
	preparedStatement = connection.prepareStatement(sql2);
	//通过PreparedStatement类的set方法,给?赋值
    preparedStatement.setInt(1,500);
    preparedStatement.setInt(2,2);
    //执行SQL语句
	i = preparedStatement.executeUpdate();
	System.out.println(i);
	// ***异常***
	int iii = 3/0;
	//【提交事务】
	connection.commit();
} catch (Exception e) {
	//【事务回滚】
	try {
        if (connection!= null) {
            connection.rollback();
        }
	} catch (SQLException ex) {
		ex.printStackTrace();
	}
	e.printStackTrace();
} finally {
// 释放资源
JDBCUtils.close(preparedStatement,connection);
}
}

你可能感兴趣的:(Java基础)