数据库分片使用ShardingSphere-Proxy作为数据库代理端,java应用连接有时候表现并不是跟直连后端数据库一样,切换到连接数据库代理就会遇到各种各样的问题,比如最近就遇到在做包含时间分片的数据查询时抛出错误Sharding value must implements Comparable. 的问题。网上很多遇到该问题都是在数据插入时遇到,而我们这次不同,是在做数据查询时遇到该问题。以下将描述问题情况及解决办法。

1. 问题描述

1.1 当前使用环境

ShardingSphere-Proxy版本:shardingsphere-proxy-5.3.1

后端数据库:PostgreSQL-12.1

java端:Mybatis-Plus

1.2 ShardingSphere-Proxy的分片算法配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    datetime_year_month_interval_algorithm:
      type: INTERVAL
      props:
        datetime-pattern: 'yyyy-MM-dd HH:mm:ss'
        datetime-lower: '2023-01-01 00:00:00'
        #默认当前时间
        datetime-upper: '2023-12-31 23:59:59'
        sharding-suffix-pattern: 'yyyyMM'
        datetime-interval-amount: 1
        datetime-interval-unit: 'MONTHS'

1.3 java调用

1
2
Date date1 = new Date();
orderMapper.getData(xxx,date1);

1.4 Mapper配置

1
2
3
4
5
6
7
8
    <select id="getData" resultType="integer">
        select
                count(*)
        from
                ttt
        where
                and create_time  <![CDATA[ <= ]]> #{qTime}
    </select>

1.5 出现报错

1
2
3
4
5
6
7
8
9
### Cause: org.postgresql.util.PSQLException: ERROR: Sharding value must implements Comparable.
; ERROR: Sharding value must implements Comparable.; nested exception is org.postgresql.util.PSQLException: ERROR: Sharding value must implements Comparable.
	at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:107)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:91)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441)
	at com.sun.proxy.$Proxy134.selectOne(Unknown Source)

从上述重点可看出报错信息是:Sharding value must implements Comparable

说明与类型有关。

2. 调试分析

先把调用的类型修改为LocalDateTime

1
2
LocalDateTime date1 = LocalDateTime.now();
orderMapper.getData(xxx,date1);

结果发现可以调用。

3. 解决办法

从上述调试分析,可以看出,我们可以通过转变Date类型为LocalDateTime来解决这个问题,但考虑尽量少改动原来的代码,我们写一个TypeHandler来全局转变Date类型。

 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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.springframework.stereotype.Component;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

/**
 * 功能描述:
 *
 * @author https://tech.hiofd.com
 * @date 2023-03-13 17:10
 */
@Component
@MappedTypes(Date.class)
@MappedJdbcTypes(value = JdbcType.DATE, includeNullJdbcType = true)
public class MyDateTypeHandler extends BaseTypeHandler<Date> {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date parameter, JdbcType jdbcType) throws SQLException {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(parameter.toInstant(), ZoneId.systemDefault());
        System.out.println(localDateTime);
        preparedStatement.setObject(i, localDateTime);
    }


    @Override
    public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Object v = rs.getObject(columnName);
        if (v == null) {
            return null;
        }

        Date date1 = null;
        try {
            date1 = formatter.parse(v.toString());
            System.out.println(date1);
            return date1;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Object v = rs.getObject(columnIndex);
        if (v == null) {
            return null;
        }
        Date date1 = null;
        try {
            date1 = formatter.parse(v.toString());
            System.out.println(date1);
            return date1;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Object v = cs.getObject(columnIndex);
        if (v == null) {
            return null;
        }
        Date date1 = null;
        try {
            date1 = formatter.parse(v.toString());
            System.out.println(date1);
            return date1;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}

至此,问题完美解决。