主从配置和分库分表
Field 'brand_id' doesn't have a default value
Insert statement does not support sharding table routing to multiple data nodes
QueryWrapper和LambdaQueryWrapper
执行testSave()和findByBrandStatus()测试
Sharding-JDBC 最早是当当网内部使用的一款分库分表框架,到2017年的时候才开始对外开源,这几年在大量社区贡献者的不断迭代下,功能也逐渐完善,现已更名为 ShardingSphere,2020年4⽉16⽇正式成为 Apache 软件基⾦会的顶级项⽬。
随着版本的不断更迭 ShardingSphere 的核心功能也变得多元化起来。如图7-1,ShardingSphere生态包含三款开源分布式数据库中间件解决方案,Sharding-JDBC、Sharding-Proxy、Sharding-Sidecar。
Apache ShardingSphere 5.x 版本开始致力于提供可插拔架构,项目的功能组件能够灵活的以可插拔的方式进行扩展。 目前,数据分片、读写分离、数据加密、影子库压测等功能,以及对 MySQL、PostgreSQL、SQLServer、Oracle 等 SQL 与协议的支持,均通过插件的方式织入项目。 开发者能够像使用积木一样定制属于自己的独特系统。Apache ShardingSphere 目前已提供数十个 SPI 作为系统的扩展点,而且仍在不断增加中。
| Sharding-JDBC | Sharding-Proxy | Sharding-Sidecar | |
| 数据库 | 任意 | MySQL | MySQL |
| 连接消耗数 | 高 | 低 | 高 |
| 异构语言 | JAVA | 任意 | 任意 |
| 性能 | 损耗低 | 损耗略高 | 损耗低 |
| 无中心化 | 是 | 否 | 是 |
| 静态入口 | 无 | 有 | 无 |
Sharding-JDBC是比较常用的一个组件,它定位的是一个增强版的JDBC驱动,简单来说就是在应用端来完成数据库分库分表相关的路由和分片操作,也是我们本阶段重点去分析的组件。
我们在项目内引入Sharding-JDBC的依赖,我们的业务代码在操作数据库的时候,就会通过Sharding-JDBC的代码连接到数据库。也就是分库分表的一些核心动作,比如SQL解析,路由,执行,结果处理,都是由它来完成的,它工作在客户端。Sharding-JDBC是对原有JDBC驱动的增强,在分库分表的场景中,为应用提供了如图所示的功能。
| 数据分片 | 分布式事务 | 数据库治理 |
| 分库分表 | 标准化事务接口 | 配置动态化 |
| 读写分离 | XA强一致事务 | 编排治理 |
| 分片策略定制化 | 柔性事务 | 数据脱敏 |
| 无中心化分布式主键 | 可视化链路追踪 |
Sharding-Proxy有点类似于Mycat,它是提供了数据库层面的代理,什么意思呢?简单来说,以前我们的应用是直连数据库,引入了Sharding-Proxy之后,我们的应用是直连Sharding-Proxy,然后Sharding-Proxy通过处理之后再转发到mysql中。
这种方式的好处在于,用户不需要感知到分库分表的存在,相当于正常访问mysql。目前Sharding-Proxy支持Mysql和PostgreSQL两种数据库协议
看到Sidecar,大家应该就能想到服务网格架构,它主要定位于 Kubernetes 的云原生数据库代理,以 Sidecar 的形式代理所有对数据库的访问。目前Sharding-Sidecar还处于开发阶段未发布。
在Sharding-JDBC中,有一些表的概念,需要给大家普及一下,逻辑表、真实表、分片键、数据节点、动态表、广播表、绑定表。
配置文件中的定义,t_order、t_user等就是逻辑表。 后面的分库db1.t_user或者分表t_user_0等才是真实的表
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=ds-$->{0..1}.t_order_$->{0..1}

广播表也叫全局表,也就是它会存在于多个库中冗余,避免跨库查询问题,比如省份、字典等一些基础数据,为了避免分库分表后关联表查询这些基础数据存在跨库问题,所以可以把这些数据同步给每一个数据库节点,这个就叫广播表
配置文件中的定义
# 广播表, 其主节点是ds0
spring.shardingsphere.sharding.broadcast-tables=t_config
spring.shardingsphere.sharding.tables.t_config.actual-data-nodes=ds$->{0}.t_config

