Spring Boot 를 통 해 동적 데이터 원본 을 설정 하여 여러 데이터베이스 에 접근 하 는 실현 코드

이전에 블 로그'Spring+Mybatis+Mysql 분포 식 데이터베이스 액세스 프레임 워 크 구축'를 통 해 Spring+Mybatis 를 통 해 동적 데이터 원본 을 설정 하여 여러 데이터 베 이 스 를 방문 하 는 방법 을 설명 한 적 이 있다.그러나 이전 방안 은 일부 제한 이 있 었 다.데이터베이스 가 동적 으로 증가 하 는 상황 에 대해 서 는 어 쩔 수 없습니다.
아래 에 설명 한 방안 은 데이터 베 이 스 를 동적 으로 삭제 할 수 있 고 수량 에 제한 이 없다.
데이터베이스 환경 준비
다음 Mysql 의 경우 로 컬 에 3 개의 데이터 베 이 스 를 만들어 테스트 에 사용 합 니 다.설명 이 필요 한 것 은 이 방안 은 데이터 베 이 스 를 제한 하지 않 고 서로 다른 데이터 베 이 스 를 서로 다른 서버 에 배치 하 는 것 을 지원 합 니 다.그림 에서 보 듯 이 dbproject_001、db_project_002、db_project_003。

자바 백 스테이지 마이크로 서비스 프로젝트 구축
Spring Boot 의 maven 프로젝트 만 들 기:

config:데이터 원본 설정 관리 클래스.
datasource:자신 이 실현 한 데이터 소스 관리 논리.
dbmgr:프로젝트 인 코딩 과 데이터베이스 IP,이름 의 맵 관 계 를 관리 합 니 다.
mapper:데이터베이스 액세스 인터페이스.
model:맵 모델.
rest:마이크로 서비스 가 대외 적 으로 발표 한 restful 인 터 페 이 스 를 테스트 합 니 다.
application.yml:데이터베이스 의 JDBC 인 자 를 설정 하 였 습 니 다.
상세 한 코드 구현
1.데이터 원본 설정 추가

package com.elon.dds.config;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.elon.dds.datasource.DynamicDataSource;
/**
 *        。
 *
 * @author elon
 * @version 2018 2 26 
 */
@Configuration
@MapperScan(basePackages="com.elon.dds.mapper", value="sqlSessionFactory")
public class DataSourceConfig {
 /**
 *            。       。
 *
 * @return    
 */
 @Bean(name="dataSource")
 @ConfigurationProperties(prefix="spring.datasource")
 public DataSource getDataSource() {
 DataSourceBuilder builder = DataSourceBuilder.create();
 builder.type(DynamicDataSource.class);
 return builder.build();
 }
 /**
 *       。
 *
 * @param dataSource    
 * @return     
 */
 @Bean(name="sqlSessionFactory")
 public SqlSessionFactory getSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) {
 SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
 bean.setDataSource(dataSource);
 try {
  return bean.getObject();
 } catch (Exception e) {
  e.printStackTrace();
  return null;
 }
 }
}
2.동적 데이터 원본 정의
1)  먼저 데이터베이스 표지 류 를 추가 하여 서로 다른 데이터 베 이 스 를 구분 하 는 데 사용 합 니 다.
우 리 는 서로 다른 프로젝트 를 위해 단독 데이터 베 이 스 를 만 들 었 기 때문에 프로젝트 인 코딩 을 데이터베이스 색인 으로 사용 합 니 다.한편,마이크로 서 비 스 는 다 중 스 레 드 병발 을 지원 하고 스 레 드 변 수 를 사용 합 니 다.

package com.elon.dds.datasource;
/**
 *         。               。
 *
 * @author elon
 * @version 2018-02-25
 */
public class DBIdentifier {
 /**
 *               
 */
 private static ThreadLocal<String> projectCode = new ThreadLocal<String>();
 public static String getProjectCode() {
 return projectCode.get();
 }
 public static void setProjectCode(String code) {
 projectCode.set(code);
 }
}
2)  DataSource 에서 DynamicDataSource 를 파생 하여 데이터베이스 연결 의 동적 전환 을 실현 합 니 다.

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import com.elon.dds.dbmgr.ProjectDBMgr;
/**
 *           。    DataSource  ,       。
 *
 * @author elon
 * @version 2018-02-25
 */
