プロジェクトの構成
チュートリアルのプロジェクトは以下のフォルダで構成されています。
フォルダ | 説明 |
---|---|
src | 設定クラス、Daoインタフェース、エンティティクラス、SQLファイル等のソースフォルダです。 SQLファイルは、Domaの規約に則ってMETA-INFフォルダ以下に配置されます。 |
test | Daoを利用するコードのためのソースフォルダです。 Daoを利用するコードはすべてJUnitのテストクラスとして記述されています。 |
.apt_generated | apt(Annotation Processing Tool)によって生成されたコードを格納するソースフォルダです。 Pckage Explorerビューでは、フィルタリングされるため表示されません(フィルタリングは解除可能です)。 Nivigatorビューで確認できます。 |
lib | このチュートリアルで利用するjarファイルを格納するフォルダです。 JUnit、HSQLDB、Domaのjarが格納されます。 これらのjarはすべてビルドパスに通っていなければいけません。 |
libsrc | このチュートリアルで利用するjarファイルのソースコードを格納するフォルダです。 |
データベース
このチュートリアルでは、データベースにHSQLDBを利用します。
使用するスキーマの定義は次のとおりです。 1つのシーケンスと1つのテーブルを使用します。
create sequence employee_seq as integer start with 100 increment by 1;
create table employee ( id integer not null primary key, name varchar(255) not null, age integer not null, salary integer, job_type varchar(20), hiredate timestamp, department_id integer, version integer not null, insertTimestamp timestamp, updateTimestamp timestamp );
HSQLDBはMemory-Onlyモードで起動し、テスト用のスキーマとデータは各テストの実行前に作成し、テスト終了後に破棄します。 スキーマとデータの作成と破棄は、共通のテストクラスで行っています。 Javaのコードは、test/tutorial/TutorialTestCase.javaになります。
主要なクラス
チュートリアル中の主要なクラスについて説明します。
設定クラス
設定クラスとは、Domaに関する各種設定を持つクラスです。 このチュートリアルで使用する設定クラスのソースコードは、src/tutorial/AppConfig.javaになります。
このクラスでは、HSQLDBのデータソースの作成とHSQLDBの方言の設定を行っています。
それ以外の設定については親クラスのDomaAbstractConfig
のものを利用しています。
設定クラスはDaoインタフェースから利用されます。
package tutorial; import javax.sql.DataSource; import org.seasar.doma.jdbc.DomaAbstractConfig; import org.seasar.doma.jdbc.SimpleDataSource; import org.seasar.doma.jdbc.dialect.Dialect; import org.seasar.doma.jdbc.dialect.HsqldbDialect; public class AppConfig extends DomaAbstractConfig { protected static final DataSource dataSource = createDataSource(); protected static final Dialect dialect = new HsqldbDialect(); @Override public DataSource getDataSource() { return dataSource; } @Override public Dialect getDialect() { return dialect; } protected static DataSource createDataSource() { SimpleDataSource dataSource = new SimpleDataSource(); dataSource.setUrl("jdbc:hsqldb:mem:tutorial"); dataSource.setUser("sa"); return dataSource; } }
エンティティクラス
エンティティクラスとは、テーブルやSQLの結果セットにマッピングされたクラスです。 エンティティクラスは、@Entityを注釈して示します。 このチュートリアルで使用するエンティティクラスのソースコードは、src/tutorial/entity/Employee.javaになります。
Employeeクラスは、EMPLOYEEテーブルに対応します。
識別子(テーブルの主キー)には@Idを注釈します。 識別子を自動生成する場合は、さらに@GeneratedValueを注釈し、自動生成の方法を示します。 ここではEMPLOYEE_SEQシーケンスを使って採番することを示しています。
プロパティ名がマッピングするカラム名と異なる場合は@Columnのname要素を使って示します。
更新や削除時の楽観的排他制御に使用するカラムとマッピングされたプロパティには@Versionを注釈します。
@Entityのlistener要素にはEmployeeListenerクラスを指定しています。
package tutorial.entity; import java.io.Serializable; import java.sql.Date; import java.sql.Timestamp; import org.seasar.doma.Column; import org.seasar.doma.Entity; import org.seasar.doma.GeneratedValue; import org.seasar.doma.GenerationType; import org.seasar.doma.Id; import org.seasar.doma.OriginalStates; import org.seasar.doma.SequenceGenerator; import org.seasar.doma.Version; import tutorial.domain.Salary; @Entity(listener = EmployeeListener.class) public class Employee { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) @SequenceGenerator(sequence = "EMPLOYEE_SEQ") Integer id; String name; int age; Salary salary; @Column(name = "JOB_TYPE") JobType jobType; Date hiredate; @Column(name = "DEPARTMENT_ID") Integer departmentId; @Version @Column(name = "VERSION") Integer version; Timestamp insertTimestamp; Timestamp updateTimestamp; @OriginalStates Serializable originalStates; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Salary getSalary() { return salary; } public void setSalary(Salary salary) { this.salary = salary; } public JobType getJobType() { return jobType; } public void setJobType(JobType jobType) { this.jobType = jobType; } public Integer getVersion() { return version; } public void setVersion(Integer version) { this.version = version; } @Override public String toString() { return "Employee [departmentId=" + departmentId + ", hiredate=" + hiredate + ", id=" + id + ", jobType=" + jobType + ", name=" + name + ", originalStates=" + originalStates + ", salary=" + salary + ", version=" + version + ", insertTimestamp=" + insertTimestamp + ", updateTimestamp=" + updateTimestamp + "]"; } }
エンティティリスナー
エンティティリスナークラスのインスタンスは、エンティティがデータベースに挿入、更新、削除されるまえに呼び出されます。 このチュートリアルで使用するエンティティリスナークラスはEmployeeListenerです。 ソースコードは、src/tutorial/entity/EmployeeListener.javaになります。
package tutorial.entity; import java.sql.Timestamp; import org.seasar.doma.jdbc.entity.EntityListener; public class EmployeeListener implements EntityListener<Employee> { @Override public void preDelete(Employee employee) { } @Override public void preInsert(Employee employee) { employee.insertTimestamp = new Timestamp(System.currentTimeMillis()); } @Override public void preUpdate(Employee employee) { employee.updateTimestamp = new Timestamp(System.currentTimeMillis()); } }
ドメインクラス
ドメインクラスは、データベースのカラムにマッピング可能なアプリケーション固有の値型です。 ドメインクラスは@Domainを注釈して示します。 このチュートリアルで使用するドメインクラスは、給料を表すSalaryクラスです。 ソースコードは、src/tutorial/domain/Salary.javaになります。
package tutorial.domain; import org.seasar.doma.Domain; @Domain(valueType = Integer.class) public class Salary { private final Integer value; public Salary(Integer value) { this.value = value; } public Integer getValue() { return value; } public Salary add(Salary salary) { if (salary == null) { throw new NullPointerException("The salary parameter is null."); } if (this.value == null || salary.value == null) { return new Salary(null); } return new Salary(this.value + salary.value); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((value == null) ? 0 : value.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Salary other = (Salary) obj; if (value == null) { if (other.value != null) { return false; } } else if (!value.equals(other.value)) { return false; } return true; } @Override public String toString() { return value != null ? String.valueOf(value) : null; } }
Daoインタフェース
Daoインタフェースとは、データベースアクセスの境界となるインタフェースです。 Daoインタフェースは@Daoを注釈して示します。 @Daoのconfig要素には設定クラスであるAppConfigクラスを指定します。 このチュートリアルで使用するDaoインタフェースのソースコードは、src/tutorial/dao/EmployeeDao.javaになります。
Daoインタフェースの実装クラスはDomaにより自動生成されます。
すべてのメソッドは、@Selectや@Updateなど問い合わせの種別を示すアノテーションが注釈されなければいけません。
package tutorial.dao; import java.sql.Timestamp; import java.util.List; import org.seasar.doma.BatchDelete; import org.seasar.doma.BatchInsert; import org.seasar.doma.BatchUpdate; import org.seasar.doma.Dao; import org.seasar.doma.Delegate; import org.seasar.doma.Delete; import org.seasar.doma.Insert; import org.seasar.doma.Select; import org.seasar.doma.Update; import org.seasar.doma.jdbc.IterationCallback; import org.seasar.doma.jdbc.SelectOptions; import tutorial.AppConfig; import tutorial.domain.Salary; import tutorial.entity.Employee; @Dao(config = AppConfig.class) public interface EmployeeDao { @Select Employee selectById(Integer id); @Select List<Employee> selectByAgeRange(Integer min, Integer max); @Select List<Employee> selectByAges(List<Integer> ages); @Select List<Employee> selectByNames(List<String> names); @Select List<Employee> selectByNameWithPrefixMatching(String prefix); @Select List<Employee> selectByNameWithSuffixMatching(String suffix); @Select List<Employee> selectByNameWithInsideMatching(String inside); @Select List<Employee> selectByHiredateRange(Timestamp from, Timestamp to); @Select List<Employee> selectBySalary(Salary salary); @Select Salary selectSummedSalary(); @Select List<Employee> selectByExample(Employee e); @Select List<Employee> selectAll(); @Select List<Employee> selectAll(SelectOptions options); @Select(iterate = true) <R> R selectByAge(int age, IterationCallback<R, Employee> callback); @Delegate(to = EmployeeDaoDelegate.class) int count(); @Insert int insert(Employee employee); @Update int update(Employee employee); @Delete int delete(Employee employee); @BatchInsert int[] batchInsert(List<Employee> employees); @BatchUpdate int[] batchUpdate(List<Employee> employees); @BatchDelete int[] batchDelete(List<Employee> employees); }