0基础学习Mybatis系列数据库操作框架——自定义类型处理器

news2025/5/17 0:17:35

大纲

  • Java模型类
  • 定义类型处理器
  • 配置文件
    • 和类型绑定
    • 和字段绑定
    • resultMap中绑定
  • Mapper代码
  • 测试
  • 类型对应关系表
  • 总结
  • 参考资料

我们有时候会在数据库中放入一个扩展字段,用于保存在表设计时尚未考虑到的、未来会加入的一些信息。这个字段我们一般使用字符串存储,格式是个Json。这样后续就可以很方便进行序列化和反序列化。
本文主要讲解如何自定义类型处理器,让Mybatis自动帮我们做序列化和反序列化。Json序列化工具我们采用fastjson库。
为了使用这个库,我们在Maven的pom.xml中加入如下片段

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.48</version>
        </dependency>

然后让Maven把依赖包下载到相应为止。

Java模型类

我们将Json字符串存储在《0基础学习Mybatis系列数据库操作框架——最小Demo》中创建的表的info_ltext字段。

info_ltext longtext comment ‘longtext’,

我们设计一个Java对象映射表结构,其中jsonElemList字段对应于表中info_ltext列,只是它不是String类型,而是我们自定义的JsonList 类型。

public class JsonType {
……
    private int intInfo;
    private JsonList jsonElemList;
}

我们将JsonList定义在JsonType内部,因为它只有在JsonType内中才有意义。JsonList中有一个成员变量jsonElemList 用于保存JsonElem数组。

    public static class JsonList {
    ……
        @JSONField()
        private List<JsonElem> jsonElemList = new ArrayList<>();
   }

JsonElem也定义在JsonType内部。它有两个成员变量,用于丰富Json结构。它实现了Cloneable接口,以方便后续对这个结构的深拷贝。

    public static class JsonElem implements Cloneable {
        @JSONField(name = "First")
        private int first;
        @JSONField(name = "Second")
        private String second;

        @Override
        public JsonElem clone() throws CloneNotSupportedException {
            JsonElem clonedJsonElem = null;
            try {
                clonedJsonElem = (JsonElem) super.clone();
                clonedJsonElem.setFirst(this.getFirst());
                clonedJsonElem.setSecond(this.getSecond());
            } finally {
            }

            return clonedJsonElem;
        }
    }

完整代码如下

package org.example.model;

import com.alibaba.fastjson.annotation.JSONField;

import java.util.ArrayList;
import java.util.List;

public class JsonType {

    public static class JsonList {
        public List<JsonElem> getJsonElemList() {
            return jsonElemList;
        }

        public void setJsonElemList(List<JsonElem> jsonElemList) {
            this.jsonElemList = jsonElemList;
        }
        @JSONField()
        private List<JsonElem> jsonElemList = new ArrayList<>();

        public JsonList(List<JsonElem> jsonElemList) {
            this.jsonElemList.addAll(jsonElemList);
        }
    }

    public static class JsonElem implements Cloneable {
        public int getFirst() {
            return first;
        }

        public void setFirst(int first) {
            this.first = first;
        }

        public String getSecond() {
            return second;
        }

        public void setSecond(String second) {
            this.second = second;
        }

        @JSONField(name = "First")
        private int first;
        @JSONField(name = "Second")
        private String second;

        public JsonElem(int first, String second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public JsonElem clone() throws CloneNotSupportedException {
            JsonElem clonedJsonElem = null;
            try {
                clonedJsonElem = (JsonElem) super.clone();
                clonedJsonElem.setFirst(this.getFirst());
                clonedJsonElem.setSecond(this.getSecond());
            } finally {
            }

            return clonedJsonElem;
        }
    }

    public int getIntInfo() {
        return intInfo;
    }

    public void setIntInfo(int intInfo) {
        this.intInfo = intInfo;
    }

    public JsonList getJsonElemList() {
        return jsonElemList;
    }

