Oracleのデータ型に、大量のデータやバイナリデータを格納できる「BLOB」「CLOB」がある。「BLOB」にはバイナリデータ、「CLOB」にはテキストデータを格納できる。
OracleテーブルとJavaオブジェクトのデータ型の関連付けを行うものが「TypeHandler」である。MyBatis3.4以降のバージョンでは、「BLOB」と「java.io.InputStream」または「byte[]」が、「CLOB」と「java.lang.String」または「java.io.Reader」を関連付けする「TypeHandler」をサポートしている。
今回は、Spring Bootのmybatisを利用して、「BLOB」「CLOB」を含むテーブルへのデータ追加・データ参照を行ってみたので、そのサンプルプログラムを共有する。
ここでは、エンティティクラスで「BLOB」型データを「java.io.InputStream」で、「CLOB」型データを「java.lang.String」で定義している。
前提条件
以下の記事の「Oracle JDBC接続用jarの配置」までの処理が完了していること。
やってみたこと
BLOB,CLOBを含むテーブルの作成
今回作成するサンプルプログラムがアクセスするテーブル「file_data」を作成した。実行したSQLは以下の通り。
create table file_data (
ID NUMBER(6) PRIMARY KEY NOT NULL
, file_path CLOB
, file_obj BLOB
);
利用するファイルの配置
今回作成するサンプルプログラム内でアクセスするファイル「テスト.txt」を作成した。そのファイルの中身は以下の通り。


サンプルプログラムの作成
「build.gradle」の内容は以下の通りで、lombokとmybatisの定義を入れている。
plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
compile files('lib/ojdbc6.jar')
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1'
}
また、「application.properties」には以下のように、DB接続定義とSQLログ出力定義を追加している。
server.port = 8084 # DB接続情報 spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe spring.datasource.username=USER01 spring.datasource.password=USER01 spring.datasource.driverClassName=oracle.jdbc.driver.OracleDriver # SQLログ出力 logging.level.org.springframework=warn logging.level.com.example.demo.FileDataMapper=debug
エンティティクラス「FileData.java」は以下の通りで、「BLOB」型データを「java.io.InputStream」で、「CLOB」型データを「java.lang.String」で定義している。
さらに、直列化可能にするために、Serializableインタフェースを継承し、シリアルバージョンIDを設定している。
package com.example.demo;
import lombok.Data;
import java.io.InputStream;
import java.io.Serializable;
/**
* ファイルデータテーブル(file_data)アクセス用エンティティ
*/
@Data
public class FileData implements Serializable{
/** シリアルバージョンID */
private static final long serialVersionUID = 1L;
/** ID */
private long id;
/** ファイルパス */
private String filePath;
/** ファイルオブジェクト */
private InputStream fileObj;
}
なお、Serializableについては、以下のページが参考になる。
https://qiita.com/NBT/items/9f76c9fd1c7a90506658
また、「file_data」テーブルとアクセスするMapperクラスは以下の通りで、VARCHAR2等他のデータ型の場合と同等の記載となっている。
package com.example.demo;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface FileDataMapper {
/**
* 指定したIDをもつファイルデータテーブル(file_data)のデータを取得する
* @param id ID
* @return ファイルデータテーブル(file_data)の指定したIDのデータ
*/
@Select("SELECT id, file_path as filePath, file_obj as fileObj "
+ " FROM file_data WHERE id = #{id}")
FileData findById(Long id);
/**
* 指定したファイルデータテーブル(file_data)のデータを追加する
* @param fileData ファイルデータテーブル(file_data)
*/
@Insert("INSERT INTO file_data ( id, file_path, file_obj ) "
+ " VALUES ( #{id}, #{filePath}, #{fileObj} )")
void insert(FileData fileData);
/**
* ファイルデータテーブル(file_data)のデータを全件削除する
*/
@Delete("DELETE FROM file_data")
void deleteAll();
}
さらに、FileDataMapperクラスのメソッドを呼び出すコントローラクラスの定義は、以下の通りとなる。InputStreamデータは、下記getFileObjDataメソッドで記載した通り、BufferedReaderクラスのreadLineメソッドを利用して取得できる。
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.transaction.annotation.Transactional;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
@Controller
public class DemoController {
@Autowired
private FileDataMapper fileDataMapper;
@RequestMapping("/")
@Transactional(readOnly = false)
public String index(){
//全件削除する
fileDataMapper.deleteAll();
//1件追加する
insertFileData("C:\\tmp\\テスト.txt");
//追加したデータを確認する
getFileData();
//サンプルページへ移動する
return "sample";
}
/**
* 指定したファイルパスのデータを追加する
* @param filePath
*/
private void insertFileData(String filePath){
FileData fileData = new FileData();
fileData.setId(1);
fileData.setFilePath(filePath);
try{
fileData.setFileObj(new FileInputStream(filePath));
}catch(Exception e){
System.err.println(e);
}
//1件追加する
fileDataMapper.insert(fileData);
}
/**
* ファイルデータ(file_data)テーブルのデータを確認する
*/
private void getFileData(){
//指定したIDのデータを取得する
FileData fileData = fileDataMapper.findById(Long.valueOf(1));
if(fileData != null){
System.out.println("idの値 : " + fileData.getId());
System.out.println("file_pathの値 : " + fileData.getFilePath());
System.out.println("file_objの値 : "
+ getFileObjData(fileData.getFileObj()));
}else{
System.out.println("fileDataはnull");
}
}
/**
* 入力ストリームを文字列に変換し返す
* @param inputStream 入力ストリーム
* @return ファイルオブジェクトの文字列
*/
private String getFileObjData(InputStream inputStream){
StringBuilder builder = new StringBuilder();
if(inputStream != null){
try (BufferedReader br = new BufferedReader(
new InputStreamReader(inputStream))) {
String line;
while((line = br.readLine()) != null){
builder.append(line);
}
}catch(Exception e){
System.err.println(e);
}
}
return builder.toString();
}
}
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-oracle-lob/demo
サンプルプログラムの実行結果
Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスした場合、以下の画面が表示される。

このときのコンソールログは以下の通りで、SQLログが出力されていることや、取得データの内容が確認できる。

また、実行後の「file_data」テーブルのデータの内容は以下の通り。なお、「UTL_RAW.CAST_TO_VARCHAR2」を利用すると、BLOBのデータをテキストで表示できる。
select * from file_data

select UTL_RAW.CAST_TO_VARCHAR2(FILE_OBJ) from file_data

要点まとめ
- Oracleのデータ型に、大量のデータやバイナリデータを格納できる「BLOB」「CLOB」がある。「BLOB」にはバイナリデータ、「CLOB」にはテキストデータを格納できる。
- Javaのエンティティクラスでは、「BLOB」型データを「java.io.InputStream」で、「CLOB」型データを「java.lang.String」で定義すれよい。







