作者 hekf2021

多数据库动态切换

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com</groupId>
<artifactId>autodatasource</artifactId>
<version>1.0</version>
<name>autodatasource</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
... ...
CREATE TABLE aberrant
(
aberrant_id INT NOT NULL AUTO_INCREMENT,
order_no INT,
reason VARCHAR(64),
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NULL,
PRIMARY KEY (aberrant_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO aberrant (aberrant_id, order_no, reason, created) VALUES (1, 3, 'ds01', '2018-06-27 16:37:39');
INSERT INTO aberrant (aberrant_id, order_no, reason, created) VALUES (2, 2, 'fagrgas', '2018-06-21 16:28:25');
INSERT INTO aberrant (aberrant_id, order_no, reason, created) VALUES (6, 4, 'fdsafagsa', '2018-06-28 09:40:35');
INSERT INTO aberrant (aberrant_id, order_no, reason, created) VALUES (7, 1, 'aaa', '2018-06-28 09:40:35');
INSERT INTO aberrant (aberrant_id, order_no, reason, created) VALUES (8, 2, '333', '2018-07-02 21:57:47');
INSERT INTO aberrant (aberrant_id, order_no, reason, created) VALUES (2323, 1, 'dddd', '2018-07-17 22:12:30');
... ...
package com.eck.auto;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Import;
@EnableAutoConfiguration
@EntityScan(basePackages = "com.eck.auto.model")
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
... ...
package com.eck.auto;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
}
}
... ...
package com.eck.auto.config;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Order(1)
@Configuration
public class DataSourceAspect {
private static final String dsNo="dsNo";//数据库编号 从header中取
/**
* 切入点,放在controller的每个方法上进行切入,更新数据源
*/
@Pointcut("execution(* com.eck.auto.controller..*.*(..))")
private void anyMethod(){}//定义一个切入点
@Before("anyMethod()")
public void dataSourceChange()
{
//请求头head中获取对应数据库编号
String no = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader(dsNo);
System.out.print("当前数据源编号:"+no);
if(StringUtils.isEmpty(no)){
//TODO 根据业务抛异常
}
DataSourceHolder.setDataSource(no);
/*这里数据库项目编号来更改对应的数据源*/
}
}
... ...
package com.eck.auto.config;
public class DataSourceHolder {
//线程本地环境
private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
//设置数据源,动态切换,就是调用这个setDataSource方法
public static void setDataSource(String customerType) {
dataSources.set(customerType);
}
//获取数据源
public static String getDataSource() {
return (String) dataSources.get();
}
//清除数据源
public static void clearDataSource() {
dataSources.remove();
}
}
... ...
package com.eck.auto.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 每次请求动态请求哪一个数据源
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
public DynamicDataSource(){
Map<Object, Object> dataSources=new ConcurrentHashMap<>();
for(int i=1;i<=4;i++){
DataSource dataSource = druidDataSource(i);
dataSources.put(String.valueOf(i),dataSource);
if(i==1){
super.setDefaultTargetDataSource(dataSource);
}
}
super.setTargetDataSources(dataSources);
}
/**
* 此处数据库信配置,可以来源于redis等,然后再初始化所有数据源
* 重点说明:一个DruidDataSource数据源,它里面本身就是线程池了,
* 所以我们不需要考虑线程池的问题
* @param no
* @return
*/
public DataSource druidDataSource(int no) {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl("jdbc:mysql://localhost:3306/ds0"+no);
datasource.setUsername("root");
datasource.setPassword("123456");
datasource.setDriverClassName("com.mysql.jdbc.Driver");
datasource.setInitialSize(5);
datasource.setMinIdle(5);
datasource.setMaxActive(20);
//datasource.setDbType("com.alibaba.druid.pool.DruidDataSource");
datasource.setMaxWait(60000);
datasource.setTimeBetweenEvictionRunsMillis(60000);
datasource.setMinEvictableIdleTimeMillis(300000);
datasource.setValidationQuery("SELECT 1 FROM DUAL");
datasource.setTestWhileIdle(true);
datasource.setTestOnBorrow(false);
datasource.setTestOnReturn(false);
try {
datasource.setFilters("stat,wall,log4j");
} catch (SQLException e) {
e.printStackTrace();
}
return datasource;
}
}
... ...
package com.eck.auto.controller;
import com.eck.auto.config.DataSourceHolder;
import com.eck.auto.model.Aberrant;
import com.eck.auto.service.DataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/app")
public class DataController {
@Autowired
private DataService dataService;
@RequestMapping(value="/cc",method = RequestMethod.GET)
public List<Aberrant> cc(Integer id) {
List<Aberrant> list = dataService.find(id);
System.out.println(list);
return list;
}
@RequestMapping(value="/cc1",method = RequestMethod.GET)
public List<Aberrant> cc1(Integer id) {
DataSourceHolder.setDataSource("1");
List<Aberrant> list = dataService.find(id);
System.out.println(list);
return list;
}
@RequestMapping(value="/cc2",method = RequestMethod.GET)
public List<Aberrant> cc2(Integer id) {
DataSourceHolder.setDataSource("2");
List<Aberrant> list = dataService.find(id);
System.out.println(list);
return list;
}
@RequestMapping(value="/cc3",method = RequestMethod.GET)
public List<Aberrant> cc3(Integer id) {
DataSourceHolder.setDataSource("3");
List<Aberrant> list = dataService.find(id);
System.out.println(list);
return list;
}
@RequestMapping(value="/update",method = RequestMethod.GET)
public String update(Integer id) {
dataService.upate();
return "ok";
}
}
... ...
package com.eck.auto.dao;
import com.eck.auto.model.Aberrant;
import java.util.List;
public interface AberrantDao{
List<Aberrant> findAll();
public int update();
}
... ...
package com.eck.auto.dao;
/**
* Created by Administrator on 2018/7/21.
*/
public interface DataDao {
}
... ...
package com.eck.auto.dao.impl;
import com.eck.auto.dao.AberrantDao;
import com.eck.auto.model.Aberrant;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.List;
@Repository
public class AberrantDaoImpl implements AberrantDao {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Aberrant> findAll() {
String hql="from Aberrant where aberrantId=:aberrantId ";
Query query=entityManager.createQuery(hql.toString());
query.setParameter("aberrantId",1);
List<Aberrant> lists=query.getResultList();
if(lists.size()>0){
return query.getResultList();
}else{
return null;
}
}
public int update(){
String hql="update Aberrant set orderNo=88 where aberrantId=:aberrantId ";
Query query=entityManager.createQuery(hql.toString());
query.setParameter("aberrantId",1);
return query.executeUpdate();
}
}
... ...
package com.eck.auto.dao.impl;
import com.eck.auto.dao.DataDao;
import org.springframework.stereotype.Repository;
//@Repository
public class DataDaoImpl implements DataDao {
}
... ...
package com.eck.auto.exception;
import com.eck.auto.model.vo.ResultInfoVo;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
@RestController
@ControllerAdvice
public class ExceptionHandlers {
@ExceptionHandler(MyException.class)
public ResponseEntity<ResultInfoVo> DataQualityException(MyException e) {
e.printStackTrace();
ResultInfoVo info = new ResultInfoVo(e.getHttpCode(),e.getCode(),e.getMessage());
return new ResponseEntity<ResultInfoVo>(info, HttpStatus.valueOf(e.getHttpCode()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ResultInfoVo> Exception(Exception e) {
e.printStackTrace();
ResultInfoVo vo = null;
if(e instanceof NullPointerException) {
vo = new ResultInfoVo(HttpStatus.INTERNAL_SERVER_ERROR.value(),"InternalServerError", "空指针异常");
}else{
vo = new ResultInfoVo(HttpStatus.INTERNAL_SERVER_ERROR.value(),"InternalServerError",e.getMessage());
}
return new ResponseEntity<ResultInfoVo>(vo, HttpStatus.valueOf(500));
}
}
... ...
package com.eck.auto.exception;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* 统一处理错误异常消息格式
*/
public class MyException extends RuntimeException {
private Log logger = LogFactory.getLog(getClass());
private int httpCode;//http错误码
private String code;//业务code:成都为success,失败为其它业务,如roleIdIsNull")
private String message;//错误详情
private Object data;//其它数据
public MyException(int httpCode, String code, String message) {
this.httpCode = httpCode;
this.code = code;
this.message = message;
}
public int getHttpCode() {
return httpCode;
}
public void setHttpCode(int httpCode) {
this.httpCode = httpCode;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
String log=" httpCode:"+ httpCode +"\n code:"+code+"\n message:"+ message;
logger.debug(log);
return log;
}
}
... ...
package com.eck.auto.model;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
/**
* The persistent class for the aberrant database table.
*
*/
@Entity
@Table(name="aberrant")
public class Aberrant implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="aberrant_id")
private Integer aberrantId;
@Temporal(TemporalType.TIMESTAMP)
private Date created;
@Column(name="order_no")
private Integer orderNo;
private String reason;
public Integer getAberrantId() {
return this.aberrantId;
}
public void setAberrantId(Integer aberrantId) {
this.aberrantId = aberrantId;
}
public Date getCreated() {
return this.created;
}
public void setCreated(Date created) {
this.created = created;
}
public Integer getOrderNo() {
return this.orderNo;
}
public void setOrderNo(Integer orderNo) {
this.orderNo = orderNo;
}
public String getReason() {
return this.reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
\ No newline at end of file
... ...
package com.eck.auto.model.vo;
public class ResultInfoVo<T> {
public static final String SUCCESS="success";
private Integer httpCode=200;//httpCode 成功为200
private String code="success";//业务code 成功为 success 失败为 其它业务编号,如paramIsNull
private String message="处理成功";//描述信息
public T data;//页数据
public ResultInfoVo(){}
public ResultInfoVo(T data) {
this.data = data;
}
public ResultInfoVo(Integer httpCode, String code, String message) {
this.httpCode = httpCode;
this.code = code;
this.message = message;
}
public ResultInfoVo(Integer httpCode, String code, String message, T data) {
this.httpCode = httpCode;
this.code = code;
this.message = message;
this.data = data;
}
public Integer getHttpCode() {
return httpCode;
}
public void setHttpCode(Integer httpCode) {
this.httpCode = httpCode;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
... ...
package com.eck.auto.service;
import com.eck.auto.model.Aberrant;
import java.util.List;
/**
* Created by Administrator on 2018/7/21.
*/
public interface DataService {
public List<Aberrant> find(Integer id);
public void upate();
}
... ...
package com.eck.auto.service.impl;
import com.eck.auto.dao.AberrantDao;
import com.eck.auto.dao.DataDao;
import com.eck.auto.exception.MyException;
import com.eck.auto.model.Aberrant;
import com.eck.auto.service.DataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* Created by Administrator on 2018/7/21.
*/
@Service
public class DataServiceImpl implements DataService {
//@Autowired
private DataDao dataDao;
@Autowired
private AberrantDao aberrantDao;
public List<Aberrant> find(Integer id){
List<Aberrant> list = aberrantDao.findAll();
return list;
}
/**
* 测试事务
*/
@Transactional
public void upate() {
aberrantDao.update();
if(1==1){
throw new MyException(HttpStatus.OK.value(),"xxxxx","主动异常");
}
}
}
... ...
package com.eck.auto.test;
import com.eck.auto.utils.HttpRequest;
/**
* Created by Administrator on 2018/7/22.
*/
public class MyThread extends Thread {
int dsNo;
String ds;
String url;
public MyThread(int no,String ds,String url){
this.dsNo =no;
this.ds=ds;
this.url=url;
}
@Override
public void run() {
for(int i = 0; i< dsNo; i++) {
String result1 = HttpRequest.sendGet(url);
System.out.println(ds + "===" + result1);
}
}
}
... ...
package com.eck.auto.test;
import com.eck.auto.utils.HttpRequest;
import java.util.Scanner;
/**
* Created by Administrator on 2018/7/22.
*/
public class Test {
public static void main(String[] args){
String url1="http://localhost:8080/app/hekf/1";
String url2="http://localhost:8080/all/lp/2";
MyThread mt1 = new MyThread(10000,"ds1",url1);
MyThread mt2 = new MyThread(10000,"ds2",url2);
MyThread mt3 = new MyThread(10010,"ds3",url1);
mt1.start();
mt2.start();
mt3.start();
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()){
String str =scanner.next();
System.out.println("当前输入:"+str);
}
}
}
... ...
package com.eck.auto.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class HttpRequest {
/**
* 向指定URL发送GET方法的请求
*
* @param url
* 发送请求的URL
* @param param
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url;
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际的连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍历所有的响应头字段
for (String key : map.keySet()) {
System.out.println(key + "--->" + map.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url
* 发送请求的 URL
* @param param
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!"+e);
e.printStackTrace();
}
//使用finally块来关闭输出流、输入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
}
\ No newline at end of file
... ...
server:
port: 8082
spring:
application:
name: mydata
jpa:
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
#durid登陆账号密码
durid:
login:
username: root
password: 123456
... ...
log4j.rootLogger=DEBUG,ERROR,consoleAppender,logfile
# ConsoleAppender 输出
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%l]-[%p] %m%n
#文件输出:RollingFileAppender
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
#保存log文件路径
log4j.appender.logfile.File=${catalina.home}\\logs\\insoft-log.txt
# 默认为true,添加到末尾,false在每次启动时进行覆盖
log4j.appender.logfile.Append = true
# 一个log文件的大小,超过这个大小就又会生成1个日志 # KB ,MB,GB
log4j.appender.logfile.MaxFileSize = 10MB
log4j.appender.logfile.ImmediateFlush=false
# 最多保存20个文件备份
log4j.appender.logfile.MaxBackupIndex = 20
# 输出文件的格式
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n
# Druid
log4j.logger.druid.sql=ERROR,consoleAppender,logfile,DEBUG
log4j.logger.druid.sql.DataSource=INFO,DEBUG,consoleAppender,logfile
log4j.logger.druid.sql.Connection=INFO,DEBUG,consoleAppender,logfile
log4j.logger.druid.sql.Statement=INFO,DEBUG,consoleAppender,logfile
log4j.logger.druid.sql.ResultSet=INFO,DEBUG,consoleAppender,logfile
\ No newline at end of file
... ...
package com;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
}
... ...