InfluxDB持久层封装

InfluxDB持久层封装

了解如何使用spring-boot来操作InfluxDB数据库,首先我们来看下整个的系统结构图例:

在这里插入图片描述

对比下mybatis中的执行流程:

在这里插入图片描述

1_自动装配

首先,我们来看下第一步自动装配:依赖spring-boot自动装配出InfluxDB对象,并把对象交于IOC容器管理,对于spring-boot来说它最大的特点就是自动装配,这里我们用到2个类:配置文件类InfluxDbProperties,配置类InfluxDbAutoConfiguration,如下图所示:

在这里插入图片描述

spring-boot-autoconfigure中已经构建好对应的类信息,下面我们逐一解读一下,首先我们看下 InfluxDbProperties 配置:

package org.springframework.boot.autoconfigure.influx;import org.springframework.boot.context.properties.ConfigurationProperties;/*** Configuration properties for InfluxDB*/
@ConfigurationProperties(prefix = "spring.influx")
public class InfluxDbProperties {/*** URL of the InfluxDB instance to which to connect.*/private String url;/*** Login user.*/private String user;/*** Login password.*/private String password;/*** setter、getter略*/
}

当我们在使用时,只需要在对应项目的bootstrap.yml文件做如下配置:

spring:influx:url: http://192.168.193.141:8086password: 123456user: admin

spring-boot 在发现我们引入 InfluxDB.class 后自动按照 InfluxDbProperties 的属性帮我们构建InfluxDB 对象交于spring-IOC容器

package org.springframework.boot.autoconfigure.influx;@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(InfluxDB.class)
@EnableConfigurationProperties(InfluxDbProperties.class)
public class InfluxDbAutoConfiguration {@Bean@ConditionalOnMissingBean@ConditionalOnProperty("spring.influx.url")public InfluxDB influxDb(InfluxDbProperties properties,ObjectProvider<InfluxDbOkHttpClientBuilderProvider> builder) {return new InfluxDBImpl(properties.getUrl(), properties.getUser(), properties.getPassword(),determineBuilder(builder.getIfAvailable()));}private static OkHttpClient.Builder determineBuilder(InfluxDbOkHttpClientBuilderProvider builder) {if (builder != null) {return builder.get();}return new OkHttpClient.Builder();}}

2_配置管理

构建好InfluxDB 对象后,那如何使用呢?下面我们来看下第二步配置管理:项目启动时,通过InfluxDBConfig构建出业务执行器、参数处理器、结果处理器,并把对象交于IOC容器管理,framework-influxdb项目中我们构建了一个InfluxDBConfig配置类,内容如下:

package org.example.influxDd.config;import org.example.influxDd.core.Executor;
import org.example.influxDd.core.ParameterHandler;
import org.example.influxDd.core.ResultSetHandler;
import org.influxdb.InfluxDB;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 时序数据库配置类*/
@Configuration
public class InfluxDBConfig {@Bean(name = "executor")public Executor executor(InfluxDB influxDB) {return new Executor(influxDB);}@Bean(name = "parameterHandler")public ParameterHandler parameterHandler(InfluxDB influxDB) {return new ParameterHandler();}@Bean(name = "resultSetHandler")public ResultSetHandler resultSetHandler(InfluxDB influxDB) {return new ResultSetHandler();}
}

将其配置到自动装配文件META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.example.influxDd.config.InfluxDBConfig

业务执行器Executor :需要从spring-IOC中拿到InfluxDB来完成构建,用于与influxDB进行交互

