一张图让Java程序员搞懂JDBC

一、概述

1.什么事JDBC?

Java DataBase Connectivity(Java语言连接数据库)。

2.本质

  • JDBC是SUN公司制定的一套接口(interface)。
  • Java.sql.*
  • 接口都有调用者和实现者
  • 面向接口调用,面向接口写实现类,这都属于接口编程。

3.为什么要面向接口编程?

  • 解耦合:降低程序的耦合度,提高程序的扩展能力。
  • 态机制就是非常典型的:面向抽象编程。

在这里插入图片描述
有了各个数据库厂家对JDBC的实现,程序员就不需要关心需要向数据库编程的问题了,只是面向JDBC编程,调用JDBC就可以了。

4.JDBC 开发前的准备工作。

先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath中。

二、常用接口

1.Driver接口

Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:
  装载MySql驱动:Class.forName(“com.mysql.jdbc.Driver”);
  装载Oracle驱动:Class.forName(“oracle.jdbc.driver.OracleDriver”);

2.Connection接口

  • Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url,
    user, password)方法建立在JDBC URL中定义的数据库Connection连接上。

  • 连接MySql数据库:Connection conn =
    DriverManager.getConnection(“jdbc:mysql://host:port/database”,
    “user”, “password”);

  • 连接Oracle数据库:Connection conn =
    DriverManager.getConnection(“jdbc:oracle:thin:@host:port:database”,
    “user”, “password”);

  • 连接SqlServer数据库:Connection conn =
    DriverManager.getConnection(“jdbc:microsoft:sqlserver://host:port;
    DatabaseName=database”, “user”, “password”);

常用方法:

createStatement():创建向数据库发送sql的statement对象。prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
prepareCall(sql):创建执行存储过程的callableStatement对象。
事务

		 1)conn.setAutoCommit(false);//开启事务
		 
         2) conn.commit();//提交事务

         3)conn.rollback();//回滚事务

添加事务的代码

事务能解决那些问题!请参考
https://blog.csdn.net/shang_0122/article/details/106048617

public class JDBCTest10 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dmsd?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123");
            //将自动提交机制修改为手动提交
            conn.setAutoCommit(false);//开启事务

            //3.获取预编译的数据库操作对象
            String sql = "update t_act set balance=? where actno=?";
            ps = conn.prepareStatement(sql);
            //给?传值
            ps.setDouble(1, 10000);
            ps.setInt(2, 111);
            int count = ps.executeUpdate();//执行第一条UPDATE语句

            // System.out.println(count);
            //String s = null;
            //s.toString();
            //重新给占位符传值
            ps.setDouble(1, 10000);
            ps.setInt(2, 222);
            count += ps.executeUpdate();//执行第二条UPDATE语句
            //4.执行SQL
            System.out.println(count == 2 ? "转账成功" : "转账失败");
            //程序能够走到这里说明以上的程序没有异常,事务结束,手动提交数据。
            conn.commit();//提交事务
        } catch (Exception e) {
            //回滚事务
            if (conn!=null){
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            //6.释放资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }

3.Statement接口

用于执行静态SQL语句并返回它所生成结果的对象,在下文会有Statement用法及介绍。

4.ResultSet接口

ResultSet提供检索不同类型字段的方法,常用的有:

  • getString(int index)、getString(String
    columnName):获得在数据库里是varchar、char等类型的数据对象。
  • getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
  • getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
  • getBoolean(int index)、getBoolean(String
    columnName):获得在数据库里是Boolean类型的数据。
  • getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。

ResultSet还提供了对结果集进行滚动的方法:

  • next():移动到下一行 Previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动resultSet的最前面。
  • afterLast() :移动到resultSet的最后面。
    使用后依次关闭对象及连接:ResultSet → Statement → Connection

三、JDBC编程六步

第一步:注册驱动
作用:告诉java程序,即将要连接的是那个品牌数据库

第二步:获取链接
作用:表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭连接。

第三步:获取数据库操作对象
作用:(专门执行sql语句的对象)

第四步:执行SQL语句(增删改查)

第五步:处理查询结果集
只有当第四步执行的是select语句的时候,才有第五步处理查询结果集。

第六步:释放资源
使用完资源之后一定要关闭资源。java和数据库之间的通信,开启之后一定要关闭。

四、典型代码

1.获取连接内容写在代码中的方式。

这种写法比较麻烦,每次都需要写,所以就想办法把这些重复性的东西得拿出来了。

public class JDBCTest01 {
    public static void main(String[] args) {
        Connection conn=null;
        Statement stmt=null;
        try {
            //第一步:注册驱动
            Driver driver=new com.mysql.jdbc.Driver();
            DriverManager .registerDriver(driver);

            //第二步:获取连接
            String url="jdbc:mysql://127.0.0.1:3306/db";
            String user="root";
            String password="123456";
             conn=DriverManager.getConnection(url,user,password);
            System.out.println("数据库连接对象:="+conn);

            //第三步:获取数据库操作对象
            //createStatment:专门执行SQL语句。
             stmt=conn.createStatement();

            //第四步:执行SQL语句
            String sql="insert into dept(deptno,dname,loc) values(xx,'xx','xx')";
            //专门执行DML语句的(insert,delete,update)
            //返回值是“影响数据库中记录条数”
            int count = stmt.executeUpdate(sql);
            System.out.println(count == 1 ? "保存成功" : "保存失败");

            //第五步:处理查询结果集

        }catch ( SQLException e){
            e.printStackTrace();
        }
        //为了保证资源一定释放,在finally语句块中关闭资源
        //遵循从小到大依次关闭
        //分别对其try...catch
        //第六步:释放资源
        if (stmt!=null){
            try {
                stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (conn!=null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

2. 将获取连接写成一行代码,避免了上面那个写法,这种写法最重要的是在

Statement和preparedStatem的对比。
1)相比于上面的Satatement用法和方法,以及PreparedStatement解决了什么问题?

①解决SQL注入问题

  • 只要用户提供的信息不参与SQL语句的编译,问题就解决了。

  • 即使用户提供的信息中含有SQL语句的关键字,但没有参与编译,不起作用。

  • 要想用户信息不参与SQL语句编译,那么使用java.sql.PreparedStatement

  • PreparedStatement接口继承了java.sql.Statement

  • PreparedStatement是属于预编译的数据库操作对象

  • PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”

②解决SQL注入的关键
用户提供的信息中即使含有sql语句的关键字,但这些关键字并没有参与编译,不起作用。

③对比Statement和preparedStatement

  • Statement存在SQL 注入问题,PreparedStatement解决了SQL注入问题。

  • Statement是编译一次执行一次,PreparedStatement是编译一次,执行N次,PreparedStatement效率较高一些。

  • PreparedStatement会在编译阶段做类型的安全检查
    Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。

  • PreparedStatement
    :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL

  • CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。

④常用Statement方法:

  • execute(String sql):运行语句,返回是否有结果集

  • executeQuery(String sql):运行select语句,返回ResultSet结果集。

  • executeUpdate(String sql):运行insert/update/delete操作,返回更新的行

数。

  • addBatch(String sql) :把多条sql语句放到一个批处理中。

  • executeBatch():向数据库发送一批sql语句执行。
    通过对比:PreparedStatement使用较多,只有在极少数情况下使用Statement

    业务方面必须支持SQL注入的时候。
    Statement支持SQL注入,凡是业务方面要求进行SQL语句拼接的,必须使用Statement

运行PreparedStatement解决SQL注入以后的代码

public class JDBCTest09 {
    public static void main(String[] args) {
        Connection conn=null;
        PreparedStatement ps=null;
        try {
            //1.注册驱动
            Class .forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/dmsd?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123");

            //3.获取预编译的数据库操作对象
            String  sql="update t_user set loginName=? where id=?";
            ps=conn.prepareStatement(sql);
            ps.setString(1,"wangmazi");
            ps.setInt(2,1);
            int count =ps.executeUpdate();//执行第一条UPDATE语句
            System.out.println(count);
            //重新给占位符传值
            ps.setString(1,"zhangsi");
            ps.setInt(2,2);
            count=ps.executeUpdate();//执行第二条UPDATE语句

            //4.执行SQL
            System.out.println(count);
        }catch (Exception e){
            e.printStackTrace();
        }finally {

            //6.释放资源
            if (ps!=null){
                try {
                    ps.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn !=null){
                try {
                   conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

3.代码优化,遵循面向对象的思想,运用封装。

封装JDBC 工具类

public class DBUtil {
    /**
     * 工具类中的构造方法都是私有的
     * 因为工具类中的方法都是静态的,不需要new对象,直接采用类名调用
     */
    private DBUtil() {
    }

    //静态代码块在类加载时执行,并且只执行一次。
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库操作对象
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/dmsd?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123");
    }

    /**
     * 关闭资源
     * @param conn 连接对象
     * @param ps   数据库操作对象
     * @param rs   结果集
     */
    public static void close(Connection conn, Statement ps, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

调用工具类,让代码看起来很精简。

public class JDBCTest11 {
    public static void main(String[] args) {
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            //获取连接
            conn= DBUtil.getConnection();
            //获取预编译的数据库操作对象
            //错误写法
            /*
            String sql="select loginName from t_user where loginName like'_?%'";
            ps=conn.prepareStatement(sql);
            ps.setString(1,"w");
            */
             //正确写法
            String sql="select loginName from t_user where loginName like ?";
            ps=conn.prepareStatement(sql);
            ps.setString(1,"z%");
            rs=ps.executeQuery();
            while (rs.next()){
               System.out.println(rs.getString("loginName"));
           }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //释放资源
            DBUtil.close(conn,ps,rs);
        }
    }
}

总结:在代码中会发现,加了一句?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai,这句代码解决了在连接mysql数据库字符编码出错和时区的问题,可以参考

https://blog.csdn.net/yyp0304Devin/article/details/106061531。

猜你喜欢

转载自blog.csdn.net/yyp0304Devin/article/details/106099833