Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC是面向关系型数据库的,同时JDBC也是Hibernate、Mybatis等框架的基础。本文主要回顾下JDBC的基本知识。
JDBC使用步骤
我们都知道,使用JDBC不外乎遵循下面几个步骤:
- 加载特定的数据库驱动
- 获得连接对象
- 创建SQL语句
- 执行SQL语句(增、删、改、查)
- 拿到数据库中数据并进行相应的处理
- 关闭相关的资源
准备活动
要复习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' andtype
= '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的语法。为了避免篇幅过长,将更多用法如:批处理、事务和数据源等放在下一篇文章中。