如何将Mybatis连接到ClickHouse
作者:空夜 发布时间:2023-11-06 02:35:51
场景
最近在做数据分析项目,里面有这样一个业务:把匹配的数据打上标签,放到新的索引中。
数据量:累计亿级的数据
使用场景:可能会单次查询大量的数据,但不会设置复杂的条件,且这些数据不会被再次修改
原来使用的数据库:ElasticSearch
问题:上面也说了我这里打上标记后,这些数据几乎不会再修改了。ES 是一个全文检索引擎,更适用于进行大量文本检索的情况。这里与我上面的使用场景就不太匹配了。
技术选型的考虑:改用战斗民族开发的 ClickHouse,它适用于 OLAP 也就是数据分析的场景,当数据写入后,通过不同维度不断挖掘、分析,发现其中的商业价值。ClickHouse 适用于读远大于写的情况。
此外,相比ES,ClickHouse 占用的硬盘空间更小,也有利于降低运维成本。
下面是我在尝试接入 ClickHouse 时的一些实践,以及关于 ClickHouse数组类型转换问题的解决方案。
关于 ClickHouse 更详细的知识参考:https://zhuanlan.zhihu.com/p/98135840
示例代码已经上传到了 Git,目前更新第 28 节:https://github.com/laolunsi/spring-boot-examples/
Mybatis + ClickHouse
以前一直用 Mybatis 去操作 MySQL,其实 Mybatis 还可以操作 ClickHouse,这里用 Druid 进行连接管理。
maven 配置
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>ru.yandex.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.2.6</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
配置文件:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
# 注意这里是自定义的配置,通过 JdbcParamConfig 来加载配置到 Spring 中
# 然后由 DruidConfig 来配置数据源
click:
driverClassName: ru.yandex.clickhouse.ClickHouseDriver
url: jdbc:clickhouse://127.0.0.1:8123/test # ip:port/database
userName: default
password: default # 按照自己连接的 clickhouse 数据库来
initialSize: 10
maxActive: 100
minIdle: 10
maxWait: 6000
validationQuery: SELECT 1
加载配置项的类:
@Component
@ConfigurationProperties(prefix = "spring.datasource.click")
public class JdbcParamConfig {
private String userName;
private String password;
private String driverClassName ;
private String url ;
private Integer initialSize ;
private Integer maxActive ;
private Integer minIdle ;
private Integer maxWait ;
private String validationQuery;
// ignore getters and setters
}
配置 Druid:
@Configuration
@MapperScan(basePackages = {
"com.aegis.analysis.clickhousestorage.dao"
})
public class DruidConfig {
@Resource
private JdbcParamConfig jdbcParamConfig ;
@Bean(name = "clickDataSource")
public DataSource dataSource() throws ClassNotFoundException {
Class classes = Class.forName("com.alibaba.druid.pool.DruidDataSource");
DruidDataSource dataSource = (DruidDataSource) DataSourceBuilder
.create()
.driverClassName(jdbcParamConfig.getDriverClassName())
.type(classes)
.url(jdbcParamConfig.getUrl())
.username(jdbcParamConfig.getUserName())
.password(jdbcParamConfig.getPassword())
.build();
dataSource.setMaxWait(jdbcParamConfig.getMaxWait());
dataSource.setValidationQuery(jdbcParamConfig.getValidationQuery());
return dataSource;
}
@Bean
public SqlSessionFactory clickHouseSqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource());
// 实体 model的 路径 比如 com.order.model
factory.setTypeAliasesPackage("com.example.clickhousedemo.model");
//添加XML目录
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factory.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
//开启驼峰命名转换
factory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return factory.getObject();
}
}
定义一个 UserInfo 类,建表语句如下:
CREATE TABLE test.user (
`id` Int16,
`name` String,
`score` Float32,
`score2` Float64,
`state` Int8,
`createTime` DateTime,
`ranks` Array(UInt8)
) ENGINE = MergeTree() ORDER BY id;
实体类:
public class UserInfo {
private Integer id; // int16
private String name; // String
private Float score; // float16
private Double score2; // float32
private Boolean state; // int8
private Date createTime; // datetime
private Integer[] ranks; // Array - Array 类型需要进行类型转换
// 具体转换方法与配置参考 ClickArrayToIntHandler 类与 UserMapper.xml 中关于查询和插入时 ranks 字段的配置
// ignore getters and setters
}
DAO 和 Mapper 文件就按照连接 MYSQL 时的写法一样。
这里有个需要注意的点,ClickHouse 有个 Array 类型,可以用来存数组,就像 ES 一样。问题是类型转换需要自己定义。网上一些资料仅列出了基本类型的场景,我自己实现了一个转换器,可以参考一下:
/**
* Java Int 数组与 ClockHouse Array Int 转换器
* @version 1.0
* @since 2019/11/14 9:59
*/
public class ClickArrayToIntHandler extends BaseTypeHandler<Integer[]> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Integer[] integers, JdbcType jdbcType) throws SQLException {
preparedStatement.setObject(i, integers);
}
@Override
public Integer[] getNullableResult(ResultSet resultSet, String s) throws SQLException {
Object obj = resultSet.getObject(s);
return parseClickHouseArrayToInt(obj);
}
@Override
public Integer[] getNullableResult(ResultSet resultSet, int i) throws SQLException {
Object obj = resultSet.getObject(i);
return parseClickHouseArrayToInt(obj);
}
@Override
public Integer[] getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
Object obj = callableStatement.getObject(i);
return parseClickHouseArrayToInt(obj);
}
private Integer[] parseClickHouseArrayToInt(Object obj) {
if (obj instanceof ClickHouseArray) {
int[] res = new int[0];
try {
res = (int[]) ((ClickHouseArray) obj).getArray();
} catch (SQLException ex) {
ex.printStackTrace();
}
if (res != null && res.length > 0) {
Integer[] resI = new Integer[res.length];
for (int i = 0; i < res.length; i++) {
resI[i] = res[i];
}
return resI;
}
}
return new Integer[0];
}
}
DAO.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.aegis.analysis.clickhousestorage.dao.UserInfoMapper">
<resultMap id="BaseResultMap" type="com.example.clickhousedemo.model.UserInfo">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="name" property="name" />
<result column="score" property="score" />
<result column="score2" property="score2" />
<result column="state" property="state" />
<result column="createTime" property="createTime" />
<!-- <result column="ranks" property="ranks" jdbcType="JAVA_OBJECT" javaType="java.lang.Object" />-->
<result column="ranks" property="ranks" typeHandler="com.example.clickhousedemo.dao.ClickArrayToIntHandler" />
</resultMap>
<sql id="Base_Column_List">
*
</sql>
<insert id="saveData" parameterType="com.aegis.analysis.clickhousestorage.model.UserInfo" >
INSERT INTO user
(id,name, score, score2, state, createTime, ranks)
VALUES
(#{id},#{name}, #{score}, #{score2}, #{state}, #{createTime}, #{ranks, jdbcType=ARRAY,
typeHandler=com.example.clickhousedemo.dao.ClickArrayToIntHandler})
</insert>
<select id="selectById" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user
where id = #{id}
limit 1
</select>
<select id="selectList" resultMap="BaseResultMap" >
select
<include refid="Base_Column_List" />
from user
</select>
</mapper>
具体代码可以去我的 Git 仓库里查看,还有 SpringBoot 整合其他中间件技术的示例,欢迎 Star!
https://github.com/laolunsi/spring-boot-examples
来源:104.116.116.112.58.47.47.119.119.119.46.101.107.110.111.119.110.46.99.110.47.105.110.100.101.120.46.112.104.112.47.115.112.114.105.110.103.45.98.111.111.116.47.99.108.105.99.107.104.111.117.115.101.46.104.116.109.108.


