如何將Mybatis連接到ClickHouse
場景
最近在做數(shù)據(jù)分析項目,里面有這樣一個業(yè)務(wù):把匹配的數(shù)據(jù)打上標(biāo)簽,放到新的索引中。
數(shù)據(jù)量:累計億級的數(shù)據(jù)
使用場景:可能會單次查詢大量的數(shù)據(jù),但不會設(shè)置復(fù)雜的條件,且這些數(shù)據(jù)不會被再次修改
原來使用的數(shù)據(jù)庫:ElasticSearch
問題:上面也說了我這里打上標(biāo)記后,這些數(shù)據(jù)幾乎不會再修改了。ES 是一個全文檢索引擎,更適用于進(jìn)行大量文本檢索的情況。這里與我上面的使用場景就不太匹配了。
技術(shù)選型的考慮:改用戰(zhàn)斗民族開發(fā)的 ClickHouse,它適用于 OLAP 也就是數(shù)據(jù)分析的場景,當(dāng)數(shù)據(jù)寫入后,通過不同維度不斷挖掘、分析,發(fā)現(xiàn)其中的商業(yè)價值。ClickHouse 適用于讀遠(yuǎn)大于寫的情況。
此外,相比ES,ClickHouse 占用的硬盤空間更小,也有利于降低運維成本。
下面是我在嘗試接入 ClickHouse 時的一些實踐,以及關(guān)于 ClickHouse數(shù)組類型轉(zhuǎn)換問題的解決方案。
關(guān)于 ClickHouse 更詳細(xì)的知識參考:https://zhuanlan.zhihu.com/p/98135840
示例代碼已經(jīng)上傳到了 Git,目前更新第 28 節(jié):https://github.com/laolunsi/spring-boot-examples/
Mybatis + ClickHouse
以前一直用 Mybatis 去操作 MySQL,其實 Mybatis 還可以操作 ClickHouse,這里用 Druid 進(jìn)行連接管理。
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 來配置數(shù)據(jù)源 click: driverClassName: ru.yandex.clickhouse.ClickHouseDriver url: jdbc:clickhouse://127.0.0.1:8123/test # ip:port/database userName: default password: default # 按照自己連接的 clickhouse 數(shù)據(jù)庫來 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"));
//開啟駝峰命名轉(zhuǎn)換
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 類型需要進(jìn)行類型轉(zhuǎn)換
// 具體轉(zhuǎn)換方法與配置參考 ClickArrayToIntHandler 類與 UserMapper.xml 中關(guān)于查詢和插入時 ranks 字段的配置
// ignore getters and setters
}
DAO 和 Mapper 文件就按照連接 MYSQL 時的寫法一樣。
這里有個需要注意的點,ClickHouse 有個 Array 類型,可以用來存數(shù)組,就像 ES 一樣。問題是類型轉(zhuǎn)換需要自己定義。網(wǎng)上一些資料僅列出了基本類型的場景,我自己實現(xiàn)了一個轉(zhuǎn)換器,可以參考一下:
/**
* Java Int 數(shù)組與 ClockHouse Array Int 轉(zhuǎn)換器
* @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 整合其他中間件技術(shù)的示例,歡迎 Star!
https://github.com/laolunsi/spring-boot-examples
以上就是如何將Mybatis連接到ClickHouse的詳細(xì)內(nèi)容,更多關(guān)于Mybatis連接到ClickHouse的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
jackson json序列化實現(xiàn)首字母大寫,第二個字母需小寫
這篇文章主要介紹了jackson json序列化實現(xiàn)首字母大寫,第二個字母需小寫方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
Spring?Security權(quán)限管理實現(xiàn)接口動態(tài)權(quán)限控制
這篇文章主要為大家介紹了Spring?Security權(quán)限管理實現(xiàn)接口動態(tài)權(quán)限控制,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

