mybatis の一般的なSQL Mapper の指定は、Mapper インターフェースクラスのパッケージと
同じ階層に、SQLMap XML を配置するか、Configuration のXMLで、
<mappers> <mapper resource="sql/sqlmap.xml"/> </mappers>
で書くであろう。
Configuration のXMLを書かずに、さらに、SQLMap XML の PATH をMapper インターフェースクラス
のパッケージと一致させない、XMLファイル名もインターフェースクラス名と全く異なる。
というスタンダードではない設計の時、どうするか?
先日の、mybatis config のXMLを書かない - Oboe吹きプログラマの黙示録
では、対応できていない。
Mapperを記述したXML を
org.apache.ibatis.builder.xml.XMLMapperBuilder
のインスタンスを生成して parse() をメソッドを実行すれば、
インターフェース Mapper のパッケージ階層に置かず、まったく異なるXMLファイル名にしても
Configuration に、Mapper を配置してくれる。
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments)
↑↑↑
が、XMLMapperBuilder のコンストラクタ
new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()).parse();
inputStream に、対象の SQLMap XMLファイルの入力ストリームを指定
configuration は、Datasource から、Environment を作って、Configuration 生成したもの
resource は、あたかも、SQLMap XMLファイルリソースの指定のように見えてもここが罠で、
SQLMap XMLで記述する namespace である。つまり マッパーインターフェースクラス名になる。
を実行して、SqlSessionFactoryBuilder の build メソッドで、configuration を指定して SqlSessionFactory を生成する。
SQLMap XMLを書かないものと、 XMLをルールどおりのパッケージ階層に置かない場合、
両方に対応するものを書くと。。。
key=namesapce 、value=マッパーXML のパス(リソースローダーで読み込むための相対パス)
のマップと、マッパークラスの可変引数で、以下のようなメソッドが用意できる。
public static SqlSession getSqlSession(Map<String, String> mappers, Class<?>... mapperClasses){ UnpooledDataSource dataSource = GenericBuilder.of(UnpooledDataSource::new) .with(UnpooledDataSource::setDriver, "com.mysql.cj.jdbc.Driver") .with(UnpooledDataSource::setUrl, "jdbc:mysql://localhost:3306/testDB?serverTimezone=JST") .with(UnpooledDataSource::setUsername, "root") .with(UnpooledDataSource::setPassword, "pass") .build(); 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); } mappers.entrySet().stream().forEach(e->{ try(InputStream input = ClassLoader.getSystemResourceAsStream(e.getValue())){ new XMLMapperBuilder(input, config, e.getKey(), config.getSqlFragments()).parse(); }catch(Exception ex){ throw new IllegalArgumentException(ex.getMessage(), ex); }finally{ } }); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config); return factory.openSession(); }
使用例。。。
SampleMapper.class のパッケージ階層に置かない SampleMapper の SQL Map XMLである
"sql/sample.xml" を指定、
SQLMap XML を持たない FooMapper.class を指定
というケースで、、、
Map<String, String> mappers = new HashMap<>(); mappers.put(SampleMapper.class.getName(), "sql/sample.xml"); try(SqlSession session = getSqlSession(mappers, FooMapper.class)){ // TODO }catch(Exception ex){ // TODO }finally{ }