jdbc

  1. JDBC
    1. Java链接数据库
      1. 事务处理
        1. 事务设置setAuntoCommit()
        2. 预编译PreparedStatement
        3. 封装工具类

JDBC

JDBC是什么?

jdbc是一个由sun公司(Oracle)提出的规范(一套接口),jdbc指的就是java语言连接数据库

jdbc就是一套面向所有数据库厂家的接口

每一个数据库厂家都希望可以通过java语言连接到自己开发的数据库,

这个时候,厂家肯定要写一套程序,java语言连接数据库的具体实现,这个实现类必须要实现JDBC接口

  • 作为开发人员来说,我们可能会连接到很多组不同的数据库,根据公司的业务不同,可能会连接mysql,SqlServer等等,但是我们不需要关注数据库具体的实现类是什么。我们关注的是jdbc这样的一套接口,因为这个接口提供了具体获取数据库对象的方法,还有相关执行增删改查的方法,还有相关事务提交的方法,事物处理的方法,我们只需要关注接口中有哪些方法,方法具体的实现交由数据库的厂家去完成!

例如:java语言连接 Mysql

mysql的厂家就必须要实现jdbc这个接口

JDBC的驱动类型

  • ODBC是微软为了解决跨数据平台的一个方案,sun提供支持
  • 部分java驱动程序使用特定数据库的API(一般是C和C++)将jdbc翻译成数据库的特定代码
  • Jdbc-net将jdbc翻译成不针对特定数据库的网络协议,由数据库服务器翻译成特定代码
  • 本地协议纯java驱动程序将jdbc请求转化成针对特定数据库的网络协议

JDBC的用途

  1. 加载对应数据库驱动(Load Driver)
  2. 与数据库建立连接(connection)
  3. 发送操作数据库的语句(createStatement)
  4. 执行并处理返回结果(executeQuery)

Java链接数据库

保证mysql服务是开启的

将java包导入到工程lib下,并且添加到类路径下(保证版本对应)

注册驱动,使用Class类下的一个静态方法forName,使得类加载

获取数据库连接 DriverManager类提供了getConnection方法 (需要传入数据源信息)

获取数据库操作对象 createStatement方法

编写sql语句

按照sql语句的类别调用不同方法,获取到返回的结果

关闭数据库资源(放在finally语句块中)

package com.os467.test;

import java.sql.*;

/**
 * JDBC编程六步:
 * 1.注册驱动
 * 2.获取数据库连接
 * 3.获取数据库操作对象
 * 4.执行sql
 * 5.返回记录条数/获取结果集对象
 * 6.关闭资源
 */

public class JdbcTest01 {

    public static void main(String[] args) {

        //使三个实例作为成员变量存在在类中,使得finally语句块中可以读取到
        Connection connection = null;

        Statement statement = null;

        ResultSet resultSet = null;

        try{

            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取数据库链接
            /**
             * String url 链接数据库的地址
             * String user 数据库的用户名 root
             * String password 数据库密码 root
             * jdbc:mysql:// 协议
             * localhost 本地的ip地址
             * 3306 默认端口号
             * os467test 需要使用的数据库
             * ?serverTimezone=GMT 设置时区
             */

            String url = "jdbc:mysql://localhost:3306/os467test?serverTimezone=GMT";

            String user = "root";

            String password = "root";

            connection = DriverManager.getConnection(url, user, password);

            System.out.println(connection);

            //3.获取数据库操作对象
            statement = connection.createStatement();

            //4.编写sql语句,执行sql
            String sql = "select * from emp";

            //5.执行sql,获取返回的结果集对象,遍历结果集
            resultSet = statement.executeQuery(sql);

            while(resultSet.next()){

                //取出结果集中的数据
                int empno = resultSet.getInt("EMPNO");

                String ename = resultSet.getString("ENAME");

                System.out.println("员工的编号为"+empno+" 员工的姓名为"+ename);

            }


        }catch (Exception e){

            e.printStackTrace();

        }finally {

            //6.关闭数据库资源,先进行非空校验,对close()可能发生的异常进行处理
            if (resultSet != null){

                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

            }

            if (statement != null){

                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

            }

            if (connection != null){

                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

            }

        }

    }


}

注意:在为字符集添加中文数据时,在url后面用**&拼接characterEncoding=utf-8**来设置字符集

并且数据库校对中每个字段的字符集也要设置为utf-8

package com.os467.test;

import javax.swing.*;
import java.sql.*;

/**
 * 往学生表中添加数据
 */
public class JdbcTest03 {