    public void setJsonElemList(JsonList jsonElemList) {
        this.jsonElemList = jsonElemList;
    }

    private int intInfo;

    private JsonList jsonElemList;
}

定义类型处理器

我们的类型处理器继承于org.apache.ibatis.type.BaseTypeHandler,只要覆盖其几个NonNull方法即可

package org.example.typehandlers;

import com.alibaba.fastjson.JSON;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.example.model.JsonType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@MappedJdbcTypes(JdbcType.LONGVARCHAR)
public class JsonListHandler extends BaseTypeHandler<JsonType.JsonList> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, JsonType.JsonList parameter, JdbcType jdbcType) throws SQLException {
        String jsonStr = JSON.toJSONString(parameter);
        ps.setString(i, jsonStr);
    }

    @Override
    public JsonType.JsonList getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String jsonStr = rs.getString(columnName);
        return JSON.parseObject(jsonStr, JsonType.JsonList.class);
    }


    @Override
    public JsonType.JsonList getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String jsonStr = rs.getString(columnIndex);
        return JSON.parseObject(jsonStr, JsonType.JsonList.class);
    }

    @Override
    public JsonType.JsonList getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String jsonStr = cs.getString(columnIndex);
        return JSON.parseObject(jsonStr, JsonType.JsonList.class);
    }
}

这儿有个概念,就是Java类型、JDBC类型和数据库字段类型的对应关系。我们选用的longtext是Mysql数据库字段类型,其对应的Java类型是java.lang.string,对应的jdbc类型是LONGVARCHAR。所以上述代码我们加了@MappedJdbcTypes(JdbcType.LONGVARCHAR)注解。
关于这些对应关系,可以见文后内容。
setNonNullParameter方法是将JsonType.JsonList对象序列化成String,这样就和数据库中longtext类型对应上了。

info_ltext longtext comment ‘longtext’,

getNullableResult方法则是将String内容反序列化成为JsonType.JsonList对象,这样就和Java模型类对应上了。

public class JsonType {
……
    private int intInfo;
    private JsonList jsonElemList;
}

配置文件

配置文件主要完成自定义类型处理器JsonListHandler绑定的问题。主要有两种方法

和类型绑定

和类型绑定,即让自定义类型处理器和JDBC或者Java类型绑定。
本例我们采用和JDBC类型绑定。下面代码放置于mybatis-config-json.xml中。

    <typeHandlers>
        <typeHandler handler="org.example.typehandlers.JsonListHandler" jdbcType="LONGVARCHAR"/>
    </typeHandlers>

然后在SQL Mapper XML中,指定jsonElemList字段类型是上述绑定的类型。这样Mybatis在处理info_ltext字段时,就会使用自定义类型处理器JsonListHandler来处理。

    <update id="updateJsonTypeElems">
        update all_type set info_ltext  = #{jsonElemList, jdbcType=LONGVARCHAR} where info_int = #{intInfo}
    </update>

和字段绑定

