目录
1. 基本使用
2. 事务
3. 批处理
4. LOB(Large Objects,大型对象)大数据
安装好数据库后,应用是不能直接使用数据库的,需要通过相应的数据库驱动程序去和数据库打交道。SUN公司为了统一、简化对数据库的操作,定义了一套Java操作数据库的接口规范JDBC(Java Data Base Connectivity)。JDBC接口的具体实现(即数据库驱动程序)由各数据库厂商去完成,开发人员只需调用JDBC,JDBC会加载具体的驱动去操作对应的数据库。
因此,开发JDBC应用需要导入相应的数据库驱动依赖包。
1. 基本使用(以mysql数据库为例)
下载mysql-connector-java-8.0.11.jar(mysql数据库驱动), 选Platform Indepentdent。放入WEB-INF的lib目录下
注意:
1. mysql8.0及之后的版本的数据库连接由com.mysql.jdbc.Driver更换为com.mysql.cj.jdbc.Driver,且需要显式关闭SSL连接和指定CST。
- 示例(简单使用)
- 创建数据库、表、插入数据
在终端输入sql语句:
create database test character set utf8 collate utf8_general_ci;
use Test;
create table users(
id int primary key,
name varchar(40),
password varchar(40),
email varchar(60),
birthday date
);
insert into users(id,name,password,email,birthday) values(1,'zhansan','123456','[email protected]','2220-12-04');
insert into users(id,name,password,email,birthday) values(2,'lisi','123456','[email protected]','2222-12-04');
insert into users(id,name,password,email,birthday) values(3,'wangwu','123456','[email protected]','2222-12-04');
可使用Navicat Premium可视化工具(localhost 3306 root 数据库密码) 连接本地数据库查看数据库中的数据。
- 创建数据库操作类
package com.sst.cx;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class TestJSDBCModel {
public static void main(String[] args) throws Exception {
// 1. 加载驱动
// 推荐使用这种方式来加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.1 设置 要连接的数据库URL
String url = "jdbc:mysql://localhost:3306/test";
// 2.2 设置 连接的数据库时使用的用户名
String username = "root";
// 2.3 设置 连接的数据库时使用的密码
String password = "密码";
// 2.4 获取 与数据库的链接
Connection conn = DriverManager.getConnection(url, username, password);
// 3. 获取 用于向数据库发送sql语句的statement
Statement st = conn.createStatement();
// -----------增-----------
String insertSql = "insert into user(….) values(…..) ";
int num = st.executeUpdate(insertSql);
if(num>0){
System.out.println("插入成功!!!");
}
// -----------删-----------
String sql = "delete from user where id=1";
int deleNum = st.executeUpdate(sql);
if(deleNum>0){
System.out.println("删除成功!!!");
}
// -----------改-----------
String updateSql = "update user set name='' where name=''";
int updateNum = st.executeUpdate(updateSql);
if(updateNum>0){
System.out.println("修改成功!!!");
}
// -----------查-----------
String querySql = "select id,name,password,email,birthday from users";
// 4. 向数据库发送sql语句,并获取代表结果集的resultset
ResultSet rs = st.executeQuery(querySql);
// 5. 取出结果集中的数据
while(rs.next()){
// 获取 字段值
System.out.println("id=" + rs.getObject("id"));
System.out.println("name=" + rs.getObject("name"));
System.out.println("password=" + rs.getObject("password"));
System.out.println("email=" + rs.getObject("email"));
System.out.println("birthday=" + rs.getObject("birthday"));
}
// 6. 关闭链接,释放资源
// 释放资源代码应放在finally语句中,以确保一定会执行)
rs.close();
st.close();
conn.close();
}
}
使用到的类
1. DriverManager(用来加载驱动、创建与数据库的连接)
1. DriverManager.registerDriver(new com.mysql.jdbc.Driver())
加载数据库驱动(不推荐使用该方式来加载驱动,推荐使用Class.forName("com.mysql.jdbc.Driver");灵活性高)
1. 通过Driver的源码可知,这种方式会导致驱动程序注册两次(在内存中会有两个Driver对象)。
2. 程序依赖mysql的API,脱离mysql的jar包,程序将无法编译。切换底层数据库将会非常麻烦。
2. DriverManager.getConnection(url, user, password)
获取与数据库的链接
2. 数据库URL
协议:子协议://主机:端口号/数据库名?参数名:参数值
用于标识数据库的类型和位置。
说明:
协议固定为jdbc,子协议为各数据库规定的协议名。
主机、端口号分别为服务器的ip地址和端口号。
常用数据库URL:
1. Oracle数据库:jdbc:oracle:thin:@localhost:1521:test
2. SqlServer数据库:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=test
3. MySql数据库:jdbc:mysql://localhost:3306/test(连接本地数据库且端口为3306时,可简写为:jdbc:mysql:///test)
3. Connection类(代表与数据库的链接)最重要的一个对象
如果Connection不能及时正确关闭,极易导致系统宕机。应尽量晚创建早释放。
1. createStatement():创建向数据库发送sql语句的Statement对象。
2. prepareStatement(sql) :创建向数据库发送预编译sql语句的PreparedStatement对象(避免了Statement对象的SQL注入问题、频繁编译SQL造成数据库缓存溢出问题)。PreparedStatement对象可对SQL进行预编译从而提高执行效率,对于sql中的参数允许使用占位符的形式进行替换从而简化了sql语句的编写。
3. prepareCall(sql):创建执行存储过程的callableStatement对象。
4. setAutoCommit(boolean autoCommit):设置事务是否自动提交。
5. commit() :在此链接上提交事务。
6. rollback() :在此链接上回滚事务。
4. Statement类(用于向数据库发送sql语句)
1. executeQuery(String sql) :用于向数据发送查询语句(返回ResultSet结果集)。
2. executeUpdate(String sql):用于向数据库发送insert、update或delete语句,返回一个整数(数据库中有几行数据发生变化)。
3. execute(String sql):用于向数据库发送任意sql语句。
4. addBatch(String sql) :把多条sql语句放到一个批处理中。
5. executeBatch():向数据库发送一批sql语句执行。
5. ResultSet类(存储了SQL语句的执行结果)
采用类似表格的方式封装执行结果。维护了一个指向表格数据行的游标,初始时游标在第一行之前。
1. 获取数据
// 获取任意类型的数据
getObject(int index)
getObject(string columnName)
// 获取指定类型的数据
getString(int index)
getString(String columnName)
...
2. 操作结果集
1. next():移动到下一行
2. Previous():移动到前一行
3. absolute(int row):移动到指定行
4. beforeFirst():移动resultSet的最前面。
5. afterLast() :移动到resultSet的最后面。
可能遇到的错误:
1. java.lang.UnsupportedClassVersionError。解决:
换jdk版本为高版本即可(MyEclipse|设置|Java|JRE换jdk版本)。
- 示例(规范使用)
第1步. 创建db.properties
在src下创建db.properties文件,内容如下:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=密码
第2步. 创建数据库处理类
package com.sst.cx;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static{
try{
// 读取db.properties文件中的数据库连接信息
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);
// 获取数据库连接驱动
driver = prop.getProperty("driver");
// 获取数据库连接URL地址
url = prop.getProperty("url");
// 获取数据库连接用户名
username = prop.getProperty("username");
// 获取数据库连接密码
password = prop.getProperty("password");
// 加载数据库驱动
Class.forName(driver);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
// 获取数据库连接对象
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username,password);
}
// 释放资源:Connection数据库连接对象、负责执行SQL命令的Statement对象、存储查询结果的ResultSet对象
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close(); // 关闭存储查询结果的ResultSet对象
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close(); // 关闭负责执行SQL命令的Statement对象
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close(); // 关闭Connection数据库连接对象
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
第3步. 使用
package com.sst.cx;
import java.sql.*;
public class TestJdbcM {
public static void main(String[] args){
(new TestJdbcM()).insert();
}
// 增
public void insert(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
// 获取一个数据库连接
conn = JdbcUtils.getConnection();
// 通过conn对象创建负责执行SQL命令的Statement对象
st = conn.createStatement();
// 要执行的SQL命令
String sql = "insert into users(id,name,password,email,birthday) values(6,'ss','123','[email protected]','2222-02-02')";
// 执行插入操作,executeUpdate方法返回成功的条数
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
// SQL执行完成之后释放相关资源
JdbcUtils.release(conn, st, rs);
}
}
// 删
public void delete(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "delete from users where id=3";
st = conn.createStatement();
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("删除成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
// 改
public void update(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "update users set name='hello',email='[email protected]' where id=3";
st = conn.createStatement();
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("更新成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
// 查
public void find(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from users where id=3";
st = conn.createStatement();
rs = st.executeQuery(sql);
if(rs.next()){
System.out.println(rs.getString("name"));
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
第3步. 另一种使用方式 (占位符)
public class JdbcCRUDByPreparedStatement {
// 增
public void insert(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
// 获取一个数据库连接
conn = JdbcUtils.getConnection();
// 要执行的SQL命令,SQL中的参数使用?作为占位符
String sql = "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)";
// 通过conn对象获取负责执行SQL命令的prepareStatement对象
st = conn.prepareStatement(sql);
// 为SQL语句中的参数赋值,注意,索引是从1开始的
/**
* SQL语句中各个字段的类型如下:
* +----------+-------------+
| Field | Type |
+----------+-------------+
| id | int(11) |
| name | varchar(40) |
| password | varchar(40) |
| email | varchar(60) |
| birthday | date |
+----------+-------------+
*/
st.setInt(1, 1); // id是int类型的
st.setString(2, "hello");// name是varchar(字符串类型)
st.setString(3, "123");// password是varchar(字符串类型)
st.setString(4, "[email protected]");// email是varchar(字符串类型)
st.setDate(5, new java.sql.Date(new Date().getTime()));// birthday是date类型
int num = st.executeUpdate(); // 执行插入操作,executeUpdate方法返回成功的条数
if(num>0){
System.out.println("插入成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs); // SQL执行完成之后释放相关资源
}
}
// 删
public void delete(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "delete from users where id=?";
st = conn.prepareStatement(sql);
st.setInt(1, 1);
int num = st.executeUpdate();
if(num>0){
System.out.println("删除成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
// 改
public void update(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "update users set name=?,email=? where id=?";
st = conn.prepareStatement(sql);
st.setString(1, "hello");
st.setString(2, "[email protected]");
st.setInt(3, 2);
int num = st.executeUpdate();
if(num>0){
System.out.println("更新成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
// 查
public void find(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from users where id=?";
st = conn.prepareStatement(sql);
st.setInt(1, 1);
rs = st.executeQuery();
if(rs.next()){
System.out.println(rs.getString("name"));
}
}catch (Exception e) {
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
获取数据库自动生成的主键
sql
create table test{
id int primary key auto_increment,
name varchar(20)
}
代码
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
String sql = "insert into test(name) values(?)";
st = conn.prepareStatement(sql);
st.setString(1, "zhangsan");
st.executeUpdate();
// 获取数据库自动生成的主键
rs = st.getGeneratedKeys();
if(rs.next()){
System.out.println(rs.getInt(1));
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
示例(JSTL标签)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*,java.sql.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
JDBC
<%--
JDBC驱动名、数据库 URL、数据库的用户名与密码
useUnicode=true&characterEncoding=utf-8 防止中文乱码
--%>
<%-- 插入数据
INSERT INTO website (NAME,url,age,country) VALUES ('京东', 'https://www.jd.com/', 15, 'CN');
--%>
<%-- 删除website表中id为9数据
DELETE FROM website WHERE id = ?
--%>
<%-- 修改website表中id为8数据
UPDATE website SET name="biancheng" WHERE id = ?
--%>
<%-- 查询数据 --%>
SELECT * from website;
id
name
url
age
country
2. 事务
事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。
- 概念
1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。
2. 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。以转账为例子,A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性。
3. 隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
4. 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
隔离性详解
事务的隔离级别
多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。
事务不考虑隔离性可能会引发的问题:
1、脏读
脏读指一个事务读取了另外一个事务未提交的数据。
这是非常危险的,假设A向B转帐100元,对应sql语句如下所示
1. update account set money=money+100 where name='B';
2. update account set money=money-100 where name='A';
当第1条sql执行完,第2条还没执行(A未提交时),如果此时B查询自己的帐户,就会发现自己多了100元钱。如果A等B走后再回滚,B就会损失100元。
2、不可重复读
不可重复读指在一个事务内读取表中的某一行数据,多次读取结果不同。
3、虚读(幻读)
虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
MySQL数据库定义了四种事务隔离级别:
1. Read uncommitted(最低级别),会引发脏读、不可重复读、虚读。
A窗口
set transaction isolation level read uncommitted;--设置A用户的数据库隔离级别为Read uncommitted(读未提交)
start transaction;--开启事务
select * from account;--查询A账户中现有的钱,转到B窗口进行操作
select * from account--发现a多了100元,这时候A读到了B未提交的数据(脏读)
B窗口
start transaction;--开启事务
update account set money=money+100 where name='A';--不要提交,转到A窗口查询
2. Read committed时,会引发不可重复读和虚读,但避免了脏读
A窗口
set transaction isolation level read committed;
start transaction;
select * from account;--发现a帐户是1000元,转到b窗口
select * from account;--发现a帐户多了100,这时候,a读到了别的事务提交的数据,两次读取a帐户读到的是不同的结果(不可重复读)
B窗口
start transaction;
update account set money=money+100 where name='aaa';
commit;--转到a窗口
3. Repeatable read(mysql默认级别)时,会引发虚读,但避免了脏读、不可重复读。
A窗口
set transaction isolation level repeatable read;
start transaction;
select * from account;--发现表有4个记录,转到b窗口
select * from account;--可能发现表有5条记录,这时候发生了a读取到另外一个事务插入的数据(虚读)
B窗口
start transaction;
insert into account(name,money) values('ggg',1000);
commit;--转到a窗口
4. Serializable时,会避免所有问题。
A窗口
set transaction isolation level Serializable;
start transaction;
select * from account;--转到b窗口
B窗口
start transaction;
insert into account(name,money) values('ggg',1000);--发现不能插入,只能等待a结束事务才能插入
终端查询MySQL当前事务隔离级别:select @@tx_isolation
MySQL默认事务隔离级别:Repeatable read(可重复读)
MySQL设置事务隔离级别:set transaction isolation level 隔离级别名
- 使用事务
/* 终端下在MySQL数据库中使用事务。
// 开启事务
start transaction;
// 执行语句列表...
// 回滚(在捕获到异常后)会回滚之前执行的sql语句。
rollback;
// 提交事务(未提交时关闭终端,之前执行的sql语句并不会被持久化到数据库中)
commit;
*/
代码中通过JDBC使用事务
1. 开启事务(禁止自动提交,默认情况下执行后会立即向数据库提交SQL)
conn.setAutoCommit(false);
2. 手动回滚事务(执行SQL语句时出现异常后会自动回滚,也可以手动回滚)
conn.rollback();
3. 提交事务
conn.commit();
手动设置事物回滚点
// 设置事务回滚点(在有可能发生异常的SQL语句前设置)
sp = conn.setSavepoint();
在捕获到异常中+
// 回滚到设置的事务回滚点(之前的SQL语句是没问题的,回滚后要提交事务)
conn.rollback(sp);
// 回滚之后,通知数据库提交正常的SQL部分。
conn.commit();
示例
public void testTransaction1(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);// 通知数据库开启事务(start transaction)
String sql1 = "update account set money=money-100 where name='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
String sql2 = "update account set money=money+100 where name='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
conn.commit(); // 提交事务
System.out.println("成功!!!");
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
3. 批处理
开发项目时,有时需要向数据库发送一批SQL语句执行,这时应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
JDBC实现批处理有两种方式:Statement 和 Preparedstatement。
- Statement完成批处理
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
// 1. 添加要批量执行的SQL
st.addBatch(sql1);
st.addBatch(sql2);
// 2. 执行批处理SQL语句
st.executeBatch();
// 3. 清除批处理命令
st.clearBatch();
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
优点:
1. 可以向数据库发送多条不同的SQL语句。
缺点:
1. SQL语句没有预编译。
2. 当向数据库发送仅参数不同的多条SQL语句时,需重复写上很多条SQL语句。
- PreparedStatement完成批处理
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
String sql = "insert into testbatch(id,name) values(?,?)";
st = conn.prepareStatement(sql);
for(int i=1;i<1000008;i++){ // i=1000 2000
st.setInt(1, i);
st.setString(2, "aa" + i);
st.addBatch();
if(i%1000==0){
st.executeBatch();
st.clearBatch();
}
}
st.executeBatch();
st.clearBatch();
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
优点:
1. 发送的是预编译后的SQL语句,执行效率高。
缺点:
1. 只能应用在SQL语句相同 但 参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入或更新数据。
4. LOB(Large Objects,大型对象)大数据
LOB类型的字段现在用得越来越多了。因为这种类型的字段容量大,且一个表中可以有多个这种类型的字段,很灵活,适用于数据量非常大的业务领域(如图象、档案等)。
分为2类:
1. clob用于存储大文本。
2. blob用于存储二进制数据(如:图像、声音、二进制文等)。
- MySQL
blob存储二进制,分为:
TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
Text存储大文本(MySQL没有clob),分为:
TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
大文本
1. 添加(向数据库写入大文本---Text类型数据)
PreparedStatement.setCharacterStream(index, reader, length); // length长度必须设置且为int型
2. 获取(从数据库中读取大文本)
方式1 reader = resultSet.getCharacterStream(String columnLabel);
方式2 string s = resultSet.getString(String columnLabel);
例:
sql语句
create table testclob(
id int primary key auto_increment,
hello_content text
);
添加
public void add(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
Reader reader = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into testclob(hello_content) values(?)";
st = conn.prepareStatement(sql);
// 这种方式获取的路径,其中的空格会被使用“%20”代替。需调用replaceAll方法将“%20”再替换回空格。
String path = JdbcOperaClob.class.getClassLoader().getResource("data.txt").getPath();
path = path.replaceAll("%20", " ");
File file = new File(path);
reader = new FileReader(file);
st.setCharacterStream(1, reader,(int) file.length());
int num = st.executeUpdate();
if(num>0){
System.out.println("插入成功!!");
}
// 关闭流
reader.close();
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
读取
public void read(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select hello_content from testclob where id=1";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
//
String contentStr ="";
String content = "";
if(rs.next()){
// 使用resultSet.getString("字段名")获取大文本数据的内容
content = rs.getString("hello_content");
// 使用resultSet.getCharacterStream("字段名")获取大文本数据的内容
Reader reader = rs.getCharacterStream("hello_content");
char buffer[] = new char[1024];
int len = 0;
FileWriter out = new FileWriter("下载路径");
while((len=reader.read(buffer))>0){
contentStr += new String(buffer);
out.write(buffer, 0, len);
}
out.close();
reader.close();
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
二进制
1. 添加(向数据库写入二进制数据)
PreparedStatement.setBinaryStream(i, inputStream, length);
2. 获取(从数据库中读取二进制数据)
方式1
InputStream in = resultSet.getBinaryStream(String columnLabel);
方式2
InputStream in = resultSet.getBlob(String columnLabel).getBinaryStream();
例:
sql
create table testblob(
id int primary key auto_increment,
hello_image longblob
);
添加
public void add(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into testblob(hello_image) values(?)";
st = conn.prepareStatement(sql);
// 这种方式获取的路径,其中的空格会被使用“%20”代替,需调用replaceAll方法将“%20”再替换回空格。
String path = JdbcOperaBlob.class.getClassLoader().getResource("01.jpg").getPath();
path = path.replaceAll("%20", " ");
File file = new File(path);
FileInputStream fis = new FileInputStream(file);// 生成的流
st.setBinaryStream(1, fis,(int) file.length());
int num = st.executeUpdate();
if(num>0){
System.out.println("插入成功!!");
}
fis.close();
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
读取
public void read() {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "select hello_image from testblob where id=?";
st = conn.prepareStatement(sql);
st.setInt(1, 1);
rs = st.executeQuery();
if (rs.next()) {
// 或InputStream in = rs.getBlob("image").getBinaryStream();
InputStream in = rs.getBinaryStream("image");
int len = 0;
byte buffer[] = new byte[1024];
FileOutputStream out = new FileOutputStream("下载路径");
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, rs);
}
}
- Oracle
sql
CREATE TABLE TEST_CLOB ( ID NUMBER(3), CLOBCOL CLOB);
CREATE TABLE TEST_BLOB ( ID NUMBER(3), BLOBCOL BLOB);
db.properties(src目录下创建)
oracleDb_Driver=oracle.jdbc.driver.OracleDriver
oracleDb_Url=jdbc:oracle:thin:@localhost:1521:test
oracleDb_UserName=hello
oracleDb_Password=hello
数据库处理类
public class JdbcUtils {
private static String oracleDb_Driver = null;
private static String oracleDb_Url = null;
private static String oracleDb_UserName = null;
private static String oracleDb_Password = null;
static{
try{
// 读取db.properties文件中的数据库连接信息
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);
// 获取数据库连接驱动
oracleDb_Driver = prop.getProperty("oracleDb_Driver");
// 获取数据库连接URL地址
oracleDb_Url = prop.getProperty("oracleDb_Url");
// 获取数据库连接用户名
oracleDb_UserName = prop.getProperty("oracleDb_UserName");
// 获取数据库连接密码
oracleDb_Password = prop.getProperty("oracleDb_Password");
// 加载数据库驱动
Class.forName(oracleDb_Driver);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
// 获取Oracle数据库连接对象
public static Connection getOracleConnection() throws SQLException{
return DriverManager.getConnection(oracleDb_Url, oracleDb_UserName,oracleDb_Password);
}
// 释放资源:Connection数据库连接对象、负责执行SQL命令的Statement对象、存储查询结果的ResultSet对象
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
// 关闭存储查询结果的ResultSet对象
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
// 关闭负责执行SQL命令的Statement对象
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
// 关闭Connection数据库连接对象
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
CLOB数据(大文本)
public class JdbcOperaOracleClob {
// 添加(向数据库中插入大文本)
public void clobInsert() throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
conn = JdbcUtils.getOracleConnection();
boolean defaultCommit = conn.getAutoCommit();
// 开启事务,设定不自动提交
conn.setAutoCommit(false);
try {
// 插入一个空的CLOB对象
String sql = "INSERT INTO TEST_CLOB VALUES (?, EMPTY_CLOB())";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
stmt.executeUpdate();
// 查询此CLOB对象并锁定
sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
rs = stmt.executeQuery();
if (rs.next()) {
// 取出此CLOB对象
oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL");
// 向CLOB对象中写入数据
BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream());
// 这种方式获取的路径,其中的空格会被使用“%20”代替,需调用replaceAll方法将“%20”再替换回空格。
String path = JdbcOperaClob.class.getClassLoader().getResource("data.txt").getPath();
path = path.replaceAll("%20", " ");
BufferedReader in = new BufferedReader(new FileReader(path));
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
in.close();
out.close();
}
// 正式提交
conn.commit();
System.out.println("插入成功");
} catch (Exception ex) {
// 出错回滚
conn.rollback();
throw ex;
}finally{
// 恢复原提交状态
conn.setAutoCommit(defaultCommit);
JdbcUtils.release(conn,stmt,rs);
}
}
// 读取(从数据库中读取大文本)
public void clobRead() throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
conn = JdbcUtils.getOracleConnection();
boolean defaultCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
try {
// 查询CLOB对象
String sql = "SELECT * FROM TEST_CLOB WHERE ID=?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
rs = stmt.executeQuery();
if (rs.next()) {
// 获取CLOB对象
oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL");
// 以字符形式输出
BufferedReader in = new BufferedReader(clob.getCharacterStream());
BufferedWriter out = new BufferedWriter(new FileWriter("下载路径"));
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
out.close();
in.close();
}
} catch (Exception ex) {
conn.rollback();
throw ex;
}finally{
// 恢复原提交状态
conn.setAutoCommit(defaultCommit);
JdbcUtils.release(conn,stmt,rs);
}
}
// 修改CLOB对象(是在原CLOB对象基础上进行覆盖式的修改)
public void clobModify() throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
conn = JdbcUtils.getOracleConnection();
boolean defaultCommit = conn.getAutoCommit();
// 开启事务
conn.setAutoCommit(false);
try {
// 查询CLOB对象并锁定
String sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
rs = stmt.executeQuery();
if (rs.next()) {
// 获取此CLOB对象
oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL");
// 进行覆盖式修改
BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream());
// 这种方式获取的路径,其中的空格会被使用“%20”代替,需调用replaceAll方法将“%20”再替换回空格。
String path = JdbcOperaClob.class.getClassLoader().getResource("data2.txt").getPath();
path = path.replaceAll("%20", " ");
BufferedReader in = new BufferedReader(new FileReader(path));
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
in.close();
out.close();
}
// 提交事务
conn.commit();
} catch (Exception ex) {
// 出错回滚事务
conn.rollback();
throw ex;
}finally{
// 恢复原提交状态
conn.setAutoCommit(defaultCommit);
JdbcUtils.release(conn,stmt,rs);
}
}
// 替换CLOB对象(将原CLOB对象清除,换成一个全新的CLOB对象)
public void clobReplace() throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
conn = JdbcUtils.getOracleConnection();
boolean defaultCommit = conn.getAutoCommit();
// 开启事务
conn.setAutoCommit(false);
try {
// 清空原CLOB对象
String sql = "UPDATE TEST_CLOB SET CLOBCOL=EMPTY_CLOB() WHERE ID=?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
stmt.executeUpdate();
// 查询CLOB对象并锁定
sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
rs = stmt.executeQuery();
if (rs.next()) {
// 获取此CLOB对象
oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL");
// 更新数据
BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream());
// 这种方式获取的路径,其中的空格会被使用“%20”代替,需调用replaceAll方法将“%20”再替换回空格。
String path = JdbcOperaClob.class.getClassLoader().getResource("db.properties").getPath();
path = path.replaceAll("%20", " ");
BufferedReader in = new BufferedReader(new FileReader(path));
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
in.close();
out.close();
}
// 正式提交
conn.commit();
} catch (Exception ex) {
// 出错回滚
conn.rollback();
throw ex;
} finally {
// 恢复原提交状态
conn.setAutoCommit(defaultCommit);
JdbcUtils.release(conn, stmt, rs);
}
}
}
BLOB数据(二进制)
Oracle定义了一个BLOB字段用于保存二进制数据,但这个字段并不能存放真正的二进制数据,只能向这个字段存一个指针,然后把数据放到指针所指向的Oracle的LOB段中, LOB段是在数据库内部表的一部分。因而在操作Oracle的Blob之前,必须获得指针(定位器)才能进行Blob数据的读取和写入。
获得表中的Blob指针: 可以先使用insert语句向表中插入一个空的blob(调用oracle的函数empty_blob()),这将创建一个blob的指针,然后再把这个empty的blob的指针查询出来,这样就可得到BLOB对象,从而读写blob数据了。
1、插入空blob:insert into testblob(id,image) values(?,empty_blob())
2、获得blob的指针:
// 注意: 必须加for update锁定该行,直至该行被修改完毕,可保证不产生并发冲突。
select image from testblob where id=? for update
Blob b = rs.getBlob("image");
3、利用io和获取到的cursor指针往数据库读写数据
注意:以上操作需开启事务。
public class JdbcOperaOracleBlob {
// 添加(向数据库中插入二进制数据)
public void blobInsert() throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
boolean defaultCommit = true;
try {
conn = JdbcUtils.getOracleConnection();
// 得到数据库事务处理的默认提交方式
defaultCommit = conn.getAutoCommit();
// 1、开启事务
conn.setAutoCommit(false);
// 2、插入一个空的BLOB对象
String sql = "INSERT INTO TEST_BLOB VALUES (?, EMPTY_BLOB())";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
stmt.executeUpdate();
// 3、查询此BLOB对象并锁定。
// 注意: 必须加for update锁定该行,直至该行被修改完毕,可保证不产生并发冲突。
sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
rs = stmt.executeQuery();
if (rs.next()) {
// 4、取出此BLOB对象 ,并强制转换成Oracle的BLOB对象
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL");
// 5、使用IO向BLOB对象中写入数据
BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream());
BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("01.jpg"));
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
in.close();
out.close();
}
// 6、提交事务
conn.commit();
} catch (Exception ex) {
// 7、出错回滚事务
conn.rollback();
throw ex;
} finally {
// 8、恢复数据库事务处理的默认提交方式
conn.setAutoCommit(defaultCommit);
// 释放资源
JdbcUtils.release(conn, stmt, rs);
}
}
// 修改BLOB对象(是在原BLOB对象基础上进行覆盖式的修改)
public void blobModify() throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
boolean defaultCommit = true;
try {
conn = JdbcUtils.getOracleConnection();
// 得到数据库事务处理的默认提交方式
defaultCommit = conn.getAutoCommit();
// 1、开启事务
conn.setAutoCommit(false);
// 2、查询此BLOB对象并锁定。注意: 必 须加for update锁定该行,直至该行被修改完毕,保证不产生并发冲突
String sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
rs = stmt.executeQuery();
if (rs.next()) {
// 3、取出此BLOB对象 ,并强制转换成Oracle的BLOB对象
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL");
// 4、使用IO向BLOB对象中写入数据
BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream());
BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("02.jpg"));
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
in.close();
out.close();
}
// 5、提交事务
conn.commit();
} catch (Exception ex) {
// 6、出错回滚事务
conn.rollback();
throw ex;
} finally {
// 8、恢复数据库事务处理的默认提交方式
conn.setAutoCommit(defaultCommit);
// 释放资源
JdbcUtils.release(conn, stmt, rs);
}
}
// 替换BLOB对象(将原BLOB对象清除,换成一个全新的BLOB对象)
public void blobReplace() throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
boolean defaultCommit = true;
try {
conn = JdbcUtils.getOracleConnection();
// 得到数据库事务处理的默认提交方式
defaultCommit = conn.getAutoCommit();
// 1、开启事务
conn.setAutoCommit(false);
// 2、清空原BLOB对象
String sql = "UPDATE TEST_BLOB SET BLOBCOL=EMPTY_BLOB() WHERE ID=?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
stmt.executeUpdate();
// 3、查询此BLOB对象并锁定。注意: 必 须加for update锁定该行,直至该行被修改完毕,保证不产生并发冲突
sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
rs = stmt.executeQuery();
if (rs.next()) {
// 4、取出此BLOB对象 ,并强制转换成Oracle的BLOB对象
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL");
// 5、使用IO向BLOB对象中写入数据
BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream());
BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("01.jpg"));
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
in.close();
out.close();
}
// 6、提交事务
conn.commit();
} catch (Exception ex) {
// 7、出错回滚事务
conn.rollback();
throw ex;
} finally {
// 8、恢复数据库事务处理的默认提交方式
conn.setAutoCommit(defaultCommit);
// 释放资源
JdbcUtils.release(conn, stmt, rs);
}
}
// 读取(从数据库表中读取二进制数据)
public void blobRead() throws Exception {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
boolean defaultCommit = true;
try {
conn = JdbcUtils.getOracleConnection();
// 得到数据库事务处理的默认提交方式
defaultCommit = conn.getAutoCommit();
// 1、开启事务
conn.setAutoCommit(false);
// 2、查询BLOB对象
String sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
rs = stmt.executeQuery();
if (rs.next()) {
// 3、取出此BLOB对象 ,并强制转换成Oracle的BLOB对象
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL");
// 4、以二进制流的形式输出
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("下载路径"));
BufferedInputStream in = new BufferedInputStream(blob.getBinaryStream());
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
in.close();
out.close();
}
// 5、提交事务
conn.commit();
} catch (Exception ex) {
// 6、出错回滚事务
conn.rollback();
throw ex;
} finally {
// 8、恢复数据库事务处理的默认提交方式
conn.setAutoCommit(defaultCommit);
// 释放资源
JdbcUtils.release(conn, stmt, rs);
}
}
}
通过JDBC操纵Oracle数据库的LOB字段,不外乎插入、修改、替换、读取四种方式。
和其它类型字段的不同点:
1、必须取消自动提交。
存取操作开始前,必须用setAutoCommit(false)取消自动提交。
其它类型字段则无此特殊要求。这是因为存取LOB类型字段时,通常要进行多次操作可以完成。不这样的话,Oracle将抛出“读取违反顺序”错误。
2、插入方式不同。
LOB数据不能象其它类型数据一样直接插入(INSERT)。插入前必须先插入一个空的LOB对象,CLOB类型 的空对象为EMPTY_CLOB(),BLOB类型的空对象为EMPTY_BLOB()。之后通过SELECT命令查询得到先前插入的记录并锁定,继而将 空对象修改为所要插入的LOB对象。
3、修改方式不同。
其它类型的字段修改时,用UPDATE … SET…命令即可。
而LOB类型字段,则只能用SELECT … FOR UPDATE命令将记录查询出来并锁定,然后才能修改。且修改也有两种改法:一是在原数据基础上的修改(即覆盖式修改),执行SELECT … FOR UPDATE后再改数据;二是替换(先将原数据清掉,再修改),先执行UPDATE命令将LOB字段值设为空的LOB对象,然后进行第一种改法。建议使 用替换的方法,以实现与其它字段UPDATE操作后一样的效果。
4、存取时应使用由数据库JDBC驱动程序提供的LOB操作类。
对于Oracle数据库,应使用oracle.sql.CLOB和oracle.sql.BLOB。不使用由数据库JDBC驱动程序提供的LOB类时,程序运行时易于出现“抽象方法调用”的错误,这是因为JDBC所定义的java.sql.Clob与 java.sql.Blob接口,其中的一些方法并未在数据库厂家提供的驱动程序中真正实现。
5、存取手段与文件操作相仿。
对于BLOB类型,应用InputStream/OutputStream类,此类不进行编码转换,逐个字节存取。oracle.sql.BLOB类相应提供了getBinaryStream()和getBinaryOutputStream()两个方法,前一个 方法用于读取Oracle的BLOB字段,后一个方法用于将数据写入Oracle的BLOB字段。
对于CLOB类型,应用Reader/Writer类,此类进行编码转换。oracle.sql.CLOB类相应 提供了getCharacterStream()和getCharacterOutputStream()两个方法,前一个方法用于读取Oracle的 CLOB字段,后一个方法用于将数据写入Oracle的CLOB字段。
为了大幅提高程序执行效率,对BLOB/CLOB字段的读写操作,应该使用缓冲操作类(带 Buffered前缀,如:BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter)。