一、创建库表
1、创建两个数据库
CREATE SCHEMA `shard_db_0` DEFAULT CHARACTER SET utf8 ;
CREATE SCHEMA `shard_db_1` DEFAULT CHARACTER SET utf8 ;
2、在每个数据库各创建三个分表
CREATE TABLE `tb_order_0` (
`order_id` bigint(20) NOT NULL,
`buyer_id` bigint(20) not null comment '买家ID',
`seller_id` bigint(20) not null comment '卖家ID',
`order_name` varchar(64) not NULL COMMENT '商品名称',
`price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `tb_order_1` (
`order_id` bigint(20) NOT NULL,
`buyer_id` bigint(20) not null comment '买家ID',
`seller_id` bigint(20) not null comment '卖家ID',
`order_name` varchar(64) not NULL COMMENT '商品名称',
`price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `tb_order_2` (
`order_id` bigint(20) NOT NULL,
`buyer_id` bigint(20) not null comment '买家ID',
`seller_id` bigint(20) not null comment '卖家ID',
`order_name` varchar(64) not NULL COMMENT '商品名称',
`price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2、创建工程
1、引入maven依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>sharding-sphere-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sharding-sphere-demo</name>
<description>sharding-sphere-demo</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/shardingsphere-jdbc-core-spring-boot-starter -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、创建po
@Builder
@Data
@TableName("tb_buyer")
public class TbBuyer {
@TableId
private Long buyerId;
private String buyerName;
private Boolean sex;
private Integer age;
}
@Builder
@Data
@TableName("tb_order")
public class TbOrder {
@TableId
private Long orderId;
private Long buyerId;
private Long sellerId;
private String orderName;
private BigDecimal price;
}
@Builder
@Data
@TableName("tb_seller")
public class TbSeller {
@TableId
private Long sellerId;
private String sellerName;
private Boolean sex;
private Integer age;
}
3、创建mapper
package com.example.shardingsphere.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shardingsphere.po.TbBuyer;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface TbBuyerMapper extends BaseMapper<TbBuyer> {
// 可以在这里定义自定义方法
}
package com.example.shardingsphere.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shardingsphere.po.TbOrder;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface TbOrderMapper extends BaseMapper<TbOrder> {
// 可以在这里定义自定义方法
}
package com.example.shardingsphere.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shardingsphere.po.TbSeller;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface TbSellerMapper extends BaseMapper<TbSeller> {
// 可以在这里定义自定义方法
}
4、创建controller控制器
package com.example.shardingsphere.web;
import com.example.shardingsphere.mapper.TbBuyerMapper;
import com.example.shardingsphere.mapper.TbOrderMapper;
import com.example.shardingsphere.mapper.TbSellerMapper;
import com.example.shardingsphere.po.TbOrder;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class OrderController {
@Resource
private TbBuyerMapper tbBuyerMapper ;
@Resource
private TbSellerMapper tbSellerMapper ;
@Resource
private TbOrderMapper tbOrderMapper ;
/**
* 查询订单详情
*/
@GetMapping("/order/info/{orderId}")
public Map<String,Object> orderInfo (@PathVariable Long orderId){
Map<String,Object> orderMap = new HashMap<>() ;
TbOrder order = tbOrderMapper.selectById(orderId) ;
if (order != null){
orderMap.put("order",order) ;
orderMap.put("buyer",tbBuyerMapper.selectById(order.getBuyerId())) ;
orderMap.put("seller",tbSellerMapper.selectById(order.getSellerId())) ;
}
return orderMap ;
}
}
5、用到的工具类(雪花算法生成关联表主键ID,测试用,无特殊意义)
package com.example.shardingsphere.provider;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 雪花算法
*
*/
@Slf4j
@Component
public class SnowFlakeProvider implements ApplicationRunner {
private long workerId;
private Snowflake snowFlake;
private long datacenterId;
/**
* 初始化方法,用于在类实例化后进行必要的设置。
* 本方法主要用于确定WorkerId,这是一个在分布式系统中唯一标识当前节点的ID。
* 它通过获取本地主机名或IP并转换为long值来实现。
* 如果无法获取本地主机名或IP,或者转换过程中发生异常,将不设置workerId,可能导致后续ID生成失败。
*/
@Override
public void run(ApplicationArguments args) throws Exception {
//初始化workId
initWorkId();
initDataCenterId();
//初始化SnowflakeID生成器
createSnowFlake(workerId, datacenterId);
}
/**
* 根据IP Address 生成workId
*
* @return
*/
private void initWorkId() {
try {
String hostAddress = Inet4Address.getLocalHost().getHostAddress();
log.info("hostAddress========={}", hostAddress);
int[] ints = StringUtils.toCodePoints(hostAddress);
int sums = 0;
for (int b : ints) {
sums += b;
}
workerId= (long) (sums % 32);
} catch (UnknownHostException e) {
log.error("根据IP获取workId失败。", e);
// 如果获取失败,则使用随机数备用
workerId = RandomUtil.randomLong(0, 31);
}
}
/**
* 根据HostName 生成dataCenterId
* @return
*/
private void initDataCenterId() {
String hostName = getHostName();
log.info("hostName========={}", hostName);
int[] ints = StringUtils.toCodePoints(hostName);
int sums = 0;
for (int i : ints) {
sums += i;
}
datacenterId = (long) (sums % 32);
}
/**
* 获取 hostName
* SystemUtils.getHostName() 在mac系统为空处理
* @return
*/
public static String getHostName() {
//获取当前操作系统名称,例如:windows xp,linux 等
String osName = System.getProperty("os.name");
String hostName = null;
if(!StringUtils.startsWithIgnoreCase(osName,"mac")){
hostName = SystemUtils.getHostName();
}else{
try {
hostName = InetAddress.getLocalHost().getHostName().toUpperCase();
} catch (UnknownHostException e) {
hostName = "N/A";
log.error("获取 hostName错误:", e);
}
}
return hostName;
}
/**
* 初始化SnowflakeID生成器。
* 使用指定的workerId和datacenterId创建SnowflakeID生成器实例。如果创建失败,将抛出异常。
*
* @param workerId 工作节点ID,用于标识当前节点。
* @param datacenterId 数据中心ID,用于标识数据中心。
* @throws IllegalArgumentException 如果Snowflake实例创建失败,则抛出此异常。
* @throws RuntimeException 如果Snowflake实例创建过程中发生其他异常,则抛出此异常。
*/
private Snowflake createSnowFlake(long workerId, long datacenterId) {
try {
this.snowFlake = IdUtil.createSnowflake(workerId, datacenterId);
// 参数合法性检查
if (null == snowFlake) {
throw new IllegalArgumentException("Failed to create Snowflake instance. Check workerId and datacenterId.");
}
return snowFlake;
} catch (Exception e) {
log.error("创建Snowflake实例失败,异常:{}", e.getMessage());
throw new RuntimeException("Initialization failed for Snowflake ID generator.", e);
}
}
/**
* 获取一个唯一的雪花ID。使用Snowflake算法生成ID,该算法由Twitter开源。
* 具体来说,这个方法调用了Snowflake实例的nextId方法来获取一个唯一的长整型ID。
* 使用synchronized关键字确保了这个方法在多线程环境下的线程安全,
* 保证了ID的生成不会因为并发而产生重复或错乱。
*
* @return 生成的唯一长整型ID。
*/
public synchronized long snowflakeId() {
// 调用Snowflake实例的nextId方法获取唯一ID
return this.snowFlake.nextId();
}
/**
* 生成基于Snowflake算法的唯一ID。
* <p>
* 使用Snowflake算法生成唯一的分布式ID。该算法由Twitter提出,通过组合时间戳、工作机器ID和序列号来生成全局唯一的ID。
* 具体结构如下:
* - 1位符号位,用于区分正负,由于ID只能是正数,所以这个位始终为0。
* - 41位时间戳,精确到毫秒,可以使用约69年。
* - 10位工作机器ID,可以部署在1024个节点,包括5位数据中心ID和5位工作机器ID。
* - 12位序列号,用于同一毫秒内生成的ID去重,每个节点每毫秒可以生成4096个ID。
* <p>
* 参数:
* workerId - 工作机器ID,用于标识不同的工作机器或进程。
* datacenterId - 数据中心ID,用于标识不同的数据中心。
* <p>
* 返回:
* 一个长整型的唯一ID,根据Snowflake算法生成。
*/
public synchronized long snowflakeId(long workerId, long datacenterId) {
return createSnowFlake(workerId, datacenterId).nextId();
}
}
6、测试用例
package com.example.shardingsphere;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.shardingsphere.mapper.TbOrderMapper;
import com.example.shardingsphere.po.TbOrder;
import com.example.shardingsphere.provider.SnowFlakeProvider;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class ShardTest extends ShardingSphereDemoApplicationTests{
@Autowired
private SnowFlakeProvider snowFlakeProvider;
@Autowired
private TbOrderMapper tbOrderMapper ;
/**
* 写入100条数据
*/
@Test
public void testOrderInsert (){
List<TbOrder> list = new ArrayList<>();
for (int i=1 ; i<= 10 ; i++){
TbOrder order = TbOrder.builder()
// .orderId(snowFlakeProvider.snowflakeId())
.buyerId(snowFlakeProvider.snowflakeId())
.sellerId(snowFlakeProvider.snowflakeId())
.orderName("订单"+ RandomUtil.randomInt(6))
.price(RandomUtil.randomBigDecimal().setScale(2, BigDecimal.ROUND_HALF_UP))
.build();
list.add(order);
}
tbOrderMapper.insert(list);
}
@Test
public void testOrderQuery (){
TbOrder order = tbOrderMapper.selectById(5) ;
Assert.assertNotNull(order);
log.info("查询结果:"+ JSONUtil.toJsonStr(order));
}
@Test
public void testOrderUpdate (){
TbOrder order = tbOrderMapper.selectById(3) ;
Assert.assertNotNull(order);
order.setBuyerId(1l);
order.setSellerId(3l);
int count = tbOrderMapper.updateById(order) ;
log.info("更新记录数:"+count);
}
@Test
public void testOrderPage (){
//分页参数
Page<TbOrder> rowPage = new Page<>(1, 2);
//queryWrapper组装查询where条件
LambdaQueryWrapper<TbOrder> queryWrapper = new LambdaQueryWrapper<>();
Page<TbOrder> page = tbOrderMapper.selectPage(rowPage, queryWrapper);
log.info("分页查询结果:"+ JSONUtil.toJsonStr(page));
}
}
7、yml配置
server:
port: 8095
spring:
application:
name: dynamic-datasource-spring-boot-starter
shardingsphere:
mode:
type: Standalone
repository:
type: JDBC
database:
name: db0
# 数据源配置
datasource:
# 数据源名称,多数据源以逗号分隔
names: db0,db1
db0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://xx:3306/shard_db_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: xx
password: xx
db1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://xx:3306/shard_db_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: xx
password: xx
# 分片规则配置
rules:
sharding:
# 分片算法配置
sharding-algorithms:
database-inline:
# 分片算法类型
type: INLINE
props:
# 分片算法的行表达式(算法自行定义,此处为方便演示效果)
algorithm-expression: db${order_id % 2}
table-inline:
# 分片算法类型
type: INLINE
props:
# 分片算法的行表达式
algorithm-expression: tb_order_${order_id % 3}
tables:
# 逻辑表名称
tb_order:
# 行表达式标识符可以使用 ${...} 或 $->{...},但前者与 Spring 本身的属性文件占位符冲突,因此在 Spring 环境中使用行表达式标识符建议使用 $->{...}
actual-data-nodes: db${0..1}.tb_order_${0..2}
# 分库策略
database-strategy:
standard:
# 分片列名称
sharding-column: order_id
# 分片算法名称
sharding-algorithm-name: database-inline
# 分表策略
table-strategy:
standard:
# 分片列名称
sharding-column: order_id
# 分片算法名称
sharding-algorithm-name: table-inline
# 属性配置
props:
# 展示修改以后的sql语句
sql-show: true
mybatis-plus:
type-aliases-package: com.example.dynamic.po
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations:
- classpath:/mapper/*.xml
至此,一个支持分库分表的工程搭建完成。
########################################################
关于搭建过程中遇到的问题(shardingsphere-jdbc-core-spring-boot-starter 5.2.1)
1、Caused by: java.lang.NoSuchMethodError: org.yaml.snakeyaml.representer.Representer: method 'void <init>()' not found
解答:该问题是由于版本问题导致的,是个兼容性问题。
shardingsphere-jdbc-core-spring-boot-starter 5.2.1中使用snakeyaml版本1.33 spring-boot-starter-web使用snakeyaml版本2.2 2.2中删除了Representer和SafeRepresenter的无参构造器,因此导致了该异常。
可通过降低boot版本号从而降低其中snakeyaml版本号解决,但由于snakeyaml 1.x版本有安全问题,而shardingsphere-jdbc-core-spring-boot-starter已是当前最高版本,因此可通过重新jar中bean新增无参构造器去覆盖jar bean去解决。
com.main.java下新增目录org.yaml.snakeyaml.representer
目录下新建bean
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.yaml.snakeyaml.representer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.introspector.PropertyUtils;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;
public class Representer extends SafeRepresenter {
protected Map<Class<? extends Object>, TypeDescription> typeDefinitions = Collections.emptyMap();
public Representer() {
this.representers.put(null, new RepresentJavaBean());
}
public Representer(DumperOptions options) {
super(options);
this.representers.put(null, new RepresentJavaBean());
}
public TypeDescription addTypeDescription(TypeDescription td) {
if (Collections.EMPTY_MAP == this.typeDefinitions) {
this.typeDefinitions = new HashMap();
}
if (td.getTag() != null) {
this.addClassTag(td.getType(), td.getTag());
}
td.setPropertyUtils(this.getPropertyUtils());
return (TypeDescription)this.typeDefinitions.put(td.getType(), td);
}
public void setPropertyUtils(PropertyUtils propertyUtils) {
super.setPropertyUtils(propertyUtils);
Collection<TypeDescription> tds = this.typeDefinitions.values();
Iterator var3 = tds.iterator();
while(var3.hasNext()) {
TypeDescription typeDescription = (TypeDescription)var3.next();
typeDescription.setPropertyUtils(propertyUtils);
}
}
protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
List<NodeTuple> value = new ArrayList(properties.size());
Tag customTag = (Tag)this.classTags.get(javaBean.getClass());
Tag tag = customTag != null ? customTag : new Tag(javaBean.getClass());
MappingNode node = new MappingNode(tag, value, FlowStyle.AUTO);
this.representedObjects.put(javaBean, node);
DumperOptions.FlowStyle bestStyle = FlowStyle.FLOW;
Iterator var8 = properties.iterator();
while(true) {
NodeTuple tuple;
do {
if (!var8.hasNext()) {
if (this.defaultFlowStyle != FlowStyle.AUTO) {
node.setFlowStyle(this.defaultFlowStyle);
} else {
node.setFlowStyle(bestStyle);
}
return node;
}
Property property = (Property)var8.next();
Object memberValue = property.get(javaBean);
Tag customPropertyTag = memberValue == null ? null : (Tag)this.classTags.get(memberValue.getClass());
tuple = this.representJavaBeanProperty(javaBean, property, memberValue, customPropertyTag);
} while(tuple == null);
if (!((ScalarNode)tuple.getKeyNode()).isPlain()) {
bestStyle = FlowStyle.BLOCK;
}
Node nodeValue = tuple.getValueNode();
if (!(nodeValue instanceof ScalarNode) || !((ScalarNode)nodeValue).isPlain()) {
bestStyle = FlowStyle.BLOCK;
}
value.add(tuple);
}
}
protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) {
ScalarNode nodeKey = (ScalarNode)this.representData(property.getName());
boolean hasAlias = this.representedObjects.containsKey(propertyValue);
Node nodeValue = this.representData(propertyValue);
if (propertyValue != null && !hasAlias) {
NodeId nodeId = nodeValue.getNodeId();
if (customTag == null) {
if (nodeId == NodeId.scalar) {
if (property.getType() != Enum.class && propertyValue instanceof Enum) {
nodeValue.setTag(Tag.STR);
}
} else {
if (nodeId == NodeId.mapping && property.getType() == propertyValue.getClass() && !(propertyValue instanceof Map) && !nodeValue.getTag().equals(Tag.SET)) {
nodeValue.setTag(Tag.MAP);
}
this.checkGlobalTag(property, nodeValue, propertyValue);
}
}
}
return new NodeTuple(nodeKey, nodeValue);
}
protected void checkGlobalTag(Property property, Node node, Object object) {
if (!object.getClass().isArray() || !object.getClass().getComponentType().isPrimitive()) {
Class<?>[] arguments = property.getActualTypeArguments();
if (arguments != null) {
Class t;
Iterator iter;
Iterator var9;
if (node.getNodeId() == NodeId.sequence) {
t = arguments[0];
SequenceNode snode = (SequenceNode)node;
Iterable<Object> memberList = Collections.emptyList();
if (object.getClass().isArray()) {
memberList = Arrays.asList((Object[])object);
} else if (object instanceof Iterable) {
memberList = (Iterable)object;
}
iter = ((Iterable)memberList).iterator();
if (iter.hasNext()) {
var9 = snode.getValue().iterator();
while(var9.hasNext()) {
Node childNode = (Node)var9.next();
Object member = iter.next();
if (member != null && t.equals(member.getClass()) && childNode.getNodeId() == NodeId.mapping) {
childNode.setTag(Tag.MAP);
}
}
}
} else if (object instanceof Set) {
t = arguments[0];
MappingNode mnode = (MappingNode)node;
Iterator<NodeTuple> ite = mnode.getValue().iterator();
Set<?> set = (Set)object;
var9 = set.iterator();
while(var9.hasNext()) {
Object member = var9.next();
NodeTuple tuple = (NodeTuple)ite.next();
Node keyNode = tuple.getKeyNode();
if (t.equals(member.getClass()) && keyNode.getNodeId() == NodeId.mapping) {
keyNode.setTag(Tag.MAP);
}
}
} else if (object instanceof Map) {
t = arguments[0];
Class<?> valueType = arguments[1];
MappingNode mnode = (MappingNode)node;
iter = mnode.getValue().iterator();
while(iter.hasNext()) {
NodeTuple tuple = (NodeTuple)iter.next();
this.resetTag(t, tuple.getKeyNode());
this.resetTag(valueType, tuple.getValueNode());
}
}
}
}
}
private void resetTag(Class<? extends Object> type, Node node) {
Tag tag = node.getTag();
if (tag.matches(type)) {
if (Enum.class.isAssignableFrom(type)) {
node.setTag(Tag.STR);
} else {
node.setTag(Tag.MAP);
}
}
}
protected Set<Property> getProperties(Class<? extends Object> type) {
return this.typeDefinitions.containsKey(type) ? ((TypeDescription)this.typeDefinitions.get(type)).getProperties() : this.getPropertyUtils().getProperties(type);
}
protected class RepresentJavaBean implements Represent {
protected RepresentJavaBean() {
}
public Node representData(Object data) {
return Representer.this.representJavaBean(Representer.this.getProperties(data.getClass()), data);
}
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.yaml.snakeyaml.representer;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Pattern;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.DumperOptions.NonPrintableStyle;
import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.reader.StreamReader;
class SafeRepresenter extends BaseRepresenter {
protected Map<Class<? extends Object>, Tag> classTags;
protected TimeZone timeZone = null;
protected DumperOptions.NonPrintableStyle nonPrintableStyle;
private static final Pattern MULTILINE_PATTERN = Pattern.compile("\n|\u0085|\u2028|\u2029");
public SafeRepresenter() {
this(new DumperOptions());
}
public SafeRepresenter(DumperOptions options) {
if (options == null) {
throw new NullPointerException("DumperOptions must be provided.");
} else {
this.nullRepresenter = new RepresentNull();
this.representers.put(String.class, new RepresentString());
this.representers.put(Boolean.class, new RepresentBoolean());
this.representers.put(Character.class, new RepresentString());
this.representers.put(UUID.class, new RepresentUuid());
this.representers.put(byte[].class, new RepresentByteArray());
Represent primitiveArray = new RepresentPrimitiveArray();
this.representers.put(short[].class, primitiveArray);
this.representers.put(int[].class, primitiveArray);
this.representers.put(long[].class, primitiveArray);
this.representers.put(float[].class, primitiveArray);
this.representers.put(double[].class, primitiveArray);
this.representers.put(char[].class, primitiveArray);
this.representers.put(boolean[].class, primitiveArray);
this.multiRepresenters.put(Number.class, new RepresentNumber());
this.multiRepresenters.put(List.class, new RepresentList());
this.multiRepresenters.put(Map.class, new RepresentMap());
this.multiRepresenters.put(Set.class, new RepresentSet());
this.multiRepresenters.put(Iterator.class, new RepresentIterator());
this.multiRepresenters.put((new Object[0]).getClass(), new RepresentArray());
this.multiRepresenters.put(Date.class, new RepresentDate());
this.multiRepresenters.put(Enum.class, new RepresentEnum());
this.multiRepresenters.put(Calendar.class, new RepresentDate());
this.classTags = new HashMap();
this.nonPrintableStyle = options.getNonPrintableStyle();
this.setDefaultScalarStyle(options.getDefaultScalarStyle());
this.setDefaultFlowStyle(options.getDefaultFlowStyle());
}
}
protected Tag getTag(Class<?> clazz, Tag defaultTag) {
return this.classTags.containsKey(clazz) ? (Tag)this.classTags.get(clazz) : defaultTag;
}
public Tag addClassTag(Class<? extends Object> clazz, Tag tag) {
if (tag == null) {
throw new NullPointerException("Tag must be provided.");
} else {
return (Tag)this.classTags.put(clazz, tag);
}
}
public TimeZone getTimeZone() {
return this.timeZone;
}
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
protected class RepresentUuid implements Represent {
protected RepresentUuid() {
}
public Node representData(Object data) {
return SafeRepresenter.this.representScalar(SafeRepresenter.this.getTag(data.getClass(), new Tag(UUID.class)), data.toString());
}
}
protected class RepresentByteArray implements Represent {
protected RepresentByteArray() {
}
public Node representData(Object data) {
char[] binary = Base64Coder.encode((byte[])data);
return SafeRepresenter.this.representScalar(Tag.BINARY, String.valueOf(binary), ScalarStyle.LITERAL);
}
}
protected class RepresentEnum implements Represent {
protected RepresentEnum() {
}
public Node representData(Object data) {
Tag tag = new Tag(data.getClass());
return SafeRepresenter.this.representScalar(SafeRepresenter.this.getTag(data.getClass(), tag), ((Enum)data).name());
}
}
protected class RepresentDate implements Represent {
protected RepresentDate() {
}
public Node representData(Object data) {
Calendar calendar;
if (data instanceof Calendar) {
calendar = (Calendar)data;
} else {
calendar = Calendar.getInstance(SafeRepresenter.this.getTimeZone() == null ? TimeZone.getTimeZone("UTC") : SafeRepresenter.this.timeZone);
calendar.setTime((Date)data);
}
int years = calendar.get(1);
int months = calendar.get(2) + 1;
int days = calendar.get(5);
int hour24 = calendar.get(11);
int minutes = calendar.get(12);
int seconds = calendar.get(13);
int millis = calendar.get(14);
StringBuilder buffer = new StringBuilder(String.valueOf(years));
while(buffer.length() < 4) {
buffer.insert(0, "0");
}
buffer.append("-");
if (months < 10) {
buffer.append("0");
}
buffer.append(months);
buffer.append("-");
if (days < 10) {
buffer.append("0");
}
buffer.append(days);
buffer.append("T");
if (hour24 < 10) {
buffer.append("0");
}
buffer.append(hour24);
buffer.append(":");
if (minutes < 10) {
buffer.append("0");
}
buffer.append(minutes);
buffer.append(":");
if (seconds < 10) {
buffer.append("0");
}
buffer.append(seconds);
if (millis > 0) {
if (millis < 10) {
buffer.append(".00");
} else if (millis < 100) {
buffer.append(".0");
} else {
buffer.append(".");
}
buffer.append(millis);
}
int gmtOffset = calendar.getTimeZone().getOffset(calendar.getTime().getTime());
if (gmtOffset == 0) {
buffer.append('Z');
} else {
if (gmtOffset < 0) {
buffer.append('-');
gmtOffset *= -1;
} else {
buffer.append('+');
}
int minutesOffset = gmtOffset / '\uea60';
int hoursOffset = minutesOffset / 60;
int partOfHour = minutesOffset % 60;
if (hoursOffset < 10) {
buffer.append('0');
}
buffer.append(hoursOffset);
buffer.append(':');
if (partOfHour < 10) {
buffer.append('0');
}
buffer.append(partOfHour);
}
return SafeRepresenter.this.representScalar(SafeRepresenter.this.getTag(data.getClass(), Tag.TIMESTAMP), buffer.toString(), ScalarStyle.PLAIN);
}
}
protected class RepresentSet implements Represent {
protected RepresentSet() {
}
public Node representData(Object data) {
Map<Object, Object> value = new LinkedHashMap();
Set<Object> set = (Set)data;
Iterator var4 = set.iterator();
while(var4.hasNext()) {
Object key = var4.next();
value.put(key, (Object)null);
}
return SafeRepresenter.this.representMapping(SafeRepresenter.this.getTag(data.getClass(), Tag.SET), value, FlowStyle.AUTO);
}
}
protected class RepresentMap implements Represent {
protected RepresentMap() {
}
public Node representData(Object data) {
return SafeRepresenter.this.representMapping(SafeRepresenter.this.getTag(data.getClass(), Tag.MAP), (Map)data, FlowStyle.AUTO);
}
}
protected class RepresentPrimitiveArray implements Represent {
protected RepresentPrimitiveArray() {
}
public Node representData(Object data) {
Class<?> type = data.getClass().getComponentType();
if (Byte.TYPE == type) {
return SafeRepresenter.this.representSequence(Tag.SEQ, this.asByteList(data), FlowStyle.AUTO);
} else if (Short.TYPE == type) {
return SafeRepresenter.this.representSequence(Tag.SEQ, this.asShortList(data), FlowStyle.AUTO);
} else if (Integer.TYPE == type) {
return SafeRepresenter.this.representSequence(Tag.SEQ, this.asIntList(data), FlowStyle.AUTO);
} else if (Long.TYPE == type) {
return SafeRepresenter.this.representSequence(Tag.SEQ, this.asLongList(data), FlowStyle.AUTO);
} else if (Float.TYPE == type) {
return SafeRepresenter.this.representSequence(Tag.SEQ, this.asFloatList(data), FlowStyle.AUTO);
} else if (Double.TYPE == type) {
return SafeRepresenter.this.representSequence(Tag.SEQ, this.asDoubleList(data), FlowStyle.AUTO);
} else if (Character.TYPE == type) {
return SafeRepresenter.this.representSequence(Tag.SEQ, this.asCharList(data), FlowStyle.AUTO);
} else if (Boolean.TYPE == type) {
return SafeRepresenter.this.representSequence(Tag.SEQ, this.asBooleanList(data), FlowStyle.AUTO);
} else {
throw new YAMLException("Unexpected primitive '" + type.getCanonicalName() + "'");
}
}
private List<Byte> asByteList(Object in) {
byte[] array = (byte[])in;
List<Byte> list = new ArrayList(array.length);
for(int i = 0; i < array.length; ++i) {
list.add(array[i]);
}
return list;
}
private List<Short> asShortList(Object in) {
short[] array = (short[])in;
List<Short> list = new ArrayList(array.length);
for(int i = 0; i < array.length; ++i) {
list.add(array[i]);
}
return list;
}
private List<Integer> asIntList(Object in) {
int[] array = (int[])in;
List<Integer> list = new ArrayList(array.length);
for(int i = 0; i < array.length; ++i) {
list.add(array[i]);
}
return list;
}
private List<Long> asLongList(Object in) {
long[] array = (long[])in;
List<Long> list = new ArrayList(array.length);
for(int i = 0; i < array.length; ++i) {
list.add(array[i]);
}
return list;
}
private List<Float> asFloatList(Object in) {
float[] array = (float[])in;
List<Float> list = new ArrayList(array.length);
for(int i = 0; i < array.length; ++i) {
list.add(array[i]);
}
return list;
}
private List<Double> asDoubleList(Object in) {
double[] array = (double[])in;
List<Double> list = new ArrayList(array.length);
for(int i = 0; i < array.length; ++i) {
list.add(array[i]);
}
return list;
}
private List<Character> asCharList(Object in) {
char[] array = (char[])in;
List<Character> list = new ArrayList(array.length);
for(int i = 0; i < array.length; ++i) {
list.add(array[i]);
}
return list;
}
private List<Boolean> asBooleanList(Object in) {
boolean[] array = (boolean[])in;
List<Boolean> list = new ArrayList(array.length);
for(int i = 0; i < array.length; ++i) {
list.add(array[i]);
}
return list;
}
}
protected class RepresentArray implements Represent {
protected RepresentArray() {
}
public Node representData(Object data) {
Object[] array = (Object[])data;
List<Object> list = Arrays.asList(array);
return SafeRepresenter.this.representSequence(Tag.SEQ, list, FlowStyle.AUTO);
}
}
private static class IteratorWrapper implements Iterable<Object> {
private final Iterator<Object> iter;
public IteratorWrapper(Iterator<Object> iter) {
this.iter = iter;
}
public Iterator<Object> iterator() {
return this.iter;
}
}
protected class RepresentIterator implements Represent {
protected RepresentIterator() {
}
public Node representData(Object data) {
Iterator<Object> iter = (Iterator)data;
return SafeRepresenter.this.representSequence(SafeRepresenter.this.getTag(data.getClass(), Tag.SEQ), new IteratorWrapper(iter), FlowStyle.AUTO);
}
}
protected class RepresentList implements Represent {
protected RepresentList() {
}
public Node representData(Object data) {
return SafeRepresenter.this.representSequence(SafeRepresenter.this.getTag(data.getClass(), Tag.SEQ), (List)data, FlowStyle.AUTO);
}
}
protected class RepresentNumber implements Represent {
protected RepresentNumber() {
}
public Node representData(Object data) {
Tag tag;
String value;
if (!(data instanceof Byte) && !(data instanceof Short) && !(data instanceof Integer) && !(data instanceof Long) && !(data instanceof BigInteger)) {
Number number = (Number)data;
tag = Tag.FLOAT;
if (number.equals(Double.NaN)) {
value = ".NaN";
} else if (number.equals(Double.POSITIVE_INFINITY)) {
value = ".inf";
} else if (number.equals(Double.NEGATIVE_INFINITY)) {
value = "-.inf";
} else {
value = number.toString();
}
} else {
tag = Tag.INT;
value = data.toString();
}
return SafeRepresenter.this.representScalar(SafeRepresenter.this.getTag(data.getClass(), tag), value);
}
}
protected class RepresentBoolean implements Represent {
protected RepresentBoolean() {
}
public Node representData(Object data) {
String value;
if (Boolean.TRUE.equals(data)) {
value = "true";
} else {
value = "false";
}
return SafeRepresenter.this.representScalar(Tag.BOOL, value);
}
}
protected class RepresentString implements Represent {
protected RepresentString() {
}
public Node representData(Object data) {
Tag tag = Tag.STR;
DumperOptions.ScalarStyle style = SafeRepresenter.this.defaultScalarStyle;
String value = data.toString();
if (SafeRepresenter.this.nonPrintableStyle == NonPrintableStyle.BINARY && !StreamReader.isPrintable(value)) {
tag = Tag.BINARY;
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
String checkValue = new String(bytes, StandardCharsets.UTF_8);
if (!checkValue.equals(value)) {
throw new YAMLException("invalid string value has occurred");
}
char[] binary = Base64Coder.encode(bytes);
value = String.valueOf(binary);
style = ScalarStyle.LITERAL;
}
if (SafeRepresenter.this.defaultScalarStyle == ScalarStyle.PLAIN && SafeRepresenter.MULTILINE_PATTERN.matcher(value).find()) {
style = ScalarStyle.LITERAL;
}
return SafeRepresenter.this.representScalar(tag, value, style);
}
}
protected class RepresentNull implements Represent {
protected RepresentNull() {
}
public Node representData(Object data) {
return SafeRepresenter.this.representScalar(Tag.NULL, "null");
}
}
}