<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
    <channel>
            <title>张宇顺的博客</title>
            <link>https://www.zhangyushun.com</link>
        <generator>Halo 1.6.1</generator>
        <lastBuildDate>Fri, 17 Feb 2023 09:39:11 CST</lastBuildDate>
                <item>
                    <title>
                        <![CDATA[内网端口回流出现原因]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/nei-wang-duan-kou-hui-liu-chu-xian-yuan-yin</link>
                    <description>
                            <![CDATA[<p><img src="https://lsky.zhangyushun.com/i/2022/11/18/6376e38739ad2.png" alt="image-20221118094440016" /></p><h2 id="%E7%BD%91%E7%BB%9C%E7%8E%AF%E5%A2%83%E4%BB%8B%E7%BB%8D%EF%BC%9A" tabindex="-1">网络环境介绍：</h2><p>公司内网有一台web服务器，地址是192.168.100.100，web服务端口为80，并且为这台web服务器申请了DNS A记录的域名解析服务，解析记录是公司出口ip地址100.100.100.100。在办公区网络环境里，还有内网192.168.10.0网段，需要通过申请的域名来访问公司内网的192.168.100.100的web服务。</p><p>做法是在防火墙的出口，做一条端口映射，100.100.100.100:80到192.168.100.100:80的端口映射。</p><p>问题来了，做好端口映射以后，其他外部网络通过域名访问公司的web服务是正常的，但是公司内网用户通过域名访问公司自己的web服务，却无法访问；而公司内网用户通过192.168.100.100:80私有地址访问，是正常的。这种情况，就是因为做好端口映射以后，访问web服务的流量在响应的时候流量没有回流到防火墙导致的。</p><h2 id="%E5%8E%9F%E5%9B%A0%E5%88%86%E6%9E%90%EF%BC%9A" tabindex="-1">原因分析：</h2><p>如上图，假如是192.168.10.10通过申请的域名访问192.168.100.100的web服务。这里假设访问的源端口是1234，目标端口是80，数据包分析如下：</p><p>PC1主机发起web请求.因为通过域名访问的，DNS解析服务正常，那么访问目标就是100.100.100.100:80</p><p>192.168.10.10:1234—&gt;100.100.100.100:80</p><p>数据包最终会被路由到防火墙上，防火墙检查访问的目的地址，匹配到它的端口映射策略，将目标地址改为对192.168.100.100的访问，建立起一个针对目标ip地址转换的NAT会话表</p><p>192.168.10.10:1234—&gt;192.168.100.100:80</p><p>然后数据包到会被转发到192.168.100.100服务器上并会响应192.168.10.10主机的请求，将上述访问的源目ip地址及端口进行倒转，并将数据包交给它的网关处理，图中就是R1路由器</p><p>192.168.100.100:80—&gt;192.168.10.10:1234</p><p>R1路由器检查访问者的源ip和目标ip地址，发现目标ip地址是192.168.10.10，是R1路由器一个可路由的内网ip地址，就会将数据包直接路由到PC1主机上</p><p>PC1主机接收到数据包，检查数据包的源ip和端口是192.168.100.100:80，发现其本身并没有这样一个http会话与之相匹配，就是说PC1主机并没有主动发起对192.168.100.100:80的访问，发起的是对100.100.100.100:80的访问，那么PC1主机就会丢弃这个数据包，导致内网用户通过域名或者公网ip地址访问自己的内网服务器不通的现象。</p><p>192.168.100.100:80—&gt;192.168.10.10:1234</p><p>上述就是造成我们开头所说问题的原因。发生上述问题的原因，就是因为其R1路由器发现响应数据包的目的ip地址是内网一个可直接路由的地址，就会直接在内网进行路由转发，而不是将数据包交给防火墙进行路由转发。</p>]]>
                    </description>
                    <pubDate>Thu, 17 Nov 2022 18:47:57 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[【Bug记录】Mybatis-Plus和Mybatis混着写，导致的updateById把SQL语句的结果给覆盖了]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/mybatis-plus-he-mybatis-hun-zhe-xie--dao-zhi-de-updatebyid-ba-sql-yu-ju-de-jie-guo-gei-fu-gai-le</link>
                    <description>
                            <![CDATA[<p><img src="https://lsky.zhangyushun.com/i/2022/11/17/6375a20949a16.png" alt="image-20221117105256031" /></p>]]>
                    </description>
                    <pubDate>Tue, 15 Nov 2022 14:24:42 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[Acme.sh 申请多域名、泛域名SSL证书]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/acmesh申请多域名泛域名ssl证书</link>
                    <description>
                            <![CDATA[<h2 id="%E5%89%8D%E8%A8%80" tabindex="-1">前言</h2><p>Acme地址：<a href="https://github.com/deepmind/acme" target="_blank">GitHub</a></p><p>Acme脚本申请证书分为：</p><ul><li>80端口空闲验证的方式</li><li>Nginx验证的方式</li><li>Http验证的方式</li><li>DNS验证的方式</li></ul><p>这里我是用腾讯云的域名DNS来申请证书</p><p><img src="https://lsky.zhangyushun.com/i/2022/11/14/6371dd9d33728.png" alt="image-20221114141803924" /></p><p>效果如图 整个博客全部通过一张证书 实现Https</p><pre><code class="language-shell">apesblog.com*.apesblog.com*.frp.apesbog.com</code></pre><p><strong>这里（*）泛域名只能对下一级全部域名生效、而不是无限递归</strong></p><h2 id="%E6%9B%B4%E6%96%B0%E7%8E%AF%E5%A2%83" tabindex="-1">更新环境</h2><pre><code class="language-shell">apt update -y#Debian/Ubuntu 命令apt install -y curl#Debian/Ubuntu 命令apt install -y socat#Debian/Ubuntu 命令</code></pre><h2 id="%E5%AE%89%E8%A3%85acme%E8%84%9A%E6%9C%AC" tabindex="-1">安装Acme脚本</h2><pre><code class="language-shell">curl https://get.acme.sh | sh</code></pre><h2 id="%E5%8E%BB%E8%85%BE%E8%AE%AF%E4%BA%91dnspod%E7%94%B3%E8%AF%B7%E5%AF%86%E9%92%A5%EF%BC%88%E6%B3%A8%E6%84%8F%E8%BF%99%E9%87%8C%E6%98%AFdns%E5%AF%86%E9%92%A5-%E4%B8%8D%E6%98%AFapi%E5%AF%86%E9%92%A5%EF%BC%89" tabindex="-1">去腾讯云DNSPOD申请密钥（注意这里是DNS密钥 不是API密钥）</h2><p><img src="https://lsky.zhangyushun.com/i/2022/11/14/6371f7394bd86.png" alt="image-20221114160720604" /></p><h2 id="%E8%AE%BE%E7%BD%AE%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F" tabindex="-1">设置环境变量</h2><pre><code class="language-shell">export DP_Id=&quot;xxx&quot;export DP_Key=&quot;xxxxxxxxxxxxxxx&quot;</code></pre><p>这里不同的DNS服务器 环境变量名不同 查看官方文档：<a href="https://github.com/acmesh-official/acme.sh/wiki/dnsapi" target="_blank">dnsapi · acmesh · GitHub</a></p><h2 id="%E7%94%B3%E8%AF%B7%E8%AF%81%E4%B9%A6" tabindex="-1">申请证书</h2><p>其中<code>dns_dp</code>为 腾讯云DNSPod.服务商,自行根据官方<a href="https://github.com/acmesh-official/acme.sh/wiki/dnsapi" target="_blank">dnsapi</a>修改.例如:<code>dns_ali</code>为阿里云,<code>dns_cf</code>为CloudFlare<br />Acme可以同时申请多个域名.例如拥有域名只需要在每个域名前加上 <code>-d</code>  + <code>空格</code>  +<code>域名</code> 即可.</p><pre><code class="language-shell">~/.acme.sh/acme.sh  --issue --server letsencrypt \--dns dns_dp \-d apesblog.com \-d *.apesblog.com \-d *.frp.apesblog.com</code></pre><h2 id="%E5%AE%89%E8%A3%85%E8%AF%81%E4%B9%A6" tabindex="-1">安装证书</h2><pre><code class="language-shell">~/.acme.sh/acme.sh --installcert -d apesblog.com \--key-file /etc/nginx/conf.d/acme/private.key \--fullchain-file /etc/nginx/conf.d/acme/cert.crt</code></pre><h2 id="%E8%84%9A%E6%9C%AC%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0" tabindex="-1">脚本自动更新</h2><p>Let’s Encrypt 的证书有效期为三个月.为避免证书失效 Acme 会每60天根据你的历史申请记录和部署记录重新续签证书并部署</p><p>由于Acme脚本更新频繁，所以打开自动更新 ，避免续签失败</p><pre><code class="language-shell">~/.acme.sh/acme.sh  --upgrade  --auto-upgrade</code></pre>]]>
                    </description>
                    <pubDate>Mon, 14 Nov 2022 14:27:25 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[【Bug记录】核销单据成功微信打款后 记录一下报错事务回滚了，导致单据没核销，可以一直刷]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/he-xiao-dan-ju-cheng-gong-wei-xin-da-kuan-hou-ji-lu-yi-xia-bao-cuo-shi-wu-hui-gun-le--dao-zhi-dan-ju-mei-he-xiao--ke-yi-yi-zhi-shua</link>
                    <description>
                            <![CDATA[<p>真有我的，还好测试出来了，一直刷钱，有点恐怖</p><p><img src="https://lsky.zhangyushun.com/i/2022/11/17/6375988bbcf11.png" alt="image-20221117101226549" /></p>]]>
                    </description>
                    <pubDate>Wed, 02 Nov 2022 14:24:24 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[【Bug记录】唤醒奖励（被唤醒人重复登录一直送奖励）]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/huan-xing-jiang-li--bei-huan-xing-ren-zhong-fu-deng-lu-yi-zhi-song-jiang-li-</link>
                    <description>
                            <![CDATA[<h2 id="%E9%9C%80%E6%B1%82" tabindex="-1">需求</h2><p>玩家B长时间不登陆游戏，玩家B可以分享链接给玩家A，玩家A点击链接进入游戏，双方便可获取奖励</p><h2 id="bug" tabindex="-1">Bug</h2><p>被唤醒玩家B通过玩家A分享的链接登陆，MQ回调以后，查询是否有唤醒任务时，我没加唤醒任务是否已完成这个条件，导致玩家A一直点链接进入游戏，奖励一直发放</p><p><img src="https://lsky.zhangyushun.com/i/2022/11/15/63733ecf6fe98.png" alt="image-20221115152450615" /></p><p><img src="https://lsky.zhangyushun.com/i/2022/11/15/63733e9d87d0f.png" alt="image-20221115152411725" /></p>]]>
                    </description>
                    <pubDate>Sat, 10 Sep 2022 14:23:42 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[动手实现Tomcat]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/dong-shou-shi-xian-tomcat</link>
                    <description>
                            <![CDATA[<h3 id="简单的实现一个web容器">简单的实现一个Web容器</h3><h4 id="前提">前提</h4><ul><li>Socket</li><li>IO</li></ul><h4 id="思路">思路</h4><ul><li>启动 Socket 服务，循环接收浏览器请求</li><li>接收到请求之后，将流中的数据取出</li><li>判断目标资源是否存在，若不存在，返回 404</li><li>若存在，将目标资源通过输出流响应给客户端</li></ul><h4 id="代码">代码</h4><ol><li><p>Tomcat</p><pre><code class="language-java">package com.apesblog.tomcat;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;/** * @author ZhangYuShun * @since 2022/8/31 */public class Tomcat {    /**     * 一个简单的Web容器     */    public static void main(String[] args) throws IOException {        //定义ServerSocket8080端口        ServerSocket serverSocket = new ServerSocket(8080);        //循环接受请求        while (true) {            Socket socket = serverSocket.accept();            System.out.println(socket);            new Thread(() -&gt; {                try {                    InputStream inputStream = socket.getInputStream();                    Request request = new Request(inputStream);                    //创建Response                    OutputStream outputStream = socket.getOutputStream();                    Response response = new Response(outputStream);                    //进行响应                    response.sendRedirect(request.getUrl());                } catch (Exception e) {                    e.printStackTrace();                }            }).start();        }    }}</code></pre></li><li><p>Request</p><pre><code class="language-java">package com.apesblog.tomcat;import java.io.IOException;import java.io.InputStream;/** * @author ZhangYuShun * @since 2022/8/31 */public class Request {    private String url;    private String Method;    public Request(InputStream inputStream) throws IOException {        /**         * 要一次读取多个字节时，经常用到InputStream.available()方法，这个方法可以在读写操作前先得知数据流里有多少个字节可以读取。需要注意的是，如果这个方法用在从本         * 地文件读取数据时，一般不会遇到问题，但如果是用于网络操作，就经常会遇到一些麻烦。         * 比如，Socket通讯时，对方明明发来了1000个字节，但是自己的程序调用available()方法却只得到900，或者100，甚至是0，感觉有点莫名其妙，怎么也找不到原因。         * 其实，这是因为网络通讯往往是间断性的，一串字节往往分几批进行发送。本地程序调用available()方法有时得到0，         * 这可能是对方还没有响应，也可能是对方已经响应了，但是数据还没有送达本地。对方发送了1000个字节给你，也许分成3批到达，         * 这你就要调用3次available()方法才能将数据总数全部得到。         */        int count = 0;        while (count == 0) {            count = inputStream.available();        }        byte[] bytes = new byte[count];        inputStream.read(bytes);        String content = new String(bytes);        System.out.println(content);        if (&quot;&quot;.equals(content)) {            System.out.println(&quot;空请求&quot;);        } else {            //正则\s 转义\            String firstLine = content.split(&quot;\\n&quot;)[0];            this.setMethod(firstLine.split(&quot;\\s&quot;)[0]);            this.setUrl(firstLine.split(&quot;\\s&quot;)[1]);        }    }    public String getUrl() {        return url;    }    public void setUrl(String url) {        this.url = url;    }    public String getMethod() {        return Method;    }    public void setMethod(String method) {        Method = method;    }}</code></pre></li><li><p>Response</p><pre><code class="language-java">package com.apesblog.tomcat;import java.io.BufferedInputStream;import java.io.File;import java.io.OutputStream;import java.nio.file.Files;public class Response {    private OutputStream outputStream;    public Response(OutputStream outputStream) {        this.outputStream = outputStream;    }    public void sendRedirect(String uri) {        //判断uri是否存在        //不存在返回404        //存在直接返回目标资源数据        File file = new File(Response.class.getResource(&quot;/&quot;).getPath() + uri);        if (file.exists()) {            try {                //返回目标资源数据                BufferedInputStream bufferedInputStream = new BufferedInputStream(Files.newInputStream(file.toPath()));                //存储每次读取的数据                byte[] bytes = new byte[1024];                //记录每次读取的有效字节个数                int len = 0;                //TODO 感觉写的不好 字节转字符再转字节输出   不够优雅                StringBuilder result = new StringBuilder();                while ((len = bufferedInputStream.read(bytes)) != -1) {                    result.append(new String(bytes, 0, len));                }                String response = getResponseMessage(&quot;200&quot;, result.toString());                System.out.println(response);                this.outputStream.write(response.getBytes());            } catch (Exception e) {                e.printStackTrace();            }        } else {            try {                //返回404                String error = getResponseMessage(&quot;404&quot;, &quot;404 File Not Found!&quot;);                this.outputStream.write(error.getBytes());            } catch (Exception e) {                e.printStackTrace();            }        }    }    public String getResponseMessage(String code, String message) {        return &quot;HTTP/1.1 &quot; + code + &quot;\r\n&quot; + &quot;Content-type: text/html\r\n&quot; + &quot;Content-Length: &quot; + message.length() + &quot;\r\n&quot; + &quot;\r\n&quot; + message;    }}</code></pre></li></ol>]]>
                    </description>
                    <pubDate>Wed, 31 Aug 2022 16:18:55 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[【Bug记录】Spring Boot按条件注入]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/springboot-an-tiao-jian-zhu-ru</link>
                    <description>
                            <![CDATA[<p>看了看同事的代码，看来吃过亏，在测试环境把订单推送到订单系统了</p><p><img src="https://lsky.zhangyushun.com/i/2022/11/15/63733b5d32a04.png" alt="image-20221115151003632" /></p><p>不错不错，原来条件注入是这样使用的，之前看<a href="https://www.bilibili.com/video/BV1gW411W7wy/?p=7" target="_blank">组件注册-@Conditional-按照条件注册bean</a>还没有深刻的体会，现在理解了</p>]]>
                    </description>
                    <pubDate>Fri, 12 Aug 2022 14:22:07 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[【Bug记录】聊天功能，两位玩家只能创建一条会话]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/liao-tian-gong-neng--liang-wei-wan-jia-zhi-neng-chuang-jian-yi-tiao-hui-hua</link>
                    <description>
                            <![CDATA[<p>学到了学到了，两个变量之间要加锁的时候，可以先给它们排序，这样只用加一条锁了</p><p>没有return。。。</p><p>原来 <strong>Integer.getInteger()</strong></p><p>是根据指定的名称得到系统属性的整数值。第一个参数将被认为是系统属性的名称。属性值<a href="https://so.csdn.net/so/search?q=%E5%AD%97%E7%AC%A6%E4%B8%B2&amp;spm=1001.2101.3001.7020" target="_blank">字符串</a>将被解释成一个</p><p>整数，并且以表示这个值的Integer对象形式返回。</p><h2 id="%E9%9C%80%E6%B1%82" tabindex="-1">需求</h2><p>聊天功能一共就两张表，一张会话表，一张消息表</p><p>我要保证两位玩家之间的会话只有一条</p><p><img src="https://lsky.zhangyushun.com/i/2022/11/15/6373450521ecf.png" alt="image-20221115155131220" /></p>]]>
                    </description>
                    <pubDate>Mon, 08 Aug 2022 14:24:02 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[动手实现JQuery(选择元素设置属性)]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/dong-shou-shi-xian-jquery-xuan-ze-yuan-su-she-zhi-shu-xing-</link>
                    <description>
                            <![CDATA[<p>大家对于<code>$</code>符号很熟悉，我现在的公司对jq进行了封装， 调用的方法是finedo，这次总结一下jq是如何实现$().css（）的</p><p>代码贴出来了，如果看不懂，那你可能就需要去了解下prototype和proto了，</p><p><a href="https://www.jianshu.com/p/dee9f8b14771" target="_blank">https://www.jianshu.com/p/dee9f8b14771</a></p><p>或者去b站看一下pink老师的视频也行</p><pre><code class="language-js">&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;   &lt;meta charset=&quot;UTF-8&quot;&gt;   &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;   &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;   &lt;title&gt;jQuery&lt;/title&gt;&lt;/head&gt;&lt;body&gt;   &lt;div id=&quot;main&quot;&gt;main&lt;/div&gt;   &lt;button type=&quot;submit&quot; id=&quot;btn_submit&quot; onclick=&quot;submit()&quot;&gt;jQuery&lt;/button&gt;   &lt;script&gt;       var Apesblog = function (dom) {           return new Apesblog.prototype.init(dom);       }       Apesblog.prototype = {           // constructor: Apesblog,           init: function (dom) {               if (dom.indexOf(&quot;#&quot;) == 0) {                   dom = dom.substr(1, dom.length);                   console.log(dom);                   this.elem = document.getElementById(dom);               }               // this.__proto__ = Apesblog.prototype;               console.log(this);           },           css: function (key, value) {               this.elem.style[key] = value;           }       }         Apesblog.prototype.init.prototype =  Apesblog.prototype;       window.apesblog = Apesblog;   &lt;/script&gt;   &lt;script&gt;       function submit(){           apesblog(&quot;#main&quot;).css(&quot;color&quot;, &quot;red&quot;);       }   &lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</code></pre>]]>
                    </description>
                    <pubDate>Mon, 06 Jun 2022 10:24:19 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[分组后取每组前几条记录]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/fen-zu-hou-qu-mei-zu-qian-ji-tiao-ji-lu</link>
                    <description>
                            <![CDATA[<blockquote><p>窗口函数很强大，row_number(顺序排序),rank(跳跃排序),dense_rank(连续排序)</p></blockquote><h2 id="mysql-8.0" tabindex="-1">MYSQL 8.0</h2><pre><code class="language-mysql">CREATE TABLE IF NOT EXISTS  student(   id varchar(20),-- 编号   class varchar(20),-- 班级   stu_name varchar(20),-- 学生姓名   score int-- 分数);</code></pre><h2 id="%E6%8F%92%E5%85%A5%E6%95%B0%E6%8D%AE%3A" tabindex="-1">插入数据:</h2><pre><code class="language-mysql">insert student values(&#39;1&#39;,&#39;classOne&#39;,&#39;Jack&#39;,82);insert student values(&#39;2&#39;,&#39;classOne&#39;,&#39;Jame&#39;,95);insert student values(&#39;3&#39;,&#39;classOne&#39;,&#39;Toney&#39;,82);insert student values(&#39;4&#39;,&#39;classOne&#39;,&#39;Nike&#39;,40);insert student values(&#39;5&#39;,&#39;classOne&#39;,&#39;Ha&#39;,20);insert student values(&#39;6&#39;,&#39;classOne&#39;,&#39;Tom&#39;,95);insert student values(&#39;7&#39;,&#39;classTwo&#39;,&#39;Elik&#39;,40);insert student values(&#39;8&#39;,&#39;classTwo&#39;,&#39;T&#39;,3);insert student values(&#39;9&#39;,&#39;classTwo&#39;,&#39;Kim&#39;,60);insert student values(&#39;10&#39;,&#39;classTwo&#39;,&#39;Tim&#39;,10);insert student values(&#39;11&#39;,&#39;classTwo&#39;,&#39;Li&#39;,95);insert student values(&#39;12&#39;,&#39;classThree&#39;,&#39;C.Ronaldo&#39;,60);insert student values(&#39;13&#39;,&#39;classThree&#39;,&#39;Messi&#39;,40);insert student values(&#39;14&#39;,&#39;classThree&#39;,&#39;Neymar&#39;,90);insert student values(&#39;15&#39;,&#39;classThree&#39;,&#39;Moyi&#39;,20);insert student values(&#39;16&#39;,&#39;classThree&#39;,&#39;Sun&#39;,70);</code></pre><h2 id="%E5%88%86%E7%BB%84%E6%9F%A5%E8%AF%A2%E6%AF%8F%E4%B8%AA%E7%8F%AD%E7%BA%A7%E7%9A%84%E6%9C%80%E9%AB%98%E5%88%86" tabindex="-1"><strong>分组查询每个班级的最高分</strong></h2><pre><code class="language-mysql">SELECTa.* FROMstudent aINNER JOIN ( SELECT max( score ) AS score, class AS class FROM student GROUP BY class ) b ON a.class = b.class AND a.score = b.score SELECT* FROMstudent a WHERE( SELECT count( 1 ) FROM student b WHERE a.class = b.class AND a.score &lt; b.score ) = 0SELECT* FROM( SELECT rank ( ) over ( PARTITION BY class ORDER BY a.score DESC) rownumber, a.* FROM student a ) b WHEREb.rownumber = 1</code></pre>]]>
                    </description>
                    <pubDate>Mon, 15 Nov 2021 10:22:06 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[【Bug记录】null！！！PageParamDomain对象是null。]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/bug-ji-lu-nullpageparamdomain-dui-xiang-shi-null</link>
                    <description>
                            <![CDATA[<h2 id="bug%E7%8E%B0%E8%B1%A1%EF%BC%9A" tabindex="-1">bug现象：</h2><p>分页不正常， 总条数是对的。可显示的都是第一页。</p><h2 id="bug%E5%88%86%E6%9E%90%E6%80%BB%E7%BB%93%EF%BC%9A" tabindex="-1">bug分析总结：</h2><p>很明显公司框架，在判断出我传入pageparam为null时，默认查询limit 条数</p><p>limit 条数 等同于 limit  0 ,条数</p><p>找bug过程很曲折。找到之后我可真是笨比啊。</p>]]>
                    </description>
                    <pubDate>Mon, 30 Aug 2021 10:19:17 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[JdbcTemplate和Spring MVC执行过程]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/jdbctemplate-he-springmvc-zhi-xing-guo-cheng</link>
                    <description>
                            <![CDATA[<blockquote><p>JdbcTemplate</p></blockquote><ul><li>JdbcTemplate是Spring对JDBC的封装，目的是使JDBC更加易于使用，JdbcTemplate类通过模板设计模式帮助我们消除了冗长的代码，只做需要做的事情（即可变部分），并且帮我们做哪些固定部分，如连接的创建及关闭。</li><li>增删改操作不用返回结果集，所以可以抽取成一个方法update()，公司框架传入的形参是update(String sql,Object arg1)，Spring当中是update(String sql, Object… args)。</li><li>批量增加、删除、更新数据调用batchUpdate()方法。批量操作比for循环单次操作效率高。</li><li>查询操作调用query()方法因为查询有返回结果集，所以需要传入domain.class。</li></ul><blockquote><p>SpringMVC的执行流程</p></blockquote><ul><li>1.用户向服务器发送请求，请求被SpringMVC 前端控制器 DispatcherServlet捕获。</li><li>2.DispatcherServlet对请求URL进行解析，得到请求资源标识符（URI），判断请求URI对应的映射。</li><li>3.如果DispatcherServlet没找到对应的映射则交给DefaultServlet处理，都没找到会展示404。</li><li>4.开始执行拦截器的preHandler(…)方法(正向)。</li><li>5.开始执行Controller方法。</li><li>6.异常处理器（项目中没找到配置）。</li><li>7.开始执行拦截器的postHandle(…)方法(逆向 )。</li><li>8.渲染视图完毕执行拦截器的afterCompletion(…)方法(逆向)。</li></ul><p>因为在项目中的web.xml中将前端控制器DispatcherServlet的映射请求设置为“/”,所以需要添加 标签用来相应静态文件。<br />Tomcat容器的web.xml中有一个DefaultServlet(用于处理静态资源)，映射路径是&quot;/“，项目中的web.xml最终相当于会与容器的web.xml合并，而项目DispatchServlet一般也是使用”/&quot;，导致容器中的DefaultServlet被覆盖，从而静态资源请求也会被发送到SpringMVC，SpringMVC会去找这个路径的映射器(相当于对应的Controller，这是找不到的)，配置这个，就是在SpringMVC找不到映射路径后，再将其转给Tomcat的DefaultServlet这是就可以Tomcat就可以正确解析静态资源路径。</p>]]>
                    </description>
                    <pubDate>Thu, 26 Aug 2021 10:17:33 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[IOC全注解和JDK动态代理实现]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/ioc-quan-zhu-jie-he-jdk-dong-tai-dai-li-shi-xian</link>
                    <description>
                            <![CDATA[<h2 id="1.dao%E5%92%8C%E5%AE%9E%E7%8E%B0%E7%B1%BB" tabindex="-1">1.dao和实现类</h2><pre><code class="language-java">package com.apesblog.dao;public interface UserDao {    int add(int a,int b);    int multiply(int a,int b);}package com.apesblog.dao;import org.springframework.stereotype.Repository;@Repositorypublic class UserDaoImpl implements UserDao {    @Override    public int multiply(int a, int b) {        return a * b;    }    @Override    public int add(int a, int b) {        return a + b;    }}</code></pre><h2 id="2.myinvocationhandler" tabindex="-1">2.MyInvocationHandler</h2><pre><code class="language-java">package com.apesblog.dao;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;@Componentpublic class MyInvocationHandler implements InvocationHandler {    @Autowired    UserDao userDao;    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println(&quot;----------&quot;);        Object invoke = method.invoke(userDao, args);        System.out.println(invoke);        System.out.println(&quot;----------&quot;);        return invoke;    }}</code></pre><h2 id="3.%40configuration%E6%88%96%E8%80%85xml" tabindex="-1">3.@Configuration或者xml</h2><pre><code class="language-java">import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScan(basePackages = {&quot;com.apesblog&quot;})public class SpringConfig {}&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;       xmlns:context=&quot;http://www.springframework.org/schema/context&quot;       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;&gt;    &lt;context:component-scan base-package=&quot;com.apesblog&quot;&gt;&lt;/context:component-scan&gt;&lt;/beans&gt;</code></pre><h2 id="4.%E6%B5%8B%E8%AF%95%E7%B1%BB" tabindex="-1">4.测试类</h2><pre><code class="language-java">import com.apesblog.dao.MyInvocationHandler;import com.apesblog.dao.UserDao;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.lang.reflect.Proxy;public class Test1 {    public static void main(String[] args) {        //xml//        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(&quot;bean1.xml&quot;);        //全注解        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);        MyInvocationHandler myInvocationHandler = (MyInvocationHandler) applicationContext.getBean(&quot;myInvocationHandler&quot;);        Class[] interfaces = {UserDao.class};        UserDao o = (UserDao) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), interfaces, myInvocationHandler);        o.add(1, 2);        o.multiply(1, 2);    }}</code></pre>]]>
                    </description>
                    <pubDate>Wed, 25 Aug 2021 10:16:08 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[AspectJ全注解]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/aspectj-quan-zhu-jie</link>
                    <description>
                            <![CDATA[<pre><code class="language-java">package com.apesblog;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration@ComponentScan(basePackages = {&quot;com.apesblog&quot;})@EnableAspectJAutoProxy(proxyTargetClass = true)public class SpringConfig {}package com.apesblog;import org.springframework.stereotype.Component;@Componentpublic class User {     public void add(){         System.out.println(&quot;add&quot;);     }}package com.apesblog;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;@Component@Aspectpublic class UserProxy {    @Before(value = &quot;execution(* com.apesblog.User.*(..))&quot;)    public void before(){        System.out.println(&quot;before&quot;);    }}package com.apesblog;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Test {    public static void main(String[] args) {        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);        User user = applicationContext.getBean(&quot;user&quot;, User.class);        user.add();    }}</code></pre>]]>
                    </description>
                    <pubDate>Wed, 25 Aug 2021 10:15:06 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[Spring IOC作用域和生命周期（xml）]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/springioc-zuo-yong-yu-he-sheng-ming-zhou-qi-xml</link>
                    <description>
                            <![CDATA[<p>首先单例和多例的结果不一样，因为ClassPathXmlApplicationContext如果是单例的话是在一开始运行服务器的时候就建好对象，而多例则是getBean时再建；</p><h2 id="1.mybeanpost%E5%90%8E%E7%BD%AE%E5%A4%84%E7%90%86%E5%99%A8" tabindex="-1">1.MyBeanPost后置处理器</h2><pre><code class="language-java">package com.apesblog.demo;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPost implements BeanPostProcessor {    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        System.out.println(&quot;postProcessBeforeInitialization&quot;);        return null;    }    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        System.out.println(&quot;postProcessAfterInitialization&quot;);        return null;    }}</code></pre><h2 id="2.user%E7%B1%BB" tabindex="-1">2.User类</h2><pre><code class="language-java">package com.apesblog.demo;public class User {    String name;    public User(String name) {        this.name = name;    }    public User() {        System.out.println(&quot;执行无参构造&quot;);    }    public void setName(String name) {        this.name = name;        System.out.println(&quot;执行set方法&quot;);    }    public void initMethod(){        System.out.println(&quot;initMethod&quot;);    }    public void destroyMethod(){        System.out.println(&quot;destroyMethod&quot;);    }}</code></pre><h2 id="3.%E6%B5%8B%E8%AF%95%E7%B1%BB" tabindex="-1">3.测试类</h2><pre><code class="language-java">package com.apesblog.demo.Test1;import com.apesblog.demo.User;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test1 {    @Test    public void test1(){        System.out.println(&quot;启动服务器&quot;);        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(&quot;bean1.xml&quot;);        System.out.println(&quot;-----------&quot;);        User user = classPathXmlApplicationContext.getBean(&quot;user&quot;, User.class);        System.out.println(user);        System.out.println(&quot;-----------&quot;);        User user1 = classPathXmlApplicationContext.getBean(&quot;user&quot;, User.class);        System.out.println(user1);        classPathXmlApplicationContext.close();    }}</code></pre><h2 id="%E7%BB%93%E6%9E%9C%EF%BC%88%E5%8D%95%E4%BE%8Bxml%EF%BC%89" tabindex="-1">结果（单例xml）</h2><pre><code class="language-java">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;    &lt;bean id=&quot;user&quot; class=&quot;com.apesblog.demo.User&quot; init-method=&quot;initMethod&quot; destroy-method=&quot;destroyMethod&quot; scope=&quot;singleton&quot;&gt;        &lt;property name=&quot;name&quot; value=&quot;apesblog&quot;&gt;&lt;/property&gt;    &lt;/bean&gt;    &lt;bean id=&quot;mybeanpost&quot; class=&quot;com.apesblog.demo.MyBeanPost&quot;&gt;&lt;/bean&gt;&lt;/beans&gt;</code></pre><h2 id="%E7%BB%93%E6%9E%9C%EF%BC%88%E5%A4%9A%E4%BE%8Bxml%EF%BC%89" tabindex="-1">结果（多例xml）</h2><pre><code class="language-java">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;&lt;bean id=&quot;user&quot; class=&quot;com.apesblog.demo.User&quot; init-method=&quot;initMethod&quot; destroy-method=&quot;destroyMethod&quot; scope=&quot;prototype&quot;&gt;    &lt;property name=&quot;name&quot; value=&quot;apesblog&quot;&gt;&lt;/property&gt;&lt;/bean&gt;&lt;bean id=&quot;mybeanpost&quot; class=&quot;com.apesblog.demo.MyBeanPost&quot;&gt;&lt;/bean&gt;&lt;/beans&gt;</code></pre><h2 id="%E6%80%BB%E7%BB%93%EF%BC%9A" tabindex="-1">总结：</h2><pre><code class="language-javaa">ClassPathXmlApplicationContext类会在启动服务器的时候创建好Bean，BeanFactory会在getBean时才创建Bean，但是ClassPathXmlApplicationContext提前创建可能运行时性能上快一点。</code></pre><h3 id="%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E4%B8%8B%EF%BC%9A" tabindex="-1">单例模式下：</h3><p>Bean是在启动服务器就创建好的，每次执行getBean返回同一个引用就行，最后再执行destroyMethod方法。</p><h3 id="%E5%A4%9A%E4%BE%8B%E6%A8%A1%E5%BC%8F%E4%B8%8B%EF%BC%9A" tabindex="-1">多例模式下：</h3><p>Bean是在每次执行getBean才创建，但是destroyMethod方法为啥不执行，目前不清楚。</p><h2 id="%E8%A1%A5%E5%85%85%EF%BC%9A" tabindex="-1">补充：</h2><ol><li><strong>多实例的时候bean不会随着IOC容器的构建而创建，而是在使用的时候创建的getBean()</strong></li><li><strong>多实例的时候，当容器进行关闭的时候，bean实例不会调用destroy方法，说明容器不控制多实例的销毁</strong></li><li><strong>多实例的情况下，返回的bean的对象是不一样的</strong></li></ol>]]>
                    </description>
                    <pubDate>Wed, 25 Aug 2021 10:13:36 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[封装JDBCUtil]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/feng-zhuang-jdbcutil</link>
                    <description>
                            <![CDATA[<h2 id="jdbcutils%E5%A6%82%E4%B8%8B%EF%BC%9A" tabindex="-1">JDBCUtils如下：</h2><pre><code class="language-java">package com.apesblog.jdbcutil1.util;import java.io.InputStream;import java.lang.reflect.Field;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.ResultSetMetaData;import java.sql.SQLException;import java.sql.Statement;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.List;import java.util.Properties;import java.util.Set;import com.apesblog.jdbcutil1.bean.Order;public class JDBCUtils {    private static Connection getConnection() throws Exception {        // 1.读取配置文件中的4个基本信息        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(&quot;jdbc.properties&quot;);        Properties pros = new Properties();        pros.load(is);        String user = pros.getProperty(&quot;user&quot;);        String password = pros.getProperty(&quot;password&quot;);        String url = pros.getProperty(&quot;url&quot;);        String driverClass = pros.getProperty(&quot;driverClass&quot;);        // 2.加载驱动        Class.forName(driverClass);        // 3.获取连接        Connection conn = DriverManager.getConnection(url, user, password);        return conn;    }    private static void closeResource(Connection conn, Statement ps) {        try {            if (ps != null)                ps.close();        } catch (SQLException e) {            e.printStackTrace();        }        try {            if (conn != null)                conn.close();        } catch (SQLException e) {            e.printStackTrace();        }    }    private static void closeResource(Connection conn, Statement ps, ResultSet rs) {        try {            if (ps != null)                ps.close();        } catch (SQLException e) {            e.printStackTrace();        }        try {            if (conn != null)                conn.close();        } catch (SQLException e) {            e.printStackTrace();        }        try {            if (rs != null)                rs.close();        } catch (SQLException e) {            e.printStackTrace();        }    }    public static void update(String sql, Object... args) {// sql中占位符的个数与可变形参的长度相同！        Connection conn = null;        PreparedStatement ps = null;        try {            // 1.获取数据库的连接            conn = JDBCUtils.getConnection();            // 2.预编译sql语句，返回PreparedStatement的实例            ps = conn.prepareStatement(sql);            // 3.填充占位符            for (int i = 0; i &lt; args.length; i++) {                ps.setObject(i + 1, args[i]);// 小心参数声明错误！！            }            // 4.执行            ps.execute();        } catch (Exception e) {            e.printStackTrace();        } finally {            // 5.资源的关闭            JDBCUtils.closeResource(conn, ps);        }    }    public static &lt;T&gt; List&lt;T&gt; query(Class&lt;T&gt; clazz, String sql, Object... args) {        Connection conn = null;        PreparedStatement ps = null;        ResultSet rs = null;        try {            conn = JDBCUtils.getConnection();            ps = conn.prepareStatement(sql);            for (int i = 0; i &lt; args.length; i++) {                ps.setObject(i + 1, args[i]);            }            rs = ps.executeQuery();            // 获取结果集的元数据 :ResultSetMetaData            ResultSetMetaData rsmd = rs.getMetaData();            // 通过ResultSetMetaData获取结果集中的列数            int columnCount = rsmd.getColumnCount();            // 创建集合对象            ArrayList&lt;T&gt; list = new ArrayList&lt;T&gt;();            while (rs.next()) {                T t = clazz.newInstance();                // 处理结果集一行数据中的每一个列:给t对象指定的属性赋值                for (int i = 0; i &lt; columnCount; i++) {                    // 获取列值                    Object columValue = rs.getObject(i + 1);                    // 获取每个列的列名                    // String columnName = rsmd.getColumnName(i + 1);                    String columnLabel = rsmd.getColumnLabel(i + 1);                    // 给t对象指定的columnName属性，赋值为columValue：通过反射                    Field field = clazz.getDeclaredField(columnLabel);                    field.setAccessible(true);                    field.set(t, columValue);                }                list.add(t);            }            return list;        } catch (Exception e) {            e.printStackTrace();        } finally {            JDBCUtils.closeResource(conn, ps, rs);        }        return null;    }    public static void updateDomain(String sql, Object object) {        Connection conn = null;        PreparedStatement ps = null;        Class&lt;? extends Object&gt; clazz = object.getClass();        Field[] fields = clazz.getDeclaredFields();        HashMap&lt;Integer, String&gt; hashMap = new HashMap&lt;Integer, String&gt;();        for (int i = 0; i &lt; fields.length; i++) {            String lowerCase = fields[i].getName().toLowerCase();            int indexOf = sql.indexOf(lowerCase);            if (indexOf == -1)                continue;            hashMap.put(indexOf, fields[i].getName());        }        try {            // 1.获取数据库的连接            conn = JDBCUtils.getConnection();            // 2.预编译sql语句，返回PreparedStatement的实例            ps = conn.prepareStatement(sql);            // 3.填充占位符            int size = hashMap.size();            for (int i = 0; i &lt; size; i++) {// --for() begin                Integer minKey = (Integer) getMinKey(hashMap);                String string = hashMap.get(minKey);                Field field = clazz.getDeclaredField(string);                field.setAccessible(true);                Object value = field.get(object);                if (value == null)                    continue;                ps.setObject(i + 1, value);// 小心参数声明错误！！                hashMap.remove(minKey);            }            // 4.执行            ps.execute();        } catch (Exception e) {            e.printStackTrace();        } finally {            // 5.资源的关闭            JDBCUtils.closeResource(conn, ps);        }    }    public static &lt;T&gt; List&lt;T&gt; queryDomain(Class&lt;T&gt; clazz, String sql, Object object) {        Connection conn = null;        PreparedStatement ps = null;        ResultSet rs = null;        Class&lt;? extends Object&gt; objclazz = object.getClass();        Field[] fields = objclazz.getDeclaredFields();        HashMap&lt;Integer, String&gt; hashMap = new HashMap&lt;Integer, String&gt;();        for (int i = 0; i &lt; fields.length; i++) {            String lowerCase = fields[i].getName().toLowerCase();            int indexOf;            if (sql.indexOf(&quot;from&quot;) == -1) {                indexOf = sql.substring(sql.indexOf(&quot;FROM&quot;)).indexOf(lowerCase);            } else {                indexOf = sql.substring(sql.indexOf(&quot;from&quot;)).indexOf(lowerCase);            }            if (indexOf == -1)                continue;            hashMap.put(indexOf, fields[i].getName());        }        try {            conn = JDBCUtils.getConnection();            ps = conn.prepareStatement(sql);            int size = hashMap.size();            for (int i = 0; i &lt; size; i++) {// --for() begin                Integer minKey = (Integer) getMinKey(hashMap);                String string = hashMap.get(minKey);                Field field = clazz.getDeclaredField(string);                field.setAccessible(true);                Object value = field.get(object);                if (value == null)                    continue;                ps.setObject(i + 1, value);// 小心参数声明错误！！                hashMap.remove(minKey);            }            rs = ps.executeQuery();            // 获取结果集的元数据 :ResultSetMetaData            ResultSetMetaData rsmd = rs.getMetaData();            // 通过ResultSetMetaData获取结果集中的列数            int columnCount = rsmd.getColumnCount();            // 创建集合对象            ArrayList&lt;T&gt; list = new ArrayList&lt;T&gt;();            while (rs.next()) {                T t = clazz.newInstance();                // 处理结果集一行数据中的每一个列:给t对象指定的属性赋值                for (int i = 0; i &lt; columnCount; i++) {                    // 获取列值                    Object columValue = rs.getObject(i + 1);                    // 获取每个列的列名                    // String columnName = rsmd.getColumnName(i + 1);                    String columnLabel = rsmd.getColumnLabel(i + 1);                    // 给t对象指定的columnName属性，赋值为columValue：通过反射                    Field field = clazz.getDeclaredField(columnLabel);                    field.setAccessible(true);                    field.set(t, columValue);                }                list.add(t);            }            return list;        } catch (Exception e) {            e.printStackTrace();        } finally {            JDBCUtils.closeResource(conn, ps, rs);        }        return null;    }    private static Object getMinKey(HashMap&lt;Integer, String&gt; hashMap) {        if (hashMap == null)            return null;        Set&lt;Integer&gt; set = hashMap.keySet();        Object[] obj = set.toArray();        Arrays.sort(obj);        return obj[0];    }}</code></pre><h2 id="%E6%B5%8B%E8%AF%95%E7%B1%BB%E5%A6%82%E4%B8%8B%EF%BC%9A" tabindex="-1">测试类如下：</h2><pre><code class="language-java">package com.apesblog.jdbcutil1.util;import java.util.List;import org.junit.Test;import org.junit.internal.requests.OrderingRequest;import com.apesblog.jdbcutil1.bean.Customer;import com.apesblog.jdbcutil1.bean.Order;public class Test1 {    @Test    public void testUpdateDoamin() {        String sql = &quot;delete from customers where id = ?&quot;;        Customer customer = new Customer();        customer.setId(4);        JDBCUtils.updateDomain(sql, customer);        sql = &quot;update &#96;order&#96; set ordername = ? where orderid = ?&quot;;        Order order = new Order();        order.setOrderName(&quot;zzzz&quot;);        order.setOrderId(1);        JDBCUtils.updateDomain(sql, order);    }    @Test    public void testQueryDomain() {        String sql = &quot;select id,name,email from customers where id &lt; ?&quot;;        Customer customer = new Customer();        customer.setId(12);        List&lt;Customer&gt; list = JDBCUtils.queryDomain(Customer.class, sql, customer);        list.forEach(System.out::println);        String sql1 = &quot;select orderid orderId,ordername orderName from &#96;order&#96;&quot;;        List&lt;Order&gt; orderList = JDBCUtils.queryDomain(Order.class, sql1, new Order());        orderList.forEach(System.out::println);    }    @Test    public void testUpdate() {//      String sql = &quot;delete from customers where id = ?&quot;;//      update(sql,3);        String sql = &quot;update &#96;order&#96; set ordername = ? where orderid = ?&quot;;        JDBCUtils.update(sql, &quot;zys&quot;, &quot;2&quot;);    }    @Test    public void testQuery() {        String sql = &quot;select id,name,email from customers where id &lt; ?&quot;;        List&lt;Customer&gt; list = JDBCUtils.query(Customer.class, sql, 12);        list.forEach(System.out::println);        String sql1 = &quot;select orderid orderId,ordername orderName from &#96;order&#96;&quot;;        List&lt;Order&gt; orderList = JDBCUtils.query(Order.class, sql1);        orderList.forEach(System.out::println);    }}</code></pre><h2 id="%E6%80%BB%E7%BB%93%EF%BC%9A" tabindex="-1">总结：</h2><h3 id="1.0" tabindex="-1">1.0</h3><p>增删改没有结果集，封装成一个方法，但是传的是具体的值，不是domain，公司框架传的是domain，那是如何获取值得呢？</p><p>查询操作因为需要返回不同的domainList，所以需要传入domain.class</p><h3 id="2.0" tabindex="-1">2.0</h3><p>新增updateDomain通过反射获取属性</p><h3 id="3.0" tabindex="-1">3.0</h3><p>新增queryDomain，完成告一段落</p>]]>
                    </description>
                    <pubDate>Thu, 19 Aug 2021 10:11:49 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[Java Socket Tcp]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/javasockettcp</link>
                    <description>
                            <![CDATA[<pre><code class="language-java">package com.apesblog.day_27;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;import java.net.UnknownHostException;import org.junit.jupiter.api.Test;public class TcpTest {    @Test    public void client() {        Socket socket = null;        OutputStream outputStream = null;        try {            socket = new Socket(InetAddress.getByName(&quot;127.0.0.1&quot;), 4399);            outputStream = socket.getOutputStream();            outputStream.write(&quot;您好，tcp&quot;.getBytes());        } catch (UnknownHostException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            try {                if (outputStream == null)                    outputStream.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            try {                if (socket == null)                    socket.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }    @Test    public void server() {        ServerSocket serverSocket = null;        Socket accept = null;        InputStream inputStream = null;        ByteArrayOutputStream byteArrayOutputStream = null;        try {            serverSocket = new ServerSocket(4399);            accept = serverSocket.accept();            inputStream = accept.getInputStream();            byteArrayOutputStream = new ByteArrayOutputStream();            byte[] buffer = new byte[5];            int len;            while ((len = inputStream.read(buffer)) != -1) {                byteArrayOutputStream.write(buffer, 0, len);            }            System.out.println(byteArrayOutputStream.toString());        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        try {            byteArrayOutputStream.close();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            try {                if (byteArrayOutputStream == null)                    byteArrayOutputStream.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            try {                if (inputStream == null)                    inputStream.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            try {                if (accept == null)                    accept.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            try {                if (serverSocket == null)                    serverSocket.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }}</code></pre>]]>
                    </description>
                    <pubDate>Wed, 18 Aug 2021 10:10:51 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[测试缓冲流和节点流速度]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/ce-shi-huan-chong-liu-he-jie-dian-liu-su-du</link>
                    <description>
                            <![CDATA[<pre><code class="language-java">package com.apesblog.day_26;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class Test6 {    public static void main(String[] args) {        long start = System.currentTimeMillis();        bufferTest(&quot;src/com/apesblog/day_26/01.zip&quot;, &quot;src/com/apesblog/day_26/buffercopy.zip&quot;);        long end = System.currentTimeMillis();        System.out.println(end - start);        start = System.currentTimeMillis();        fileTest(&quot;src/com/apesblog/day_26/01.zip&quot;, &quot;src/com/apesblog/day_26/filecopy.zip&quot;);        end = System.currentTimeMillis();        System.out.println(end - start);    }    public static void bufferTest(String sfile, String scopy) {        BufferedInputStream bufferInputStream = null;        BufferedOutputStream bufferedOutputStream = null;        try {            byte[] buffer = new byte[1024];            int len;            bufferInputStream = new BufferedInputStream(new FileInputStream(new File(sfile)));            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(new File(scopy)));            while ((len = bufferInputStream.read(buffer)) != -1) {                bufferedOutputStream.write(buffer, 0, len);            }        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (bufferInputStream != null)                    bufferInputStream.close();            } catch (IOException e) {                e.printStackTrace();            }            try {                if (bufferedOutputStream != null)                    bufferedOutputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    public static void fileTest(String sfile, String scopy) {        FileInputStream fileInputStream = null;        FileOutputStream fileOutputStream = null;        try {            byte[] buffer = new byte[1024];            int len;            fileInputStream = new FileInputStream(sfile);            fileOutputStream = new FileOutputStream(scopy);            while ((len = fileInputStream.read(buffer)) != -1) {                fileOutputStream.write(buffer, 0, len);            }        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (fileInputStream != null)                    fileInputStream.close();            } catch (IOException e) {                e.printStackTrace();            }            try {                if (fileOutputStream != null)                    fileOutputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}</code></pre>]]>
                    </description>
                    <pubDate>Tue, 17 Aug 2021 10:09:55 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[I/O(BufferedInputStream和BufferedOutputStream实现非文本文件的复制)]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/iobufferedinputstream-he-bufferedoutputstream-shi-xian-fei-wen-ben-wen-jian-de-fu-zhi-</link>
                    <description>
                            <![CDATA[<p>关闭外层处理流（bufferInputStream、bufferedOutputStream），内层节点流（fileInputStream、fileOutputStream）也自动关闭。</p><pre><code class="language-java">package com.apesblog.day_26;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class Test5 {    public static void main(String[] args) {        FileInputStream fileInputStream = null;        FileOutputStream fileOutputStream = null;        BufferedInputStream bufferInputStream = null;        BufferedOutputStream bufferedOutputStream = null;        try {            File file = new File(&quot;src/com/apesblog/day_26/1.png&quot;);            File file2 = new File(file.getParent(), &quot;copy.png&quot;);            byte[] buffer = new byte[1024];            int len;            fileInputStream = new FileInputStream(file);            fileOutputStream = new FileOutputStream(file2);            bufferInputStream = new BufferedInputStream(fileInputStream);            bufferedOutputStream = new BufferedOutputStream(fileOutputStream);            while ((len = bufferInputStream.read(buffer)) != -1) {                bufferedOutputStream.write(buffer, 0, len);            }        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (bufferInputStream != null)                    bufferInputStream.close();            } catch (IOException e) {                e.printStackTrace();            }            try {                if (bufferedOutputStream != null)                    bufferedOutputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}</code></pre>]]>
                    </description>
                    <pubDate>Tue, 17 Aug 2021 10:06:00 CST</pubDate>
                </item>
                <item>
                    <title>
                        <![CDATA[I/O(FileInputStream 和FileOutputStream实现图片复制)]]>
                    </title>
                    <link>https://www.zhangyushun.com/archives/iofileinputstream-he-fileoutputstream-shi-xian-tu-pian-fu-zhi-</link>
                    <description>
                            <![CDATA[<pre><code class="language-java">package com.apesblog.day_26;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class Test4 {    public static void main(String[] args) {        FileInputStream fileInputStream = null;        FileOutputStream fileOutputStream = null;        try {            File file = new File(&quot;src/com/apesblog/day_26/1.png&quot;);            File file2 = new File(file.getParent(), &quot;copy.png&quot;);            byte[] buffer = new byte[1024];            int len;            fileInputStream = new FileInputStream(file);            fileOutputStream = new FileOutputStream(file2);            while ((len = fileInputStream.read(buffer)) != -1) {                fileOutputStream.write(buffer, 0, len);            }        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (fileInputStream != null)                    fileInputStream.close();            } catch (IOException e) {                e.printStackTrace();            }            try {                if (fileOutputStream != null)                    fileOutputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}</code></pre>]]>
                    </description>
                    <pubDate>Mon, 16 Aug 2021 10:01:15 CST</pubDate>
                </item>
    </channel>
</rss>