JDBC详解,绝对有你想看的细节!


   JDBC(Java DataBase Connectivity)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。
  不同的数据库管理系统厂商提供接口的实现类供开发者使用,将写好的 class文件打包成 jar包,称之为 驱动。这样一来,开发者不用关心不同厂商的数据库底层实现原理,下载不同的驱动,直接面向接口编程即可。这样做的好处是降低了程序的耦合度,提高了程序的扩展力。

1、JDBC编程步骤

  1. 注册驱动。即选择需要连接的数据库管理系统,如MySQL、SQL Server等;
  2. 获取连接Connection。即打开JVM进程和数据库进程之间的通道;
  3. 获取数据库操作对象Statement。用来将相关SQL语句发送到数据库;
  4. 执行SQL语句;
  5. 处理查询结果集ResultSet。只有执行DQL语句才会有此步骤;
  6. 释放资源。

常用方法

接口方法作用
DriverManagerstatic void registerDriver(Driver driver)注册给定的驱动程序
DriverManagerstatic Connection getConnection(String url, String user, String password)尝试建立与给定数据库URL的连接
ConnectionStatement createStatement()创建数据库操作对象
StatementResultSet executeQuery(String sql)执行给定的select语句
Statementint executeUpdate(String sql)执行给定的insert、update、delete语句
ResultSetint getInt(String columnLabel)检索当前行中指定列名的整型值
ResultSetString getString(int columnIndex)检索当前行中指定列的值,下标从1开始
ResultSetString getString(String columnLabel)检索的当前行中指定列名的值
ResultSetboolean next()将光标从当前位置向前移动一行

注释

  • int executeUpdate(String sql)返回的值是sql语句影响的数据条数;
  • ResultSet executeQuery(String sql) 返回的结果集中,光标初始位置在查询结果的前一行;
  • boolean next()光标所在行无数据时返回false

1.1 示例

//IDEA中已经导入了mysql-connector-java-8.0.17.jar驱动
//package JDBC下有配置文件class.properties
//xzy01为MySQL下的一个数据库,8.0版本要在其后加上?...字符串
url=jdbc:mysql://localhost:3306/xzy01?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT
user=root
password=123

package JDBC;

import java.sql.*;
import java.util.ResourceBundle;

