Spring 데이터베이스 읽 기와 쓰기 분 리 를 위 한 예제
읽 기와 쓰기 분리 기술 의 목표:Master 라 이브 러 리 의 압력 을 효과적으로 줄 이 고 사용자 가 데 이 터 를 조회 하 는 요 구 를 서로 다른 Slave 라 이브 러 리 에 나 누 어 시스템 의 건장 성 을 확보 할 수 있 습 니 다.읽 기와 쓰기 가 분 리 된 배경 을 살 펴 보 자.
사이트 의 업무 가 계속 확대 되 고 데이터 가 계속 증가 하 며 사용자 가 점점 많아 지면 서 데이터 뱅 크 의 부담 도 점점 커지 고 전통 적 인 방식 을 사용한다.예 를 들 어 데이터 뱅 크 나 SQL 의 최적화 는 대체적으로 요구 에 이 르 지 못 한다.이 럴 때 읽 기와 쓰기 분리 전략 으로 현황 을 바 꿀 수 있다.
구체 적 으로 개발 과정 에서 어떻게 읽 기와 쓰기 의 분 리 를 편리 하 게 실현 할 수 있 습 니까?현재 자주 사용 하 는 방법 은 두 가지 가 있다.
1.첫 번 째 방식 은 우리 가 가장 자주 사용 하 는 방식 이다.바로 2 개의 데이터 베이스 연결 을 정의 하 는 것 이다.하 나 는 MasterDataSource 이 고 다른 하 나 는 SlaveDataSource 이다.데 이 터 를 업데이트 할 때 MasterDataSource 를 읽 고 데 이 터 를 조회 할 때 SlaveDataSource 를 읽 습 니 다.이런 방식 은 매우 간단 해서 나 는 군말 하지 않 겠 다.
2.두 번 째 방식 은 동적 데이터 원본 전환 입 니 다.프로그램 이 실 행 될 때 데이터 원본 을 프로그램 에 동적 으로 짜 서 메 인 라 이브 러 리 를 읽 을 지,라 이브 러 리 에서 읽 을 지 선택 하 는 것 입 니 다.주로 사용 되 는 기술 은 annotation,Spring AOP,반사 입 니 다.실현 방식 을 상세히 소개 하 겠 습 니 다.
실현 방식 을 소개 하기 전에 필요 한 지식,spring 의 AbstractRouting DataSource 류 를 준비 합 니 다.
AbstractRouting DataSource 라 는 종 류 는 spring 2.0 이후 에 추 가 된 것 입 니 다.먼저 AbstractRouting DataSource 의 정 의 를 살 펴 보 겠 습 니 다.
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {}
AbstractRouting DataSource 는 AbstractDataSource 를 계승 하고 AbstractDataSource 는 DataSource 의 하위 클래스 입 니 다.DataSource 는 javax.sql 의 데이터 원본 인터페이스 로 다음 과 같이 정의 합 니 다.
public interface DataSource extends CommonDataSource,Wrapper {
/**
* <p>Attempts to establish a connection with the data source that
* this <code>DataSource</code> object represents.
*
* @return a connection to the data source
* @exception SQLException if a database access error occurs
*/
Connection getConnection() throws SQLException;
/**
* <p>Attempts to establish a connection with the data source that
* this <code>DataSource</code> object represents.
*
* @param username the database user on whose behalf the connection is
* being made
* @param password the user's password
* @return a connection to the data source
* @exception SQLException if a database access error occurs
* @since 1.4
*/
Connection getConnection(String username, String password)
throws SQLException;
}
DataSource 인 터 페 이 스 는 데이터베이스 연결 을 가 져 오 는 두 가지 방법 을 정의 합 니 다.AbstractRouting DataSource 가 DataSource 인 터 페 이 스 를 어떻게 실현 하 는 지 살 펴 보 겠 습 니 다.
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
자신의 determineTargetDataSource()방법 으로 connection 을 얻 은 것 이 분명 하 다.determineTargetDataSource 방법 은 다음 과 같이 정의 합 니 다.
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
우리 가 가장 관심 을 가 지 는 것 은 역시 다음 두 마디 이다.
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
determineCurrentLookupkey 방법 은 lookupKey 를 되 돌려 줍 니 다.resolvedDataSources 방법 은 lookupKey 에 따라 맵 에서 데이터 원본 을 얻 는 것 입 니 다.resolvedDataSources 와 determineCurrentLookupkey 는 다음 과 같이 정의 합 니 다.
private Map<Object, DataSource> resolvedDataSources;
protected abstract Object determineCurrentLookupKey()
상기 정 의 를 보면 우 리 는 생각 이 좀 있 지 않 습 니까?resolvedDataSources 는 Map 유형 입 니 다.우 리 는 MasterDataSource 와 SlaveDataSource 를 Map 에 저장 할 수 있 습 니 다.다음 과 같 습 니 다.key
value
master
MasterDataSource
slave
SlaveDataSource
저 희 는 DynamicDataSource 가 AbstractRouting DataSource 를 계승 하여 determineCurrentLookupkey()방법 을 실현 하고 있 습 니 다.이 방법 은 Map 의 key,master 또는 slave 를 되 돌려 줍 니 다.
자,이렇게 많은 말 을 했 으 니 좀 짜증 이 난다.다음은 어떻게 실현 하 는 지 보 자.
위 에서 이미 우리 가 사용 해 야 할 기술 을 언급 하 였 으 니,우 리 는 먼저 annotation 의 정 의 를 살 펴 보 자.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
우 리 는 spring 의 추상 적 인 추상 류 AbstractRouting DataSource 를 실현 해 야 합 니 다.바로 determineCurrentLookupkey 를 실현 하 는 방법 입 니 다.
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// TODO Auto-generated method stub
return DynamicDataSourceHolder.getDataSouce();
}
}
public class DynamicDataSourceHolder {
public static final ThreadLocal<String> holder = new ThreadLocal<String>();
public static void putDataSource(String name) {
holder.set(name);
}
public static String getDataSouce() {
return holder.get();
}
}
DynamicDataSource 의 정 의 를 보면 그 가 돌아 온 것 은 DynamicDataSourceHolder.getDataSouce()값 입 니 다.프로그램 이 실 행 될 때 DynamicDataSourceHolder.putDataSource()방법 을 호출 하여 값 을 부여 해 야 합 니 다.다음은 우리 가 실현 하 는 핵심 부분,즉 AOP 부분 입 니 다.DataSourceAspect 의 정 의 는 다음 과 같 습 니 다.
public class DataSourceAspect {
public void before(JoinPoint point)
{
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m
.getAnnotation(DataSource.class);
DynamicDataSourceHolder.putDataSource(data.value());
System.out.println(data.value());
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
테스트 를 편리 하 게 하기 위해 저 는 2 개의 데이터 베 이 스 를 정 의 했 습 니 다.shop 시 뮬 레이 션 Master 라 이브 러 리,test 시 뮬 레이 션 Slave 라 이브 러 리,shop 과 test 의 표 구조 가 일치 하지만 데이터 가 다 릅 니 다.데이터 베 이 스 는 다음 과 같 습 니 다.
<bean id="masterdataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/shop" />
<property name="username" value="root" />
<property name="password" value="yangyanping0615" />
</bean>
<bean id="slavedataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test" />
<property name="username" value="root" />
<property name="password" value="yangyanping0615" />
</bean>
<beans:bean id="dataSource" class="com.air.shop.common.db.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- write -->
<entry key="master" value-ref="masterdataSource"/>
<!-- read -->
<entry key="slave" value-ref="slavedataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="masterdataSource"/>
</beans:bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:config/mybatis-config.xml" />
</bean>
spring 설정 에 op 설정 추가
<!-- aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<beans:bean id="manyDataSourceAspect" class="com.air.shop.proxy.DataSourceAspect" />
<aop:config>
<aop:aspect id="c" ref="manyDataSourceAspect">
<aop:pointcut id="tx" expression="execution(* com.air.shop.mapper.*.*(..))"/>
<aop:before pointcut-ref="tx" method="before"/>
</aop:aspect>
</aop:config>
<!-- aop -->
다음은 MyBatis 의 UserMapper 의 정의 입 니 다.테스트 에 편리 하도록 로그 인하 여 Master 라 이브 러 리 를 읽 었 습 니 다.사용자 목록 에서 Slave 라 이브 러 리 를 읽 었 습 니 다.
public interface UserMapper {
@DataSource("master")
public void add(User user);
@DataSource("master")
public void update(User user);
@DataSource("master")
public void delete(int id);
@DataSource("slave")
public User loadbyid(int id);
@DataSource("master")
public User loadbyname(String name);
@DataSource("slave")
public List<User> list();
}
자,이 클립 스 를 실행 하여 효 과 를 보고 사용자 이름 admin 을 입력 하여 로그 인 해 보 세 요.이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[MeU] Hashtag 기능 개발➡️ 기존 Tag 테이블에 존재하지 않는 해시태그라면 Tag , tagPostMapping 테이블에 모두 추가 ➡️ 기존에 존재하는 해시태그라면, tagPostMapping 테이블에만 추가 이후에 개발할 태그 기반 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.