表的数据是存在逻辑的主外键关系的,比如订单表order_info,存的是汇总的商品数,商品金额;订单明细表order_detail,是每个商品的价格,个数等等。或者叫做从属关系,父表和子表的关系。他们之间会经常有关联查询的操作,如果父表的数据和子表的数据分别存储在不同的数据库,跨库关联查询也比较麻烦。所以我们能不能把父表和数据和从属于父表的数据落到一个节点上呢?比如order_id=1001的数据在node1,它所有的明细数据也放到node1;order_id=1002的数据在node2,它所有的明细数据都放到node2,这样在关联查询的时候依然是在一个数据库

绑定表规则,多组绑定规则使用数组形式配置
spring.shardingsphere.rules.sharding.binding-tables=t_order,t_order_item
如果存在多个绑定表规则,可以用数组的方式声明
# 绑定表规则列表
spring.shardingsphere.rules.sharding.binding-tables[0]= spring.shardingsphere.rules.sharding.binding-tables[1]=
Sharding-JDBC内置了很多常用的分片策略,这些算法主要针对两个维度
Sharding-JDBC的分片策略包含了分片键和分片算法;
Sharding-JDBC提供内置了多种分片算法,包含四种类型分别是
自动分片算法,就是根据我们配置的算法表达式完成数据的自动分发功能,在Sharding-JDBC中提供了五种自动分片算法
标准分片策略(StandardShardingStrategy),它只支持对单个分片健(字段)为依据的分库分表,Sharding-JDBC提供了两种算法实现
类型:INLINE
使用 Groovy 的表达式,提供对 SQL 语句中的 = 和 IN 的分片操作支持,只支持单分片键。 对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的 Java 代码开发,如: t_user_$->{u_id % 8} 表示 t_user 表根据 u_id 模 8,而分成 8 张表,表名称为 t_user_0 到 t_user_7
配置方法如下。
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINEspring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds-$->{user_id % 2}spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.type=INLINEspring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.props.algorithm-expression=t_order_$->{order_id % 2}
和前面自动分片算法的自动时间段分片算法类似。
类型:INTERVAL
可配置属性:
| 属性名称 | 数据类型 | 说明 | 默认值 |
| datetime-pattern | String | 分片键的时间戳格式,必须遵循 Java DateTimeFormatter 的格式。例如:yyyy-MM-dd HH:mm:ss | - |
| datetime-lower | String | 时间分片下界值,格式与 datetime-pattern 定义的时间戳格式一致 | - |
| datetime-upper (?) | String | 时间分片上界值,格式与 datetime-pattern 定义的时间戳格式一致 | 当前时间 |
| sharding-suffix-pattern | String | 分片数据源或真实表的后缀格式,必须遵循 Java DateTimeFormatter 的格式,必须和 datetime-interval-unit 保持一致。例如:yyyyMM | - |
| datetime-interval-amount (?) | int | 分片键时间间隔,超过该时间间隔将进入下一分片 | 1 |
| datetime-interval-unit (?) | String | 分片键时间间隔单位,必须遵循 Java ChronoUnit 的枚举值。例如:MONTHS |
使用场景:SQL 语句中有>,>=, <=,<,=,IN 和 BETWEEN AND 等操作符,不同的是复合分片策略支持对多个分片健操作。
除了默认提供了分片算法之外,我们可以根据实际需求自定义分片算法,Sharding-JDBC同样提供了几种类型的扩展实现
Sharding-JDBC中默认提供了两种分布式序列算法
可配置属性:
| 属性名称 | 数据类型 | 说明 | 默认值 |
| worker-id (?) | long | 工作机器唯一标识 | 0 |
| max-vibration-offset (?) | int | 最大抖动上限值,范围[0, 4096)。注:若使用此算法生成值作分片值,建议配置此属性。此算法在不同毫秒内所生成的 key 取模 2^n (2^n一般为分库或分表数) 之后结果总为 0 或 1。为防止上述分片问题,建议将此属性值配置为 (2^n)-1 | 1 |
| max-tolerate-time-difference-milliseconds (?) | long | 最大容忍时钟回退时间,单位:毫秒 |
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- Table structure for brand_info_0
DROP TABLE IF EXISTS brand_info_0;
CREATE TABLE brand_info (
brand_id varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '品牌ID',
brand_name varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '品牌名称',
telephone varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '联系电话',
brand_web varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌网络',
brand_logo varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌logo URL',
brand_desc varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '品牌描述',
brand_status tinyint(1) NOT NULL DEFAULT 0 COMMENT '品牌状态,0禁用,1启用',
brand_order tinyint(4) NOT NULL DEFAULT 0 COMMENT '排序',
modified_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
PRIMARY KEY (brand_id) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '品牌信息表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
/**
* @author zhousong
* @ClassName ShardingApplication
* @description: 分库分表测试类
* @datetime 2021年 11月 27日 15:05
* @version: 1.0
*/
@SpringBootTest
public class BrandInfoServiceImplTest {
private static final Logger logger= LoggerFactory.getLogger(BrandInfoServiceImplTest.class);
@Resource
public IBrandInfoService iBrandInfoService;
@Test
public void testSave(){
List<BrandInfo> brandInfos=new ArrayList<BrandInfo>(12);
BrandInfo brandInfo ;
for (int i = 20; i < 42; i++) {
brandInfo = new BrandInfo();
brandInfo.setBrandDesc("toker.zhou品牌测试"+i);
brandInfo.setBrandStatus(i%2);
brandInfo.setBrandLogo("");
brandInfo.setBrandName("toker.zhou品牌测试"+i);
brandInfo.setBrandOrder(1);
if (i<10){
brandInfo.setTelephone("1336757129"+i);
}else{
brandInfo.setTelephone("133675712"+i);
}
brandInfo.setBrandWeb("http://minorcode.cn");
brandInfos.add(brandInfo);
iBrandInfoService.save(brandInfo);
}
logger.info("BrandInfo保存成功");
// iBrandInfoService.saveOrUpdateBatch(brandInfos);
}
}
spring:
shardingsphere:
# 内存模式,元数据保存在当前进程中
mode:
type: Memory
datasource:
names: master,slave$->{0..1}
master:
#shardingsphere默认连接池是Hikari
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://192.168.118.121:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: xxxx
slave0:
type: com.alibaba.druid.pool.DruidDataSource #shardingsphere默认连接池是Hikari
url: jdbc:mysql://192.168.118.120:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: xxxx
slave1:
type: com.alibaba.druid.pool.DruidDataSource #shardingsphere默认连接池是Hikari
url: jdbc:mysql://192.168.118.122:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: xxxx
rules:
readwrite-splitting:
data-sources:
toker-source:
load-balancer-name: round_robin_toker
write-data-source-name: master
read-data-source-names: slave0,slave1
load-balancers:
round_robin_toker:
type: ROUND_ROBIN
sharding:
tables:
brand:
actual-data-nodes: master.brand_info_copy$->{0..1},slave$->{0..1}.brand_info_copy$->{0..1} #也可以使用${0..n1}的形式,但是会与Spring属性文件占位符冲突,注意不要写成了$->{0,1}我之前就是在这个逗号上栽了跟头
database-strategy:
standard:
sharding-column: brand_status
sharding-algorithm-name: brand_mode
table-strategy:
standard:
sharding-column: brand_id
sharding-algorithm-name: brandId_mode
sharding-algorithms:
brand_mode:
type: MOD
props:
sharding-count: 2
brandId_mode:
type: MOD
props:
sharding-count: 2
key-generator:
column: brand_id
type: SNOWFLAKE
props:
sql:
show: true 实操注意的问题:创建实体类时,默认一个实体类对应一张表,若要对应两张表,需要在properties文件中添加配置(spring.main.allow-bean-definition-overriding=true)
5.x版本以前
spring.shardingsphere.props.sql.show=true
5.x版本以后,sql.show参数调整为sql-show
spring.shardingsphere.props.sql-show=true
所以上面配置文件应该是
props:
sql-show:true
// @TableId(value = "brand_id", type = IdType.AUTO)
private String brandId;
.................... ....................
sharding:
tables:
brand_info:
....................
column: brand_id
key-generator-name: Brand_SNOWFLAKE
....................
key-generators:
Brand_SNOWFLAKE:
type: SNOWFLAKE
### The error may involve com.toker.cloud.platform.sharding.mapper.BrandInfoMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO brand_info ( brand_name, telephone, brand_web, brand_logo, brand_desc, brand_status, brand_order ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
### Cause: java.sql.SQLException: Field 'brand_id' doesn't have a default value
; Field 'brand_id' doesn't have a default value; nested exception is java.sql.SQLException: Field 'brand_id' doesn't have a default value
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:251)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70)
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:88)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
........................................................................................................................................................
Caused by: java.sql.SQLException: Field 'brand_id' doesn't have a default value
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)
at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:354)
at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:497)
at org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement$2.executeSQL(ShardingSpherePreparedStatement.java:412)
at org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement$2.executeSQL(ShardingSpherePreparedStatement.java:408)
at org.apache.shardingsphere.infra.executor.sql.execute.engine.driver.jdbc.JDBCExecutorCallback.execute(JDBCExecutorCallback.java:86)
at org.apache.shardingsphere.infra.executor.sql.execute.engine.driver.jdbc.JDBCExecutorCallback.execute(JDBCExecutorCallback.java:66)
at org.apache.shardingsphere.infra.executor.kernel.ExecutorEngine.syncExecute(ExecutorEngine.java:135)
at org.apache.shardingsphere.infra.executor.kernel.ExecutorEngine.parallelExecute(ExecutorEngine.java:131)
参看开头我贴出错的配置文件
sharding:
tables:
brand: ##错误原因在这里
....................
column: brand_id
key-generator-name: Brand_SNOWFLAKE
....................
key-generators: ####这里从原来的key-generators和tables同属于sharding的子配置项
Brand_SNOWFLAKE:
type: SNOWFLAKE
错误原因。我用mybatis生成的实体类brandInfo以及mapper默认的表对应的是brand_info。当shardingsphere执行 INSERT INTO brand_info ( brand_name....这条语句的时候, 由于配置文件没有brand_info这个表对应的分表配置。那么直接放过。这个时候对brand_id配置的雪花算法自然无效。所以这里有两种解决办法
解决办法:第一种办法在实体类上加上@TableName("brand"),第二种办法修改bootstrap.yml这段配置,直接修改为brand_info
sharding:
tables:
brand_info: ####这里从原来的brand直接修改为brand_info
....................
column: brand_id
key-generator-name: Brand_SNOWFLAKE
....................
key-generators: ####这里从原来的key-generators和tables同属于sharding的子配置项
Brand_SNOWFLAKE:
type: SNOWFLAKE
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: java.lang.IllegalStateException: Insert statement does not support sharding table routing to multiple data nodes.
### The error may exist in com/toker/cloud/platform/sharding/mapper/BrandInfoMapper.java (best guess)
### The error may involve com.toker.cloud.platform.sharding.mapper.BrandInfoMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO brand_info ( brand_name, telephone, brand_web, brand_logo, brand_desc, brand_status, brand_order ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
### Cause: java.lang.IllegalStateException: Insert statement does not support sharding table routing to multiple data nodes.
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
at com.sun.proxy.$Proxy216.insert(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:271)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:60)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
at com.sun.proxy.$Proxy217.insert(Unknown Source)
解决方案:看错误明显是insert没有路由到的问题。发现sharding-algorithms路径写到了 brand_info下面。它应该属于sharding的子项才对
sharding:
tables:
brand_info:
....................................
sharding-algorithms:
brand_mode:
type: MOD
props:
sharding-count: 2
brandId_mode:
type: MOD
props:
sharding-count: 2
### SQL: INSERT INTO brand_info ( brand_name, telephone, brand_web, brand_logo, brand_desc, brand_status, brand_order ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
### Cause: java.lang.IllegalStateException: No database route info
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
at com.sun.proxy.$Proxy216.insert(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:271)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:60)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
at com.sun.proxy.$Proxy217.insert(Unknown Source)
正确配置要点
切勿网上拷贝的配置,从github找到相应的版本的example核对下配置.比如我是5.1.2的版本。则核对位置;https://github.com/apache/shardingsphere/blob/5.1.2/examples/shardingsphere-jdbc-example/mixed-feature-example/sharding-readwrite-splitting-example/sharding-readwrite-splitting-spring-boot-mybatis-example/src/main/resources/application-sharding-readwrite-splitting.properties
spring:
shardingsphere:
# 内存模式,元数据保存在当前进程中
mode:
type: Memory
datasource:
names: master,slave0,slave1
master:
#shardingsphere默认连接池是Hikari
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://192.168.118.121:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: xxxx
slave0:
type: com.alibaba.druid.pool.DruidDataSource #shardingsphere默认连接池是Hikari
url: jdbc:mysql://192.168.118.120:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: xxxx
slave1:
type: com.alibaba.druid.pool.DruidDataSource #shardingsphere默认连接池是Hikari
url: jdbc:mysql://192.168.118.122:3306/tokercart?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: xxxx
rules:
readwrite-splitting:
data-sources:
readwrite_ds:
type: Static
props:
write-data-source-name: master
read-data-source-names: slave0,slave1
load-balancer-name: round_robin_toker
load-balancers:
round_robin_toker:
type: ROUND_ROBIN
sharding:
tables:
brand_info:
actual-data-nodes: readwrite_ds.brand_info_$->{0..1}
# database-strategy:
# standard:
# sharding-column: brand_status
# sharding-algorithm-name: brand_mode
table-strategy:
standard:
sharding-column: brand_status
sharding-algorithm-name: brand_mode
key-generate-strategy:
column: brand_id
key-generator-name: Brand_SNOWFLAKE
sharding-algorithms:
brand_mode:
type: MOD
props:
sharding-count: 2
# brandId_mode:
# type: MOD
# props:
# sharding-count: 2
key-generators:
Brand_SNOWFLAKE:
type: SNOWFLAKE
props:
# sql:
# show: true
sql-show: true
enabled: true
main:
allow-bean-definition-overriding: true
在前面的BrandInfoServiceImplTest中再添加一个查询类,结合前面的写入类观测结果
@Test
public void findByBrandStatus(){
QueryWrapper<BrandInfo> queryWrapper = new QueryWrapper<BrandInfo>();
// 面向表字段的查询
queryWrapper.eq("brand_status",0);
Page<BrandInfo> page=new Page<BrandInfo>(1,2);
IPage<BrandInfo> results=iBrandInfoService.page(page,queryWrapper);
logger.info("case1查询出的分页对象为:{}", JSON.toJSON(results));
Page<BrandInfo> page2=new Page<BrandInfo>(2,2);
//面向对象的写法
LambdaQueryWrapper<BrandInfo> lambdaQueryWrapper = new LambdaQueryWrapper<BrandInfo>();
lambdaQueryWrapper.eq(BrandInfo::getBrandStatus,1);
//下面这段方法在mybatis在做属性转换时候值是一段函数。而非从get或者is解析出来的值
// lambdaQueryWrapper.eq((x)->{
// return x.getBrandStatus();
// },1);
IPage<BrandInfo> results2=iBrandInfoService.page(page2,lambdaQueryWrapper);
logger.info("case2查询出的分页对象为:{}", JSON.toJSON(results2));
// queryWrapper.clear();
// queryWrapper.eq("brand_status",1);
// IPage<BrandInfo> results3=iBrandInfoService.page(page,lambdaQueryWrapper);
// logger.info("case3查询出的分页对象为:{}", JSON.toJSON(results3));
}