public class TestJDBC {
    public static void main(String[] args) {
        Statement mysqlstatement = null;
        Connection mysqlconnection = null;
        try {
            //1、注册MySQL驱动
            Driver mysqldriver=new com.mysql.cj.jdbc.Driver();
            DriverManager.registerDriver(mysqldriver);
            //2、获取连接,利用资源绑定器
            ResourceBundle resource = ResourceBundle.getBundle("JDBC/connection");
            String url = resource.getString("url");
            String user = resource.getString("user");
            String password = resource.getString("password");
            mysqlconnection = DriverManager.getConnection(url, user, password);
            //3、获取数据库操作对象
            mysqlstatement = mysqlconnection.createStatement();
            //4、执行DML语句
            String sql = "insert into user(name,password) values('Alice','01')";
            mysqlstatement.executeUpdate(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
        	//5、关闭资源
            if (mysqlstatement != null) {
                try {
                    mysqlstatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (mysqlconnection != null) {
                try {
                    mysqlconnection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

说明

  1. user表结构如下:
mysql> desc user;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| ID       | int(11)      | NO   | PRI | NULL    | auto_increment |
| name     | varchar(255) | YES  |     | NULL    |                |
| password | varchar(255) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
  1. 执行结果如下:
mysql> select * from user;
+----+-------+----------+
| ID | name  | password |
+----+-------+----------+
|  1 | Alice | 01       |
+----+-------+----------+
  1. 注册MySQL驱动有更简便的方法:Class.forName("com.mysql.cj.jdbc.Driver");。该方法调用时默认实现该包下Driver接口的静态代码块:DriverManager.registerDriver(new Driver()),比较方便。

1.2 将代码封装成工具类

  我们发现获取连接和释放资源代码过多, 故考虑将其封装在工具类JDBCUtil中。

package JDBC;

import java.sql.*;
import java.util.ResourceBundle;

public class JDBCUtil {
    /**
     * 私有化构造方法,防止实例化对象出来
     * 工具类中的方法都是静态的,可直接调用
     */
    private JDBCUtil() {
    }
    /**
     * 静态代码块在类加载(调用类方法)时仅执行一次
     * 即使多次连接时也只需要获取一次驱动
     */
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 将JVM连接到MySQL数据库
     * @return 返回连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        ResourceBundle resource = ResourceBundle.getBundle("JDBC/connection");
        String url = resource.getString("url");
        String user = resource.getString("user");
        String password = resource.getString("password");
        return DriverManager.getConnection(url, user, password);
    }

    /**
     * 关闭打开的资源通道
     * @param c 连接对象
     * @param s 数据库操作对象
     * @param r 查询结果集
     */
    public static void close(Connection c, Statement s, ResultSet r){
        if(c!=null) {
            try {
                c.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(s!=null){
            try {
                s.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(r!=null){
            try {
                r.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

1.3 DAO封装

  DAO(Data Access Object) (数据访问对象)是一个面向对象的数据库接口,在实际开发中讲针对某张表的操作封装成对应的类,比如add()方法表示向相应的表插入数据,可大大减少代码量。开发规则如下:

  1. DAO类命要求是表名+Dao
  2. DAO类所在的包名要求是公司域名.dao

1.4 实体类(Entity)

  当对查询方法(select)进行DAO封装时,需要返回的是查询结果,为了数据传输的方便,往往根据表结构创建对应的实体类,类命即表名。将查询到的每条结果转化为实体类对象,然后装箱返回。

2、SQL注入现象

  假如有这么一条查询语句String sql="select * from user where name='"+loginname+"' and password='"+loginpassword+"'";,模拟用户输入loginnameloginpassword之后在数据库中查询有无这样一个账号信息。如果这样输入的话:账号:Alice、密码:a' or 'a'='a,即使密码没对,数据库也能查询出结果,这是为什么呢?
  细心一点的话我们会发现把输入信息带入到查询语句后:select * from user where name='Alice' and password='a' or 'a'='a';,很明显,输入的信息中含有SQL关键词or,参与到SQL语句的编译,执行结果与预期不符,这就是SQL注入现象
  如果我们不想让用户提供的信息参与到SQL语句的编译,可以利用java.sql Interface PreparedStatement(表示预编译的SQL语句的对象)。
  与此同时,对提供的SQL语句编写也有了要求,在需要用户提供的信息处用表示占位符,然后预编译SQL语句,给占位符赋值之后执行SQL语句。
常用方法

接口方法作用
ConnectionPreparedStatement prepareStatement(String sql)创建预编译的数据库操作对象
PreparedStatementvoid setString(int parameterIndex, String x)给占位符(索引从1开始)传值,要注意类型
PreparedStatementResultSet executeQuery()执行select查询语句
PreparedStatementint executeUpdate()执行insert、update、delete语句

示例(记得打开MySQL服务)

package JDBC;

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

public class UserLogin {
    public static void main(String[] args) {
        Map<String,String> loginme=getMessage();
        boolean flag=checkUser(loginme);
        if(flag)
            System.out.println("登陆成功");
        else
            System.out.println("登录失败");
    }
    /**
     * 收集用户登陆信息
     * @return 以哈希表的方式存储用户名和密码
     */
    static Map<String,String> getMessage(){
        Map<String,String> loginme=new HashMap<>();
        Scanner s=new Scanner(System.in);

        System.out.print("用户名:");
        String loginname=s.nextLine();
        System.out.print("密码:");
        String loginpassword=s.nextLine();

        loginme.put("loginname",loginname);
        loginme.put("loginpassword",loginpassword);

        return loginme;
    }

    /**
     * 在数据库中的user表中查询有无用户信息
     * @param loginme 登陆信息
     * @return 查询成功返回true,否则返回false
     */
    static boolean checkUser(Map<String,String> loginme){
        Connection mysqlconnection=null;
        PreparedStatement mysqlPstatement=null;
        ResultSet mysqlresult=null;
        boolean flag=false;
        try{
            //注册驱动并获取连接
            mysqlconnection=JDBCUtil.getConnection();
            //预编译并执行语句
            String loginname=loginme.get("loginname");
            String loginpassword=loginme.get("loginpassword");
            String sql="select * from user where name=? and password=?";
            mysqlPstatement=mysqlconnection.prepareStatement(sql);
            //给占位符传值,自动给字符串加''
            mysqlPstatement.setString(1,loginname);
            mysqlPstatement.setString(2,loginpassword);
            //执行sql语句
            mysqlresult=mysqlPstatement.executeQuery();
            if(mysqlresult.next())
                flag=true;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtil.close(mysqlconnection,mysqlPstatement,mysqlresult);
        }
        return flag;
    }
}

  PreparedStatementStatement相比,前者预编译一次可执行多次,效率较高;后者编译一次执行一次。当我们仅需要把值传入到SQL语句中时选择前者;当业务需要有SQL注入时选择后者。比如查询语句为select * from user order by name 字符串(表示升降序),用后者可以做到SQL语句拼接,让用户自己选择升序还是降序查询。

3、JDBC事务

  和MySQL一样,JDBC默认的事务行为是执行一条SQL语句就自动提交一次。实际业务中,有时需要多条语句联合执行,即开启事务机制。
常用方法

接口方法作用
Connectionvoid setAutoCommit(boolean autoCommit)将此连接的自动提交模式设置为给定状态
Connectionvoid commit()使自上次提交/回滚以来所做的所有更改都将永久性
Connectionvoid rollback()撤消在当前事务中所做的所有更改

4、悲观/乐观锁

  悲观锁:有多个事务时,当其中一个事务的SQL语句最后含有for update关键词时,代表给所选的数据加上了悲观锁,意味着其它事务在当前事务结束之前无法对上锁的数据操作。
  乐观锁:事务对数据进行SQL语句操作时会拿到一个当前版本号,修改数据之后再次查看版本号。若版本号没有发生改变,意味着没有其它事务对数据进行修改,那么就提交当前事务并修改版本号,否则回滚当前事务。

欢迎评论区交流~👍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值