よくあるご質問
本文書は、MyBatis-Plus の使用中に発生する様々なよくある問題をまとめたものです。MyBatis-Plus の使用中に問題が発生した場合は、まずこの文書をご確認ください。
非テーブルカラムの除外方法
以下の3つの方法からいずれか1つを選択してください:
-
transient
修飾子を使用するprivate transient String noColumn; -
static
修飾子を使用するprivate static String noColumn; -
TableField
アノテーションを使用する@TableField(exist=false)private String noColumn;
エンティティの親クラス属性を除外する方法
除外したい親クラスの属性に transient
修飾子を使用します
/** * 親クラスの createTime フィールドのマッピングを無視 */private transient String createTime;
Invalid bound statement (not found)
例外が発生する
この例外は通常、設定が正しくないか、Mapperが適切にスキャンされていないことが原因で発生します。解決策は以下の通りです:
-
jarパッケージの競合がないか確認してください。
-
Mapper.javaのスキャンパスを確認してください:
-
方法1:
Configuration
クラスに注釈@MapperScan
を使用する@Configuration@MapperScan("com.yourpackage.*.mapper")public class YourConfigClass{...} -
方法2:
Configuration
クラスでMapperScannerConfigurer
を設定する(サンプルを確認)@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();// 環境変数からmapperのパスを取得できます。これにより、mapperのスキャンは設定ファイルで設定可能になりますscannerConfigurer.setBasePackage("com.yourpackage.*.mapper");return scannerConfigurer;}
-
-
主キーが指定されているか確認してください。指定されていない場合、
selectById
関連のID操作ができなくなります。注釈@TableId
を使用してテーブルID主キーを注釈してください。もちろん、@TableId
注釈は省略可能ですが、主キーはid(大文字小文字を区別しない)という名前にする必要があります。 -
ネイティブのSqlSessionFactoryを使用せず、MybatisSqlSessionFactoryを使用してください。
-
SqlInjectorをカスタマイズしたかどうか、
getMethodList()
メソッドをオーバーライドしたかどうか確認してください。このメソッドで必要なメソッドが注入されているかどうか確認してください(DefaultSqlInjectorを参考にしてください)。 -
IDEAのデフォルトのビルド手順により、mapperファイルが対応するresourcesフォルダに正常にコンパイルされない場合があります。ビルド後、関連するリソースフォルダに対応するXMLファイルがあるか確認してください。ない場合は、IDEAのビルド設定を調整してください。MavenまたはGradleのビルドに調整することを推奨します。
カスタム SQL が実行できない
問題説明: XML 内でカスタム SQL を定義しているにもかかわらず、呼び出せない状況を指します。この機能は MyBatis
と同様に、XML スキャンパスの設定が必要です:
- Spring MVC 設定(mybatisplus-spring-mvc を参照)
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="xxx.entity" /> <property name="mapperLocations" value="classpath*:/mybatis/*/*.xml"/> ...</bean>
- Spring Boot 設定(mybatisplus-spring-boot を参照)
mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml
-
IDEA
シリーズのエディターでは、XML ファイルを java フォルダー内に配置することはできません。IDEA はデフォルトでソースフォルダー内の XML ファイルをコンパイルしません。以下の方法で解決できます:- 設定ファイルを resource フォルダーに配置する
- Maven プロジェクトの場合、POM ファイルで resource を指定する
<build><resources><resource><!-- xmlをjavaディレクトリに配置する場合--><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><!-- リソースの位置を指定(xmlをresourcesに配置する場合は、指定不要)--><resource><directory>src/main/resources</directory></resource></resources></build>
起動時例外のトラブルシューティング
-
例外1:
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.ClassMapperScan では、com.baomidou.mybatisplus.mapper.BaseMapper クラスおよびそのサブクラス(カスタム共通 Mapper)を除外する必要があります。例:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface SuperMapper<T> extends BaseMapper<T> {// カスタムメソッド} -
例外2:
Injection of autowired原因:低バージョンではジェネリックスの注入をサポートしていません。Spring のバージョンを 4 以上にアップグレードしてください。
-
例外3:
java.lang.NoSuchMethodError: org.apache.ibatis.session.Configuration.getDefaultScriptingLanguageInstance() Lorg/apache/ibatis/scripting/LanguageDriverバージョン導入の問題:このメソッドは 3.4.1 バージョンには存在せず、3.4.2 バージョンで導入されました!
Long 型主キーの自動入力が有効にならない問題について
Long
ではなくlong
を使用していないか確認してください!
生成される主キーが長すぎることによる JS の精度損失
JavaScript は Java の長整数型 Long を処理できず、精度損失が発生します。具体的な現象として、主キーの最後の2桁が常に 0 になります。解決策:Long を String に変換して返す。
-
FastJson での処理方法
@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();FastJsonConfig fjc = new FastJsonConfig();// シリアライゼーション戦略を設定fjc.setSerializerFeatures(SerializerFeature.BrowserCompatible);fastJsonConverter.setFastJsonConfig(fjc);converters.add(fastJsonConverter);} -
JackJson での処理方法
-
方法 1
// アノテーションによる処理、ここでは共通の baseEntity 処理を設定可能@JsonSerialize(using=ToStringSerializer.class)public long getId() {return id;} -
方法 2
// グローバルなシリアライゼーション設定による JSON 返却処理final ObjectMapper objectMapper = new ObjectMapper();SimpleModule simpleModule = new SimpleModule();simpleModule.addSerializer(Long.class, ToStringSerializer.instance);objectMapper.registerModule(simpleModule);
-
-
比較的一般的な処理方法:
public String getIdStr()
メソッドを追加し、フロントエンドでidStr
を取得する
挿入または更新するフィールドに空文字列または null
がある場合
FieldStrategy には 3 つの戦略があります:
- IGNORED:無視する
- NOT_NULL:NULL でない(デフォルト戦略)
- NOT_EMPTY:空でない
ユーザーがフィールドを空文字列または null
に更新する必要がある場合、FieldStrategy
の戦略を調整する必要があります:
-
方法 1:グローバルな検証戦略を調整する
GlobalConfiguration 属性 fieldStrategy を注入して設定します
-
方法 2:フィールド検証アノテーションを調整する
具体的な状況に応じて、更新が必要なフィールドで検証アノテーションを調整します。例えば、空でないことを検証する場合:
@TableField(strategy=FieldStrategy.NOT_EMPTY) -
方法 3:
UpdateWrapper
を使用する (3.x)以下の方法を使用して更新または挿入操作を実行します:
//updateAllColumnById(entity) // 全フィールド更新: 3.0 で削除されましたmapper.update(new User().setName("mp").setAge(3),Wrappers.<User>lambdaUpdate().set(User::getEmail, null) //email を null に設定.eq(User::getId, 2));//以下の書き方も参考にできますmapper.update(null,Wrappers.<User>lambdaUpdate().set(User::getAge, 3).set(User::getName, "mp").set(User::getEmail, null) //email を null に設定.eq(User::getId, 2));
フィールドタイプが bit
、tinyint(1)
の場合の boolean
型へのマッピング
デフォルトでは、MySQL ドライバは tinyint(1)
フィールドを boolean 型にマッピングします: 0=false, 0以外=true
MyBatis はこのマッピングを自動的に処理しません。tinyint(1)
を boolean 型にマップしたくない場合は、以下のいずれかの方法を採用してください:
- タイプを
tinyint(1)
からtinyint(2)
またはint
に変更する - リクエスト接続文字列にパラメータ
tinyInt1isBit=false
を追加する。例:
jdbc:mysql://127.0.0.1:3306/mp?tinyInt1isBit=false
2 つの limit
文が出現する
原因:2 つのページネーションインターセプターが設定されています!設定ファイルまたはコードを確認し、1 つだけ残してください!
insert 后如何返回主鍵
insert 後、主キーは自動的にエンティティの ID フィールドに設定されます。そのため、getId()
を呼び出すだけで取得できます。
MP で特定のフィールドのみを検索する方法
EntityWrapper.sqlSelect で検索したいフィールドを設定します
EntityWrapper<H2User> ew = new EntityWrapper<>();ew.setSqlSelect("test_id as id, name, age");//3つのフィールドのみを検索List<H2User> list = userService.selectList(ew);for(H2User u:list){ Assert.assertNotNull(u.getId()); Assert.assertNotNull(u.getName()); Assert.assertNull(u.getPrice()); // このフィールドは検索されていません}
//3.xmapper.selectList( Wrappers.<User>lambdaQuery() .select(User::getId, User::getName));//またはQueryWrapperを使用mapper.selectList( new QueryWrapper<User>() .select("id","name"));
Mapper 層のセカンドレベルキャッシュ問題
キャッシュは service 層に配置することを推奨します。独自の BaseServiceImpl をカスタマイズして、アノテーション付きの親クラスメソッドをオーバーライドし、独自の実装を継承することができます。
Mapper 層のセカンダリキャッシュ更新問題
MyBatis の方法でサードパーティのセカンダリキャッシュを設定し、かつ 2.0.9 以上のバージョンを使用している場合、組み込みのメソッドではキャッシュ内容を更新できないことがあります。以下のいずれかの方法で解決してください(二択):
- コード内の MyBatis の Mapper 層にキャッシュ注釈を追加し、implementation または eviction の値をキャッシュインターフェースの実装クラスとして宣言する
@CacheNamespace(implementation=MybatisRedisCache.class,eviction=MybatisRedisCache.class)public interface DataResourceMapper extends BaseMapper<DataResource>{}
- 対応する mapper.xml で既存の注釈をリンク式宣言に変更し、xml ファイル内のキャッシュが正常に機能するようにする
<cache-ref namespace="com.mst.cms.dao.DataResourceMapper"></cache-ref>
Cause: org.apache.ibatis.type.TypeException:Error setting null for parameter #1 with JdbcType OTHER
MyBatis-Plus において、jdbcTypeForNull
は重要な設定オプションであり、Java の null
値をデータベースにどのようにマッピングするかを決定します。
デフォルトでは、Java でフィールドが null
の場合、MyBatis-Plus はそれをデータベースの JdbcType.OTHER
値にマッピングします。しかし、一部の JDBC ドライバ(Oracle など)は JdbcType.OTHER
をサポートしていないため、デフォルト設定ではエラーが発生します: Cause: org.apache.ibatis.type.TypeException:Error setting null for parameter #1 with JdbcType OTHER . Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: 無効な列タイプ: 1111
.
jdbcTypeForNull
を設定するには、MyBatis-Plus の設定クラスに以下のコードを追加する必要があります(いずれか一つを選択してください):
Spring Bean 設定
MybatisConfiguration configuration = new MybatisConfiguration();configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);configuration.setJdbcTypeForNull(JdbcType.NULL);sqlSessionFactory.setConfiguration(configuration);
Yaml 設定
mybatis-plus: configuration: jdbc-type-for-null: 'null'
Properties 設定
mybatis-plus.configuration.jdbc-type-for-null=NULL
XML 設定
<configuration> <settings> <setting name="jdbcTypeForNull" value="NULL" /> </settings></configuration>
注意点として、プロジェクトで
SqlSessionFactory
をカスタマイズしている場合、jdbc-type-for-null
設定が無効になる可能性があります。
Page オブジェクトをカスタム SQL でパラメータとして渡せない問題
カスタム SQL において、Page オブジェクトは RowBounds を継承しているため、Mapper 内で直接取得することができません。この問題を解決するには、以下の代替案をご検討ください:
- カスタムの Map オブジェクトまたは通常の Java オブジェクトを使用してパラメータを渡す
- メソッドパラメータで
@Param("page") int page, @Param("size") int size
を使用して、ページ番号とページサイズを渡す
これらの方法により、カスタム SQL でパラメータを正しく渡すことができ、コードの円滑な実行を確保できます。
Map 下划线自動キャメルケース変換の使用方法
resultType="java.util.Map"
を使用する場合、Spring Boot で下線からキャメルケースへの自動変換を実装するには、以下の手順に従ってください。
Spring Boot プロジェクトに設定クラスを作成します。
@Configurationpublic class MybatisConfigurationCustomizer { @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory()); }}
この設定により、Map 内の下線からキャメルケースへの自動変換が実現します。これにより、MyBatis のクエリ結果が Map オブジェクトにマッピングされる際、キー名が自動的に変換され、コード内でデータにアクセスする際により便利になります。
Wrapper での limit
を使用した SQL 制限方法
Wrapper で limit
を使用して SQL の結果セットを制限するには、以下の方法があります:
// データを1件のみ取得wrapper.last("limit 1");
このコードは SQL 文の末尾に limit 1 を追加し、結果セットが返す行数を1行に制限します。
汎用的なバッチ挿入操作を Service 層で処理する理由
汎用的なバッチ挿入操作を Service 層で処理する理由は以下の通りです:
- SQL 長の制限: 大量データを処理する場合、単一の SQL 文では実行できない、またはメモリリークや JDBC 接続タイムアウトなどの問題を引き起こす可能性があります。
- データベースによるバッチ構文の不一致: 異なるデータベースでは単一 SQL 文でのバッチ挿入構文が異なるため、汎用性に欠けます。
- 解決策: ループ処理とプリペアドステートメントを使用したバッチコミット方式を採用します。単一 SQL 文での挿入よりもパフォーマンスは若干低下しますが、上記の問題を解決できます。
単一 SQL 文での挿入方式を使用したい場合は、オプションメソッド insertbatchsomecolumn を自行注入するか、SQL インジェクターで提供されているメソッドを参照してください。
3.xバージョンにおけるデータベースキーワードの処理方法
MyBatis-Plus 3.xでは、キーワードを自動的に識別して処理する機能は提供されなくなりました。データベースキーワードの処理方法には以下のいくつかの方法があります:
-
異なるデータベースではキーワードの処理方法が異なるため、保守が困難です。データベース設計時には、フィールド名やテーブル名としてキーワードを使用しないことを推奨します。
-
キーワードを使用する必要がある場合は、フィールドまたはテーブル名の前後にバッククォート(`)を追加して処理することができます。以下の例をご覧ください:
@TableField(value = "`status`")private Boolean status;
以上のことから、問題を回避するためには、データベース設計においてキーワードの使用を可能な限り避けることをお勧めします。
MybatisPlusException: プロパティ名 “xxx” に対応するデータベース列名が見つかりません!
MyBatis Plus 3.1.1 以降のバージョンで発生する問題について:
現象: 単体テストでは問題ありませんが、サーバーを起動してデバッグする際にこの例外が発生します。
原因: 3.1.1 以降のバージョンでは、フィールドキャッシュの最適化が行われており、元のクラス名(className)の代わりに .class
をキーとして使用しています。しかし、dev-tools を使用している場合、.class
が異なるクラスローダーでロードされる可能性があり、その結果、プロパティが見つからない状況が発生します。
解決策: dev-tools プラグインを削除してください。これにより、異なるクラスローダーで .class
がロードされるのを防ぎ、この例外問題を解決できます。
カラム “create_time” を結果セットから取得しようとした際のエラー。原因: java.sql.SQLFeatureNotSupportedException
MyBatis Plus 3.1.1 以降のバージョンで発生する問題です:
現象: Druid データソースを統合している環境で、3.1.1 以降のバージョンにアップグレードした後、エラー java.sql.SQLFeatureNotSupportedException
が発生します。3.1.0 以前のバージョンではこの問題は発生しません。
原因: MyBatis Plus 3.1.1 以降のバージョンでは、新しいバージョンの JDBC が採用され、新しい日付型(例: LocalDateTime
)の処理方法がアップグレードされました。しかし、Druid は 1.1.21 より前のバージョンではこの機能をサポートしておらず、この例外が発生します。詳細については、関連する Issue を参照してください。
解決策:
- Druid データソースを 1.1.21 以降のバージョンにアップグレードすることで、この問題を解決できます。
- Druid データソースをアップグレードできない場合は、MyBatis Plus のバージョンを 3.1.0 以前に維持することを選択できます。
- 最新の MyBatis Plus バージョンを引き続き使用する場合は、この例外を回避するために、新しいバージョンの JDBC と互換性のある他のデータソースへの変更を検討できます。
MyBatis Plus を 3.1.1 以降にアップグレード後、新しい日付型がマッピングできないエラー
MyBatis Plus を 3.1.0 以下のバージョンから上位バージョンにアップグレードし、新しい日付型(LocalDateTime など)がマッピングできないエラーが発生した場合、以下の理由が考えられます:
MP_3.1.0 およびそれ以前のバージョンは MyBatis 3.5.0 に依存しています。一方、MP_3.1.1 では MyBatis の依存関係が 3.5.1 にアップグレードされました。MyBatis 3.5.1 では、新しい日付型には JDBC 4.2 API をサポートする JDBC ドライバが必要です。
お使いの JDBC ドライバのバージョンが JDBC 4.2 API をサポートしていない場合、新しい日付型がマッピングできないエラーが発生します。
MyBatis 公式ブログ の内容を参照:
There is one backward incompatible changes since 3.5.0. Because of the fix for #1478 , LocalDateTypeHandler, LocalTimeTypeHandler and LocalDateTimeTypeHandler now require a JDBC driver that supports JDBC 4.2 API. [EDIT] These type handlers no longer work with Druid. Please see #1516 .
解決策:
- JDBC ドライバを JDBC 4.2 API をサポートするバージョンにアップグレードしてください。
- JDBC ドライバをアップグレードできない場合は、MyBatis Plus のバージョンを 3.1.0 またはそれ以前のバージョンにロールバックすることを検討してください。
“mybatis-plus.configuration.incomplete-result-maps[0].assistant.configuration.mapped-statements[0].parameter-map.parameter-mappings[0]” 配下のプロパティを org.apache.ibatis.mapping.ParameterMapping にバインドできませんでした
Spring Boot バージョンを 2.2.0 からより新しいバージョンにアップグレードする際にこの問題が発生した場合、以下の理由が考えられます。
現象: ローカル環境での起動時には問題ありませんが、war パッケージにビルドしてサーバーにデプロイするとこの問題が発生します。
原因: Spring Boot 2.2.0 にはコンストラクタインジェクションの問題が存在し、MyBatis のプライベートコンストラクタがプロパティを正しくバインドできないため、MyBatis に依存するフレームワーク(MyBatis Plus など)でエラーが発生します。詳細については 関連 issue を参照してください。この問題は Spring Boot 2.2.1 で修正されました。
解決策:
- Spring Boot を 2.1.x バージョンにダウングレードするか、2.2.1 以降のバージョンにアップグレードしてください。より良い安定性と修正を確保するために、Spring Boot 2.2.2 への直接アップグレードを推奨します。
分離パッケージデプロイ時の ClassNotFoundException
現象:開発ツール内での実行では問題ありませんが、プロジェクトをパッケージ化してサーバーにデプロイした後、Lambda 式を実行すると ClassNotFoundException が発生します。
MyBatis-Plus 3.3.2 より前のバージョンでは、分離パッケージデプロイ時に ClassNotFoundException の問題が発生する場合、逆シリアル化操作の実行中にクラスローダーでエラーが発生している可能性があります。
解決策:
spring-boot-maven-plugin
プラグインを除去してパッケージ化する、または- MyBatis-Plus 3.3.2 バージョンにアップグレードする。操作については、サンプル 分離パッケージ を参照してください。
MyBatis 内部ログ記録の有効化
MyBatis 内部のログ記録を有効にするには、以下の2つの方法があります:
方法一:
application.yml
または application.properties
ファイルに以下の設定を追加します:
mybatis-plus: configuration: # プロジェクトにログフレームワークがない場合、org.apache.ibatis.logging.stdout.StdOutImpl を指定することができます(実際の本番環境では使用しないでください)。 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
これにより、MyBatis 組み込みの StdOutImpl ログ記録実装を使用して、ログがコンソールに出力されます。
方法二:
application.yml
または application.properties
ファイルにログレベル設定を追加し、特定のパッケージのログレベルを指定します。例:
logging: level: com.baomidou.example.mapper: debug
これにより、com.baomidou.example.mapper パッケージ以下のログレベルが debug に指定されます。必要に応じてレベルを調整できます。
上記の設定により、MyBatis 内部のログ記録を有効にし、必要に応じてログレベルを調整することができます。
update 時にフィールドをインクリメントする方法
更新操作時に特定のフィールドをインクリメントしたい場合、MyBatis Plus が提供する Wrapper
を使用して更新を行うことができます。以下に実現可能なソリューションを示します:
UpdateWrapper<Entity> wrapper = new UpdateWrapper<>();wrapper.setSql("column = column + 1");
// または Lambda 式を使用UpdateWrapper<Entity> wrapper = new UpdateWrapper<>();wrapper.setSql("column = column + 1");
// update メソッドを呼び出すbaseMapper.update(null, wrapper);
この方法で、更新時にフィールドをインクリメントすることができます。setSql
メソッド内で直接 SQL 更新文を指定する必要があることに注意してください。
データベースキーワードのグローバル処理方法
データベースキーワードをグローバルに処理したい場合は、MyBatis Plusが提供するグローバル設定を使用できます。以下は設定例です(MySQLの場合):
mybatis-plus: global-config: db-config: column-format: "`%s`"
この設定により、MyBatis PlusはSQL文を生成する際、データベースフィールド名をバッククォート(“)で囲み、データベースキーワードとの衝突を防ぎます。
以下の点に注意してください:
@TableField
アノテーションを使用しており、グローバルな書式設定を維持したい場合は、パラメータkeepGlobalFormat=true
を設定する必要があります。@TableField
アノテーション内で固定形式のデータベースキーワードを直接指定することもできます。例:@TableField("'status'")
上記の設定により、データベースキーワードをグローバルに処理し、生成されるSQL文がキーワードの影響を受けないようにすることができます。
XML でデータベースの種類に基づいて異なる SQL フラグメントを選択する方法
XML でデータベースの種類に基づいて異なる SQL フラグメントを選択したい場合、MyBatis Plus が提供する database-id
パラメータを使用できます。以下は設定例です(MySQL を例として):
mybatis-plus: configuration: database-id: mysql
このように設定すると、MyBatis Plus は SQL 文を実行する際に、database-id
パラメータに基づいて異なる SQL フラグメントを選択します。
XML では以下の異なる書き方で判別できます:
- 変数
_databaseId
を使用:
<select id="selectAllNames" resultType="java.lang.String">select<choose> <when test="_databaseId == 'mysql'"> GROUP_CONCAT(name SEPARATOR ',') </when> <otherwise> array_to_string(ARRAY_AGG(name), ',') </otherwise></choose>from user</select>
- タグ属性
databaseId
を使用:
<select id="selectAllNames" databaseId="mysql" resultType="java.lang.String"> select GROUP_CONCAT(name SEPARATOR ',') from user</select><select id="selectAllNames" databaseId="pgsql" resultType="java.lang.String"> select array_to_string(ARRAY_AGG(name), ',') from user</select>
上記の設定により、異なるデータベースの種類に基づいて異なる SQL フラグメントを選択できます。
MyBatis Plus が複合主キーをサポートせず、一意な ID を強制する理由について
MyBatis Plus が複合主キーをサポートせず、一意な ID の使用を強制する理由は、以下の考慮事項によるものです:
-
テーブル間の相互依存性の増加:複合主キーを使用すると、テーブル間の関係がより複雑になり、保守と管理の難易度が増加します。
-
データの複雑な制約、ルールの増加:複合主キーはデータの制約とルールを増加させます。例えば、一意性の制約が必要となりますが、これは完全に複合インデックスを使用して実現できます。
-
データ更新の制限の増加:データを更新する際、すべての複合主キーの値を更新する必要があり、更新操作の制限と複雑さが増加します。
-
深刻なデータ冗長性と更新異常の問題:複合主キーは、データの冗長性と更新異常の問題を引き起こす可能性があります。特に大規模なシステムでは、更新異常が発生する場合があります。
-
パフォーマンスの問題:複合主キーを使用する場合、特定の ID をクエリする際にインデックスを使用できず、パフォーマンスの低下を招きます。
以上をまとめると、複合主キーを使用することで 1 つの ID フィールドを省くことはできますが、この方法の欠点は利点を上回ります。そのため、このような方法は推奨しません。MyBatis Plus は、データ管理の単純性、保守性、およびパフォーマンスを保証するために、一意な ID の使用を堅持しています。
プロジェクト起動速度の低下
-
デフォルトのSnowflake ID初期化が原因
- ローカルマシンのホスト名を確認
- システムのhostsファイルを編集し、ローカルマシンのホスト名をhostsファイルに書き込む
# 例: ホスト名がnieqiurong-PCの場合、hostsファイルに以下のように設定します127.0.0.1 localhost nieqiurong-PC -
データベース接続プールの初期化を確認
Hikariを使用した場合、Linuxシステムで起動が遅くなる事例があります
解決策:
Java起動コマンドで -Djava.security.egd=file:/dev/urandom を指定し、乱数取得方法を /dev/random から /dev/urandom に変更します
例: java -Djava.security.egd=file:/dev/urandom -jar xxxx.jar
3.5.7+ バージョンにおける Db.saveBatch の戻り値が常に false になる問題
- 原因:ドライバーの設定に互換性がないため
解決策:ドライバー接続から
rewriteBatchedStatements=true
設定を削除してください
PageまたはListが返す結果セットがnull、または一部のnull要素を含む場合
- 原因:MyBatisはデフォルトでは、返される行のすべての列が空の場合(属性が自動マッピングできない場合を含む)、デフォルトでnullを返します
解決策:returnInstanceForEmptyRow を true に設定します
mybatis-plus: configuration: return-instance-for-empty-row: true
戻り値が Map または List<Map> の場合、null 値に対応する key が Map に追加されない
- 原因:MyBatis はデフォルトでは、null が返される場合に put メソッドを呼び出しません
解決策:callSettersOnNulls を true に設定します
mybatis-plus: configuration: call-setters-on-nulls: true
BaseMapperメソッドの書き換え方法
インターフェースメソッドの書き換えを行う場合は、defaultメソッドと抽象インターフェースメソッドを区別してください。書き換えるメソッドは、最終的に呼び出される実際のメソッドに準拠する必要があります。
抽象インターフェースメソッド: XMLで直接このメソッドを書き換えることで完了します
defaultメソッド: 実際に呼び出されるメソッドを直接書き換えるか、元のdefaultメソッドを実際のインターフェースメソッドとして書き換えた後、XMLまたはアノテーション方式で実行文を書き換えます。
// 方式一: アノテーション方式によるBaseMapperのselectPageメソッドの書き換え(低バージョンではインターフェースメソッドの場合があります。ここでは新版のdefaultを例とします)@Override@Select("select * from h2user")<P extends IPage<H2User>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<H2User> queryWrapper);
// 方式二: XML方式によるBaseMapperのselectPageメソッドの書き換え(低バージョンではインターフェースメソッドの場合があります。ここでは新版のdefaultを例とします)@Override<P extends IPage<H2User>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<H2User> queryWrapper);// ここでXML内でselectPageのクエリ文を自分で書き換えます
// 方式三: BaseMapperのselectPageメソッドの書き換え@Overridedefault <P extends IPage<User>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<User> queryWrapper) { return xxxx(); //実際に呼び出される自分で定義した実際のインターフェースメソッド}
Kotlinでインターフェースのdefaultメソッドを呼び出すと Invalid bound statement (not found) が発生する
- 原因:https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-m3-generating-default-methods-in-interfaces/
解決策:コンパイルプラグインのコンパイルパラメータに
-Xjvm-default=all
を設定する
<plugin> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-plugin</artifactId> <version>${kotlin.version}</version> <configuration> <args> <arg>-Xjsr305=strict</arg> <arg>-Xjvm-default=all</arg> </args> </configuration></plugin>
compileKotlin { kotlinOptions { freeCompilerArgs = ['-Xjvm-default=all'] }}
Encountered unexpected token: “\n\n\n” 或 Could not set parameters for mapping
-
原因:jsqlParser4.9では、連続した改行はステートメントの終了と見なされ、Encountered unexpected token解析エラーがスローされます。5.0以降では、連続した
\n\n
に遭遇すると、後続のステートメントが切り捨てられ、mybatis層でCould not set parameters for mappingエラーが発生します。参考リンク: https://github.com/JSQLParser/JSqlParser/issues/1988
mybatis-plusはバージョン3.5.3.2以降、フレームワーク内で組み込み注入されるSQLの改行処理に対応していますが、プロジェクト内で独自に記述されたステートメントについては、自身で処理する必要があります。
-
注意: バージョン3.5.10および3.5.10.1では、プロジェクト内の改行状況を処理しますが、プロジェクト内で単行コメント(
--
または#
のようなステートメント)を使用している場合の処理はサポートされていません。バージョン3.5.11以降ではこの処理は行われなくなります。連続した改行を含むステートメントを処理する必要がある場合は、以下の方法で対応できます。
方法一:
static { JsqlParserGlobal.setParserMultiFunc((sql)-> { String formatSql = CCJSqlParserUtil.sanitizeSingleSql(sql); return CCJSqlParserUtil.parseStatements(formatSql, JsqlParserGlobal.getExecutorService(), null); }); JsqlParserGlobal.setParserSingleFunc((sql)-> { String formatSql = CCJSqlParserUtil.sanitizeSingleSql(sql); return CCJSqlParserUtil.parse(formatSql, JsqlParserGlobal.getExecutorService(), null); });}
// jsqlParserが5.0未満の場合、以下のメソッドを自身のプロジェクトにコピーしてください。コードはJsqlParser5.0バージョンに由来します。public static String sanitizeSingleSql(String sqlStr) { Pattern SQL_DELIMITER_SPLIT = Pattern.compile("((?:'[^']*+'|[^\\n])*+)"); StringBuilder builder = new StringBuilder(); Matcher matcher = SQL_DELIMITER_SPLIT.matcher(sqlStr); while(matcher.find()) { for(int i = 1; i <= matcher.groupCount(); ++i) { if (!matcher.group(i).isEmpty()) { builder.append("\n").append(matcher.group(i)); } } } return builder.toString();}
方法二: mybatisの改行除去ステートメントを有効にする(汎用性はそれほど高くなく、単行コメント --
または #
のようなものを処理できません)
mybatis-plus: configuration: shrink-whitespaces-in-sql: true
コードジェネレータで XXXRepository を生成するための設定方法
今後は Service と Repository のメンテナンスを行わないため、継続して使用することは推奨しません。生成が必要な場合は、以下の手順に従って service を repository に変換してください。
AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);generator.packageInfo(new PackageConfig.Builder() //-- 変換開始---- .service("repository") .serviceImpl("repository.impl") //-- 変換終了---- .build());generator.strategy( new StrategyConfig.Builder() //-- 変換開始---- .serviceBuilder().convertServiceFileName(entityName -> "I" + entityName + "Repository") .superServiceClass(IRepository.class).convertServiceImplFileName(entityName -> entityName + "Repository") .superServiceImplClass(CrudRepository.class) //-- 変換終了---- .build());generator.execute();