    public static void main(String[] args) {

        Connection connection = null;

        Statement statement = null;

        try {

            //加载jdbc驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            String url = "jdbc:mysql://localhost:3306/os467test?serverTimezone=GMT&characterEncoding=utf-8";

            String user = "root";

            String password = "root";

            //链接数据库
            connection = DriverManager.getConnection(url, user, password);

            //获取操作对象
            statement = connection.createStatement();

            String sql = "insert into tb_student(name,age,hobby)" +
                    "value('Tom',18,'打篮球')";

            int num = statement.executeUpdate(sql);

            if (num != 0){

                System.out.println("添加成功");
            }else{

                System.out.println("添加失败");

            }

        }catch (Exception e){

            e.printStackTrace();

        }finally {
            
          //关闭数据库资源,避免占用系统资源
          if (statement != null){

              try {
                  statement.close();
              } catch (SQLException throwables) {
                  throwables.printStackTrace();
              }

          }

          if (connection != null){

              try {
                  connection.close();
              } catch (SQLException throwables) {
                  throwables.printStackTrace();
              }

          }



        }


    }


}

//模拟用户登入

package com.os467.test;

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

public class UserJDBCTest02 {

    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();


        Connection connection = null;

        Statement statement = null;

        ResultSet resultSet = null;

        try{

            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            String url = "jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8";

            //获取数据库对象
            connection = DriverManager.getConnection(url,"root","root");

            //获取数据库操作对象
            statement = connection.createStatement();

            resultSet = statement.executeQuery("select * from tb_user where username = '" + username + "' and password = '" + password + "'");

            //判断结果集是否为空和集合中是否有元素
            if(resultSet != null && resultSet.next()){

                System.out.println("登入成功");

            }else{

                System.out.println("登入失败");

            }

        }catch (Exception e){

            e.printStackTrace();

        }finally {

            if (resultSet != null){

                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

            }

            //关闭数据库资源
            if (statement != null){

                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

            }

            if (connection != null){

                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

            }

        }



    }

}

数据库中创建的每一张表 在java中都有一个实体与之对应

table中的字段对应的是实体beans中的属性

tb_user User

uid uid

username username

password password

通过jdbc来读取数据,这些数据也是以对象的形式被封装在结果集容器中ResultSet

resultSet !=null && resultSet.next()

第一个条件保证容器对象不为空,判断容器中是否有元素

我们会将读取的数据封装成对象,然后将对象装进集合中(ArrayList/HashMap)

后面学习了前端之后,我们会将集合对象传到前端,在前端取出集合中的数据,渲染数据!

事务处理

java事务

实际上,一个Java应用系统,如果要操作数据库,则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的,事务的控制也相应转移到java程序代码中。因此,数据库操作的事务习惯上就称为Java事务。

通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简单的说就是:要么全部执行成功,要么撤销不执行。

  • 事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity),一致性(consistency),隔离性(isolation)和持久性(durability)的缩写
  • 通常的观念认为,事务仅与数据库相关
  • 事务只存在于增删改状态下

事务的原子性:表示事务执行过程中的任何失败都将导致事务所作的任何修改失败

事务的一致性:表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态

事务的隔离性:表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见

事务的持久性:表示已提交的数据在事务执行失败时,数据的状态都应该正确

案例:

银行存钱

银行取钱

转账方法 A B

假设有一个转账的场景:A账户转账给B账户,这个时候我们需要在A账户上扣钱,在B账户上加钱

所以我们需要去写两条sql来完成这样的需求,但是我们要开启事务的支持,因为这两条sql是在一条事务线上的,所以要么同时执行成功,要么同时失败,要满足事务的原子性和一致性。

事务设置setAuntoCommit()

事务案例代码:

jdbc默认状态下是自动提交的

  • A账户向B账户转账1000

  • 模拟一个异常

  • 开启对事务的支持

  • 在jdbc中如何开启对事务的支持

  • jdbc默认是关闭事务的,如果想开启事务的话需要 connection.setAutoCommit();
    然后在sql都执行成功的情况下我们要进行一个事务提交的操作 connection.commit();

  • connection.rollback()是一个回滚的方法,是为了让事务回到执行之前的状态

/**
 * A账户向B账户转账1000
 *
 * 模拟一个异常
 *
 * 开启对事务的支持
 *
 * 在jdbc中如何开启对事务的支持
 *
 * jdbc默认是关闭事务的,如果想开启事务的话需要 connection.setAutoCommit();
 */
public class JdbcDemo02 {

