目录
JDBC
Java Data Base Connectivity(java数据库连接)
应用
JDBC (位于第二层)
各种数据库驱动
数据库
1. 使用
下载mysql-connector-java-8.0.11.jar,并放入WEB-INF | lib下
下载mysql-connector-java-8.0.11.jar , 选Platform Indepentdent
简单使用
- 创建数据库、表、插入数据
终端输入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','xx@sina.com','2220-12-04');
insert into users(id,name,password,email,birthday) values(2,'lisi','123456','xx@sina.com','2222-12-04');
insert into users(id,name,password,email,birthday) values(3,'wangwu','123456','xx@sina.com','2222-12-04');
Navicat Premium可视化工具连接本地数据库
localhost
3306
root
数据库密码
2.数据库操作类
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.加载驱动
/*不推荐使用这种方式来加载驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
1、查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
2、程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
* */
// 推荐使用
Class.forName("com.mysql.jdbc.Driver");
/* 2.1 设置 要连接的数据库URL
* 协议:子协议:主机:端口号/数据库名
*
Oracle写法:jdbc:oracle:thin:@localhost:1521:sid
SqlServer写法:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
MySql写法:jdbc:mysql://localhost:3306/sid
*/
String url = "jdbc:mysql://localhost:3306/数据库名";
// 2.2 设置 连接的数据库时使用的用户名
String username = "root";
// 2.3 设置 连接的数据库时使用的密码
String password = "密码";
// 2.4 获取 与数据库的链接
Connection conn = DriverManager.getConnection(url, username, password);
/*
Connection类
createStatement():创建向数据库发送sql的statement对象。
prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
prepareCall(sql):创建执行存储过程的callableStatement对象。
setAutoCommit(boolean autoCommit):设置事务是否自动提交。
commit() :在链接上提交事务。
rollback() :在此链接上回滚事务。
* */
// 3. 获取 用于向数据库发送sql语句的statement
Statement st = conn.createStatement();
/*
Statement类
executeQuery(String sql) :用于向数据发送查询语句。
executeUpdate(String sql):用于向数据库发送insert、update或delete语句
execute(String sql):用于向数据库发送任意sql语句
addBatch(String sql) :把多条sql语句放到一个批处理中。
executeBatch():向数据库发送一批sql语句执行。
* */
// -----------增-----------
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);
/*
ResultSet类
getObject(int index)
getObject(string columnName)
// 指定数据类型
getString(int index)
getString(String columnName)
。。。
next():移动到下一行
Previous():移动到前一行
absolute(int row):移动到指定行
beforeFirst():移动resultSet的最前面。
afterLast() :移动到resultSet的最后面。
* */
// 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.关闭链接,释放资源
rs.close();
st.close();
conn.close();
}
}
遇到的错误:java.lang.UnsupportedClassVersionError
解决:
换jdk版本为高版本即可
MyEclipse|设置|Java|JRE换jdk版本
查看jdk版本
/usr/libexec/java_home -V
规范使用
- 新建db.properties
在src下新建File
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/Test
username=root
password=密码
- 新建数据库处理类
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("jdbc2.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);
}
}
/**
* @Method: getConnection
* @Description: 获取数据库连接对象
*
* @return Connection数据库连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username,password);
}
/**
* @Method: release
* @Description: 释放资源,
* 要释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
*
* @param conn
* @param st
* @param rs
*/
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();
}
}
}
}
3.处理类
package com.sst.cx;
import java.sql.*;
public class TestJdbcM {
public static void main(String[] args){
(new TestJdbcM()).insert();
}
/*
create database Test;
use Test;
create table users(
id int primary key,
name varchar(40),
password varchar(40),
email varchar(60),
birthday date
);
* */
// 增
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','xx@qq.com','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='xxxx@sina.com' 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);
}
}
}
4.处理类2(占位符)
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, "xx@sina.com");//email是varchar(字符串类型)
st.setDate(5, new java.sql.Date(new Date().getTime()));//birthday是date类型
//执行插入操作,executeUpdate方法返回成功的条数
int num = st.executeUpdate();
if(num>0){
System.out.println("插入成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
//SQL执行完成之后释放相关资源
JdbcUtils.release(conn, st, rs);
}
}
// 删
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, "gacl");
st.setString(2, "gacl@sina.com");
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);
}
}
}
获取数据库自动生成的主键
id int primary key auto_increment
String sql = "insert into test1(name) values(?)";
st = conn.prepareStatement(sql);
st.setString(1, "aaa");
st.executeUpdate();
// 获取数据库自动生成的主键
rs = st.getGeneratedKeys();
if(rs.next()){
System.out.println(rs.getInt(1));
}
2. 事务
概念
原子性(Atomicity) �
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败
一致性(Consistency)
�事务必须使数据库从一个一致性状态变换到另外一个一致性状态。以转账为例子,A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性。
隔离性(Isolation)复杂,详细介绍在下�
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(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结束事务才能插入
终端查询当前事务隔离级别:select @@tx_isolation
mysql数据库默认的事务隔离级别是:Repeatable read(可重复读)
set transaction isolation level 隔离级别名
使用
终端使用
// 开启事务
start transaction;
// 执行语句列表。。。
// 提交
commit;
// 回滚(异常中)
rollback;
代码使用
conn.setAutoCommit(false); // 通知数据库开启事务
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();//上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)
System.out.println("成功!!!"); //log4j
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
设置事物回滚点
// 设置事务回滚点
sp = conn.setSavepoint();
异常中+
conn.rollback(sp);//回滚到设置的事务回滚点
// 回滚了要记得通知数据库提交事务
conn.commit();
3. 批处理
JDBC实现批处理有两种方式:statement和preparedstatement
Statement完成批处理
st = conn.createStatement();
// 添加要批量执行的SQL
st.addBatch(sql1);
st.addBatch(sql2);
// 执行批处理SQL语句
st.executeBatch();
// 清除批处理命令
st.clearBatch();
优点:可以向数据库发送多条不同的SQL语句。
缺点:SQL语句没有预编译。
当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句
PreparedStatement完成批处理
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();
优点:发送的是预编译后的SQL语句,执行效率高。
缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。
4. 大数据
大数据LOB(Large Objects)分为:
clob用于存储大文本
blob用于存储二进制数据,例如图像、声音、二进制文等。
MySQL
MySQL
blob(没有clob)存储二进制,分为:
TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
Text存储大文本,分为:
TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
大文本
sql
helloContent text类型
添加
// 这种方式获取的路径,其中的空格会被使用“%20”代替
String path = JdbcOperaClob.class.getClassLoader().getResource("data.txt").getPath();
// 将“%20”替换回空格
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();
读取
String sql = "select resume from testclob where id=2";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
String contentStr ="";
String content = "";
if(rs.next()){
//使用resultSet.getString("字段名")获取大文本数据的内容
content = rs.getString("resume");
//使用resultSet.getCharacterStream("字段名")获取大文本数据的内容
Reader reader = rs.getCharacterStream("resume");
char buffer[] = new char[1024];
int len = 0;
FileWriter out = new FileWriter("D:\\1.txt");
while((len=reader.read(buffer))>0){
contentStr += new String(buffer);
out.write(buffer, 0, len);
}
out.close();
reader.close();
}
二进制
sql
helloImage longblob类型
添加
conn = JdbcUtils.getConnection();
String sql = "insert into testblob(image) values(?)";
st = conn.prepareStatement(sql);
//这种方式获取的路径,其中的空格会被使用“%20”代替
String path = JdbcOperaBlob.class.getClassLoader().getResource("01.jpg").getPath();
//将“%20”替换会空格
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();
读取
String sql = "select 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("D:\\1.jpg");
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
Oracle
LOB(Large Object,大型对象)类型的字段现在用得越来越多了。
因为这种类型的字段,容量大(最多能容纳4GB的数据),且一个表中可以有多个这种类型的字段,很灵活,适用于数据 量非常大的业务领域(如图象、档案等)。
LOB类型分为种:
BLOB二进制大型对象(Binary Large Object)
CLOB字符型大型对象(Character Large Object)
sql
CREATE TABLE TEST_CLOB ( ID NUMBER(3), CLOBCOL CLOB)
CREATE TABLE TEST_BLOB ( ID NUMBER(3), BLOBCOL BLOB)
db.properties
oracleDb_Driver=oracle.jdbc.driver.OracleDriver
oracleDb_Url=jdbc:oracle:thin:@localhost:1521:GACL
oracleDb_UserName=GACL_XDP
oracleDb_Password=P
数据库处理类
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 {
// 往数据库中插入一个新的CLOB对象
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”代替
String path = JdbcOperaClob.class.getClassLoader().getResource("data.txt").getPath();
// 将“%20”替换回空格
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);
}
}
// CLOB对象读取
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("D:\\2.txt"));
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”代替
String path = JdbcOperaClob.class.getClassLoader().getResource("data2.txt").getPath();
// 将“%20”替换回空格
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”代替
String path = JdbcOperaClob.class.getClassLoader().getResource("db.properties").getPath();
// 将“%20”替换回空格
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的cursor:
select image from testblob where id=? for update �注意: 必 须加for update锁定该行,直至该行被修改完毕,保证不产生并发冲突。
Blob b = rs.getBlob("image");
3、利用 io和获取到的cursor往数据库读写数据
注意:以上操作需开启事务。
public class JdbcOperaOracleBlob {
// 向数据库中插入一个新的BLOB对象
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);
}
}
// BLOB对象读取
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("D:/1.jpg"));
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字段,不外乎插入、修改、替换、读取四种方式,掌握起来并不难。观察上述程序对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。 例程中全部使用了缓冲操作类。
网友评论