Actual SQL: slave0 ::: SELECT brand_id,brand_name,telephone,brand_web,brand_logo,brand_desc,brand_status,brand_order,modified_time FROM brand_info_0 WHERE (brand_status = ?) LIMIT ? ::: [0, 2]
2022-11-29 15:21:35.003 INFO 56624 --- [ main] c.t.c.p.s.s.i.BrandInfoServiceImplTest : case1查询出的分页对象为:{"current":1,"total":17,"hitCount":false,"pages":9,"optimizeCountSql":true,"size":2,"records":[{"brandWeb":"http://minorcode.cn","modifiedTime":"2022-11-29T10:10:10","brandName":"toker.zhou品牌测试0","brandDesc":"toker.zhou品牌测试0","brandId":"804289715246202880","telephone":"13367571290","brandStatus":0,"brandLogo":"","brandOrder":1},{"brandWeb":"http://minorcode.cn","modifiedTime":"2022-11-29T10:10:12","brandName":"toker.zhou品牌测试2","brandDesc":"toker.zhou品牌测试2","brandId":"804292230620643328","telephone":"13367571292","brandStatus":0,"brandLogo":"","brandOrder":1}],"searchCount":true,"orders":[]}
: Actual SQL: slave1 ::: SELECT brand_id,brand_name,telephone,brand_web,brand_logo,brand_desc,brand_status,brand_order,modified_time FROM brand_info_1 WHERE (brand_status = ?) LIMIT ?,? ::: [1, 2, 2]
2022-11-29 15:21:35.066 INFO 56624 --- [ main] c.t.c.p.s.s.i.BrandInfoServiceImplTest : case2查询出的分页对象为:{"current":2,"total":17,"hitCount":false,"pages":9,"optimizeCountSql":true,"size":2,"records":[{"brandWeb":"http://minorcode.cn","modifiedTime":"2022-11-29T10:10:17","brandName":"toker.zhou品牌测试5","brandDesc":"toker.zhou品牌测试5","brandId":"804292251910930433","telephone":"13367571295","brandStatus":1,"brandLogo":"","brandOrder":1},{"brandWeb":"http://minorcode.cn","modifiedTime":"2022-11-29T10:10:19","brandName":"toker.zhou品牌测试7","brandDesc":"toker.zhou品牌测试7","brandId":"804292259913662465","telephone":"13367571297","brandStatus":1,"brandLogo":"","brandOrder":1}],"searchCount":true,"orders":[]} 至此主从+分库分表配置测试成功。 下一步计划将跟踪Debug的源码分析贴出来。
错误写法:lambdaQueryWrapper.eq((x)->{
return x.getBrandStatus();
},1);
正确写法:lambdaQueryWrapper.eq(BrandInfo::getBrandStatus,1);
如果用lambda表达式,这个name的值会被判定是一段函数,mybatis在做属性转换的时候直接报错