    public static void main(String[] args) {

        Connection connection = null;

        Statement statement = null;

        try{

            Class.forName("com.mysql.cj.jdbc.Driver");

            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8", "root", "root");

            //将自动提交设置为手动提交
            connection.setAutoCommit(false);

            statement = connection.createStatement();

            //A账户扣钱
            int num = statement.executeUpdate("update tb_account set balance_A = balance_A - 1000 where id = 1");

            System.out.println(num==1 ? "扣钱成功" : "扣钱失败");

            //模拟一个异常
            int i = 1 / 0;

            //B账户加钱
            num = statement.executeUpdate("update tb_account set balance_B = balance_B + 1000 where id = 1");

            System.out.println(num==1?"转账成功":"转账失败");

            //提交事务
            connection.commit();

        } catch (Exception e) {

            e.printStackTrace();

        }finally{

            if (statement != null){

                try {
                    statement.close();
                } catch (SQLException throwables) {

                    //事务回滚
                    try {
                        connection.rollback();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }

                    throwables.printStackTrace();
                }

            }

            if (connection != null){

                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

            }

        }

    }

}

预编译PreparedStatement

sql注入:指的就是通过在sql中加入数据库的关键字,导致最终sql执行的结果是错误的

PreparedStatement 这个对象可以防止sql注入 因为这个对象在执行sql的时候

是分为两个步骤: 1.先预编译sql,先把sql的架子给搭建出来,但是没有赋值,2.给sql中注入具体的数据,执行sql

Statement对象没有预编译的过程,直接就是执行sql

考虑到安全性我们以后不会直接使用Statement而是使用PreparedStatement

              //获取数据库操作对象并且预编译sql
             PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user where username = ? and password = ? ");

             //给占位符?赋值,占位符从1开始
            preparedStatement.setString(1,"jack001");
            preparedStatement.setString(2,"123456");

            //执行sql
            resultSet = preparedStatement.executeQuery();

            if (resultSet != null && resultSet.next()){

                System.out.println("登录成功");

            }

关于数据库的悲观锁和乐观锁(都是跟事务有关的):

​ 乐观锁:假设在多线程场景下,多个线程同时对数据库中的一个表进行操作

​ 如果多个线程会去修改同一条数据,乐观锁的思想就是觉得这种情况不可能发生

乐观锁:乐观锁可以不用去开启事务的支持(采用的是自动提交的策略)

update tb_user set username = 'tom' where id = 1 and version = "影响记录条数只要大于等于1,这个	版本号就会改变"

悲观锁:针对于以上描述的情况,悲观锁思想就会觉得这种情况一定会发生,然后要提前去解决这个问题

悲观锁一定要去开启对事务的支持,采用手动提交的策略来解决问题,行级锁,在sql后面加上 for update

select * from tb_account where id = 1 for update;

当一个线程在对某一条记录进行查询的时候,那么别的线程只能等待

封装工具类

工具类不能实例化对象,构造函数被private修饰

所有的方法都要被static修饰

package com.os467.utils;

import java.sql.*;

/**
 * 封装工具类的步骤:
 *
 * 1.构造方法私有化
 * 2.所有的方法都得是静态方法
 *
 */

public class JdbcUtils {

    //私有化构造方法
    private JdbcUtils(){

    }

    //注册驱动
    static {

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取数据库链接方法
     */
    public static Connection getConnection() throws SQLException{

        return DriverManager.getConnection("jdbc:mysql://localhost:3306" +
                "/web_test?serverTimezone=GMT&characterEncoding=utf-8",
                "root", "root");

    }

    /**
     * 用于关闭资源的方法
     */
    public static void getClose(ResultSet resultSet, Statement statement,Connection connection){

        if (resultSet != null){

            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

        }

        if (statement != null){

            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

        }

        if (connection != null){

            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

        }

    }

}

利用工具类来执行sql

package com.os467.demmo;

import com.os467.utils.JdbcUtils;

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

public class JdbcDemo03 {

    public static void main(String[] args) {

        PreparedStatement preparedStatement = null;

        ResultSet resultSet =null;

        Connection connection = null;

        try {
            //获取数据库链接对象
            connection = JdbcUtils.getConnection();

            //获取数据库操作对象
            preparedStatement = connection.prepareStatement("select * from tb_user");

            //执行sql
            resultSet = preparedStatement.executeQuery();

            if (resultSet != null){

                while (resultSet.next()){

                    int uid = resultSet.getInt("uid");

                    String username = resultSet.getString("username");

                    String password = resultSet.getString("password");

                    System.out.println(uid+" "+username+" "+password);

                }

            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {

            JdbcUtils.getClose(resultSet,preparedStatement,connection);

        }

    }

}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:jdbc

字数:3.5k

本文作者:Os467

发布时间:2022-07-01, 08:24:49

最后更新:2022-09-05, 00:08:35

原始链接:https://os467.github.io/2022/07/01/jdbc/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