和字段绑定就不用在mybatis-config-json.xml中做任何配置,只要在SQL Mapper XML中配置即可。
比如下例,定义item.jsonElemList类型处理器即可。

    <insert id="insertJsonTypeElems">
        insert into all_type(info_int, info_ltext) values
        <foreach item="item" collection="list" separator=",">
            (#{item.intInfo}, #{item.jsonElemList, typeHandler=org.example.typehandlers.JsonListHandler})
        </foreach>
    </insert>

resultMap中绑定

对于Select语句,我们可以使用《0基础学习Mybatis系列数据库操作框架——字段映射》中的方法,在映射表中定义。也不用在mybatis-config-json.xml中做任何配置。

    <resultMap id="jsonTypeResultMap" type="JsonType">
        <result property="intInfo" column="info_int"/>
        <result property="jsonElemList" column="info_char" jdbcType="LONGVARCHAR" typeHandler="org.example.typehandlers.JsonListHandler"/>
    </resultMap>

    <select id="selectJsonTypeElems" resultMap="jsonTypeResultMap">
        select * from all_type
    </select>

完整配置代码如下

<?xml version="1.0" encoding="UTF-8"?>
<!-- AllTypeMapper-1.xml -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.AllTypeMapper">
    <parameterMap id="jsonTypeParameterMap" type="JsonType">
        <parameter property="jsonElemList" jdbcType="LONGVARCHAR" typeHandler="org.example.typehandlers.JsonListHandler"/>
    </parameterMap>

    <resultMap id="jsonTypeResultMap" type="JsonType">
        <result property="intInfo" column="info_int"/>
        <result property="jsonElemList" column="info_char" jdbcType="LONGVARCHAR" typeHandler="org.example.typehandlers.JsonListHandler"/>
    </resultMap>

    <insert id="insertJsonTypeElems">
        insert into all_type(info_int, info_ltext) values
        <foreach item="item" collection="list" separator=",">
            (#{item.intInfo}, #{item.jsonElemList, typeHandler=org.example.typehandlers.JsonListHandler})
        </foreach>
    </insert>

    <update id="updateJsonTypeElems">
        update all_type set info_ltext  = #{jsonElemList, jdbcType=LONGVARCHAR} where info_int = #{intInfo}
    </update>

    <select id="selectJsonTypeElems" resultMap="jsonTypeResultMap">
        select * from all_type
    </select>
</mapper>

Mapper代码

在《0基础学习Mybatis系列数据库操作框架——增删改操作》引入的AllTypeMapper中新增如下三行方法以对应SQL Mapper XML中的三个方法id。

    long insertJsonTypeElems(List<JsonType> jsonTypeList);
    long updateJsonTypeElems(JsonType jsonType);
    List<JsonType> selectJsonTypeElems(int intInfo);

测试

package org.example;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.example.mapper.AllTypeMapper;
import org.example.model.AllType;
import org.example.model.JsonType;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class JsonTest {
    private static SqlSessionFactory sqlSF;

    @BeforeAll
    static void CreateSessionFactory() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis/config/mybatis-config-json.xml");
        sqlSF = new SqlSessionFactoryBuilder().build(in);
    }

    @Test
    void testUpdate() {
        try (SqlSession s = sqlSF.openSession(true)) {
            AllTypeMapper all_type_mapper = s.getMapper(AllTypeMapper.class);
            JsonType a = new JsonType();
            a.setIntInfo(1);

            List<JsonType.JsonElem> jsonElemList = Arrays.asList(
                    new JsonType.JsonElem(1,"1"),
                    new JsonType.JsonElem(2,"2")
            );
            JsonType.JsonList jsonList = new JsonType.JsonList(jsonElemList);

            a.setJsonElemList(jsonList);
            long count = all_type_mapper.updateJsonTypeElems(a);
            System.out.println(count);
        }
    }


    @Test
    void testSelect() {
        try (SqlSession s = sqlSF.openSession(true)) {
            AllTypeMapper all_type_mapper = s.getMapper(AllTypeMapper.class);
            List<JsonType> all = all_type_mapper.selectJsonTypeElems(1);
            for (JsonType a : Objects.requireNonNull(all)) {
                JsonType.JsonList jsonList = a.getJsonElemList();
                if (null == jsonList) {
                    continue;
                }
                for (JsonType.JsonElem b: jsonList.getJsonElemList()) {
                    System.out.printf("%d %s\n", b.getFirst(), b.getSecond());
                }
            }
        }
    }


    @Test
    void testinsertJsonTypeElems() {
        List<JsonType> jsonTypeList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            JsonType a = new JsonType();
            a.setIntInfo(i+100);

            List<JsonType.JsonElem> jsonElemList = Arrays.asList(
                    new JsonType.JsonElem(i+1100, "1"),
                    new JsonType.JsonElem(i+1200, "2")
            );
            JsonType.JsonList jsonList = new JsonType.JsonList(jsonElemList);
            a.setJsonElemList(jsonList);
            jsonTypeList.add(a);
        }


        try (SqlSession s = sqlSF.openSession(true)) {
            AllTypeMapper all_type_mapper = s.getMapper(AllTypeMapper.class);
            long count = all_type_mapper.insertJsonTypeElems(jsonTypeList);
            System.out.println(count);
        }
    }
}

