目录
多数据源
application.yml
spring:
profiles:
active: test
application-test.yml
server:
tomcat:
connection-timeout: 60000
port: 9093
shutdown: graceful
cus:
default: t1
msg: t1
spring:
datasource:
t1:
driver-class-name: org.postgresql.Driver
jdbc-url: url
username: username
password: password
type: com.zaxxer.hikari.HikariDataSource
maximum-pool-size: 3
# 最小連接池
minimum-idle: 0
# 允許在連接池中保持空閒的最大時長
idle-timeout: 30000
# 存活時間
max-lifetime: 120000
auto-commit: true #開啟自動提交
# 自動回收鏈接的時間
leak-detection-threshold: 60000
pool-name: T1HikariCP
connection-timeout: 30000
connection-test-query: SELECT 1
t11:
driver-class-name: org.postgresql.Driver
jdbc-url: url
username: username
password: password
type: com.zaxxer.hikari.HikariDataSource
maximum-pool-size: 3
# 最小連接池
minimum-idle: 0
# 允許在連接池中保持空閒的最大時長
idle-timeout: 30000
# 存活時間
max-lifetime: 120000
auto-commit: true #開啟自動提交
# 自動回收鏈接的時間
leak-detection-threshold: 60000
pool-name: T11HikariCP
connection-timeout: 30000
connection-test-query: SELECT 1
t12:
driver-class-name: org.postgresql.Driver
jdbc-url: url
username: username
password: password.2024
type: com.zaxxer.hikari.HikariDataSource
maximum-pool-size: 3
# 最小連接池
minimum-idle: 0
# 允許在連接池中保持空閒的最大時長
idle-timeout: 30000
# 存活時間
max-lifetime: 120000
auto-commit: true #開啟自動提交
# 自動回收鏈接的時間
leak-detection-threshold: 60000
pool-name: T12HikariCP
connection-timeout: 30000
connection-test-query: SELECT 1
lifecycle:
timeout-per-shutdown-phase: 30s
redis:
# IP地址
host:
# 端口號
port: 6379
# 数据库索引
database: 0
# 密碼
password:
# 连接超时时间
connect-timeout: 10s
timeout: 5000ms
lettuce:
pool:
#连接池中的最小空闲连接
min-idle: 0
#连接池中的最大空闲连接(使用负值表示没有限制)
max-idle: 5
# 连接池的最大数据库连接数(使用负值表示没有限制)
max-active: 5
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
mvc:
# # 静态文件请求匹配方式
static-path-pattern: /**
web:
resources:
static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:*/js,classpath:/templates,classpath:/static/,classpath:/public/,classpath:/os/,classpath:/resources/static
# static-locations: class\ath:/templates
pathmatch:
matching-strategy: ant_path_matcher
logging:
config: classpath:logback-spring.xml
mybatis:
configuration:
call-setters-on-nulls: true
application-test1.yml
server:
tomcat:
connection-timeout: 60000
port: 9093
shutdown: graceful
cus:
default: t2
msg: t2
spring:
datasource:
t2:
driver-class-name: org.postgresql.Driver
jdbc-url: url
username: username
password: password
type: com.zaxxer.hikari.HikariDataSource
maximum-pool-size: 3
# 最小連接池
minimum-idle: 0
# 允許在連接池中保持空閒的最大時長
idle-timeout: 30000
# 存活時間
max-lifetime: 120000
auto-commit: true #開啟自動提交
# 自動回收鏈接的時間
leak-detection-threshold: 60000
pool-name: T2HikariCP
connection-timeout: 30000
connection-test-query: SELECT 1
t21:
driver-class-name: org.postgresql.Driver
jdbc-url: url
username: username
password: password
type: com.zaxxer.hikari.HikariDataSource
maximum-pool-size: 3
# 最小連接池
minimum-idle: 0
# 允許在連接池中保持空閒的最大時長
idle-timeout: 30000
# 存活時間
max-lifetime: 120000
auto-commit: true #開啟自動提交
# 自動回收鏈接的時間
leak-detection-threshold: 60000
pool-name: T21HikariCP
connection-timeout: 30000
connection-test-query: SELECT 1
t22:
driver-class-name: org.postgresql.Driver
jdbc-url: url
username: username
password: password.2024
type: com.zaxxer.hikari.HikariDataSource
maximum-pool-size: 3
# 最小連接池
minimum-idle: 0
# 允許在連接池中保持空閒的最大時長
idle-timeout: 30000
# 存活時間
max-lifetime: 120000
auto-commit: true #開啟自動提交
# 自動回收鏈接的時間
leak-detection-threshold: 60000
pool-name: T22HikariCP
connection-timeout: 30000
connection-test-query: SELECT 1
lifecycle:
timeout-per-shutdown-phase: 30s
redis:
# IP地址
host:
# 端口號
port: 6379
# 数据库索引
database: 0
# 密碼
password:
# 连接超时时间
connect-timeout: 10s
timeout: 5000ms
lettuce:
pool:
#连接池中的最小空闲连接
min-idle: 0
#连接池中的最大空闲连接(使用负值表示没有限制)
max-idle: 5
# 连接池的最大数据库连接数(使用负值表示没有限制)
max-active: 5
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
mvc:
# # 静态文件请求匹配方式
static-path-pattern: /**
web:
resources:
static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:*/js,classpath:/templates,classpath:/static/,classpath:/public/,classpath:/os/,classpath:/resources/static
# static-locations: class\ath:/templates
pathmatch:
matching-strategy: ant_path_matcher
logging:
config: classpath:logback-spring.xml
mybatis:
configuration:
call-setters-on-nulls: true
DataSourceAspect.java
注意:@Aspect标记一个类为切面;@Pointcut
注解通常与 @Aspect
注解一起使用,定义切点;@EnableAspectJAutoProxy
是一个 Spring 注解,用于启用基于 AspectJ 的 AOP(面向切面编程)。当你在 Spring 的配置类上使用这个注解时,Spring 会自动扫描和处理用 @Aspect
注解标记的类,并为这些类创建代理,以支持 AOP 功能。在 Spring Boot 应用中可以直接添加到 Spring Boot 启动类上,这样可以简化配置,并使得 AOP 功能在整个应用中生效;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DataSourceAspect {
@Value("${cus.default}")
private String db;
@Pointcut("execution(* com.example.controller.*.*(..))")
public void controllerMethods() {
}
@Before("controllerMethods()")
public void beforeControllerMethod() {
DataSourceContextHolder.setDataSource(db); // 设置默认数据源
}
@After("controllerMethods()")
public void afterControllerMethod() {
DataSourceContextHolder.setDataSource(db); // 设置回默认数据源
}
}
DataSourceConfig.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DataSourceConfig {
@Value("${cus.default}")
private String dbDefault;
@Bean(name = "t1")
@ConfigurationProperties(prefix = "spring.datasource.t1")
public DataSource dataSourceT1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "t11")
@ConfigurationProperties(prefix = "spring.datasource.t11")
public DataSource dataSourceT11() {
return DataSourceBuilder.create().build();
}
@Bean(name = "t12")
@ConfigurationProperties(prefix = "spring.datasource.t12")
public DataSource dataSourceT12() {
return DataSourceBuilder.create().build();
}
@Bean(name = "t2")
@ConfigurationProperties(prefix = "spring.datasource.t2")
public DataSource dataSourceT2() {
return DataSourceBuilder.create().build();
}
@Bean(name = "t21")
@ConfigurationProperties(prefix = "spring.datasource.t21")
public DataSource dataSourceT21() {
return DataSourceBuilder.create().build();
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
* @return
*/
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
// 配置多数据源
Map<Object, Object> dsMap = new HashMap<>();
dsMap.put("t1", dataSourceT1());
dsMap.put("t11", dataSourceT11());
dsMap.put("t12", dataSourceT12());
dsMap.put("t2", dataSourceT2());
dsMap.put("t21", dataSourceT21());
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(dsMap.get(dbDefault));
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
/**
* 配置多数据源后IOC中存在多个数据源了,事务管理器需要重新配置,不然不知道选择哪个数据源
* 事务管理器此时管理的数据源将是动态数据源dynamicDataSource
* 配置@Transactional注解
* @return
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
DataSourceContextHolder.java
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSource(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
public static String getDataSource() {
String dataSource = CONTEXT_HOLDER.get();
return dataSource;
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
DynamicDataSource.java
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
自定义注解说明数据源
注意:使用方式为在mapper中的方法上加@DynamicDS("数据源名")注解,在DynamicDataSourceAspect类中特殊处理后,@DynamicDS("")取默认数据源,@DynamicDS("1")取t11数据源,@DynamicDS("2")则取t12数据源
DynamicDS.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynamicDS {
String value() default "";
}
DynamicDataSourceAspect.java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DynamicDataSourceAspect {
@Value("${cus.default}")
private String defaultName;
@Before("@annotation(dynamicDS)")
public void before(JoinPoint joinPoint, DynamicDS dynamicDS) {
String suffix = dynamicDS.value();
String dataSourceName = defaultName + suffix;
// 动态设置数据源的逻辑
System.out.println("Using data source: " + dataSourceName);
DataSourceContextHolder.setDataSource(dataSourceName);
// 例如,通过 ThreadLocal 或其他方式设置当前数据源
}
@After("@annotation(dynamicDS)")
public void after(JoinPoint joinPoint, DynamicDS dynamicDS) {
// 清理当前线程中的数据源
DataSourceContextHolder.clearDataSource();
}
}
全局捕获异常
BaseErrorInfoInterface.java
public interface BaseErrorInfoInterface {
/**
* 错误码
*/
int getResultCode();
/**
* 错误描述
*/
String getResultMsg();
}
BusinessException.java
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = -4879677283847539655L;
private int errorCode;
private String errorMessage;
private String exceptionMessage;
private Exception exception;
public BusinessException(String errorMessage) {
super();
this.errorMessage = errorMessage;
}
public BusinessException(int errorCode, String errorMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public BusinessException(int errorCode, String errorMessage, Exception exception) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.exception = exception;
}
public BusinessException(String errorMessage, String exceptionMessage) {
super();
this.exceptionMessage = exceptionMessage;
this.errorMessage = errorMessage;
}
public String getExceptionMessage() {
return exceptionMessage;
}
public void setExceptionMessage(String exceptionMessage) {
this.exceptionMessage = exceptionMessage;
}
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public Exception getException() {
return exception;
}
public void setException(Exception exception) {
this.exception = exception;
}
public BusinessException(int errorCode, String errorMessage, String exceptionMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.exceptionMessage = exceptionMessage;
}
}
CommonEnum.java
public enum CommonEnum implements BaseErrorInfoInterface {
// 数据操作错误定义
SUCCESS(200, "成功!"),
BODY_NOT_MATCH(400,"请求的数据格式不符!"),
SIGNATURE_NOT_MATCH(401,"请求的数字签名不匹配!"),
NOT_FOUND(404, "未找到该资源!"),
INTERNAL_SERVER_ERROR(500, "服务器内部错误!"),
SERVER_BUSY(503,"服务器正忙,请稍后再试!"),
CLIENT_ABORT_ERROR(600, "客户端中断连接")
;
/** 错误码 */
private int resultCode;
/** 错误描述 */
private String resultMsg;
CommonEnum(int resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
@Override
public int getResultCode() {
return resultCode;
}
@Override
public String getResultMsg() {
return resultMsg;
}
}
ErrController.java
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ErrController implements ErrorController {
public String getErrorPath() {
// TODO Auto-generated method stub
return "/error";
}
@RequestMapping("/error")
public boolean handlerError() {
throw new BusinessException(-1,"页面不存在");
}
}
GlobalExceptionHandler.java
注意:clientAbortExceptionHandler
用于处理 ClientAbortException
异常。该方法记录了警告级别的日志,而不是错误级别,因为这种异常通常表示客户端主动断开连接,这种情况不一定是服务器端的错误
import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理自定义的业务异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public Result bizExceptionHandler(HttpServletRequest req, BusinessException e){
StackTraceElement[] stackTrace = e.getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
sb.append(stackTraceElement.toString()).append("\n");
}
logger.error("未知异常:"+e.getErrorMessage()+"!原因是:"+e);
logger.error("未知异常!錯誤信息是:"+sb.toString());
return Result.error(e.getErrorCode(),e.getErrorMessage());
}
/**
* 处理空指针的异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =NullPointerException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, NullPointerException e){
StackTraceElement[] stackTrace = e.getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
sb.append(stackTraceElement.toString()).append("\n");
}
logger.error("未知异常!原因是:"+e);
logger.error("未知异常!錯誤信息是:"+sb.toString());
return Result.error(CommonEnum.BODY_NOT_MATCH);
}
/**
* 处理 ClientAbortException 异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = ClientAbortException.class)
@ResponseBody
public Result clientAbortExceptionHandler(HttpServletRequest req, ClientAbortException e){
logger.warn("客户端中断连接!异常信息是:" + e.getMessage());
return Result.error(CommonEnum.CLIENT_ABORT_ERROR);
}
/**
* 处理其他异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =Exception.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, Exception e){
StackTraceElement[] stackTrace = e.getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
sb.append(stackTraceElement.toString()).append("\n");
}
logger.error("未知异常!原因是:"+e);
logger.error("未知异常!錯誤信息是:"+sb.toString());
return Result.error(CommonEnum.INTERNAL_SERVER_ERROR);
}
}
MyCustomErrorAttributes.java
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import java.util.Map;
@Component
public class MyCustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
return errorAttributes;
}
}
Result.java
import com.alibaba.fastjson.JSONObject;
public class Result {
/**
* 响应代码
*/
private int code;
/**
* 响应消息
*/
private String message;
/**
* 响应结果
*/
private Object result;
public Result() {
}
public Result(BaseErrorInfoInterface errorInfo) {
this.code = errorInfo.getResultCode();
this.message = errorInfo.getResultMsg();
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
/**
* 成功
*
* @return
*/
public static Result success() {
return success(null);
}
/**
* 成功
* @param data
* @return
*/
public static Result success(Object data) {
Result rb = new Result();
rb.setCode(CommonEnum.SUCCESS.getResultCode());
rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
rb.setResult(data);
return rb;
}
/**
* 失败
*/
public static Result error(BaseErrorInfoInterface errorInfo) {
Result rb = new Result();
rb.setCode(errorInfo.getResultCode());
rb.setMessage(errorInfo.getResultMsg());
rb.setResult(null);
return rb;
}
/**
* 失败
*/
public static Result error(int code, String message) {
Result rb = new Result();
rb.setCode(code);
rb.setMessage(message);
rb.setResult(null);
return rb;
}
/**
* 失败
*/
public static Result error(String message) {
Result rb = new Result();
rb.setCode(-1);
rb.setMessage(message);
rb.setResult(null);
return rb;
}
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
}
日志
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<conversionRule conversionWord="ip" converterClass="com.example.util.LogIpConfig" />
<!-- Log file path -->
<property name="log.path" value="../ScheduleLogs/" />
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- <pattern> [%ip]%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>-->
<pattern> [host:%ip]%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="fileRolling_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM-dd}/info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--每个文件的大小限制-->
<MaxFileSize>1GB</MaxFileSize>
<!--最多保留10天的文件,10天之前的将被清除-->
<MaxHistory>10</MaxHistory>
<!--该滚动策略日志的总大小,超过的日志会被清除-->
<!-- <totalSizeCap>10GB</totalSizeCap>-->
<!--启动时清理日志文件 此项置灰清理超过保留天数的 也会清理超过总大小的-->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern> [host:%ip]%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n
</pattern>
</encoder>
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level>
<onMatch>DENY</onMatch> <onMismatch>NEUTRAL</onMismatch> </filter> -->
</appender>
<!-- Log file error output -->
<appender name="fileRolling_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM-dd}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--每个文件的大小限制-->
<MaxFileSize>1GB</MaxFileSize>
<!--最多保留10天的文件,10天之前的将被清除-->
<MaxHistory>10</MaxHistory>
<!--该滚动策略日志的总大小,超过的日志会被清除-->
<!-- <totalSizeCap>10GB</totalSizeCap>-->
<!--启动时清理日志文件 此项置灰清理超过保留天数的 也会清理超过总大小的-->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern> [host:%ip]%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n </pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="debug">
<appender-ref ref="console"/>
</root>
<root level="info">
<!-- <appender-ref ref="console"/>-->
<appender-ref ref="fileRolling_info" />
<appender-ref ref="fileRolling_error" />
</root>
<logger name="org.springframework" level="INFO"/>
<logger name="org.mybatis" level="INFO"/>
<logger name="com.example" level="debug"/>
<!-- Enable MDC -->
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
</configuration>
工具类
LogIpConfig.java
说明:获取前端访问后端的ip地址
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class LogIpConfig extends ClassicConverter {
@Override
public String convert(ILoggingEvent event) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return "127.0.0.1";
}
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
String remoteAddr = "";
if (request != null) {
remoteAddr = request.getHeader("X-Forwarded-For");
if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
remoteAddr = request.getHeader("X-Real-IP");
}
if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
remoteAddr = request.getRemoteAddr();
}
}
return remoteAddr;
}
public static String getIp(){
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return "127.0.0.1";
}
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
String remoteAddr = "";
if (request != null) {
remoteAddr = request.getHeader("X-Forwarded-For");
if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
remoteAddr = request.getHeader("X-Real-IP");
}
if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
remoteAddr = request.getRemoteAddr();
}
}
return remoteAddr;
}
}
JobUtil.java
说明:通过post或get请求访问外部接口
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
@Configuration //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling // 2.开启定时任务
public class JobUtil {
public static Object postHTTP(String requestId, String colum, String requestIp,String apiUrl) {
/*
*第一种方式post请求
*/
// JSONObject result = new JSONObject();
// JSONObject jsons = new JSONObject();
// jsons.put("key", "value");
JSONObject jsonss = null;
try {
URL url = new URL(apiUrl);
//打开和url之间的连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
PrintWriter out = null;
//请求方式 这里设置你需要请求的方式
//conn.setRequestMethod("POST");
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "close");
//这里设置请求头
conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
//设置是否向httpUrlConnection输出,设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
//post与get的 不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
conn.setDoOutput(true);
conn.setDoInput(true);
// 创建一个 OutputStreamWriter 对象,并指定 UTF-8 编码
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
//获取URLConnection对象对应的输出流
out = new PrintWriter(writer);
out = new PrintWriter(conn.getOutputStream());
//设置请求参数
Map map = new HashMap();
map.put("requestId", requestId);
map.put("requestIp", requestIp);
if (!colum.equals("")) //精度的不需要传楼层,报警履历需要传楼层
map.put("colum",colum);
String toJSONString = JSON.toJSONString(map);
//发送请求参数即数据
out.print(toJSONString);
//缓冲数据
out.flush();
//获取URLConnection对象对应的输入流
InputStream is = conn.getInputStream();
//构造一个字符流缓存
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String str = "";
while ((str = br.readLine()) != null) {
jsonss = JSONObject.parseObject(str);
}
// System.out.println("获取到的报文数据为:" + jsonss);
//关闭流
is.close();
//断开连接,最好写上,disconnect是在底层tcp socket链接空闲时才切断。如果正在被 其他线程使用就不切断。
//固定多线程的话,如果不disconnect,链接会增多,直到收发不出信息。写上 disconnect后正常一些。
conn.disconnect();
// System.out.println("结束");
} catch (Exception e) {
e.printStackTrace();
}
return jsonss;
}
/**
* 以get方式调用对方接口方法
* @param pathUrl
*/
public static String getHTTP(String pathUrl){
BufferedReader br = null;
String result = "";
try {
URL url = new URL(pathUrl);
//打开和url之间的连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设定请求的方法为"GET",默认是GET
//post与get的不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
conn.setRequestMethod("GET");
//设置30秒连接超时
conn.setConnectTimeout(30000);
//设置30秒读取超时
conn.setReadTimeout(30000);
// 设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在http正文内,因此需要设为true, 默认情况下是false;
conn.setDoOutput(true);
// 设置是否从httpUrlConnection读入,默认情况下是true;
conn.setDoInput(true);
// Post请求不能使用缓存(get可以不使用)
conn.setUseCaches(false);
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive"); //维持长链接
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
//连接,从上述url.openConnection()至此的配置必须要在connect之前完成,
conn.connect();
/**
* 下面的代码相当于,获取调用第三方http接口后返回的结果
*/
//获取URLConnection对象对应的输入流
InputStream is = conn.getInputStream();
//构造一个字符流缓存
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String str = "";
while ((str = br.readLine()) != null){
result += str;
}
// System.out.println(result);
//关闭流
is.close();
//断开连接,disconnect是在底层tcp socket链接空闲时才切断,如果正在被其他线程使用就不切断。
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (br != null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 通用的方法,用post方式与外部系统的api通讯
* @param requestMap post请求的参数,存在requestMap
* @param apiUrl post请求的路径
* @return
*/
public static Object post(Map requestMap,String apiUrl) {
/*
*第一种方式post请求
*/
// JSONObject result = new JSONObject();
// JSONObject jsons = new JSONObject();
// jsons.put("key", "value");
com.alibaba.fastjson.JSONObject jsonss = null;
try {
URL url = new URL(apiUrl);
//打开和url之间的连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
PrintWriter out = null;
//请求方式 这里设置你需要请求的方式
//conn.setRequestMethod("POST");
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "close");
//这里设置请求头
conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
//设置是否向httpUrlConnection输出,设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
//post与get的 不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
conn.setDoOutput(true);
conn.setDoInput(true);
// 创建一个 OutputStreamWriter 对象,并指定 UTF-8 编码
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
//获取URLConnection对象对应的输出流
// out = new PrintWriter(writer);
out = new PrintWriter(conn.getOutputStream());
//设置请求参数
// Map map = new HashMap();
// map.put("requestId", requestId);
// map.put("requestIp", requestIp);
String toJSONString = JSON.toJSONString(requestMap);
//发送请求参数即数据
out.print(toJSONString);
//缓冲数据
out.flush();
//获取URLConnection对象对应的输入流
InputStream is = conn.getInputStream();
//构造一个字符流缓存
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String str = "";
while ((str = br.readLine()) != null) {
jsonss = JSONObject.parseObject(str);
}
// System.out.println("获取到的报文数据为:" + jsonss);
//关闭流
is.close();
//断开连接,最好写上,disconnect是在底层tcp socket链接空闲时才切断。如果正在被 其他线程使用就不切断。
//固定多线程的话,如果不disconnect,链接会增多,直到收发不出信息。写上 disconnect后正常一些。
conn.disconnect();
// System.out.println("结束");
} catch (Exception e) {
e.printStackTrace();
}
return jsonss;
}
/**
* 通用的方法,用post方式与外部系统的api通讯
* @param requestMap post请求的参数,存在requestMap
* @param apiUrl post请求的路径
* @return
*/
public static String post2(Map requestMap,String apiUrl) {
/*
*第一种方式post请求
*/
// JSONObject result = new JSONObject();
JSONObject jsons = new JSONObject();
String result = "";
jsons.put("key", "value");
com.alibaba.fastjson.JSONObject jsonss = null;
try {
URL url = new URL(apiUrl);
//打开和url之间的连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
PrintWriter out = null;
//请求方式 这里设置你需要请求的方式
//conn.setRequestMethod("POST");
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "close");
//这里设置请求头
conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
//设置是否向httpUrlConnection输出,设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
//post与get的 不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
conn.setDoOutput(true);
conn.setDoInput(true);
// 创建一个 OutputStreamWriter 对象,并指定 UTF-8 编码
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
//获取URLConnection对象对应的输出流
out = new PrintWriter(writer);
out = new PrintWriter(conn.getOutputStream());
//设置请求参数
// Map map = new HashMap();
// map.put("requestId", requestId);
// map.put("requestIp", requestIp);
String toJSONString = JSON.toJSONString(requestMap);
//发送请求参数即数据
out.print(toJSONString);
//缓冲数据
out.flush();
//获取URLConnection对象对应的输入流
InputStream is = conn.getInputStream();
//构造一个字符流缓存
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
// String str = "";
//
// while ((str = br.readLine()) != null) {
// jsonss = JSONObject.parseObject(str);
// }
StringBuilder response = new StringBuilder(); //读取大量数据时,StringBuilder构建完整的字符串可能会占用较多的内存,因此需要根据实际情况权衡使用。
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
result=response.toString();
// System.out.println("requestMap = " + toJSONString + ", apiUrl = " + apiUrl);
//关闭流
is.close();
//断开连接,最好写上,disconnect是在底层tcp socket链接空闲时才切断。如果正在被 其他线程使用就不切断。
//固定多线程的话,如果不disconnect,链接会增多,直到收发不出信息。写上 disconnect后正常一些。
conn.disconnect();
// System.out.println("结束");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* @param headers
* @param apiUrl
* @return
*/
public static Object postHeaders(Map requestMap,Map<String, String> headers, String apiUrl) {
// JSONObject jsons = new JSONObject();
// jsons.put("key", "value");
com.alibaba.fastjson.JSONObject jsonss = null;
try {
// 创建信任所有证书的 TrustManager
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// 设置 SSL 上下文
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// 设置默认 SSL Socket 工厂
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// 设置默认 HostnameVerifier
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
URL url = new URL(apiUrl);
//打开和url之间的连接,使用ssl的HttpsURLConnection!!!(对方需求是hhtps)
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
// PrintWriter out = null;
// 关闭 SSL 验证
// conn.setSSLSocketFactory(HttpsURLConnection.getDefaultSSLSocketFactory());
// conn.setHostnameVerifier((hostname, session) -> true);
//请求方式 这里设置你需要请求的方式
conn.setRequestMethod("POST");
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "close");
// 设置请求头部
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
}
//如果存在请求参数,则这样
if(requestMap!=null)
conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
//设置是否向httpUrlConnection输出,设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
//post与get的 不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
conn.setDoOutput(true); // 设置是否向连接输出
conn.setDoInput(true); // 设置是否从连接读入
conn.setConnectTimeout(5000); // 设置连接超时时间
// 创建一个 OutputStreamWriter 对象,并指定 UTF-8 编码
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
//获取URLConnection对象对应的输出流
writer.write(JSON.toJSONString(requestMap));//发送请求参数即数据
writer.flush();
writer.close();
//获取URLConnection对象对应的输入流
InputStream is = conn.getInputStream();
//构造一个字符流缓存
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
// String str = "";
//
// while ((str = br.readLine()) != null) {
// jsonss = JSONObject.parseObject(str);
// }
System.out.println("获取到的报文数据为:" + jsonss);
// //关闭流
// is.close();
StringBuilder response = new StringBuilder(); //读取大量数据时,StringBuilder构建完整的字符串可能会占用较多的内存,因此需要根据实际情况权衡使用。
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
jsonss=JSON.parseObject(response.toString());
br.close();
//断开连接,最好写上,disconnect是在底层tcp socket链接空闲时才切断。如果正在被 其他线程使用就不切断。
//固定多线程的话,如果不disconnect,链接会增多,直到收发不出信息。写上 disconnect后正常一些。
conn.disconnect();
// System.out.println("结束");
} catch (Exception e) {
e.printStackTrace();
}
return jsonss;
}
/**
* SSL验证,使用 SSL/TLS 连接时永远信任所有证书
* @param requestMap
* @param apiUrl
* @return
*/
public static String post3(Map<String,String> requestMap,String apiUrl) {
/*
*第一种方式post请求
*/
// JSONObject result = new JSONObject();
JSONObject jsons = new JSONObject();
String result = "";
jsons.put("key", "value");
JSONObject jsonss = null;
try {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : requestMap.entrySet()) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(entry.getKey()).append("=").append(entry.getValue());
}
String params = sb.toString();
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// 设置 SSL 上下文
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// 设置默认 SSL Socket 工厂
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// 设置默认 HostnameVerifier
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
URL url = new URL(apiUrl);
//打开和url之间的连接
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
PrintWriter out = null;
//请求方式 这里设置你需要请求的方式
conn.setRequestMethod("POST");
//设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "close");
//这里设置请求头
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
//设置是否向httpUrlConnection输出,设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
//post与get的 不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取连接对象对应的输出流
OutputStream os = conn.getOutputStream();
// 传递参数
os.write(params.getBytes("UTF-8"));
os.flush();
os.close();
//获取URLConnection对象对应的输入流
InputStream is = conn.getInputStream();
//构造一个字符流缓存
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
StringBuilder response = new StringBuilder(); //读取大量数据时,StringBuilder构建完整的字符串可能会占用较多的内存,因此需要根据实际情况权衡使用。
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
result=response.toString();
// System.out.println("requestMap = " + toJSONString + ", apiUrl = " + apiUrl);
// System.out.println("獲取到的報文數據為:" + result);
//关闭流
is.close();
//断开连接,最好写上,disconnect是在底层tcp socket链接空闲时才切断。如果正在被 其他线程使用就不切断。
//固定多线程的话,如果不disconnect,链接会增多,直到收发不出信息。写上 disconnect后正常一些。
conn.disconnect();
// System.out.println("结束");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
JobUtil测试类
import com.alibaba.fastjson.JSONObject;
import com.example.util.JobUtil;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@CrossOrigin//添加允許跨域註解
@RestController
public class AlarmCodeMaintenanceController {
@RequestMapping("/test1")
public void test(){
JSONObject json= (JSONObject)JobUtil.postHTTP(requestId,colum,clientIp,apiUrl);
System.out.println(json);
}
@RequestMapping("/test2")
public void test(){
Map<String, String> map2=new HashMap<>();
map2.put("statDate","2024-06-06");
map2.put("colum1","colum1");
map2.put("colum2","colum2");
String result=JobUtil.post2(map,url);
// JSONObject jsonObject= (JSONObject) JSONObject.parse(result);
System.out.println("x"+result);
// System.out.println(jsonObject);
List<?> list=JSONObject.parseArray(result);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
@RequestMapping("/test3")
public void test(){
Map<String, String> map3 = new HashMap<>();
map3.put("empNo",userId);
map3.put("pwd",psw);
map3.put("userIP",clientIp);
map3.put("authNo",authNo);
map3.put("tenantCode",tenantCode);
String result=JobUtil.post3(map3, url);
// JSONObject jsonObject= (JSONObject) JSONObject.parse(result);
System.out.println("x"+result);
// System.out.println(jsonObject);
}
}
PgUtil.java
说明:COPYIN方法配置
import org.postgresql.PGConnection;
import org.postgresql.copy.CopyManager;
import org.postgresql.core.BaseConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.Statement;
import java.util.List;
@Component
public class PgUtil {
// private static Logger logger = Logger.getLogger(PgUtil.class);
// @Autowired
// private DataSourceConfig dataSourceConfig;
@Resource
private DataSource dataSource;
public void copyIn(String tableName, List<String> s){
if(tableName == null || s.isEmpty()){
return;
}
PGConnection pgConnection = null;
Connection conn = null;
long affectedRowCount = 0;
try {
//获得连接
conn = dataSource.getConnection();
conn.setAutoCommit(false);
if (conn.isWrapperFor(PGConnection.class)) {
pgConnection = conn.unwrap(PGConnection.class);
}
CopyManager copyManager = new CopyManager((BaseConnection) pgConnection);
//System.out.println("执行copy....");
String copyIn = "COPY " + tableName + " FROM STDIN DELIMITER AS '|' ";
System.out.println(copyIn);
affectedRowCount = copyManager.copyIn(copyIn, getInputStream(s));
conn.commit();
System.out.println(affectedRowCount);
} catch (Exception e) {
e.printStackTrace();
// logger.error("Failed to copy data SQLException: " + e);
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// logger.info("affectedRowCount :" + affectedRowCount);
}
public void copyInByHandCommit(String tableName,String tableColumnStr, List<String> s){
if(tableName == null || s.isEmpty()){
return;
}
PGConnection pgConnection = null;
Connection conn = null;
Statement statement = null;
long affectedRowCount = 0;
try {
//获得连接
// conn = DruidUtil.getConnection();
conn = dataSource.getConnection();
conn.setAutoCommit(false);
if (conn.isWrapperFor(PGConnection.class)) {
pgConnection = conn.unwrap(PGConnection.class);
}
statement = conn.createStatement();
String truncation = "TRUNCATE TABLE " + tableName ;
// String truncation = "DELETE from " + tableName ;
statement.executeUpdate(truncation);
CopyManager copyManager = new CopyManager((BaseConnection) pgConnection);
String copyIn = "COPY " + tableColumnStr + " FROM STDIN DELIMITER AS '|'";
affectedRowCount = copyManager.copyIn(copyIn, getInputStream(s));
conn.commit();
System.out.println(affectedRowCount); //打印影响行数
} catch (Exception e) {
e.printStackTrace();
// logger.error("Failed to no autoCommit copy data SQLException: " + e);
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// logger.info("affectedRowCount :" + affectedRowCount);
}
public void copyInBandTmCommit(String tableName,String tableColumnStr, List<String> s,String colum){
if(tableName == null || s.isEmpty()){
return;
}
PGConnection pgConnection = null;
Connection conn = null;
Statement statement = null;
long affectedRowCount = 0;
try {
//获得连接
// conn = DruidUtil.getConnection();
conn = dataSource.getConnection();
conn.setAutoCommit(false);
if (conn.isWrapperFor(PGConnection.class)) {
pgConnection = conn.unwrap(PGConnection.class);
}
statement = conn.createStatement();
String truncation = "DELETE from " + tableName +" where colum='"+colum+"'";
statement.executeUpdate(truncation);
CopyManager copyManager = new CopyManager((BaseConnection) pgConnection);
String copyIn = "COPY " + tableColumnStr + " FROM STDIN DELIMITER AS '|'";
affectedRowCount = copyManager.copyIn(copyIn, getInputStream(s));
conn.commit();
System.out.println(affectedRowCount); //打印影响行数
} catch (Exception e) {
e.printStackTrace();
// logger.error("Failed to no autoCommit copy data SQLException: " + e);
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// logger.info("affectedRowCount :" + affectedRowCount);
}
public void copyInBandTmWorkStationCommit(String tableName,String tableColumnStr, List<String> s){
if(tableName == null || s.isEmpty()){
return;
}
PGConnection pgConnection = null;
Connection conn = null;
Statement statement = null;
long affectedRowCount = 0;
try {
//获得连接
// conn = DruidUtil.getConnection();
conn = dataSource.getConnection();
conn.setAutoCommit(false);
if (conn.isWrapperFor(PGConnection.class)) {
pgConnection = conn.unwrap(PGConnection.class);
}
statement = conn.createStatement();
String truncation = "TRUNCATE TABLE " + tableName ;
statement.executeUpdate(truncation);
CopyManager copyManager = new CopyManager((BaseConnection) pgConnection);
String copyIn = "COPY " + tableColumnStr + " FROM STDIN DELIMITER AS '|'";
affectedRowCount = copyManager.copyIn(copyIn, getInputStream(s));
conn.commit();
System.out.println(affectedRowCount); //打印影响行数
} catch (Exception e) {
e.printStackTrace();
// logger.error("Failed to no autoCommit copy data SQLException: " + e);
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// logger.info("affectedRowCount :" + affectedRowCount);
}
private static InputStream getInputStream(List<String> list) {
StringBuilder sb = new StringBuilder();
for (String data : list) {
sb.append(data + "\r\n");
}
return new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
}
}
定时任务
Component.java
import com.exampl.service.TestService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class Component implements Job {
@Resource
private TestService testService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
testService.updateSchedule();
}
}
QuartzConfig.java
import com.example.component.Component;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
@Bean
public JobDetail jobDetail() {
return JobBuilder.newJob(Component.class)
.withIdentity("job1")
.storeDurably()
.build();
}
/**
* 写法1
* @return
*/
/*@Bean
public Trigger jobTrigger() {
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(4) // 每4秒执行一次
.repeatForever(); // 无限次重复
return TriggerBuilder.newTrigger()
.forJob(jobDetail())
.withIdentity("trigger1")
.withSchedule(scheduleBuilder)
.build();
}*/
/**
* 写法2
* @return
*/
@Bean
public Trigger jobTrigger() {
// CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0 30 9 * * ?"); // 每天早上9:30执行一次
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0 30 9,15 * * ? "); // 每天早上9:30和15:30执行一次
return TriggerBuilder.newTrigger()
.forJob(jobDetail())
.withIdentity("trigger2")
.withSchedule(scheduleBuilder)
.build();
}
}
功能代码
TestController.java
import com.example.service.TestService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class TestController {
@Resource
private TestService testService;
@RequestMapping("/updateSchedule")
public String updateSchedule() {
return testService.updateSchedule();
}
}
TestService.java
public interface TestService {
String updateSchedule();
}
TestServiceImpl.java
import com.exmaple.mapper.testMapper.TestMapper;
import com.exmaple.mapper.test1Mapper.Test1Mapper;
import com.exmaple.mapper.test2Mapper.Test2Mapper;
import com.exmaple.service.TestService;
import com.exmaple.util.PgUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.stream.Collectors;
@Service
public class TestServiceImpl implements TestService{
private static final Logger logger = LoggerFactory.getLogger(TestServiceImpl.class);
@Value("${cus.msg}")
private String msg;
@Resource
private TestMapper testMapper;
@Resource
private Test1Mapper test1Mapper;
@Resource
private Test1Mapper test2Mapper;
@Resource
private PgUtil pgUtil;
@Override
public String updateSchedule() {
String successMsg = "";
String errorMsg = "";
String resultMessage = "";
String tableName = "schema.tableName";
String tableColumnStr = "schema.tableName(colum1,colum2,colum3,colum4,colum5)";
List<Map> messageList = testMapper.getMessage();
Set<String> messageFilter = messageList1 .stream()
.map(map -> map.get("colum1") + "|" + map.get("colum2"))
.collect(Collectors.toSet());
List<Map<String, String>> messageList1 = test1Mapper.getMessage1("", msg);
List<Map<String, String>> messageList2 = test2Mapper.getMessage2("", msg);
List<Map<String, String>> allList = new ArrayList<>(messageList1);
allList.addAll(messageList2);
List<String> resultList = allList.stream()
.filter(map -> messageFilter.contains(map.get("colum1") + "|" + map.get("colum2")))
.map(map -> {
//提取字段值并拼接
String rowString = map.get("colum1") + "|" +
(map.get("colum2")==null?"\\N":map.get("colum2")) + "|" + //在这里\\N代表占位符,使用COPY命令将数据插入PostgreSQL表时,\\N会被正确识别为NULL值
map.get("colum3") + "|" +
map.get("colum4") + "|" +
map.get("colum5");
return rowString;
})
.collect(Collectors.toList());
try {
pgUtil.copyInBandTmWorkStationCommit(tableName, tableColumnStr, resultList);
logger.info("数据(条数:" + resultList.size() + ")临时表插入成功!");
} catch (Exception e) {
errorMsg += "数据临时表插入失败!\n";
logger.info("数据临时表插入失败!");
}
try {
testMapper.insertTest1();
logger.info("insertTest1数据插入成功!");
} catch (Exception e) {
errorMsg += "insertTest1数据插入失败!\n";
logger.info("insertTest1数据插入失败!");
}
try {
testMapper.insertTest2();
logger.info("insertTest2数据插入成功!");
} catch (Exception e) {
errorMsg += "insertTest2数据插入失败!\n";
logger.info("insertTest2数据插入失败!");
}
successMsg += "临时表数据(条数:" + resultList.size() + ")\n数据更新成功\n";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
resultMessage = "successMsg日期:" + sdf.format(new Date()) + "Start\n" +successMsg + "successMsg" +"日期日期:" + sdf.format(new Date()) + "End\n";
resultMessage += "errorMsg日期:" + sdf.format(new Date()) + "Start\n" + errorMsg + "errorMsg日期日期:" + sdf.format(new Date()) + "End";
logger.info(resultMessage);
return resultMessage;
}
}
TestMapper.java
import com.github.houbb.heaven.annotation.reflect.Param;
import com.example.config.DynamicDS;
import java.util.List;
import java.util.Map;
public interface TestMapper{
@DynamicDS("")
List<Map> getMessage();
@DynamicDS("")
void insertTest1();
@DynamicDS("")
void insertTest2();
}
TestMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.testMapper.TestMapper">
<resultMap id="message" type="map">
<result column="colum1" property="colum1"/>
<result column="colum2" property="colum2"/>
</resultMap>
<select id="getMessage" resultMap="message">
</select>
<insert id="insertTest1">
</insert>
<insert id="insertTest2">
</insert>
</mapper>
Test1Mapper.java
import com.github.houbb.heaven.annotation.reflect.Param;
import com.example.config.DynamicDS;
import java.util.List;
import java.util.Map;
public interface Test1Mapper{
@DynamicDS("1")
List<Map<String,String>> getMessage1(@Param("test1") String test1, @Param("msg") String msg);
}
Test1Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.test1Mapper.Test1Mapper">
<resultMap id="message1" type="map">
<result column="colum1" property="colum1"/>
<result column="colum2" property="colum2"/>
<result column="colum3" property="colum3"/>
<result column="colum4" property="colum4"/>
<result column="colum5" property="colum5"/>
</resultMap>
<select id="getMessage1" resultMap="message1" >
</select>
</mapper>