public class DynamicDataSource extends DataSource {
 private static Logger log = LogManager.getLogger(DynamicDataSource.class);
 /**
 *                             。
 */
 @Override
 public Connection getConnection(){
 String projectCode = DBIdentifier.getProjectCode();
 //1、     
 DataSource dds = DDSHolder.instance().getDDS(projectCode);
 //2、           
 if (dds == null) {
  try {
  DataSource newDDS = initDDS(projectCode);
  DDSHolder.instance().addDDS(projectCode, newDDS);
  } catch (IllegalArgumentException | IllegalAccessException e) {
  log.error("Init data source fail. projectCode:" + projectCode);
  return null;
  }
 }
 dds = DDSHolder.instance().getDDS(projectCode);
 try {
  return dds.getConnection();
 } catch (SQLException e) {
  e.printStackTrace();
  return null;
 }
 }
 /**
 *                。
 *
 * @return dds
 * @throws IllegalAccessException
 * @throws IllegalArgumentException
 */
 private DataSource initDDS(String projectCode) throws IllegalArgumentException, IllegalAccessException {
 DataSource dds = new DataSource();
 // 2、  PoolConfiguration   
 PoolProperties property = new PoolProperties();
 Field[] pfields = PoolProperties.class.getDeclaredFields();
 for (Field f : pfields) {
  f.setAccessible(true);
  Object value = f.get(this.getPoolProperties());
  try
  {
  f.set(property, value);  
  }
  catch (Exception e)
  {
  log.info("Set value fail. attr name:" + f.getName());
  continue;
  }
 }
 dds.setPoolProperties(property);
 // 3、        IP(    ,      、         )
 String urlFormat = this.getUrl();
 String url = String.format(urlFormat, ProjectDBMgr.instance().getDBIP(projectCode),
  ProjectDBMgr.instance().getDBName(projectCode));
 dds.setUrl(url);
 return dds;
 }
}
3)  DDSTimer 를 통 해 데이터 연결 방출 제어(지 정 된 시간 을 초과 하여 사용 하지 않 은 데이터 원본 방출)

package com.elon.dds.datasource;
import org.apache.tomcat.jdbc.pool.DataSource;
/**
 *           。              。
 *
 * @author elon
 * @version 2018 2 25 
 */
public class DDSTimer {
 /**
 *       。                    。   10  。
 */
 private static long idlePeriodTime = 10 * 60 * 1000;
 /**
 *      
 */
 private DataSource dds;
 /**
 *         
 */
 private long lastUseTime;
 public DDSTimer(DataSource dds) {
 this.dds = dds;
 this.lastUseTime = System.currentTimeMillis();
 }
 /**
 *         
 */
 public void refreshTime() {
 lastUseTime = System.currentTimeMillis();
 }
 /**
 *             。
 *
 * @return true-     ; false-   
 */
 public boolean checkAndClose() {
 if (System.currentTimeMillis() - lastUseTime > idlePeriodTime)
 {
  dds.close();
  return true;
 }
 return false;
 }
 public DataSource getDds() {
 return dds;
 }
}
4)      DDSHolder 를 추가 하여 서로 다른 데이터 원본 을 관리 하고 데이터 원본 의 추가,조회 기능 을 제공 합 니 다.

package com.elon.dds.datasource;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import org.apache.tomcat.jdbc.pool.DataSource;
/**
 *         。
 *
 * @author elon
 * @version 2018 2 25 
 */
public class DDSHolder {
 /**
 *          。<    ,   >
 */
 private Map<String, DDSTimer> ddsMap = new HashMap<String, DDSTimer>();
 /**
 *                   
 */
 private static Timer clearIdleTask = new Timer();
 static {
 clearIdleTask.schedule(new ClearIdleTimerTask(), 5000, 60 * 1000);
 };
 private DDSHolder() {
 }
 /*
 *       
 */
 public static DDSHolder instance() {
 return DDSHolderBuilder.instance;
 }
 /**
 *        。
 *
 * @param projectCode     
 * @param dds dds
 */
 public synchronized void addDDS(String projectCode, DataSource dds) {
 DDSTimer ddst = new DDSTimer(dds);
 ddsMap.put(projectCode, ddst);
 }
 /**
 *        
 *
 * @param projectCode     
 * @return dds
 */
 public synchronized DataSource getDDS(String projectCode) {
 if (ddsMap.containsKey(projectCode)) {
  DDSTimer ddst = ddsMap.get(projectCode);
  ddst.refreshTime();
  return ddst.getDds();
 }
 return null;
 }
 /**
 *             。
 */
 public synchronized void clearIdleDDS() {
 Iterator<Entry<String, DDSTimer>> iter = ddsMap.entrySet().iterator();
 for (; iter.hasNext(); ) {
  Entry<String, DDSTimer> entry = iter.next();
  if (entry.getValue().checkAndClose())
  {
  iter.remove();
  }
 }
 }
 /**
 *      
 * @author elon
 * @version 2018 2 26 
 */
 private static class DDSHolderBuilder {
 private static DDSHolder instance = new DDSHolder();
 }
}
5)      타이머 작업 ClearIdleTimer Task 는 남 은 데이터 원본 을 정시 에 제거 하 는 데 사 용 됩 니 다.

package com.elon.dds.datasource;
import java.util.TimerTask;
/**
 *         。
 *
 * @author elon
 * @version 2018 2 26 
 */
