1. 项目中使用一个 32 位的int 类型值error表示各种错误,每一个二进制位的0表示出现错误,1表示未出现错误。定义下面的错误协议:
0000 0000 0000 0000 0000 0000 0000 0000 正常
0000 0000 0000 0000 0000 0000 0000 0001 蓝牙错误
0000 0000 0000 0000 0000 0000 0000 0010 电量低
0000 0000 0000 0000 0000 0000 0000 0100 gps信号弱
0000 0000 0000 0000 0000 0000 0000 1000 启动备用电池
0000 0000 0000 0000 0000 0000 0001 0000 倒地
...
如果出现多个错误就将各个错误 |起来,例如 蓝牙错误 + 电量低 + gps信号弱,那么我们数据库存储的值就是:
0000 0000 0000 0000 0000 0000 0000 0001
|
0000 0000 0000 0000 0000 0000 0000 0010
|0000 0000 0000 0000 0000 0000 0000 0100
=
0000 0000 0000 0000 0000 0000 0000 1110
转换为十进制存储到error数据就是 2 + 4 + 8 = 14;
2. 在条件搜索蓝牙错误 + 电量低的时候,将各个错误|起来得到:
0000 0000 0000 0000 0000 0000 0000 0001
|
0000 0000 0000 0000 0000 0000 0000 0010
=
0000 0000 0000 0000 0000 0000 0000 0110
,然后0000 0000 0000 0000 0000 0000 0000 0110&14 > 0 那么就是满足搜索条件的。
项目中使用的JPA的Specification来构建查询条件,但是JPA内置的查询函数是不支持位运算的。
- Specification<Log> specification = LogSpecification.newInstance(criteria);
-
- public static Specification<Log> newInstance(LogCriteria criteria) {
-
- return Specification.where(LogSpecification.id(criteria.getId()))
- .and(LogSpecification.error(criteria.getErrors()));
-
- }
-
- Page<Log> logs = loqRepository.findAll(specification, pagination);
查询相关资料我们可以扩展JPA的MySQL5Dialect类,自己定义位运算的函数如下:
- public class ExtendedMySQL5Dialect extends MySQL5Dialect {
- public ExtendedMySQL5Dialect() {
- super();
- // added bitwise operators
- registerFunction("bit_and", new SQLFunctionTemplate(IntegerType.INSTANCE, "(?1 & ?2)"));
- registerFunction("bit_or", new SQLFunctionTemplate(IntegerType.INSTANCE, "(?1 | ?2)"));
- registerFunction("bit_xor", new SQLFunctionTemplate(IntegerType.INSTANCE, "(?1 ^ ?2)"));
- }
- }
然后配置application.yaml文件替换掉JAP默认的MySQL5Dialect:
spring:
jpa:
hibernate:
ddl-auto: update
database-platform: com.darmi.log.ExtendedMySQL5Dialect
然后使用Specification的时候就能正常使用自定义函数了。
- public static Specification<TbVehicle> error(List<Integer> errors) {
- return CollectionUtils.isEmpty(errors) ?
- (root, query, builder) -> builder.conjunction() :
- (root, query, builder) -> {
- // convert list to integer
- Integer res = Error.codeCompress(errors).intValue();
- Expression<Integer> errorMask = builder.literal(res);
- // bit_and function is configured in ExtendedMySQL5Dialect
- Expression<Integer> errorFunction =
- builder.function("bit_and", Integer.class, root.get("error"), errorMask);
-
- return builder.greaterThan(errorFunction, 0);
- };
- }