JDBC回顾(一)

Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC是面向关系型数据库的,同时JDBC也是Hibernate、Mybatis等框架的基础。本文主要回顾下JDBC的基本知识。

JDBC使用步骤

我们都知道,使用JDBC不外乎遵循下面几个步骤:

  1. 加载特定的数据库驱动
  2. 获得连接对象
  3. 创建SQL语句
  4. 执行SQL语句(增、删、改、查)
  5. 拿到数据库中数据并进行相应的处理
  6. 关闭相关的资源

准备活动

要复习JDBC,离不开数据库,这里首先要用MySQL创建一张表跟插入几条数据:

<pre>
create database jdbc;
use jdbc;
#创建账户表
create table account(
user_id int primary key auto_increment comment '用户id',
user_name varchar(20) not null comment '用户名',
balance double not null default 0.0 comment '用户余额'
);
#插入几条数据
insert into account(user_name,balance) values('小明',1000.0),('小红',2000.0),('小李',1500.0);
</pre>

三种语句

JDBC在java.sql包中提供了三个语句接口供程序员使用,分别是Statement、PreparedStatement以及CallableStatement它们之间的类图如下:

即PreparedStatement接口继承了Statement接口,而CallableStatement接口又继承了PreparedStatement接口。至于三种接口有什么区别,请看下文。

Statement

Statement用于执行一条静态的SQL语句并返回它执行的结果。
下面来看一个最简单的JDBC例子:

<pre>
public class TestStatement {

