JDBC 学习笔记

这篇文章是我对李刚老师的《疯狂Java讲义》第三版第十三章:MySQL 数据库与JDBC编程的学习总结。适合有一定SQL基础的同学,如果你还不是很了解SQL,建议去: w3school 熟悉一下SQL的概念和基本语法。本文更专注于数据库在Java程序中的实践。最近刚学会用Github和Hexo来搭建自己的个人博客,有兴趣的朋友可以关注一下,一起探讨技术。

SQL语句基础

语句 内容
查询语句 select
DML(Data Manipulation Language) insert, update, delete
DDL(Data Definition Language) create, alter, drop, truncate
DCL(Data Control Language) grant, revoke

JDBC 常用类

接口/类 解释
Driver Manager 用于管理JDBC驱动
Connection 数据库连接对象
Statement 用于执行SQL语句的工具接口
PreparedStatement 预编译的Statement对象
CallableStatement 用于调用储存过程
ResultSet 结果集对象

JDBC 编程步骤

  1. 加载数据库驱动: <code>Class.forName("com.mysql.jdbc.Driver");</code>

  2. 通过<code>DriverManager</code>获取数据库连接,获得<code>Connection</code>对象:
    <code>jdbc:mysql://hostname:port/databasename</code>

  3. 通过<code>Connection</code>对象创建<code>Statement</code>对象

  4. 使用Statement对象执行SQL语句

  • <code>execute()</code> 可执行任何SQL语句
  • <code>executeUpdate()</code> 用于执行DML和DDL语句。在执行DML语句返回受影响行数,执行DDL语句返回0。
  • <code>executeQuery()</code> 只执行查询语句,返回<code>ResultSet</code>对象
  1. 操作结果集
  • 程序可通过 <code>next()</code>, <code>previous()</code>, <code>first()</code>, <code>last()</code>, <code>beforeFirst()</code>, <code>beforeLast()</code>, <code>afterLast()</code>, <code>absolute()</code>等方法移动记录指针
  • 通过<code>getXXX(column index)</code>获取记录指针指向行、列的值。
  1. 回收数据库资源:关闭<code>ResultSet</code>、<code>Statement</code>、<code>Connection</code>等资源。

1. 用Properties类来加载属性文件

1.1 属性文件格式

drv = com.mysql.jdbc.Driver
url = jdbc:mysql://hostname:port/databasename
usr = username
pwd = password 

例如下面的配置建立的连接会占用电脑的3306端口,生成一个people_list数据库的<code>Connection</code>对象,我们把这个配置文件命名为<code>mysql.ini</code>并将其放置在项目的根目录中:

drv = com.mysql.jdbc.Driver
url = jdbc:mydql://127.0.0.1:3306/people_list
usr = enzeM
pwd = 1234567

需要注意的是 mysql 在计算机中的默认端口是3306,除非更改mysql的默认端口值,否则把配置文件端口设为其他端口会出现mysql连接异常

1.2 在Java程序中加载属性文件

private String drv;
private String url;
private String usr;
private String pwd;

void initConnFileds(String fileName) throws Exception {
    Properties p = new Properties();
    p.load(new FileInputStream(fileName));
    drv = p.getProperties("drv");
    url = p.getProperties("url");
    usr = p.getProperties("usr");
    pwd = p.getProperties("pwd");
    //step1: load the database
    Class.forName("mysql.ini")  
}

2. 通过JDBC实现数据库的基本操作

2.1 用Statement和execute()写一个可以执行任何SQL语句的函数

使用情境:

  • 不清楚SQL语句类型的情况下可以使用。
  • 使用getString()方法可以取得除Blob之外的任意类型列的值。
void executeSQL(String query) throws Exception {
    //step2: get Connection object
    try(Connection conn = DriverManager.getConnection(url, usr, pwd); 
        Statement stmt = conn.createStatement()) {//step3: get Statement object
        //step4: get ResutSet object
        boolean hasResultSet = stmt.execute(query);
        if(hasResultSet) {
            try(ResultSet rs = stmt.getResutSet()) {
                //use metadata object to visit all possible info from tables
                ResultSetMetaData rsmd = rs.getMetaData();
                int colunmCount = rsmd.getColumnCount();

                //print result after execute the query
                while(rs.next()) {
                    for(int i=0; i<colunmCount; i++) {
                        System.out.print(rs.getString(i+1) + "\t");
                    }
                    System.out.println();
                }
            }
        } else {
            System.out.println("this query effect "
                    + stmt.getUpdateCount() + " record(s) in the database");
        }
    }
}

