清朝云资源网

作者: 清朝云

  • 01月02日,星期四, 每天60秒读懂全世界!

    百度热搜新闻

    新闻来源:百度热搜榜

    1. 赵露思经纪公司致歉 1月1日,银河酷娱传媒发文表示:一直坚定与赵露思女士一起应对面临的困难,对自身工作的不足之处深刻反省和诚恳致歉。

    2. 日本电视塔跨年倒数完竟直接关灯 当地时间1月1日,日本札幌大通公园。人群聚集在日本札幌电视塔下倒数跨年,结果刚过零点电视塔直接全部熄灯。

    3. 这一年,总书记的惦念与牵挂 2024年的这一幕幕,亲切温暖!一次次交谈互动中,一句句叮咛关切里,是习近平总书记对人民群众不变的牵挂。

    4. 何炅 我现在有点惊弓之鸟了

    5. 老太迎124岁生日:阎王爷把我忘了 12月31日,在四川省南充市南部县花罐镇柴氏祠村,邱柴氏老人即将迎来124岁生日。她表示:阎王爷把我忘了。

    6. 下水道捡到国家一级保护动物

    7. 蔡磊称四肢已基本瘫痪 1月1日,渐冻症抗争者蔡磊发布新年公开信称,四肢已基本瘫痪,依靠眼控技术办公和沟通,但依然相信,接力棒不会掉落。

    8. 贾乃亮带货赚疯了 黄景瑜默默数钱 12月31日,贾乃亮跨年还在直播赚钱,龚俊被销售额吓出表情包,黄景瑜在一旁默默数钱。

    9. 这个年你是怎么“跨”的? 这个跨年夜,你是怎么过的?跟随镜头一键穿越到多个城市,在跨年活动现场感受辞旧迎新时刻。

    10. 银河酷娱前CEO否认殴打赵露思 1月1日,银河酷娱前老板李炜回应称:之前点赞赵露思负面是不小心手滑,并没有殴打赵露思。

    —- 百度热搜新闻 End —-

    知乎新闻

    新闻来源:知乎日榜

    标题: 蜘蛛为什么长相恐怖给人不亲近感觉?
    链接: https://daily.zhihu.com/story/9777967
    ———————-
    标题: 为什么现在汉语无法产出新的成语了呢?类似「叶公好龙」这种,四个字就表达了非常复杂的意思,现在却没了?
    链接: https://daily.zhihu.com/story/9777953
    ———————-
    标题: 甲醇、乙醇只差一个碳,结构相似,为什么甲醇剧毒,乙醇毒性较小?
    链接: https://daily.zhihu.com/story/9777959
    ———————-

    —- 知乎新闻 End —-

    IT之家新闻

    新闻来源:ITHome之家科技新闻

    标题: 2025 年政府补贴 1 月再上新:单笔补贴 2000 元,Apple Watch Ultra2 立打 6.8 折
    发布时间: 2025-01-01T15:59:45.78
    新闻简介: 京东年货节 + 淘宝双旦大促现已正式开始,2 分钟速读 2025 年 1 月国补攻略。
    ———————-
    标题: 华为冲刺关键一年,2025 年所有新手机、平板将出厂默认搭载原生鸿蒙
    发布时间: 2025-01-01T12:55:31.84
    新闻简介: 在 2024 年 11 月的华为 Mate 品牌盛典上,华为常务董事、终端 BG 董事长、智能汽车解决方案 BU 董事长余承东也表达了类似的说法,他称 2025 年华为新推出的所有手机、所有平板将全部搭载原生鸿蒙。
    ———————-
    标题: 雷军辟谣小米只招聘 35 岁以下员工:只有招聘未来之星时才有年龄限制
    发布时间: 2025-01-01T07:32:50.76
    新闻简介: 昨晚小米创始人、董事长兼 CEO 雷军在直播中辟谣了“小米只招聘 35 岁以下员工”的相关传闻,他表示,小米只有在招聘“未来之星”的时候才有年龄限制,因为这是一个面对博士生的专项招募。
    ———————-
    标题: 雷军:中国汽车产业应少些歪门邪道,不要发黑稿、背后捅刀子
    发布时间: 2025-01-01T09:38:51.167
    新闻简介: 雷军昨晚(12 月 31 日)在小米汽车工厂举办的跨年直播活动中,表示希望中国的汽车产业能够健康有序地发展,减少背后发黑稿、互相背后捅刀子等不良现象,呼吁大家专注于把产品做好,摒弃那些歪门邪道的做法。
    ———————-
    标题: 真问了:《黑神话:悟空》制作人冯骥吐槽 Xbox 主机 10G 共享内存“没个几年优化经验拿不下来”
    发布时间: 2025-01-01T20:16:45.543
    新闻简介: 冯骥发文称“虽然也没有太大意外,全拿下后还是有点感慨,各位战斗力恐怖如斯,独缺了 XBOX 那件袈裟…… 似乎有点不该(可那 10G 的共享内存啊,没有几年优化经验真拿不下来)。”
    ———————-
    标题: 路特斯正式更名莲花跑车:5 年努力终于拿下“莲花”中文商标
    发布时间: 2025-01-01T19:46:54.53
    新闻简介: 莲花集团 CEO 冯擎峰今日发布内部公告,宣布历经超过五年的努力,在中国取得了一项历史性的法务胜利 —— 北京时间 2024 年 12 月 7 日起,LOTUS 的圆标、字标、“莲花”的中文商标回到了莲花集团手中。
    ———————-
    标题: 雷军听劝:小米 SU7 Ultra 量产版可选配开孔机盖
    发布时间: 2025-01-01T13:01:20.907
    新闻简介: 昨晚雷军在跨年直播中称小米 SU7 Ultra 可选配开孔机盖,并称他本来是反对这个功能的,但是要的人实在是太多了,就加了一个选配开孔机盖,预计明年四五月份上。
    ———————-
    标题: 2024 年国产汽车销量全面疯涨:比亚迪 427.21 万辆领跑,多家车企创历史新高(附排名)
    发布时间: 2025-01-01T18:04:48.34
    新闻简介: 2024 年是国产车企全面崛起的一年,2025 新年伊始,目前各大厂商都公布了喜人数据,IT之家为大家一文汇总。
    ———————-
    标题: 华为应用市场“鸿蒙升级”入口仅当前设备支持升级鸿蒙 HarmonyOS 5.0 可见
    发布时间: 2025-01-01T16:15:33.6
    新闻简介: “鸿蒙升级”入口仅当前设备可支持升级至 HarmonyOS 5.0 才能看到,如用户的设备暂不支持升级,则入口不会显示(通过其他界面或者链接进入该入口会提示“敬请期待鸿蒙升级适配您的机型”)。
    ———————-
    标题: 小米手机“xiaomi SU7 全新定制主题”上线,支持免费下载使用
    发布时间: 2025-01-01T17:06:16.807
    新闻简介: 小米主题官方今日发文宣布,小米手机“xiaomi SU7 全新定制主题”上线,用户打开主题壁纸(主题商店)App,搜索“小米 SU7”可免费下载使用。
    ———————-
    标题: 消息称苹果青春版 iPhone 叫 16E:自研 5G 芯片首秀、后置 4800 万单摄、OLED 屏幕
    发布时间: 2025-01-01T07:07:04.977
    新闻简介: 消息源 Majin Bu 昨日(12 月 31 日)在 X 平台发布推文,曝料称苹果会在 2025 年推出新款 iPhone,产品名称不会叫做 iPhone SE 4,而是叫做 iPhone 16E。
    ———————-
    标题: 8 折:京东官宣开启 2025 以旧换新国家补贴活动,1 月 1 日上线湖北、江苏、湖南等地
    发布时间: 2025-01-01T15:02:12.18
    新闻简介: 京东今日发文官宣上线 2025 以旧换新国家补贴活动,1 月 1 日率先在湖北、江苏、湖南等地上线,国家补贴享 8 折,单件 / 单次至高补贴 2000 元,其他省市陆续上线中。
    ———————-

    —- IT之家新闻 End —-

  • 微服务系列之 Docker 安装 ELK


    共计 2025 个字符,预计需要花费 6 分钟才能阅读完成。

    ELK 简介

    ELK 是一个开源的日志管理解决方案,由 Elasticsearch、Logstash 和 Kibana 三个组件构成。每个组件的作用如下:

    1. Elasticsearch:分布式搜索引擎,用于存储、搜索和分析日志数据。

    2. Logstash:日志收集、处理和转换工具,用于将不同来源的日志数据收集并处理成统一格式,以便于存储和分析。

    3. Kibana:数据可视化工具,用于查询、分析和展示 Elasticsearch 中存储的日志数据。

    ELK 可以帮助用户快速、高效地管理海量的日志数据,从而实现对系统性能、安全性等方面的监控和分析。它支持多种数据源,包括文件、网络流量、数据库等,还提供了丰富的插件和扩展机制,可以满足不同的业务需求。同时,它还提供了强大的搜索、聚合、可视化和警报功能,可以让用户更加方便地理解和利用日志数据。

    ELK 搭建

    1. 创建网卡

    创建一块 elk 的网卡,待会所有容器通过这块网卡进行通信。

    docker network create elk

    2. 安装 elasticsearch

    注意:这里指定容器名称需要指定为 elasticsearch,因为 kibana 容器的配置文件 kibana.yml 中默认 es 的地址为 elasticsearch。否则,需要修改 kibana 的配置文件。

    docker run -d \
    --name elasticsearch \
    --net elk  \
    -p 9200:9200 \
    -p 9300:9300 \
    -e "discovery.type=single-node" \
    -v /home/docker/es/plugins:/usr/share/elasticsearch/plugins \
    -v /home/docker/es/data:/usr/share/elasticsearch/data \
    elasticsearch:7.16.1

    如果提示报错:

    ElasticsearchException[failed to bind service]; nested: AccessDeniedException[/usr/share/elasticsearch/data/nodes];
    Likely root cause: java.nio.file.AccessDeniedException: /usr/share/elasticsearch/data/nodes

    这是因为 es 没有权限,执行如下代码即可。

    chmod 777 /home/docker/es/data && docker restart elasticsearch

    安装完成后访问 9200 端口。

    微服务系列之 Docker 安装 ELK

    />

    3. 安装 kibana

    docker run -d \
    --name kibana \
    --net elk \
    -p 5601:5601 \
    kibana:7.14.2

    安装后访问 5601 端口用接口查询 es 数据。

    微服务系列之 Docker 安装 ELK

    />

    4. 安装 logstash

    这里因为用作测试,把宿主机 /var/log/messages 文件映射到容器内部供 logstash 容器进行读取并输出到 elasticsearch。

    # 创建 logstash 全局配置文件
    mkdir -p /home/docker/logstash/conf.d && 
    cat > /home/docker/logstash/logstash.yml <<EOF
    path.config: /usr/share/logstash/conf.d/*.conf
    path.logs: /var/log/logstash
    EOF
    # 创建 log 配置文件
    cat > /home/docker/logstash/conf.d/log.conf <<EOF
    input {
      file {
        #标签
        type => "systemlog-localhost"
        #采集点
        path => "/var/log/messages"
        #开始收集点
        start_position => "beginning"
        #扫描间隔时间,默认是1s,建议5s
        stat_interval => "5"
      }
    }
    output {
      elasticsearch { hosts => ["elasticsearch:9200"] }
      stdout { codec => rubydebug }
    }
    EOF
    
    docker run -d \
    --name logstash \
    --net elk \
    -u root \
    -p 5044:5044 \
    -v /etc/timezone:/etc/timezone:ro \
    -v /etc/localtime:/etc/localtime:ro \
    -v /home/docker/logstash/logstash.yml:/usr/share/logstash/config/logstash.yml \
    -v /home/docker/logstash/conf.d/:/usr/share/logstash/conf.d/ \
    -v /var/log/messages:/var/log/messages \
    logstash:6.8.19

    在 kibana 里面获取索引,会发现多了一条 logstash 产生的索引。

    微服务系列之 Docker 安装 ELK

    />

    查看日志。

    微服务系列之 Docker 安装 ELK

    提醒:本文发布于632天前,文中所关联的信息可能已发生改变,请知悉!

    />

    Tips:清朝云网络工作室

  • WordPresss 主题美化-给代码块添加 mac 风格的背景图片


    共计 321 个字符,预计需要花费 1 分钟才能阅读完成。

    今天突然觉得代码块光秃秃的,想给它美化一下,就像下面这样。

    Wordpresss 主题美化-给代码块添加 mac 风格的背景图片

    />

    只需要插入 css 代码即可:

    /** 代码块美化 */
    .post-content-post pre::before {
        content: "";
        display: block;
        background: url(https://www.qcyqq.com/img/uploads/2023/04/6433ac582d5b9.png) 6px 10px / 40px no-repeat #2d3748;
        height: 30px;
        border-radius: 5px;
        margin: 0px 2px -7px;
    }
    
    .post-content-post pre {
       max-height: unset;
        padding: 0 0.5em 0.5em 0.5em;
    }

    提醒:本文发布于631天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • Docker 搭建最新版本 Minio 以及设置桶为公开权限


    共计 2038 个字符,预计需要花费 6 分钟才能阅读完成。

    Minio 简介

    Minio是一个高性能、分布式的对象存储服务,可以用于存储和检索大量的非结构化数据,例如图像、视频、日志文件和备份数据等。Minio可以部署在本地或云端环境中,支持常用的API和协议,例如Amazon S3 API、OpenStack Swift API和Azure Blob Storage API等。

    Minio采用了分布式架构,可以将数据分散存储在多个节点中,从而提高了数据的可靠性和可用性。它还支持数据加密、访问控制和存储桶策略等安全功能,可以帮助用户更好地管理数据的安全性和隐私性。

    安装minio

    1. 新版本安装

    docker run -d \
    --name minio \
    -p 9000:9000 \
    -p 9200:9200 \
    -v /home/docker/minio/data:/data \
    -v /home/docker/minio/config:/root/.minio \
    -e "MINIO_ROOT_USER=root" \
    -e "MINIO_ROOT_PASSWORD=123456" \
    --restart always \
    minio/minio:RELEASE.2021-12-09T06-19-41Z \
    server /data \
    --console-address ":9200"

    新版本区分了 Console 和 API 两个服务的端口。原来都是共同使用 9000,现在需要在启动命令中映射两个端口,然后指定哪个端口做哪项服务。

    比如指定 9000 端口为 api,9200 为控制端口,则运行命令为:

    minio server --address '0.0.0.0:9000'  --console-address '0.0.0.0:8000'  /data

    并且新版本使用 MINIO_ROOT_USERMINIO_ROOT_PASSWORD 指定用户名和密码,若使用旧版本的 MINIO_ACCESS_KEYMINIO_SECRET_KEY 则会产生警告。

    Docker 搭建最新版本 Minio 以及设置桶为公开权限

    />

    2. 旧版本安装:

    docker run -d \
    --name minio \
    -p 9000:9000  \
    -v /home/docker/minio/data:/data \
    -v /home/docker/minio/config:/root/.minio \
    -e "MINIO_ACCESS_KEY=root" \
    -e "MINIO_SECRET_KEY=123456" \
    --restart=always \
    minio/minio:RELEASE.2020-12-03T00-03-10Z \
    server /data

    调整桶策略

    1. 通过Web页面调整

    对于能够访问到minio的web的情况,只需要访问 minio 地址,找到需要修改策略的桶进行编辑。

    新版本:点击 Manage,修改 Access Policy 为 public 即可。

    老版本:点击桶选择 Edit policy,选择 Add and Write,再点击 Add 即可。

    2. 通过mc客户端调整

    对于不方便使用 web 的场景,下载 mc 客户端进行调整桶策略。

    2.1 下载

    minio中国镜像下载linux mc客户端

    http://dl.minio.org.cn//mc/release/

    2.2 安装

    chmod +x /usr/bin/mc && mv mc /usr/bin
    
    # 设置mc自动补全
    mc --autocompletion

    2.3 使用

    这里只说明桶策略相关的命令。

    添加服务端:

    [root@localhost ~]# mc config host add mi http://172.26.1.21:9000 root xxxxxx
    Added `mi` successfully.

    查看服务列表:

    [root@localhost ~]# mc config host list
    gcs  
      URL       : https://storage.googleapis.com
      AccessKey : YOUR-ACCESS-KEY-HERE
      SecretKey : YOUR-SECRET-KEY-HERE
      API       : S3v2
      Path      : dns
    
    local
      URL       : http://localhost:9000
      AccessKey : 
      SecretKey : 
      API       : 
      Path      : auto
    
    mi   
      URL       : http://172.26.1.21:9000
      AccessKey : root
      SecretKey : xxxxxx
      API       : s3v4
      Path      : auto
    
    play 
      URL       : https://play.min.io
      AccessKey : Q3AM3UQ867SPQQA43P2F
      SecretKey : zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
      API       : S3v4
      Path      : auto
    
    s3   
      URL       : https://s3.amazonaws.com
      AccessKey : YOUR-ACCESS-KEY-HERE
      SecretKey : YOUR-SECRET-KEY-HERE
      API       : S3v4
      Path      : dns

    创建桶:

    mc mb mi/default

    将桶设置为公开:

    mc policy set public mi/default

    提醒:本文发布于631天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • 01月01日,星期三, 每天60秒读懂全世界!

    百度热搜新闻

    新闻来源:百度热搜榜

    1. 不愧是央视跨年 大气 12月31日晚,孙楠央视跨年激情献唱献唱《红旗飘飘》,高音振奋人心,展现浓浓的爱国情怀。

    2. 老一辈歌手业务能力真能打 31日晚,莫文蔚现身江苏台跨年晚会,演唱《每当我》。网友:老牌艺术家实力真强,我们小时候吃太好了。

    3. 祝大家所愿皆所成 新年前夕,国家主席习近平发表了二〇二五年新年贺词。习近平:祝大家所愿皆所成,多喜乐、长安宁。

    4. 电影《小小的我》总票房破3亿

    5. 刘亦菲祝大家新年快乐

    6. 张杰跨年唱《逆战》 全场燃起来了 12月31日晚,张杰举行2025跨年演唱会,演唱歌曲《逆战》掀起高潮,全场都燃起来了。

    7. 跨年晚会高清镜头下的女明星 2024年最后一天,从央视到湖南、东方、江苏、浙江、北京五大卫视,六大跨年晚会同时开启,高清镜头下的女明星们美呆了!

    8. 《当》前奏一出 谁的DNA动了 浙江卫视跨年开场歌《当》,BGM一响起,全场都沸腾了!

    9. 一起说出2025年的心愿 2024年即将过去,回望温暖瞬间,期许新的一年。元旦海采“新年新愿”,一起聊聊2024的收获,说说2025年的心愿。

    10. 苏有朋黄晓明唱还珠三部曲 致敬琼瑶 12月31日晚,苏有朋和黄晓明在跨年晚会上共同演唱还珠三部曲致敬琼瑶,网友直呼:太感动!

    —- 百度热搜新闻 End —-

    知乎新闻

    新闻来源:知乎日榜

    标题: 封神之都朝歌改名淇县,知名度下降,你还知道哪些「改名失败」的城市?其中是否有你的家乡?
    链接: https://daily.zhihu.com/story/9777628
    ———————-
    标题: 为什么加入沸石(或碎瓷片)能防止暴沸?
    链接: https://daily.zhihu.com/story/9777891
    ———————-
    标题: 有哪些生物是由复杂演化到简单,有没有多细胞生物演化成单细胞的例子?
    链接: https://daily.zhihu.com/story/9777898
    ———————-

    —- 知乎新闻 End —-

    IT之家新闻

    新闻来源:ITHome之家科技新闻

    标题: 小米宠物员工“樱桃”在银川走失,官方恳请网友帮忙寻找
    发布时间: 2024-12-31T07:06:51.697
    新闻简介: 今天凌晨小米公司官微转发微博称:小米集团最可爱的宠物员工“樱桃”在银川市不慎走失,恳请朋友们帮忙留心寻找一下。
    ———————-
    标题: 小米 YU7 SUV 实车再曝光:采用括号状转向灯
    发布时间: 2024-12-31T09:47:13.027
    新闻简介: 小米第二款车型 YU7 SUV 将于明年 6 月正式上市,近期关于该车型的曝光愈发频繁。近日,一辆编号为“029”的 YU7 实车再度被目击,网友拍摄到了关于该车更多的信息。
    ———————-
    标题: 璀璨洋红:小米 SU7 小米 15 周年限定色亮相,明日正式开售
    发布时间: 2024-12-31T09:03:08.877
    新闻简介: 小米 SU7 汽车小米 15 周年限定色今日正式亮相,命名为“璀璨洋红”,将于 1 月 1 日 0 点在小米汽车 App 开售。
    ———————-
    标题: 华为轮值董事长孟晚舟新年致辞:2024 年是原生鸿蒙关键一年,一年走过其它操作系统十多年发展之路
    发布时间: 2024-12-31T10:44:05.987
    新闻简介: 华为轮值董事长孟晚舟今日发布新年致辞,对客户、生态伙伴、产业链伙伴、员工和家属等表达了感谢。
    ———————-
    标题: 王腾晋升为小米中国区市场部总经理,兼任 REDMI 品牌总经理
    发布时间: 2024-12-31T12:44:29.163
    新闻简介: 王腾微博认证今日发生变更,晋升为双料总经理,负责 REDMI 品牌发展和管理团队。
    ———————-
    标题: 华为余承东自费订购鸿蒙智行享界 S9,称春节打算全程智驾回安徽老家
    发布时间: 2024-12-31T12:01:54.867
    新闻简介: 华为常务董事、终端 BG 董事长、智能汽车解决方案 BU 董事长余承东今日在微博发文称,“订购一台鸿蒙智行享界 S9。这个春节打算开 S9 全程智驾回安徽老家,陪年迈的父母一起过年!我选择了 S9 的 Ultra 长航版,CLTC 续航 816 公里,冬季加上高速行驶,续航里程肯定会打一些折扣,但 S9 支持 800V 高压超充,充电很快,不担心呵。”
    ———————-
    标题: 不挂四个圈,奥迪新品牌 AUDI 首车谍照曝光
    发布时间: 2024-12-31T12:12:01.52
    新闻简介: 上个月初奥迪推出了其全新豪华电动车品牌 AUDI,并发布了该品牌首款概念车 AUDI E。这一品牌由奥迪与上汽集团联合打造,但未沿用奥迪经典的四环车标,而是采用了全新设计的品牌标识。近日,有网友拍摄到了疑似 AUDI E 量产版车型。尽管测试车辆覆盖有厚重伪装,但其整体轮廓延续了概念车的设计语言,同时配备了数字外后视镜和激光雷达等。
    ———————-
    标题: 保时捷多地突然关店,车主新年礼物取消
    发布时间: 2024-12-31T09:12:52.923
    新闻简介: 对此,保时捷方面表示,新年礼品发放是根据每年公司和市场的具体情况来制定,而“凌驾风雪”活动今年确实暂不举办。
    ———————-
    标题: 微信 iOS 版 8.0.55 最新官方正式版下载发布
    发布时间: 2024-12-31T15:56:04.587
    新闻简介: 微信 iOS 版 8.0.55 最新官方正式版下载发布
    ———————-
    标题: 雷军:小米汽车在研发领域花费 130 多亿,算上工厂建设、工资等费用大概花了 300 亿元
    发布时间: 2024-12-31T21:10:48.737
    新闻简介: 雷军今日在直播中对“小米只花费 100 多亿元造车”的传闻进行解释,他称“这真的是个误解,我们在汽车研发花了 130 多亿,其实我们有工厂建设,一万多人的工资费用,我们到现在大概花了 300 个亿左右。”
    ———————-
    标题: 小米之家门店现可领取 500 万张小米新年明信片
    发布时间: 2024-12-31T12:17:51.213
    新闻简介: 小米公司今日宣布,准备了 500 万张小米新年明信片送给大家,照片全部是来自小米徕卡影像大赛的美好瞬间。现在起,就可以到小米之家门店领取。
    ———————-
    标题: 新款特斯拉 Model Y 再现身,配备贯穿式尾灯
    发布时间: 2024-12-31T08:28:23.537
    新闻简介: 近期疑似特斯拉 Model Y“Juniper”(内部代号)原型车的多次曝光,似乎印证了此前关于这款纯电动跨界车改款的诸多传闻,包括其前大灯、悬架系统以及车尾设计等关键部分。
    ———————-

    —- IT之家新闻 End —-

  • Docker 基本使用教程 – 学会 docker 看懂这篇文章就够了


    共计 6249 个字符,预计需要花费 16 分钟才能阅读完成。

    一、Docker是什么

    Docker 是一个开源的应用容器引擎,基于Go 语言并遵从 Apache2.0 协议开源。

    Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

    容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

    二、Docker 安装

    1. Centos安装与卸载

    手动卸载旧版本

    $ sudo yum remove docker \
                      docker-client \
                      docker-client-latest \
                      docker-common \
                      docker-latest \
                      docker-latest-logrotate \
                      docker-logrotate \
                      docker-engine

    使用官方安装脚本安装

    curl -fsSL https://get.docker.com | bash -s docker --mirror aliyun

    2. Windows安装

    点击 控制面板 – 程序 – 启用或关闭Windows功能,开启Hyper-V。

    进入 docker官网 下载Docker Desktop,双击进行安装

    三、Docker 镜像

    1. 获取镜像

    $ docker pull docker/getting-started

    2. 列出镜像列表

    $ docker images
    REPOSITORY               TAG       IMAGE ID       CREATED        SIZE
    ubuntu                   latest    1318b700e415   3 weeks ago    72.8MB
    mysql                    5.7.35    8cf625070931   3 weeks ago    448MB
    mysql                    8.0.26    c60d96bd2b77   3 weeks ago    514MB
    docker/getting-started   latest    083d7564d904   2 months ago   28MB
    • REPOSITORY:表示镜像的仓库源
    • TAG:镜像的标签
    • IMAGE ID:镜像ID
    • CREATED:镜像创建时间
    • SIZE:镜像大小

    3. 查找镜像

    $ docker search httpd
    NAME     DESCRIPTION                                  STARS     OFFICIAL   AUTOMATED
    httpd    The Apache HTTP Server Project               3628          [OK]
    • NAME: 镜像仓库源的名称

    • DESCRIPTION: 镜像的描述

    • OFFICIAL: 是否 docker 官方发布

    • STARS: 类似 Github 里面的 star,表示点赞、喜欢的意思。

    • AUTOMATED: 自动构建。

    4. 删除镜像

    $ docker rmi docker/getting-started

    四、Docker 容器

    1. 启动容器

    $ docker run -d -p 80:80 docker/getting-started

    参数说明:

    • -d:后台运行
    • -p:指定暴露端口

    可通过 docker run –help 命令查看参数作用

    2. 停止容器运行

    $ docker stop d6340abacfa7 # 容器ID

    3. 查看所有容器

    $ docker ps -a

    该命令将列出所有运行中的容器和已停止的容器

    4. 启动已停止运行的容器

    $ docker start d6340abacfa7 # 容器ID
    $ docker restart d6340abacfa7 # 容器ID

    5. 容器交互

    对于可以交互的容器,可以进入到容器内部,进行相关操作。

    $ docker pull ubuntu    # 拉取ubuntu镜像
    $ docker run -it ubuntu /bin/bash # 启动容器并进入容器内部

    参数说明:

    • -i: 以交互模式运⾏容器,通常与 -t 同时使⽤
    • -t:为容器重新分配⼀个伪输⼊终端,通常与 -i 同时使⽤

    6. 进入容器

    $ docker run -itd ubuntu # 启动容器并以后台方式运行

    当容器以参数 -d 进入后台, 此时想要进入容器,可使用 docker attachdocker exec 进入容器,推荐使用 docker exec

    $ docker exec -it 4d2516648ce0 /bin/bash

    7. 退出容器内部

    比如想退出ubuntu容器内部,使用exit退出。

    root@4d2516648ce0:/# exit

    8. 删除容器

    直接删除报错

    $ docker rm 4d2516648ce0
    Error response from daemon: You cannot remove a running container 4d2516648ce085a97d147bc3b8beef02e81f6e77c5ea2a012d572104963f4154. Stop the container before attempting removal or force remove

    在删除容器之前,需要先停止运行容器

    $ docker stop 4d2516648ce0
    $ docker rm 4d2516648ce0

    五、容器连接

    创建一个 python 应用的容器

    $ docker run -d -P training/webapp python app.py
    a1d62312db4e3041772cf567607ed6a9995bc39f73f8990af0e9c760fd5de775
    $ docker ps
    CONTAINER ID   IMAGE             COMMAND           CREATED              STATUS              PORTS                                         NAMES
    a1d62312db4e   training/webapp   "python app.py"   About a minute ago   Up About a minute   0.0.0.0:55000->5000/tcp, :::55000->5000/tcp   adoring_swartz

    1. 网络端口映射

    (1)指定端口

    两种方式的区别是:

    • -P :是容器内部端口随机映射到主机的高端口。
    • -p : 是容器内部端口绑定到指定的主机端口。
    $ docker run -d -p 5000:5000 training/webapp python app.py
    732f30ae05ae03cb7de71dca186a4d667960fbbcf4f417de9c11a6dac41d5228
    $ docker ps
    CONTAINER ID   IMAGE             COMMAND           CREATED         STATUS         PORTS                                         NAMES
    732f30ae05ae   training/webapp   "python app.py"   6 seconds ago   Up 3 seconds   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp     fervent_ride
    a1d62312db4e   training/webapp   "python app.py"   7 minutes ago   Up 6 minutes   0.0.0.0:55000->5000/tcp, :::55000->5000/tcp   adoring_swartz

    (2)指定网络地址

    $ docker run -d -p 127.0.0.1:5001:5000 training/webapp python app.py
    3c10889d00dc140043175745987d169227e6a06d648a9deb641156b6ec4499cc
    $ docker ps
    CONTAINER ID   IMAGE             COMMAND           CREATED          STATUS          PORTS                                         NAMES
    3c10889d00dc   training/webapp   "python app.py"   20 seconds ago   Up 18 seconds   127.0.0.1:5001->5000/tcp                      reverent_goldberg
    732f30ae05ae   training/webapp   "python app.py"   3 minutes ago    Up 3 minutes    0.0.0.0:5000->5000/tcp, :::5000->5000/tcp     fervent_ride
    a1d62312db4e   training/webapp   "python app.py"   10 minutes ago   Up 9 minutes    0.0.0.0:55000->5000/tcp, :::55000->5000/tcp   adoring_swartz

    (3)指定 UDP 端口

    $ docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
    1192a45b607a3c8e364f8227c650ead85fff80fd0edb794a72f1e5a1b16b3114
    $ docker ps
    CONTAINER ID   IMAGE             COMMAND           CREATED              STATUS              PORTS                                         NAMES
    1192a45b607a   training/webapp   "python app.py"   17 seconds ago       Up 15 seconds       5000/tcp, 127.0.0.1:5000->5000/udp            bold_shaw
    3c10889d00dc   training/webapp   "python app.py"   About a minute ago   Up About a minute   127.0.0.1:5001->5000/tcp                      reverent_goldberg
    732f30ae05ae   training/webapp   "python app.py"   4 minutes ago        Up 4 minutes        0.0.0.0:5000->5000/tcp, :::5000->5000/tcp     fervent_ride
    a1d62312db4e   training/webapp   "python app.py"   11 minutes ago       Up 11 minutes       0.0.0.0:55000->5000/tcp, :::55000->5000/tcp   adoring_swartz

    2. Docker 容器互联

    (1)创建一个Docker网络

    $ docker network create -d bridge test-net
    7edbd75ff78c01ad5f5eb7872f7965d57e6cb98a7df80dc7cb47d3dfef179891
    $ docker network ls
    NETWORK ID     NAME       DRIVER    SCOPE
    d2e544c3d9da   bridge     bridge    local
    efab27602b34   host       host      local
    b1a535169706   none       null      local
    7edbd75ff78c   test-net   bridge    local

    (2)连接容器

    运行一个容器并连接到新建的 test-net 网络

    $ docker run -itd --name test1 --network test-net ubuntu /bin/bash

    打开新的终端,再运行一个容器并加入到 test-net 网络

    $ docker run -itd --name test2 --network test-net ubuntu /bin/bash

    (3)测试

    当前容器没有ping命令

    $ docker exec -it test1 /bin/bash
    root@19440cb02ddd:/# ping www.baidu.com
    bash: ping: command not found

    安装ping命令

    apt-get update
    apt install iputils-ping

    在 test1 容器输入以下命令

    root@19440cb02ddd:/# ping test2
    PING test2 (172.18.0.3) 56(84) bytes of data.
    64 bytes from test2.test-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.078 ms
    64 bytes from test2.test-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.124 ms
    64 bytes from test2.test-net (172.18.0.3): icmp_seq=3 ttl=64 time=0.121 ms

    同理在 test2 容器也会成功连接到

    root@2f15a07298ae:/# ping test1
    PING test1 (172.18.0.2) 56(84) bytes of data.
    64 bytes from test1.test-net (172.18.0.2): icmp_seq=1 ttl=64 time=0.050 ms
    64 bytes from test1.test-net (172.18.0.2): icmp_seq=2 ttl=64 time=0.122 ms
    64 bytes from test1.test-net (172.18.0.2): icmp_seq=3 ttl=64 time=0.119 ms

    六、仓库管理

    1. 注册

    https://hub.docker.com 免费注册一个 Docker 账号

    2. 登陆与退出

    $ docker login # 登录
    Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
    Username: hausen1012
    Password:
    Login Succeeded
    $ docker logout # 退出
    Removing login credentials for https://index.docker.io/v1/

    3. 推送镜像

    $ docker images 
    REPOSITORY               TAG       IMAGE ID       CREATED        SIZE
    docker/getting-started   latest    083d7564d904   2 months ago   28MB
    $ docker tag docker/getting-started docker:v1 # 标记本地镜像,将其归入docker仓库
    $ docker images 
    REPOSITORY               TAG       IMAGE ID       CREATED        SIZE
    docker/getting-started   latest    083d7564d904   2 months ago   28MB
    docker                   v1        083d7564d904   2 months ago   28MB
    $ docker push docker:v1 # 推送镜像

    七、打包镜像

    1 克隆 mblog 源码

    git clone https://gitee.com/hausen1012/mblog.git

    2 打包

    mvn clean package

    3 Dockerfile

    FROM acaiee/alpine-java:cst
    
    WORKDIR /app/mblog
    
    COPY /mblog-latest.jar app.jar
    
    ENV JAVA_OPTS="\
    -Xms128m \
    -Xmx512m \
    -XX:MetaspaceSize=128m \
    -XX:MaxMetaspaceSize=512m"
    
    EXPOSE 8080
    
    ENTRYPOINT java ${JAVA_OPTS} -jar app.jar

    4 制作镜像

    将 jar 包与 Dockerfile 放在同一目录。

    docker build -t hausen1012/unipower-blog .

    5 运行

    docker run -d \
    --name mblog \
    -p 80:8080 \
    -v /docker/mblog/storage:/app/mblog/storage \
    -v /docker/mblog/logs:/app/mblog/logs \
    -e DB_HOST="localhost" \
    -e DB_PORT="3306" \
    -e DB_NAME="mblog" \
    -e DB_USER="root" \
    -e DB_PWD="123456" \
    hausen1012/unipower-blog

    提醒:本文发布于630天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • Mybatis 详解及详细使用教程


    共计 42318 个字符,预计需要花费 106 分钟才能阅读完成。

    一、Mybatis 是什么

    • MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂的过程,开发者只需要关注 SQL 语句本身。

    • MyBatis 支持定制化 SQL、存储过程以及高级映射,可以在实体类和 SQL 语句之间建立映射关系,是一种半自动化的 ORM 实现。其封装性低于 Hibernate,但性能优秀、小巧、简单易学、应用广泛。

    • MyBatis 的主要思想是将程序中的大量 SQL 语句剥离出来,使用 XML 文件或注解的方式实现 SQL 的灵活配置,将 SQL 语句与程序代码分离,在不修改程序代码的情况下,直接在配置文件中修改 SQL 语句。

    • MyBatis 与其它持久性框架最大的不同是,MyBatis 强调使用 SQL,而其它框架(例如 Hibernate)通常使用自定义查询语言,即 HQL(Hibernate查询语言)或 EJB QL(Enterprise JavaBeans查询语言)。

    二、Mybatis 优缺点

    1 优点

    • MyBatis 是免费且开源的。
    • 与 JDBC 相比,减少了 50% 以上的代码量。
    • MyBatis 是最简单的持久化框架,小巧并且简单易学。
    • MyBatis 相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 中,和程序逻辑代码分离,降低耦合度,便于同一管理和优化,提高了代码的可重用性。
    • 提供 XML 标签,支持编写动态 SQL 语句。
    • 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
    • 支持存储过程。MyBatis 以存储过程的形式封装 SQL,可以将业务逻辑保留在数据库之外,增强应用程序的可移植性、更易于部署和测试。

    2 缺点

    • 编写 SQL 语句工作量较大,对开发人员编写 SQL 语句的功底有一定要求。
    • SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

    三、Mybatis 核心对象

    1 核心接口和类

    每个 MyBatis 应用程序都以一个 SqlSessionFactory 对象的实例为核心。首先获取 SqlSessionFactoryBuilder 对象,可以根据 XML 配置文件或者 Configuration 类的实例构建该对象。

    然后获取 SqlSessionFactory 对象,该对象实例可以通过 SqlSessionFactoryBuilder 对象来获取。

    有了 SqlSessionFactory 对象之后,就可以进而获取 SqlSession 实例。SqlSession 对象中完全包含以数据库为背景的所有执行 SQL 操作的方法,用该实例可以直接执行已映射的 SQL 语句。

    1)SqlSessionFactoryBuilder

    该类有多个build()重载方法:

    • build(InputStream inputStream, String environment, Properties properties)
    • build(Reader reader, String environment, Properties properties)
    • build(Configuration config)

    通过上述分析,发现配置信息可以以三种形式提供给 SqlSessionFactoryBuilder 的 build() 方法,分别是 InputStream(字节流)、Reader(字符流)、Configuration(类)。

    构建一个 SqlSessionFactory 有两种方式 :

    • xml 配置文件
    • 编写代码

    以上 xml 配置文件方式可以通过读取字节流或字符流创建,也可以是编写类的方式创建。

    SqlSessionFactoryBuilder的生命周期和作用域

    SqlSessionFactoryBuilder 的最大特点就是用过即丢。创建 SqlSessionFactory 对象之后,这个类就不存在了,因此 SqlSessionFactoryBuilder 的最佳范围就是存在于方法体内,也就是局部变量。

    2)SqlSessionFactory

    SqlSessionFactory 是工厂接口而不是现实类,它的任务就是创建 SqlSession。

    所有的 MyBatis 应用都以 SqlSessionFactory 实例为中心,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 对象来获取。有了它以后,顾名思义,就可以通过 SqlSession 提供的 openSession() 方法来获取 SqlSession 实例。 源码如下:

    public interface SqlSessionFactory {
        SqlSession openSession();
        SqlSession openSession(boolean autoCommit);
        SqlSession openSession(Connection connection);
        SqlSession openSession(TransactionIsolationLevel level);
        SqlSession openSession(ExecutorType execType);
        SqlSession openSession(ExecutorType execType, boolean autoCommit);
        SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
        SqlSession openSession(ExecutorType execType, Connection connection);
        Configuration getConfiguration();
    }

    SqlSessionFactory的生命周期和作用域

    SqlSessionFactory 对象一旦创建,就会在整个应用程序过程中始终存在。没有理由去销毁或再创建它,并且在应用程序运行中也不建议多次创建 SqlSessionFactory。因此 SqlSessionFactory 的最佳作用域是 Application,即随着应用程序的生命周期一直存在。这种“存在于整个应用运行期间,并且只存在一个对象实例”的模式就是所谓的单例模式(指在运行期间有且仅有一个实例)。

    3)SqlSession

    SqlSession 是用于执行持久化操作的对象,类似于 JDBC 中的 Connection。它提供了面向数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例直接运行已映射的 SQL 语句。

    SqlSession 的用途主要有两种:

    1. 获取映射器。让映射器通过命名空间和方法名称找到对应的 SQL,并发送给数据库,执行后返回结果。
    2. 直接通过“命名空间(namespace)+SQL id”的方式执行 SQL,不需要获取映射器。这是 iBatis 版本留下的方式。

    SqlSession生命周期和作用域

    SqlSession 对应一次数据库会话。由于数据库会话不是永久的,因此 SqlSession 的生命周期也不是永久的,每次访问数据库时都需要创建 SqlSession 对象。

    需要注意的是:每个线程都有自己的 SqlSession 实例,SqlSession 实例不能被共享,也不是线程安全的。因此 SqlSession 的作用域范围是 request 作用域或方法体作用域内。

    2 MyBatis核心配置文件

    MyBatis 配置文件的结构如下 :

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration><!-- 配置 -->
        <properties /><!-- 属性 -->
        <settings /><!-- 设置 -->
        <typeAliases /><!-- 类型命名 -->
        <typeHandlers /><!-- 类型处理器 -->
        <objectFactory /><!-- 对象工厂 -->
        <plugins /><!-- 插件 -->
        <environments><!-- 配置环境 -->
            <environment><!-- 环境变量 -->
                <transactionManager /><!-- 事务管理器 -->
                <dataSource /><!-- 数据源 -->
            </environment>
        </environments>
        <databaseIdProvider /><!-- 数据库厂商标识 -->
        <mappers /><!-- 映射器 -->
    </configuration>
    <!-- mybatis-config.xml 文件中的元素节点是有一定顺序的,节点位置必须按以上位置排序,否则会编译错误。 -->

    1)properties 标签

    properties 标签可以通过 resource 属性指定外部 properties 文件(database.properties),也可以通过 properties 子元素配置。

    1.指定文件

    <properties resource="mybatisDemo/resources/database.properties"/>

    database.properties 用于描述数据库连接的相关配置,例如数据库驱动、连接数据库的 url、数据库用户名、数据库密码等。

    2.properties子元素配置

    通过 properties 子元素 property 配置 username 和 password 变量,然后在 environments 节点中引用这些变量,代码如下:

    <properties>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>

    在 environments 节点中引用 username 和 password 变量:

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    也可以不使用 properties 标签,直接将属性值写在 value 中。

    2)settings 标签

    settings 标签用于配置 MyBatis 的运行时行为,它能深刻的影响 MyBatis 的底层运行,一般不需要大量配置,大部分情况下使用其默认值即可。

    settings 的配置项很多,但是真正用到的不会太多。settings 配置项说明如下表所示(表中加粗的配置项为常用配置项)。

    配置项 作用 配置选项 默认值
    cacheEnabled 该配置影响所有映射器中配置缓存的全局开关 true|false true
    lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态 true|false false
    aggressiveLazyLoading 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 true|false 版本3.4.1 (不包含) 之前默认值为 true,之后为 false
    multipleResultSetsEnabled 是否允许单一语句返回多结果集(需要兼容驱动) true|false true
    useColumnLabel 使用列标签代替列名。不同的驱动会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 true|false true
    useGeneratedKeys 允许JDBC 支持自动生成主键,需要驱动兼容。如果设置为 true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby) true|false false
    autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射。 PARTIAL 表示只会自动映射,没有定义嵌套结果集和映射结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套) NONE、PARTIAL、FULL PARTIAL
    autoMappingUnkno wnColumnBehavior 指定自动映射当中未知列(或未知属性类型)时的行为。 默认是不处理,只有当日志级别达到 WARN 级别或者以下,才会显示相关日志,如果处理失败会抛出 SqlSessionException 异常 NONE、WARNING、FAILING NONE
    defaultExecutorType 配置默认的执行器。SIMPLE 是普通的执行器;REUSE 会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新 SIMPLE、REUSE、BATCH SIMPLE
    defaultStatementTimeout 设置超时时间,它决定驱动等待数据库响应的秒数 任何正整数 Not Set (null)
    defaultFetchSize 设置数据库驱动程序默认返回的条数限制,此参数可以重新设置 任何正整数 Not Set (null)
    safeRowBoundsEnabled 允许在嵌套语句中使用分页(RowBounds)。如果允许,设置 false true|false false
    safeResultHandlerEnabled 允许在嵌套语句中使用分页(ResultHandler)。如果允许,设置false true|false true
    mapUnderscoreToCamelCase 是否开启自动驼峰命名规则映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射 true|false false
    localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速联复嵌套査询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlScssion 的不同调用将不会共享数据 SESSION|STATEMENT SESSION
    jdbcTypeForNull 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER NULL、VARCHAR、OTHER OTHER
    lazyLoadTriggerMethods 指定哪个对象的方法触发一次延迟加载 equals、clone、hashCode、toString
    defaultScriptingLanguage 指定动态 SQL 生成的默认语言 org.apache.ibatis .script.ing.xmltags .XMLDynamicLanguageDriver
    callSettersOnNulls 指定当结果集中值为 null 时,是否调用映射对象的 setter(map 对象时为 put)方法,这对于 Map.kcySet() 依赖或 null 值初始化时是有用的。注意,基本类型(int、boolean 等)不能设置成 null true|false false
    logPrefix 指定 MyBatis 增加到日志名称的前缀 任何字符串 Not set
    loglmpl 指定 MyBatis 所用日志的具体实现,未指定时将自动査找 SLF4J|LOG4J|LOG4J2|JDK_LOGGING |COMMONS_LOGGING |ST DOUT_LOGGING|NO_LOGGING Not set
    proxyFactory 指定 MyBatis 创建具有延迟加栽能力的对象所用到的代理工具 CGLIB|JAVASSIST JAVASSIST (MyBatis 版本为 3.3 及以上的)
    vfsImpl 指定 VFS 的实现类 提供 VFS 类的全限定名,如果存在多个,可以使用逗号分隔 Not set
    useActualParamName 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) true|false true

    3)typeAliases 标签

    为了不在任何地方都指定类的全限定名,我们可以使用 typeAliases 标签定义一个别名。

    例如,在 net.bianchengbang.po 包中有一个 Student 类,则该类的全限定名称为 net.bianchengbang.po.Student。使用 typeAliases 标签定义别名,这样就不用每次都书写类的全限定名称了,代码如下:

    <typeAliases>
        <typeAlias alias = "Student" type = "net.bianchengbang.po.Student"/>
    </typeAliases>

    如果需要对同一个包下的多个类定义别名,则可以定义为:

    <typeAliases>
        <package name="net.biancheng.po"/>
    </typeAliases>

    这样 MyBatis 将扫描 net.biancheng.po 包里面的类,将其第一个字母变为小写作为其别名,例如 Student 别名为 student,User 别名为 user。

    4)typeHandlers 标签

    typeHandlers 主要将获取的值合理地转化为 Java 类型。在 typeHandler 中,分为 jdbcType 和 javaType,其中 jdbcType 用于定义数据库类型,而 javaType 用于定义 Java 类型,typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换。

    MyBatis 支持自定义处理类型,在自定义处理类型时,需要实现 org.apache.ibatis.type.TypeHandler 接口或继承 org.apache.ibatis.type.BaseTypeHandle 类。

    5)environments 标签

    在 environments 标签中,可以配置 MyBatis 的多套运行环境,将 SQL 映射到多个不同的数据库上。

    environment 是 environments 的子标签,用来配置 MyBatis 的一套运行环境,需指定运行环境 ID、事务管理、数据源配置等相关信息。

    可以通过配置多个 environment 标签来连接多个数据库,需要注意的是必须指定其中一个为默认运行环境(通过default指定)。

    environment 标签提供了两个子标签,即 transactionManager 和 dataSource。

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    6)mappers 标签

    mappers 标签用于指定 MyBatis SQL 映射文件的路径。

    mapper 是 mappers 的子标签,mapper 中的 resource 属性用于指定 SQL 映射文件的路径(类资源路径)

    例如,SQL 映射文件的名称是 Student.xml,它位于名为 net.biancheng.mapper 的包中,那么可以这样配置:

    <mappers>
        <mapper resource="net/biancheng/mapper/Student.xml"/>
    </mappers>

    3 Mybatis 映射器

    映射器是 MyBatis 中最重要的文件,文件中包含一组 SQL 语句(例如查询、添加、删除、修改),这些语句称为映射语句或映射 SQL 语句。

    映射器由 Java 接口和 XML 文件(或注解)共同组成,它的作用如下:

    • 定义参数类型
    • 配置缓存
    • 提供 SQL 语句和动态 SQL
    • 定义查询结果和 POJO 的映射关系

    映射器有以下两种实现方式。

    1. 通过 XML 文件方式实现,比如我们在 mybatis-config.xml 文件中描述的 XML 文件,用来生成 mapper。

    2. 通过注解的方式实现,使用 Configuration 对象注册 Mapper 接口。

    如果 SQL 语句存在动态 SQL 或者比较复杂,使用注解写在 Java 文件里可读性差,且增加了维护的成本。所以一般建议使用 XML 文件配置的方式,避免重复编写 SQL 语句。

    1)xml 实现映射器:

    XML 定义映射器分为两个部分:接口和XML。

    定义接口 WebsiteMapper :

    package net.biancheng.mapper;
    import java.util.List;
    import net.biancheng.po.Website;
    public interface WebsiteMapper {
        public List<Website> selectAllWebsite();
    }

    WebsiteMapper.xml 代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="net.biancheng.mapper.WebsiteMapper">
        <!-- 查询所有网站信息 -->
        <select id="selectAllWebsite"
            resultType="net.biancheng.po.Website">
            select * from website
        </select>
    </mapper>

    下面对上述 XML 文件进行讲解。

    • namespace 用来定义命名空间,该命名空间和定义接口的全限定名一致。

    • select 元素表明这是一条查询语句,属性 id 用来标识这条 SQL。resultType 表示返回的是一个 Website 类型的值。

    在 MyBatis 配置文件中添加以下代码 :

    <mapper resource="net/biancheng/mapper/WebsiteMapper.xml" />

    该语句用来引入 XML 文件,MyBatis 会读取 WebsiteMapper.xml 文件,生成映射器。

    2)注解实现映射器

    使用注解的方式实现映射器,只需要在接口中使用 Java 注解,注入 SQL 即可。如下所示 :

    package net.biancheng.mapper;
    import java.util.List;
    import org.apache.ibatis.annotations.Select;
    import net.biancheng.po.Website;
    public interface WebsiteMapper2 {
        @Select(value = "select * from website")
        public List<Website> selectAllWebsite();
    }

    这里使用了 @Select 注解,并且注入了和 XML 中相同的 select 语句。

    注意: 如果使用注解和 XML 文件两种方式同时定义,那么 XML 方式将覆盖掉注解方式。

    虽然这里注解的方式看起来比 XML 简单,但是现实中我们遇到的 SQL 会比该例子复杂得多。如果 SQL 语句中有多个表的关联、多个查询条件、级联、条件分支等,显然这条 SQL 就会复杂的多。

    此外,XML 可以相互引入,而注解是不可以的,所以在一些比较复杂的场景下,使用 XML 方式会更加灵活和方便。因此大部分的企业都以 XML 为主,本教程也会保持一致,以 XML 方式来创建映射器。当然在一些简单的表和应用中使用注解方式也会比较简单。

    在 MyBatis 配置文件中添加以下代码 :

    <mapper resource="com/mybatis/mapper/WebsiteMapper2" />

    MyBatis 映射器的主要元素

    元素名称 描述 备注
    mapper 映射文件的根节点,只有 namescape 一个属性 namescape 作用如下:用于区分不同的 mapper,全局唯一绑定DAO接口,即面向接口编程。当 namescape 绑定某一接口后,可以不用写该接口的实现类,MyBatis 会通过接口的完整限定名查找到对应的 mapper 配置来执行 SQL 语句。因此 namescape 的命名必须要跟接口同名。
    select 查询语句,最常用、最复杂的元素之一 可以自定义参数,返回结果集等
    insert 插入语句 执行后返回一个整数,代表插入的条数
    update 更新语句 执行后返回一个整数,代表更新的条数
    delete 删除语句 执行后返回一个整数,代表删除的条数
    parameterMap 定义参数映射关系 即将被删除的元素,不建议使用
    sql 允许定义一部分的 SQL,然后在各个地方引用它 例如,一张表列名,我们可以一次定义,在多个 SQL 语句中使用
    resultMap 用来描述数据库结果集与对象的对应关系,它是最复杂、最强大的元素 提供映射规则
    cache 配置给定命名空间的缓存
    cache-ref 其它命名空间缓存配置的引用

    四、Mybatis 常用标签

    1 select 标签

    在 MyBatis 中,select 标签是最常用也是功能最强大的 SQL 语言,用于执行查询操作 。

    执行 SQL 语句时可以定义参数,参数可以是一个简单的参数类型,例如 int、float、String;也可以是一个复杂的参数类型,例如 JavaBean、Map 等。MyBatis 提供了强大的映射规则,执行 SQL 后,MyBatis 会将结果集自动映射到 JavaBean 中 。

    为了使数据库的查询结果和返回值类型中的属性能够自动匹配,通常会对 MySQL 数据库和 JavaBean 采用同一套命名规则,即 Java 命名驼峰规则,这样就不需要再做映射了(数据库表字段名和属性名不一致时需要手动映射)。

    常用属性:

    属性名称 描 述 备注
    id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+id不唯一,那么 MyBatis 抛出异常
    parameterType 表示传入 SQL 语句传入参数类型的全限定名或别名。它是一个可选属性,MyBatis 能推断出具体传入语句的参数 支持基本数据类型和 JavaBean、Map 等复杂数据类型
    resultType SQL 语句执行后返回的类型(全限定名或者别名)。如果是集合类型,返回的是集合元素的类型,返回时可以使用 resultType 或 resultMap 之一
    resultMap 它是映射集的引用,与 元素一起使用,返回时可以使用 resultType 或 resultMap 之一 是 MyBatis 最复杂的元素,可以配置映射规则、级联、typeHandler 等
    flushCache 用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存 默认值为 false,如果设置为 true,则任何时候只要 SQL 语句被调用都将清空本地缓存和二级缓存
    useCache 启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中
    timeout 用于设置超时参数,单位是秒(s),超时将抛出异常
    fetchSize 获取记录的总条数设定 默认值是数据库厂商提供的 JDBC 驱动所设置的条数
    statementType 告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)、 PREPARED(PreparedStatement)、CALLABLE(CallableStatement)
    resultSetType 这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新)

    传递多个参数

    现在需要根据 id 和 name 来模糊查询网站信息,显然这涉及到了两个参数。给映射器传递多个参数分为以下三种方法。

    • 使用Map传递参数

    • 使用注解传递参数

    • 使用JavaBean传递参数

    1 使用 Map 传递参数

    使用 MyBatis 提供的 Map 接口作为参数实现,如下所示 :

    <!-- 根据name和url模糊查询网站信息 -->
    <select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
        SELECT id,NAME,url FROM website
        WHERE name LIKE CONCAT ('%',#{name},'%')
        AND url LIKE CONCAT ('%',#{url},'%')
    </select>

    在 WebsiteMapper 接口中,方法如下

    public List<Website> selectWebsiteByMap(Map<String, String> params);

    测试代码如下 :

    Map<String,String> paramsMap = new HashMap<String,String>();
    paramsMap.put("name","编程");
    paramsMap.put("url","biancheng");
    websiteMapper.selectWebsiteByMap(paramsMap);

    2 使用注解传递参数

    使用 MyBatis 的注解 @Param() 传递参数,如下所示:

    <!-- 根据name和url模糊查询网站信息 -->
    <select id="selectWebsiteByAn" resultType="net.biancheng.po.Website">
        SELECT id,NAME,url FROM website
        WHERE name LIKE CONCAT ('%',#{name},'%')
        AND url LIKE CONCAT ('%',#{url},'%')
    </select>

    WebsiteMapper 接口中方法如下:

    public List<Website> selectWebsiteByAn(@Param("name") String name, @Param("url") String url);

    当我们把参数传递给后台时,MyBatis 通过 @Param 提供的名称就会知道 #{name} 代表 name 参数,提高了参数可读性。但是如果这条 SQL 拥有 10 个参数的查询,就会造成可读性下降,增强了代码复杂性。

    3 使用JavaBean传递参数

    在参数过多的情况下,MyBatis 允许组织一个 JavaBean,通过简单的 setter 和 getter 方法设置参数,提高可读性。如下所示 :

    <!-- 根据name和url模糊查询网站信息 -->
    <select id="selectWebsiteByAn" resultType="net.biancheng.po.Website">
        SELECT id,NAME,url FROM website
        WHERE name LIKE CONCAT ('%',#{name},'%')
        AND url LIKE CONCAT ('%',#{url},'%')
    </select>

    WebsiteMapper 接口中方法如下:

    public List<Website> selectWebsiteByAn(Website website);

    以上 3 种方式的区别如下:

    • 使用 Map 传递参数会导致业务可读性的丧失,继而导致后续扩展和维护的困难,所以在实际应用中我们应该果断废弃该方式。
    • 使用 @Param 注解传递参数会受到参数个数的影响。当 n≤5 时,它是最佳的传参方式,因为它更加直观;当 n>5 时,多个参数将给调用带来困难。
    • 当参数个数大于 5 个时,建议使用 JavaBean 方式。

    2 insert 标签

    MyBatis insert 标签用来定义插入语句,执行插入操作。当 MyBatis 执行完一条插入语句后,就会返回其影响数据库的行数。

    常用属性:

    属性名称 描述 备注
    id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
    parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
    keyProperty 该属性的作用是将插入操作的返回值赋给 PO 类的某个属性,通常为主键对应的属性。如果是联合主键,可以将多个值用逗号隔开。
    useGeneratedKey 该属性用来设置,是否使用 JDBC 提供的 getGenereatedKeys() 方法,获取数据库内部产生的主键并赋值到 keyProperty 属性设置的请求对象的属性中,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。 该属性值设置为 true 后,会将数据库生成的主键回填到请求对象中,以供其他业务使用。
    flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。
    timeout 该属性用于设置执行该操作的最大时限,如果超时,就抛异常。
    databaseId 取值范围 oracle、mysql 等,表示数据库厂家;元素内部可通过 来为特定数据库指定不同的 sql 语句。 MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。
    keyColumn 该属性用于设置第几列是主键,当主键列不是表中的第 1 列时,就需要设置该属性。如果是联合主键,可以将多个值用逗号隔开。

    注意:insert 标签中没有 resultType 属性,只有查询操作才需要对返回结果类型进行相应的指定。

    传递多个参数

    在实际的开发过程中,我们的插入语句往往需要使用多个参数,Mybatis 为我们提供以下 3 种方式,来实现给映射器传递多个参数:

    • 使用 Map 传递参数
    • 使用注解传递参数
    • 使用 JavaBean 传递参数

    1 使用 Map 传递参数java

    可以将参数封装到一个 Map 对象中,然后传递给 MyBatis 的映射器。在 WebsiteMapper 接口中,定义一个 addWebsiteByMap() 方法,并使用 Map 传递参数,方法如下 :

    int addWebsiteByMap(Map<String, String> params);

    在 WebsiteMapper.xml 中,使用 insert 标签定义一条插入语句,并接收通过 Map 传递的参数,配置如下 :

    <!--接收 Map 参数-->
    <insert id="addWebsiteByMap" parameterType="map">
        insert into Website (name, url) values (#{name}, #{url})
    </insert>

    测试代码如下 :

    Map<String, String> params = new HashMap<>();
    params.put("name", "编程帮");
    params.put("url", "https://www.biancheng.net/");
    int i = websiteMapper.addWebsiteByMap(params);
    System.out.println("通过 Map 成功向数据库中添加了 " + i + " 条记录");

    2 使用注解传递参数

    还可以使用 MyBatis 提供的 @Param 注解给注解器传递参数, 在 WebsiteMapper 接口中,定义一个 addWebsiteByParam() 方法,并使用 @Param 注解传递参数,方法如下 :

    int addWebsiteByParam(@Param("name") String name, @Param("url") String url);

    在 WebsiteMapper.xml 使用 insert 标签定义一条插入语句,并接收通过 @Param 注解传递的参数,配置如下 :

    <!--接收 @Param 注解传递的参数-->
    <insert id="addWebsiteByParam">
        insert into Website (name, url)  values (#{name}, #{url})
    </insert>

    3 使用JavaBean传递参数

    在参数过多的情况下,我们可以将参数通过 setter 方法封装到 JavaBean(实体类)对象中 ,传递给映射器 。 在 WebsiteMapper 接口中,定义一个 addWebsiteByJavaBean() 方法,并使用 JavaBean 传递参数 ,如下所示 :

    int addWebsiteByJavaBean(Website website);

    在 WebsiteMapper.xml 中使用 insert 标签定义一条插入语句,并接收通过 JavaBean 传递的参数,配置如下 :

    <!--接收通过 JavaBean 传递的参数-->
    <insert id="addWebsiteByJavaBean" parameterType="net.biancheng.www.po.Website">
        insert into Website (name, url) values (#{name}, #{url})
    </insert>

    以上 3 种方式的区别如下:

    • 使用 Map 传递参数会导致业务可读性的丧失,继而导致后续扩展和维护的困难,所以在实际应用中我们应该果断废弃该方式。
    • 使用 @Param 注解传递参数会受到参数个数的影响。当 n≤5 时,它是最佳的传参方式,因为它更加直观;当 n>5 时,多个参数将给调用带来困难。
    • 当参数个数大于 5 个时,建议使用 JavaBean 方式。

    3 update 标签

    MyBatis update 标签用于定义更新语句,执行更新操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。

    常用属性:

    属性名称 描述 备注
    id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
    parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
    flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。
    timeout 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。
    statementType 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。

    4 delete 标签

    MyBatis delete 标签用于定义 delete 语句,执行删除操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数 。

    常用属性:

    属性名称 描述 备注
    id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
    parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
    flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。
    timeout 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。
    statementType 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。

    5 resultMap 标签

    resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象。

    现有的 MyBatis 版本只支持 resultMap 查询,不支持更新或者保存,更不必说级联的更新、删除和修改。

    1)构成

    resultMap 元素还可以包含以下子元素,代码如下:

    <resultMap id="" type="">
        <constructor><!-- 类再实例化时用来注入结果到构造方法 -->
            <idArg/><!-- ID参数,结果为ID -->
            <arg/><!-- 注入到构造方法的一个普通结果 --> 
        </constructor>
        <id/><!-- 用于表示哪个列是主键 -->
        <result/><!-- 注入到字段或JavaBean属性的普通结果 -->
        <association property=""/><!-- 用于一对一关联 -->
        <collection property=""/><!-- 用于一对多、多对多关联 -->
        <discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
            <case value=""/><!-- 基于某些值的结果映射 -->
        </discriminator>
    </resultMap>

    其中:

    • 元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。
    • 子元素 用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。
    • 子元素 用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。
    • 子元素 用于表示 POJO 和 SQL 列名的映射关系。
    • 子元素 用在级联的情况下。

    id 和 result 元素都有以下属性 :

    元素 说明
    property 映射到列结果的字段或属性。如果 POJO 的属性和 SQL 列名(column元素)是相同的,那么 MyBatis 就会映射到 POJO 上
    column 对应 SQL 列
    javaType 配置 Java 类型。可以是特定的类完全限定名或 MyBatis 上下文的别名
    jdbcType 配置数据库类型。这是 JDBC 类型,MyBatis 已经为我们做了限定,基本支持所有常用数据库类型
    typeHandler 类型处理器。允许你用特定的处理器来覆盖 MyBatis 默认的处理器。需要指定 jdbcType 和 javaType 相互转化的规则

    一条 SQL 查询语句执行后会返回结果集,结果集有两种存储方式,即使用 Map 存储和使用 POJO 存储 。

    2)使用Map存储结果集

    任何 select 语句都可以使用 Map 存储,代码如下:

    <!-- 查询所有网站信息存到Map中 -->
    <select id="selectAllWebsite" resultType="map">
        select * from website
    </select>

    在 WebsiteMapper 接口中添加以下方法:

    public List<Map<String,Object>> selectAllWebsite();

    Map 的 key 是 select 语句查询的字段名(必须完全一样),而 Map 的 value 是查询返回结果中字段对应的值,一条记录映射到一个 Map 对象中。

    使用 Map 存储结果集很方便,但可读性稍差,所以一般推荐使用 POJO 的方式。

    3)使用POJO存储结果集

    因为 MyBatis 提供了自动映射,所以使用 POJO 存储结果集是最常用的方式。但有时候需要更加复杂的映射或级联,这时就需要使用 select 元素的 resultMap 属性配置映射集合。

    package net.biancheng.po;
    import java.util.Date;
    public class Website {
        private int id;
        private String uname;
        private String url;
        private int age;
        private String country;
        private Date createtime;
        /* setter和getter方法*/
        @Override
        public String toString() {
            return "Website[id=" + id + ",uname=" + uname + ",url=" + url + ",age=" + age + ",country=" + country
                    + ",createtime=" + createtime + "]";
        }
    }

    WebsiteMapper.xml 代码如下:

    <!--使用自定义结果集类型 -->
    <resultMap type="net.biancheng.po.Website" id="myResult">
        <!-- property 是 net.biancheng.po.Website 类中的属性 -->
        <!-- column是查询结果的列名,可以来自不同的表 -->
        <id property="id" column="id" />
        <result property="uname" column="name" />
    </resultMap>

    resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。我们可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。

    这里使用 property 元素指定 Website 的属性名称 uname,column 表示数据库中 website 表的 SQL 列名 name,将 POJO 和 SQL 的查询结果一 一对应。

    WebsiteMapper.xml 中 select 元素配置代码如下:

    <select id="selectAllWebsite" resultMap="myResult">
        select id,name,url from website
    </select>

    可以发现 SQL 语句的列名和 myResult 中的 column 一一对应。

    4)resultType和resultMap的区别

    MyBatis 的每一个查询映射的返回类型都是 resultMap,只是当我们提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性,而当我们提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。

    需要注意的是,resultMap 和 resultType 不能同时使用。

    五、Mybatis 注解

    1 SQL 映射语句

    1)@Insert:实现新增功能

    @Insert("insert into user(id,name) values(#{id},#{name})")
    public int insert(User user);

    2)@Select:实现查询功能

    @Select("Select * from user")
    List<User> queryAllUser();

    3)@SelectKey:插入后,获取id的值

    以 MySQL 为例,MySQL 在插入一条数据后,使用 select last_insert_id() 可以获取到自增 id 的值

    @Insert("insert into user(id,name) values(#{id},#{name})")
    @SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int,before = false)
    public int insert(User user);

    @SelectKey 各个属性含义如下:

    • statement:表示要运行的 SQL 语句;
    • keyProperty:可选项,表示将查询结果赋值给代码中的哪个对象;
    • keyColumn:可选项,表示将查询结果赋值给数据表中的哪一列;
    • resultType:指定 SQL 语句的返回值;
    • before:默认值为 true,在执行插入语句之前,执行 select last_insert_id()。值为 flase,则在执行插入语句之后,执行 select last_insert_id()。

    4)@Update:实现更新功能

    @Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
    void updateUserById(User user);

    5)@Delete:实现删除功能

    @Delete("delete from  user  where id =#{id}")
    void deleteById(Integer id);

    6)@Param:映射多个参数

    @Param 用于在 Mapper 接口中映射多个参数

    int saveUser(@Param(value="user") User user,@Param("name") String name,@Param("age") Int age);

    @Param 中的 value 属性可省略,用于指定参数的别名

    2 结果集映射

    @Result、@Results、@ResultMap 是结果集映射的三大注解

    声明结果集映射关系代码:

    @Select({"select id, name, class_id from student"})
    @Results(id="studentMap", value={
        @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
        @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
        @Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
    })
    List<Student> selectAll();

    下面为 @Results 各个属性的含义:

    • id:表示当前结果集声明的唯一标识;
    • value:表示结果集映射关系;
    • @Result:代表一个字段的映射关系。其中,column 指定数据库字段的名称,property 指定实体类属性的名称,jdbcType 数据库字段类型,id 为 true 表示主键,默认 false。

    可使用 @ResultMap 来引用映射结果集,其中 value 可省略:

    @Select({"select id, name, class_id from student where id = #{id}"})
    @ResultMap(value="studentMap")
    Student selectById(Integer id);

    3 关系映射

    1)@one:用于一对一关系映射

    @Select("select * from student") 
    @Results({ 
      @Result(id=true,property="id",column="id"), 
      @Result(property="name",column="name"), 
      @Result(property="age",column="age"), 
      @Result(property="address",column="address_id",one=@One(select="net.biancheng.mapper.AddressMapper.getAddress")) 
    }) 
    public List<Student> getAllStudents();  

    2)@many:用于一对多关系映射

    @Select("select * from t_class where id=#{id}") 
    @Results({ 
        @Result(id=true,column="id",property="id"), 
        @Result(column="class_name",property="className"), 
        @Result(property="students", column="id",             many=@Many(select="net.biancheng.mapper.StudentMapper.getStudentsByClassId")) 
        }) 
    public Class getClass(int id); 

    六、Mybatis 关联查询

    级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。

    在级联中存在 3 种对应关系。

    • 一对一的关系。每个软件工程师都有一个编号(ID),这是他在公司的标识,它与工程师是一对一的关系。

    • 一对多的关系,如角色和用户的关系。通俗的理解就是,一家软件公司会存在许多软件工程师,公司和软件工程师就是一对多的关系。

    • 多对多的关系,有些公司一个角色可以对应多个用户,但是一个用户可以兼任多个角色。通俗的说,一个人既可以是总经理,同时也是技术总监,而技术总监这个职位可以对应多个人,这就是多对多的关系。

    级联的优点是获取关联数据十分便捷。但是级联过多会增加系统的复杂度,同时降低系统的性能,此增彼减。所以记录超过 3 层时,就不要考虑使用级联了,因为这样会造成多个对象的关联,导致系统的耦合、负载和难以维护。

    1 一对一关联查询

    一对一级联关系在现实生活中是十分常见的,例如一个大学生只有一个学号,一个学号只属于一个学生。同样,人与身份证也是一对一的级联关系。

    在 MyBatis 中,通过 元素的子元素 处理一对一级联关系。示例代码如下 :

    <association property="studentCard" column="cardId"
                javaType="net.biancheng.po.StudentCard"
                select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" />

    元素中通常使用以下属性:

    • property:指定映射到实体类的对象属性
    • column:指定表中对应的字段(即查询返回的列名)
    • javaType:指定映射到实体对象属性的类型
    • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询

    一对一关联查询可采用以下两种方式:

    • 单步查询,通过关联查询实现
    • 分步查询,通过两次或多次查询,为一对一关系的实体 Bean 赋值

    示例:

    创建数据表

    创建 student(学生)和 studentcard(学号)数据表,SQL 语句如下:

    CREATE TABLE `student` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
      `sex` tinyint(4) DEFAULT NULL,
      `cardId` int(20) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `cardId` (`cardId`),
      CONSTRAINT `student_ibfk_1` FOREIGN KEY (`cardId`) REFERENCES `studentcard` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    insert  into `student`(`id`,`name`,`sex`,`cardId`) values (1,'C语言中文网',0,1),(2,'编程帮',0,2),(3,'赵小红',1,3),(4,'李晓明',0,4),(5,'李紫薇',1,5),(6,'钱百百',0,NULL);
    DROP TABLE IF EXISTS `studentcard`;
    CREATE TABLE `studentcard` (
      `id` int(20) NOT NULL AUTO_INCREMENT,
      `studentId` int(20) DEFAULT NULL,
      `startDate` date DEFAULT NULL,
      `endDate` date DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `studentId` (`studentId`)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
    insert  into `studentcard`(`id`,`studentId`,`startDate`,`endDate`) values (1,20200311,'2021-03-01','2021-03-11'),(2,20200314,'2021-03-01','2021-03-11'),(3,20200709,'2021-03-01','2021-03-11'),(4,20200508,'2021-03-01','2021-03-11'),(5,20207820,'2021-03-01','2021-03-11');

    创建持久化类

    在 myBatisDemo 应用的 net.biancheng.po 包下创建数据表对应的持久化类 Student 和 StudentCard

    Student 的代码如下:

    package net.biancheng.po;
    public class Student {
        private int id;
        private String name;
        private int sex;
        private StudentCard studentCard;
        /*省略setter和getter方法*/
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", studentCard=" + studentCard + "]";
        }
    }

    StudentCard 的代码如下:

    package net.biancheng.po;
    import java.util.Date;
    public class StudentCard {
        private int id;
        private int studentId;
        private Date startDate;
        private Date endDate;
        /*省略setter和getter方法*/
        @Override
        public String toString() {
            return "StudentCard [id=" + id + ", studentId=" + studentId + "]";
        }
    }

    分步查询:

    新建 StudentCardMapper 类,代码如下 :

    package net.biancheng.mapper;
    import net.biancheng.po.StudentCard;
    public interface StudentCardMapper {
        public StudentCard selectStuCardById(int id);
    }

    StudentCardMapper.xml 对应映射 SQL 语句代码如下 :

    <mapper namespace="net.biancheng.mapper.StudentCardMapper">
        <select id="selectStuCardById"
            resultType="net.biancheng.po.StudentCard">
            SELECT * FROM studentCard WHERE id = #{id}
        </select>
    </mapper>

    StudentMapper 类方法代码如下 :

    package net.biancheng.mapper;
    import net.biancheng.po.Student;
    public interface StudentMapper {
        public Student selectStuById1(int id);
        public Student selectStuById2(int id);
    }

    StudentMapper.xml 代码如下:

    <mapper namespace="net.biancheng.mapper.StudentMapper">
        <!-- 一对一根据id查询学生信息:级联查询的第一种方法(嵌套查询,执行两个SQL语句) -->
        <resultMap type="net.biancheng.po.Student" id="cardAndStu1">
            <id property="id" column="id" />
            <result property="name" column="name" />
            <result property="sex" column="sex" />
            <!-- 一对一级联查询 -->
            <association property="studentCard" column="cardId"
                javaType="net.biancheng.po.StudentCard"
                select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" />
        </resultMap>
        <select id="selectStuById1" parameterType="Integer"
            resultMap="cardAndStu1">
            select * from student where id=#{id}
        </select>
    </mapper>

    单步查询:

    在 StudentMapper.xml 中添加以下代码:

    <resultMap type="net.biancheng.po.Student" id="cardAndStu2">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="sex" column="sex" />
        <!-- 一对一级联查询 -->
        <association property="studentCard"
            javaType="net.biancheng.po.StudentCard">
            <id property="id" column="id" />
            <result property="studentId" column="studentId" />
        </association>
    </resultMap>
    <select id="selectStuById2" parameterType="Integer"
        resultMap="cardAndStu2">
        SELECT s.*,sc.studentId FROM student s,studentCard sc
        WHERE
        s.cardId = sc.id AND s.id=#{id}
    </select>

    在 StudentMapper 中添加以下方法:

    public Student selectStuById2(int id);

    2 一对多关联查询

    在实际生活中也有许多一对多级联关系,例如一个用户可以有多个订单,而一个订单只属于一个用户。同样,国家和城市也属于一对多级联关系。

    在 MyBatis 中,通过 元素的子元素 处理一对多级联关系,collection 可以将关联查询的多条记录映射到一个 list 集合属性中。示例代码如下:

    <collection property="orderList"
            ofType="net.biancheng.po.Order" column="id"
            select="net.biancheng.mapper.OrderMapper.selectOrderById" />

    元素中通常使用以下属性:

    • property:指定映射到实体类的对象属性
    • column:指定表中对应的字段(即查询返回的列名)
    • ofType:指定映射到实体对象属性的类型
    • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询

    一对多关联查询可采用以下两种方式:

    • 分步查询,通过两次或多次查询,为一对多关系的实体 Bean 赋值
    • 单步查询,通过关联查询实现

    示例

    1)创建数据表

    本实例需要两张数据表,一张是用户表 user,一张是订单表 order,这两张表具有一对多的级联关系。SQL 语句如下:

    CREATE TABLE `order` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `ordernum` int(25) DEFAULT NULL,
      `userId` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `userId` (`userId`),
      CONSTRAINT `order_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
    insert  into `order`(`id`,`ordernum`,`userId`) values (1,20200107,1),(2,20200806,2),(3,20206702,3),(4,20200645,1),(5,20200711,2),(6,20200811,2),(7,20201422,3),(8,20201688,4);
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) DEFAULT NULL,
      `pwd` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    insert  into `user`(`id`,`name`,`pwd`) values (1,'编程帮','123'),(2,'C语言中文网','456'),(3,'赵小红','123'),(4,'李晓明','345'),(5,'杨小胤','123'),(6,'谷小乐','789');

    2)创建持久化类

    创建持久化类 User 和 Order,代码分别如下:

    package net.biancheng.po;
    import java.util.List;
    public class User {
        private int id;
        private String name;
        private String pwd;
        private List<Order> orderList;
        /*省略setter和getter方法*/
        @Override
        public String toString() {
            return "User [id=" + id + ", name=" + name + ", orderList=" + orderList + "]";
        }
    }

    Order 类代码如下:

    package net.biancheng.po;
    public class Order {
        private int id;
        private int ordernum;
        /*省略setter和getter方法*/
        @Override
        public String toString() {
            return "Order [id=" + id + ", ordernum=" + ordernum + "]";
        }
    }

    分步查询:

    OrderMapper 类代码如下:

    public List<Order> selectOrderById(int id);

    OrderMapper.xml 中相应的映射 SQL 语句如下:

    <!-- 根据id查询订单信息 -->
    <select id="selectOrderById" resultType="net.biancheng.po.Order"
        parameterType="Integer">
        SELECT * FROM `order` where userId=#{id}
    </select>

    UserMapper 类代码如下:

    public User selectUserOrderById1(int id);

    UserMapper.xml 中相应的映射 SQL 语句如下:

    <!-- 一对多 根据id查询用户及其关联的订单信息:级联查询的第一种方法(分步查询) -->
    <resultMap type="net.biancheng.po.User" id="userAndOrder1">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="pwd" column="pwd" />
        <!-- 一对多级联查询,ofType表示集合中的元素类型,将id传递给selectOrderById -->
        <collection property="orderList"
            ofType="net.biancheng.po.Order" column="id"
            select="net.biancheng.mapper.OrderMapper.selectOrderById" />
    </resultMap>
    <select id="selectUserOrderById1" parameterType="Integer"
        resultMap="userAndOrder1">
        select * from user where id=#{id}
    </select>

    测试代码如下:

    public class Test {
        public static void main(String[] args) throws IOException {
            InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
            SqlSession ss = ssf.openSession();
            User us = ss.getMapper(UserMapper.class).selectUserOrderById1(1);
            System.out.println(us);
        }
    }

    单步查询:

    该种方式实现一对多关联查询需要修改 Order 持久化类,因为 Order 中的 id 不能和 User 中的 id 重复

    package net.biancheng.po;
    public class Order {
        private int oId;
        private int ordernum;
        /*省略setter和getter方法*/
        @Override
        public String toString() {
            return "Order [id=" + oId+ ", ordernum=" + ordernum + "]";
        }
    }

    UserMapper 类代码如下:

    public User selectUserOrderById2(int id)

    UserMapper.xml 中相关映射 SQL 语句如下:

    <!-- 一对多 根据id查询用户及其关联的订单信息:级联查询的第二种方法(单步查询) -->
    <resultMap type="net.biancheng.po.User" id="userAndOrder2">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="pwd" column="pwd" />
        <!-- 一对多级联查询,ofType表示集合中的元素类型 -->
        <collection property="orderList"
            ofType="net.biancheng.po.Order">
            <id property="oId" column="oId" />
            <result property="ordernum" column="ordernum" />
        </collection>
    </resultMap>
    <select id="selectUserOrderById2" parameterType="Integer"
        resultMap="userAndOrder2">
        SELECT u.*,o.id as oId,o.ordernum FROM `user` u,`order` o
        WHERE
        u.id=o.`userId` AND u.id=#{id}
    </select>

    测试代码修改调用方法,如下:

    public class Test {
        public static void main(String[] args) throws IOException {
            InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
            SqlSession ss = ssf.openSession();
            User us = ss.getMapper(UserMapper.class).selectUserOrderById2(1);
            System.out.println(us);
        }
    }

    3 多对多关联查询

    实际应用中,由于多对多的关系比较复杂,会增加理解和关联的复杂度,所以应用较少。MyBatis 没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。

    例如,一个订单可以有多种商品,一种商品可以对应多个订单,订单与商品就是多对多的级联关系。可以使用一个中间表(订单记录表)将多对多级联转换成两个一对多的关系。

    示例

    下面以订单和商品(实现“查询所有订单以及每个订单对应的商品信息”的功能)为例讲解多对多关联查询

    1)创建数据表

    创建 order(订单),product(商品)和 order_details(订单和商品中间表),SQL 语句如下:

    CREATE TABLE `order` (
      `oid` int(11) NOT NULL AUTO_INCREMENT,
      `ordernum` int(25) DEFAULT NULL,
      `userId` int(11) DEFAULT NULL,
      PRIMARY KEY (`oid`),
      KEY `userId` (`userId`),
      CONSTRAINT `order_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
    insert  into `order`(`oid`,`ordernum`,`userId`) values (1,20200107,1),(2,20200806,2),(3,20206702,3),(4,20200645,1),(5,20200711,2),(6,20200811,2),(7,20201422,3),(8,20201688,4),(9,NULL,5);
    
    DROP TABLE IF EXISTS `orders_detail`;
    CREATE TABLE `orders_detail` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `orderId` int(11) DEFAULT NULL,
      `productId` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    insert  into `orders_detail`(`id`,`orderId`,`productId`) values (1,1,1),(2,1,2),(3,1,3),(4,2,3),(5,2,1),(6,3,2);
    
    DROP TABLE IF EXISTS `product`;
    CREATE TABLE `product` (
      `pid` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(25) DEFAULT NULL,
      `price` double DEFAULT NULL,
      PRIMARY KEY (`pid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
    insert  into `product`(`pid`,`name`,`price`) values (1,'Java教程',128),(2,'C语言教程',138),(3,'Python教程',132.35);

    2)创建持久化类

    Order 类代码如下:

    package net.biancheng.po;
    import java.util.List;
    public class Order {
        private int oid;
        private int ordernum;
        private List<Product> products;
        /*省略setter和getter方法*/
        @Override
        public String toString() {
            return "Order [id=" + oid + ", ordernum=" + ordernum + ", products=" + products + "]";
        }
    }

    Product 类方法如下:

    package net.biancheng.po;
    import java.util.List;
    public class Product {
        private int pid;
        private String name;
        private Double price;
        // 多对多中的一个一对多
        private List<Order> orders;
        /*省略setter和getter方法*/
        @Override
        public String toString() {
            return "Product [id=" + pid + ", name=" + name + ", price=" + price + "]";
        }
    }

    3.)创建接口和映射文件

    OrderMapper 接口代码如下:

    package net.biancheng.mapper;
    import java.util.List;
    import net.biancheng.po.Order;
    public interface OrderMapper {
        public List<Order> selectAllOrdersAndProducts();
    }

    OrderMapper.xml 代码如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="net.biancheng.mapper.OrderMapper">
        <resultMap type="net.biancheng.po.Order" id="orderMap">
            <id property="oid" column="oid" />
            <result property="ordernum" column="ordernum" />
            <collection property="products"
                ofType="net.biancheng.po.Product">
                <id property="pid" column="pid" />
                <result property="name" column="name" />
                <result property="price" column="price" />
            </collection>
        </resultMap>
        <select id="selectAllOrdersAndProducts" parameterType="Integer"
            resultMap="orderMap">
            SELECT o.oid,o.`ordernum`,p.`pid`,p.`name`,p.`price` FROM
            `order` o
            INNER JOIN orders_detail od ON o.oid=od.`orderId`
            INNER JOIN
            product p
            ON p.pid = od.`productId`
        </select>
    </mapper>

    4)创建测试类

    测试类代码如下:

    package net.biancheng.test;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    
    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 net.biancheng.mapper.OrderMapper;
    import net.biancheng.po.Order;
    
    public class Test {
        public static void main(String[] args) throws IOException {
            InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
            SqlSession ss = ssf.openSession();
    
            List<Order> orderList = ss.getMapper(OrderMapper.class).selectAllOrdersAndProducts();
            for (Order or : orderList) {
                System.out.println(or);
            }
        }
    }

    七、Mybatis 动态SQL

    动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,开发人员通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。例如,拼接时要确保添加了必要的空格,还要注意去掉列表最后一个列名的逗号。而动态 SQL 恰好解决了这一问题,可以根据场景动态的构建查询。

    MyBatis 的动态 SQL 包括以下几种元素,如下表所示:

    元素 作用 备注
    if 判断语句 单条件分支判断
    choose(when、otherwise) 相当于 Java 中的 switch case 语句 多条件分支判断
    trimwhere 辅助元素 用于处理一些SQL拼装问题
    foreach 循环语句 在in语句等列举条件常用
    bind 辅助元素 拼接参数

    1 if 标签

    if 语句使用方法简单,常常与 test 属性联合使用。语法如下:

    <if test="判断条件">
        SQL语句
    </if>

    当判断条件为 true 时,才会执行所包含的 SQL 语句。

    if 语句中包含 where 子句,例如:

    <select id="selectAllWebsite" resultMap="myResult">
        select id,name,url from website
        <if test="name != null">
            where name like #{name}
        </if>
    </select>

    多个 if 语句同时使用,例如:

    <select id="selectAllWebsite" resultMap="myResult">
        select id,name,url from website where 1=1
        <if test="name != null">
            AND name like #{name}
        </if>
        <if test="url!= null">
            AND url like #{url}
        </if>
    </select>

    2 choose-when-otherwise 标签

    MyBatis 中动态语句 choose-when-otherwise 类似于 Java 中的 switch-case-default 语句。由于 MyBatis 并没有为 if 提供对应的 else 标签,如果想要达到 的效果,可以借助 来实现

    动态语句 choose-when-otherwise 语法如下:

    <choose>
        <when test="判断条件1">
            SQL语句1
        </when >
        <when test="判断条件2">
            SQL语句2
        </when >
        <when test="判断条件3">
            SQL语句3
        </when >
        <otherwise>
            SQL语句4
        </otherwise>
    </choose>

    choose 标签按顺序判断其内部 when 标签中的判断条件是否成立,如果有一个成立,则执行相应的 SQL 语句,choose 执行结束;如果都不成立,则执行 otherwise 中的 SQL 语句。这类似于 Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。

    以下示例要求:

    • 当网站名称不为空时,只用网站名称作为条件进行模糊查询;
    • 当网站名称为空,而网址不为空时,则用网址作为条件进行模糊查询;
    • 当网站名称和网址都为空时,则要求网站年龄不为空。

    示例:

    <mapper namespace="net.biancheng.mapper.WebsiteMapper">
        <select id="selectWebsite"
            parameterType="net.biancheng.po.Website"
            resultType="net.biancheng.po.Website">
            SELECT id,name,url,age,country
            FROM website WHERE 1=1
            <choose>
                <when test="name != null and name !=''">
                    AND name LIKE CONCAT('%',#{name},'%')
                </when>
                <when test="url != null and url !=''">
                    AND url LIKE CONCAT('%',#{url},'%')
                </when>
                <otherwise>
                    AND age is not null
                </otherwise>
            </choose>
        </select>
    </mapper>

    3 where 标签

    在上面的使用多个 if 标签时,SQL语句中加入了一个条件“1=1”,如果没有加入这个条件,那么可能就会变成下面这样一条错误的语句:

    SELECT id,name,url,age,country FROM website AND name LIKE CONCAT('%',#{name},'%')

    显然以上语句会出现 SQL 语法异常,但加入“1=1”这样的条件又非常奇怪,所以 MyBatis 提供了 where 标签。

    where 标签主要用来简化 SQL 语句中的条件判断,可以自动处理 AND/OR 条件,语法如下:

    <where>
        <if test="判断条件">
            AND/OR ...
        </if>
    </where>

    示例:

    <select id="selectWebsite" resultType="net.biancheng.po.Website">
        select id,name,url from website
        <where>
            <if test="name != null">
                AND name like #{name}
            </if>
            <if test="url!= null">
                AND url like #{url}
            </if>
        </where>
    </select>

    这是对使用多个 if 标签的优化。

    4 set 标签

    在 Mybatis 中,update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号。不为空才更新,能够精确更新修改的地方。

    示例:

    根据 id 修改网站名称或网址:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="net.biancheng.mapper.WebsiteMapper">
        <!--使用set元素动态修改一个网站记录 -->
        <update id="updateWebsite"
            parameterType="net.biancheng.po.Website">
            UPDATE website
            <set>
                <if test="name!=null">name=#{name}</if>
                <if test="url!=null">url=#{url}</if>
            </set>
            WHERE id=#{id}
        </update>
    </mapper>

    5 foreach 标签

    对于一些 SQL 语句中含有 in 条件,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。

    Mybatis foreach 标签用于循环语句,它很好的支持了数据和 List、set 接口的集合,并对此提供遍历的功能。语法格式如下:

    <foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
        参数值
    </foreach>

    foreach 标签主要有以下属性,说明如下:

    • item:表示集合中每一个元素进行迭代时的别名
    • index:指定一个名字,表示在迭代过程中每次迭代到的位置
    • open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)
    • separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)
    • close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)

    使用 foreach 标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:

    • 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
    • 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
    • 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。

    示例:

    现有 website 表包含以下记录:

    id name url age country createtime
    1 编程帮 https://www.biancheng.net/ 10 CN
    2 C语言中文网 http://c.biancheng.net/ 12 CN
    3 百度 https://www.baidu.com/ 18 CN
    4 淘宝 https://www.taobao.com/ 17 CN
    5 Google https://www.google.com/ 23 US
    6 GitHub https://github.com/ 13 US
    7 Stack Overflow https://stackoverflow.com/ 16 US
    8 Yandex http://www.yandex.ru/ 11 RU

    WebsiteMapper.xml 中代码如下:

    <select id="selectWebsite"
        parameterType="net.biancheng.po.Website"
        resultType="net.biancheng.po.Website">
        SELECT id,name,url,age,country
        FROM website WHERE age in
        <foreach item="age" index="index" collection="list" open="("
            separator="," close=")">
            #{age}
        </foreach>
    </select>

    6 bind 标签

    每个数据库的拼接函数或连接符号都不同,例如 MySQL 的 concat 函数、Oracle 的连接符号“||”等。这样 SQL 映射文件就需要根据不同的数据库提供不同的实现,显然比较麻烦,且不利于代码的移植。幸运的是,MyBatis 提供了 bind 标签来解决这一问题。

    比如,按照网站名称进行模糊查询,SQL 映射文件如下:

    <select id="selectWebsite" resultType="net.biancheng.po.Website">
        <bind name="pattern" value="'%'+_parameter+'%'" />
        SELECT id,name,url,age,country
        FROM website
        WHERE name like #{pattern}
    </select>

    bind 元素属性如下。

    • value:对应传入实体类的某个字段,可以进行字符串拼接等特殊处理
    • name:给对应参数取的别名

    以上代码中的 “_parameter” 代表传递进来的参数,它和通配符连接后,赋给了 pattern,然后就可以在 select 语句中使用这个变量进行模糊查询,不管是 MySQL 数据库还是 Oracle 数据库都可以使用这样的语句,提高了可移植性。

    多参数传递:

    <select id="selectWebsite" resultType="net.biancheng.po.Website">
        <bind name="pattern_name" value="'%'+name+'%'" />
        <bind name="pattern_url" value="'%'+url+'%'" />
        SELECT id,name,url,age,country
        FROM website
        WHERE name like #{pattern_name}
        AND url like #{pattern_url}
    </select>

    7 trim 标签

    MyBatis 中除了使用 where + if 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。

    trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。trim 语法格式如下:

    <trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
        SQL语句
    </trim>

    属性说明:

    属性 描述
    prefix 给SQL语句拼接的前缀,为 trim 包含的内容加上前缀
    suffix 给SQL语句拼接的后缀,为 trim 包含的内容加上后缀
    prefixOverrides 去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。
    suffixOverrides 去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定。

    示例

    <select id="selectWebsite" resultType="net.biancheng.po.Website">
        SELECT id,name,url,age,country
        FROM website
        <trim prefix="where" prefixOverrides="and">
            <if test="name != null and name !=''">
                AND name LIKE CONCAT ('%',#{name},'%')
            </if>
            <if test="url!= null">
                AND url like concat ('%',#{url},'%')
            </if>
        </trim>
    </select>

    八、Mybatis 缓存

    缓存可以将数据保存在内存中,是互联网系统常常用到的。目前流行的缓存服务器有 MongoDB、Redis、Ehcache 等。缓存是在计算机内存上保存的数据,读取时无需再从磁盘读入,因此具备快速读取和使用的特点。

    和大多数持久化框架一样,MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 只开启一级缓存。

    1 一级缓存

    • 一级缓存是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。

    • 在参数和 SQL 完全一样的情况下,使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。

    • 由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。

    示例

    在 WebsiteMapper 类中添加 selectWebsiteById 方法,代码如下:

    public Website selectWebsiteById(int id);

    WebsiteMapper.xml 中添加相应的映射 SQL 语句,代码如下:

    <select id="selectWebsiteById"
        resultType="net.biancheng.po.Website">
        SELECT * FROM website
        WHERE id=#{id}
    </select>

    测试代码如下:

    package net.biancheng.test;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    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.apache.log4j.Logger;
    import net.biancheng.po.Website;
    public class Test {
        public static Logger logger = Logger.getLogger(Test.class);
        public static void main(String[] args) throws IOException {
            InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); // 根据配置文件构建
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
            SqlSession ss = ssf.openSession();
            Website site = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);
            logger.debug("使用同一个sqlsession再执行一次");
            Website site2 = ss.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);
            // 请注意,当我们使用二级缓存的时候,sqlSession调用了 commit方法后才会生效
            ss.commit();
            logger.debug("现在创建一个新的SqlSeesion对象在执行一次");
            SqlSession ss2 = ssf.openSession();
            Website site3 = ss2.selectOne("net.biancheng.mapper.WebsiteMapper.selectWebsiteById", 1);
            // 请注意,当我们使用二级缓存的时候,sqlSession调用了 commit方法后才会生效
            ss2.commit();
        }
    }

    运行结果如下:

    DEBUG [main] - ==>  Preparing: SELECT * FROM website WHERE id=?
    DEBUG [main] - ==> Parameters: 1(Integer)
    DEBUG [main] - <==      Total: 1
    DEBUG [main] - 使用同一个sqlsession再执行一次
    DEBUG [main] - 现在创建一个新的SqlSeesion对象在执行一次
    DEBUG [main] - ==>  Preparing: SELECT * FROM website WHERE id=?
    DEBUG [main] - ==> Parameters: 1(Integer)
    DEBUG [main] - <==      Total: 1

    从运行结果可以看出,第一个 SqlSession 实际只发生过一次查询,而第二次查询就从缓存中取出了,也就是 SqlSession 层面的一级缓存。为了克服这个问题,我们往往需要配置二级缓存,使得缓存在 SqlSessionFactory 层面上能够提供给各个 SqlSession 对象共享。

    2 二级缓存

    二级缓存是全局缓存,作用域超出 session 范围之外,可以被所有 SqlSession 共享。

    一级缓存缓存的是 SQL 语句,二级缓存缓存的是结果对象。

    二级缓存的配置

    1)MyBatis 的全局缓存配置需要在 mybatis-config.xml 的 settings 元素中设置,代码如下:

    <settings>
        <setting name="cacheEnabled" value="true" />
    </settings>

    2)在 mapper 文件(如 WebMapper.xml)中设置缓存,默认不开启缓存。需要注意的是,二级缓存的作用域是针对 mapper 的 namescape 而言,即只有再次在 namescape 内(net.biancheng.WebsiteMapper)的查询才能共享这个缓存,代码如下:

    <mapper namescape="net.biancheng.WebsiteMapper">
        <!-- cache配置 -->
        <cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true" />
        ...
    </mapper>

    以上属性说明如下:

    属性 说明
    eviction 代表的是缓存回收策略,目前 MyBatis 提供以下策略。LRU:使用较少,移除最长时间不用的对象;FIFO:先进先出,按对象进入缓存的顺序来移除它们;SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象;WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    flushInterval 刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存。
    size 引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象。
    readOnly 只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存。

    3)在 mapper 文件配置支持 cache 后,如果需要对个别查询进行调整,可以单独设置 cache,代码如下:

    <select id="getWebsiteList" resultType="net.biancheng.po.Website" usecache="true">
        ...
    </select>

    对于 MyBatis 缓存仅作了解即可,因为面对一定规模的数据量,内置的 Cache 方式就派不上用场了,并且对查询结果集做缓存并不是 MyBatis 所擅长的,它专心做的应该是 SQL 映射。对于缓存,采用 OSCache、Memcached 等专门的缓存服务器来做更为合理。

    提醒:本文发布于630天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • Logstash 事件处理管道:输入、过滤器和输出的工作原理


    共计 1239 个字符,预计需要花费 4 分钟才能阅读完成。

    Logstash事件处理管道有三个阶段:输入→过滤器→输出,输入生成事件,过滤器修改它们,然后输出将它们发送到其他地方。输入和输出支持编解码器,使你能够在数据进入或离开管道时对其进行编码或解码,而无需使用单独的过滤器。

    1. 输入

    你使用输入将数据获取到Logstash中,一些比较常用的输入是:

    属性 解析
    file 从文件系统上的文件进行读取,非常类似于UNIX命令tail -0F。
    syslog 在众所周知的端口514上监听syslog消息并根据RFC3164格式进行解析。
    redis 从redis服务器读取数据,同时使用Redis通道和Redis列表,Redis通常被用作集中式Logstash安装中的“broker”,它将从远程Logstash “shipper”中的Logstash事件排队。
    beats 处理Beats发送的事件。

    有关可用输入的更多信息,请参见输入插件。

    2. 过滤器

    过滤器是Logstash管道中的中间处理设备,如果事件符合一定的条件,你可以将过滤器与条件语句组合在一起,对其执行操作,一些有用的过滤器包括:

    属性 解析
    grok 解析和构造任意文本,Grok是目前Logstash中解析非结构化日志数据到结构化和可查询数据的最佳方式,使用内置的120种模式,你很可能会找到一个满足你的需要!
    mutate 对事件字段执行一般的转换,你可以重命名、删除、替换和修改事件中的字段。
    drop 完全删除事件,例如debug事件。
    clone 复制事件,可能添加或删除字段。
    geoip 添加关于IP地址地理位置的信息(在Kibana中还显示了令人惊叹的图表!)

    有关可用过滤器的更多信息,请参见过滤器插件。

    3. 输出

    输出是Logstash管道的最后阶段,事件可以通过多个输出,但是一旦所有的输出处理完成,事件就完成了它的执行,一些常用的输出包括:

    属性 解析
    elasticsearch 发送事件数据到Elasticsearch,如果你打算以一种高效、方便、易于查询的格式保存数据,那么使用Elasticsearch是可行的。
    file 将事件数据写入磁盘上的文件。
    graphite 将事件数据发送到graphite,这是一种流行的用于存储和绘制指标的开源工具。http://graphite.readthedocs.io/en/latest/
    statsd 发送事件到statsd,“监听统计信息(如计数器和计时器)、通过UDP发送聚合并将聚合发送到一个或多个可插拔后端服务”的服务,如果你已经在使用statsd,这可能对你很有用!

    有关可用输出的更多信息,请参见输出插件。

    4. 编解码器

    Codecs是基本的流过滤器,可以作为输入或输出的一部分进行操作,Codecs使你能够轻松地将消息的传输与序列化过程分开,流行的codecs包括json、msgpack和plain(text)。

    属性 解析
    json 以JSON格式对数据进行编码或解码。
    multiline 将多行文本事件(如java异常和stacktrace消息)合并到单个事件中。

    提醒:本文发布于630天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • 12月31日,星期二, 每天60秒读懂全世界!

    百度热搜新闻

    新闻来源:百度热搜榜

    1. 缅北明家靠诈骗过奢靡生活

    2. 韩新任代总统仅接任3天就被立案 30日,因韩国市民团体举报韩国代总统崔相穆等人,崔相穆等已被警方立案调查。距其接任总统一职代行职权,仅过去三天。

    3. 中国经济活力、潜力持续显现 我们期待,越来越多攀“高”向“新”逐“绿”的新产业、新项目萌发破土、转化落地,一批又一批新增长点、新增长极加快孕育、夯基成势,高质量发展的新动能、新优势不断显现,新质生产力的故事精彩继续。

    4. 赵露思1月到12月的变化 赵露思1月到12月的变化明显,粉丝表示:是谁把一个好好的健康的人,变成这样的?要求剧组和公司解释。

    5. 韩33岁幸存空少恐全身瘫痪

    6. 杨采钰内娱一款很新的恶女

    7. 从服务员到副厅官员卸任8年后落马 据悉,临汾人大常委会原副主任柴高潮涉嫌严重违纪违法,正在接受纪委监委纪律审查和监察调查。据了解,其曾在临汾宾馆做过服务员。

    8. 误杀3最后10分钟cpu干烧了 近日,电影《误杀3》最后10分钟高能反转,引发热议。网友直呼:cpu干烧了。

    9. 男子钓到56斤鳡鱼绕城5小时炫耀

    10. 曝头部短剧演员片酬日薪最高3万 近日,有文章曝出短剧演员片酬,参演过《司藤》的李沐宸片酬能达上百万,普通演员也在几万到几十万。

    —- 百度热搜新闻 End —-

    知乎新闻

    新闻来源:知乎日榜

    标题: 熊吃生鱼会感染寄生虫吗?
    链接: https://daily.zhihu.com/story/9777627
    ———————-
    标题: 审配字正南,郭淮之弟郭配字仲南,名字相关,「配」与「南」有何相关义?
    链接: https://daily.zhihu.com/story/9777885
    ———————-
    标题: 为啥项羽可以破釜沉舟,韩信可以背水一战,马谡怎么置之死地,就真死了?
    链接: https://daily.zhihu.com/story/9777888
    ———————-
    标题: 瞎扯 · 如何正确地吐槽
    链接: https://daily.zhihu.com/story/9777507
    ———————-

    —- 知乎新闻 End —-

    IT之家新闻

    新闻来源:ITHome之家科技新闻

    标题: 小米 YU7 SUV 标准版实车现身:不带激光雷达,刹车灯亮起
    发布时间: 2024-12-30T15:04:46.747
    新闻简介: 继小米 YU7 实车首次曝光后,网上再次流出更多该车的谍照,其中一辆标准版车型被网友拍到。在外观设计上,YU7 的低配版本与高阶版整体风格保持一致,延续了家族化的设计语言。前脸部分,标志性的十字形日间行车灯嵌于大灯腔内,极具辨识度。从车尾来看,YU7 保留了类似 SU7 的土星环贯穿式 LED 尾灯,不过在样式上进行了调整,以更贴合 SUV 车型的特性。
    ———————-
    标题: 华为畅享 70X 手机官宣 1 月 3 日发布:北斗卫星消息首次下放,消息称首发麒麟 8000A 处理器
    发布时间: 2024-12-30T10:24:49.15
    新闻简介: 华为畅享 70X 手机官宣将于 1 月 3 日 14:30 发布,口号为“一键北斗,鸿蒙安全”,支持北斗卫星消息、5000 万 RYYB 暗光影像。
    ———————-
    标题: 韩国济州航空又一架波音 737-800 飞机发生起落架故障,现已成功降落
    发布时间: 2024-12-30T07:55:41.927
    新闻简介: 涉事客机机型为波音 737-800,与此前一天济州航空事故客机为同一机型,且均为济州航空运营。
    ———————-
    标题: 奔驰车标供应商宣布破产:曾扛住拿破仑入侵、两次世界大战
    发布时间: 2024-12-30T10:30:46.987
    新闻简介: 德国的格哈迪塑料技术公司(Gerhardi Kunststofftechnik GmbH)经受住了拿破仑的入侵、大萧条和两次世界大战,但没能扛过欧洲目前的汽车业低迷。
    ———————-
    标题: 小米创业十五周年,SU7 限定色明天见
    发布时间: 2024-12-30T19:07:32.337
    新闻简介: 小米汽车今日宣布,小米 SU7 小米 15 周年限定色,明天见。
    ———————-
    标题: Windows 黑暗模式十年仍未完善,微软被指“不作为”
    发布时间: 2024-12-30T08:15:42.12
    新闻简介: Windows Central 发文称,自 2016 年微软为 Windows 10 引入黑暗模式切换功能以来,用户一直期待系统界面能够实现全面的暗色主题。然而,近十年过去了,Windows 的黑暗模式仍然存在诸多缺陷。
    ———————-
    标题: 比亚迪夏 MPV 代言人曝光:实力派影帝张涵予
    发布时间: 2024-12-30T11:48:24.113
    新闻简介: 比亚迪昨日预热,“夏”中大型智能旗舰 MPV 形象代言人即将登场,海报关键词为技艺精湛、沉稳、硬朗、铁汉柔情、实力派影帝、戏路宽广、民族精神的演绎者。
    ———————-
    标题: 小米推出磁吸自带线充电宝 10000 33W,1 月 2 日 169 元首销
    发布时间: 2024-12-30T11:58:54.623
    新闻简介: 小米现已在京东上架一款小米磁吸自带线充电宝 10000 33W,定价为 169 元,将于 1 月 2 日 10:00 首销。
    ———————-
    标题: 京东科技宣布明年将实现“全员平均 20 薪”
    发布时间: 2024-12-30T15:30:21.787
    新闻简介: 京东科技今日宣布将落实20薪计划——2025年将实现全员平均20薪,这也是京东集团继公布2024年年终奖发放计划和超2万名京东客服涨薪通知后,在2024年底再次落地大规模加薪计划。
    ———————-
    标题: 小米 REDMI Turbo 4 手机定档 2025 年 1 月 2 日,发布即开售
    发布时间: 2024-12-30T09:11:15.707
    新闻简介: 官方表示,REDMI Turbo 4 手机预约现已开启,发布即开售。
    ———————-
    标题: 春运期间铁路 12306 试点推出“系统自动提交购票订单”功能
    发布时间: 2024-12-30T11:10:07.14
    新闻简介: 在购票信息预填的基础上,试点推出“系统自动提交购票订单”功能,旅客朋友可预填购票信息并预支付票款,铁路 12306 将根据旅客设定自动为其提交订单,使购票更加便捷。
    ———————-
    标题: 华为彭博:花了 5 年基本实现供应独立,绝对是前无古人
    发布时间: 2024-12-30T19:45:49.85
    新闻简介: 彭博还称一定要持续保持开放,如果华为变成一个以中国市场本土供应链为主的公司,就会变得封闭,而绝对的封闭就是死亡。
    ———————-

    —- IT之家新闻 End —-

  • iptables 两种持久化方式 – centos 和 ubuntu 下 iptables 的持久化


    共计 671 个字符,预计需要花费 2 分钟才能阅读完成。

    这里介绍两种 iptables 持久化的方法:

    1. iptables-persistent 用于基于 deb 的 Linux 发行版,比如 ubuntu
    2. iptables-services 用于基于 rpm 的 Linux 发行版,比如 centos

    1. iptables-persistent

    iptables-persistent 会在重启后自动加载 iptables 规则。

    1.1 安装并设置自启动

    安装过程会提示是否保存 ipv4 规则和 ipv6 规则。

    apt install iptables-persistent && \
    systemctl enable netfilter-persistent

    1.2 添加一条规则

    # 添加一条规则
    iptables -A INPUT -p tcp --dport 39999 -j ACCEPT

    1.3 保存规则

    # 保存 ipv4 规则
    iptables-save > /etc/iptables/rules.v4
    # 保存 ipv6 规则
    ip6tables-save > /etc/iptables/rules.v6

    现在可以重启,然后使用 iptabels -L 命令查看刚刚添加的规则是否还在。

    2. iptables-services

    2.1 安装并设置自启动

    yum install iptables-services && \
    systemctl enable iptables.service

    2.2 添加一条规则

    # 添加一条规则
    iptables -A INPUT -p tcp --dport 39999 -j ACCEPT

    2.3 保存规则

    service iptables save

    现在可以重启,然后使用 iptabels -L 命令查看刚刚添加的规则是否还在。

    提醒:本文发布于629天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室