目的
MyBatisは通常XMLで各種定義を行いますが、XMLを使わないことも可能です。
手順
SqlSessionFactoryの取得
CDI+JNDIの場合
Java EEサーバ上で動かす場合はJNDIルックアップと組み合わせます。JNDI定義は例えば次の記事が参考になります。
@Resource(lookup = "jdbc/********") // JNDI名
private DataSource dataSource;
@Produces
public SqlSessionFactory produceSQLSessionFactory() {
var config = new Configuration();
config.addMappers("package.to.mappers"); // Mapperクラスが存在するパッケージ
config.setEnvironment(new Environment("development", new JdbcTransactionFactory(), dataSource));
config.setLocalCacheScope(LocalCacheScope.STATEMENT); // キャッシュなし
config.setMapUnderscoreToCamelCase(true); // SNAKEをCAMELに
var sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
return sqlSessionFactory;
}
手動で生成する場合
JUnitやスタンドアロンアプリでCDI+JNDIが使えない場合。ただしPGPoolingDataSourceはdeprecatedなので、正式に使う場合はHikari CPなどを挟むとよいでしょう。
public static SqlSessionFactory getSqlSessionFactory() throws Exception {
//(注)PGPoolingDataSourceの使用は現在推奨されていない
var dataSource = new PGPoolingDataSource();
dataSource.setURL("jdbc:postgresql://localhost:5432/********");
dataSource.setUser("postgres");
dataSource.setPassword("********");
dataSource.setProperty("options", "-c lock_timeout=10000");
var config = new Configuration();
config.addMappers("package.to.mappers"); // Mapperクラスが存在するパッケージ
config.setEnvironment(new Environment("development", new JdbcTransactionFactory(), (DataSource) dataSource));
config.setLocalCacheScope(LocalCacheScope.STATEMENT); // キャッシュなし
config.setMapUnderscoreToCamelCase(true); // SNAKEをCAMELに
return new SqlSessionFactoryBuilder().build(config);
}
Mapperクラスの作成
好みの分かれるところですが、SQLはアノテーションで指定できます。Spring Dataを真似して次のようなリポジトリクラス(マッパー)を用意します。このクラスは先ほどaddMappers()で指定したパッケージに配置します。
package package.to.mappers;
import java.util.Optional;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.cursor.Cursor;
public interface SampleRepository {
@Select("SELECT id,xxx,yyy_at FROM samples")
public Cursor<Sample> findAll();
@Select("SELECT id,xxx,yyy FROM users WHERE id=#{id}")
public Optional<Sample> findById(int id);
@Insert("INSERT INTO samples (xxx,yyy) VALUES (#{xxx},#{yyy})")
public void create(Sample sample);
@Update("UPDATE samples SET xxx=#{xxx},yyy=#{yyy} WHERE id=#{id}")
public void update(Sample sample);
@Delete("DELETE FROM samples WHERE ID=#{id}")
public void delete(User user);
@Delete("DELETE FROM samples WHERE ID=#{id}")
public void deleteById(int id);
}
対応するエンティティクラスも用意しておきます。
import lombok.Data;
@Data
public class Sample {
private int id;
private String xxx;
private String yyy;
}
使い方
次のようにリポジトリ(マッパー)クラスを取得し、エンティティの出し入れをします。
@Inject
private SqlSessionFactory sqlSessionFactory;
public void createSample() {
try (var session = sqlSessionFactory.openSession()) {
var sampleRepository = session.getMapper(SampleRepository.class);
var sample = new Sample();
sample.setXxx("aaa");
sample.setYyy("bbb");
sampleRepository.create(sample);
session.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
メリット
- どこでどのSQLを使っているか一目瞭然
- XMLファイルに悩まされなくてよい
- チーム開発で競合しにくい
デメリット
- アノテーションは改行できないので、SQLが長くなると読みづらくなる
- 素直にSpring Data使えばという気になってくる