過去、、いろいろ試行錯誤を書いていた。。
mybatis XML設定を使わない場合 - Oboe吹きプログラマの黙示録
mybatis xml設定ファイル使わない場合の補足 - Oboe吹きプログラマの黙示録
【再興】mybatis XML設定を使わない場合 - Oboe吹きプログラマの黙示録
mybatis config のXMLを書かない - Oboe吹きプログラマの黙示録
XMLMapperBuilder を使ってみる。 - Oboe吹きプログラマの黙示録
改めて、config XMLを記述しない、spring - mybatis に依存しない、
SQL Map XML を書かなかったり、書いた場合でも、Mapper インターフェースクラスのパッケージ階層に
XMLを置いたり、別のリソース クラスパスで読める場所に置いたりする場合にも
対応できるものを作りたかった。ようやく、そういう曖昧さに対応する形を作った。
先日の「XMLMapperBuilder を使ってみる。」 が、かなりヒントになった。
ちゃんと Mapper の XMLをディレクトリの深さに関係なく探すようにするのである。
(XMLを探す分のパフォーマンスが悪くなるのは覚悟の上である。。。Springが充分に起動が遅いではないか。。)
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import Objects.requireNonNull; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import javax.sql.DataSource; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * SqlWorker */ public class SqlWorker{ private SqlSessionFactory factory; public SqlWorker(DataSource dataSource, String path, Class<?>... mapperClasses) { Objects.requireNonNull(path); Environment environment = new Environment("deployment", new JdbcTransactionFactory(), dataSource); Configuration config = new Configuration(environment); // snake Case → camel Case config.setMapUnderscoreToCamelCase(true); for(Class<?> cls: mapperClasses){ config.addMapper(cls); } List<File> list = getMapperCanditateFiles(new File(Thread.currentThread().getContextClassLoader().getResource("./"+path).getPath())); list.stream().forEach(f->{ try(InputStream input = new FileInputStream(f)){ new XMLMapperBuilder(input, config, f.getAbsolutePath(), config.getSqlFragments()).parse(); }catch(Exception e){ } }); factory = new SqlSessionFactoryBuilder().build(config); } private List<File> getMapperCanditateFiles(File file) { List<File> list = new ArrayList<>(); for(File f:file.listFiles()) { if (f.isDirectory()){ searchMapFiles(f, list); }else if(f.getAbsolutePath().endsWith(".xml")){ list.add(f); } } return list; } private void searchMapFiles(File file, List<File> list){ for(File f:file.listFiles()) { if (f.isDirectory()){ searchMapFiles(f, list); }else if(f.getAbsolutePath().endsWith(".xml")){ list.add(f); } } } public SqlSession getSqlSession(){ return factory.openSession(); } public void execution(Consumer<SqlSession> consumer) { SqlSession session = factory.openSession(); try(session){ consumer.accept(session); session.commit(); }catch(Exception e){ Logger logger = LoggerFactory.getLogger(getClass()); logger.error(e.getMessage(), e); session.rollback(); throw new RuntimeException(e); } } public void execution(Consumer<SqlSession> consumer, BiConsumer<SqlSession, Throwable> error) { SqlSession session = factory.openSession(); try(session){ consumer.accept(session); session.commit(); }catch(Exception e){ Logger logger = LoggerFactory.getLogger(getClass()); logger.error(e.getMessage(), e); session.rollback(); error.accept(session, e); throw new RuntimeException(e); } } public <R> R getObject(Function<SqlSession, R> function) { try(SqlSession session = factory.openSession()){ return function.apply(session); }catch(Exception e){ Logger logger = LoggerFactory.getLogger(getClass()); logger.error(e.getMessage(), e); throw new RuntimeException(e); } } }
Mapper クラスを SqlWorker のコンストラクタで指定しなくても、resources として
読み込める場所に SQL Map XMLを配置すれば、自動的に読んでくれる。
XMLがなくて Mapper インターフェースクラスだけなら、コンストラクタで指定すれば良い。
コンストラクタで指定する Mapperクラスを書くこと。
resources に置くXMLの場所を問わない。ということである。
DataSource をコンストラクタで渡すようにしている。
開発プロジェクトによって策定される様々なルールで、spring-mybatis で書くあの面倒くさい設定に
つき合わされるのが厭だった。のである。
コンストラクタで指定するDataSource は、
プールを使わないなら、
UnpooledDataSource dataSource = new UnpooledDataSource(); dataSource.setDriver("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/testDB"); dataSource.setUsername("root"); dataSource.setPassword("password"); // sql/ 配下にSQLMap xml がある。 "/" を指定すればツリー全て探してくれる。 SqlWorker worker = new SqlWorker(dataSource, "sql/", SampleMapper.class, ItemMapper.class); try(SqlSession session = worker.getSqlSession()){ // TODO }
プールを使うなら、( org.apache.ibatis.datasource.pooled.PooledDataSource )
PooledDataSource dataSource = new PooledDataSource(); dataSource.setDriver("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/testDB"); dataSource.setUsername("root"); dataSource.setPassword("password"); dataSource.setPoolMaximumActiveConnections(10); dataSource.setPoolMaximumCheckoutTime(10000); dataSource.setPoolMaximumIdleConnections(4); dataSource.setPoolMaximumLocalBadConnectionTolerance(4); dataSource.setPoolPingConnectionsNotUsedFor(4); dataSource.setPoolPingEnabled(true); dataSource.setPoolPingQuery("SELECT 1"); dataSource.setPoolTimeToWait(10000); SqlWorker worker = new SqlWorker(dataSource, "sql/", SampleMapper.class, ItemMapper.class); try(SqlSession session = worker.getSqlSession()){ // TODO }
Spring-Boot と合わせて良く使われるプール、HikariCPを使うなら、、
com.zaxxer:HikariCP 5.0.1
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency>
HikariDataSource dataSource = new HikariDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testDB?serverTimezone=JST"); dataSource.setUsername("root"); dataSource.setPassword("password"); dataSource.setAutoCommit(false); dataSource.setMaximumPoolSize(10); dataSource.setMinimumIdle(4); dataSource.setConnectionTestQuery("SELECT 1"); SqlWorker worker = new SqlWorker(dataSource, "sql/", SampleMapper.class, ItemMapper.class); try(SqlSession session = worker.getSqlSession()){ // TODO }