public class ClearIdleTimerTask extends TimerTask {
 @Override
 public void run() {
 DDSHolder.instance().clearIdleDDS();
 }
}
3.       프로젝트 인 코딩 과 데이터베이스 IP 와 이름 의 매 핑 관 계 를 관리 합 니 다.

package com.elon.dds.dbmgr;
import java.util.HashMap;
import java.util.Map;
/**
 *        。                IP   。
 * @author elon
 * @version 2018 2 25 
 */
public class ProjectDBMgr {
 /**
 *                 。      ,                redis   ;
 *                      。                    。
 */
 private Map<String, String> dbNameMap = new HashMap<String, String>();
 /**
 *           IP     。
 */
 private Map<String, String> dbIPMap = new HashMap<String, String>();
 private ProjectDBMgr() {
 dbNameMap.put("project_001", "db_project_001");
 dbNameMap.put("project_002", "db_project_002");
 dbNameMap.put("project_003", "db_project_003");
 dbIPMap.put("project_001", "127.0.0.1");
 dbIPMap.put("project_002", "127.0.0.1");
 dbIPMap.put("project_003", "127.0.0.1");
 }
 public static ProjectDBMgr instance() {
 return ProjectDBMgrBuilder.instance;
 }
 //             
 public String getDBName(String projectCode) {
 if (dbNameMap.containsKey(projectCode)) {
  return dbNameMap.get(projectCode);
 }
 return "";
 }
 //             
 public String getDBIP(String projectCode) {
 if (dbIPMap.containsKey(projectCode)) {
  return dbIPMap.get(projectCode);
 }
 return "";
 }
 private static class ProjectDBMgrBuilder {
 private static ProjectDBMgr instance = new ProjectDBMgr();
 }
}
4.       데이터베이스 접근 을 정의 하 는 mapper

package com.elon.dds.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.elon.dds.model.User;
/**
 * Mybatis      。
 *
 * @author elon
 * @version 2018 2 26 
 */
@Mapper
public interface UserMapper
{
 /**
 *         
 * @return       
 */
 @Results(value= {
  @Result(property="userId", column="id"),
  @Result(property="name", column="name"),
  @Result(property="age", column="age")
 })
 @Select("select id, name, age from tbl_user")
 List<User> getUsers();
}
5.       정의 조회 대상 모델

package com.elon.dds.model;
public class User
{
 private int userId = -1;
 private String name = "";
 private int age = -1;
 @Override
 public String toString()
 {
 return "name:" + name + "|age:" + age;
 }
 public int getUserId()
 {
 return userId;
 }
 public void setUserId(int userId)
 {
 this.userId = userId;
 }
 public String getName()
 {
 return name;
 }
 public void setName(String name)
 {
 this.name = name;
 }
 public int getAge()
 {
 return age;
 }
 public void setAge(int age)
 {
 this.age = age;
 }
}
6.       사용자 데 이 터 를 조회 하 는 restful 인 터 페 이 스 를 정의 합 니 다.

package com.elon.dds.rest;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.elon.dds.datasource.DBIdentifier;
import com.elon.dds.mapper.UserMapper;
import com.elon.dds.model.User;
/**
 *         。
 *
 * @author elon
 * @version 2018 2 26 
 */
@RestController
@RequestMapping(value="/user")
public class WSUser {
 @Autowired
 private UserMapper userMapper;
 /**
 *            
 *
 * @param projectCode     
 * @return     
 */
 @RequestMapping(value="/v1/users", method=RequestMethod.GET)
 public List<User> queryUser(@RequestParam(value="projectCode", required=true) String projectCode)
 {
 DBIdentifier.setProjectCode(projectCode);
 return userMapper.getUsers();
 }
}
매번 조회 할 때마다 procject Code 인 자 를 가 져 오 라 고 요구 합 니 다.
 7.       Spring Boot App 의 시작 코드 를 작성 합 니 다.

package com.elon.dds;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * Hello world!
 *
 */
@SpringBootApplication
public class App
{
 public static void main( String[] args )
 {
 System.out.println( "Hello World!" );
 SpringApplication.run(App.class, args);
 }
}
8.       application.yml 에 데이터 원본 설정
데이터베이스 IP 와 데이터베이스 이름 은%s 를 사용 합 니 다.조회 사용자 데이터 에서 동적 전환.

spring:
 datasource:
 url: jdbc:mysql://%s:3306/%s?useUnicode=true&characterEncoding=utf-8
 username: root
 password:
 driver-class-name: com.mysql.jdbc.Driver
logging:
 config: classpath:log4j2.xml
테스트 방안
1.       조회 project001 데이터,정상 반환 

2.       조회 project002 데이터,정상 반환

총결산
위 에서 말씀 드 린 것 은 스프링 부 트 를 통 해 동적 데이터 원본 을 설정 하여 여러 데이터 베 이 스 를 방문 하 는 실현 코드 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 메 시 지 를 남 겨 주세요.편집장 은 신속하게 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기