• java

  • 首页

  • 文章归档

  • 默认分类

  • 关于页面
W E L C O M E
W E L C O M E

A股让人很难再爱

音乐是各个时间段各种心情下的随机收藏,所以风格各异

01月
10
学习
PG

mybatis读取roaringbitmap遇到的坑

发表于 2024-01-10 • 字数统计 8278 • 被 451 人看爆

问题

我们使用roaringbitmap都知道,这个是一个扩展安装到PG里面的,你安装后新建字段的时候可以选择一个叫***roaringbitmap***的数据类型,但是这个类型java里面是没有的,也就是说你查出来mybatis不能直接映射,数据库里面的值长这样:\x3a3000000100000000000200100000008a278b278f27。那么你如果想直接读,可以自己通过位图函数rb_to_array()查出来的时候转成数组然后返回给java。

select rb_to_array(bitmap) from table where id=xxxx

这样可以,但是咱都用mybatisplus了,本来就是图省事,可以少写sql,内置的getById方法或者save方法这些都用不了就很难受。

解决问题

为了能继续偷懒,那肯定是要自己去实现roaringbitmap的RoaringBitmapTypeHandler。

相关依赖

  	<dependency>
            <groupId>org.roaringbitmap</groupId>
            <artifactId>RoaringBitmap</artifactId>
            <version>1.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.1</version>
        </dependency>

注意如果你maven里面用了dependencyManagement,spring-boot-dependencies里面自己是带了postgresql的,自己检查看一下版本对不对,不对的手动排除或者升级你的spring-boot-dependencies

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.postgresql</groupId>
                        <artifactId>postgresql</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
</dependencyManagement>

实体类样例

import org.roaringbitmap.RoaringBitmap;

@TableName("public.entity")
public class Entity implements Serializable {

    private Long id;

    @TableField(typeHandler = RoaringBitmapTypeHandler.class)
    private RoaringBitmap userbits;
}

这里的RoaringBitmap就是刚才引入的依赖里面的。

自定义类型处理器

mybatis的自定义类型处理器需要继承BaseTypeHandler这个抽象类,实现相应方法。

public class RoaringBitmapTypeHandler extends BaseTypeHandler<RoaringBitmap> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, RoaringBitmap roaringBitmap, JdbcType jdbcType) throws SQLException {
       
    }
    @Override
    public RoaringBitmap getNullableResult(ResultSet rs, String columnName) throws SQLException {
      
    }

    @Override
    public RoaringBitmap getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    
    }

    @Override
    public RoaringBitmap getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
      
    }
}

咱先把类建好再看里面的方法,主要就是set和get对应就是你插入更新和查询的时候遇到这个类型的处理逻辑。
我们开头说过咱数据库里的数据长这样“\x3a30000000000000”,除去\x其实是一个16进制数。这边提供一种实现思路:

    @Override
    public RoaringBitmap getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 从数据库中读取字符串
        String roaringBitmapString = rs.getString(columnName);
        roaringBitmapString = roaringBitmapString.replace("\\x", ""); // 去除 \x 前缀
        byte[] byteArray = DatatypeConverter.parseHexBinary(roaringBitmapString);
        RoaringBitmap roaringBitmap = new RoaringBitmap();
        try {
            roaringBitmap.deserialize(new DataInputStream(new ByteArrayInputStream(byteArray)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return roaringBitmap;
    }

 @Override
    public RoaringBitmap getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String roaringBitmapString = rs.getString(columnIndex);
        roaringBitmapString = roaringBitmapString.replace("\\x", ""); // 去除 \x 前缀
        return RoaringBitmap.bitmapOf(Integer.parseInt(roaringBitmapString, 16));
    }

这样我们读取roaringbitmap类型的处理就好了

划重点

刚才我们直接通过rs.getString获取到了值没毛病,按照刚才的思路,我set值的时候,也先给他转成16进制字符串再set应该也没毛病吧。

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, RoaringBitmap roaringBitmap, JdbcType jdbcType) throws SQLException {
	// convertRoaringToHex(roaringBitmap)等于是\x3a30000000000000这样的一个字符串
        ps.setString(i, convertRoaringToHex(roaringBitmap));
    }

如果你这么写了,恭喜你,你会看到这样的结果

Caused by: org.postgresql.util.PSQLException: ERROR: column "userbits" is of type roaringbitmap but expression is of type character varying

