如何将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.

标签:Mybatis,ClickHouse
0
投稿

猜你喜欢

  • Java模拟qq软件的详细过程

    2022-01-27 15:06:19
  • Java无限级树(递归)超实用案例

    2023-08-01 13:31:57
  • Java String类的理解及字符串常量池介绍

    2022-11-14 15:42:22
  • java实现给出分数数组得到对应名次数组的方法

    2021-06-01 13:58:05
  • netty pipeline中的inbound和outbound事件传播分析

    2023-08-27 06:57:00
  • c# SendMail发送邮件实例代码

    2023-03-13 11:17:21
  • 详解android 中animation-list 动画的应用

    2022-09-13 18:28:31
  • C#设置输入法实例分析

    2022-07-07 14:30:05
  • Android App仿QQ制作Material Design风格沉浸式状态栏

    2021-06-07 01:10:48
  • Java各种排序算法汇总(冒泡,选择,归并,希尔及堆排序等)

    2021-10-05 14:46:23
  • java 抛出异常处理的方法

    2023-05-12 21:55:54
  • Android用 Mob 实现发送短信验证码实例

    2021-10-17 03:53:16
  • android底部弹出iOS7风格对话选项框(QQ对话框)--第三方开源之IOS_Dialog_Library

    2023-02-18 17:42:44
  • 在eclipse中中文汉字乱码的解决方案

    2023-11-25 10:13:27
  • Android开发中通过手机号+短信验证码登录的实例代码

    2023-03-10 10:57:13
  • MyBatis特殊字符转义拦截器问题针对(_、\\、%)

    2023-09-12 00:46:39
  • 将15位身份证补全为18位身份证的算法示例详解

    2022-07-15 19:33:43
  • java实现砸金蛋抽奖功能

    2022-02-22 01:26:50
  • 如何解决通过spring-boot-maven-plugin package失败问题

    2021-10-16 15:43:45
  • Android开发之RecyclerView控件

    2023-08-14 08:57:37
  • asp之家 软件编程 m.aspxhome.com