在这里插入图片描述

类型对应关系表

类型处理器Java 类型JDBC 类型
BooleanTypeHandlerjava.lang.Boolean, boolean数据库兼容的 BOOLEAN
ByteTypeHandlerjava.lang.Byte, byte数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandlerjava.lang.Short, short数据库兼容的 NUMERIC 或 SMALLINT
IntegerTypeHandlerjava.lang.Integer, int数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandlerjava.lang.Long, long数据库兼容的 NUMERIC 或 BIGINT
FloatTypeHandlerjava.lang.Float, float数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandlerjava.lang.Double, double数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandlerjava.math.BigDecimal数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandlerjava.lang.StringCHAR, VARCHAR
ClobReaderTypeHandlerjava.io.Reader-
ClobTypeHandlerjava.lang.StringCLOB, LONGVARCHAR
NStringTypeHandlerjava.lang.StringNVARCHAR, NCHAR
NClobTypeHandlerjava.lang.StringNCLOB
BlobInputStreamTypeHandlerjava.io.InputStream-
ByteArrayTypeHandlerbyte[]数据库兼容的字节流类型
BlobTypeHandlerbyte[]BLOB, LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
DateOnlyTypeHandlerjava.util.DateDATE
TimeOnlyTypeHandlerjava.util.DateTIME
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandlerjava.sql.DateDATE
SqlTimeTypeHandlerjava.sql.TimeTIME
ObjectTypeHandlerAnyOTHER 或未指定类型
EnumTypeHandlerEnumeration TypeVARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值)
EnumOrdinalTypeHandlerEnumeration Type任何兼容的 NUMERIC 或 DOUBLE 类型,用来存储枚举的序数值(而不是名称)。
SqlxmlTypeHandlerjava.lang.StringSQLXML
InstantTypeHandlerjava.time.InstantTIMESTAMP
LocalDateTimeTypeHandlerjava.time.LocalDateTimeTIMESTAMP
LocalDateTypeHandlerjava.time.LocalDateDATE
LocalTimeTypeHandlerjava.time.LocalTimeTIME
OffsetDateTimeTypeHandlerjava.time.OffsetDateTimeTIMESTAMP
OffsetTimeTypeHandlerjava.time.OffsetTimeTIME
ZonedDateTimeTypeHandlerjava.time.ZonedDateTimeTIMESTAMP
YearTypeHandlerjava.time.YearINTEGER
MonthTypeHandlerjava.time.MonthINTEGER
YearMonthTypeHandlerjava.time.YearMonthVARCHAR 或 LONGVARCHAR
JapaneseDateTypeHandlerjava.time.chrono.JapaneseDateDATE

总结

  • 自定义类型处理器类比较好写。只要定义好序列化和反序列化即可。
  • 主要容易混乱的点是在配置文件。
    • JDBC、Jave类型和自定义处理器绑定,需要在mybatis-config.xml中定义它们绑定关系;同时需要在SQL Mapper XML中的需要处理的字段上,用jdbcType或者javaType强调类型,才能让自定义类型处理器在这个字段上生效。
    • 直接在SQL Mapper XML中的需要处理的字段上指定类型处理器。这样就不用在mybatis-config.xml中做任何配置。
    • Select类型的SQL,除了直接在字段上标明类型处理器,还可以在resultMap上指定。

参考资料

  • https://mybatis.org/mybatis-3/zh_CN/configuration.html#typeHandlers

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1571693.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数学矩阵GCD和lCM(详解)

