概要
エンティティの定義の仕方を説明します。ここでいっているエンティティとは、 データベースに永続化されるものだと考えてください。
注意点
エンティティ用のアノテーションは、JPAのものをそのまま利用していますが、 すべてのアノテーションや属性をサポートしているわけではありません。 ここに取り上げられているもののみサポートされているとお考えください。
エンティティ定義
エンティティにするためには、
@Entity
を必ず指定する必要があります。
@Entity public class Employee { ... }
name属性でエンティティ名を指定することができます。 次の例では、Empというエンティティ名を指定しています。
@Entity(name = "Emp") public class Employee { ... }
name属性を指定しなかった場合、エンティティのクラス名からパッケージ名を除いた部分が、
デフォルトのエンティティ名になります。例えば、クラス名が
examples.entity.Employee
の場合、デフォルトのエンティティ名は、
Employee
になります。
テーブル定義
テーブル情報を指定するには、
@Table
を使います。
name属性でテーブル名を指定することができます。
指定しなかった場合、テーブル名はエンティティ名と同じになります。エンティティ名が、
AaaBbb
のようなキャメル記法の場合、テーブル名は、
AAA_BBB
のように'_'区切りだとみなされます。
このルールは、convention.diconで指定されている
org.seasar.framework.convention.impl.PersistenceNamingConventionImpl
のfromEntityNameToTableName()の実装を変えることで、カスタマイズすることができます。
次の例では、EMPというテーブル名を指定しています。
@Entity @Table(name = "EMP") public class Employee { ... }
schema属性でスキーマを指定することができます。 指定した場合、自動生成されるSQLのテーブル名がスキーマ.テーブル名になります。
catalog属性でカタログを指定することができます。 指定した場合、自動生成されるSQLのテーブル名がカタログ.テーブル名になります。 catalog属性とschema属性の両方を指定した場合、自動生成されるSQLのテーブル名はカタログ.スキーマ.テーブル名になります。
継承
エンティティは
@MappedSuperclass
が指定されたクラスを継承できます。
@MappedSuperclass public abstract class AbstractEmployee { ... }
@Entity public class Employee extends AbstractEmployee { ... }
@MappedSuperclass
には、エンティティと同じように永続プロパティを定義できます。
定義された永続プロパティは、サブクラスの永続プロパティとみなされます。
スーパークラスとサブクラスでインスタンスフィールドの名前が重複しないように定義してください。
エンティティは
@MappedSuperclass
が指定されていないクラスも継承できます。
しかし、この場合、スーパークラスのプロパティはサブクラスの永続プロパティとみなされません。
エンティティが他のエンティティを継承することはサポートされていません。
プロパティ定義
永続プロパティの型
関連 および 永続化対象外 を除いたプロパティとして利用可能な型は次のとおりです。
- プリミティブ型
- boolean
- char
- byte
- short
- int
- long
- float
- double
- ラッパー型
- Boolean
- Character
- Byte
- Short
- Integer
- Long
- Float
- Double
- 文字列型
- java.lang.String
※ ラージオブジェクト にすることができます。
- 数値型
- java.math.BigDecimal
- java.math.BigInteger
- 日付・時刻型
- java.util.Date
- java.util.Calendar
- java.sql.Date
- java.sql.Time
- java.sql.Timestamp
※
java.util.Date
およびjava.util.Calendar
型のプロパティには 時制 の指定が必須です.- バイト列型
- byte[]
※ ラージオブジェクト にすることができます。
- シリアライズ可能型
- java.io.Serializable
※ ラージオブジェクト にすることができます。
カラム定義
カラム情報を指定するには、
@Column
を使います。
name属性でカラム名を指定することができます。
指定しなかった場合、カラム名はフィールド名と同じになります。フィールド名が、
aaaBbb
のようなキャメル記法の場合、カラム名は、
AAA_BBB
のように'_'区切りだとみなされます。
このルールは、convention.diconで指定されている
org.seasar.framework.convention.impl.PersistenceNamingConventionImpl
のfromPropertyNameToColumnName()の実装を変えることで、カスタマイズすることができます。
デフォルトでは、プロパティ名とフィールド名は同じになりますが、convention.diconで指定されている
org.seasar.framework.convention.impl.PersistenceNamingConventionImpl
のfromFieldNameToPropertyName()の実装を変えることで、カスタマイズすることができます。
次の例では、AB1234というカラム名を指定しています。
@Column(name = "AB1234") public String shortName;
insertable属性で挿入可能かどうかを指定することができます。デフォルトはtrueです。 falseの場合、挿入用のSQLにこのカラムは含まれません。 カラムにデフォルト値を適用したい場合は、falseにすると良いでしょう。
updatable属性で更新可能かどうかを指定することができます。デフォルトはtrueです。 falseの場合、更新用のSQLにこのカラムは含まれません。 カラムにデフォルト値を適用したい場合は、falseにすると良いでしょう。
識別子定義
識別子(主キー)であることを指定するには、
@Id
を使います。
@Id public Integer id;
複合主キーの場合は、
@Id
を複数つけます。
@Id public Integer id; @Id public Integer id2;
識別子は、アプリケーション側で生成することもできますが、
Seasar2に自動生成させることもできます。自動生成させるには、
@GeneratedValue
を使います。
自動生成のタイプは、
@GeneratedValue
の
strategy
属性で指定します。指定できるタイプのは次の4つです。
- GenerationType.TABLE
- テーブルを使います。
- GenerationType.SEQUENCE
- シーケンスを使います。
- GenerationType.IDENTITY
- データベース固有の識別子自動生成を使います。
- GenerationType.AUTO(デフォルト)
- データベースに応じてTABLE・SEQUENCE・IDENTITYのどちらかが選択されます。 IDENTITYが使える場合はIDENTITYに、 IDENTITYが使えなくてSEQUENCEが使える場合はSEQUENCEに、 IDENTITYもSEQUENCEも使えない場合はTABLEになります。
GenerationType.TABLE
GenerationType.TABLEは次のようにして使います。
@Id @GeneratedValue(strategy = GenerationType.TABLE) public Integer id;
上記のように指定した場合、あらかじめ、次のようなテーブルとデータを用意しておく必要があります。
create table ID_GENERATOR { PK varchar(80) not null primary key, VALUE integer);
insert into ID_GENERATOR (PK, VALUE) values ('EMPLOYEE_ID', 1);
ID_GENERATORテーブルのPKカラムには、<テーブル名>_<識別子のカラム名>を設定します。
テーブル名やカラム名をカスタマイズするには、次のように@TableGeneratorアノテーションで指定します。
@Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "EMPLOYEE_GEN") @TableGenerator( name = "EMPLOYEE_GEN", table = "ID_GEN", pkColumnName = "GEN_NAME", valueColumnName = "GEN_VALUE") public Integer id;
上記のようなTableGeneratorを定義した場合、 あらかじめ、次のようなテーブルとデータを用意しておく必要があります。
create table ID_GEN { GEN_NAME varchar(80) not null primary key, GEN_VALUE integer);
insert into ID_GEN (GEN_NAME, GEN_VALUE) values ('EMPLOYEE_GEN', 1);
GenerationType.SEQUENCE
GenerationType.SEQUENCEは次のようにして使います。
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE) public Integer id;
上記のように指定した場合、あらかじめ、次のようなシーケンスを用意しておく必要があります。
create sequence EMPLOYEE_ID incremet by 50;
シーケンス名は、<テーブル名>_<識別子のカラム名>となります。
シーケンス名をカスタマイズするには、次のように@SequenceGeneratorアノテーションで指定します。
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "EMPLOYEE_GEN") @SequenceGenerator( name = "EMPLOYEE_GEN", sequenceName = "EMPLOYEE_SEQ") public Integer id;
上記のようなSequenceGeneratorを定義した場合、 あらかじめ、次のようなシーケンスを用意しておく必要があります。
create sequence EMPLOYEE_SEQ incremet by 50;
GenerationType.IDENTITY
GenerationType.IDENTITYは次のようにして使います。
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Integer id;
GenerationType.IDENTITYを使う場合、 識別子はデータベース固有の方法を使って自動生成されるようにしてください。
create table Employee { ID integer not null primary key generated always as identity, ...);
バージョン定義
バージョンチェック用であることを指定するには、
@Version
を使います。更新時に、エンティティの値とカラムの値が同一かどうかをチェックし、
同一ならカラムの値がインクリメントされて更新されます。同一でない場合、
javax.persistence.OptimisticLockException
が発生します。
例えば、エンティティのバージョンチェック用のプロパティの値が1だったとします。
更新時にカラムの値が1のままならOKで、2に更新されます。
カラムの値が1でない場合は、他で更新されているということなので、
javax.persistence.OptimisticLockException
が発生します。
@Version public Long version = 0L;
注意点
@Versionアノテーションは数値型のフィールドにのみ指定することができます。JPA仕様ではTimestamp型のフィールドもバージョンチェックに使用できることになっていますが、S2JDBCではサポートしていません。更新時刻の保持と排他制御は目的が違うので別のフィールドにしてください。
時制定義
java.util.Date
および
java.util.Calendar
型のプロパティには時制を指定します。 時制を指定するには、
@Temporal
を使います。
@Temporal
で指定可能な値は次のとおりです。
TemporalType.DATE
-
SQL標準の
DATE
型 (日付のみ) として扱うことを指定します。 TemporalType.TIME
-
SQL標準の
TIME
型 (時刻のみ) として扱うことを指定します。 TemporalType.TIMESTAMP
-
SQL標準の
TIMESTAMP
型 (日付と時刻) として扱うことを指定します。
@Temporal(TemporalType.DATE) public Date date;
関連定義
多対一関連定義
多対一関連であることを指定するには、
@ManyToOne
を使います。複数のEmployeeに1つのDepartementが関連付けられる場合、
EmployeeからみてDepartmentは多対一関連になります。
関連には、所有者、非所有者という概念があり、外部キーを持っているほうが所有者になります。
上記のケースは、Employeeのテーブルに、 department_id(プロパティ名はdepartmentId)という外部キーがあるので、 Employeeは関連の所有者になります。
@ManyToOne
を定義するエンティティは、必ず関連の所有者になるので、外部キーに対応するプロパティが必要です。
public class Employee { ... public Integer departementId; @ManyToOne public Department department; }
一対多関連定義
一対多関連であることを指定するには、
@OneToMany
を使います。1つのDepartmentに複数のEmployeeが関連付けられる場合、
DepartmentからみてEmployeeは一対多関連になります。
関連には、所有者、非所有者という概念があり、外部キーを持っているほうが所有者になります。
上記のケースは、Employeeのテーブルに、 department_id(プロパティ名はdepartmentId)という外部キーがあり、 Departmentの方は、外部キーを持っていないので、 Departmentは関連の非所有者になります。
関連の非所有者の場合、必ずmappedBy属性で逆側の関連のプロパティ名を指定します。
Departmentからみて一対多でEmployeeが関連付けられていて、
そのEmployeeからみるとDepartmentがdepartmentプロパティとして、
多対一で関連付けられています。お互いが相互の関連であることを示すために
mappedBy
でdepartmentを指定します。
一対多の関連の型は、
List<エンティティ型>
にします。
どのエンティティのリストなのかを示すためにGenericsの指定を忘れないようにしてください。
public class Department { ... @OneToMany(mappedBy = "department") public List<Employee> employeeList; }
一対一関連定義
一対一関連であることを指定するには、
@OneToOne
を使います。1つのEmployeeに1つのAddressが関連付けられる場合、
EmployeeからみてAddressは一対一関連になります。
同じように、1つのAddressに1つのEmployeeが関連付けられているので、
AddressからみてEmployeeも一対一関連になります。
関連には、所有者、非所有者という概念があり、外部キーを持っているほうが所有者になります。
上記のケースは、Employeeのテーブルに、 address_id(プロパティ名はaddressId)という外部キーがあるので、 Employeeは関連の所有者になります。Addressの方は、外部キーを持っていないので、 Addressは関連の非所有者になります。
関連の非所有者の場合、必ずmappedBy属性で逆側の関連のプロパティ名を指定します。
Addressから見ると一対一でEmployeeが関連付けられていて、
そのEmployeeからみるとAddressがaddressプロパティとして、
一対一で関連付けられています。お互いが相互の関連であることを示すために
mappedBy
でaddressを指定します。
public class Employee { ... public Integer addressId; @OneToOne public Address address; }
public class Address { ... @OneToOne(mappedBy = "address") public Employee employee; }
結合カラム定義
外部キーを持っている方を関連の所有者といいますが、関連の所有者側では、
@JoinColumn
を使って、結合用のカラムを指定することができます。
name属性で、外部キーを指定します。 name属性を省略した場合、「関連のプロパティ名_関連テーブルの主キー」が 自動的に設定されます。 主キーは、プロパティ名ではなく、カラム名なので注意してください。
public class Employee { ... public Integer departementId; @ManyToOne public Department department; }
上記の例では、
@JoinColumn
が省略されているので、
name属性は、「関連のプロパティ名(department)_関連テーブルの主キー(ID)」、
つまりDEPARTMENT_IDになります。
departmentがDEPARTMENTに変換されているのは、 プロパティ名をカラム名に変換するときに、 キャメル記法は、'_'区切りになり、'_'以外は大文字に変換されるというルールがあるからです。
このルールは、convention.diconで指定されている
org.seasar.framework.convention.impl.PersistenceNamingConventionImpl
の
fromPropertyNameToColumnName()の実装を変えることで、カスタマイズすることができます。
referencedColumnName属性で、関連テーブルの主キーを指定します。 referencedColumnName属性を省略した場合、 「関連テーブルの主キー」が自動的に設定されます。 主キーは、プロパティ名ではなく、カラム名なので注意してください。
public class Employee { ... public Integer departementId; @ManyToOne public Department department; }
上記の例では、
@JoinColumn
が省略されているので、
referencedColumnName属性は、「関連テーブルの主キー(ID)」、
つまりIDになります。