你是不可以直接把一个string塞到roaringbitmap类型里面的setNonNullParameter方法得通过setObject设置值,注意这里得PGobject老版本的依赖里面是没有的。

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, RoaringBitmap roaringBitmap, JdbcType jdbcType) throws SQLException {
        PGobject object = new PGobject();
        object.setType("roaringbitmap");
        object.setValue(convertRoaringToHex(roaringBitmap));
        ps.setObject(i, object);
    }

    private String convertRoaringToHex(RoaringBitmap rb) {
        try {
            // 将 RoaringBitmap 对象序列化为字节数组
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(bos);
            rb.serialize(dos);
            dos.close();
            byte[] byteArray = bos.toByteArray();

            // 将字节数组转换为16进制字符串
            StringBuilder hex = new StringBuilder(byteArray.length * 2);
            hex.append("\\x");
            for (byte b : byteArray) {
                hex.append(String.format("%02x", b & 0xFF));
            }
            return hex.toString();
        } catch (IOException e) {
            throw new RuntimeException("Failed to convert RoaringBitmap to hex string", e);
        }
    }

到这所有问题都解决了,注意你要使用RoaringBitmap的地方都加了这个typehandler,通过这个例子咱以后遇到其他PG特有的数据类型也可以同理写自定义处理器。

别忘了注册你的自定义处理器

mybatis-plus:
  type-handlers-package: com.merkle.tagging_framework.config.handle

最后附上完整的处理器代码:

public class RoaringBitmapTypeHandler extends BaseTypeHandler<RoaringBitmap> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, RoaringBitmap roaringBitmap, JdbcType jdbcType) throws SQLException {
        PGobject object = new PGobject();
        object.setType("roaringbitmap");
        object.setValue(convertRoaringToHex(roaringBitmap));
        ps.setObject(i, object);
    }

    private String convertRoaringToHex(RoaringBitmap rb) {
        try {
            // 将 RoaringBitmap 对象序列化为字节数组
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(bos);
            rb.serialize(dos);
            dos.close();
            byte[] byteArray = bos.toByteArray();

            // 将字节数组转换为16进制字符串
            StringBuilder hex = new StringBuilder(byteArray.length * 2);
            hex.append("\\x");
            for (byte b : byteArray) {
                hex.append(String.format("%02x", b & 0xFF));
            }
            return hex.toString();
        } catch (IOException e) {
            throw new RuntimeException("Failed to convert RoaringBitmap to hex string", e);
        }
    }

    @Override
    public RoaringBitmap getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String roaringBitmapString = rs.getString(columnName);
        roaringBitmapString = roaringBitmapString.replace("\\x", "");
        byte[] byteArray = DatatypeConverter.parseHexBinary(roaringBitmapString);
        RoaringBitmap roaringBitmap = new RoaringBitmap();
        try {
            roaringBitmap.deserialize(new DataInputStream(new ByteArrayInputStream(byteArray)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return roaringBitmap;
    }

    @Override
    public RoaringBitmap getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String roaringBitmapString = rs.getString(columnIndex);
        roaringBitmapString = roaringBitmapString.replace("\\x", ""); 
        byte[] byteArray = DatatypeConverter.parseHexBinary(roaringBitmapString);
        RoaringBitmap roaringBitmap = new RoaringBitmap();
        try {
            roaringBitmap.deserialize(new DataInputStream(new ByteArrayInputStream(byteArray)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return roaringBitmap;
    }

    @Override
    public RoaringBitmap getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String roaringBitmapString = cs.getString(columnIndex);
        roaringBitmapString = roaringBitmapString.replace("\\x", ""); // 去除 \x 前缀
        byte[] byteArray = DatatypeConverter.parseHexBinary(roaringBitmapString);
        RoaringBitmap roaringBitmap = new RoaringBitmap();
        try {
            roaringBitmap.deserialize(new DataInputStream(new ByteArrayInputStream(byteArray)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return roaringBitmap;
    }
}
分享到:
Camunda---------------1.选型和外部任务(External Task)介绍
ubuntu安装roaringbitmap插件
  • 文章目录
  • 站点概览
A股让人很难再爱

~ 沐白

别问,问就是点金

Github QQ Email RSS
最喜欢的游戏
最喜欢的音乐
最喜欢的图书
最喜欢的动漫
DOTA2 生存游戏....
周杰伦,林俊杰,薛之谦,and ....
无
海贼王,火影
看爆 Top5
  • springboot实现word转pdf(基于document4j) 3,579次看爆
  • 使用Jgit操作git 981次看爆
  • 关于guava的限流器RateLimiter 818次看爆
  • ubuntu安装roaringbitmap插件 723次看爆
  • Mysql索引学习 722次看爆

站点已萌萌哒运行 00 天 00 小时 00 分 00 秒(●'◡'●)ノ♥

Copyright © 2025 A股让人很难再爱 苏ICP备2021008439号-1

由 Halo 强力驱动 · Theme by Sagiri · 站点地图