一、介绍
二、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 << ${index}) <!-- Setting the 20th bit to 1 -->
ELSE flag & ~(1 << ${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查看: