InfluxDB持久层封装

news2024/10/17 9:38:34

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:8086
    password: 123456
    user: 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,所以嵌套List
                    List<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就存入null
                                queryMap.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);
            }
            //获取数据库名和rp
            Measurement 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
     */
    @SneakyThrows
    public 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声明返回结果为list
        if (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();
    }

    @SneakyThrows
    public 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 {
    //执行的influxQL
    String 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;

    @Autowired
    public 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();
        //获得执行sql
        String 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> {

    @Insert
    void insertOne(T entity);

    @Insert
    void 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> {

        @Override
        public LocalDateTime convertTo(LocalDateTime localDateTime, Type<LocalDateTime> type, MappingContext mappingContext) {
            return LocalDateTime.from(localDateTime);
        }

        @Override
        public LocalDateTime convertFrom(LocalDateTime localDateTime, Type<LocalDateTime> type, MappingContext mappingContext) {
            return LocalDateTime.from(localDateTime);
        }
    }
    private static class LocalDateConverter extends BidirectionalConverter<LocalDate, LocalDate> {
        @Override
        public LocalDate convertTo(LocalDate localDate, Type<LocalDate> type, MappingContext mappingContext) {
            return LocalDate.from(localDate);
        }

        @Override
        public LocalDate convertFrom(LocalDate localDate, Type<LocalDate> type, MappingContext mappingContext) {
            return LocalDate.from(localDate);
        }
    }
    private static class LocalTimeConverter extends BidirectionalConverter<LocalTime, LocalTime> {

        @Override
        public LocalTime convertTo(LocalTime localTime, Type<LocalTime> type, MappingContext mappingContext) {
            return LocalTime.from(localTime);
        }

        @Override
        public 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 {


    @Resource
    private BusinessLogMapper businessLogMapper;

    @Test
    public 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.coloradmin.cn/o/2209689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第十五届蓝桥杯C/C++学B组(解)

1.握手问题 解题思路一 数学方法 50个人互相握手 &#xff08;491&#xff09;*49/2 &#xff0c;减去7个人没有互相握手&#xff08;61&#xff09;*6/2 答案&#xff1a;1024 解题思路二 package 十五届;public class Min {public static void main(String[] args) {i…

实时从TDengine数据库采集数据到Kafka Topic

实时从TDengine数据库采集数据到Kafka Topic 一、认识TDengine二、TDengine Kafka Connector三、什么是 Kafka Connect&#xff1f;四、前置条件五、安装 TDengine Connector 插件六、启动 Kafka七、验证 kafka Connect 是否启动成功八、TDengine Source Connector 的使用九、添…

【更新】A股上市公司企业网络安全治理数据集(2007-2023年)

一、测算方式&#xff1a;参考C刊《金融评论》王辉&#xff08;2024&#xff09;老师的做法&#xff0c;安全治理种子词的选取主要依托于《中华人民共和国网络安全法》、《中华人民共和国数据安全法》、《关键信息基础设施安全保护条例》等法律法规文件与《网络安全审查办法》、…

蓝桥杯刷题--幸运数字

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

量化之一:均值回归策略

文章目录 均值回归策略理论基础数学公式 关键指标简单移动平均线&#xff08;SMA&#xff09;标准差Z-Score 交易信号实际应用优缺点分析优点缺点 结论 实践backtrader参数&#xff1a;正常情况&#xff1a;异常情况&#xff1a; 均值回归策略 均值回归&#xff08;Mean Rever…

华为公有云实战

1.申请一台ECS云主机&#xff0c;并且可以提供web服务 1.1访问云主机-华为特有技术novnc&#xff0c;KVM中提到vnc技术&#xff0c;novnc是不用安装vnc客户端用浏览器html语言实现。 1.2cloudshell 1.3小工具 ssh 弹性ip 1.4.安装httpd服务 建立索引文件 浏览器上输入弹性ip可…

网络资源模板--Android Studio 实现简易记事本App

目录 一、项目演示 二、项目测试环境 三、项目详情 四、完整的项目源码 一、项目演示 网络资源模板--基于Android studio 实现的简易记事本App 二、项目测试环境 三、项目详情 首页 创建一个空的笔记本列表 mNotebookList。使用该列表和指定的布局资源 item_notebook 创建…

前端开发笔记--html 黑马程序员1

文章目录 前端开发工具--VsCode前端开发基础语法VsCode优秀插件Chinese --中文插件Auto Rename Tag --自动重命名插件open in browserOpen in Default BrowserOpen in Other Browser Live Server -- 实时预览 前端开发工具–VsCode 轻量级与快速启动 快速加载&#xff1a;VSCo…

WordPress添加meta标签做seo优化

一、使用function.php文件添加钩子函数添加 方法1、使用is_page()判断不同页面的page_id进行辨别添加不同页面keyword和description &#xff08;1&#xff09;通过页面前台源码查看对应页面的id &#xff08;2&#xff09;或者通过wordpress后台&#xff0c;点击页面列表&…

云计算ftp 服务器实验

创建VLAN 10 划分端口 创建VLAN 10 的地址 10.1.1.1 服务器的地址是 10.1.1.2 这是服务上的配置 服务器上选择ftp 启动 &#xff0c;文件目录选择一下 在 交换机上 ftp 10.1.1.2 服务器的地址 把刚才创建的shenyq txt 文件下载下到本地交换机 我们能看到交换…

有关安科瑞Acrel-1000DP分布式光伏监控系统在某公司分布式光伏发电项目中的应用探讨-安科瑞 蒋静

摘要&#xff1a;分布式光伏作为可再生能源的一种重要形式&#xff0c;能够根据不同场地的实际情况进行定制&#xff0c;尽可能地利用可用空间&#xff0c;减少对传统化石燃料的依赖&#xff0c;也能降低温室气体排放、改善环境质量。在政策支持和市场需求的双重推动下&#xf…

电脑查不到IP地址是什么原因?怎么解决

在日常使用电脑的过程中&#xff0c;有时会遇到无法查询到电脑IP地址的情况&#xff0c;这可能会影响到网络的正常使用。本文将探讨电脑查不到IP地址的可能原因&#xff0c;并提供相应的解决方案。 一、原因分析 ‌网络连接问题‌&#xff1a;首先&#xff0c;网络连接不稳定或…

MySQL(B站CodeWithMosh)——2024.10.11(14)

ZZZZZZ目的ZZZZZZ代码ZZZZZZ重点ZZZZZZ操作&#xff08;非代码&#xff0c;需要自己手动&#xff09; 8- CASE运算符The CASE Operator_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1UE41147KC?p62&vd_sourceeaeec77dfceb13d96cce76cc299fdd08 在sql_store中&am…

智能网联汽车安全隐患,如何化解?

0. 智能网联汽车安全问题如何才能解决&#xff1f;1. TARA 威胁分析与风险评估平台2. CSTP 智能网联汽车网络安全测试平台3. 智能网联汽车安全解决方案4. 车联网测试认证与培训解决方案5. 车联网网络安全实验室建设方案 0. 智能网联汽车安全问题如何才能解决&#xff1f; 智能…

FFmpeg的简单使用【Windows】--- 简单的视频混合拼接

实现功能 点击【选择文件】按钮在弹出的对话框中选择多个视频&#xff0c;这些视频就是一会将要混剪的视频素材&#xff0c;点击【开始处理】按钮之后就会开始对视频进行处理&#xff0c;处理完毕之后会将处理后的文件路径返回&#xff0c;并在页面展示处理后的视频。 视频所…

【数据结构】排序算法系列——桶排序(附源码+图解)

桶排序 算法思想 桶排序&#xff08;BucketSort)&#xff0c;也被叫做箱排序&#xff0c;它将整个数据组分为n个相同大小的子区间&#xff0c;这类子区间或称为桶。输入数据是均匀、独立分布的&#xff0c;所以一般不会出现一个桶中装有过多数据的情况。作为一种排序算法&…

160页PPT | 埃森哲-制造业变革转型八大领域:痛点剖析与改进策略

PT下载链接见文末~ 引言&#xff1a;制造业数字化转型规划 制造业正处于数字化转型的关键时期&#xff0c;旨在通过技术革新和流程优化&#xff0c;灵活应对市场波动&#xff0c;强化竞争优势&#xff0c;并紧跟技术进步的步伐。此规划围绕三大核心要素展开&#xff1a; 1、…

Pytest中fixture的scope详解

pytest作为Python技术栈下最主流的测试框架&#xff0c;功能极为强大和灵活。其中Fixture夹具是它的核心。而且pytest中对Fixture的作用范围也做了不同区分&#xff0c;能为我们利用fixture带来很好地灵活性。 下面我们就来了解下这里不同scope的作用 fixture的scope定义 首…

8.优化存储过程的性能(8/10)

优化存储过程的性能 1.引言 存储过程是数据库系统中预先编写好的SQL语句集合&#xff0c;它们被保存在数据库服务器上&#xff0c;可以在需要时被调用执行。存储过程的使用可以提高数据库操作的效率&#xff0c;减少网络通信&#xff0c;并且可以封装复杂的逻辑&#xff0c;使…

中科星图GVE(案例)——AI实现建筑用地变化前后对比情况

目录 简介 函数 gve.Services.AI.ConstructionLandChangeExtraction(image1,image2) 代码 结果 知识星球 机器学习 简介 AI可以通过分析卫星图像、航拍影像或其他地理信息数据&#xff0c;实现建筑用地变化前后对比。以下是一种可能的实现方法&#xff1a; 数据获取&am…