mysql存储比特位

一、介绍

二、SQL

复制代码
CREATE TABLE bits_table (
    id INT PRIMARY KEY AUTO_INCREMENT,
    bit_value BIGINT UNSIGNED
);
 
-- 插入一个 8 位的 BIT 值
INSERT INTO bits_table (bit_value) VALUES (B'10101010');
 
-- 查询并格式化输出
SELECT 
    id,
    bit_value,
    CONCAT('b', LPAD(BIN(bit_value), 64, '0')) AS formatted_bit_value -- 将 BIGINT 转换为 64 位的二进制字符串
FROM 
    bits_table;

在这个例子中,CONCAT('b', LPAD(BIN(bit_value), 64, '0')) 用于将 bit_value 转换为一个以 'b' 开头的 64 位二进制字符串,LPAD 用于在左边填充 '0' 以达到 64 位的长度。

请注意,如果你需要存储非整数数量的位或者位数不固定,你可能需要以文本形式存储或者使用其他数据库特性来实现。

复制代码
-- 假设我们有一个表 `bits_table`,其中有一个 `BIGINT` 类型的列 `bigint_col`
-- 我们要修改 `bigint_col` 列的第二位
 
-- 将 `bigint` 转换为 `bit` 字符串,并取得第二位的值
SELECT 
    bigint_col,
    -- 将 `bigint` 转换为 `bit` 字符串,并取得第二位的值
    SUBSTRING(BIN(bigint_col), 2, 1) AS second_bit
FROM
    bits_table;
 