上面的代码使用<code>execute()</code>来执行SQL语句返回了一个<code>boolean</code>值,它表明SQL是否返回了<code>ResultSet</code>对象。然后程序用<code>getResutSet()</code> 获取<code>Statement</code>执行查询语句后的<code>RsultSet</code>。使用<code>getUpdateCount()</code>获取<code>Statement</code> DML语句所影响的记录行数。

2.2 用PreparedStatement执行SQL语句

使用情境:

  • 适用于经常需要反复执行的一条语句
  • 使用占位符<code>?</code>代替需要输入的参数
  • 预编译SQL语句,性能更好
  • 防止SQL注入攻击,安全性高

<code>PreparedStatement</code>对象储存了预编译SQL语句,因此可以高效的反复执行该语句。
若清楚表格的参数类型,我们可以使用</code>setXXX(int index, XXX value)</code>的方法传入参若不清楚表格的参数类型,我们可以使用<code>setObject()</code>的方法传入参数。接下来,我们在数据库里建一个Employees,然后完成:1)加入新的employee record2)根据ID找出Employee在Table里记录的讯息

CREATE TABLE Employees (
    ID INT AUTO_INCREMENT PRIMARY KEY,
    Name VARCHAR(255) NOT NULL,
    Occupation VARCHAR(255),
    Salary VARCHAR(255)
)
  • 加入新的 employee record:
void insertNewEmployee(String name, String occupation, int salary) throws Exception {
    try(Connection conn = DriverManager.getConnection(url, usr, pwd); 
        PreparedStatement pstmt = conn.preparedStatement("INSERT INTO Employee VALUES(null, ?, ?, ?)")) {
        //pstmt.setXXX(index, value)
        pstmt.setString(1, "Tom");
        pstmt.setString(2, "Java Enginner");
        pstmt.setInt(3, 20000);
        pstmt.executeUpdate();
    }
}

这里的index指的是占位符<code>?</code>的位置,跟数组不同的是,SQL中的index相关的起始位置为1而不是0,比如column也是从0开始的。上面的代码用<code>PreparedStatement</code>的<code>setXXX()</code>方法添加了新的employee record并且在最后使用<code>executeUpdate()</code>更新了Table, 下面的代码实现了根据ID找出Employee在数据库里面的讯息

  • 根据ID找出Employee在Table里记录的讯息:
void findEmployeeBy(int id) throws Exception {
    try(Connection conn = DriverManager.getConnection(url, usr, pwd);
            PreparedStatement pstmt = conn.preparedStatement("SELECT * FROM Employee WHERE ID=?")) {
        pstmt.setInt(1, id); //replace ? to id parameter
        ResultSet rs = pstmt.executeQuery(); //get result set
        //if we know number of column in the table 
        /*
           if(rs.next()) {
           for(int i=0; i<4; i++) {
           System.out.print(rs.getString(i+1)+"\t");
           }
           System.out.println();
           }*/
        //if we do not know abut the table, we use metadata
        ResultSetMetaData rsmd = rs.getMetaData();
        int columnCount = rsmd.getColumnCount();
        if(rs.next()) {
            for(int i=0; i<columnCount; i++) {
                System.out.print(rs.getString(i+1)+"\t");
            }
            System.out.println();
        }
    }
}

2.3 用CallableStatement调用储存过程(Procedure)

使用场景:

  • 需要调用数据库中定义的procedure来处理数据

例子:建立一个储存过程:

delimiter //
create procedure diff_salary(a int, b int, out diff int)

begin
set diff = abs(a - b)
end;

以上例子在数据库里定义了一个procedure diff_salary 来计算两个input的差值。我们要通过程序调用这个procedure,需要使用<code>CallableStatement</code>,以下是具体步骤:

  • 使用<code>CallableStatement</code>调用 procedure:
  1. 用<code>Connection</code>对象的<code>prepareCall()</code>方法生成<code>CallableStatement</code>对象
  2. 用<code>CallableStatment</code>的<code>setXXX()</code>为procedure传入参数
  3. 用<code>rigisterOutParameter()</code>的方法来注册获取储存过程的值
  4. 用<code>CallStatement</code>的<code>execute()</code>方法来执行储存过程
  5. 用<code>getXXX(int index)</code>获取指定参数的值
void callDiffSalaryProc(int a, int b) throws Exception {
    try(Connection conn = DriverManager.getConnection(url, usr, pwd); 
            CallableStatment cstmt = conn.prepareCall("{call diff_salary(?, ?, ?)}")) { //注意这里使用的格式
        cstmt.setInt(1, a);
        cstmt.setInt(2, b);
        cstmt.registerOutParameter(3, TYPE.INTEGER);
        cstmt.execute();
        System.out.println("result is:"+cstmt.getInt(3));
    }
}

管理结果集(to be continue)

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

推荐阅读更多精彩内容