猜你喜欢
- 架构:MVC架构基于JWT的身份认证Spring Data (JPA)应用用户密码加密数据库密码加密SQL ServerSlf4j基于Swa
- 在Monkeyrunner做自动化测试时,可以使用模拟器,当然也可以选择用真机。不过,要想通过电脑来安装软件,操作手机,则必须先安装手机驱动
- 1.IO流介绍IO流可以用到的地方很多,就比如设计模式、下载、传输等等。学好IO流,为之后的进一步学习打下基础,那么,先来说说什么是流?流是
- Java Collection API提供了一些列的类和接口来帮助我们存储和管理对象集合。其实Java中的集合工作起来像是一个数组,不过集合
- 最近公司项目需要在WebView上调用手机系统相册来上传图片,开发过程中发现在很多机器上无法正常唤起系统相册来选择图片。解决问题之前我们先来
- 只使用try和finally不使用catch的原因和场景JDK并发工具包中,很多异常处理都使用了如下的结构,如AbstractExecuto
- 1、创建实体属性标记public class CellAttribute : Attribute {&n
- 先看代码://设置可以同时处于活动状态的线程池的请求数目。 bool pool = ThreadPool.SetMaxThreads(8,
- MongoDBMongoDB作为一种NoSQL数据库产品,其实已经非常著名了。去年,由于MongoDB安全认证的薄弱,上万家公司中招。虽然是
- 问题:对于多线程编程,很多时候往往需要向线程中传递多个参数,而C#中的线程只接收1个object类型的参数(如下):Thread t = n
- 1.拉取centos镜像docker pull centos:72.基于拉取到的镜像运行一个容器docker run -it --name
- FileOutPutStream:子类,写出数据的通道步骤:1.获取目标文件2.创建通道(如果原来没有目标文件,则会自动创建一个)3.写入数
- 前言作为一个服务端开发感觉一直挺排斥框架这种东西的,总觉得什么实现逻辑都帮你封装在里面了,你只需要配置这配置那个,出了问题也不知道怎么排查,
- 本文实例讲述了android编程之xml文件读取和写入方法。分享给大家供大家参考。具体分析如下:一、环境:主机:WIN8开发环境:Eclip
- 基础概念百度百科是这么描述归并排序的: 归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。设有数列{6,
- Hystrix 是一个帮助解决分布式系统交互时超时处理和容错的类库, 它同样拥有保护系统的能力。Netflix的众多开源项目之一。设计流程:
- Maven搭建springboot项目本文是基于Windows 10系统环境,使用Maven搭建springboot项目Windows 10
- Android自定义View仿探探卡片滑动这种效果网上有很多人已经讲解了实现思路,大多都用的是RecyclerView来实现的,但是我们今天
- 一、RequestMapping注解RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系RequestMappin
- 前言我们在日常的开发中有时候会遇到需要用到相机的需求,而相机也是很常用的东西,例如扫二维码啊拍照上传啊等等。这里我不讲像qq那样自定义很强的