    public static void main(String[] args) {
        final String URL="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false";
        final String USER="root";
        final String PASSWORD="123456";
        Connection conn=null;
        Statement stmt=null;
        ResultSet rs=null;
        
        try {
            //加载数据库驱动程序
            Class.forName("com.mysql.jdbc.Driver");
            //获得连接对象
            conn=DriverManager.getConnection(URL,USER,PASSWORD);
            //创建语句对象
            stmt=conn.createStatement();
            //执行一条查询的SQL语句,并获取结果集
            rs=stmt.executeQuery("select * from account");
            //对结果集进行处理
            while(rs.next()){
                int id=rs.getInt(1);
                String name=rs.getString("user_name");
                double balance=rs.getDouble("balance");
                System.out.println("id:"+id+" name:"+name+"  balance:"+balance);
            }
            //构造并执行一条插入的SQL语句
            String sql ="insert into account(user_name,balance) values('小王',500)";
            stmt.executeUpdate(sql);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();//可以用日志文件记录下来
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            //关闭相关的资源
            try{
                if(rs!=null){
                    rs.close();
                    rs=null;
                }
                if(stmt!=null){
                    stmt.close();
                    stmt=null;
                }
                if(conn!=null){
                    conn.close();
                    conn=null;
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

</pre>

运行上述代码,不出错的话会看到下列结果:

查看数据库,可以看到小王那条记录也成功插入了:

ps:不想在数据库工具与IDE来回切换的话可以在IDE(如eclipse,IDEA等)中进行相应配置,请自行搜索

然而,大部分我们不会直接使用Statement,而是使用运行速度更快且安全性更高的PreparedStatement。

PreparedStatement

PreparedStatement对象代表着一条预编译好的语句,因此相对于Statement执行效率更高。
下面看一个PreparedStatement的例子:

<pre>
public class TestPreparedStatement {
public static void main(String[] args) {
final String URL="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false";
final String USER="root";
final String PASSWORD="123456";
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
String s ="insert into account(user_name,balance) values(?,?)";
try {
Class.forName("com.mysql.jdbc.Driver");
conn=DriverManager.getConnection(URL,USER,PASSWORD);
pstmt=conn.prepareStatement(s);
pstmt.setString(1,"小吴");
pstmt.setDouble(2, 1000.0);
int number=pstmt.executeUpdate();
if(number>0){
System.out.println("插入记录成功!");
}
}
//...
}
}
</pre>

ps:为节省空间,省去catch语句块和finally语句块的内容

查看数据库,应该可以看到记录插入成功。

以下是PreparedStatement和Statement的主要区别:

  • PreparedStatement可以写动态参数化的查询(使用占位符"?")
  • 由于使用了预编译,PreparedStatement的运行速度更快
  • PreparedStatement能过滤掉特殊字符(如单引号等),因此能有效防止SQL注入等攻击,安全性更高

从上面几点来看,我们都应该尽可能地使用PreparedStatement。然而,如果要调用存储过程,还得用到PreparedStatement的子接口CallableStatement。

CallableStatement

简介

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

可以通过CallableStatement用来调用存储过程。先来看一下MySQL创建存储过程的语法:

<pre>
CREATE
[DEFINER = { user | CURRENT_USER }]
PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body

proc_parameter:
    [ IN | OUT | INOUT ] param_name type

characteristic:
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }

routine_body:
    Valid SQL routine statement

</pre>

下面使用MySQL创建三个存储过程,分别不带参数,带输出参数和带输入参数:

<pre>
#无参数存储过程(查询所有记录)
DELIMITER //
CREATE PROCEDURE select_nofilter()
BEGIN
SELECT * from account;
END
//
DELIMITER ;

#带输出参数的存储过程(查询记录条数)
DELIMITER //
CREATE PROCEDURE `select_count`(out count int)
BEGIN
    SELECT COUNT(*) into count from account;
END
//
DELIMITER ;

#带输入参数的存储过程(查询余额大于money的记录)
DELIMITER //
CREATE PROCEDURE `select_filter`(in money double)
BEGIN
    select * from account where balance > money;
END
//
DELIMITER ;

</pre>

在MySQL中调用存储过程可使用call命令,如:

<pre> call select_filter(1000);</pre>

查看存储过程有两种方法:

  • <pre> select name from mysql.proc where db = 'your_db_name' and type = 'PROCEDURE'</pre>
  • <pre> show procedure status;</pre>

查看存储过程创建:

<pre> show create procedure proc_name;</pre>

调用

接下来,使用JDBC调用存储过程。
为了方便,先创建一个Connection工具类:

<pre>
public class ConnectionUtil {
private final static String URL="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
private final static String USER="root";
private final static String PASSWORD="123456";

    private static Connection conn=null;
    
    static{
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    public static Connection getConnection(){
        if(conn!=null){
            return conn;
        }
        
        try {
            conn=DriverManager.getConnection(URL,USER,PASSWORD);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}

</pre>

不带参数的存储过程

调用不带参数的存储过程核心代码:

<pre>
public static void callProcedureWithoutP() throws SQLException {
Connection conn=ConnectionUtil.getConnection();
CallableStatement cstmt=conn.prepareCall("call select_nofilter()");
cstmt.execute();
ResultSet rs=cstmt.getResultSet();
while(rs.next()){
System.out.println("name:"+rs.getString("user_name")+" balance:"+rs.getString("balance"));
}
}
</pre>

结果:

带输入参数的存储过程

调用带输入参数的存储过程核心代码:

<pre>
public static void callProcedureWithIn(double money) throws SQLException {
Connection conn=ConnectionUtil.getConnection();
CallableStatement cstmt=conn.prepareCall("call select_filter(?)");
cstmt.setDouble(1, money);
cstmt.execute();
ResultSet rs=cstmt.getResultSet();
while(rs.next()){
System.out.println("name:"+rs.getString("user_name")+" balance:"+rs.getString("balance"));
}
}
</pre>

结果:

带输出参数的存储过程

调用带输出参数的存储过程核心代码:

<pre>
public static void callProcedureWithOut() throws SQLException {
Connection conn=ConnectionUtil.getConnection();
CallableStatement cstmt=conn.prepareCall("call select_count(?)");
cstmt.registerOutParameter(1, Types.INTEGER);
cstmt.execute();
int rowNumber=cstmt.getInt(1);
System.out.println("rowNumber:"+rowNumber);
}
</pre>

结果:

总结

以上是JDBC的基本用法,包括主要包括JDBC编程步骤,三种Statement使用方法和一些MySQL的语法。为了避免篇幅过长,将更多用法如:批处理、事务和数据源等放在下一篇文章中。

参考

维基百科

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

推荐阅读更多精彩内容

  • 本节介绍Statement接口及其子类PreparedStatement和CallableStatement。 它...
    zlb阅读 1,134评论 0 0
  • JDBC概述 在Java中,数据库存取技术可分为如下几类:JDBC直接访问数据库、JDO技术、第三方O/R工具,如...
    usopp阅读 3,530评论 3 75
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,429评论 0 4
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,587评论 18 399
  • 傅盛 1 预测未来,看到趋势,三年内会发生什么(金山和小米,移动互联网全球化) 放下安全感,找到趋势比勤奋工作更重...
    荣码人生阅读 357评论 0 0