Springboot整合ShardingJdbc實現(xiàn)分庫分表方式
創(chuàng)建庫表
1、創(chuàng)建兩個數(shù)據(jù)庫
CREATE SCHEMA `shard_db_0` DEFAULT CHARACTER SET utf8 ; CREATE SCHEMA `shard_db_1` DEFAULT CHARACTER SET utf8 ;
2、在每個數(shù)據(jù)庫各創(chuàng)建三個分表
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;
創(chuàng)建工程
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、創(chuàng)建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、創(chuàng)建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、創(chuàng)建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、用到的工具類(雪花算法生成關聯(lián)表主鍵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,這是一個在分布式系統(tǒng)中唯一標識當前節(jié)點的ID。 * 它通過獲取本地主機名或IP并轉(zhuǎn)換為long值來實現(xiàn)。 * 如果無法獲取本地主機名或IP,或者轉(zhuǎn)換過程中發(fā)生異常,將不設置workerId,可能導致后續(xù)ID生成失敗。 */ @Override public void run(ApplicationArguments args) throws Exception { //初始化workId initWorkId(); initDataCenterId(); //初始化SnowflakeID生成器 createSnowFlake(workerId, datacenterId); } /** * 根據(jù)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("根據(jù)IP獲取workId失敗。", e); // 如果獲取失敗,則使用隨機數(shù)備用 workerId = RandomUtil.randomLong(0, 31); } } /** * 根據(jù)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系統(tǒng)為空處理 * @return */ public static String getHostName() { //獲取當前操作系統(tǒng)名稱,例如: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創(chuàng)建SnowflakeID生成器實例。如果創(chuàng)建失敗,將拋出異常。 * * @param workerId 工作節(jié)點ID,用于標識當前節(jié)點。 * @param datacenterId 數(shù)據(jù)中心ID,用于標識數(shù)據(jù)中心。 * @throws IllegalArgumentException 如果Snowflake實例創(chuàng)建失敗,則拋出此異常。 * @throws RuntimeException 如果Snowflake實例創(chuàng)建過程中發(fā)生其他異常,則拋出此異常。 */ private Snowflake createSnowFlake(long workerId, long datacenterId) { try { this.snowFlake = IdUtil.createSnowflake(workerId, datacenterId); // 參數(shù)合法性檢查 if (null == snowFlake) { throw new IllegalArgumentException("Failed to create Snowflake instance. Check workerId and datacenterId."); } return snowFlake; } catch (Exception e) { log.error("創(chuàng)建Snowflake實例失敗,異常:{}", e.getMessage()); throw new RuntimeException("Initialization failed for Snowflake ID generator.", e); } } /** * 獲取一個唯一的雪花ID。使用Snowflake算法生成ID,該算法由Twitter開源。 * 具體來說,這個方法調(diào)用了Snowflake實例的nextId方法來獲取一個唯一的長整型ID。 * 使用synchronized關鍵字確保了這個方法在多線程環(huán)境下的線程安全, * 保證了ID的生成不會因為并發(fā)而產(chǎn)生重復或錯亂。 * * @return 生成的唯一長整型ID。 */ public synchronized long snowflakeId() { // 調(diào)用Snowflake實例的nextId方法獲取唯一ID return this.snowFlake.nextId(); } /** * 生成基于Snowflake算法的唯一ID。 * <p> * 使用Snowflake算法生成唯一的分布式ID。該算法由Twitter提出,通過組合時間戳、工作機器ID和序列號來生成全局唯一的ID。 * 具體結(jié)構(gòu)如下: * - 1位符號位,用于區(qū)分正負,由于ID只能是正數(shù),所以這個位始終為0。 * - 41位時間戳,精確到毫秒,可以使用約69年。 * - 10位工作機器ID,可以部署在1024個節(jié)點,包括5位數(shù)據(jù)中心ID和5位工作機器ID。 * - 12位序列號,用于同一毫秒內(nèi)生成的ID去重,每個節(jié)點每毫秒可以生成4096個ID。 * <p> * 參數(shù): * workerId - 工作機器ID,用于標識不同的工作機器或進程。 * datacenterId - 數(shù)據(jù)中心ID,用于標識不同的數(shù)據(jù)中心。 * <p> * 返回: * 一個長整型的唯一ID,根據(jù)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條數(shù)據(jù) */ @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("查詢結(jié)果:"+ 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("更新記錄數(shù):"+count); } @Test public void testOrderPage (){ //分頁參數(shù) Page<TbOrder> rowPage = new Page<>(1, 2); //queryWrapper組裝查詢where條件 LambdaQueryWrapper<TbOrder> queryWrapper = new LambdaQueryWrapper<>(); Page<TbOrder> page = tbOrderMapper.selectPage(rowPage, queryWrapper); log.info("分頁查詢結(jié)果:"+ 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 # 數(shù)據(jù)源配置 datasource: # 數(shù)據(jù)源名稱,多數(shù)據(jù)源以逗號分隔 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 # 分片規(guī)則配置 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 環(huán)境中使用行表達式標識符建議使用 $->{...} 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的無參構(gòu)造器,因此導致了該異常。
可通過降低boot版本號從而降低其中snakeyaml版本號解決,但由于snakeyaml 1.x版本有安全問題,而shardingsphere-jdbc-core-spring-boot-starter已是當前最高版本,因此可通過重新jar中bean新增無參構(gòu)造器去覆蓋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"); } } }
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
JavaWeb實現(xiàn)Session跨頁面?zhèn)鬟f數(shù)據(jù)
本文主要介紹了 JavaWeb實現(xiàn)Session跨頁面?zhèn)鬟f數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-07-07SpringBoot整合TKMyBatis實現(xiàn)單表增刪改查操作
據(jù)說tk.mybatis能夠讓我不寫sql代碼就可以所有單表操作問題,作為熱愛偷懶的我,怎么能放過這種機會。talk is cheap, show me the code。趕緊搞個例子爽一把先2023-01-01全鏈路監(jiān)控平臺Pinpoint?SkyWalking?Zipkin選型對比
這篇文章主要為大家介紹了全鏈路監(jiān)控平臺Pinpoint?SkyWalking?Zipkin實現(xiàn)的選型對比,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-03-03Java調(diào)用groovy實現(xiàn)原理代碼實例
這篇文章主要介紹了Java調(diào)用groovy實現(xiàn)原理代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-12-12mybatis QueryWrapper的條件構(gòu)造之a(chǎn)pply、last、select解析
這篇文章主要介紹了mybatis QueryWrapper的條件構(gòu)造之a(chǎn)pply、last、select,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03