矩阵乘法 知阵乘法是《线性代数》中的基础内容&#xff0c;但在考察数学的算法题中也会出现。 本节我们学习基础的矩阵乘法规则。 每个矩阵会有一个行数和一个列数&#xff0c;只有当相乘的两个矩阵的左矩阵的列数等于右矩阵的行数 时&#xff0c;才能相乘&#xff0c;否则不允…

JavaSE-10笔记【多线程1(+2024新)】

文章目录 1.进程与线程2.并发与并行3.线程的调度模型4.实现线程4.1 第一种方式&#xff1a;继承Thread4.2 第二种方式&#xff1a;实现Runnable接口4.3 t.start()和t.run()的本质区别&#xff1f;4.4 线程常用的三个方法 5.线程的生命周期&#xff08;把生命周期图背会&#xf…

考研回忆录【二本->211】

备考时长差不多快一年半&#xff0c;从22年的11月底开始陆陆续续地准备考研&#xff0c;因为开始的早所以整个备考过程显得压力不是很大&#xff0c;中途还去一些地方旅游&#xff0c;我不喜欢把自己绷得太紧。虽然考的不是很好&#xff0c;考完我甚至都没准备复试&#xff0c;…

《QT实用小工具·十三》FlatUI辅助类之各种炫酷的控件集合

1、概述 源码放在文章末尾 FlatUI辅助类之各种炫酷的控件集合 按钮样式设置。文本框样式设置。进度条样式。滑块条样式。单选框样式。滚动条样式。可自由设置对象的高度宽度大小等。自带默认参数值。 下面是demo演示&#xff1a; 项目部分代码如下所示&#xff1a; #ifnd…

《QT实用小工具·十四》面板容器控件和图形字体示例

1、概述 源码放在文章末尾 面板容器控件包含如下功能&#xff1a; 支持所有widget子类对象&#xff0c;自动产生滚动条。 支持自动拉伸自动填充。 提供接口获取容器内的所有对象的指针。 可设置是否自动拉伸宽度高度。 可设置设备面板之间的间距和边距。 超级图形字体类…

论文笔记:Large Language Models as Analogical Reasoners

iclr 2024 reviewer打分5558 1 intro 基于CoT prompt的大模型能够更好地解决复杂推理问题 然而传统CoT需要提供相关的例子作为指导&#xff0c;这就增加了人工标注的成本——>Zero-shot CoT避免了人工标注来引导推理 但是对于一些复杂的任务难以完成推理&#xff0c;例如c…

2024最全AI绘画Midjourney绘画提示词Prompt大全,AI换脸、垫图混图【宝藏级收藏】

一、AI绘画工具 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧。已支…

Win10 下 git error unable to create file Invalid argument 踩坑实录

原始解决方案参看&#xff1a;https://stackoverflow.com/questions/26097568/git-pull-error-unable-to-create-file-invalid-argument 本问题解决于 2024-02-18&#xff0c;使用 git 版本 2.28.0.windows.1 解决方案 看 Git 抛出的出错的具体信息&#xff0c;比如如下都来自…

7(8)-2-CSS 盒子模型

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 CSS 盒子模型1 盒子模型&#xff08;Box Model&#xff09;组成2 边框&#x…

Git 如何去使用

目录 1. Git暂存区的使用 1.1. 暂存区的作用 1.2. 暂存区覆盖工作区&#xff08;注意&#xff1a;完全确认覆盖时使用&#xff09; 1.3. 暂存区移除文件 1.4. 练习 2. Git回退版本 2.1. 概念 2.2. 查看提交历史 2.3. 回退命令 2.4. 注意 3. Git删除文件 3.1. 需求 …

docker安装Nexus,maven私服

文章目录 前言安装创建文件夹设置文件夹权限docker创建指令制作docker-compose.yaml文件 查看网站访问网页查看密码 前言 nexus作为私服的maven仓库&#xff0c;在企业级应用中&#xff0c;提供了依赖来源的稳定性&#xff0c;为构建庞大的微服务体系&#xff0c;打下基础 安…

