• 検索結果がありません。

第 16 章 マッピング アノテーション マッピングアノテーションは エンティティとデータベーステーブルとの関係を割 り当てるものと エンティティ同士の関係をデータベーステーブルに割り当てるもの とがあります この章は 前者の解説です システム開発の上でとても重要な部分です 役に立つ応用としてデータ

N/A
N/A
Protected

Academic year: 2021

シェア "第 16 章 マッピング アノテーション マッピングアノテーションは エンティティとデータベーステーブルとの関係を割 り当てるものと エンティティ同士の関係をデータベーステーブルに割り当てるもの とがあります この章は 前者の解説です システム開発の上でとても重要な部分です 役に立つ応用としてデータ"

Copied!
42
0
0

読み込み中.... (全文を見る)

全文

(1)

16

マッピング・アノテーション

 マッピングアノテーションは、エンティティとデータベーステーブルとの関係を割 り当てるものと、エンティティ同士の関係をデータベーステーブルに割り当てるもの とがあります。この章は、前者の解説です。システム開発の上でとても重要な部分です。 役に立つ応用としてデータベースからの画像出力も5節で解説します。 1 マッピング・アノテーションの概要... 2 エンティティの構成を指定する ... 3 主キーの構成を指定する ... 4 ファイル、列挙、日付と時刻の扱い方 ... 5 マッピングの適用例 ... 6 コレクションとマップを使う ... 7 まとめ ...

(2)

16

 この章で扱うマッピング・アノテーションは、主にエンティティとデータベースとの間 でのさまざまな割り付けを指定するものです。どのテーブルやカラムと対応づけるかとか、 エンティティのフィールドをデータベースでどう扱うかなどを指定します。

1.1 アノテーションの概要

 アノテーションの数が多いので、一覧できるように解説するアノテーションとその機能 を表に示しておきます。表全体に目を通してから、後の解説を読んでください。 アノテーション 機 能 テーブルの構成を指定する @Table エンティティに対応するテーブル名を指定する @SecondaryTables @SecondaryTable エンティティに複数のテーブルのカラムを対応付ける 対応付ける個々のテーブルを指定する @Embeddable @Embedded ※エンティティを複数のクラスから構成する エンティティの1つのフィールドとして取り込まれるクラスに指定する エンティティ側に付けて取り込まれたフィールド(クラス)を示す 主キーの構成を指定する @Id @GeneratedValue 主キーであるフィールドを指定する 主キーの値を自動生成を指示し、その方法も指定する @Embeddable @EmbeddedId ※他のクラスを主キーとして取り込む機能 エンティティの1つのフィールドとして取り込まれるクラスに指定する エンティティの主キーとして取り込まれたフィールド(クラス)を示す @IdClass @Id ※他のクラスを主キーとして取り込む機能 エンティティ側に付け、取り込むクラスを指定する 別クラスから取り込んだ各フィールドに付けて主キーであることを示す カラムの属性を指定する @Column フィールドに対するテーブルのカラム名を指定する。nullを禁止するなどの 設定もできる @Basic @Lob ファイルなどのフィールドに対して遅延フェッチを指定できる ファイルなどの大きなデータが入るフィールドであることを示す @Enumerated 列挙型を名前で記録するか序数で記録するか指定する @Transient データベースに保存しないフィールドを指定する @ElementCollection @CollectionTable @MapKeyColumn @Column

フィールド変数がSet、List、Mapである場合(ただしその要素は基本データ 型のラッパークラスや文字列)、データベーステーブルへの割り付けを指定す る アクセスタイプを指定する @Access フィールドアクセスにするかプロパティアクセスにするか指定する Section Section Section Section Section S Secttiion Se Sectctioionn Section S i S ti

(3)

16

1.2 デフォルトのマッピング

 エンティティをリレーショナルデータベースのテーブルに割り当てる場合、テーブル名 やテーブルのカラム名、カラムの型などについて、デフォルトの設定が決まっています。 ・クラス名をテーブル名にする ・フィールド変数名をカラム名にする ・データ型は、デフォルトの変換規則にしたがって変換する(コラム参照)  テーブル名やカラム名は、マッピングアノテーションで変更できます。  一方、基本データ型やラッパークラスの型がデータベースではどのような型に割り付け られるかは、データベース側の仕様です。 コラム データ型のデフォルトの変換規則  どのデータベースも同じような型のバリエーションを持っているので、おおむね同 じようなデータベースの型に割り当てられますが、完全に同じではありません。参考 までにJava言語のデータ型に対するMySQLデータベースでの対応表を示します。

Java Type MySQL Server

boolean,java.lang.Boolean TINYINT(1) int,java.lang.Integer INTEGER long, java.lang.Long BIGINT float,java.lang.Float FLOAT double,java.lang.Double DOUBLE short,java.lang.Short SMALLINT byte, java.lang.Byte SMALLINT java.lang.Number DECIMAL(38) java.math.BigInteger BIGINT java.math.BigDecimal DECIMAL(38) java.lang.String VARCHAR(255) char,java.lang.Character CHAR(1) byte[],java.lang.Byte[],java.sql.Blob BLOB(64000) char[],java.lang.Character[],java.sql.Clob TEXT(64000) java.sql.Date DATE java.sql.Time TIME java.sql.Timestamp DATETIME

(4)

16

 エンティティにアノテーションを付けることによって、エンティティの構成方法を指定 できます。エンティティに対応するテーブルの名前を指定したり、1つのエンティティをい くつかのテーブルに分割したり、あるいはいくつかのクラスで1つのエンティティを構成し たりできます。  この章の例題では、Persistence Unit(持続性ユニット)に、"drop-and-Create"を指定してい ます。そのため、毎回データベーステーブルが消去され、新規に作成し直されます。 また、以下では、解説を読んだ後で例題を実行し、結果を確認してください。

2.1テーブルの名前や項目の名前を変える

 

@Table

はデータベースのテーブルにエンティティとは違う名前をつけます。また、

@Column

はカラム(列項目)の名前に、フィールド変数とは違う名前を付けます。 テーブル、カラムの名前を変える sample23-01/beans/Employee.java 例題1 1 @Entity 2 @Table(name = "EMP")

3 public class Employee implements Serializable { 4 @Id

5 @NotNull

6 private String id; 7 @Column(name="HYOKA")

8 private String eval; 9 private String name; 10 public Employee() {}

11 public Employee(String id, String eval, String name) { 12 this.id = id; 13 this.eval = eval; 14 this.name = name; 15 } 16 // セッター、ゲッターを省略 17 }  例題は、データベーステーブルで、テーブル名をEMPLOYEEからEMPに変更し、eval という項目のカラム名(列項目名)をHYOKAに変更します。 Section Section Section Section Section S Secttiion Se Sectctioionn Section S i S ti

(5)

16

【注】 ・「テーブル」はデータベースのtable(表)を指す用語です ・データベースのテーブル名は一般に大文字小文字の区別はありません ・ただし、一般には大文字で表記します

データベーステーブルの内容を表示する方法

 例題を実行した後、NetBeansのサービスタブをクリックし、EMPテーブルが生成され ていることを確認してください。また、EMPテーブルを右クリックし、[データを表示する] を選択すると、テーブルのカラム名がHYOKAになっていることも確認できます。  EMPテーブルが表示されない場合は、[表]を右クリックして[リフレッシュ]を選択してくだ さい。実行結果が反映されます。  最初なので、全ソースコードを解説しておきます。最初は、バッキングビーンです。 例題1(続き):バッキングビーン sample23-01/beans/Bb.java 1 @Named 2 @RequestScoped 3 public class Bb { 4 @EJB 5 EmployeeDb db;

6 public List<Employee> getAll() { // 全データのLISTを返す 7 return db.getAll();

8 }

9 @PostConstruct

10 public void init() { 11 if(db.isEmpty){

12 db.create(new Employee("100", "A", "田中宏")); 13 db.create(new Employee("110", "B", "鈴木太郎")); 14 }

15 } 16 }

(6)

16

 

@PostConstruct

が付けられた

init

メソッドは、コンストラクタの実行が終了した直 後、自動的に実行されるライフサイクル・コールバックメソッドです(13章)。  このメソッドの中で、データベーステーブルが空の時だけ、

Employee

型のオブジェク トを2件作成し、登録します。なお、

db.isEmpty()

メソッドは、テーブルが空かどうか 調べるメソッドです。次項の「データベース処理」を見てください。  これは

@Table

アノテーションを適用して、実際にデータベーステーブルを作成する処 理です。登録した2件のデータは、後で読みだされJSFページに表示されます。  ところで、同じことがコンストラクタの中でもできそうに思えますが、実はできません。 なぜなら、データベース処理に必要な

db

が使えないからです。

db

@EJB

によりインジェ クトしますが、インジェクションが行われるのはコンストラクタの実行後です。したがって、 コンストラクタの中では

db

null

なのです。  一方、

@PostConstruct

を付けたライフサイクル・コールバックメソッドは、コンスト ラクタの「直後」に実行されるのでインジェクトした

db

が使えます。このため、他の例題で も最初に行っておくデータベース処理は、ライフサイクル・コールバックメソッドの中で 実行しています。  バッキングビーンには、データベース処理を行う

getAll

メソッドもあります(6、7行目)。

getAll

メソッドは登録した全データを返します。

getAll

メソッドが使っている

db

は、次 に示す

EmployeeDb

@EJB

でインジェクトしたものです。 例題1(続き):データベース処理 sample23-01/beans/EmployeeDb.java 1 @Stateless

2 public class EmployeeDb { 3 @PersistenceContext 4 private EntityManager em; 5

6 public void create(Employee emp) { // 新規登録 7 em.persist(emp);

8 }

9 public List<Employee> getAll() { // 全データのListを返す

10 return em.createQuery("SELECT c FROM Employee c").getResultList(); 11 }

12 public boolean isEmpty() { // 1件だけListに読み出して空かどうか調べる 13 return em.createQuery("SELECT c FROM Employee3 c") 14 .setMaxResults(1) // 読み出し件数を最高1件にする 15 .getResultList() // 読み出し結果をListで返す 16 .isEmpty(); // Listは空か(trueなら空) 17 }

(7)

16

 このデータベース処理のクラスは、必要なメソッドだけを定義しています。新しいメソッ ドとして、テーブルに1件もデータがなく空かどうかを調べる

isEmpty()

メソッドがあり ます。文法的なことは、後の章で解説しますが、テーブルから1件だけ読み出してListとし て取り出し、そのListが空かどうか調べています。  データベース処理のEJBは、この章の他のプロジェクトでもこれと同じです。  さて、最後は表示用の

index.xhtml

です。 例題1(続き):データテーブルによる表示 sample23-01/index.xhtml 1 <h:body> 2 <h1>テーブルの名前を変える</h1> 3 <h:form>

4 <h:dataTable value="#{bb.all}" var="item">

5 <h:column>#{item.id}</h:column> 6 <h:column>#{item.name}</h:column> 7 <h:column>#{item.eval}</h:column> 8 </h:dataTable> 9 </h:form> 10 </h:body>  

h:dataTable

を使ってデータベース内の全データを表示します。

#{bb.all}

は、フィー ルド変数

all

へのアクセスですが、実際にはゲッターである

getAll()

をアクセスします。 つまり、バッキングビーンの

getAll()

メソッドにアクセスして、全エンティティの入っ た

List

を取得します。このような表示方法も、この後の例題では皆同じです。

2.2 複数のテーブルで構成する

 

@SecondaryTables

@SecondaryTable

は、

@column

タグと共に用いてエンティ

ティをいくつかのテーブルのカラム(列項目のこと)で構成する時に使用します。

 例題の

Employee1

クラスでは、

city

eval

は、実際にはADDRESSテーブルとSKILL テーブルのカラムです。したがって、図で示すようにこのエンティティは、EMPLOYEE1、 ADDRESS、CITYの3つのデータベーステーブルを使用します。

(8)

16

複数のテーブルから構成する sample23-02/beans/Employee1.java 例題2 1 @Entity 2 @SecondaryTables( { 3 @SecondaryTable(name="address"), 4 @SecondaryTable(name="skill") 5 })

6 public class Employee1 implements Serializable{ 7 @Id

8 private String id; 9 @Column(table="address")

10 private String city; 11 @Column(table="skill")

12 private String eval; 13 private String name;

14 // コンストラクタ、セッターとゲッター(省略) 15 }

 

@SecondaryTables

アノテーションの引数に

@SecondaryTable

を書いて、まず使用 するテーブル名を指定しておきます。書き方に注意してください。

 指定したテーブルのどのカラムを使うかは、使う時に

@Column

で指定します。9行目では、

city

がADDRESSテーブルのカラムであることを示しています。また11行目では、

eval

がSKILLテーブルのカラムであることを示しています。

 さて、sample23-02を開いて動かしてみましょう。sample23-02は、バッキングビーン で次のようにエンティティを登録します。

@PostConstruct public void init() { if(db.isEmpty()){

db.create(new Employee1("100", "東京", "A", "田中宏")); db.create(new Employee1("110", "大阪", "B", "鈴木太郎")); } }  実行すると、データベースに登録したこの2件のデータが表示されます。  その後、NetBeansのサービスタブをクリックして、[表]を右クリック⇒[リフレッシュ] を選択します。データベースにADDRESS、EMPLOYEE1、SKILLの3つのテーブルがで きていることを確認してください。

(9)

16

 ADDRESSなどのテーブルを右ボタンでクリックし、[データを表示]を選択するとテー ブルの中身を見ることができます。"東京" などのデータが保管されていることも確認して みてください。

2.3 他のクラス全体を1つのフィールドとして組み込む

 

@Embeddable

を付けたクラスを、

@Embedded

を使ってエンティティの1つの属性 (フィールド変数)として組み込みます。  組み込まれるクラスには

@Embeddable

を付けます。また、組み込むエンティティ側では、 フィールド変数に

@Embedded

を付けて他のクラスを組み込んでいることを示します。  この方法ではデータベースには、プレーンな 1 つのテーブルだけが作成されます。 他のクラスを組み込む sample23-03/beans/Tel.java 例題3 1 @Embeddable

2 public class Tel implements Serializable { 3 private String telephone;

4 private String cellular;

5 // コンストラクタ、セッターとゲッター(省略) 6 }

例題3(続き):Telクラスを組み込むエンティティ sample23-03/beans/Employee2.java 1 @Entity

2 public class Employee2 implements Serializable { 3 @Id

4 private Long id; 5 private String name; 6 @Embedded

7 private Tel tel; 8 // コンストラクタ(省略)

9 // セッターとゲッター(省略)

(10)

16

 例題では、

Tel

クラスに

@Embeddable

を付けています。一方、

Employee2

エンティティ・ クラスでは、

@Embedded

を使って

Tel

型の

tel

という変数として組み込んでいます。  さて、sample23-03はバッキングビーンで次のようにエンティティを登録しています。

@PostConstruct public void init() { if(db.isEmpty()){

db.create(new Employee2(100L, "田中宏", new Tel("123-123", "092-111"))); db.create(new Employee2(110L, "鈴木太郎", new Tel("321-332", "090-333"))); } }  実行してテーブルを見ると、TELテーブルはなく、EMPLOYEE2だけが生成されていま す。エンティティではTelクラスのフィールドがありますが、データベースではTelクラス のフィールドを取り込んだ1つのテーブルだけが生成されることに注意してください。

2.4 カラムの名前を変える

 テーブルのカラムに、エンティティのフィールドと異なる名前を付けるには、

@Column

を使います。これは、既存のデータベースをJPAで扱う時に必要になります。また、

null

を許容するかどうかも

@Column

で指定できます。使用例は、5節の例題7を見てください。 // nameをデータベースではUSERという名前にする @column(name="USER", nullable=false) private String name;

2.5 カラムをデーベースに保管しないように指示する

 

@Transient

は、エンティティの中でデータベースに保存しないフィールドを指定しま

す。エンティティ内だけの一時的な変数になります。使用例は、5節の例題7を見てください。 @Transient

(11)

16

 主キーの自動生成や複合キーの構成を指示するアノテーションです。

3.1 主キーを自動生成する

 例えば、ブログの投稿記事に付ける主キーは、一連番号のような他と重複しないもので あれば何でもよいはずです。このようなタイプの主キーは入力するのではなく、データベー ス側で自動生成させます。 主キーの自動生成指定 sample23-04/beans/Employee3.java 例題4 1 @Entity

2 public class Employee3 implements Serializable { 3 @Id

4 @GeneratedValue(strategy = GenerationType.AUTO)

5 private Long id; 6 private String name;

7 public Employee3(String name) { // コンストラクタ 8 this.name = name; 9 } 10 // 引数なしコンストラクタ、セッターとゲッター(省略) 11 }  

@Id

と共に

@GeneratedValue

を主キーに指定するとキーの値を自動生成します。  キーの生成方式には次のように4種類ありますが、多くの場合GenerationType.AUTOを 指定します。 キー 生成方法 GenerationType.SEQUENCE データベースのSQLシーケンスを使って自動生成する GenerationType.IDENTITY データベースのIDカラムの値を主キーの値とする GenerationType.TABLE SQLシーケンス名と現在値を格納したテーブルを使って自動生成する GenerationType.AUTO 使用しているデータベースシステムの既定の方法を使う Section Section Section Section Section S Secttiion Se Sectctioionn Section S i S ti

(12)

16

 さて、sample23-04はバッキングビーンで次のようにエンティティを登録しています。 @PostConstruct

public void init() { if(db.isEmpty()){ db.create(new Employee3("田中宏")); db.create(new Employee3("鈴木太郎")); } }  主キーは自動生成するので、コンストラクタに主キーの指定がないことに注意してくだ さい。なお、自動生成する主キーのデータ型は、一般にはLong型を使います。  sample23-04を実行してください。エンティティが2件表示されますが、それぞれ1、2と いう

id

が割り振られています。また、データベースでは、SEQUENCEという名前で番号 を管理するシーケンステーブルが自動作成されていることがわかります。

3.2 複合キーを使う

 主キーが複数の値の寄せ集めで構成されることは、よくあるケースです。例えば、業務 サービスを利用するユーザーの主キーは、所属するグループ(責任者、管理者、利用者など) のコードとユーザー番号を連結したものを使います。このような主キーを複合キーといい ます。  複合キーを使うには、複合キーのクラスを作成し

@Embeddable

を付けておきます。一 方エンティティでは、そのクラス型の変数をフィールド変数に定義し、複合キーであるこ とを示すために

@EmbeddedId

を付けます。

(13)

16

複合キーを使う sample23-05/beans/CompositeKey.java 例題5

1 @Embeddable

2 public class CompositeKey implements Serializable { // Serializableを実装 3 private String groupId;

4 private String userId; 5 6 public CompositeKey(){} // 引数のないコンストラクタ 7 // equals()とhash()を実装する(詳細は後で) 8 // その他のコンストラクタ、セッターとゲッター(省略) 9 } 例題5(続き):複合キーを持つエンティティ sample23-05/beans/Empoyee4.java 1 @Entity

2 public class Employee4 implements Serializable { 3 @EmbeddedId

4 private CompositeKey id; 5 private String name; 6

7 public Employee4(CompositeKey id, String name) { // コンストラクタ 8 this.id = id; 9 this.name = name; 10 } 11 // 引数なしコンストラクタ、セッターとゲッター(省略) 12 }  例題は、

CompositeKey

クラスが複合キークラスです。複合キーとして使うクラスには、

@Embeddable

を付けます。また、次の3点を守ってください。 ① Serializableインタフェースを実装する ② 引数のないコンストラクタを定義する ② hashメソッドとequalsメソッドをオーバーライドする  

equals

hash

メソッドについてはこの後で別に解説します。

 一方、

Employee4

エンティティは

@EmbeddedId

を使って、

CompositeKey

型の

id

を 主キーに指定しています。この場合

@EmbeddedId

があるので

@Id

は不要です。

 さて、sample23-05では、次のように、エンティティを登録しています。

CompositeKey

オブジェクトを

new

で生成して主キーとしている点に注意してください。

(14)

16

@PostConstruct public void init() { if(db.isEmpty()){

db.create(new Employee4(new CompositeKey("A10", "100"), "田中宏")); db.create(new Employee4(new CompositeKey("A20", "200"), "鈴木太郎")); } }  sample23-05を実行してください。2件のエンティティが表示されます。実行後、図のよ うにEMPLOYEE4テーブルが作成されていることを確認してください。

【重要】複合キーのクラスではequalsとhashCodeをオーバーライドする

 キーが 1 つの値なら何の問題もないのですが、複合キーの場合、あるキーと別のキーが 同じかどうか判定するメソッドを、プログラマが用意しなければなりません。  それには複合キークラスのための

hashCode

メソッドと

equals

メソッドを定義する必 要があります。ただ、それらを自分で作成するよりもNetBeansで自動生成する方が確実で 簡単です。次の手順で自動生成します。 equalsとhashCodeの自動生成の手順 ① メソッドを挿入する位置にカーソルを置く ② メニューで[ソース]→[コードを挿入]→[equalsおよびhashCode...]と選択する ③ ダイアログで、groupIdとuserIdにチェックを入れ[生成]を押す

(15)

16

例題5(続き):自動生成されたequalsとhashCode sample23-05/beans/CompositeKey.java(抜粋) 1 @Override

2 public int hashCode() { 3 int hash = 7;

4 hash = 17 * hash + Objects.hashCode(this.groupId); 5 hash = 17 * hash + Objects.hashCode(this.userId); 6 return hash;

7 } 8

9 @Override

10 public boolean equals(Object obj) { 11 if (obj == null) { 12 return false; 13 } 14 if (getClass() != obj.getClass()) { 15 return false; 16 }

17 final CompositeKey other = (CompositeKey) obj; 18 if (!Objects.equals(this.groupId, other.groupId)) { 19 return false; 20 } 21 if (!Objects.equals(this.userId, other.userId)) { 22 return false; 23 } 24 return true; 25 }  NetBeansの自動生成では、この他に

toString

メソッドも簡単に生成できます。併せて 利用するとよいでしょう。 【API:Java.util.Objects】

public static boolean equals(Object a, Object b)

 a==b==nullならtrueが返され、aとbのどちらか一方だけがnullの場合はfalseが返されます。それ以外の 場合は、a.equals(b)の値が返されます。a、bがnullであっても比較できるのが特徴です。

public static int hashCode(Object o)

 oがnullでなければそのハッシュ・コードを計算して返し、o==nullなら0を返します。

public static String toString(Object o)

 oがnullでなければo.toString()の呼び出し結果を返し、nullならば"null"を返します。 ※ ObjectsはJava7から追加されたクラスで、便利なクラスメソッドがあります。

3.3 複合キーを使う(@IdClass)

次の ページへ

(16)

16

ラスを指定します。複合キークラスに

@Embeddable

を付けておく必要はありません。  エンティティクラスでは、

@Id

を使って複数のキーをそれぞれ指定します。複合キーの 中身が見えるのでわかりやすいのですが、反面、

CompositeKey

クラスで定義したフィー ルド変数名を間違わないようにエンティティの方にも書かねばならないという不便さもあ ります。 複合キーを使う(その2) sample23-06/beans/CompositeKey.java 例題6

1 public class CompositeKey implements Serializable { 2 private String groupId;

3 private String userId; 4 5 public CompositeKey(){} // 引数のないコンストラクタ 6 // equals()とhash() (省略) 7 // その他のコンストラクタ、セッターとゲッター(省略) 8 } 例題6(続き):複合キーを使うエンティティ(その2) sample23-06/beans/Empoyee5.java 1 @Entity 2 @IdClass(CompositeKey.class)

3 public class Employee5 implements Serializable { 4 @Id

5 private String groupId; 6 @Id

7 private String userId; 8 private String name;

9 public Employee5(String groupId, String id, String name){ 10 this.groupId = groupId; 11 this.userId = id; 12 this.name = name; 13 } 14 // 引数なしコンストラクタ、セッターとゲッター(省略) 15 }  さて、sample23-06のバッキングビーンでは、次のようにしてエンティティを登録します。 キーをそのままコンストラクタの中に記入している点に注意してください。

(17)

16

@PostConstruct public void init() { if(db.isEmpty()){ db.create(new Employee5("A10", "100", "田中宏")); db.create(new Employee5("A20", "200", "鈴木太郎")); } }  sample23-06を実行してください。2件のエンティティが表示されます。実行後、図のよ うにEMPLOYEE5テーブルが作成されていることを確認してください。

(18)

16

 エンティティは、一般的なJavaクラスなので、フィールドに、プリミティブ型の値だけ でなく、オブジェクトを指定することができます。手始めに、この節では、ファイル(画像ファ イルなど)、列挙型の値、日付と時刻などの扱い方を解説し、次節でその具体例を示します。

4.1 ファイルなどの大きなデータを記録する

 1つのファイルデータを、エンティティの1つのフィールド変数に持たせることも珍しく ありません。例えば画像ファイルなどをデータベースに保存するために使います。ただデー タサイズが大きくなるので、データベース内では特別な形式で格納します。

@Lob

はそのよ うな大きなデータであること示すアノテーションです。  また、大きなデータを持つエンティティを何件も同時に読み出すと効率が悪くなります。 そこで、検索した時にフィールドは空にしておき、実際にそのフィールドをアクセスした 時にデータを読み出すようにできます。  これを遅延フェッチといい、

@Basic(fetch=FetchType.LAZY)

によって指定します。

@Basic

を指定しない場合、

FetchType.EAGER

(速やかに取得する)を指定したものとみ なされます。 @Basic(fetch=FetchType.LAZY) @Lob

private byte[] picture; // 画像などの大きなデータ

@Basic(fetch=FetchType.LAZY) @Lob

private String bigText; // 大きなテキストファイル

4.2 列挙の記録方法を指定する

 エンティティのフィールドが列挙型の場合、内部表現を序数(ODINAL)にするか文字列 (STRING)にするか指定するのが、

@Enumerated

です。

@Enumerated

を付けないか、引

数に何も指定しなければODINALが指定されたことになります。  将来、列挙定数の変更がありそうな場合は、STRINGを指定しておくと安全です。 Section Section Section Section Section S Secttiion Se Sectctioionn Section S i S ti

(19)

16

enum GroupColor{ GREEN, BLUE, RED } @Entity

public class Employee implements Serializable { @id

private String key; private String name;

@Enumerated(EnumType.STRING)

private GroupColor color; }

4.3 日付と時刻の取り扱い

 日付と時刻は

Date

型や

Calendar

型を使う方法もありますが、エンティティのフィール ドとして使うには、

@Temporal

アノテーションを付けて、データ型の種類を指定する必要 がありました。 @Temporal(TemporalType.DATE)

private Calendar birthday;

 しかし、JPA2.2からDate and Time APIのクラスを使えるようになり、扱いが便利にな りました。JDBCの基本型としてJPAに取り込まれたので、これらを使う時は、アノテーショ ンは、一切、不要です。JDBCの型に対応する使用できる型は次の5つです。

種類 Java言語での型 JDBCでのデータ型

日付 java.time.LocalDate DATE 時刻 java.time.LocalTime TIME タイムスタンプ java.time.LocalDateTime TIMESTAMP

オフセット時刻 java.time.OffsetTime TIME_WITH_TIMEZONE オフセットタイムスタンプ java.time.OffsetDateTime TIMESTAMP_WITH_TIMEZONE

(20)

16

 4節で解説したアノテーションを使った例題を示します。また、この例題では、役に立 つ応用として、画像をデータベースから読み出して表示する方法についても解説します。  データベースに保存した画像をウェブに表示するために、PrimeFacesのイメージタグ (

p:graphicImage

)を 使 います。PrimeFaces(

http://www.primefaces.org/

)は、

標準のJSF機能を補うサードパーティ製のFaceletsタグライブラリです。  PrimeFacesを利用するには、

pom.xml

に次の依存性を追加する必要があります。また、 JSFページの名前空間の定義でPrimeFaces用の追加指定が必要です。 ▼Pomファイルへの追加 <dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>7.0</version> <type>jar</type> </dependency> ▼JSFページの名前空間定義の追加 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:f="http://xmlns.jcp.org/jsf/core">  プロジェクトファイルの

pom.xml

index.xhtml

を見て、確認してください。  PrimeFacesは「Java標準」ではありませんが、どうしても必要なケースがあります。デー タベースから取り出した画像データをウェブに表示する今回のケースがまさにそのひとつ です。  もしもPrimeFacesの

p:graphicImage

タグを使わないとしたら、とても面倒なプログ ラムが必要になるでしょう。図は例題の画面です。データベースから2件のエンティティを 読み出し、

h:dataTable

を使って表示しています。 Section Section Section Section Section S Secttiion Se Sectctioionn Section S i S ti

(21)

16

5.1 エンティティ・クラス

多様なフィールドを持つエンティティ sample23-07/beans/Employee6.java 例題7

1 @Entity

2 public class Employee6 implements Serializable { 3 @Id

4 @Column(name = "EMP_ID", nullable = false)// テーブルでは名前を変える、null不可 5 private Integer id;

6 private String name;

7 @Lob // 画像などの大きなデータ

8 @Basic(fetch = FetchType.LAZY) // 遅延フェッチする 9 private byte[] picture;

10 @Enumerated(EnumType.STRING) // 列挙型を文字列で記録する 11 private GroupColor color;

12 private LocalDateTime dateTime; // タイプスタンプ 13 private LocalTime startTime; // 時刻 14 private LocalDate birthday; // 日付

15 @Transient // データベースには書き込まない 16 private int interval;

17 // コンストラクタ、セッター、ゲッターを省略 18 }

 エンティティには、4 節で解説した全てのアノテーションほか、2節で解説した

@Column

@Transient

が含まれています。

(22)

16

color

)、時刻と日付(

dateTime

startTime

birthday

)が含まれています。どれも 現実のアプリケーションでは必要になるデータ型です。  画像データは、@

Lob

で大きなデータであることを示し、

@Basic

で遅延フェッチを指定 しています。また、列挙型のデータは、

@Enumerated

で文字列表現で記録するよう指定 しています。なお、

interval

に付けた

@Transient

は、データベースに保管しないこと を指定するアノテーションです。  日付と時刻は、オブジェクトであるにも関わらずJDBCの基本型にマップされるので特 別なアノテーションは付ける必要がありません。

5.2 バッキングビーン (全体のソースリストは5.5に掲載)

 バッキングビーンでは、初めてアクセスがあった時、次のように2件のエンティティを生 成してデータベースに保管します。 例題7(続き):エンティティの登録 sample23-07/beans/Bb.java(抜粋) 1 // データベースにレコードを登録する 2 @PostConstruct

3 public void init() {

4 if(db.isEmpty()){ // テーブルが空ならば登録する 5 Employee6 tanaka = 6 new Employee6(1, 7 "田中宏", 8 bf.getBinary("/resources/images/pic-1.jpg"), // 画像 9 GroupColor.BLUE, // 列挙 10 LocalDateTime.of(2020, 6, 12, 10, 25, 12), // タイムスタンプ 11 LocalTime.of(10, 25, 12), // 時刻 12 LocalDate.of(2002, 7, 21)); // 日付 13 db.create(tanaka); 14 15 Employee6 suzuki = 16 new Employee6(1, 17 "鈴木一郎", 18 bf.getBinary("/resources/images/pic-2.jpg"), // 画像 19 GroupColor.GREEN, // 列挙 20 LocalDateTime.of(2023, 3, 8, 17, 11, 43), // タイムスタンプ 21 LocalTime.of(17, 11, 43), // 時刻 22 LocalDate.of(200,12, 4)); // 日付 23 db.create(suzuki); 24 } 25 }

(23)

16

 これまでの他の例題と同じように、

@PostConstruct

を付けたライフサイクル・コール バックメソッドの中でデータベースに2件のエンティティを新規登録します。  8、18行目の

bf.getBinary

メソッドは、画像ファイルへのパスを引数にして実行すると、 画像データを読み出してbyte型の配列に入れて返すユーティリティメソッドです。バッキ ングビーンでは次のようにインジェクトしています。 @Inject BinFileUtil bf; // バイナリファイルの読み書きを行うユーティリティ  また、引数のファイルパスは、プロジェクトのリソースフォルダを指定していますが、 このようなパスをアプリケーションルートからのパスといいます。

getBinary

メソッドで は、これを絶対パスに変換して使っています。  また、9、19行目は列挙型の値を指定しています。最後に、10 ~ 12、20 ~ 22行は、日付・ 時刻のデータの指定です。それぞれのクラスのクラスメソッドでインスタンスを生成しま す。 ※列挙型、日付、時刻は、「わかりやすいJava オブジェクト指向徹底解説」に詳しい解説があります。 【API:BinFileUtil(バイナリファイルの入出力)】 static byte[] getBinary(String path)

 ファイル内の全データを読み出して返す。引数にアプリケーションルートからのファイルURLを指定する

static void putBinary(byte[] data, String path)

(24)

16

5.3 index.xhtml

例題7(続き):画像を含む表示 sample23-07/index.xhtml 1 <h:body> 2 <h1>フィールドに属性を指定する</h1> 3 <h:form>

4 <h:dataTable value="#{bb.all}" var="item"> 5 <h:column>

6 <p:graphicImage value="#{bb.pic}">

7 <f:param name="empId" value="#{item.id}"/> 8 </p:graphicImage>

9 </h:column> 10 <h:column>

11 <h:panelGrid columns="2">

12 userid:<h:outputText value="#{item.id}"/> 13 名 前:<h:outputText value="#{item.name}"/> 14 カラー:<h:outputText value="#{item.color}"/> 15 記録日時: 16 <h:outputText value="#{item.dateTime}"> 17 <f:convertDateTime type="localDateTime" 18 pattern="y年MM月dd日 HH時mm分ss秒"/> 19 </h:outputText> 20 開始時刻: 21 <h:outputText value="#{item.startTime}"> 22 <f:convertDateTime type="localTime" 23 pattern="H:m:s" /> 24 </h:outputText> 25 誕生日: 26 <h:outputText value="#{item.birthday}"> 27 <f:convertDateTime type="localDate" 28 pattern="y年MM月dd日(eee)" /> 29 </h:outputText> 30 </h:panelGrid> 31 </h:column> 32 </h:dataTable> 33 </h:form> 34 </h:body>

 表示には、

h:dataTable

(4行目)を使っています。

h:dataTable

は、

value= "#{bb.

all}"

によりバッキングビーンの

all

から値を受け取ります。

all

の実体は

getAll

メソッ ドで、次のようにデータベースの全データを

List

に入れて返すメソッドです。

(25)

16

public List<Employee6> getAll() {

return db.getAll(); // データベースから全件を読み取り、Listに入れて返す }  表示する列は2列です。

h:dataTable

column

のうち、枠で囲った1列目が画像表示、 残りが2列目の表示です。2列目には、さらに

h:panelGrid

を入れてその他のデータを表 示していることに注意してください。表示は

var="item"

により、

item

という変数を使っ て行います。  また、2列目の日付、時刻の表示は8章のコンバータで解説した方法です。表示形式を pattern属性で指定します。

5.4 画像データの受け渡し

 

index.xhtml

は、1列目の画像の表示に、

p:graphicImage

タグを使います。これは PrimeFacesの画像用タグです。 <p:graphicImage value="#{bb.pic}">

<f:param name="empId" value="#{item.id}"/> </p:graphicImage>  本来、JSFページの画像タグには、画像ファイルのURLが必用です。ブラウザはその URLをサーバーに送信することで、表示する画像データを取得します。しかし、画像デー タがファイルではなく、データベースの中にある場合は、そもそもURLがありません。  そこで、ブラウザは、URLの代わりに画像データを持つエンティティの主キー(

item.

id

)を送ります。サーバーは、そのキーを使ってデータベースを検索し、ブラウザに送信 すべき画像データを取り出すという仕組みです。  例題では、ブラウザがサーバーにキーを送れるように、

f:param

タグを使います。  ここで

f:param

タグについて、少し説明しましょう。

f:param

タグは、ブラウザがサー バーにアクセスする時のリクエストパラメータになります。  例えば、

value

に指定された

#{item.id}

の値が "1" だった場合、ブラウザからサーバー へのアクセスは次のようなgetアクセスになります。

(26)

16

 サーバー側ではこの値を次のようなコードで取得するのが普通です。 @Inject

ExternalContext ec; // 実行環境のパラメータを持つオブジェクト ・・・

String id = ec.getRequestParameterMap().get("empId"); // 常にString型になる

 ExternalContextは、実行環境にかかわるパラメータなどを持つオブジェクトです。ブ ラウザから受け取ったパラメータは、ExternalContextが持つMapに格納してあるので、 Mapから値を取り出すのがこのコードです。

 そこで、もう一度

index.xhtml

を見ましょう。

p:graphicImage

タグを見ると次のよ うです。

<p:graphicImage value="#{bb.pic}">

 

#{bb.pic}

は、バッキングビーンの

getPic()

メソッドをアクセスします。つまり

getPic()

メソッドが返す値を画像として表示するわけです。

getPic()

メソッドは、次 のような内容です。

例題7(続き)バッキングビーン(抜粋) sample23-07/beans/Bb.java

public StreamedContent getPic() {

if (fc.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { // フェーズ判定 // 1回目のアクセス。HTMLを返す

return new DefaultStreamedContent(); } else {

// 2回目のアクセス。画像データを返す

// ブラウザから受け取ったidの値を取り出し、データベースからエンティティを検索する String id = ec.getRequestParameterMap().get("empId");

Employee6 e = db.find(Integer.valueOf(id));

// 画像データをブラウザが読み出せるストリームにして返す

ByteArrayInputStream in = new ByteArrayInputStream(e.getPicture());

DefaultStreamedContent ds = new DefaultStreamedContent(in); return ds;

} }

 青枠の部分がブラウザから受け取った

id

の値を使って、データベースから該当のエン ティティを検索する部分です。これについては説明しましたので、残りの部分を解説します。

(27)

16

 処理が

if

文で2つに分けられています。知っていることかもしれませんが、ブラウザは 画像データを表示する際、2度、サーバーにアクセスします。1度目のアクセスが普通のア クセスでHTMLドキュメントを受け取ります。しかし、HTMLの中に画像タグがあった場 合、ブラウザは、もう1度サーバーにアクセスして画像データを受け取ります。  

if

文で2つに分けられているのは、どちらのアクセスか判定して、HTMLを返すのか、 画像データを返すのか、処理を分けるためです。判定には、FacesContext(JSFの実行環 境についてのパラメータなどを持つオブジェクト)を使います。  FacesContextはあらかじめインジェクトしてある(fc)ので、その

getCurrentPhaseId()

メソッドが返す値で判定できます。 if (fc.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { // フェーズ判定  

PhaseId.RENDER_RESPONSE

という列挙定数は、HTMLの読み取りフェーズを意味し ます。そこで、この場合は、HTMLデータを返します。

DefaultStreamedContent()

を返していますがこれは、ブラウザとの通信に使うPrimeFacesの専用ストリームで、いつ もこのように書きます。  

PhaseId.RENDER_RESPONSE

でなかった場合は、画像データを要求されているわけ ですから、青枠の部分でエンティティを検索し、エンティティのゲッターメソッドである

getPicture()

を使って画像データを取り出します。  処理を見るとわかるように、やはり

DefaultStreamedContent()

を返すのですが、 今回は、画像データのストリームにして返します。この部分は、PrimeFacesの定型的な処 理です。

(28)

16

5.5 バッキングビーン(全体)

 最後に、バッキングビーン(

Bb.java

)のソースコード全体を通して再掲します。 例題7(続き):バッキングビーン sample23-07/beans/Bb.java 1 @Named 2 @SessionScoped

3 public class Bb implements Serializable { 4 @EJB 5 Employee6Db db; 6 @Inject 7 BinFileUtil bf; // バイナリファイルの読み書きを行うユーティリティ 8 @Inject 9 FacesContext fc; // JSF環境のパラメータなどを持つオブジェクト 10 @Inject 11 ExternalContext ec; // 実行環境のパラメータなどを持つオブジェクト 12 // 全データのリストを返す

13 public List<Employee6> getAll() { 14 return db.getAll();

15 }

16 // 画像データを返す

17 public StreamedContent getPic() {

18 if (fc.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { // フェーズ判定 19 // 1回目のアクセス。HTMLを返す

20 return new DefaultStreamedContent(); 21 } else { 22 // 2回目のアクセス。画像データを返す 23 // ブラウザから受け取ったidの値を取り出し、データベースからエンティティを検索する 24 String id = ec.getRequestParameterMap().get("empId"); 25 Employee6 e = db.find(Integer.valueOf(id)); 26 // 画像データをブラウザが読み出せるストリームにして返す

27 ByteArrayInputStream in = new ByteArrayInputStream(e.getPicture()); 28 DefaultStreamedContent ds = new DefaultStreamedContent(in); 29 return ds;

30 } 31 }

(29)

16

32 @PostConstruct

33 public void init() { // データベースにレコードを初期登録する 34 if(db.isEmpty()){ 35 Employee6 tanaka = 36 new Employee6(1, 37 "田中宏", 38 bf.getBinary("/resources/images/pic-1.jpg"), // 画像 39 GroupColor.BLUE, // 列挙 40 LocalDateTime.of(2020, 6, 12, 10, 25, 12), // タイムスタンプ 41 LocalTime.of(10, 25, 12), // 時刻 42 LocalDate.of(2002,7, 21)); // 日付 43 db.create(tanaka); 44 Employee6 suzuki = 45 new Employee6(2, 46 "鈴木一郎", 47 bf.getBinary("/resources/images/pic-2.jpg"), // 画像 48 GroupColor.GREEN, // 列挙 49 LocalDateTime.of(2023, 3, 8, 17, 11, 43), // タイムスタンプ 50 LocalTime.of(17, 11, 43), // 時刻 51 LocalDate.of(200,12, 4)); // 日付 52 db.create(suzuki); 53 } 54 } 55 }  なお、データベース処理を行うセッションビーン(

Employee6Db.java

)と列挙型の定 義(

Color.java

)はsample23-07プロジェクトのファイルを参照してください。

(30)

16

6.1 フィールド変数にListやSetを使う

 フィールド変数が、コレクション(

Collection

Set

List

)の場合のアノテーション です。ここで扱うコレクションの要素は、

String

、または

Integer

のようなラッパーク ラス型です。一般のオブジェクトが要素の場合は、17章で解説します。

List型のフィールド sample23-08/beans/Employee7 例題8

1 @Entity

2 public class Employee7 implements Serializable{ 3 @Id

4 private Long id; 5 private String name;

6 @ElementCollection // コレクション型の変数であることを知らせる 7 @CollectionTable(name="notes") // コレクションのテーブル名をNOTESとする 8 private List<String> notes; // List型のフィールド

9 public Employee7(Long id, String name, List<String> notes){ // コンストラクタ 10 this.id=id; 11 this.name=name; 12 this.notes=notes; 13 } 14 public Employee7(){} 15 // セッターとゲッター(省略) 16 }  コレクションには、

@ElementCollection

を付加します。コレクション型の変数は、 多数のデータを含むので、別のデータベーステーブルに作成されます。そのため、テーブ ルが 2 つできることに注意してください。そのテーブル名は、元クラスの名前とコレクショ ン変数名をアンダーバーでつないだ名前になります。別の名前を指定したい時は、例題の ように、

@CollectionTable

で指定します。

 変数

notes

に対応して作成されるテーブル(

NOTES

)は、変数

notes

の全要素を含む テーブルです。図に示すように、

EMPLOYEE7

id

notes

の値が対で記録されます。例 えば、NOTESテーブルの中で

employee7_id

が1の要素が2つありますが、これらは、

EMPLOYEE7

テーブルで、

id

が 1 であるエンティティが持つリスト(

notes

)の要素です。 Section Section Section Section Section S Secttiion Se Sectctioionn Section S i S ti

(31)

16

例題では、バッキングビーンで次のようにエンティティを登録しています。 例題8(続き):List型の要素を持つエンティティの作成 sample23-08/beans/Bb.java 1 @Named 2 @RequestScoped 3 public class Bb { 4 @EJB 5 Employee7Db db;

6 public List<Employee7> getAll() { 7 return db.getAll();

8 }

9 @PostConstruct 10 private void init() { 11 if (db.isEmpty()) {

12 Employee7 emp1 = new Employee7( 13 1L, 14 "田中宏", 15 Arrays.asList( "5日15時:ミーティング", 16 "12日13時:営業会議", 17 "14日10時:報告書提出") 18 );

19 Employee7 emp2 = new Employee7( 20 2L, 21 "鈴木一郎", 22 Arrays.asList( "7日∼12日:出張", 23 "13日午後:打ち合わせ", 24 "14日7時:懇親会") 25 ); 26 db.create(emp1); 27 db.create(emp2); 28 } 29 } 30 }

(32)

16

 Employee7のエンティティは、コンストラクタで作成します。コンストラクタの引数 は、

id

name

notes

の3つ で す。

notes

List

型(

ArrayList

)な の で、

Arrays.

asList()

メソッドで作成しています。  表示は

h:dataTable

を使います。リストの要素を表示するために、子要素にも

h:date

Table

を使います。sample23-08を実行してください。 例題8(続き):List型のフィールドの表示 sample23-08/index.xhtml <h:body> <h1>List型のフィールド変数</h1> <h:form>

<h:dataTable value="#{bb.all}" var="item" columnClasses="c1,c2,c3"> <h:column>#{item.id}</h:column>

<h:column>#{item.name}</h:column> <h:column>

<h:dataTable value="#{item.notes}" var ="n"> // Listの要素を表示する <h:column>#{n}</h:column> </h:dataTable> </h:column> </h:dataTable> </h:form> </h:body>

 この

h:dateTable

は3列の表で、それぞれに

id

name

notes

を表示します。データ ベースから取得した

List

の1つの要素を

item

という変数でアクセスして表示します。しか し、

item.notes

は単一の値ではなく

List

です。

 そこで枠で囲った部分に示すように、3列目にはさらに

h:dateTable

を作成しています。 具体的には、

item.notes

から要素を取り出す変数として

n

を宣言し、

#{n}

で要素を表 示します。

(33)

16

6.2 フィールド変数にMapを使う

 フィールド変数が

Map

の場合も、

@ElementCollection

を付加するだけです。マップ 型の変数もリストと同様に、別のデータベーステーブルに移されます。  なお、テーブル名を指定したい時は、

@CollectionTable

で名前を指定します。指定 しない場合、元クラスの名前とマップ変数名をアンダーバーでつないだ名前のテーブルが 作成されます。また、

@MapKeyColumn

を使ってマップのキーを持つカラムの名前を指定 し、

@Column

を使ってマップの値を持つカラムの名前を指定できます。 マップを属性に持つエンティティ sample23-09/beans/Employee8 例題9 @Entity

public class Employee8 implements Serializable { @Id

private Long id; private String name;

@ElementCollection // Map型の変数であることを知らせる @CollectionTable(name="items") // Mapのテーブル名をITEMSとする @MapKeyColumn(name="drink") // Mapのキーカラム名をDRINKとする @Column(name="num") // Mapの値カラム名をNUMBERとする private Map<String, Integer> items;

// コンストラクタ、セッターとゲッター(省略) }  このエンティティからもテーブルは、本体(EMPLOYEE8)とマップ(ITEMS)の2つがで きます。ITEMSテーブルには、マップである

items

のすべての要素が含まれます。    例題では、バッキングビーンで次のようにしてエンティティを作成し、データベースに 登録しています。

(34)

16

例題9(続き):Map型の要素を持つエンティティの作成 sample23-09/beans/Bb.java 1 @Named 2 @RequestScoped 3 public class Bb { 4 @EJB 5 Employee8Db db;

6 public List<Employee8> getAll() { 7 return db.getAll();

8 }

9 @PostConstruct 10 private void init() { 11 if(db.isEmpty()){

12 Map<String, Integer> map1 = new LinkedHashMap(); 13 map.put("ビール", 10);

14 map.put("ウィスキー", 11); 15

16 db.create(new Employee8(1L, "田中宏", map)); 17 db.create(new Employee8(2L, "中村太郎", map)); 18 } 19 } 20 }  2つのエンティティが同じ

Map

を持つようにしてい ます。

Map

の場合、

List

と違って何かの選択肢など に使われます。そこでこれを

h:dataTable

の中のラ ジオボタンとして表示したのが、次の

index.xhtml

です。 例題9(続き):Map型のフィールドの表示 sample23-09/index.xhtml 1 <h:body> 2 <h3>Map型のフィールド変数</h3> 3 <h:form>

4 <h:dataTable value="#{bb.all}" var="item"> 5 <h:column>#{item.id}</h:column>

6 <h:column>#{item.name}</h:column> 7 <h:column>

8 <h:selectOneRadio value=""> // Mapの要素を表示する 9 <f:selectItems value="#{item.items}"/> 10 </h:selectOneRadio> 11 </h:column> 12 </h:dataTable> 13 </h:form> 14 </h:body>

(35)

16

 3つの列からなる

h:dateTable

にエンティティを表示しますが、3列目は

Map

なので、

h:selectOneRadio

の選択肢にしています。表示するとラジオボタンが2つ出て選択でき るようになります。ただ、選択結果を入れる変数などはエンティティにないのでここでは 空欄にしています(8行)。

(36)

16

 システム設計の最初の段階で、どのようなエンティティを使うのか決める時に、この章で解説 したマッピングの知識が必要になります。主キーの自動生成や複合キーはよく使うものの代表で す。また、データベースにファイルなどを格納することもしばしばあるので、使い方を知っておか ねばなりません。画像表示の方法はやや難しいのですが、実用上知っていないと困る知識の1つで す。

要点

※理解した項目にはチェックを入れましょう

マッピング・アノテーションの概要

□ デフォルトのマッピングでは、 ①クラス名をテーブル名にする ②フィールド変数名をカラム名にする ③データ型はデフォルトの変換規則にしたがって変換する

エンティティの構成を指定する

□ テーブルに付ける名前を指定する @Table(name="EMP")

public class Employee{・・・}

□ 複数のテーブルのカラムを集めてエンティティを構成する @SecondaryTables( {

@SecondaryTable(name="address"), @SecondaryTable(name="skill") })

public class Employee1{・・・}

□ クラス全体を1つのフィールドとして組み込む @Embeddable

public class Tel {・・・} public class Employee2{ ・・・

@Embedded private Tel tel; ・・・

}

□ フィールドとデータベース・カラムで違う名前を使う

@Column(name="m_name", nullable=false) // m_nameカラム、nullは禁止 private String name;

Section Section Section Section Section S Secttiion Se Sectctioionn Section S i S ti

(37)

16

□ データベースに保管しないよう指示する @Transient

private int interval;

主キーの構成を指定する

□ 主キーを自動生成する

@Id

@GeneratedValue(strategy = GenerationType.AUTO) private Long id;

□ 複合キーを使う(1) @Embeddable

public class CompositeKey implements Serializable {・・・} public class Employee4 implements Serializable

@EmbeddedId

private CompositeKey id; ・・・

}

□ 複合キーを使う(2)

public class CompositeKey implements Serializable { private String groupId;

private String userId;

public CompositeKey(){} // 引数のないコンストラクタ ・・・

} @Entity

@IdClass(CompositeKey.class)

public class Employee5 implements Serializable { @Id private String groupId;

@Id private String userId; ・・・ } □ 複合キーのクラスでは、hashメソッドとequalsメソッドをオーバーライドする

ファイル、列挙、日付と時刻の扱い方

□ 大きなデータを格納する @Basic(fetch=FetchType.LAZY) @Lob

private byte[] picture; // 画像などの大きなバイナリデータ @Basic(fetch=FetchType.LAZY)

@Lob

private String text; // 大きなテキストデータ □ 列挙を文字で記録するように指示する

@Enumerated(EnumType.STRING) private GroupColor color;

(38)

16

フィールド・マッピングの例題

□ データベースからの画像を表示するにはPrimeFacesのp:graphicImageを使う

<p:graphicImage value="#{bb.pic}">

<f:param name="empId" value="#{item.id}"/> </p:graphicImage> ・エンティティのキーをf:paramタグでサーバーに送信しておく ・表示処理はプログラムで処理する(#{bb.pic})

コレクションとマップを使う

□ フィールド変数にListやSetを使う @ElementCollection // コレクション型の変数であることを知らせる @CollectionTable(name="notes") // コレクションのテーブル名をNOTESとする private List<String> notes; // List型のフィールド

□ フィールド変数にMapを使う

@ElementCollection // Map型の変数であることを知らせる @CollectionTable(name="items") // Mapのテーブル名をITEMSとする @MapKeyColumn(name="drink") // Mapのキーカラム名をDRINKとする @Column(name="number") // Mapの値カラム名をNUMBERとする private Map<String, Integer> items;

練習

 9章「Ajax機能」では、コマンドボタンにAjax機能を持たせることにより操作しやすいテキスト編 集処理を作成しました(9章の例題3)。ただ、9章はテキストを普通のファイルから読み書きしてい たので、この練習ではテキストをデータベースに入れて編集するように変更します。  完成時の画面を示します。データベースに登録するのは、Bookクラス(Bookエンティティ)です。 Bookエンティティには、書籍の題名や著者名、作成年月日なども登録するので、本文テキスト以 外に、それらも画面の上端に表示します。

(39)

16

 exercise_chap16プロジェクトを開いて、以下の手順にしたがって作成しなさい。 1. 複合キークラス(exercise_chap16/beans/BkKey.java)の修正  データベースに保存するBookエンティティの主キーは、複合キーです。最初に複合キー・ク ラスであるBkKey.javaを開いて次の修正をしなさい  クラスは、次のようなフィールド変数からなります。これらが全体で1つのキーとなる複合キー です。

private String publisher; // 出版社コード private String code; // 書籍コード

① 複合キークラスであることを示す@Embeddableアノテーションを付ける

② 複合キークラスに必須のequals()メソッドとhashcode()メソッドをNetBeansの機能を使っ

て自動生成しなさい。自動生成の手順は次の通りです。  空白行(23行~)にカーソルを置き、メニューで次のようにする   <1>[ソース]→[コードを挿入]と選択

(40)

16

2. Bookエンティティ・クラス(exercise_chap16/beans/Book.java)の修正

 Bookエンティティは次のようなフィールドから構成されます。

private BkKey id; // 主キー(複合キー) private String title; // 題名

private String author; // 出版社 private LocalDate pdate; // 作成年月日 private String text; // テキスト

① idに複合キーであることを示す@EmbeddedIdアノテーションを付けなさい

② textに@Lobアノテーションと@Basicアノテーションを付け、遅延フェッチするように指定し

なさい

3. バッキングビーン(exercise_chap16/beans/Bb.java)の修正

 バッキングビーンのフィールド変数は次のようです。これらがindex.xhtmlに表示される

データです。

private BkKey id; // 主キー private String title; // 題名 private String author; // 著者名 private Calendar pdate; // 作成年月日 private String text; // 本文

 バッキングビーンには、@PostConstructを付けたinit()メソッドがあります。init()メソッ

ドは、システムの起動時にBookエンティティを作成してデータベースに登録します。ただし、2度

目以降のアクセスでは、データベースから読み出したエンティティをフィールド変数にセットして 表示します。

 この部分の処理は何も書いていないので、自分で作成しなさい。ただし、次の疑似コードの手 順に従いなさい。

① 複合キーを作成する --- BkKey key=new BkKey("aozora", "1");

② 複合キーでデータベースを検索しエンティティを得る --- Book book = db.find(key);

③ bookはnullか? <nullの場合> --- 登録がないということなので、フィールド変数に値を代入し、それを使って bookエンティティを作成して登録する   ①id(フィールド変数)にkeyを代入する   ②titleに"杜子春"を代入する   ③authorに"芥川龍之介"を代入する   ④pdateにLocalDateオブジェクトを代入する    --- pdate = LocalDate.now();   ⑤textにresources/data/toshishun.txtを読み込む    --- text = FileUtil.getText("/resources/data/toshishun.txt")

(41)

16

  ⑥以上のフィールド変数とBookのコンストラクタを使ってエンティティbookを作成

   --- Book book = new Book(id, title, author, pdate, text)

  ⑦bookをデータベースに新規登録する(BookDbクラスを使う)

<nullでない場合> --- 登録があったので、検索結果をフィールド変数に代入する

  id, title, author, pdate, text にそれぞれ値を代入する

  [例 id = book.getId(); のようにする]

4. 動作テスト

(42)

参照

関連したドキュメント

関係委員会のお力で次第に盛り上がりを見せ ているが,その時だけのお祭りで終わらせて

断面が変化する個所には伸縮継目を設けるとともに、斜面部においては、継目部受け台とすべり止め

と言っても、事例ごとに意味がかなり異なるのは、子どもの性格が異なることと同じである。その

つまり、p 型の語が p 型の語を修飾するという関係になっている。しかし、p 型の語同士の Merge

□ ゼミに関することですが、ゼ ミシンポの説明ではプレゼ ンの練習を主にするとのこ とで、教授もプレゼンの練習

【フリーア】 CIPFA の役割の一つは、地方自治体が従うべきガイダンスをつくるというもの になっております。それもあって、我々、

自分ではおかしいと思って も、「自分の体は汚れてい るのではないか」「ひどい ことを周りの人にしたので

これらの事例は、照会に係る事実関係を前提とした一般的