-- 更新第二位为1
UPDATE bits_table
SET 
    bigint_col = 
    -- 将 `bigint` 转换为 `bit` 字符串,将第二位设置为1,然后转换回 `bigint`
    (CONV(CONCAT(SUBSTRING(BIN(bigint_col), 1, 1), '1', SUBSTRING(BIN(bigint_col), 3)), 2, 10)
WHERE
    -- 你的条件语句,比如 id = 1
    id = 1;

<!-- MyBatis的mapper文件 -->
<update id="updateBit">
  UPDATE your_table_name
  SET your_bigint_column = bitor(
    bitand(your_bigint_column, bnot(1 << 20)), 
    (#{value} << 20)
  )
  WHERE your_condition
</update>



这里使用了两个位运算符:

bitand(a, b): 对两个bigint数进行按位与操作。

bitor(a, b): 对两个bigint数进行按位或操作。

bnot(x): 对bigint数进行按位取反操作,结果是把x的第y位取反。

<<: 左移运算符,用于将一个整数左移指定的位数。

确保你的mapper接口中有相应的方法:

UPDATE your_table_name
SET your_bigint_column = BIN(CONV(CONV(your_bigint_column, 2, 10) + POW(2, 19 - 1), 2, 10))
WHERE your_condition;


your_table_name是你的表名,your_bigint_column是你想要更新的列名,your_condition是你的更新条件。

请注意,这个例子中假设了以下几点:

你想要将第20位设置为1。

你的列是无符号的,因此最高位是第20位。如果是有符号的,请适当调整位数。

如果你想将第20位设置为0或某个特定值,只需要将POW(2, 19 - 1)中的1改为你想要设置的值(以二进制表示)。如果是将第20位设置为0,就是POW(2, 19 - 0)

三、demo

1、使用bigint类型存储bit

总览

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>bit-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/>
    </parent>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--尽量不要同时导入mybatis 和 mybatis_plus,避免版本差异-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.5</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

server.port=6666
server.servlet.context-path=/bitDemo
#mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3308/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=wtyy
#mybatis
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
#
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
1.1、表
复制代码
create table demo.user_sys_flag
(
    id      int auto_increment
        primary key,
    flag    bigint      null,
    user_id varchar(50) null
);
1.2、dto与枚举
复制代码
package com.bit.demo.dto;

import com.baomidou.mybatisplus.annotation.TableName;
import com.bit.demo.enums.UserSysFlagEnums;
import com.bit.demo.util.BitUtil;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
@TableName("user_sys_flag")
public class UserSysFlagDTO {

    private Integer id;

    private Long flag;

    private String userId;

    public boolean isEnableFlag1(){
        return BitUtil.isSet(
                flag,
                UserSysFlagEnums.FLAG_1.bitPosition);
    }

    public boolean isEnableFlag2(){
        return BitUtil.isSet(
                flag,
                UserSysFlagEnums.FLAG_2.bitPosition);
    }

    public boolean isEnableFlag60() {
        return BitUtil.isSet(
                flag,
                UserSysFlagEnums.FLAG_60.bitPosition);
    }
}

package com.bit.demo.enums;

public enum UserSysFlagEnums {
    FLAG_1("flag_1",1L),
    FLAG_2("flag_2",1L<<1),
    FLAG_3("flag_3",1L<<2),
    FLAG_60("flag_60",1L<<59);

    public final String key;
    public final Long bitPosition;

    UserSysFlagEnums(String key, Long bitPosition){
        this.key = key;
        this.bitPosition = bitPosition;
    }
}
1.3、dao
复制代码
package com.bit.demo.repository;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bit.demo.dto.UserSysFlagDTO;
import com.bit.demo.mapper.UserSysFlagMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class UserSysFlagRepository {

    @Autowired
    public UserSysFlagMapper userSysFlagMapper;

    public void insert(UserSysFlagDTO userSysFlagDTO) {
        userSysFlagMapper.insert(userSysFlagDTO);
    }

    public UserSysFlagDTO getByUserId(String userId) {
        LambdaQueryWrapper<UserSysFlagDTO> userQuery = new LambdaQueryWrapper<>();
        userQuery.eq(UserSysFlagDTO::getUserId,userId);
        return userSysFlagMapper.selectOne(userQuery);
    }

    public void updateFlagByUserIdAndIndex(String userId, int index, int indexValue) {
        userSysFlagMapper.updateFlagByUserIdAndIndex(userId,index,indexValue);
    }

    public String queryBitByUserId(Integer bitLength,String userId) {
        return userSysFlagMapper.queryBitByUserId(bitLength,userId);
    }
}

package com.bit.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bit.demo.dto.UserSysFlagDTO;
import org.apache.ibatis.annotations.Param;


public interface UserSysFlagMapper extends BaseMapper<UserSysFlagDTO> {

    void updateFlagByUserIdAndIndex(@Param("userId") String userId,
                                    @Param("index") int index,
                                    @Param("bitValue") int bitValue);

    String queryBitByUserId(@Param("bitLength")Integer bitLength,
                            @Param("userId") String userId);
}

<?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.bit.demo.mapper.UserSysFlagMapper">

    <update id="updateFlagByUserIdAndIndex">
        UPDATE user_sys_flag
        SET flag =
          CASE
            WHEN #{bitValue} = 1 THEN flag | (1 &lt;&lt; ${index})   <!-- Setting the 20th bit to 1 -->
            ELSE flag &amp; ~(1 &lt;&lt; ${index})                     <!-- Setting the 20th bit to 0 -->
          END
        where user_id=#{userId}
    </update>

    <select id="queryBitByUserId" resultType="string">
        SELECT
            CONCAT('b', LPAD(BIN(flag), ${bitLength}, '0')) AS formatted_bit_value
        FROM
            user_sys_flag WHERE
            user_id = #{userId}
    </select>

</mapper>
1.4、service
复制代码
package com.bit.demo.service.impl;

import com.bit.demo.dto.UserSysFlagDTO;
import com.bit.demo.mapper.UserSysFlagMapper;
import com.bit.demo.repository.UserSysFlagRepository;
import com.bit.demo.service.UserSysFlagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("userSysFlagService")
public class UserSysFlagServiceImpl implements UserSysFlagService {

    @Autowired
    private UserSysFlagRepository userSysFlagRepository;
    @Autowired
    private UserSysFlagMapper userSysFlagMapper;

    @Override
    public void insert(UserSysFlagDTO userSysFlagDTO) {
        userSysFlagRepository.insert(userSysFlagDTO);
    }

    @Override
    public UserSysFlagDTO getByUserId(String userId) {
        return userSysFlagRepository.getByUserId(userId);
    }

    @Override
    public void updateFlagByUserIdAndIndex(String userId, int index, int indexValue) {
        userSysFlagRepository.updateFlagByUserIdAndIndex(userId,index,indexValue);
    }

    @Override
    public String queryBitByUserId(Integer bitLength,String userId) {
        return userSysFlagRepository.queryBitByUserId(bitLength,userId);
    }
}
1.5、util
复制代码
package com.bit.demo.util;

import com.bit.demo.enums.UserSysFlagEnums;

public class BitUtil {
    public static boolean isSet(long options, long bit) {
        return (options & bit) == bit;
    }
}
1.6、启动类
复制代码
package com.bit.demo;



import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.bit.demo.mapper")
@SpringBootApplication
public class BitApplication {

    public static void main(String[] args) {
        SpringApplication.run(BitApplication.class, args);
    }
}
1.7、test
复制代码
package com.bit.demo;

import com.bit.demo.dto.UserSysFlagDTO;
import com.bit.demo.service.UserSysFlagService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest(classes = {BitApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
@Slf4j
public class UserSysFlagTest {

    @Autowired
    private UserSysFlagService userSysFlagService;

    //1、初始化。最大64位,假如需要60个开关,0代表关,1代表开,默认为关。
    @Test
    public void init() {
        UserSysFlagDTO userSysFlagDTO = UserSysFlagDTO.builder()
                .userId("zs")
                .flag(0L)
                .build();
        userSysFlagService.insert(userSysFlagDTO);
    }

    //2、更新,更新第n位的flag
    @Test
    public void update() {
        //将 `bigint` 转换为 `bit` 字符串,将第index位设置为indexValue,然后转换回 `bigint`
        //index从0开始
        String userId = "zs";
        int index = 0;
        int indexValue = 0;
        userSysFlagService.updateFlagByUserIdAndIndex(userId,index,indexValue);
    }

    //3、查询
    @Test
    public void query() {
        UserSysFlagDTO userSysFlagDTO = userSysFlagService.getByUserId("zs");
        log.info(userSysFlagDTO.toString());
        log.info("flag1值为:{}", userSysFlagDTO.isEnableFlag1());
        log.info("flag2值为:{}", userSysFlagDTO.isEnableFlag2());
        log.info("flag60值为:{}", userSysFlagDTO.isEnableFlag60());
    }

    //查询二进制
    @Test
    public void queryBit(){
        Integer bitLength = 64;
        String userId = "zs";
        String bitStr = userSysFlagService.queryBitByUserId(bitLength,userId);
        System.out.println(bitStr);
    }
}
1.8、测试:
(1)初始化

如我初始化了ls,默认是0

(2)更新第1位

更新ls第1位为1

复制代码
String userId = "ls";
        int index = 0;
        int indexValue = 1;

queryBit:可以看到第一位是1了

复制代码
query:可以看到isEnableFlag1是true了
(3)更新其他位

如更新第60位为1:

复制代码
  String userId = "ls";
        int index = 59;
        int indexValue = 1;

queryBit:可以看到第60位是1了

query:可以看到isEnableFlag1、isEnableFlag60都是true了

(4)再次更新第1位

更新为0,也即关闭功能

复制代码
String userId = "ls";
        int index = 0;
        int indexValue = 0;
复制代码
queryBit查看:
复制代码
query查看:
相关推荐
带刺的坐椅6 分钟前
Snack4 Json 流式解析与自动结构修复深度指南
java·llm·json·jsonpath
zb2006412010 分钟前
Spring Boot 实战篇(四):实现用户登录与注册功能
java·spring boot·后端
我命由我1234514 分钟前
Android 多进程开发 - FileDescriptor、Uri、AIDL 接口定义不能抛出异常
android·java·java-ee·kotlin·android studio·android-studio·android runtime
xyhuix21 分钟前
Spring+Quartz实现定时任务的配置方法
java
分享牛24 分钟前
Operaton入门到精通22-Operaton 2.0 升级指南:Spring Boot 4 核心变更详解
java·spring boot·后端
jinanmichael24 分钟前
SpringBoot 如何调用 WebService 接口
java·spring boot·后端
深蓝轨迹25 分钟前
吃透 Spring Boot dataSource与Starter
java·spring boot·笔记·后端
spring29979227 分钟前
springboot和springframework版本依赖关系
java·spring boot·后端
文公子WGZ37 分钟前
将java 21切换成java 25
java·开发语言
一直都在57238 分钟前
Java序列化和反序列化
java·开发语言