关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,
我有一个在Linux服务器上运行的ruby脚本。它不使用rails或任何东西。它基本上是一个命令行ruby脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m
注意:本文主要掌握DCN自研无线产品的基本配置方法和注意事项,能够进行一般的项目实施、调试与运维AP基本配置命令AP登录用户名和密码均为:adminAP默认IP地址为:192.168.1.10AP默认情况下DHCP开启AP静态地址配置:setmanagementstatic-ip192.168.10.1AP开启/关闭DHCP功能:setmanagementdhcp-statusup/downAP设置默认网关:setstatic-ip-routegeteway192.168.10.254查看AP基本信息:getsystemgetmanagementgetmanaged-apgetrouteAP配
基础版云数据库RDS的产品系列包括基础版、高可用版、集群版、三节点企业版,本文介绍基础版实例的相关信息。RDS基础版实例也称为单机版实例,只有单个数据库节点,计算与存储分离,性价比超高。说明RDS基础版实例只有一个数据库节点,没有备节点作为热备份,因此当该节点意外宕机或者执行重启实例、变更配置、版本升级等任务时,会出现较长时间的不可用。如果业务对数据库的可用性要求较高,不建议使用基础版实例,可选择其他系列(如高可用版),部分基础版实例也支持升级为高可用版。基础版与高可用版的对比拓扑图如下所示。优势 性能由于不提供备节点,主节点不会因为实时的数据库复制而产生额外的性能开销,因此基础版的性能相对于
1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模
我是ruby的新手,正在配置IRB。我喜欢pretty-print(需要'pp'),但总是输入pp来漂亮地打印它似乎很麻烦。我想做的是默认情况下让它漂亮地打印出来,所以如果我有一个var,比如说,'myvar',然后键入myvar,它会自动调用pretty_inspect而不是常规检查。我从哪里开始?理想情况下,我将能够向我的.irbrc文件添加一个自动调用的方法。有什么想法吗?谢谢! 最佳答案 irb中默认pretty-print对象正是hirb被迫去做。Theseposts解释hirb如何将几乎所有内容转换为ascii表。虽