Apache DuridApache Durid1. Durid概述Apache Druid是一个集时间序列数据库、数据仓库和全文检索系统特点于一体的分析性数据平台。本文将带你简单了解Druid的特性,使用场景,技术特点和架构。这将有助于你选型数据存储方案,深入了解Druid存储,深入了解时间序列存储等。 1.1 为什么使用1.1.1 云原生数据库一个现代化的云原生,流原生,分析型数据库 1.1.2 轻松集成轻松与现有的数据管道集成 1.1.3 超高性能比传统方案快100倍的性能
1.1.4 工作流解锁新的工作流 1.1.5 多种部署方式可以部署在AWS/GCP/Azure,混合云,k8s和租用服务器上 1.2 使用场景Apache Druid适用于对实时数据提取,高性能查询和高可用要求较高的场景。因此,Druid通常被作为一个具有丰富GUI的分析系统,或者作为一个需要快速聚合的高并发API的后台。Druid更适合面向事件数据。 1.2.1 常见的使用场景比较常见的使用场景 1.2.1.1 用户活动和行为Druid经常用在点击流,访问流,和活动流数据上。具体场景包括:衡量用户参与度,为产品发布追踪A/B测试数据,并了解用户使用方式。Druid可以做到精确和近似计算用户指标,例如不重复计数指标。这意味着,如日活用户指标可以在一秒钟计算出近似值(平均精度98%),以查看总体趋势,或精确计算以展示给利益相关者。Druid可以用来做“漏斗分析”,去测量有多少用户做了某种操作,而没有做另一个操作。这对产品追踪用户注册十分有用。 1.2.1.2 网络流Druid常常用来收集和分析网络流数据。Druid被用于管理以任意属性切分组合的流数据。Druid能够提取大量网络流记录,并且能够在查询时快速对数十个属性组合和排序,这有助于网络流分析。这些属性包括一些核心属性,如IP和端口号,也包括一些额外添加的强化属性,如地理位置,服务,应用,设备和ASN。Druid能够处理非固定模式,这意味着你可以添加任何你想要的属性。 1.2.1.3 数字营销Druid常常用来存储和查询在线广告数据。这些数据通常来自广告服务商,它对衡量和理解广告活动效果,点击穿透率,转换率(消耗率)等指标至关重要。 1.2.1.4 应用性能管理Druid常常用于追踪应用程序生成的可运营数据。和用户活动使用场景类似,这些数据可以是关于用户怎样和应用程序交互的,它可以是应用程序自身上报的指标数据。Druid可用于下钻发现应用程序不同组件的性能如何,定位瓶颈,和发现问题。 1.2.1.5 物联网和设备指标Driud可以作为时间序列数据库解决方案,来存储处理服务器和设备的指标数据。收集机器生成的实时数据,执行快速临时的分析,去估量性能,优化硬件资源,和定位问题。 1.2.1.6 OLAP和商业智能Druid经常用于商业智能场景。公司部署Druid去加速查询和增强应用。和基于Hadoop的SQL引擎(如Presto或Hive)不同,Druid为高并发和亚秒级查询而设计,通过UI强化交互式数据查询。这使得Druid更适合做真实的可视化交互分析。 1.2.2 适合的场景如果您的使用场景符合以下的几个特征,那么Druid是一个非常不错的选择:
1.2.3 不适合的场景如果您的使用场景符合以下特征,那么使用Druid可能是一个不好的选择:
2. Durid是什么Apache Druid 是一个开源的分布式数据存储引擎。 2.1 主要特性列式存储 2.2 和其他技术对比
ES:最大的特点是使用了倒排索引解决索引问题。根据研究,ES在数据获取和聚集用的资源比在Druid高。
2.3 数据摄入Druid同时支持流式和批量数据摄入。Druid通常通过像Kafka这样的消息总线(加载流式数据)或通过像HDFS这样的分布式文件系统(加载批量数据)来连接原始数据源。 2.4 数据存储像大多数分析型数据库一样,Druid采用列式存储。根据不同列的数据类型(string,number等),Druid对其使用不同的压缩和编码方式。Druid也会针对不同的列类型构建不同类型的索引。 2.5 查询Druid支持JSON-over-HTTP和SQL两种查询方式。除了标准的SQL操作外,Druid还支持大量的唯一性操作,利用Druid提供的算法套件可以快速的进行计数,排名和分位数计算。 数据副本 3. 安装部署3.1 环境介绍Durid端口列表 3.2 安装方式获取Druid安装包有以下几种方式 3.2.1 源代码编译druid/release,主要用于定制化需求时,比如结合实际环境中的周边依赖,或者是加入支持特定查询的部分的优化必定等。 3.2.2 官网下载官网安装包下载:download,包含Druid部署运行的最基本组件 3.2.3 Imply组合套件Imply,该套件包含了稳定版本的Druid组件、实时数据写入支持服务、图形化展示查询Web UI和SQL查询支持组件等,目的是为更加方便、快速地部署搭建基于Druid的数据分析应用产品。 3.3 单机配置参考3.3.1 Nano-Quickstart1 CPU, 4GB 内存 3.3.2 微型快速入门4 CPU, 16GB 内存 3.3.3 小型8 CPU, 64GB 内存 (~i3.2xlarge) 3.3.4 中型16 CPU, 128GB 内存 (~i3.4xlarge) 3.3.5 大型32 CPU, 256GB 内存 (~i3.8xlarge) 3.3.6 超大型64 CPU, 512GB 内存 (~i3.16xlarge) 3.4 单机版安装3.4.1 软件要求Java 8 (8u92+) 3.4.2 硬件要求Druid包括几个单服务配置示例,以及使用这些配置启动Druid进程的脚本。 3.5 imply方式安装安装推荐Imply方式,Imply方式出了提供druid组件,还有图形化、报表等功能 3.5.1 安装perl因为启动druid 需要用到perl环境,需要安装下 yum install perl gcc kernel-devel 3.5.2 关闭防火墙#查看防火状态 systemctl status firewalld #暂时关闭防火墙 systemctl stop firewalld #永久关闭防火墙 systemctl disable firewalld 3.5.3 安装JDK选择与自己系统相匹配的版本,我的是Centos7 64位的,所以如果是我的话我会选择此版本,要 3.5.3.1 下载JDK到Oracle 官网下载jdk1.8,选择 jdk-8u301-linux-x64.tar.gz 上传文件,解压目录 mkdir /usr/local/java tar -zxvf jdk-8u301-linux-x64.tar.gz 配置环境变量 vi /etc/profile export JAVA_HOME=/usr/local/java/jdk1.8.0_291 export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools .jar export PATH=$PATH:$JAVA_HOME/bin
source /etc/profile 检查环境 java -version 3.5.4 安装imply3.5.4.1 登录Imply官网访问https:///get-started,进入Imply官网,查找合适的imply的版本的安装包,并填写简要信息后就可以下载了 # 创建imply安装目录 mkdir /usr/local/imply # 解压imply tar -zxvf imply-2021.05-1.tar.gz 环境准备 # 进入imply目录 cd imply-2021.05-1 快速启动 # 创建日志目录 mkdir logs # 使用命令启动 nohup bin/supervise -c conf/supervise/quickstart.conf > logs/quickstart.log 2>&1 & 查看日志 tail -f logs/quickstart.log
tail -f var/sv/broker/current 访问Imply http://localhost:9095/ 访问Druid http://localhost:8888 4. 数据摄取4.1 加载本地文件我们导入演示案例种的演示文件 4.1.1.1 数据选择通过UI选择 local disk 4.1.1.2 演示数据查看演示数据在 quickstart/tutorial 目录下的 wikiticker-2015-09-12-sampled.json.gz 文件 4.1.1.3 选择数据源因为我们是通过 imply 安装的,在 Base directory 输入绝对路径 /usr/local/imply/imply- 2021.05-1/dist/druid/quickstart/tutorial , File filter 输入 wikiticker-2015-09-12- sampled.json.gz ,并选择 apply 应用配置,我们数据已经加载进来了
4.1.1.4 加载数据数据定位后,您可以点击"Next: Parse data"来进入下一步。 4.1.2 数据源规范配置4.1.2.1 设置时间列json 选择器被选中后,点击 Next:Parse time 进入下一步来决定您的主时间列。 4.1.2.2 设置转换器在这里可以新增虚拟列,将一个列的数据转换成另一个虚拟列,这里我们没有设置,直接跳过 4.1.2.3 设置过滤器这里可以设置过滤器,对于某些数据可以不进行显示,这里我们也跳过 4.1.2.4 配置schema在 Configure schema 步骤中,您可以配置将哪些维度和指标摄入到Druid中,这些正是数据在被Druid中摄取后出现的样子。 由于我们的数据集非常小,关掉rollup、确认更改 4.1.2.5 配置Partition一旦对schema满意后,点击 Next 后进入 Partition 步骤,该步骤中可以调整数据如何划分为段文件的方式,因为我们数据量非常小,这里我们按照 DAY 进行分段 4.1.3 提交任务发布数据 4.2 kafka加载流式数据4.2.1 安装Kafka这里我们使用 docker-compose 的方式启动kafka 4.2.1.1 编辑资源清单vi docker-compose.yml version: '2' services: kafka: image: wurstmeister/kafka ## 镜像 volumes: - /etc/localtime:/etc/localtime ## 挂载位置(kafka镜像和宿主机器之间时间保持一直) ports: - "9092:9092" environment: KAFKA_ADVERTISED_HOST_NAME: 192.168.64.186 ## 修改:宿主机IP KAFKA_ZOOKEEPER_CONNECT: 192.168.64.186:2181 ## 卡夫卡运行是基于zookeeper的 KAFKA_ADVERTISED_PORT: 9092 KAFKA_LOG_RETENTION_HOURS: 120 KAFKA_MESSAGE_MAX_BYTES: 10000000 KAFKA_REPLICA_FETCH_MAX_BYTES: 10000000 KAFKA_GROUP_MAX_SESSION_TIMEOUT_MS: 60000 KAFKA_NUM_PARTITIONS: 3 KAFKA_DELETE_RETENTION_MS: 1000 启动容器 docker-compose up -d docker-compose ps
#进入容器 docker exec -it kafka_kafka_1 bash #进入 /opt/kafka_2.13-2.7.0/bin/ 目录下 cd /opt/kafka_2.13-2.7.0/bin/
#运行kafka生产者发送消息 ./kafka-console-producer.sh --broker-list 192.168.64.173:9092 --topic test 发送的数据如下 {"datas": [{"channel":"","metric":"temperature","producer":"ijinus","sn":"IJA0101- 00002245","time":"1543207156000","value":"80"}],"ver":"1.0"}
./kafka-console-consumer.sh --bootstrap-server 192.168.64.173:9092 --topic test --from-beginning
4.2.4 发送数据到kafka编写代码 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Component; @Component public class KafkaSender { @Autowired private KafkaTemplate<String, String> kafkaTemplate; /** * 发送消息到kafka * * @param topic 主题 * @param message 内容体 */ public void sendMsg(String topic, String message) { kafkaTemplate.send(topic, message); } } @RestController @RequestMapping("/taxi") public class KafkaController { @Autowired private KafkaSender kafkaSender; @RequestMapping("/batchTask/{num}") public String batchAdd(@PathVariable("num") int num) { for (int i = 0; i < num; i++) { Message message = Utils.getRandomMessage(); kafkaSender.sendMsg("message", JSON.toJSONString(message)); } return "OK"; } @RequestMapping("/update") public String update(@RequestBody Message message) { kafkaSender.sendMsg("messageupdate", JSON.toJSONString(message)); return "OK"; } } server: port: 8010 spring: application: name: druid-kafka-service ######################### 数据源连接池的配置信息 ################# kafka: bootstrap-servers: localhost:9092 producer: # producer 生产者 retries: 0 # 重试次数 acks: 1 # 应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1) batch-size: 16384 # 批量大小 buffer-memory: 33554432 # 生产端缓冲区大小 key-serializer: org.apache.kafka.common.serialization.StringSerializer # value-serializer: com.itheima.demo.config.MySerializer value-serializer: org.apache.kafka.common.serialization.StringSerializer 发送消息 # 4.2.5 数据选择kafka数据查看 localhost:9092
case_simple(status,0,'测试数据',1,'发起打车',2,'排队中',3,'司机接单',4,'完成打车','状态 错误') 在这里我们新建了一个 status_text 的虚拟列来展示需要中文显示的列 nvl(age,25)
{ "type" : "bound", "dimension" : "status", "ordering": "numeric", "lower": "1", } 因为我们把数据是0的测试数据不显示了,所以只显示了一条数据为1的数据
查看数据源 4.2.8 清理数据关闭集群 # 进入impl安装目录 cd /usr/local/imply/imply-2021.05-1 # 关闭集群 ./bin/service --down
ps -ef|grep druid
ll rm -rf var
nohup bin/supervise -c conf/supervise/quickstart.conf > logs/quickstart.log 2>&1 & 查看数据源 5. 数据查询Druid支持JSON-over-HTTP和SQL两种查询方式。除了标准的SQL操作外,Druid还支持大量的唯一性操作,利用Druid提供的算法套件可以快速的进行计数,排名和分位数计算。 5.1 准备工作5.1.1 导入大量数据准备大量数据提供查询,我们插入1万条随机打车数据 http://localhost:8010/taxi/batchTask/100000 查看数据摄取进程 5.2 原生查询Druid 最开始的时候是不支持 SQL 查询的,原生查询是通过查询 Broker 提供的 http server 来实现的 5.2.1 查询语法curl -L -H'Content-Type:application/json' -XPOST --data-binary @<query_json_file> <queryable_host>:<port>/druid/v2/?pretty 查询案例 # 创建查询目录 mkdir query # 编辑查询的JSON vi query/filter1.json { "queryType":"timeseries", "dataSource":"taxi_message", "granularity":"month", "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{"type":"selector","dimension":"status","value":1}, "intervals":["2021-06-07/2022-06-07"] } 参数解释
执行查询命令 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/filter1.json http://192.168.64.177:8082/druid/v2/?pretty 我们查询了每个月发起打车的人数有多少 5.3 查询类型druid查询采用的是HTTP RESTFUL方式,REST接口负责接收客户端的查询请求,客户端只需要将查询条件封装成JSON格式,通过HTTP方式将JSON查询条件发送到broker节点,查询成功会返回JSON格式的结果数据。了解一下druid提供的查询类型 5.4.1 时间序列查询timeseries时间序列查询对于指定时间段按照查询规则返回聚合后的结果集,查询规则中可以设置查询粒度,结果排序方式以及过滤条件,过滤条件可以使用嵌套过滤,并且支持后聚合。 查询属性时间序列查询主要包括7个主要部分 { "queryType":"topN", "dataSource":"taxi_message", "dimension":"local", "threshold":2, "metric":"age", "granularity":"month", "aggregations":[ { "type":"longMin", "name":"age", "fieldName":"age" } ], "filter":{"type":"selector","dimension":"sex","value":"也"}, "intervals":["2021-06-07/2022-06-07"] } 5.4.2 TopN查询topn查询是通过给定的规则和显示维度返回一个结果集,topn查询可以看做是给定排序规则,返回单一维度的group by查询,但是topn查询比group by性能更快。metric这个属性是topn专属的按照该指标排序。 vi query/topN.json { "queryType":"topN", "dataSource":"taxi_message", "dimension":"local", "threshold":2, "metric":"age", "granularity":"Quarter", "aggregations":[ { "type":"longMin", "name":"age", "fieldName":"age" } ], "filter":{"type":"selector","dimension":"sex","value":"也"}, "intervals":["2021-06-07/2022-06-07"] } 执行查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/topN.json http://192.168.64.177:8082/druid/v2/?pretty 5.4.5 分组查询在实际应用中经常需要进行分组查询,等同于sql语句中的Group by查询,如果对单个维度和指标进行分组聚合计算,推荐使用topN查询,能够获得更高的查询性能,分组查询适合多维度,多指标聚合查询 查询属性 vi query/groupBy.json { "queryType":"groupBy", "dataSource":"taxi_message", "granularity":"Quarter", "dimensions":["sex"], "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{ "type":"bound", "dimension":"age", "lower":"21", "upper":"31", "alphaNumeric":true }, "intervals":["2021-06-07/2022-06-07"] } 执行查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/groupBy.json http://192.168.64.177:8082/druid/v2/?pretty 5.5 查询组件在介绍具体的查询之前,我们先来了解一下各种查询都会用到的基本组件,如 5.5.1 FilterFilter就是过滤器,在查询语句中就是一个JSON对象,用来对维度进行筛选和过滤,表示维度满足 "Filter":{"type":"selector","dimension":dimension_name,"value":target_value} 使用案例 vi query/filter1.json { "queryType":"timeseries", "dataSource":"taxi_message", "granularity":"month", "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{"type":"selector","dimension":"status","value":1}, "intervals":["2021-06-07/2022-06-07"] } 正则过滤器 "filter":{"type":"regex","dimension":dimension_name,"pattern":regex} 使用案例,我们搜索姓名包含数字的的用户进行聚合统计 vi query/filter2.json { "queryType":"timeseries", "dataSource":"taxi_message", "granularity":"month", "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{"type":"regex","dimension":"username","pattern":"[0-9]{1,}"}, "intervals":["2021-06-07/2022-06-07"] } 执行查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/filter2.json http://192.168.64.177:8082/druid/v2/?pretty
"filter":{"type":"and","fields":[filter1,filter2]} "filter":{"type":"or","fields":[filter1,filter2]} "filter":{"type":"not","fields":[filter]} 使用案例,我们查询每一个月,进行打车并且是女性的数量 vi query/filter3.json { "queryType":"timeseries", "dataSource":"taxi_message", "granularity":"month", "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{ "type":"and", "fields":[ {"type":"selector","dimension":"status","value":1}, {"type":"selector","dimension":"sex","value":"也"} ] }, "intervals":["2021-06-07/2022-06-07"] } 进行数据查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/filter3.json http://192.168.64.177:8082/druid/v2/?pretty
{ "type":"in", "dimension":"local", "values":['四川省','江西省','福建省'] } 使用案例,我们查询每一个月,在四川省、江西省、福建省打车的人数 vi query/filter4.json { "queryType":"timeseries", "dataSource":"taxi_message", "granularity":"month", "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{ "type":"in", "dimension":"local", "values":["四川省","江西省","福建省"] }, "intervals":["2021-06-07/2022-06-07"] } 进行数据查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/filter4.json http://192.168.64.177:8082/druid/v2/?pretty
{ "type":"bound", "dimension":"age", "lower":"21", #默认包含等于 "upper":"31", #默认包含等于 "alphaNumeric":true #数字比较时指定alphaNumeric为true } 使用案例,我们查询每一个月,年龄在21-31之间打车人的数量 vi query/filter5.json { "queryType":"timeseries", "dataSource":"taxi_message", "granularity":"month", "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{ "type":"bound", "dimension":"age", "lower":"21", "upper":"31", "alphaNumeric":true }, "intervals":["2021-06-07/2022-06-07"] } 进行数据查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/filter5.json http://192.168.64.177:8082/druid/v2/?pretty
Simple的聚合粒度
编写测试,我们这里按照季度聚合,并且我们过滤年龄是21-31的数据,并且按照地域以及性别进行分组 vi query/filter6.json { "queryType":"groupBy", "dataSource":"taxi_message", "granularity":"Quarter", "dimensions":["local","sex"], "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{ "type":"bound", "dimension":"age", "lower":"21", "upper":"31", "alphaNumeric":true }, "intervals":["2021-06-07/2022-06-07"] } 进行查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/filter6.json http://192.168.64.177:8082/druid/v2/?pretty
vi query/filter7.json { "queryType":"groupBy", "dataSource":"taxi_message", "granularity":{ "type":"duration", "duration":7200000 }, "dimensions":["local","sex"], "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{ "type":"bound", "dimension":"age", "lower":"21", "upper":"31", "alphaNumeric":true }, "intervals":["2021-06-07/2021-06-10"] } 数据查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/filter7.json http://192.168.64.177:8082/druid/v2/?pretty
vi query/filter8.json { "queryType":"groupBy", "dataSource":"taxi_message", "granularity":{ "type":"period", "period":"P1D" }, "dimensions":["sex"], "aggregations":[ { "type":"count", "name":"taxiNum" } ], "filter":{ "type":"bound", "dimension":"age", "lower":"21", "upper":"31", "alphaNumeric":true }, "intervals":["2021-06-07/2021-06-10"] } 数据查询 curl -L -H 'Content-Type:application/json' -XPOST --data-binary @query/filter8.json http://192.168.64.177:8082/druid/v2/?pretty 5.5.2聚合器Aggregator是聚合器,聚合器可以在数据摄入阶段和查询阶段使用,在数据摄入阶段使用聚合器能够在数据被查询之前按照维度进行聚合计算,提高查询阶段聚合计算性能,在查询过程中,使用聚合器能够实现各种不同指标的组合计算。 公共属性
计数聚合 vi query/aggregator1.json { "queryType":"timeseries", "dataSource":"taxi_message", "granularity":"Quarter", "aggregations":[ { "type":"count", "name":"count" } ], "filter":{ "type":"bound", "dimension":"age", "lower":"21", "upper":"31", "alphaNumeric":true }, "intervals":["2021-06-07/2022-06-07"] } 求合聚合 vi query/aggregator2.json { "queryType":"timeseries", "dataSource":"taxi_message", "granularity":"Quarter", "aggregations":[ { "type":"longSum", "name":"ageSum", "fieldName":"age" } ], "filter":{ "type":"bound", "dimension":"age", "lower":"21", "upper":"31", "alphaNumeric":true }, "intervals":["2021-06-07/2022-06-07"] } 5.6 Druid SQLDruid SQL是一个内置的SQL层,是Druid基于JSON的本地查询语言的替代品,它由基于 ApacheCalcite的解析器和规划器提供支持 5.6.1基本查询5.6.1.1查询数据总条数可以在druid的控制台进行查询 select count(1) from "taxi_message"
5.6.2 聚合功能聚合函数可以出现在任何查询的SELECT子句中。可以使用类似语法过滤任何聚合器 AGG(expr) FILTER(WHERE whereExpr) 。过滤的聚合器仅聚合与其过滤器匹配的行。同一SQL查询中的两个聚合器可能具有不同的筛选器。 select count(1) from "taxi_message" 5.7 客户端API我们在这里实现SpringBoot+Mybatis实现SQL查询ApacheDruid数据 5.7.1 引入Pom依赖<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.apache.calcite.avatica</groupId> <artifactId>avatica</artifactId> <version>1.18.0</version> </dependency> <dependency> <groupId>org.apache.calcite.avatica</groupId> <artifactId>avatica-server</artifactId> <version>1.18.0</version> </dependency> </dependencies> 配置数据源连接 spring: datasource: # 连接池信息 url: jdbc:avatica:remote:url=http://192.168.64.177:8082/druid/v2/sql/avatica/ # 驱动信息 driver-class-name: org.apache.calcite.avatica.remote.Driver 编写代码 @Data @ToString public class TaxiMessage { private String __time; private Integer age; private Integer createDate; private String local; private String sex; private Integer status; private String statusText; private String username; } 编写mapper @Mapper public interface TaxiMessageMapper { @Select("SELECT * FROM \"taxi_message\" where username=#{username}") public TaxiMessage findByUserName(String username); } 编写Service @Service public class TaxiMessageService { @Autowired private TaxiMessageMapper taxiMessageMapper; public TaxiMessage findByUserName(String username) { return taxiMessageMapper.findByUserName(username); } } 编写启动类 @SpringBootApplication @MapperScan(basePackages = "com.druid.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } } ** 编写测试类** @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class DruidTest { @Autowired private TaxiMessageService taxiMessageService; @Test public void test() { TaxiMessage taxiMessage = taxiMessageService.findByUserName("eFQfLXmXD4"); System.out.println(taxiMessage); Assert.assertNotNull(taxiMessage); } } |
|