Day83:服务攻防-开发组件安全JacksonFastJson各版本XStreamCVE环境复现

目录 J2EE-组件Jackson-本地demo&CVE 代码执行 (CVE-2020-8840) 代码执行 (CVE-2020-35728&#xff09; J2EE-组件FastJson-本地demo&CVE FastJson < 1.2.24 FastJson < 1.2.47 FastJson < 1.2.80 (利用条件比较苛刻) J2EE-组件XStream-靶场&CVE …

Java_自定义实体类的列表List<T>调用remove()失败讲解

示例1 前提&#xff1a; 新建一个主类Demo1。 需求&#xff1a; 在一个列表中有三条String的数据&#xff0c;想要使用remove(Object o)删掉其中一条。 结果&#xff1a; remove(Object o)成功把数据删掉。 示例2 前提&#xff1a; 新建一个自定义实体类DataExample和一个主…

PC发送指令给单片机控制LED(与上一篇文章相反)

此时要重新配置寄存器 &#xff0c;实现电脑往单片机传输数据 1、配置SCON寄存器的REN 即 REN 1 2、有TI&#xff08;发送中断&#xff09;就有RI&#xff08;接收中断&#xff09; 3、优化 发现发送 o 时&#xff0c;D5亮灯会有延迟 下面就是做到真正的无延迟的全双工通信 …

STC89C51学习笔记(四)

STC89C51学习笔记&#xff08;四&#xff09; 综述&#xff1a;本文讲述了在STC89C51中数码管、模块化编程、LCD1602的使用。 一、数码管 1.数码管显示原理 位选&#xff1a;对74HC138芯片的输入端的配置&#xff08;P22、P23、P24&#xff09;&#xff0c;来选择实现位选&…

Android 代码自定义drawble文件实现View圆角背景

简介 相信大多数Android开发都会遇到一个场景&#xff0c;给TextView或Button添加背景颜色&#xff0c;修改圆角&#xff0c;描边等需求。一看到这样的实现效果&#xff0c;自然就是创建drawble文件&#xff0c;设置相关属性shap&#xff0c;color&#xff0c;radius等。然后将…

第二节课《轻松玩转书生·浦语大模型趣味 Demo》

比较匆忙&#xff0c;假期前仿照第一期课程的内容好像被清空了&#xff0c;重新搭建一次。 https://github.com/InternLM/Tutorial/blob/camp2/helloworld/hello_world.md 按照那老师写好的&#xff0c;一步步复制就好了 浦语灵笔2的大概率是会超出显存&#xff0c;先不测试了…

JavaScript权威指南(第7版) 笔记 - 扩展操作符总结

扩展操作符 ... &#xff0c;不是真正意义上的JavaScript操作符。 let str "0123ABC" console.log(typeof ...str);// Uncaught SyntaxError: Unexpected token ... 上面的第2行代码会报错&#xff0c;… 操作符只能在数组字面量、对象字面量、函数调用中使用。 在…

JS详解-设计模式

工厂模式&#xff1a; 单例模式&#xff1a; // 1、定义一个类class SingleTon{// 2、添加私有静态属性static #instance// 3、添加静态方法static getInstance(){// 4、判断实例是否存在if(!this.#instance){// 5、实例不存在&#xff0c;创建实例this.#instance new Single…

Redis从入门到精通(七)Redis实战(四)库存超卖、一人一单与Redis分布式锁

↑↑↑请在文章开头处下载测试项目源代码↑↑↑ 文章目录 前言4.3 优惠券秒杀4.3.4 库存超卖问题及其解决4.3.4.1 问题分析4.3.4.2 问题解决 4.3.5 一人一单需求4.3.5.1 需求分析4.3.5.2 代码实现4.3.5.3 并发问题4.3.5.4 悲观锁解决并发问题4.3.5.5 集群环境下的并发问题 4.3.…