jdbc知识点(连接mysql)
jdbc连接mysql
1.JDBC简介
JDBC:
指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库。从根本上来说,JDBC 是一种规范,它提供了一套完整的接口,允许便携式访问到底层数据库
常见的 JDBC 组件:
JDBC 的 API 提供了以下接口和类:
DriverManager :这个类管理一系列数据库驱动程序。匹配连接使用通信子协议从 JAVA 应用程序中请求合适的数据库驱动程序。识别 JDBC 下某个子协议的第一驱动程序将被用于建立数据库连接。
Driver : 这个接口处理与数据库服务器的通信。你将很少直接与驱动程序互动。相反,你使用 DriverManager 中的对象,它管理此类型的对象。它也抽象与驱动程序对象工作相关的详细信息。
Connection : 此接口具有接触数据库的所有方法。该连接对象表示通信上下文,即,所有与数据库的通信仅通过这个连接对象进行。
Statement : 使用创建于这个接口的对象将 SQL 语句提交到数据库。除了执行存储过程以外,一些派生的接口也接受参数。
ResultSet : 在你使用语句对象执行 SQL 查询后,这些对象保存从数据获得的数据。它作为一个迭代器,让您可以通过它的数据来移动。
SQLException : 这个类处理发生在数据库应用程序的任何错误。
JDBC 4.0 软件包:
JDBC 4.0 主要包含 java.sql 包和 javax.sql 包,提供的主要类与数据源进行交互。
2.JDBC SQL语法
SQL语法:
结构化查询语言(SQL)是一种标准化的语言,可以让你对数据库进行curd操作,如创建项目,读取内容,更新内容和删除项目。SQL 支持你可能会使用到的任何数据库,它可以让你编写独立于底层数据库的数据库代码
创建数据库语法:create database 数据库名;
如:create database hejh;
删除数据库语法:drop database 数据库名;
如:drop database hejh;
注意:1.创建或删除数据库,必须有数据库服务器的管理员权限;
2.删除数据库会把存储在该数据库中的数据一并删除。
创建表语法:
create table 表名(
字段名1 字段类型1,
字段名2 字段类型2,
......
);
建表示例:
create table hejh(
id int not null,
age int not null,
frist varchar(20),
last varchar(20),
primary key(id)
);
删除表语法:drop table 表名;
如:drop table hejh;
insert语法:insert into 表名 values(字段值1,字段值2,...); --(这种方式默认插入所有字段的值)
如:insert into hejh values(1,22,'19990405','20190505');
select语法:select 字段1,字段2,... from 表名 where 查询限制条件;
如:select * from hejh where id=1;
注意:WHERE 子句可以使用 =,!=,<,>,<=,>=,BETWEEN 和 LIKE 这些比较操作符。
update语法:updata 表名 set 字段=字段新值 where 条件;
如:update hejh set age=33 where id=1;
注意:where子句可以使用=,!=,<,>,<=,>=,between和 like 这些比较操作符。
delete语法:delete from 表名 where 条件;
如:delete from hejh where id=1;
注意:where子句可以使用=,!=,<,>,<=,>=,between 和 like 这些比较操作符
3.设置JDBC使用环境
在开始使用 JDBC 之前,必须设置 JDBC 环境。(以mysql、windows系统为例)
1.安装jdk,配置环境变量;
2.安装mysql数据库;
3.安装eclipse;
4.JDBC简单示例
创建数据库:hejh;
create database hejh;
创建用户表:user表
create table user(
id int(5) not null,
username varchar(20) not null,
gender varchar(4),
primary key(id)
);
初始化数据:
创建JDBC应用程序
构建一个 JDBC 应用程序包括以下六个步骤:
-
导入数据包:导入含有需要进行数据库编程的 JDBC 类的包。大多数情况下,使用 import java.sql.就足够了。
-
注册 JDBC 驱动器:需要你初始化一个驱动器,以便于你打开一个与数据库的通信通道。
-
打开连接:需要使用 DriverManager.getConnection() 方法创建一个 Connection 对象,它代表与数据库的物理连接。
-
执行查询:需要使用类型声明的对象建立并提交一个 SQL 语句到数据库。
-
提取结果数据:要求使用适当的 ResultSet.getXXX() 方法从结果集中检索数据。
- 清理环境:依靠 JVM 的垃圾收集来关闭所有需要明确关闭的数据库资源。
查看数据库hejh中user表的数据有3条:
java源码如下:
package com.hejh.day0506; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.junit.Test; public class JdbcTest1 { static final String driver = "com.mysql.jdbc.Driver"; static final String url = "jdbc:mysql://localhost/hejh"; static final String user = "root"; static final String password = "root"; //开启junit局部测试 @Test public void run() { ResultSet rs=null; Statement st = null; Connection conn =null; try { //注册驱动 Class.forName(driver); //获取连接 conn = DriverManager.getConnection(url, user, password); //获取sql执行对象 st = conn.createStatement(); //编写sql语句 String sql = "select * from user"; //执行sql语句,返回一个结果集 rs = st.executeQuery(sql); //处理结果集 while(rs.next()) { int id = rs.getInt("id"); String name = rs.getString("username"); String gender = rs.getString("gender"); System.out.println("["+id+","+name+","+gender+"]"); } } catch (Exception e) { e.printStackTrace(); }finally {
//关闭资源,后开启的先关闭 try { if(rs!=null) { rs.close(); }else { rs=null; } } catch (SQLException e) { e.printStackTrace(); } if(st!=null) { try { st.close(); } catch (SQLException e) { e.printStackTrace(); } }else { st=null; } if(conn!=null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }else { conn=null; } } } }
junit局部测试结果如下:
第一个jdbc案例完成。
4.JDBC 驱动类型
什么是 JDBC 驱动程序?
JDBC 驱动实现了 JDBC API 中定义的接口,该接口用于与数据库服务器进行交互。例如,使用 JDBC 驱动程序可以让你打开数据库连接,并通过发送 SQL 或数据库命令,然后通过 Java 接收结果。java.sql 包中附带的 JDK,包含了定义各种类与他们的行为和实际实现,这些类都在第三方驱动程序中完成。第三方供应商在他们的数据库驱动程序中都实现了 java.sql.Driver 接口。
JDBC驱动程序类型:
JDBC 驱动程序的实现,因为各种各样的操作系统和 Java 运行在硬件平台的不同而不同。Sun 公司将实现类型分为四类:
1.JDBC-ODBC桥驱动程序
2.JDBC-Native API
3.JDBC-Net纯Java
4.100%纯Java
具体参见资料:https://www.w3cschool.cn/jdbc/j3xk1myd.html
该使用哪种驱动程序?
如果你正在访问一个数据库,如 Oracle,Sybase 或 IBM,首选的驱动程序是类型4。
如果你的 Java 应用程序同时访问多个数据库类型,类型3是首选的驱动程序。
类型2驱动程序是在你的数据库没有提供类型3或类型4驱动程序时使用的。
类型1驱动程序不被认为是部署级的驱动程序,它存在的目的通常仅用于开发和测试。
5.JDBC连接数据库
编写建立一个 JDBC 连接的程序是相当简单的,下面是简单的四个步骤:
-
导入 JDBC 包:在你的 Java 代码中,用 import 语句添加你所需的类。
-
注册 JDBC 驱动程序:这一步会导致 JVM 加载所需的驱动程序到内存中执行,因此它可以实现你的 JDBC 请求。
-
数据库 URL 制定:这是用来创建格式正确的地址指向你想要连接的数据库。
- 创建连接对象:最后,代码调用 DriverManager 对象的 getConnection() 方法来建立实际的数据库连接。
1.导入jar包
import 语句告诉 Java 编译器在哪里可以找到你在代码中引用的类,这些引用放置在你的源代码起始位置。使用标准的 JDBC 包,它允许你选择,插入,更新和删除 SQL 表中的数据,添加以下引用到您的源代码中。
import java.sql.*;
import javax.sql.*;
2.注册JDBC驱动程序
方法1 - Class.forName()
注册一个驱动程序中最常用的方法是使用 Java 的Class.forName() 方法来动态加载驱动程序的类文件到内存中,它会自动将其注册。这种方法更优越一些,因为它允许你对驱动程序的注册信息进行配置,便于移植。
//注册驱动
Class.forName(“com.mysql.jdbc.Driver”);//会报异常
方法2 - DriverManager.registerDriver()
你注册一个驱动程序的第二种方法是使用静态 staticDriverManager.registerDriver() 方法。如果你使用的是不兼容 JVM 的非 JDK,比如微软提供的,你必须使用 registerDriver() 方法
try {
Driver myDriver = new oracle.jdbc.driver.OracleDriver();
DriverManager.registerDriver( myDriver );
}
catch(ClassNotFoundException ex) {
System.out.println("Error: unable to load driver class!");
System.exit(1);
}
3.数据库url制定
当你加载了驱动程序之后,你可以通过 DriverManager.getConnection() 方法建立一个连接。
为方便参考,以下列出了三个加载 DriverManager.getConnection() 方法:
- getConnection(String url)
- getConnection(String url, Properties prop)
- getConnection(String url, String user, String password)
在这里,每个格式需要一个数据库 URL ,数据库 URL 是指向数据库的地址。在建立一个数据连接的时候,大多数会在配置一个数据库 URL 时遇到问题。下表列出了常用的 JDBC 驱动程序名和数据库URL。
RDBMS | JDBC 驱动程序名称 | URL 格式 |
---|---|---|
MySQL | com.mysql.jdbc.Driver | jdbc:mysql://hostname/ databaseName |
ORACLE | oracle.jdbc.driver.OracleDriver | jdbc:oracle:thin:@hostname:port Number:databaseName |
DB2 | COM.ibm.db2.jdbc.net.DB2Driver | jdbc:db2:hostname:port Number/databaseName |
Sybase | com.sybase.jdbc.SybDriver | jdbc:sybase:Tds:hostname: port Number/databaseName |
URL 格式所有加粗的部分都是静态的,你需要将剩余部分按照你的数据库实际情况进行设置。
4.创建连接对象
调用适当的用户名和密码以及 getConnection() 方法来获得一个 Connection 对象
static final String url = "jdbc:mysql://localhost/hejh";
static final String user = "root";
static final String password = "root";
Connection conn = DriverManager.getConnection(url, user, password);
使用数据库 URL 和 Properties 对象:
第三种 DriverManager.getConnection() 方法调用需要数据库 URL 和 Properties 对象-
DriverManager.getConnection(String url, Properties info);
Properties 对象保存了一组关键数值。它通过调用 getConnection() 方法,将驱动程序属性传递给驱动程序。使用下面的代码可以建立与上述示例相同的连接:
import java.util.*;
String URL = "jdbc:mysql://localhost/hejh";
Properties info = new Properties( );
info.put( "user", "username" );
info.put( "password", "password" );
Connection conn = DriverManager.getConnection(URL, info);
5.关闭 JDBC 连接
在 JDBC 程序的末尾,必须明确关闭所有连接到数据库的连接,以结束每个数据库会话。但是,如果忘了,Java 垃圾收集器也会关闭连接,它会完全清除过期的对象。依托垃圾收集器,特别是在数据库编程,是非常差的编程习惯。你应该养成用 close()方法关闭连接对象的习惯。为了确保连接被关闭,你可以在代码中的 'finally' 程序块中执行。 无论异常是否发生,finally 程序是肯定会被执行的。要关闭上面打开的连接,应该调用 close()方法,如下所示:
finally {
try {
if(rs!=null) {
rs.close();
}else {
rs=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
if(st!=null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}else {
st=null;
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}else {
conn=null;
}
}
6.JDBC Statement 对象
1.Statement 对象
获得了数据库的连接,就可以和数据库进行交互了。JDBC 的 Statement,CallableStatement 和 PreparedStatement 接口定义的方法和属性,可以发送 SQL 命令或 PL/SQL 命令到数据库,并从数据库接收数据。在数据库中,它们还定义了帮助 Java 和 SQL 数据类型之间转换数据差异的方法。下表提供了每个接口的用途概要,根据实际目的决定使用哪个接口。
接口 | 推荐使用 |
---|---|
Statement | 可以正常访问数据库,适用于运行静态 SQL 语句。 Statement 接口不接受参数。 |
PreparedStatement | 计划多次使用 SQL 语句, PreparedStatement 接口运行时接受输入的参数。 |
CallableStatement | 适用于当你要访问数据库存储过程的时候, CallableStatement 接口运行时也接受输入的参数。 |
创建 Statement 对象:
使用 Statement 对象执行 SQL 语句之前,需要用 Connection 对象创建一个 Statement()对象,如下面的示例所示:
Statement stmt = null;
try {
stmt = conn.createStatement( );
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
当创建了一个 Statement 对象之后,可以用它的三个执行方法的任一方法来执行 SQL 语句:
-
boolean execute(String SQL) : 如果 ResultSet 对象可以被检索,则返回的布尔值为 true ,否则返回 false 。当你需要使用真正的动态 SQL 时,可以使用这个方法来执行 SQL DDL 语句。
-
int executeUpdate(String SQL) : 返回执行 SQL 语句影响的行的数目。使用该方法来执行 SQL 语句,是希望得到一些受影响的行的数目,例如,INSERT,UPDATE 或 DELETE 语句。
- ResultSet executeQuery(String SQL) : 返回一个 ResultSet 对象。当你希望得到一个结果集时使用该方法,就像你使用一个 SELECT 语句。
关闭 Statement 对象:
正如关闭一个 Connection 对象来节约数据库资源,出于同样的原因你也应该关闭 Statement 对象。简单的调用 close() 方法就可以完成这项工作。如果关闭了 Connection 对象,那么它也会关闭 Statement 对象。然而,你应该始终明确关闭 Statement 对象,以确保真正的清除。
Statement stmt = null;
try {
stmt = conn.createStatement( );
. . .
}
catch (SQLException e) {
. . .
}
finally {
stmt.close();//会报异常
}
PreparedStatement 对象:
PreparedStatement 接口扩展了 Statement 接口,它让你用一个常用的 Statement 对象增加几个高级功能。这个 statement 对象可以提供灵活多变的动态参数。创建 PreparedStatement 对象:
PreparedStatement pstmt = null;
try {
String SQL = "update user set age = ? where id = ?";
pstmt = conn.prepareStatement(SQL);
psmt.setInt(1,22);
psmt.setInt(2,1);
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
JDBC 中所有的参数都被用 ? 符号表示,这是已知的参数标记。在执行 SQL 语句之前,你必须赋予每一个参数确切的数值。setXXX() 方法将值绑定到参数,其中 XXX 表示你希望绑定到输入参数的 Java 数据类型。如果你忘了赋予值,你将收到一个 SQLException。每个参数标记映射它的序号位置。第一标记表示位置 1 ,下一个位置为 2 等等。这种方法不同于 Java 数组索引是从 0 开始的。所有的 Statement对象 的方法都与数据库交互,(a) execute(),(b) executeQuery(),及 (c) executeUpdate() 也能被 PreparedStatement 对象引用。然而,这些方法被 SQL 语句修改后是可以输入参数的。
关闭 PreparedStatement 对象
关闭 PreparedStatement 对象,简单的调用 close() 方法可以完成这项工作。如果你关闭了 Connection 对象,那么它也会关闭 PreparedStatement 对象。然而,你应该始终明确关闭 PreparedStatement 对象,以确保真正的清除。
PreparedStatement pstmt = null;
try {
String SQL = "Update Employees SET age = ? WHERE id = ?";
pstmt = conn.prepareStatement(SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
pstmt.close();//会报异常
}
CallableStatement 对象:
正如一个 Connection 对象可以创建 Statement 对象和 PreparedStatement 对象,它也可以创建被用来执行调用数据库存储过程的 CallableStatement 对象。
在 MySQL 的 EMP 数据库中创建相同的存储过程:
DELIMITER $$
DROP PROCEDURE IF EXISTS `EMP`.`getEmpName` $$
CREATE PROCEDURE `EMP`.`getEmpName`
(IN EMP_ID INT, OUT EMP_FIRST VARCHAR(255))
BEGIN
SELECT first INTO EMP_FIRST
FROM Employees
WHERE ID = EMP_ID;
END $$
DELIMITER ;
三种类型的参数有:IN,OUT 和 INOUT。PreparedStatement 对象只使用 IN 参数。CallableStatement 对象可以使用所有的三个参数。这里是每个参数的定义:
参数 | 描述 |
---|---|
IN | 在 SQL 语句创建的时候该参数是未知的。你可以用 setXXX() 方法将值绑定到IN参数中。 |
OUT | 该参数由 SQL 语句的返回值提供。你可以用 getXXX() 方法获取 OUT 参数的值。 |
INOUT | 该参数同时提供输入输出的值。你可以用 setXXX() 方法将值绑定参数,并且用 getXXX() 方法获取值。 |
下面的代码片段展示了基于存储过程如何使用 Connection.prepareCall() 方法来实例化 CallableStatement 对象。
CallableStatement cstmt = null;
try {
String SQL = "{call getEmpName (?, ?)}";
cstmt = conn.prepareCall (SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
7.结果集
结果集:
SQL 语句从数据库查询中获取数据,并将数据返回到结果集中。SELECT 语句是一种标准的方法,它从一个数据库中选择行记录,并显示在一个结果集中。 java.sql.ResultSet 接口表示一个数据库查询的结果集。一个 ResultSet 对象控制一个光标指向当前行的结果集。术语“结果集”是指包含在 ResultSet 对象中的行和列的数据。
ResultSet 接口的方法可细分为三类:
- 导航方法:用于移动光标。
- 获取方法:用于查看当前行被光标所指向的列中的数据。
- 更新方法:用于更新当前行的列中的数据。这些更新也会更新数据库中的数据。
光标的移动基于 ResultSet 的属性。用相应的语句生成 ResultSet 对象时,同时生成 ResultSet 的属性。JDBC 提供了连接方法通过下列创建语句来生成你所需的 ResultSet 对象:
- createStatement(int RSType, int RSConcurrency);
- prepareStatement(String SQL, int RSType, int RSConcurrency);
- prepareCall(String sql, int RSType, int RSConcurrency);
第一个参数表示 ResultSet 对象的类型,第二个参数是两个 ResultSet 常量之一,该常量用于判断该结果集是只读的还是可修改的
ResultSet的类型:
可能的 RSType 如下所示。如果你不指定 ResultSet 类型,将自动获得的值是 TYPE_FORWARD_ONLY。
类型 | 描述 |
---|---|
ResultSet.TYPE_FORWARD_ONLY | 光标只能在结果集中向前移动。 |
ResultSet.TYPE_SCROLL_INSENSITIVE | 光标可以向前和向后移动。当结果集创建后,其他人对数据库的操作不会影响结果集的数据。 |
ResultSet.TYPE_SCROLL_SENSITIVE. | 光标可以向前和向后移动。当结果集创建后,其他人对数据库的操作会影响结果集的数据。 |
ResultSet的并发性:
RSConcurrency 的值如下所示,如果你不指定并发类型,将自动获得的值是 CONCUR_READ_ONLY。
并发性 | 描述 |
---|---|
ResultSet.CONCUR_READ_ONLY | 创建一个只读结果集,这是默认的值。 |
ResultSet.CONCUR_UPDATABLE | 创建一个可修改的结果集。 |
示例可以如下所示,初始化一个 Statement 对象,来创建一个只能前进而且只读的 ResultSet 对象:
try {
Statement stmt = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
catch(Exception ex) {
....
}
finally {
....
}
导航结果集:
在 ResultSet 接口中包括如下几种方法涉及移动光标-
S.N. | 方法 & 描述 |
---|---|
1 | public void beforeFirst() throws SQLException
将光标移动到第一行之前。 |
2 | public void afterLast() throws SQLException
将光标移动到最后一行之后。 |
3 | public boolean first() throws SQLException
将光标移动到第一行。 |
4 | public void last() throws SQLException
将光标移动到最后一行。 |
5 | public boolean absolute(int row) throws SQLException
将光标移动到指定的第 row 行。 |
6 | public boolean relative(int row) throws SQLException
将光标移动到当前指向的位置往前或往后第 row 行的位置。 |
7 | public boolean previous() throws SQLException
将光标移动到上一行,如果超过结果集的范围则返回 false。 |
8 | public boolean next() throws SQLException
将光标移动到下一行,如果是结果集的最后一行则返回 false。 |
9 | public int getRow() throws SQLException
返回当前光标指向的行数的值。 |
10 | public void moveToInsertRow() throws SQLException
将光标移动到结果集中指定的行,可以在数据库中插入新的一行。当前光标位置将被记住。 |
11 | public void moveToCurrentRow() throws SQLException
如果光标处于插入行,则将光标返回到当前行,其他情况下,这个方法不执行任何操作。 |
查看结果集:
ResultSet接口中含有几十种从当前行获取数据的方法。每个可能的数据类型都有一个 get 方法,并且每个 get 方法有两个版本-
- 一个需要列名。
- 一个需要列的索引。
例如,如果你想查看的列包含一个 int 类型,你需要在 ResultSet 中调用 getInt()方法-
S.N. | 方法 & 描述 |
---|---|
1 | public int getInt(String columnName) throws SQLException
返回当前行中名为 columnName 的列的 int 值。 |
2 | public int getInt(int columnIndex) throws SQLException
返回当前行中指定列的索引的 int 值。列索引从 1 开始,意味着行中的第一列是 1 ,第二列是 2 ,以此类推。 |
同样的,在 ResultSet 接口中还有获取八个 Java 原始类型的 get 方法,以及常见的类型,比如 java.lang.String,java.lang.Object 和java.net.URL。
也有用于获取 SQL 数据类型 java.sql.Date, java.sql.Time, java.sql.Timestamp, java.sql.Clob,java.sql.Blob 中的方法。查看文档可以了解使用这些 SQL 数据类型的更多的信息。
更新的结果集:
ResultSet 接口包含了一系列的更新方法,该方法用于更新结果集中的数据。用 get 方法可以有两个更新方法来更新任一数据类型:
- 一个需要列名。
- 一个需要列的索引。
例如,要更新一个结果集的当前行的 String 列,你可以使用任一如下所示的 updateString()方法-
S.N. | 方法 & 描述 |
---|---|
1 | public void updateString(int columnIndex, String s) throws SQLException
将指定列的字符串的值改为 s。 |
2 | public void updateString(String columnName, String s) throws SQLException
类似于前面的方法,不同之处在于指定的列是用名字来指定的,而不是它的索引。 |
八个原始数据类型都有其更新方法,比如 String,Object,URL,和在 java.sql 包中的 SQL 数据类型。
更新结果集中的行将改变当前行的列中的 ResultSet 对象,而不是基础数据库中的数据。要更新数据库中一行的数据,你需要调用以下的任一方法:
S.N. | 方法 & 描述 |
---|---|
1 | public void updateRow()
通过更新数据库中相对应的行来更新当前行。 |
2 | public void deleteRow()
从数据库中删除当前行。 |
3 | public void refreshRow()
在结果集中刷新数据,以反映数据库中最新的数据变化。 |
4 | public void cancelRowUpdates()
取消对当前行的任何修改。 |
5 | public void insertRow()
在数据库中插入一行。本方法只有在光标指向插入行的时候才能被调用。 |
8.JDBC数据类型
JDBC 驱动程序在将 Java 数据类型数据发送到数据库之前,会将其转换为相应的 JDBC 类型数据,对于大多数数据类型都采用了默认的映射关系。例如,一个 Java int 数据类型转换为 SQL INTEGER。通过默认的映射关系来提供驱动程序之间的一致性。当你调用 PreparedStatement 中的 setXXX()方法或 CallableStatement 对象或 ResultSet.updateXXX()方法时, Java 数据类型会转换为默认的 JDBC 数据类型,如下表概述。
SQL | JDBC/Java | setXXX | updateXXX |
---|---|---|---|
VARCHAR | java.lang.String | setString | updateString |
CHAR | java.lang.String | setString | updateString |
LONGVARCHAR | java.lang.String | setString | updateString |
BIT | boolean | setBoolean | updateBoolean |
NUMERIC | java.math.BigDecimal | setBigDecimal | updateBigDecimal |
TINYINT | byte | setByte | updateByte |
SMALLINT | short | setShort | updateShort |
INTEGER | int | setInt | updateInt |
BIGINT | long | setLong | updateLong |
REAL | float | setFloat | updateFloat |
FLOAT | float | setFloat | updateFloat |
DOUBLE | double | setDouble | updateDouble |
VARBINARY | byte[ ] | setBytes | updateBytes |
BINARY | byte[ ] | setBytes | updateBytes |
DATE | java.sql.Date | setDate | updateDate |
TIME | java.sql.Time | setTime | updateTime |
TIMESTAMP | java.sql.Timestamp | setTimestamp | updateTimestamp |
CLOB | java.sql.Clob | setClob | updateClob |
BLOB | java.sql.Blob | setBlob | updateBlob |
ARRAY | java.sql.Array | setARRAY | updateARRAY |
REF | java.sql.Ref | SetRef | updateRef |
STRUCT | java.sql.Struct | SetStruct | updateStruct |
JDBC 3.0 增强了对 BLOB,CLOB,ARRAY 和 REF 数据类型的支持。 ResultSet 对象现在有 UPDATEBLOB(),updateCLOB(), updateArray(),和 updateRef()方法,通过这些方法你可以直接操作服务器上的相应数据。
你能用 setXXX()方法和 updateXXX()方法将 Java 类型转换为特定的 JDBC 数据类型。你能用 setObject()方法和 updateObject()方法将绝大部分的 Java 类型映射到 JDBC 数据类型。
ResultSet 对象为任一数据类型提供相应的 getXXX()方法,该方法可以获取任一数据类型的列值。上述任一方法的使用需要列名或它的顺序位置。
SQL | JDBC/Java | setXXX | getXXX |
---|---|---|---|
VARCHAR | java.lang.String | setString | getString |
CHAR | java.lang.String | setString | getString |
LONGVARCHAR | java.lang.String | setString | getString |
BIT | boolean | setBoolean | getBoolean |
NUMERIC | java.math.BigDecimal | setBigDecimal | getBigDecimal |
TINYINT | byte | setByte | getByte |
SMALLINT | short | setShort | getShort |
INTEGER | int | setInt | getInt |
BIGINT | long | setLong | getLong |
REAL | float | setFloat | getFloat |
FLOAT | float | setFloat | getFloat |
DOUBLE | double | setDouble | getDouble |
VARBINARY | byte[ ] | setBytes | getBytes |
BINARY | byte[ ] | setBytes | getBytes |
DATE | java.sql.Date | setDate | getDate |
TIME | java.sql.Time | setTime | getTime |
TIMESTAMP | java.sql.Timestamp | setTimestamp | getTimestamp |
CLOB | java.sql.Clob | setClob | getClob |
BLOB | java.sql.Blob | setBlob | getBlob |
ARRAY | java.sql.Array | setARRAY | getARRAY |
REF | java.sql.Ref | SetRef | getRef |
STRUCT | java.sql.Struct | SetStruct | getStruct |
日期和时间数据类型:
java.sql.Date 类映射 SQL DATE 类型,java.sql.Time 类和 java.sql.Timestamp 类也分别映射 SQL TIME 数据类型和 SQL TIMESTAMP 数据类型。以下示例显示了日期和时间类如何转换成标准的 Java 日期和时间值,并匹配成 SQL 数据类型所要求的格式。
package com.hejh.day0507;
import java.util.Date;
public class TimeTest {
public static void main(String[] args) {
//java util 打印当前具体时间
Date utilDate = new Date();
long utilTime = utilDate.getTime();
System.out.println("java util date:"+utilDate.toString());//java util date:Tue May 07 17:14:53 CST 2019
//sql 打印当前时间(某年-某月-某日)
java.sql.Date sqlDate = new java.sql.Date(utilTime);
System.out.println("java sql date:"+sqlDate.toString());//java sql date:2019-05-07
//sql 打印当前时间(时:分:秒)
java.sql.Time sqlTime = new java.sql.Time(utilTime);
System.out.println("java sql time:"+sqlTime.toString());//java sql time:17:34:33
//sql 打印当前具体时间(某年-某月-某日 时:分:秒)
java.sql.Timestamp sqlTimeStamp = new java.sql.Timestamp(utilTime);
System.out.println("java sql Timestamp:"+sqlTimeStamp.toString());//java sql Timestamp:2019-05-07 17:36:44.176
}
}
处理 NULL 值:
SQL 使用 NULL 值和 Java 使用 null 是不同的概念。那么,你可以使用三种策略来处理 Java 中的 SQL NULL 值:
- 避免使用返回原始数据类型的 getXXX()方法。
- 使用包装类的基本数据类型,并使用 ResultSet 对象的 wasNull()方法来测试收到 getXXX()方法返回的值是否为 null,如果是 null,该包装类变量则被设置为 null。
- 使用原始数据类型和 ResultSet 对象的 wasNull()方法来测试通过 getXXX()方法返回的值,如果是 null,则原始变量应设置为可接受的值来代表 NULL。
下面是一个处理 NULL 值的示例:
Statement stmt = conn.createStatement( );
String sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
int id = rs.getInt(1);
if( rs.wasNull( ) ) {
id = 0;
}
9.JDBC事务
事务:
如果 JDBC 连接是处于自动提交模式下,该模式为默认模式,那么每句 SQL 语句都是在其完成时提交到数据库。对简单的应用程序来说这种模式相当好,但有三个原因你可能想关闭自动提交模式,并管理你自己的事务:
- 为了提高性能
- 为了保持业务流程的完整性
- 使用分布式事务
你可以通过事务在任意时间来控制以及更改应用到数据库。它把单个 SQL 语句或一组 SQL 语句作为一个逻辑单元,如果其中任一语句失败,则整个事务失败。
若要启用手动事务模式来代替 JDBC 驱动程序默认使用的自动提交模式的话,使用 Connection 对象的的 setAutoCommit()方法。如果传递一个布尔值 false 到 setAutoCommit()方法,你就关闭自动提交模式。你也可以传递一个布尔值 true 将其再次打开。例如,如果有一个名为 conn 的 Connection 对象,以下的代码将关闭自动提交模式:
conn.setAutoCommit(false);//关闭自动提交模式
提交和回滚:
当完成修改,并且要提交修改,可以在 connection 对象里调用 commit()方法,如下所示:
conn.commit( );
在默认情况下,事务自动提交,源码和数据库数据如下:
package com.hejh.day0508;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
public class AffairTest {
static final String driver = "com.mysql.jdbc.Driver";
static String url = "jdbc:mysql://localhost/hejh";
static final String username = "root";
static final String password = "root";
@Test
public void run() {
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
String sql = "update user set username = ? where id = ? ";
ps = conn.prepareStatement(sql);
ps.setString(1,"hejh11");
ps.setInt(2, 1);
int i = ps.executeUpdate();
System.out.println("影响了:"+i+"行"); //影响了:1行
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(ps!=null) {
ps.close();
}else {
ps=null;
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if(conn!=null) {
conn.close();
}else {
conn=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}//finally
}
}
设置为手动提交,运行下面的源码(替换try-catch部分代码),数据库数据刷新结果如下:
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
String sql = "update user set username = ? where id = ? ";
//在执行sql之前,设置事务为手动提交
conn.setAutoCommit(false);//手动提交事务,数据不会发生变化
ps = conn.prepareStatement(sql);
ps.setString(1,"hejh111");
ps.setInt(2, 1);
int i = ps.executeUpdate();
System.out.println("影响了:"+i+"行"); //影响了:1行
} catch (Exception e) {
e.printStackTrace();
}
设置为手动提交,并且在sql执行后,手动提交一下事务,运行下面的源码(替换try-catch部分代码),数据库数据刷新结果如下:
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
String sql = "update user set username = ? where id = ? ";
//在执行sql之前,设置事务为手动提交
conn.setAutoCommit(false);//手动提交事务,数据不会发生变化
ps = conn.prepareStatement(sql);
ps.setString(1,"hejh111");
ps.setInt(2, 1);
int i = ps.executeUpdate();
//sql语句执行后,手动提交事务,使sql生效
conn.commit();
System.out.println("影响了:"+i+"行"); //影响了:1行
} catch (Exception e) {
e.printStackTrace();
}
另外,用名为 conn 的连接回滚数据到数据库,
conn.rollback( );//可以放在catch块中
还原点:
新的 JDBC 3.0 还原点接口提供了额外的事务控制。大部分现代的数据库管理系统的环境都支持设定还原点,例如 Oracle 的 PL/SQL。当你在事务中设置一个还原点来定义一个逻辑回滚点。如果在一个还原点之后发生错误,那么可以使用 rollback 方法来撤消该还原点之后所做的修改。
Connection 对象有两个新的方法来管理还原点:
-
setSavepoint(String savepointName): 定义了一个新的还原点,返回一个 Savepoint 对象。
- releaseSavepoint(Savepoint savepointName): 删除一个还原点。请注意,参数是一个 Savepoint 对象,这个对象通常是由 setSavepoint() 方法生成的一个还原点。
有一个 rollback (String savepointName) 方法,该方法可以回滚到指定的还原点。下面的例子说明了如何使用 Savepoint 对象:
package com.hejh.day0508;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;
import org.junit.Test;
public class SavePointTest {
@Test
public void save() throws SQLException {
Connection conn = null;
PreparedStatement ps =null;
Savepoint beforeInsert = null;
try {
Class.forName("com.mysql.jdbc.Driver");
//prepareStatement插入的中文参数有可能会出现问号,可以在这里指定一下字符编码为utf-8
conn = DriverManager.getConnection("jdbc:mysql://localhost/hejh?useUnicode=true&characterEncoding=UTF-8",
"root", "root");
//设置一个还原点
beforeInsert = conn.setSavepoint();
String sql = "insert into user values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setInt(1,4);
ps.setString(2, "hejh222");
ps.setString(3, "男");
int i = ps.executeUpdate();
System.out.println("插入了:"+i+"行数据");
} catch (Exception e) {
//回滚到还原点BeforeInsert处
conn.rollback(beforeInsert);
e.printStackTrace();
}finally {
try {
if(ps!=null) {
ps.close();
}else {
ps = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(beforeInsert!=null) {
conn.releaseSavepoint(beforeInsert);
}else {
beforeInsert=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null) {
conn.close();
}else {
conn=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
10.JDBC异常
异常:
异常处理可以允许你处理一个异常情况,例如可控方式的程序定义错误。当异常情况发生时,将抛出一个异常。抛出这个词意味着当前执行的程序停止,控制器被重定向到最近的适用的 catch 子句。如果没有适用的 catch 子句存在,那么程序执行被终止。JDBC 的异常处理是非常类似于 Java 的异常处理,但对于 JDBC,最常见的异常是 java.sql.SQLException。
SQLException:
SQLException 异常在驱动程序和数据库中都可能出现。当出现这个异常时,SQLException 类型的对象将被传递到 catch 子句。传递的 SQLException 对象具有以下的方法,以下的方法可用于检索该异常的额外信息:
方法 | 描述 |
---|---|
getErrorCode( ) | 获取与异常关联的错误号。 |
getMessage( ) | 获取 JDBC 驱动程序的错误信息,该错误是由驱动程序处理的,或者在数据库错误中获取 Oracl 错误号和错误信息。 |
getSQLState( ) | 获取 XOPEN SQLstate 字符串。对于 JDBC 驱动程序错误,使用该方法不能返回有用的信息。对于数据库错误,返回第五位的 XOPEN SQLstate 代码。该方法可以返回 null。 |
getNextException( ) | 获取异常链的下一个 Exception 对象。 |
printStackTrace( ) | 打印当前异常或者抛出,其回溯到标准的流错误。 |
printStackTrace(PrintStream s) | 打印该抛出,其回溯到你指定的打印流。 |
printStackTrace(PrintWriter w) | 打印该抛出,其回溯到你指定的打印写入。 |
通过利用可从 Exception 对象提供的信息,你可以捕获异常并继续运行程序。这是一个 try 块的一般格式:
try {
// 可能出现异常的代码
}
catch(Exception ex) {
//异常的捕捉和处理
}
finally {// 必须执行的操作,想数据库的资源关闭,
}
package com.hejh.day0508;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;
import org.junit.Test;
public class ExceptionTest {
@Test
public void save() throws SQLException {
Connection conn = null;
PreparedStatement ps =null;
Savepoint beforeInsert = null;
try {
Class.forName("com.mysql.jdbc.Driver");
//1.数据库用户名和密码不符(root,hejh),实际用户名密码为(root,root)
conn = DriverManager.getConnection("jdbc:mysql://localhost/hejh?useUnicode=true&characterEncoding=UTF-8", "root", "hejh");
String sql = "insert into user values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setInt(1,4);
ps.setString(2, "hejh200");
ps.setString(3, "男");
int i = ps.executeUpdate();
System.out.println("插入了:"+i+"行数据");
} catch (Exception e) {
//2.在这里捕获异常,并将异常信息打印到控制台
e.printStackTrace();
}finally {
try {
if(ps!=null) {
ps.close();
}else {
ps = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(beforeInsert!=null) {
conn.releaseSavepoint(beforeInsert);
}else {
beforeInsert=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null) {
conn.close();
}else {
conn=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
出现以下异常:
java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
......
11.JDBC批处理
批处理:
批处理是指你将关联的 SQL 语句组合成一个批处理,并将他们当成一个调用提交给数据库。当你一次发送多个 SQL 语句到数据库时,可以减少通信的资源消耗,从而提高了性能。
-
JDBC 驱动程序不一定支持该功能。你可以使用 DatabaseMetaData.supportsBatchUpdates() 方法来确定目标数据库是否支持批处理更新。如果你的JDBC驱动程序支持此功能,则该方法返回值为 true。
-
Statement,PreparedStatement 和 CallableStatement 的 addBatch() 方法用于添加单个语句到批处理。
-
executeBatch() 方法用于启动执行所有组合在一起的语句。
-
executeBatch() 方法返回一个整数数组,数组中的每个元素代表了各自的更新语句的更新数目。
- 正如你可以添加语句到批处理中,你也可以用 clearBatch() 方法删除它们。此方法删除所有用 addBatch() 方法添加的语句。但是,你不能有选择性地选择要删除的语句。
Statement 对象执行批处理:
使用 Statement 对象来使用批处理所需要的典型步骤如下所示-
- 使用 createStatement() 方法创建一个 Statement 对象。
- 使用 setAutoCommit() 方法将自动提交设为 false。
- 被创建的 Statement 对象可以使用 addBatch() 方法来添加你想要的所有SQL语句。
- 被创建的 Statement 对象可以用 executeBatch() 将所有的 SQL 语句执行。
- 最后,使用 commit() 方法提交所有的更改。
批量执行3条insert语句,示例如下:
package com.hejh.day0508;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
import org.omg.Messaging.SyncScopeHelper;
//批处理
public class Batching {
@Test
public void test() {
Connection conn = null;
Statement st = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取链接
conn = DriverManager.getConnection("jdbc:mysql://localhost/hejh?useUnicode=true&characterEncoding=UTF-8", "root", "root");
//设置为:手动提交事务
conn.setAutoCommit(false);
//编写3个insert sql语句
String sql1 = "insert into user values(4,'sss','女')";
String sql2 = "insert into user values(5,'www','男')";
String sql3 = "insert into user values(6,'yyy','女')";
//获取statement对象
st = conn.createStatement();
//添加sql语句到批处理
st.addBatch(sql1);
st.addBatch(sql2);
st.addBatch(sql3);
//执行批处理,返回一个整数数组,数组中的每个元素代表了各自的更新语句的更新数目
int[] arr = st.executeBatch();
//事务提交,使sql语句生效
conn.commit();
//处理数组
System.out.print("影响行数为:");
for(int i=0;i<arr.length;i++) {
System.out.print(arr[i]+" ");//影响行数为:1 1 1
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(st!=null) {
st.close();
}else {
st = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null) {
conn.close();
}else {
conn = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
批处理和 PrepareStatement 对象:
使用 prepareStatement 对象来使用批处理需要的典型步骤如下所示:
- 使用占位符创建 SQL 语句。
- 使用任一 prepareStatement() 方法创建 prepareStatement 对象。
- 使用 setAutoCommit() 方法将自动提交设为 false。
- 被创建的 Statement 对象可以使用 addBatch() 方法来添加你想要的所有 SQL 语句。
- 被创建的 Statement 对象可以用 executeBatch() 将所有的 SQL 语句执行。
- 最后,使用 commit() 方法提交所有的更改。
示例如下:
package com.hejh.day0508;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
public class PrepareStatementBatching {
@Test
public void test() {
Connection conn = null;
PreparedStatement ps = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取链接
conn = DriverManager.getConnection("jdbc:mysql://localhost/hejh?useUnicode=true&characterEncoding=UTF-8", "root", "root");
//设置为:手动提交事务
conn.setAutoCommit(false);
//编写sql语句
String sql = "update user set username=? where id=?";
//获取statement对象
ps = conn.prepareStatement(sql);
//添加第一个sql语句到批处理
ps.setString(1, "s");
ps.setInt(2, 4);
ps.addBatch();
//添加第二个sql语句到批处理
ps.setString(1, "w");
ps.setInt(2, 5);
ps.addBatch();
//添加第三个sql语句到批处理
ps.setString(1, "y");
ps.setInt(2, 6);
ps.addBatch();
//执行批处理,返回一个整数数组,数组中的每个元素代表了各自的更新语句的更新数目
int []arr = ps.executeBatch();
//提交事务,使sql语句生效
conn.commit();
//处理数组
System.out.print("分别影响了:");
for(int i=0;i<arr.length;i++) {
System.out.print(arr[i]+" "); //分别影响了:1行 1行 1行
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(ps!=null) {
ps.close();
}else {
ps = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null) {
conn.close();
}else {
conn = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
12.JDBC流数据
流数据:
PreparedStatement 对象必须具备使用输入和输出流来提供参数数据的能力。这使你能够将整个文件存储到数据库列中,这样数据库就能存储大型数据,例如 CLOB 和 BLOB 数据类型。
用于流数据有下列几种方法:
- setAsciiStream(): 该方法是用来提供较大的 ASCII 值。
- setCharacterStream(): 该方法是用来提供较大的 UNICODE 值。
- setBinaryStream(): 该方法是用来提供较大的二进制值。
setXXXStream()方法需要一个额外的参数,该参数是除了参数占位符的文件大小。这个参数通知驱动程序通过使用流有多少数据被发送到数据库中。
假如我们到要上传一个名为 hejh.xml 的 XML 文件到数据库的表中。下面是该 XML 文件的内容
<?xml version="1.0"?>
<hejh>
<id>1</id>
<username>hjh</username>
<gender>男</gender>
</hejh>
将该 xml_data.xml文件和要运行的示例保存在相同的目录的。这个示例将创建一个数据库表 xml_data ,然后 xml_data.xml 将被上传到该表中。
将下面的示例拷贝并粘帖到 JDBCStreamingDate.java 中,编译并运行它,如下所示:
package com.hejh.day0509;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCStreamingDate {
public static void main(String[] args)throws SQLException, IOException {
Connection conn = null;
Statement st = null;
PreparedStatement pps = null;
ResultSet rs = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost/hejh?useUnicode=true&characterEncoding=UTF-8", "root", "root");
//获取 Statement 对象,来创建一张xml_data表
st = conn.createStatement();
createXMLTable(st);
//将xml_data.xml文件读取进来
File file = new File("src/xml_data.xml");
long fileLength = file.length();
FileInputStream fis = new FileInputStream(file);
//获取 对象,给表插入数据,并执行sql
String sql = "insert into xml_data values(?,?)";
pps = conn.prepareStatement(sql);
pps.setInt(1, 100);
pps.setAsciiStream(2, fis,(int)fileLength );
pps.execute();
//关闭输入流
fis.close();
String selectSql = "select data from xml_data where id =100";
rs = st.executeQuery(selectSql);
InputStream is = null;
ByteArrayOutputStream bos = null;
if(rs.next()) {
is = rs.getAsciiStream(1);
int c ;
//获取输出流,将带数据的hejh.xml文件输出
bos = new ByteArrayOutputStream();
while((c=is.read())!=-1) {
bos.write(c);
}
}
System.out.println(bos.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {//关闭资源
if(rs!=null) {
rs.close();
}else {
rs = null;
}
if(pps!=null) {
pps.close();
}else {
pps = null;
}
if(st!=null) {
st.close();
}else {
st = null;
}
if(conn!=null) {
conn.close();
}else {
conn = null;
}
}
}
//创建一张表,表名xml_data
public static void createXMLTable(Statement st) throws SQLException {
//sql语句,创建一张表xml_data
String createTable = "create table xml_data(id int,data long)";
//删除表xml_data
String dropTable = "drop table xml_data";
//在建表前,应该先删除表,如果存在相同的表名的话
try {
st.execute(dropTable);
} catch (SQLException e) {
e.printStackTrace();
}
//创建表
st.executeUpdate(createTable);
}
}
console输出为:
<?xml version="1.0" encoding="UTF-8"?>
<hejh>
<id>1</id>
<username>hjh</username>
<gender>男</gender>
</hejh>
数据库表现为:
本博文参考w3cschool网JDBC指南一文,附上网址:https://www.w3cschool.cn/jdbc/bgqu1my6.html