DBと連携したSpring Bootアプリの実行時に「TransactionRequiredException: No EntityManager with ~」というエラー(例外)が出たので、この解決方法について説明しています。
※ 本ページはプロモーションが含まれています。
Spring Bootのウェブアプリを開発していて、ウェブアプリ実行時に下記の「TransactionRequiredException: No EntityManager with〜」エラー(というか例外)が発生したので、この例外を解決するためにやった事などを書いています。
2023-02-17 00:22:15.098 ERROR 13332 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call] with root cause
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:295) ~[spring-orm-5.3.25.jar:5.3.25]
〜
◾️動作環境とバージョン情報です
Spring Bootバージョン:2.7.8
Java:11
MySQL:MySQL Community Edition 8.0.31
開発環境:Eclipse(Pleiades All in One、4.16(2020-06)、Java Full Edition版)
OS:macOS Big Sur(バージョン11.7.3)
上記エラーが発生した時の詳細についてもう少し書いておきます。
DB(MySQL)と連携したSpring Bootアプリで、RegisteredUserテーブルのインタフェース(Spring Data JPAのCrudRepositoryを継承)があり、RegisteredUserテーブルのusernameカラムを条件にレコードを削除するメソッドを用意しておきました。
//RegisteredUserのリポジトリインタフェース
public interface RegisteredUserRepository extends CrudRepository<RegisteredUser, Integer> {
//RegisteredUserテーブルのusernameカラムを条件にレコードを削除する
void deleteByUsername(String username);
}
そしてこのリポジトリをサービスクラスから呼び出すようにします(もちろんコントローラクラスから呼び出してもOK)。
@Service
public class RegisteredUserService {
@Autowired
RegisteredUserRepository registeredUserRepository;
public void deleteByUsername(String username) {
registeredUserRepository.deleteByUsername(username);
}
}
これで、Spring Bootアプリ実行中にサービスクラスの「registeredUserRepository.deleteByUsername(username);」の部分が実行されるタイミングで冒頭のエラーが出るようになりました。
Eclipseのコンソールにはdelete SQLのログは出力されてなかったので、delete SQL自体が実行された形跡は無しでした。
ちなみにSpring Boot(Spring Data JPA)でHibernateのSQLログを出力したい場合、application.propertiesファイルにspring.jpa.show-sqlを指定すればできます。
spring.jpa.show-sql=true
それでは問題のエラーの解決方法についてです。
サービスクラスのdeleteByUsernameメソッドに@Transactionalアノテーションを付けたら、ちゃんとdelete SQLが実行されるようになりました。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
〜
@Service
public class RegisteredUserService {
@Autowired
RegisteredUserRepository registeredUserRepository;
@Transactional
public void deleteByUsername(String username) {
registeredUserRepository.deleteByUsername(username);
}
}
(*ちなみに@Transactionalは"javax.transaction.Transactional"を使っても動作した。)
再度問題のエラーログをしっかり確認してみたらTransactionRequiredException例外とあるので、どうやらリポジトリで実行するSQLによってはちゃんと@Transactionalを付けてトランザクションにする必要があるみたいですね。
これでSpring Bootアプリは問題なく動作するようになったのですが、Eclipseで@Transactionalを付けたメソッド内にデバッグのブレークポイントを付けてデバッグ起動したら、「Javaブレークポイント。行番号が見つからないため、〜。欠落した行番号情報」というアラートウインドウが頻繁に出るようになりました(自分の環境だけかも)。
ちょっと意味の分からないアラートですが、Ecllipseの設定でこのアラートを出ないようにする事はできます。
Eclipseの設定ウインドウを開いてサイドメニューからJava->デバッグを選択して、「行番号属性が見つからないためにブレークポイントをインストールできない時に警告」というチェックボックスを外せばこの警告は出なくなります。
あと、@Transactionalを付けたメソッド内にデバッグのブレークポイントさえ付けなければアラートは全く出ないのでそこまで気にする必要はないかなと思います。
ですので反対に言えば、もしもEclipseでウェブアプリをデバッグ起動してJavaブレークポイントのアラートウインドウが出るようになった場合、アラートウインドウのメッセージに書いてあるメソッド内にデバッグのブレークポイントを付けている可能性があるのでそれを取るか、Eclipseの設定(Java->デバッグ)でこのアラートウインドウが出ないようにすれば解決できます。