package org.example.influxDd.core;import lombok.extern.slf4j.Slf4j;
import org.example.influxDd.util.EmptyUtil;
import org.influxdb.InfluxDB;
import org.influxdb.annotation.Measurement;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import org.influxdb.dto.Query;
import org.influxdb.dto.QueryResult;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 执行器*/
@Slf4j
public class Executor {InfluxDB influxDB;public Executor() {}public Executor(InfluxDB influxDB) {this.influxDB = influxDB;}public List<Map<String,Object>> select(String sql,String database) {QueryResult queryResult = influxDB.query(new Query(sql, database));List<Map<String,Object>> resultList = new ArrayList<>();queryResult.getResults().forEach(result -> {//查询出错抛出错误信息if (!EmptyUtil.isNullOrEmpty(result.getError())){throw new RuntimeException(result.getError());}if (!EmptyUtil.isNullOrEmpty(result)&&!EmptyUtil.isNullOrEmpty(result.getSeries())){//获取所有列的集合,一个迭代是代表一组List<QueryResult.Series> series= result.getSeries();for (QueryResult.Series s : series) {//列中含有多行数据,每行数据含有多列value,所以嵌套ListList<List<Object>> values = s.getValues();//每组的列是固定的List<String> columns = s.getColumns();for (List<Object> v:values){//循环遍历结果集,获取每行对应的value,以map形式保存Map<String,Object> queryMap =new HashMap<String, Object>();for(int i=0;i<columns.size();i++){//遍历所有列名,获取列对应的值String column = columns.get(i);if (v.get(i)==null||v.get(i).equals("null")){//如果是null就存入nullqueryMap.put(column,null);}else {//不是null就转成字符串存储String value = String.valueOf(v.get(i));//如果是时间戳还可以格式转换,我这里懒了queryMap.put(column, value);}}//把结果添加到结果集中resultList.add(queryMap);}}}});return resultList;}public void insert(Object args[]) {if (args.length != 1) {throw new RuntimeException();}Object obj = args[0];List<Object> list = new ArrayList<>();if (obj instanceof List){list = (ArrayList) obj;}else {list.add(obj);}if (list.size() > 0) {Object firstObj = list.get(0);Class<?> domainClass = firstObj.getClass();List<Point> pointList = new ArrayList<>();for (Object o : list) {Point point = Point.measurementByPOJO(domainClass).addFieldsFromPOJO(o).build();pointList.add(point);}//获取数据库名和rpMeasurement measurement = firstObj.getClass().getAnnotation(Measurement.class);String database = measurement.database();String retentionPolicy = measurement.retentionPolicy();BatchPoints batchPoints = BatchPoints.builder().points(pointList).retentionPolicy(retentionPolicy).build();influxDB.setDatabase(database);influxDB.write(batchPoints);}}public void delete(String sql, String database) {influxDB.query(new Query(sql, database));}}

参数处理器 ParameterHandler :用于执行参数的封装处理

package org.example.influxDd.core;import org.example.influxDd.anno.Param;import java.lang.reflect.Parameter;/*** 参数处理器*/
public class ParameterHandler {/*** 拼接sql** @param parameters 参数名* @param args       参数实际值* @param sql        未拼接参数的sql语句* @return 拼接好的sql*/public String handleParameter(Parameter[] parameters, Object[] args, String sql) {for (int i = 0; i < parameters.length; i++) {Class<?> parameterType = parameters[i].getType();String parameterName = parameters[i].getName();Param param = parameters[i].getAnnotation(Param.class);if (param != null) {parameterName = param.value();}if (parameterType == String.class) {sql = sql.replaceAll("\\#\\{" + parameterName + "\\}", "'" + args[i] + "'");sql = sql.replaceAll("\\$\\{" + parameterName + "\\}", args[i].toString());} else {sql = sql.replaceAll("\\#\\{" + parameterName + "\\}", args[i].toString());sql = sql.replaceAll("\\$\\{" + parameterName + "\\}", args[i].toString());}}return sql;}
}

参数处理器配合参数注解使用:

package org.example.influxDd.anno;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@Documented
public @interface Param {String value();
}

结果处理器ResultSetHandler :用于执行结构的封装处理

package org.example.influxDd.core;import lombok.SneakyThrows;
import org.example.influxDd.util.BeanConv;
import org.example.influxDd.util.EmptyUtil;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 结果集处理器*/
public class ResultSetHandler {/**** @Description 结果处理** @param reultList influx返回结果* @param method 目标方法* @param sql 执行sql* @param resultType 注解声明返回类型* @return*  java.lang.Object*/@SneakyThrowspublic Object handleResultSet(List<Map<String,Object>> reultList, Method method, String sql, Class<?> resultType) {Class<?> returnTypeTarget = method.getReturnType();//如果结果为空直接返回空构建if (EmptyUtil.isNullOrEmpty(reultList)){if (returnTypeTarget== List.class){return new ArrayList<>();}else if (returnTypeTarget==Map.class){return new HashMap<>();}else if (returnTypeTarget==String.class){return null;}else {return convertStringToObject(resultType,"0");}}//当前method声明返回结果不为list,且resultType与method声明返回结果类型不匹配if (returnTypeTarget!= List.class&&resultType!=returnTypeTarget){throw  new RuntimeException("返回类型与声明返回类型不匹配");}//当前method声明返回结果不为list,且resultType与method声明返回结果类型匹配if (returnTypeTarget!= List.class&&resultType==returnTypeTarget){//结果不唯一则抛出异常if (reultList.size()!=1){throw  new RuntimeException("返回结果不唯一");}//驼峰处理Map<String, Object> mapHandler = convertKeysToCamelCase(reultList.get(0));//单个Map类型if (resultType==Map.class){return mapHandler;//单个自定义类型} else if (!isTargetClass(resultType)){return BeanConv.toBean(mapHandler, resultType);//单个JDK提供指定类型}else {if (mapHandler.size()!=2){throw  new RuntimeException("返回结果非单值");}for (String key : mapHandler.keySet()) {if (!key.equals("time")&&!EmptyUtil.isNullOrEmpty((mapHandler.get(key)))){String target = String.valueOf(mapHandler.get(key)).replace(".0","");return convertStringToObject(resultType,target);}}}}//当前method声明返回结果为listif (returnTypeTarget== List.class){//驼峰处理List<Map<String, Object>> listHandler = convertKeysToCamelCase(reultList);//list的内部为map结果if (resultType==Map.class){return listHandler;//list的内部为自定义类型}else if (!isTargetClass(resultType)){return BeanConv.toBeanList(listHandler, resultType);//list的内部为JDK提供指定类型}else {List<Object> listResult = new ArrayList<>();listHandler.forEach(mapHandler->{if (mapHandler.size()!=2){throw  new RuntimeException("返回结果非单值");}for (String key : mapHandler.keySet()) {if (!key.equals("time")&&!EmptyUtil.isNullOrEmpty((mapHandler.get(key)))){String target = String.valueOf(mapHandler.get(key)).replace(".0","");listResult.add(convertStringToObject(resultType,target));}}});return listResult;}}return  null;}// 检查类是否是目标类型public static boolean isTargetClass(Class<?> clazz) {return clazz == Integer.class ||clazz == int.class ||clazz == Long.class ||clazz == long.class ||clazz == Float.class ||clazz == float.class ||clazz == Double.class ||clazz == double.class ||clazz == Short.class ||clazz == short.class ||clazz == Byte.class ||clazz == byte.class ||clazz == Character.class ||clazz == char.class ||clazz == Boolean.class||clazz == boolean.class||clazz== BigDecimal.class ||clazz== String.class;}public static Map<String, Object> convertKeysToCamelCase(Map<String, Object> map) {Map<String, Object> camelCaseMap = new HashMap<>();for (Map.Entry<String, Object> entry : map.entrySet()) {String originalKey = entry.getKey();Object value = entry.getValue();String camelCaseKey = convertToCamelCase(originalKey);camelCaseMap.put(camelCaseKey, value);}return camelCaseMap;}public static List<Map<String, Object>> convertKeysToCamelCase(List<Map<String, Object>> mapList) {List<Map<String, Object>> listHandler = new ArrayList<>();mapList.forEach(n->{listHandler.add(convertKeysToCamelCase(n));});return listHandler;}public static String convertToCamelCase(String snakeCase) {StringBuilder camelCase = new StringBuilder();boolean nextUpperCase = false;for (int i = 0; i < snakeCase.length(); i++) {char currentChar = snakeCase.charAt(i);if (currentChar == '_') {nextUpperCase = true;} else {if (nextUpperCase) {camelCase.append(Character.toUpperCase(currentChar));nextUpperCase = false;} else {camelCase.append(Character.toLowerCase(currentChar));}}}return camelCase.toString();}@SneakyThrowspublic static <T> T convertStringToObject(Class<?> clazz, String str){if (clazz == String.class) {return (T)str; // 如果目标类型是 String,则直接返回字符串} else if (isTargetClass(clazz)){// 获取目标类型的构造函数,参数为 String 类型的参数Constructor<?> constructor = clazz.getConstructor(String.class);return (T)constructor.newInstance(str); // 使用构造函数创建目标类型的对象}else {return (T)clazz.newInstance();}}
}

3_切面处理

下面我们来看下第三步切面处理:业务系统service调用业务Mapper时,influxDBAspect会对被@ S e l e c t Select Select @ I n s e r t @Insert @Insert注解的方法进行切面处理,封装构建参数处理器,然后通过业务执行器请求influxDB,最后交于结果处理器来封装数据。在进行前面之前我们定义了2个注解@Select 和@Insert内容如下:

package org.example.influxDd.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {//执行的influxQLString value();//返回的类型Class resultType();//执行的目标库String database();
}

Insert:

package org.example.influxDd.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Insert {
}

当Mapper中的方法被调用时,会被定义的InfluxDBAspect切面拦截处理:

package org.example.influxDd.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.example.influxDd.anno.Insert;
import org.example.influxDd.anno.Select;
import org.example.influxDd.core.Executor;
import org.example.influxDd.core.ParameterHandler;
import org.example.influxDd.core.ResultSetHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;/*** @ClassName InfluxDBAspect.java* @Description 拦截influxDb操作*/
@Aspect
@Component
public class InfluxDBAspect {private final Executor executor;private final ParameterHandler parameterHandler;private final ResultSetHandler resultSetHandler;@Autowiredpublic InfluxDBAspect(Executor executor, ParameterHandler parameterHandler, ResultSetHandler resultSetHandler) {this.executor = executor;this.parameterHandler = parameterHandler;this.resultSetHandler = resultSetHandler;}@Around("@annotation(select)")public Object select(ProceedingJoinPoint joinPoint, Select select) {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Method method = methodSignature.getMethod();Select selectAnnotation = method.getAnnotation(Select.class);//获得执行参数Parameter[] parameters = method.getParameters();//获得执行参数值Object[] args = joinPoint.getArgs();//获得执行sqlString sql = selectAnnotation.value();//替换参数sql = parameterHandler.handleParameter(parameters,args,sql);//注解声明返回类型Class<?> resultType = selectAnnotation.resultType();//查询结果List<Map<String,Object>> reultList = executor.select(sql,selectAnnotation.database());//根据返回类型返回结果return resultSetHandler.handleResultSet(reultList, method,sql,resultType);}@Around("@annotation(insert)")public void insert(ProceedingJoinPoint joinPoint, Insert insert) {//获得执行参数值Object[] args = joinPoint.getArgs();executor.insert(args);}
}

当切面select方法处理就可以通过反射拿到参数、sql、返回类型,然后通过 executor 来进行执行对应查询,而executor 中通过 parameterHandler 参数处理器解析参数,最后通过 resultSetHandler 结果处理器完成结果的处理。

4_其他工具的封装以及依赖

项目的结构如下:

framework-influxdb
└─src├─main├─java│  └─org│      └─example│          └─influxDd│              ├─anno│              ├─aspect│              ├─config│              ├─core│              └─util└─resources└─META-INF

操作influxDB的基础接口:

package org.example.influxDd;import org.example.influxDd.anno.Insert;import java.util.List;public interface InfluxDBBaseMapper<T> {@Insertvoid insertOne(T entity);@Insertvoid insertBatch(List<T> entityList);
}

对象转换工具

package org.example.influxDd.util;//这里想要使用mp的分页进行完善,不过还没有完成,可删除
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.converter.BidirectionalConverter;
import ma.glasnost.orika.converter.ConverterFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.Type;
import org.springframework.beans.BeanUtils;import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;/**** @description 对象转换工具,当对象成员变量属性:名称及类型相同时候会自动* 填充其值**/
@Slf4j
public class BeanConv {private static MapperFacade mapper;private static MapperFacade notNullMapper;static {MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();ConverterFactory converterFactory = mapperFactory.getConverterFactory();converterFactory.registerConverter(new LocalDateTimeConverter());converterFactory.registerConverter(new LocalDateConverter());converterFactory.registerConverter(new LocalTimeConverter());mapper = mapperFactory.getMapperFacade();MapperFactory notNullMapperFactory = new DefaultMapperFactory.Builder().mapNulls(false).build();notNullMapper = notNullMapperFactory.getMapperFacade();}private static class LocalDateTimeConverter extends BidirectionalConverter<LocalDateTime, LocalDateTime> {@Overridepublic LocalDateTime convertTo(LocalDateTime localDateTime, Type<LocalDateTime> type, MappingContext mappingContext) {return LocalDateTime.from(localDateTime);}@Overridepublic LocalDateTime convertFrom(LocalDateTime localDateTime, Type<LocalDateTime> type, MappingContext mappingContext) {return LocalDateTime.from(localDateTime);}}private static class LocalDateConverter extends BidirectionalConverter<LocalDate, LocalDate> {@Overridepublic LocalDate convertTo(LocalDate localDate, Type<LocalDate> type, MappingContext mappingContext) {return LocalDate.from(localDate);}@Overridepublic LocalDate convertFrom(LocalDate localDate, Type<LocalDate> type, MappingContext mappingContext) {return LocalDate.from(localDate);}}private static class LocalTimeConverter extends BidirectionalConverter<LocalTime, LocalTime> {@Overridepublic LocalTime convertTo(LocalTime localTime, Type<LocalTime> type, MappingContext mappingContext) {return LocalTime.from(localTime);}@Overridepublic LocalTime convertFrom(LocalTime localTime, Type<LocalTime> type, MappingContext mappingContext) {return LocalTime.from(localTime);}}/*** @Description 异常转换工具*/static class ExceptionsUtil {/**** <b>方法名:</b>:getStackTraceAsString<br>* <b>功能说明:</b>:将ErrorStack转化为String<br>*/public static String getStackTraceAsString(Exception e) {StringWriter stringWriter = new StringWriter();e.printStackTrace(new PrintWriter(stringWriter));return stringWriter.toString();}}/*** 分页对象复制* @param source      源对象* @param destinationClass 目标对象类型*/public static <S,D> Page<D> toPage(Page<S> source, Class<D> destinationClass) {if (EmptyUtil.isNullOrEmpty(source)){return null;}Class<? extends Page> handlerClass = source.getClass();Page<D> destination = mapper.map(source, handlerClass);destination.setRecords(mapper.mapAsList(source.getRecords(),destinationClass));return destination;}/**** @description 深度复制对象** @param source 源对象* @param destinationClass 目标类型* @return*/public static <T> T toBean(Object source, Class<T> destinationClass) {if (EmptyUtil.isNullOrEmpty(source)){return null;}return mapper.map(source, destinationClass);}/**** @description 深度复制对象** @param source 源对象* @param destinationClass 目标类型* @return*/public static <T> T toBean(Object source, Class<T> destinationClass, String... fieldsToIgnore) {try {T t = destinationClass.getDeclaredConstructor().newInstance();BeanUtils.copyProperties(source, t, fieldsToIgnore);return t;}catch (Exception e){ExceptionsUtil.getStackTraceAsString(e);return null;}}/**** @description 复制List** @param sourceList 源list对象* @param destinationClass 目标类型* @return*/public static <T> List<T> toBeanList(List<?> sourceList, Class<T> destinationClass) {if (EmptyUtil.isNullOrEmpty(sourceList)){return new ArrayList<>();}return mapper.mapAsList(sourceList,destinationClass);}}

判空工具:

package org.example.influxDd.util;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;/*** @Description 判断对象是否为空的工具类*/
public abstract class EmptyUtil {/**** @description 对string字符串是否为空判断** @param str 被判定字符串* @return*/public static boolean isNullOrEmpty(String str) {if (str == null || "".equals(str.trim()) || "null".equalsIgnoreCase(str.trim()) || "undefined".equalsIgnoreCase(str.trim())) {return true;} else {return false;}}/**** @description 对于StringBuffer类型的非空判断** @param str 被判定StringBuffer* @return*/public static boolean isNullOrEmpty(StringBuffer str) {return (str == null || str.length() == 0);}/**** @description 对于string数组类型的非空判断** @param str 被判定字符串数组* @return*/public static boolean isNullOrEmpty(String[] str) {if (str == null || str.length == 0) {return true;} else {return false;}}/**** @description 对于Object类型的非空判断** @param obj 被判定对象* @return*/public static boolean isNullOrEmpty(Object obj) {if (obj == null || "".equals(obj)) {return true;} else {return false;}}/**** @description 对于Object数组类型的非空判断** @param obj 被判定对象数组* @return*/public static boolean isNullOrEmpty(Object[] obj) {if (obj == null || obj.length == 0) {return true;} else {return false;}}/**** @description 对于Collection类型的非空判断** @param collection 被判定Collection类型对象* @return*/public static boolean isNullOrEmpty(Collection collection) {if (collection == null || collection.isEmpty()) {return true;} else {return false;}}/*** @方法名:对于Map类型的非空判断* @功能说明:对于Map类型的非空判断* @return boolean true-为空,false-不为空* @throws*/@SuppressWarnings("rawtypes")public static boolean isNullOrEmpty( Map map) {if (map == null || map.isEmpty()) {return true;} else {return false;}}/**** @方法名:removeNullUnit* @功能说明: 删除集合中的空元素* @return*/public static <T> List<T> removeNullUnit(List<T> xllxList) {List<T> need = new ArrayList<T>();for (int i = 0; i < xllxList.size(); i++) {if (!isNullOrEmpty(xllxList.get(i))) {need.add(xllxList.get(i));}}return need;}}

使用的关键依赖(除Spring框架外):

        <dependency><groupId>org.influxdb</groupId><artifactId>influxdb-java</artifactId><version>2.18</version></dependency><!--orika 拷贝工具 --><dependency><groupId>ma.glasnost.orika</groupId><artifactId>orika-core</artifactId><version>1.5.4</version></dependency><!--        切面--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency>

5_使用

经过上面的一系列封装,我们就可以使用类似mybatis注解的方式操作influxDB了。

我们来看下他在业务mapper中的使用:

import org.apache.ibatis.annotations.Mapper;
import org.example.influxDd.InfluxDBBaseMapper;
import org.example.influxDd.anno.Param;
import org.example.influxDd.anno.Select;import java.util.List;/*** BusinessLogMapper** @describe: 数据埋点日志持久层(influxDB)* @date: 2024/10/8 20:10*/
@Mapper
public interface BusinessLogMapper extends InfluxDBBaseMapper {/*** 每日新注册用户* @param begin yyyy-MM-dd HH:mm:ss* @param end yyyy-MM-dd HH:mm:ss* @return*/@Select(value = "SELECT * FROM log WHERE response_code = '200' and  time > #{begin} and time < #{end} and request_uri =~/register-user/",resultType = BusinessLog.class,database = "point_data")List<BusinessLog> dnu(@Param("begin")String begin, @Param("end")String end);}

Mock 的实体类对象

import lombok.Data;
import org.influxdb.annotation.Column;
import org.influxdb.annotation.Measurement;/*** BusinessLog.java* @describe: 数据埋点实体类--入库时间序列数据库*/
@Data
@Measurement(database = "point_data", name = "log")
public class BusinessLog {@Column(name = "request_id")public String requestId;@Column(name = "host")public String host;@Column(name = "host_address")public String hostAddress;@Column(name = "request_uri", tag = true)public String requestUri;@Column(name = "request_method", tag = true)public String requestMethod;@Column(name = "request_body")public String requestBody;@Column(name = "response_body")public String responseBody;@Column(name = "response_code", tag = true)public String responseCode;@Column(name = "response_msg")public String responseMsg;@Column(name = "user_id")public String userId;@Column(name = "user_name")public String userName;@Column(name = "business_type")public String businessType;@Column(name = "device_number")public String deviceNumber;@Column(name = "company_no")public String companyNO;@Column(name = "sex")public String sex;@Column(name = "create_by")public Long createBy;@Column(name = "update_by")public Long updateBy;@Column(name = "data_state", tag = true)public String dataState ="0";@Column(name = "province")public String province;@Column(name = "city")public String city;}

测试

package org.example;import org.example.entity.BusinessLog;
import org.example.mapper.BusinessLogMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;/*** @author shenyang* @version 1.0* @info all* @since 2024/10/12 23:25*/
@SpringBootTest
public class Test22 {@Resourceprivate BusinessLogMapper businessLogMapper;@Testpublic void test() {BusinessLog businessLog = new BusinessLog();businessLog.setRequestId("RequestId");businessLog.setHost("Host");businessLog.setHostAddress("HostAddress");businessLog.setRequestUri("RequestUri");businessLog.setRequestMethod("setRequestMethod");businessLog.setRequestBody("setRequestBody");businessLog.setResponseBody("setResponseBody");businessLog.setResponseCode("setResponseCode");businessLog.setResponseMsg("setResponseMsg");businessLog.setUserId("setUserId");businessLog.setUserName("setUserName");businessLog.setBusinessType("setBusinessType");businessLog.setDeviceNumber("setDeviceNumber");businessLog.setCompanyNO("setCompanyNO");businessLog.setSex("setSex");businessLog.setCreateBy(0L);businessLog.setUpdateBy(0L);businessLog.setDataState("setDataState");businessLog.setProvince("setProvince");businessLog.setCity("setCity");businessLogMapper.insertOne(businessLog);List<BusinessLog> dnu = businessLogMapper.dnu(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.of(2023, 12, 22, 00, 00)), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.of(2024, 12, 22, 00, 00)));System.out.println(dnu);}
}

6_总结

注意:本封装组件适用于1.X对于2.X由于认证方式的改变不能被正确兼容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/445693.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu22.04 ROS2 - 安装

参考链接:Ubuntu 22.04 LTS安装ROS2 (ros-humble-desktop)-CSDN博客 1、安装测试 ros2 run demo_nodes_cpp listener再开一个终端ros2 run demo_nodes_cpp listener2、小海龟模拟器 ros2 run turtlesim turtlesim_noderos2 run turtlesim turtle_teleop_key两个终端分别执行…

Linux内核与基础命令学习总结

Linux操作系统 Linux操作系统博大精深&#xff0c;其中对线程&#xff0c;IO&#xff0c;文件系统等概念的实现都很有借鉴意义。 ​ 文件系统和VFS 文件系统的inode上面讲过了。VFS主要用于屏蔽底层的不同文件系统&#xff0c;比如接入网络中的nfs文件系统&#xff0c;亦或…

用SpringBoot给Servlet容器Tomcat打war包步骤

首先写一个类来代替启动类 先在SpringBoot项目里打开pom.xml导入依赖,原本SpringBoot里面spring-boot-starter-web依赖里面有Tomcat,所以我们要先在spring-boot-starter-web里面导入依赖,把Tomcat给排除掉,并且加上你要打的war类型依赖 然后先刷新,再清除,最后再打包 成功之后,…

2024最新版安装教程!Python安装+PyCharm安装使用教程!!(非常简单)

Python下载安装 一、进入Python官网首页&#xff0c;下载最新版的Python 官方网址&#xff1a;Download Python | Python.org 鼠标悬浮在Downloads&#xff0c;选择最新版本 注意&#xff1a;由于Python官网服务器设立在国外&#xff0c;所以下载速度非常慢&#xff0c;我这…

管家婆-本地化-无法打开处理,链接失败

一、首先检测sql是否正常 二、检测管家婆svr是否正常 三、检测管家婆服务正常 阿雪技术观 拥抱开源与共享&#xff0c;见证科技进步奇迹&#xff0c;畅享人类幸福时光&#xff01; 让我们积极投身于技术共享的浪潮中&#xff0c;不仅仅是作为受益者&#xff0c;更要成为贡献…

数据库(1)

目录 1. 内存和外存的区别&#xff1f; 2. 常见的关系型数据库&#xff1a; 3. 常见的非关系型数据库&#xff1a; 4. 数据库与数据结构有什么关系&#xff1f; 5. SQL分类 6. 数据库的基本操作 7. 创建数据库 8. 删除数据库 9. 数值类型&#xff1a; 10. 字符串类型…

游戏如何应对薅羊毛问题

在大众眼里&#xff0c;“薅羊毛”是指在电商领域&#xff0c;“羊毛党”利用平台、商家的促销规则&#xff0c;低价获取商品和服务的行为。如前不久“小天鹅被一夜薅走7000万”的案例震惊全网。 然而实际上&#xff0c;“薅羊毛”现象不仅存在于电商场景&#xff0c;在游戏中…

【AAOS】Android Automotive 13模拟器源码下载及编译

源码下载 repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r69 repo sync -c --no-tags --no-clone-bundle 源码编译 source build/envsetup.sh lunch sdk_car_x86_64-userdebug make -j8 运行效果 emualtor Home Map All apps Sett…

提升邮件营销设计精准度秘诀,效率与效果实践

邮件营销通过确定目标群体、数据分析、邮件设计、测试优化、保持频率时效性及结合其他渠道实现精准营销&#xff0c;提高市场效益。ZohoCampaigns集成CRM、自动化功能和客户细分提升效果。 1、确定目标群体 精准营销的第一步是了解并确定你的目标群体。标定目标群体包括年龄、…

HUAWEI_HCIA_实验指南_Lib3.1_VLAN 基础配置及 Access 接口

1、原理概述 早期的局域网技术是基于总线型结构的。总线型拓扑结构是由一根单电缆连接着所有主机&#xff0c;这种局域网技术存在着冲突域问题&#xff0c;即所有用户都在一个冲突域中&#xff0c;那么同一时间内只有一台主机能发送消息&#xff0c;从任意设备发出的消息都会被…

单脉冲阵列和差波束形成实现比幅测角法(MATLAB仿真)

单脉冲阵列和差波束形成实现比幅测角法&#xff08;MATLAB仿真&#xff09; 文章目录 前言一、和差波束形成二、比幅测角法原理三、MATLAB仿真四、MATLAB仿真代码(超详细)单脉冲阵列和差波束形成实现比幅测角法MATLAB仿真超详细代码 总结 前言 单脉冲雷达天线要求产生一个主瓣…

【HTML格式PPT离线到本地浏览】

文章目录 概要实现细节小结 概要 最近在上课时总是出现网络不稳定导致的PPT无法浏览的情况出现&#xff0c;就想到下载到电脑上。但是PPT是一个HTML的网页&#xff0c;无法通过保存网页&#xff08;右键另存为mhtml只能保存当前页&#xff09;的形式全部下载下来&#xff0c;试…

在线书画展:艺术与科技携手,拓宽艺术之路

在数字化浪潮的推动下&#xff0c;在线书画展正成为艺术与科技完美融合的典范。它不仅拓宽了艺术的传播渠道&#xff0c;提升了个人书画家的开展效率&#xff0c;还促进了艺术家们的职业发展。以下是对在线书画展几大优势的深入探讨。 一、拓宽艺术传播渠道 全球化展示&#x…

蓝桥杯刷题--幸运数字

幸运数字 题目: 解析: 我们由题目可以知道,某个进制的哈沙德数就是该数和各个位的和取整为0.然后一个幸运数字就是满足所有进制的哈沙德数之和.然后具体就是分为以下几个步骤 1. 我们先写一个方法,里面主要是用来判断,这个数在该进制下是否是哈沙德数 2. 我们在main方法里面调用…

在centos(ubuntu)中如何通过预构建二进制文件安装nodejs

首先去Node.js下载你说需要的版本的预构建二进制文件Node.js — 下载 Node.js 在CentOs或Ubuntu离线服务器上安装Node.js&#xff0c;你可以通过下载Node.js的预构建二进制文件来完成。以下是具体步骤&#xff1a; 获取Node.js预构建二进制文件&#xff1a; 在有网络连接的机器…

spring boot 项目配置文件

第一种properties文件&#xff08;自带基础&#xff09; 新建项目是会在resources目录下默认properties文件 第二种yml文件&#xff08;常用&#xff09; 格式类型&#xff1a;spring boot支持3种配置文件&#xff0c;分别是xx.properties,xx.yaml,xx.yml;同一个项目若配置这3种…

【动物识别系统】Python+卷积神经网络算法+人工智能+深度学习+机器学习+计算机课设项目+Django网页界面

一、介绍 动物识别系统。本项目以Python作为主要编程语言&#xff0c;并基于TensorFlow搭建ResNet50卷积神经网络算法模型&#xff0c;通过收集4种常见的动物图像数据集&#xff08;猫、狗、鸡、马&#xff09;然后进行模型训练&#xff0c;得到一个识别精度较高的模型文件&am…

每日一题|3158. 求出出现两次数字的 XOR 值|哈希

题目给的范围很小&#xff0c;50以内&#xff0c;所以什么数据结构都可以。 这里采用set来维护访问过的数字&#xff0c;利用哈希来提升时间效率。 class Solution:def duplicateNumbersXOR(self, nums: List[int]) -> int:visited set()l []res 0for i in nums:if i i…

《机器学习与数据挖掘综合实践》实训课程教学解决方案

一、引言 随着信息技术的飞速发展&#xff0c;人工智能已成为推动社会进步的重要力量。作为人工智能的核心技术之一&#xff0c;机器学习与数据挖掘在各行各业的应用日益广泛。本方案旨在通过系统的理论教学、丰富的实践案例和先进的实训平台&#xff0c;帮助学生掌握机器学习…

【C++堆(优先队列)】1882. 使用服务器处理任务|1979

本文涉及知识点 C堆(优先队列) LeetCode1882. 使用服务器处理任务 给你两个 下标从 0 开始 的整数数组 servers 和 tasks &#xff0c;长度分别为 n​​​​​​ 和 m​​​​​​ 。servers[i] 是第 i​​​​​​​​​​ 台服务器的 权重 &#xff0c;而 tasks[j] 是处理…