MyBatis Spring Bootの別名のBugをスキャンできません.
この問題が発生した原因は複雑で、主な条件は4つあります. Spring Bootを使用して、Spring BootのMavenプラグインを使用して を包装します.はMyBatisを使用しています.(現在の最新の は、Domainを個別のJarパケット(例えば、Maven多モジュール)に配置する .は、 を指定する.
そして、開発時にIDEAを使って直接マイコンを実行するのは正常ですが、Jarパッケージにしてjava-jarを使って起動すると、Domainの別名が無効になります.
例えば私はSpring Bootプロジェクトを持っています.この中に三つのMavenモジュールがあります.
scienjus--scienjus-domain-–comp.scienjuse.domail.User--scienjus-mapper-–comp.scienjuse.mapper.UserMapper——–UserMapper.xml—scienjuse-web——–Sql Session Factfiory
Sql Session FactoryConfigにSql Session Factoryを配置する:
これを証明するために、MyBatisのソースコードを翻訳して
問題が発生したところをロックした以上、どうやって起こったのかよく見てください.
もう少し細かくして論理を呼び出せば、ブレークポイントを準備してデバッグできます.
DebugモードをオンにしてJarパケットを実行し、特定のポートを待ち受けます.
ブレークポイント調整によって、私は
IDEAで直接運転:
この方法を呼び出した時のコメントはこうです.
このクラスが正常に動作するようになるなら、それをデフォルトのVFSに設定する必要があります.MyBatisのドキュメントには、プロファイル変更
MyBatis公式の解決策は、まずmybatis-spring-bootの
これらの無駄な思いをさせないために、この過程を発表することにしました.ソースプロジェクトを使ってバグに遭遇した時、どのように順位を決めるかを教えてください.これも本文の残りの価値かもしれません.
以上の紙面はMyBatis Spring Bootの別名のBugをスキャンできません.
自分で使うのはmybatis-spring-boot-starterの1.2.0バージョンで、mybatisバージョンは3.4.2です.
そして、次のようにSession Factoryを宣言しました.
後の話
mybatis atoconfigureで生成されたSession Factoryを使えばこの問題はないかもしれません.具体的には、Mybatis AutoConfigratin.javaを見ることができます.
3.3.1
バージョンはまだこの問題があります.)SqlSessionFactoryBean.setTypeAliasesPackage
を使用して、パケットスキャンDomain そして、開発時にIDEAを使って直接マイコンを実行するのは正常ですが、Jarパッケージにしてjava-jarを使って起動すると、Domainの別名が無効になります.
例えば私はSpring Bootプロジェクトを持っています.この中に三つのMavenモジュールがあります.
scienjus--scienjus-domain-–comp.scienjuse.domail.User--scienjus-mapper-–comp.scienjuse.mapper.UserMapper——–UserMapper.xml—scienjuse-web——–Sql Session Factfiory
Sql Session FactoryConfigにSql Session Factoryを配置する:
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource());
//
sqlSessionFactory.setTypeAliasesPackage("com.scienjus.domain");
return sqlSessionFactory.getObject();
}
UserMapper.xmlに別名を使用します.<select id="get" resultType="User">
select * from user u where id = #{id}
select>
開発時にIDEAを使って起動すると正常に動作しますが、実行時にコマンドラインで起動すると以下のエラーメッセージが発生します.org.apache.ibatis.builder.BuilderException: Error resolving class. Cause:
org.apache.ibatis.type.TypeException: Could not resolve type alias 'User'. Cause:
java.lang.ClassNotFoundException: Cannot find class: User
このエラーの大体の意味はMapperを生成する時にエラーが発生しました.原因はUser
という別名を識別できないし、User
というクラスも見つけられないからです.以前に配されたパケットスキャンは、com.scienjus.domain.User
というクラスに全くスキャンされていないことが分かります.これを証明するために、MyBatisのソースコードを翻訳して
org.apache.ibatis.type.TypeAliasRegistry
のregisterAliases(String packageName, Class superType)
方法でMyBatisがどのようにパッケージ名を通して別名類をスキャンしているかを発見しました.この部分の論理を直接main
方法で実行してみます.public static void main(String[] args) {
ResolverUtil resolverUtil = new ResolverUtil();
resolverUtil.find(new IsA(Object.class), "com.scienjus.domain");
Set typeSet = resolverUtil.getClasses();
Iterator i$ = typeSet.iterator();
while(i$.hasNext()) {
Class type = (Class)i$.next();
if(!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
System.out.println(type.getName());
}
}
}
それぞれIDEAとJarで実行すると、前者はcom.scienjus.domain.User
をプリントしているが、後者は出力結果がないので、問題はここにあると説明している.問題が発生したところをロックした以上、どうやって起こったのかよく見てください.
ResolverUtil.find
方法を参照すると、VFS.getInstance().list(path)
方法でクラスファイルを取得し、VFS.getInstance()
がデフォルトで返したのはDefaultVFS
です.つまり、このクラスのlist
方法では、Spring Boot
がJarパケットに依存するクラスをスキャンできないからです.もう少し細かくして論理を呼び出せば、ブレークポイントを準備してデバッグできます.
public static void main(String[] args) throws IOException {
DefaultVFS defaultVFS = new DefaultVFS();
List children = defaultVFS.list("com/scienjus/domain");
for (String child : children) {
System.out.println(child);
}
}
ブレークポイント調整はjava -jar
で起動したプログラムを使っても、思ったより難しくないです.IDEAとEclipseは非常に優秀なデバッグツールを内蔵しています.IDEAの使い方を紹介します.DebugモードをオンにしてJarパケットを実行し、特定のポートを待ち受けます.
java -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=y -jar scienjus-web.jar
IDEA端はRun->Edit ConfigrationsでRemoteアプリケーションを作成し、IPと傍受のポート番号を記入して起動すればいいです.ブレークポイント調整によって、私は
findJarForResource
で面白いコードを発見しました.// If the file part of the URL is itself a URL, then that URL probably points to the JAR
try {
for (;;) {
url = new URL(url.getFile());
if (log.isDebugEnabled()) {
log.debug("Inner URL: " + url);
}
}
} catch (MalformedURLException e) {
// This will happen at some point and serves as a break in the loop
}
これは死の循環であり、MalformedURLException
の異常を投げ出した時だけがサイクルから飛び出すことができます.上記の注釈によれば、このことは必然的に発生し、url
を所望の結果に向けることが分かります.2つの方式の運行時のurl
の最後の結果を比較します.IDEAで直接運転:
scienjus-domain/target/classes/com/scienjus/domain
コマンドライン運転:scienjus-web/target/scienjus-web.jar!/lib/scienjus-domain.jar!/com/scienjus/domain
その後、変数jarUrl
の値はscienjus-web/target/scienjus-web.jar!/lib/scienjus-domain.jar
に割り当てられるが、最後のlistResources
方法はnull
に戻る.この方法を呼び出した時のコメントはこうです.
// First, try to find the URL of a JAR file containing the requested resource. If a JAR
// file is found, then we'll list child resources by reading the JAR.
つまり、スキャンされたファイルがJarパッケージの中にある場合、この方法はこのJarパッケージのURLに戻るべきであり、乱暴な改善を試みる.public static void main(String[] args) throws IOException {
DefaultVFS defaultVFS = new DefaultVFS() {
@Override
protected URL findJarForResource(URL url) throws MalformedURLException {
String urlStr = url.toString();
if (urlStr.contains("jar!")) {
return new URL(urlStr.substring(0, urlStr.lastIndexOf("jar") + "jar".length()));
}
return super.findJarForResource(url);
}
};
List children = defaultVFS.list("com/scienjus/domain");
for (String child : children) {
System.out.println(child);
}
}
このURLにjar!
の識別情報が含まれている場合、このJarパケットのアドレスに直接戻ります.私はこのようにして隠れている危険があるかどうかはよく分かりませんが、Domainの別名をスキャンする時だけこの類を使います.そしてこの時は正常に働きます.このクラスが正常に動作するようになるなら、それをデフォルトのVFSに設定する必要があります.MyBatisのドキュメントには、プロファイル変更
vfsImpl
によってVFSを変更することができるクラスが書いてありますが、ここではこの構成では効果がありません.なぜならば、Springの構成はMyBatisプロファイルの前に実行されるので、この構成を読み込む前にVFS.getInstall()
が既に実用化されています.そしてMyBatisにIssueを提出しました.ついでに、このスキャンができない種類のBugは去年の10月に提出されました.解決方法もとっくにありました.MyBatis公式の解決策は、まずmybatis-spring-bootの
3.4.1
バージョンを推奨しており、デフォルトではSpring Bootに対応するVFS実装クラスがすでに構成されています.またはこの実装クラスをあなたのプロジェクトに追加し、手動で設定します.これらの無駄な思いをさせないために、この過程を発表することにしました.ソースプロジェクトを使ってバグに遭遇した時、どのように順位を決めるかを教えてください.これも本文の残りの価値かもしれません.
以上の紙面はMyBatis Spring Bootの別名のBugをスキャンできません.
自分で使うのはmybatis-spring-boot-starterの1.2.0バージョンで、mybatisバージョンは3.4.2です.
そして、次のようにSession Factoryを宣言しました.
@Configuration
@AutoConfigureAfter(DataSourceConfig.class)
@MapperScan(basePackages = {"org.rainbow.persistence.mysql"}, sqlSessionFactoryRef = "mysqlSqlSessionFactory")
public class MyBatisConfigForMysql {
@Bean("mysqlSqlSessionFactory")
@Autowired
public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
// IMPORTANT: we MUST set the 'VFS',
// otherwise if you run this project as a 'executable jar', then mybatis will throw an exception saying that it can not find java POJO
sqlSessionFactory.setVfs(SpringBootVFS.class);
sqlSessionFactory.setTypeAliasesPackage("org.rainbow.model.mysql");
return sqlSessionFactory.getObject();
}
}
上記のような実装クラスに従って、コードの行を追加したことが分かる.sqlSessionFactory.setVfs(SpringBootVFS.class);
これで問題も解決できます.後の話
mybatis atoconfigureで生成されたSession Factoryを使えばこの問題はないかもしれません.具体的には、Mybatis AutoConfigratin.javaを見ることができます.