JDBC

JDBC

JDBC:Java DataBase Connectivity

JDBC的本质是什么?JDBC是SUN公司制定的一套接口(interface)
接口都有调用者和实现者。面向接口调用、面向接口写实现类,这都是面向接口编程。

为什么要面向接口编程呢?
解耦合:降低程序的耦合度,提供程序的扩展力。多态机制就是非常典型的面相抽象编程
建议:

Animal a = new Cat();
Animal a = new Dog();

public void feed(Animal a){// 面向父类型编程}

不建议

Cat a = new Cat();
Dog a = new Dog();

public void feed(Cat a){}
public void feed(Dog a){}

因此SUN要提供一套JDBC接口给各大厂家去实现,我们只需面向接口去写代码就可以了,减少开发成本。

JDBC.png

JDBC编程六步(需要熟记)

1、注册驱动(作用:告诉Java程序,即将要连接的是哪个厂家的数据库)
2、获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级,使用完之后一定要关闭)
3、获取数据库操作对象(专门执行sql语句的对象)
4、执行sql语句(DDL,DQL,DML...)
5、处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集)
6、释放资源(使用完资源之后一定要关闭资源)

第一种注册方式

import java.sql.*;

public class JDBCTest01 {

    public static void main(String[] args) throws SQLException {
        // 1.注册驱动
        Driver driver = new com.mysql.jdbc.Driver();  // 多态,父类型引用指向子类对象
        DriverManager.registerDriver(driver);
        // 2.获取连接
        String url = "jdbc:mysql://127.0.0.1:3306/crashcourse";
        String user = "root";
        String password = "root";
        Connection conn = DriverManager.getConnection(url, user, password);  // 多态
        // com.mysql.jdbc.JDBC4Connection@42110406
        System.out.println(conn);
        // 3.获取连接库操作对象(Statement专门执行sql语句的)
        Statement stmt = conn.createStatement();
        // 4.执行sql
        String sql = "select * from products";
        ResultSet rs = stmt.executeQuery(sql);
        System.out.println(rs.getFetchSize());
        // 5.释放资源
        stmt.close();
        conn.close();
    }
}

第二种注册方式(建议使用)

  • class文件加载到内存时,会执行静态代码块
  • 将驱动和数据库的连接信息放到配置文件中

新建jdbc.properties文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/crashcourse
user=root
password=root

具体操作:

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

public class JDBCTest02 {

    public static void main(String[] args) throws Exception {
        // 1.注册驱动
        // 使用资源绑定器绑定属性配置文件
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String driver = bundle.getString("driver");
        String url = bundle.getString("url");
        String user = bundle.getString("user");
        String password = bundle.getString("password");
        Class.forName(driver);
        // 2.获取连接
        Connection conn = DriverManager.getConnection(url, user, password);
        // com.mysql.jdbc.JDBC4Connection@42110406
        System.out.println(conn);
    }
}

Class.forName(driver)的内部执行原理就是加载时执行了com.mysql.jdbc.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!");
        }
    }
}

查询结果集ResultSet
方法:

  • getString(int a):更加列的索引取值(默认从第一列开始),返回的是字符串
  • getString(String column):根据列名取值
  • getInt(String column):如果数据库里的字段存的是int,可以用该方法返回int类型
        // 4.执行sql
        String sql = "select * from products";
        ResultSet rs = stmt.executeQuery(sql);
        boolean flag1 = rs.next();  // 将游标指向第一行(初始在第0行)
        // 5.处理结果集
        System.out.println(rs.getString(1));  // 获取第一列数据
        System.out.println(rs.getInt(2));  // 获取第二列数据
 
        while (rs.next()){
            System.out.println(rs.getString(1));
        }

SQL注入

在拼接sql语句的时候,传入了恶意的代码,导致返回了预料之外的结果,如:

        String id = "1";
        String userName = "'123@qq.com' or 1=1";
        String sql = "select * from user where id=" + id + " and user_name=" + userName;
        System.out.println(sql);  // select * from user where id=1 and user_name='123@qq.com' or 1=1

当执行完这条sql之后,会将user这张表里的所有数据都查出来。

Statement和PreparedStatement

  • Statement的缺陷:容易导致SQL注入;使用PreparedStatement,可以对sql语句进行预编译。
  • Statement是编译一次执行一次,PreparedStatement是编译一次,可执行N次,因为下次我只要传值过去就行了。PreparedStatement效率高一些。
  • PreparedStatement会在编译阶段做类型的安全检查
        Connection conn = DriverManager.getConnection(url, user, password);)
        String sql = "select * from products where vend_id=?";
        // 执行到此次,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预编译
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setInt(1, 1001);
        ResultSet rs = ps.executeQuery();
        System.out.println(rs);

        while (rs.next()){
            System.out.println(rs.getString(1));
        }

注意:防止SQL注入的功能是MySQL内部机制提供的,不是我们写的Java类去实现的。底层原理是使用MySQL的预编译
1.执行预编译语句,例如:prepare showUsersByLikeName from 'select * from user where username like ?';
2.设置变量,例如:set @username='%小明%';
3.执行语句,例如:execute showUsersByLikeName using @username;

详细了解底层原理请参考:JDBC:深入理解PreparedStatement和Statement

JDBC事务

jdbc中的事务是自动提交的,即执行一条DML语句则自动提交一次。但在实际的业务中,通常都是N条DML语句一起提交的(如银行转账功能)
conn.setAutoCommit(false):开启事务
conn.commit():提交事务
conn.rollback():回滚事务

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.ResourceBundle;

public class JDBCTest03 {
    public static void main(String[] args) throws Exception{
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String driver = bundle.getString("driver");
        String url = bundle.getString("url");
        String user = bundle.getString("user");
        String password = bundle.getString("password");
        Class.forName(driver);
        // 获取连接
        Connection conn = DriverManager.getConnection(url, user, password);
        try{
            conn.setAutoCommit(false);
            String sql = "update products set prod_name=? where prod_id=?";
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, "xxx");
            ps.setString(2, "FB");
            int count = ps.executeUpdate();
//            String s = null;  制造异常
//            s.toString();
            conn.commit();

        }catch (Exception e){
            System.out.println("回滚数据");
            conn.rollback();
        }finally {
            conn.close();
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356