Skip to content

Instantly share code, notes, and snippets.

@debop
Created July 21, 2013 08:26
Show Gist options
  • Save debop/6047921 to your computer and use it in GitHub Desktop.
Save debop/6047921 to your computer and use it in GitHub Desktop.
package kr.hconnect.data.jpa.mysql;
import lombok.Getter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.sql.Connection;
import java.sql.SQLException;
/**
* MySQL Replication 환경 (Master-Slave)에서
* {@link org.springframework.transaction.annotation.Transactional#readOnly()} 이 true로 정의된 Method에 대해서는
* Slave 서버로 접속하기 위해, {@link java.sql.Connection#isReadOnly()}의 속성을 true로 변경하여 작업을 수행하도록 합니다.
*
* @author 배성혁 [email protected]
* @since 13. 7. 21. 오후 3:20
*/
@Aspect
@Component
public class MySqlConnectionInterceptor {
private static final Logger log = LoggerFactory.getLogger(MySqlConnectionInterceptor.class);
private static final boolean isTraceEnabled = log.isTraceEnabled();
@PersistenceContext
EntityManager em;
/** `@Transactional` 이 있는 메소드를 intercept 해서 readOnly 값에 따라 MySQL의 Master / Slave 서버를 구분합니다. */
@Around(value = "@annotation(transactional) if transactional.readOnly()", argNames = "pjp, transactional")
public Object proceed(final ProceedingJoinPoint pjp, final Transactional transactional) throws Throwable {
if (log.isTraceEnabled())
log.trace("읽기전용 작업을 수행하기 위해 현 connection를 readonly로 설정합니다...");
Session session = em.unwrap(Session.class);
ConnectionReadOnlyWork readOnlyWork = new ConnectionReadOnlyWork();
try {
session.doWork(readOnlyWork);
return pjp.proceed();
} finally {
session.doWork(new RestoreConnectionWork(readOnlyWork));
}
}
static class ConnectionReadOnlyWork implements Work {
@Getter boolean autoCommit;
@Getter boolean readOnly;
@Override
public void execute(Connection connection) throws SQLException {
this.autoCommit = connection.getAutoCommit();
this.readOnly = connection.isReadOnly();
connection.setAutoCommit(false);
connection.setReadOnly(true);
}
}
static class RestoreConnectionWork implements Work {
@Getter boolean autoCommit;
@Getter boolean readOnly;
public RestoreConnectionWork(ConnectionReadOnlyWork readOnlyWork) {
this.autoCommit = readOnlyWork.isAutoCommit();
this.readOnly = readOnlyWork.isReadOnly();
}
@Override
public void execute(Connection connection) throws SQLException {
connection.setAutoCommit(autoCommit);
connection.setReadOnly(readOnly);
if (log.isTraceEnabled())
log.trace("읽기전용 작업을 수행하고, connection의 원래 설정으로 재설정했습니다.");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment