好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

如何将Mybatis连接到ClickHouse

场景

最近在做数据分析项目,里面有这样一个业务:把匹配的数据打上标签,放到新的索引中。

数据量:累计亿级的数据

使用场景:可能会单次查询大量的数据,但不会设置复杂的条件,且这些数据不会被再次修改

原来使用的数据库:ElasticSearch

问题:上面也说了我这里打上标记后,这些数据几乎不会再修改了。ES 是一个全文检索引擎,更适用于进行大量文本检索的情况。这里与我上面的使用场景就不太匹配了。

技术选型的考虑:改用战斗民族开发的 ClickHouse ,它适用于 OLAP 也就是数据分析的场景,当数据写入后,通过不同维度不断挖掘、分析,发现其中的商业价值。ClickHouse 适用于读远大于写的情况。

此外,相比ES,ClickHouse 占用的硬盘空间更小,也有利于降低运维成本。

下面是我在尝试接入 ClickHouse 时的一些实践,以及关于 ClickHouse数组类型转换问题的解决方案。

关于 ClickHouse 更详细的知识参考: https://zhuanlan.zhihu测试数据/p/98135840

示例代码已经上传到了 Git,目前更新第 28 节: https://github测试数据/laolunsi/spring-boot-examples/

Mybatis + ClickHouse

以前一直用 Mybatis 去操作 MySQL,其实 Mybatis 还可以操作 ClickHouse,这里用 Druid 进行连接管理。

maven 配置

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

< 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 >

配置文件:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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

加载配置项的类:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

@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 类,建表语句如下:

?

1

2

3

4

5

6

7

8

9

CREATE TABLE test. user (

    `id` Int16,

    ` name ` String,

    `score` Float32,

    `score2` Float64,

    `state` Int8,

    `createTime` DateTime,

    `ranks` Array(UInt8)

    ) ENGINE = MergeTree() ORDER BY id;

实体类:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

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 一样。问题是类型转换需要自己定义。网上一些资料仅列出了基本类型的场景,我自己实现了一个转换器,可以参考一下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

/**

  * 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 也给一个示例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

<? 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测试数据/laolunsi/spring-boot-examples

以上就是如何将Mybatis连接到ClickHouse的详细内容,更多关于Mybatis连接到ClickHouse的资料请关注其它相关文章!

原文链接:http://HdhCmsTesteknown.cn/index.php/spring-boot/clickhouse.html

查看更多关于如何将Mybatis连接到ClickHouse的详细内容...

  阅读:18次