주 해 를 기반 으로 한 springboot+my batis 의 다 중 데이터 소스 구성 요소 구현 코드
13265 단어 springbootmy batis 다 중 데이터 원본구성 요소
dataSource-sql Session Factory-SqlSession Template 설정 만 하면 됩 니 다.
다음 코드 는 먼저 메 인 데이터 원본 을 설정 합 니 다.@Primary 주석 표 지 를 통 해 기본 데이터 원본 으로 설정 하고 설정 파일 에 있 는 spring.datasource 를 데이터 원본 으로 설정 하여 SqlSession Factory Bean 을 생 성 합 니 다.마지막 으로 SqlSession Template 를 설정 합 니 다.
@Configuration
@MapperScan(basePackages = "com.xxx.mysql.mapper", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
@Bean(name = "primaryDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druid() {
return new DruidDataSource();
}
@Bean(name = "primarySqlSessionFactory")
@Primary
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
bean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return bean.getObject();
}
@Bean("primarySqlSessionTemplate")
@Primary
public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory sessionFactory) {
return new SqlSessionTemplate(sessionFactory);
}
}
그 다음 에 같은 절차 에 따라 Oacle 기반 데이터 원본 을 설정 하고 주 해 를 통 해 basePackages 스 캔 에 대응 하 는 패 키 지 를 설정 하여 특정한 패 키 지 를 사용 하 는 mapper 인 터 페 이 스 를 실현 합 니 다.
@Configuration
@MapperScan(basePackages = "com.nbclass.oracle.mapper", sqlSessionFactoryRef = "oracleSqlSessionFactory")
public class OracleDataSourceConfig {
@Bean(name = "oracleDataSource")
@ConfigurationProperties(prefix = "spring.secondary")
public DataSource oracleDruid(){
return new DruidDataSource();
}
@Bean(name = "oracleSqlSessionFactory")
public SqlSessionFactory oracleSqlSessionFactory(@Qualifier("oracleDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:oracle/mapper/*.xml"));
return bean.getObject();
}
@Bean("oracleSqlSessionTemplate")
public SqlSessionTemplate oracleSqlSessionTemplate(@Qualifier("oracleSqlSessionFactory") SqlSessionFactory sessionFactory) {
return new SqlSessionTemplate(sessionFactory);
}
}
이렇게 하면 한 프로젝트 에서 여러 개의 데이터 소스 를 사용 하 는 기능 을 실현 할 수 있 습 니 다.이런 실현 방식 에 대해 서도 충분히 간단 합 니 다.그러나 만약 에 우리 의 데이터 베이스 인 스 턴 스 가 많 고 모든 인 스 턴 스 가 설정 을 위주 로 한다 면 여기 서 유지 하면 가방 이름 이 너무 많 고 유연성 이 부족 할 수 있 습 니 다.현재 업무 에 대한 침입 이 충분 하고 mapper 방법 입도 에서 지정 한 데이터 원본 을 지원 할 수 있 는 방안 을 고려 하고 있 습 니 다.자 연 스 럽 게 주 해 를 통 해 이 루어 질 수 있다 는 생각 이 들 었 습 니 다.먼저 주 해 를 사용자 정의@DBKey:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DBKey {
String DEFAULT = "default"; //
String value() default DEFAULT;
}
사고방식 은 위 에서 springboot 원생 설정 을 바탕 으로 하 는 것 과 유사 합 니 다.먼저 기본 데이터베이스 노드 를 정의 합 니 다.mapper 인터페이스 방법/클래스 가 아무런 주석 이 지정 되 지 않 았 을 때 이 노드 를 기본 으로 갑 니 다.주석 지원 은 value 파라미터 가 선택 한 데이터 원본 노드 이름 을 표시 합 니 다.주해 의 실현 논리 에 대해 서 는 반사 로 mapper 인터페이스 방법/클래스 의 주해 값 을 얻 은 다음 에 특정한 데이터 원본 을 지정 할 수 있 습 니 다.그럼 이 조작 을 언제 실행 해서 가 져 올 까요?spring AOP 를 사용 하여 mapper 층 에 짜 는 것 을 고려 할 수 있 습 니 다.접점 에서 구체 적 인 mapper 방법 을 실행 하기 전에 해당 하 는 데이터 원본 설정 을 threa Local 에 넣 으 면 이 논리 가 있 으 면 즉시 실행 합 니 다.
우선,db 설정 의 상하 문 대상 을 정의 합 니 다.모든 데이터 원본 key 인 스 턴 스 를 유지 하고 현재 스 레 드 에서 사용 하 는 데이터 원본 key:
public class DBContextHolder {
private static final ThreadLocal<String> DB_KEY_CONTEXT = new ThreadLocal<>();
// app ,
private static Set<String> allDBKeys = new HashSet<>();
public static String getDBKey() {
return DB_KEY_CONTEXT.get();
}
public static void setDBKey(String dbKey) {
//key
if (containKey(dbKey)) {
DB_KEY_CONTEXT.set(dbKey);
} else {
throw new KeyNotFoundException("datasource[" + dbKey + "] not found!");
}
}
public static void addDBKey(String dbKey) {
allDBKeys.add(dbKey);
}
public static boolean containKey(String dbKey) {
return allDBKeys.contains(dbKey);
}
public static void clear() {
DB_KEY_CONTEXT.remove();
}
}
그 다음 에 접점 을 정의 합 니 다.접점 before 방법 에서 현재 mapper 인터페이스의@DBKey 주석 에 따라 해당 하 는 데이터 원본 key 를 선택 합 니 다.
@Aspect
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public class DSAdvice implements BeforeAdvice {
@Pointcut("execution(* com.xxx..*.repository.*.*(..))")
public void daoMethod() {
}
@Before("daoMethod()")
public void beforeDao(JoinPoint point) {
try {
innerBefore(point, false);
} catch (Exception e) {
logger.error("DefaultDSAdviceException",
"Failed to set database key,please resolve it as soon as possible!", e);
}
}
/**
* @param isClass
*/
public void innerBefore(JoinPoint point, boolean isClass) {
String methodName = point.getSignature().getName();
Class<?> clazz = getClass(point, isClass);
//
String dbKey = DBKey.DEFAULT;
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
Method method = null;
try {
method = clazz.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
throw new RuntimeException("can't find " + methodName + " in " + clazz.toString());
}
// , datasource
if (method.isAnnotationPresent(DBKey.class)) {
DBKey key = method.getAnnotation(DBKey.class);
dbKey = key.value();
} else {
// ,
clazz = method.getDeclaringClass();
if (clazz.isAnnotationPresent(DBKey.class)) {
DBKey key = clazz.getAnnotation(DBKey.class);
dbKey = key.value();
}
}
DBContextHolder.setDBKey(dbKey);
}
private Class<?> getClass(JoinPoint point, boolean isClass) {
Object target = point.getTarget();
String methodName = point.getSignature().getName();
Class<?> clazz = target.getClass();
if (!isClass) {
Class<?>[] clazzList = target.getClass().getInterfaces();
if (clazzList == null || clazzList.length == 0) {
throw new MutiDBException(" mapper class,methodName =" + methodName);
}
clazz = clazzList[0];
}
return clazz;
}
}
mapper 를 실행 하기 전에 이 mapper 인터페이스 에서 최종 적 으로 사용 하 는 데이터 원본 이 thread Local 에 들 어 갔 으 므 로 새로운 경로 데이터 원본 인터페이스 논 리 를 다시 쓰 면 됩 니 다.
public class RoutingDatasource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
String dbKey = DBContextHolder.getDBKey();
return dbKey;
}
@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
for (Object key : targetDataSources.keySet()) {
DBContextHolder.addDBKey(String.valueOf(key));
}
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
}
또한,저 희 는 서비스 가 시작 되 고 my batis 를 설정 할 때 모든 db 설정 을 불 러 옵 니 다:
@Bean
@ConditionalOnMissingBean(DataSource.class)
@Autowired
public DataSource dataSource(MybatisProperties mybatisProperties) {
Map<Object, Object> dsMap = new HashMap<>(mybatisProperties.getNodes().size());
for (String nodeName : mybatisProperties.getNodes().keySet()) {
dsMap.put(nodeName, buildDataSource(nodeName, mybatisProperties));
DBContextHolder.addDBKey(nodeName);
}
RoutingDatasource dataSource = new RoutingDatasource();
dataSource.setTargetDataSources(dsMap);
if (null == dsMap.get(DBKey.DEFAULT)) {
throw new RuntimeException(
String.format("Default DataSource [%s] not exists", DBKey.DEFAULT));
}
dataSource.setDefaultTargetDataSource(dsMap.get(DBKey.DEFAULT));
return dataSource;
}
@ConfigurationProperties(prefix = "mybatis")
@Data
public class MybatisProperties {
private Map<String, String> params;
private Map<String, Object> nodes;
/**
* mapper : location ,
*/
private String mapperLocations = "classpath*:com/iqiyi/xiu/**/mapper/*.xml";
/**
* Mapper base package
*/
private String basePackage = "com.iqiyi.xiu.**.repository";
/**
* mybatis
*/
private String configLocation = "classpath:mybatis-config.xml";
}
그럼 thread Local 의 key 는 언제 소각 합 니까?사실은 my batis 기반 차단 기 를 사용자 정의 할 수 있 습 니 다.차단기 에서 DBContextHolder.clear()방법 으로 이 key 를 소각 할 수 있 습 니 다.구체 적 인 코드 는 붙 이지 않 겠 습 니 다.이렇게 되면 우 리 는 주 해 를 바탕 으로 다 중 데이터 소스 전환 을 지원 하 는 미들웨어 를 완성 했다.그럼 최적화 할 점 이 있 나 요?사실은 mapper 인터페이스/소재 클래스 의 주 해 를 가 져 올 때 반 사 를 사용 하여 얻 은 것 을 발견 할 수 있 습 니 다.그러면 우 리 는 일반 반사 호출 이 비교적 성능 을 소모 한 다 는 것 을 알 기 때문에 여기에 로 컬 캐 시 를 추가 하여 성능 을 최적화 하 는 것 을 고려 할 수 있 습 니 다.
private final static Map<String, String> METHOD_CACHE = new ConcurrentHashMap<>();
//....
public void innerBefore(JoinPoint point, boolean isClass) {
String methodName = point.getSignature().getName();
Class<?> clazz = getClass(point, isClass);
//key +
String keyString = clazz.toString() + methodName;
//
String dbKey = DBKey.DEFAULT;
// mapper key,
if (METHOD_CACHE.containsKey(keyString)) {
dbKey = METHOD_CACHE.get(keyString);
} else {
Class<?>[] parameterTypes =
((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
Method method = null;
try {
method = clazz.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
throw new RuntimeException("can't find " + methodName + " in " + clazz.toString());
}
// , datasource
if (method.isAnnotationPresent(DBKey.class)) {
DBKey key = method.getAnnotation(DBKey.class);
dbKey = key.value();
} else {
clazz = method.getDeclaringClass();
//
if (clazz.isAnnotationPresent(DBKey.class)) {
DBKey key = clazz.getAnnotation(DBKey.class);
dbKey = key.value();
}
}
//
METHOD_CACHE.put(keyString, dbKey);
}
DBContextHolder.setDBKey(dbKey);
}
이렇게 되면 이 mapper 인 터 페 이 스 를 처음 호출 할 때 만 반사 호출 논리 로 대응 하 는 데이터 원본 을 가 져 올 수 있 고 그 다음 에는 로 컬 캐 시 를 사용 하여 성능 을 향상 시 킬 수 있 습 니 다.주 해 를 기반 으로 한 spring boot+my batis 의 다 중 데이터 소스 구성 요소 구현 코드 에 관 한 글 은 여기까지 입 니 다.더 많은 spring boot my batis 다 중 데이터 소스 구성 요소 내용 은 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 지원 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin Springboot -- 파트 14 사용 사례 REST로 전환하여 POST로 JSON으로 전환前回 前回 前回 記事 の は は で で で で で で を 使っ 使っ 使っ て て て て て リクエスト を を 受け取り 、 reqeustbody で 、 その リクエスト の ボディ ボディ を を 受け取り 、 関数 内部 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.