Spring Bootで複数のリソースを定義している場合にメインでない方のEntityManagerのコネクションが途切れる場合がある

大体は動くものの、ローカル環境で起動してしばらく放っておいてからサブのデータソースを利用するサービスにアクセスすると「この接続は既に閉じられています。」と言われてエラーになることがありました。

古いEntityManagerが使われているんだろうなぁと言うのはわかるけど、
何が悪いのかはよくわかっていませんでした。

おそらくこういうことだろうということがつかめたので記載。
→つかめていませんでした。
おそらく原因はここで議論されていることと同じだと思います。
今はdataSourceのBeanを

@Bean(destroyMethod="")

にしているのですが、現象が発生しなくなったようです。

書き方としては修正後のほうが正しいと思うので以下そのまま掲載。

修正前configuration

    @Autowired
    @Bean
    public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
        return entityManagerFactory.createEntityManager();
    }

    @Autowired
    @Bean
    public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        bean.setDataSource(dataSource);
        bean.setJpaVendorAdapter(jpaVendorAdapter);
        bean.setPackagesToScan("com.tononchi.hoge");
        bean.afterPropertiesSet();
        return bean.getObject();
    }

    @Autowired
    @Bean
    @Qualifier("hoge")
    public EntityManager entityManagerHoge(@Qualifier("entityManagerFactoryHoge") EntityManagerFactory entityManagerFactoryHoge) {
        return entityManagerFactoryHoge.createEntityManager();
    }


    @Autowired
    @Bean
    @Qualifier("entityManagerFactoryHoge")
    public EntityManagerFactory entityManagerFactoryHoge(JpaVendorAdapter jpaVendorAdapter, @Qualifier("dataSourceHoge") DataSource dataSourceHoge) {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        bean.setDataSource(dataSourceHoge);
        bean.setJpaVendorAdapter(jpaVendorAdapter);
        bean.setPackagesToScan("com.tononchi.hoge");
        bean.afterPropertiesSet();
        return bean.getObject();
    }

修正前利用サービス

@Service
public class UseHogeService {
    @Autowired
    @Qualifier("hoge")
    private EntityManager entityManagerHoge;

    public int getHoge(String abc) {
        query = entityManagerHoge.createNativeQuery(SQL_GET);
        query.setParameter(1, abc);
        result = query.getResultList();
    }
}

で、これを下記のように修正。

修正後configuration

    @Autowired
    @Bean
    public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
        return entityManagerFactory.createEntityManager();
    }

    @Autowired
    @Bean
    public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        bean.setDataSource(dataSource);
        bean.setJpaVendorAdapter(jpaVendorAdapter);
        bean.setPackagesToScan("com.tononchi.hoge");
        bean.afterPropertiesSet();
        return bean.getObject();
    }

    @Autowired
    @Bean(name = "entityManagerFactoryHoge")
    @Qualifier("entityManagerFactoryHoge")
    public EntityManagerFactory entityManagerFactoryHoge(JpaVendorAdapter jpaVendorAdapter, @Qualifier("dataSourceHoge") DataSource dataSourceHoge) {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        bean.setDataSource(dataSourceHoge);
        bean.setJpaVendorAdapter(jpaVendorAdapter);
        bean.setPackagesToScan("com.tononchi.hoge");
        bean.afterPropertiesSet();
        return bean.getObject();
    }

修正後利用サービス。

@Service
public class UseHogeService {
    @PersistenceContext(unitName = "entityManagerFactoryHoge")
    private EntityManager entityManagerHoge;

    public int getHoge(String abc) {
        query = entityManagerHoge.createNativeQuery(SQL_GET);
        query.setParameter(1, abc);
        result = query.getResultList();
    }
}

EntityManagerをBean定義して@Autowiredしてしまっていたので、古いものを取ってきてしまっているものと推測。
サブの方は定義せずにEntityManagerFactoryの定義にとどめて@PersistenceContextで取ってくるようにするとうまくいくっぽいです。

ただ、メインのEntityManagerはBean定義しないとSpring Bootが勝手に2つもEntityManagerを作ってしまって起動しないので、こっちは定義しておきます。
メインはSpringがなんとかしてくれているのか、エラーは起こらないという不思議。
コネクションタイムアウトよりセッションタイムアウトのほうが早くて必ずログインし直すからかなぁ…よくわからんです。


投稿日

カテゴリー:

投稿者:

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください