LoginSignup
17
17

More than 5 years have passed since last update.

Spring JPA でSELECT時はSlaveサーバへアクセスする

Last updated at Posted at 2014-07-08

やりたいこと

Spring JPAを使ってMaster-Slave構成の時にSELECTはSlave,その他はMasterサーバへアクセスする

ミドルウェア,フレームワーク

  • ReplicationDriver
  • MySQL 5.6
  • Spring 3.2.8
  • hibernate

テストの構成

  • Customer.java エンティティクラス
  • CustomerRepository.java JpaRepositoryを継承したクラス. findなどを行います
  • CustomerService.java CustomerRepositoryを呼び出します
  • Testクラス CustomerServiceを呼び出します

コードは下記に
https://github.com/hachi-eiji/spring-jpa-practice/

実現方法

ReplicationDriverはautoCommit=false, readOnly=trueのときにselectを発行するとslaveへアクセスします
よって、find処理を実行する前にインターセプターで設定を行います
(autoCommit=trueでも動きました。ここをfalseにする理由をご存じの方は教えてください)

また、CustomerService.javaのメソッドで @Transactional をつけます
つけない場合は下記のエラーが発生します。これはインターセプターの中で entityManager.unwrap(SessionImpl.class); を使うときはトランザクションが必須だからです。
-> PersistenceContextアノテーションがEXTENDEDの時は @Transactinalはなくてもいけました

java.lang.IllegalStateException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator
$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:224)

最後に

実際に組み込むときは、エンティティクラスにマーカーアノテーションをつけたほうが、おそらく効率はいいはずです。

参考資料

ReplicationDriver
MySQL Master/Slave Load Balancing with JPA and Spring

コード

インターセプターのコード
@Aspect
@Component
public class SlaveConnectionInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(SlaveConnectionInterceptor.class);

    private EntityManager entityManager;

    @PersistenceContext
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Around("execution(public * com.hachiyae.jpa..*.find*(..)) || execution(public * com.hachiyae.jpa..*.count*(..))")
    public Object proceed(ProceedingJoinPoint pjp) throws Throwable {
        LOGGER.info("call SlaveConnectionInterceptor from {}", pjp.getSignature().toShortString());
        SessionImpl session = entityManager.unwrap(SessionImpl.class);
        Connection connection = session.connection();
        boolean autoCommit = connection.getAutoCommit();
        boolean readOnly = connection.isReadOnly();
        connection.setAutoCommit(false);
        connection.setReadOnly(true);
        try {
            return pjp.proceed();
        } finally {
            connection.setAutoCommit(autoCommit);
            connection.setReadOnly(readOnly);
        }
    }
}
17
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
17