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は以下の通り。
1 2 3 4 5 | create table file_data ( ID NUMBER(6) PRIMARY KEY NOT NULL , file_path CLOB , file_obj BLOB ); |
利用するファイルの配置
今回作成するサンプルプログラム内でアクセスするファイル「テスト.txt」を作成した。そのファイルの中身は以下の通り。
サンプルプログラムの作成
「build.gradle」の内容は以下の通りで、lombokとmybatisの定義を入れている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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ログ出力定義を追加している。
1 2 3 4 5 6 7 8 9 | 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を設定している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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等他のデータ型の場合と同等の記載となっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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メソッドを利用して取得できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 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のデータをテキストで表示できる。
1 | select * from file_data |
1 | 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」で定義すれよい。