<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Rosy&#39;s</title>
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://yoursite.com/"/>
  <updated>2019-04-10T11:33:40.000Z</updated>
  <id>http://yoursite.com/</id>
  
  <author>
    <name>Rosy</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>DSL检索语言</title>
    <link href="http://yoursite.com/2019/04/10/DSL%E6%A3%80%E7%B4%A2%E8%AF%AD%E8%A8%80/"/>
    <id>http://yoursite.com/2019/04/10/DSL检索语言/</id>
    <published>2019-04-10T11:16:44.000Z</published>
    <updated>2019-04-10T11:33:40.000Z</updated>
    
    <content type="html"><![CDATA[<p>ES中的聚合被分为两大类：Metric度量和bucket桶。metric很像SQL中的avg、max、min等方法，而bucket就有点类似group by。</p>
<h2 id="1-单值聚合"><a href="#1-单值聚合" class="headerlink" title="1. 单值聚合"></a>1. 单值聚合</h2><ul>
<li><p>求和：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">&quot;aggs&quot; : &#123;</div><div class="line">       &quot;intraday_return&quot; : &#123; &quot;sum&quot; : &#123; &quot;field&quot; : &quot;change&quot; &#125; &#125;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
</li>
<li><p>求最大值、最小值和平均值</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;min_price&quot; : &#123; &quot;min&quot; : &#123; &quot;field&quot; : &quot;price&quot; &#125; &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;max_price&quot; : &#123; &quot;max&quot; : &#123; &quot;field&quot; : &quot;price&quot; &#125; &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;avg_grade&quot; : &#123; &quot;avg&quot; : &#123; &quot;field&quot; : &quot;grade&quot; &#125; &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>求唯一值，即不重复的字段有多少</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;author_count&quot; : &#123;</div><div class="line">            &quot;cardinality&quot; : &#123;</div><div class="line">                &quot;field&quot; : &quot;author&quot;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="2-多值聚合"><a href="#2-多值聚合" class="headerlink" title="2. 多值聚合"></a>2. 多值聚合</h2><ul>
<li>求百分比</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;load_time_outlier&quot; : &#123;</div><div class="line">            &quot;percentile_ranks&quot; : &#123;</div><div class="line">                &quot;field&quot; : &quot;load_time&quot;, </div><div class="line">                &quot;values&quot; : [15, 30]</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>stats统计</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;grades_stats&quot; : &#123; &quot;stats&quot; : &#123; &quot;field&quot; : &quot;grade&quot; &#125; &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>扩展统计</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;grades_stats&quot; : &#123; &quot;extended_stats&quot; : &#123; &quot;field&quot; : &quot;grade&quot; &#125; &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="3-terms聚合"><a href="#3-terms聚合" class="headerlink" title="3. terms聚合"></a>3. terms聚合</h2><p>terms聚合，它是按照某个字段中的值来分类：比如性别有男、女，就会创建两个桶，分别存放男女的信息。默认会搜集doc_count的信息，即记录有多少男生，有多少女生，然后返回给客户端，这样就完成了一个terms得统计</p>
<p>Bucket可以理解为一个桶，他会遍历文档中的内容，凡是符合要求的就放入按照要求创建的桶中。</p>
<ul>
<li>terms聚合</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;genders&quot; : &#123;</div><div class="line">            &quot;terms&quot; : &#123; &quot;field&quot; : &quot;gender&quot; &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>order排序</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;genders&quot; : &#123;</div><div class="line">            &quot;terms&quot; : &#123;</div><div class="line">                &quot;field&quot; : &quot;gender&quot;,</div><div class="line">                &quot;order&quot; : &#123; &quot;_count&quot; : &quot;asc&quot; &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>也可以通过order指定一个单值的metric聚合，来排序。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;genders&quot; : &#123;</div><div class="line">            &quot;terms&quot; : &#123;</div><div class="line">                &quot;field&quot; : &quot;gender&quot;,</div><div class="line">                &quot;order&quot; : &#123; &quot;avg_height&quot; : &quot;desc&quot; &#125;</div><div class="line">            &#125;,</div><div class="line">            &quot;aggs&quot; : &#123;</div><div class="line">                &quot;avg_height&quot; : &#123; &quot;avg&quot; : &#123; &quot;field&quot; : &quot;height&quot; &#125; &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>min_doc_count与shard_min_doc_count</li>
</ul>
<p>聚合的字段可能存在一些频率很低的词条，如果这些词条数目比例很大，那么就会造成很多不必要的计算。<br>因此可以通过设置min_doc_count和shard_min_doc_count来规定最小的文档数目，只有满足这个参数要求的个数的词条才会被记录返回。</p>
<ul>
<li>filter</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;tags&quot; : &#123;</div><div class="line">            &quot;terms&quot; : &#123;</div><div class="line">                &quot;field&quot; : &quot;tags&quot;,</div><div class="line">                &quot;include&quot; : &quot;.*sport.*&quot;,</div><div class="line">                &quot;exclude&quot; : &quot;water_.*&quot;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="4-Histogram-直方图聚合"><a href="#4-Histogram-直方图聚合" class="headerlink" title="4. Histogram 直方图聚合"></a>4. Histogram 直方图聚合</h2><p>Elasticsearch支持直方图聚合，它在数字字段自动创建桶，并会扫描全部文档，把文档放入相应的桶中。这个数字字段既可以是文档中的某个字段，也可以通过脚本创建得出的。举个例子，有一个price字段，这个字段描述了商品的价格，现在想每隔5就创建一个桶，统计每隔区间都有多少个文档（商品）。如果有一个商品的价格为32，那么它会被放入30的桶中。</p>
<ul>
<li>min_doc_count过滤</li>
</ul>
<p>如果不想要显示count为0的桶，可以通过min_doc_count来设置。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;prices&quot; : &#123;</div><div class="line">            &quot;histogram&quot; : &#123;</div><div class="line">                &quot;field&quot; : &quot;price&quot;,</div><div class="line">                &quot;interval&quot; : 50,</div><div class="line">                &quot;min_doc_count&quot; : 1</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>extend_bounds</li>
</ul>
<p>指定最小值和最大值边界.默认情况下，ES中的histogram聚合起始都是自动的，比如price字段，如果没有商品的价钱在0-5之间，0这个桶就不会显示。如果最便宜的商品是11，那么第一个桶就是10.<br>可以通过设置extend_bounds强制规定最小值和最大值，但是要求必须min_doc_count不能大于0，不然即便是规定了边界，也不会返回。</p>
<ul>
<li>order排序</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot; : &#123;</div><div class="line">        &quot;prices&quot; : &#123;</div><div class="line">            &quot;histogram&quot; : &#123;</div><div class="line">                &quot;field&quot; : &quot;price&quot;,</div><div class="line">                &quot;interval&quot; : 50,</div><div class="line">                &quot;order&quot; : &#123; &quot;_key&quot; : &quot;desc&quot; &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="5-Date-Histogram聚合"><a href="#5-Date-Histogram聚合" class="headerlink" title="5. Date Histogram聚合"></a>5. Date Histogram聚合</h2><p>Date histogram的用法与histogram差不多，只不过区间上支持了日期的表达式。interval字段支持多种关键字：<code>year</code>, <code>quarter</code>, <code>month</code>, <code>week</code>, <code>day</code>, <code>hour</code>, <code>minute</code>, <code>second</code>。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot;:&#123;</div><div class="line">        &quot;articles_over_time&quot;:&#123;</div><div class="line">            &quot;date_histogram&quot;:&#123;</div><div class="line">                &quot;field&quot;:&quot;date&quot;,</div><div class="line">                &quot;interval&quot;:&quot;1M&quot;,</div><div class="line">                &quot;format&quot;:&quot;yyyy-MM-dd&quot;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<ul>
<li>time_zone时区</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot;:&#123;</div><div class="line">        &quot;by_day&quot;:&#123;</div><div class="line">            &quot;date_histogram&quot;:&#123;</div><div class="line">                &quot;field&quot;:&quot;date&quot;,</div><div class="line">                &quot;interval&quot;:&quot;day&quot;,</div><div class="line">                &quot;time_zone&quot;:&quot;+08:00&quot;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>offset 使用偏移值，改变时间区间</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">&#123;&quot;aggs&quot;:&#123;</div><div class="line">    &quot;by_day&quot;:&#123;</div><div class="line">        &quot;date_histogram&quot;:&#123;</div><div class="line">            &quot;field&quot;:&quot;date&quot;,</div><div class="line">            &quot;interval&quot;:&quot;day&quot;,</div><div class="line">            &quot;offset&quot;:&quot;+6h&quot;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="6-Range区间聚合"><a href="#6-Range区间聚合" class="headerlink" title="6. Range区间聚合"></a>6. Range区间聚合</h2><ul>
<li>聚合</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot;:&#123;</div><div class="line">        &quot;grade_ranges&quot;:&#123;</div><div class="line">            &quot;range&quot;:&#123;</div><div class="line">                &quot;field&quot;:&quot;grade&quot;,</div><div class="line">                &quot;ranges&quot;:[</div><div class="line">                    &#123;&quot;to&quot;:60&#125;,</div><div class="line">                    &#123;&quot;from&quot;:60,&quot;to&quot;:80&#125;,</div><div class="line">                    &#123;&quot;from&quot;:80&#125;]</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>聚合嵌套</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot;:&#123;</div><div class="line">        &quot;price_ranges&quot;:&#123;</div><div class="line">            &quot;range&quot;:&#123;</div><div class="line">                &quot;field&quot;:&quot;price&quot;,</div><div class="line">                &quot;ranges&quot;:[</div><div class="line">                    &#123;&quot;to&quot;:50&#125;,</div><div class="line">                    &#123;&quot;from&quot;:50,&quot;to&quot;:100&#125;,</div><div class="line">                    &#123;&quot;from&quot;:100&#125;</div><div class="line">                ]&#125;,</div><div class="line">                &quot;aggs&quot;:&#123;</div><div class="line">                    &quot;price_stats&quot;:&#123;</div><div class="line">                        &quot;stats&quot;:&#123;</div><div class="line">                            &quot;field&quot;:&quot;price&quot;</div><div class="line">                        &#125;</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<h2 id="7-DateRange日期范围聚合"><a href="#7-DateRange日期范围聚合" class="headerlink" title="7. DateRange日期范围聚合"></a>7. DateRange日期范围聚合</h2><p>相比于range聚合，date range就是范围可以由时间来指定。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;aggs&quot;:&#123;</div><div class="line">        &quot;range&quot;:&#123;</div><div class="line">            &quot;date_range&quot;:&#123;</div><div class="line">                &quot;field&quot;:&quot;date&quot;,</div><div class="line">                &quot;format&quot;:&quot;MM-yyy&quot;,</div><div class="line">                &quot;ranges&quot;:[</div><div class="line">                    &#123;&quot;to&quot;:&quot;now-10M/M&quot;&#125;,</div><div class="line">                    &#123;&quot;from&quot;:&quot;now-10M/M&quot;&#125;</div><div class="line">                ]</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="8-查询的种类"><a href="#8-查询的种类" class="headerlink" title="8. 查询的种类"></a>8. 查询的种类</h2><ul>
<li>Elasticsearch中的DSL主要由两部分组成：</li>
</ul>
<p>Leaf query Cluase 叶查询子句</p>
<p>这种查询可以单独使用，针对某一特定的字段查询特定的值，比如match、term、range等</p>
<p>Compound query Cluase 复合查询子句</p>
<p>这种查询配合其他的叶查询或者复合查询，用于在逻辑上，组成更为复杂的查询，比如bool</p>
<ul>
<li>Query与Filter</li>
</ul>
<p>查询在Query查询上下文和Filter过滤器上下文中，执行的操作是不一样的：</p>
<p>查询上下文：</p>
<p>在查询上下文中，查询会回答这个问题——“这个文档匹不匹配这个查询，它的相关度高么？”</p>
<p>如何验证匹配很好理解，如何计算相关度呢？之前说过，ES中索引的数据都会存储一个_score分值，分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂的，因此也需要一定的时间。</p>
<p>查询上下文 是在 使用query进行查询时的执行环境，比如使用search的时候。</p>
<p>过滤器上下文：</p>
<p>在过滤器上下文中，查询会回答这个问题——“这个文档匹不匹配？”</p>
<p>答案很简单，是或者不是。它不会去计算任何分值，也不会关心返回的排序问题，因此效率会高一点。</p>
<p>过滤上下文 是在使用filter参数时候的执行环境，比如在bool查询中使用Must_not或者filter</p>
<h2 id="8-布尔查询Bool-Query"><a href="#8-布尔查询Bool-Query" class="headerlink" title="8. 布尔查询Bool Query"></a>8. 布尔查询Bool Query</h2><p>query的时候，会先比较查询条件，然后计算分值，最后返回文档结果；</p>
<p>而filter则是先判断是否满足查询条件，如果不满足，会缓存查询过程（记录该文档不满足结果）；满足的话，就直接缓存结果。</p>
<p>综上所述，filter快在两个方面：</p>
<p>1 对结果进行缓存</p>
<p>2 避免计算分值</p>
<p>bool查询也是采用more_matches_is_better的机制，因此满足must和should子句的文档将会合并起来计算分值。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;bool&quot; : &#123;</div><div class="line">        &quot;must&quot; : &#123;</div><div class="line">            &quot;term&quot; : &#123; &quot;user&quot; : &quot;kimchy&quot; &#125;</div><div class="line">        &#125;,</div><div class="line">        &quot;filter&quot;: &#123;</div><div class="line">            &quot;term&quot; : &#123; &quot;tag&quot; : &quot;tech&quot; &#125;</div><div class="line">        &#125;,</div><div class="line">        &quot;must_not&quot; : &#123;</div><div class="line">            &quot;range&quot; : &#123;</div><div class="line">                &quot;age&quot; : &#123; &quot;from&quot; : 10, &quot;to&quot; : 20 &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;,</div><div class="line">        &quot;should&quot; : [</div><div class="line">            &#123;</div><div class="line">                &quot;term&quot; : &#123; &quot;tag&quot; : &quot;wow&quot; &#125;</div><div class="line">            &#125;,</div><div class="line">            &#123;</div><div class="line">                &quot;term&quot; : &#123; &quot;tag&quot; : &quot;elasticsearch&quot; &#125;</div><div class="line">            &#125;</div><div class="line">        ],</div><div class="line">        &quot;minimum_should_match&quot; : 1,</div><div class="line">        &quot;boost&quot; : 1.0</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<ul>
<li>使用named query给子句添加标记</li>
</ul>
<p>如果想知道到底是bool里面哪个条件匹配，可以使用named query查询：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;bool&quot; : &#123;</div><div class="line">        &quot;should&quot; : [</div><div class="line">            &#123;&quot;match&quot; : &#123; &quot;name.first&quot; : &#123;&quot;query&quot; : &quot;shay&quot;, &quot;_name&quot; : &quot;first&quot;&#125; &#125;&#125;,</div><div class="line">            &#123;&quot;match&quot; : &#123; &quot;name.last&quot; : &#123;&quot;query&quot; : &quot;banon&quot;, &quot;_name&quot; : &quot;last&quot;&#125; &#125;&#125;</div><div class="line">        ],</div><div class="line">        &quot;filter&quot; : &#123;</div><div class="line">            &quot;terms&quot; : &#123;</div><div class="line">                &quot;name.last&quot; : [&quot;banon&quot;, &quot;kimchy&quot;],</div><div class="line">                &quot;_name&quot; : &quot;test&quot;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;ES中的聚合被分为两大类：Metric度量和bucket桶。metric很像SQL中的avg、max、min等方法，而bucket就有点类似group by。&lt;/p&gt;
&lt;h2 id=&quot;1-单值聚合&quot;&gt;&lt;a href=&quot;#1-单值聚合&quot; class=&quot;headerlink&quot; 
    
    </summary>
    
    
      <category term="logstash" scheme="http://yoursite.com/tags/logstash/"/>
    
      <category term="DSL" scheme="http://yoursite.com/tags/DSL/"/>
    
      <category term="kibana" scheme="http://yoursite.com/tags/kibana/"/>
    
  </entry>
  
  <entry>
    <title>logstash学习（五）—— aggregate插件使用</title>
    <link href="http://yoursite.com/2019/04/10/logstash-aggregate%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8/"/>
    <id>http://yoursite.com/2019/04/10/logstash-aggregate插件使用/</id>
    <published>2019-04-10T11:15:23.000Z</published>
    <updated>2019-04-10T11:33:21.000Z</updated>
    
    <content type="html"><![CDATA[<p>aggregate可以将将属于一个task的多个events的信息聚合在一起，并最终聚合到最后一个events中。使用时要注意一定要将worker数量设置成1（在logstash安装目录下的config目录下，logstash.yml文件，默认的worker数量和cpu的内核数量一致），否则处理顺序错乱会导致未知的结果。</p>
<h3 id="1-例一：输入有开始和结束标志的"><a href="#1-例一：输入有开始和结束标志的" class="headerlink" title="1. 例一：输入有开始和结束标志的"></a>1. 例一：输入有开始和结束标志的</h3><p>输入日志如下</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">INFO - 12345 - TASK_START - start</div><div class="line">INFO - 12345 - SQL - sqlQuery1 - 12</div><div class="line">INFO - 12345 - SQL - sqlQuery2 - 34</div><div class="line">INFO - 12345 - TASK_END - end</div></pre></td></tr></table></figure>
<p>配置文件如下</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line">filter &#123;</div><div class="line">   grok &#123;</div><div class="line">     match =&gt; [ &quot;message&quot;, &quot;%&#123;LOGLEVEL:loglevel&#125; - %&#123;NOTSPACE:taskid&#125; - %&#123;NOTSPACE:logger&#125; - %&#123;WORD:label&#125;( - %&#123;INT:duration:int&#125;)?&quot; ]</div><div class="line">   &#125;</div><div class="line"></div><div class="line">   if [logger] == &quot;TASK_START&quot; &#123;</div><div class="line">     aggregate &#123;</div><div class="line">       task_id =&gt; &quot;%&#123;taskid&#125;&quot;</div><div class="line">       code =&gt; &quot;map[&apos;sql_duration&apos;] = 0&quot;</div><div class="line">       map_action =&gt; &quot;create&quot;</div><div class="line">     &#125;</div><div class="line">   &#125;</div><div class="line"></div><div class="line">   if [logger] == &quot;SQL&quot; &#123;</div><div class="line">     aggregate &#123;</div><div class="line">       task_id =&gt; &quot;%&#123;taskid&#125;&quot;</div><div class="line">       code =&gt; &quot;map[&apos;sql_duration&apos;] += event.get(&apos;duration&apos;)&quot;</div><div class="line">       map_action =&gt; &quot;update&quot;</div><div class="line">     &#125;</div><div class="line">   &#125;</div><div class="line"></div><div class="line">   if [logger] == &quot;TASK_END&quot; &#123;</div><div class="line">     aggregate &#123;</div><div class="line">       task_id =&gt; &quot;%&#123;taskid&#125;&quot;</div><div class="line">       code =&gt; &quot;event.set(&apos;sql_duration&apos;, map[&apos;sql_duration&apos;])&quot;</div><div class="line">       map_action =&gt; &quot;update&quot;</div><div class="line">       end_of_task =&gt; true</div><div class="line">       timeout =&gt; 120</div><div class="line">     &#125;</div><div class="line">   &#125;</div><div class="line"> &#125;</div></pre></td></tr></table></figure>
<p>最后一个event输出如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">        &quot;host&quot; =&gt; &quot;host-10-0-251-156&quot;,</div><div class="line">      &quot;logger&quot; =&gt; &quot;TASK_END&quot;,</div><div class="line">      &quot;taskid&quot; =&gt; &quot;12345&quot;,</div><div class="line">       &quot;label&quot; =&gt; &quot;end&quot;,</div><div class="line">    &quot;loglevel&quot; =&gt; &quot;INFO&quot;,</div><div class="line">     &quot;message&quot; =&gt; &quot; INFO - 12345 - TASK_END - end&quot;,</div><div class="line">  &quot;@timestamp&quot; =&gt; 2018-12-14T01:00:04.414Z,</div><div class="line">        &quot;path&quot; =&gt; &quot;/root/logstash-6.5.0/config/test.txt&quot;,</div><div class="line">    &quot;@version&quot; =&gt; &quot;1&quot;,</div><div class="line">&quot;sql_duration&quot; =&gt; 46</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>sql_duration字段用于记录sql请求用时的总和。</p>
<h3 id="2-例二：输入油结束标志没有开始标志"><a href="#2-例二：输入油结束标志没有开始标志" class="headerlink" title="2. 例二：输入油结束标志没有开始标志"></a>2. 例二：输入油结束标志没有开始标志</h3><p>输入日志如下</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">INFO - 12345 - SQL - sqlQuery1 - 12</div><div class="line">INFO - 12345 - SQL - sqlQuery2 - 34</div><div class="line">INFO - 12345 - TASK_END - end</div></pre></td></tr></table></figure>
<p>配置文件如下</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">filter &#123;</div><div class="line">  grok &#123;</div><div class="line">    match =&gt; [ &quot;message&quot;, &quot;%&#123;LOGLEVEL:loglevel&#125; - %&#123;NOTSPACE:taskid&#125; - %&#123;NOTSPACE:logger&#125; - %&#123;WORD:label&#125;( - %&#123;INT:duration:int&#125;)?&quot; ]</div><div class="line">  &#125;</div><div class="line">  if [logger] == &quot;SQL&quot; &#123;</div><div class="line">    aggregate &#123;</div><div class="line">      task_id =&gt; &quot;%&#123;taskid&#125;&quot;</div><div class="line">      code =&gt; &quot;map[&apos;sql_duration&apos;] ||= 0 ; map[&apos;sql_duration&apos;] += event.get(&apos;duration&apos;)&quot;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">  if [logger] == &quot;TASK_END&quot; &#123;</div><div class="line">    aggregate &#123;</div><div class="line">      task_id =&gt; &quot;%&#123;taskid&#125;&quot;</div><div class="line">      code =&gt; &quot;event.set(&apos;sql_duration&apos;, map[&apos;sql_duration&apos;])&quot;</div><div class="line">      end_of_task =&gt; true</div><div class="line">      timeout =&gt; 120</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>输出结果跟例一一样。</li>
<li>“||= “是ruby的操作符，例子中当map[‘sql_duration’]没有舒适化值的时候，将被初始化为0。</li>
</ul>
<h3 id="3-例三：没有开始和结束标志"><a href="#3-例三：没有开始和结束标志" class="headerlink" title="3. 例三：没有开始和结束标志"></a>3. 例三：没有开始和结束标志</h3><p>典型的应用场景是跟踪用户行为。通过用户id跟踪用户行为事件，一旦用户停止了交互，这个事件也就相应结束了。然而却没有一个特定的标记来表示用户交互的结束。这种场景我们可以使用<em>push_map_as_event_on_timeout</em>参数，设置<em>timeout</em>，当超时发生时，将map作为一个新的事件被输出。另外我们可以设置<em>timecode</em>用于timeout事件结束时执行。</p>
<p>输入日志如下</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">INFO - 12345 - Clicked One</div><div class="line">INFO - 12345 - Clicked Two</div><div class="line">INFO - 12345 - Clicked Three</div></pre></td></tr></table></figure>
<p>聚合用户的点击量：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">filter &#123;</div><div class="line">  grok &#123;</div><div class="line">    match =&gt; [ &quot;message&quot;, &quot;%&#123;LOGLEVEL:loglevel&#125; - %&#123;NOTSPACE:user_id&#125; - %&#123;GREEDYDATA:msg_text&#125;&quot; ]</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  aggregate &#123;</div><div class="line">    task_id =&gt; &quot;%&#123;user_id&#125;&quot;</div><div class="line">    code =&gt; &quot;map[&apos;clicks&apos;] ||= 0; map[&apos;clicks&apos;] += 1;&quot;</div><div class="line">    push_map_as_event_on_timeout =&gt; true</div><div class="line">    timeout_task_id_field =&gt; &quot;user_id&quot;</div><div class="line">    timeout =&gt; 600 # 10 minutes timeout</div><div class="line">    timeout_tags =&gt; [&apos;_aggregatetimeout&apos;]</div><div class="line">    timeout_code =&gt; &quot;event.set(&apos;several_clicks&apos;, event.get(&apos;clicks&apos;) &gt; 1)&quot;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>十分钟之后输出结果，最后事件的部分为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;several_clicks&quot; =&gt; true,</div><div class="line">          &quot;@version&quot; =&gt; &quot;1&quot;,</div><div class="line">        &quot;@timestamp&quot; =&gt; 2018-12-14T05:54:09.212Z,</div><div class="line">            &quot;clicks&quot; =&gt; 3,</div><div class="line">           &quot;user_id&quot; =&gt; &quot;12345&quot;,</div><div class="line">              &quot;tags&quot; =&gt; [</div><div class="line">        [0] &quot;_aggregatetimeout&quot;</div><div class="line">    ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="4-例四：没有开始和结束标志，任务连续不断"><a href="#4-例四：没有开始和结束标志，任务连续不断" class="headerlink" title="4. 例四：没有开始和结束标志，任务连续不断"></a>4. 例四：没有开始和结束标志，任务连续不断</h3><p>和例三一样，事件没有开始和结束的标志，并且任务连续不断。这就表明你不能靠设置timeout来更新map。</p>
<p>典型的例子是通过jdbc的插件获取数据进行聚合。例如有这样的sql查询语句：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT country_name, town_name FROM town</div></pre></td></tr></table></figure>
<p>通过jdbc插件获得3个事件:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">&#123; &quot;country_name&quot;: &quot;France&quot;, &quot;town_name&quot;: &quot;Paris&quot; &#125;</div><div class="line">&#123; &quot;country_name&quot;: &quot;France&quot;, &quot;town_name&quot;: &quot;Marseille&quot; &#125;</div><div class="line">&#123; &quot;country_name&quot;: &quot;USA&quot;, &quot;town_name&quot;: &quot;New-York&quot; &#125;</div></pre></td></tr></table></figure>
<p>我们的目的是往es里存入两个数据：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">&#123; &quot;country_name&quot;: &quot;France&quot;, &quot;towns&quot;: [ &#123;&quot;town_name&quot;: &quot;Paris&quot;&#125;, &#123;&quot;town_name&quot;: &quot;Marseille&quot;&#125; ] &#125;</div><div class="line">&#123; &quot;country_name&quot;: &quot;USA&quot;, &quot;towns&quot;: [ &#123;&quot;town_name&quot;: &quot;New-York&quot;&#125; ] &#125;</div></pre></td></tr></table></figure>
<p>可以设置<em>push_previous_map_as_event</em>参数：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">filter &#123;</div><div class="line">     aggregate &#123;</div><div class="line">       task_id =&gt; &quot;%&#123;country_name&#125;&quot;</div><div class="line">       code =&gt; &quot;</div><div class="line">         map[&apos;country_name&apos;] = event.get(&apos;country_name&apos;)</div><div class="line">         map[&apos;towns&apos;] ||= []</div><div class="line">         map[&apos;towns&apos;] &lt;&lt; &#123;&apos;town_name&apos; =&gt; event.get(&apos;town_name&apos;)&#125;</div><div class="line">         event.cancel()</div><div class="line">       &quot;</div><div class="line">       push_previous_map_as_event =&gt; true</div><div class="line">       timeout =&gt; 5</div><div class="line">     &#125;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<ul>
<li>每当检测出一个新的country_name的时候，就会将当前的聚合map作为一个新的事件被推出，然后为下一个国家创建一个新的空map。</li>
<li>当5秒超时之后，最后一个聚合map会被作为一个新的事件推出。</li>
<li>event.cancel()会将起始的没有被聚合的事件丢弃。</li>
</ul>
<h3 id="5-例五：没有开始和结束标志并且尽可能快的推送事件"><a href="#5-例五：没有开始和结束标志并且尽可能快的推送事件" class="headerlink" title="5. 例五：没有开始和结束标志并且尽可能快的推送事件"></a>5. 例五：没有开始和结束标志并且尽可能快的推送事件</h3><p>这个例子也没有起始和结束标志，事件以不确定的间隔时间持续输入，并且希望用户最后交互后立即将事件推出而不用等超时时间，这要求聚合事件推送到输出的时间尽可能是实时的。典型的应场景是通过id跟踪用户行为的时候，一旦用户停止了交互，这个事件也就相应结束了。然而却没有一个特定的标记来表示用户交互的结束。如果对于同一个用户，如果没有事件输入时长达到了指定的<em>inactivity_timeout</em>时间，则认为交互结束。</p>
<p>从第一个事件开始，如果同一个用户交互时间长于我们设置的<em>timeout</em>时间，聚合的map被推出的同时会被删除。</p>
<p>和例三不同的是，当用户停止交互到达设定的<em>inactivity_timeout</em>时长，事件就会被推出而不是要等<em>timeout</em>的时间。</p>
<p>输入日志文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">INFO - 12345 - Clicked One</div><div class="line">INFO - 12345 - Clicked Two</div><div class="line">INFO - 12345 - Clicked Three</div></pre></td></tr></table></figure>
<p>记录用户点击数量的logstash文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">filter &#123;</div><div class="line"> grok &#123;</div><div class="line">   match =&gt; [ &quot;message&quot;, &quot;%&#123;LOGLEVEL:loglevel&#125; - %&#123;NOTSPACE:user_id&#125; - %&#123;GREEDYDATA:msg_text&#125;&quot; ]</div><div class="line"> &#125;</div><div class="line"> aggregate &#123;</div><div class="line">   task_id =&gt; &quot;%&#123;user_id&#125;&quot;</div><div class="line">   code =&gt; &quot;map[&apos;clicks&apos;] ||= 0; map[&apos;clicks&apos;] += 1;&quot;</div><div class="line">   push_map_as_event_on_timeout =&gt; true</div><div class="line">   timeout_task_id_field =&gt; &quot;user_id&quot;</div><div class="line">   timeout =&gt; 3600 # 1 hour timeout, user activity will be considered finished one hour after the first event, even if events keep comming</div><div class="line">   inactivity_timeout =&gt; 300 # 5 minutes timeout, user activity will be considered finished if no new events arrive 5 minutes after the last event</div><div class="line">   timeout_tags =&gt; [&apos;_aggregatetimeout&apos;]</div><div class="line">   timeout_code =&gt; &quot;event.set(&apos;several_clicks&apos;, event.get(&apos;clicks&apos;) &gt; 1)&quot;</div><div class="line"> &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>当超过五分钟没有输入或者从第一个事件开始到达了一个小时（超时时间），都会提交事件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line"> &quot;user_id&quot;: &quot;12345&quot;,</div><div class="line"> &quot;clicks&quot;: 3,</div><div class="line"> &quot;several_clicks&quot;: true,</div><div class="line">   &quot;tags&quot;: [</div><div class="line">      &quot;_aggregatetimeout&quot;</div><div class="line">   ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>Tips：</p>
<ul>
<li><em>inactivity_timeout</em>，是从某个task_id的最后一个输入开始计时，直到超过设定的时间开始执行<em>timeout_code</em>的内容并输出。如果有多个task_id交叉输入，则将会分别计时。比如task_id=1输入一次，然后task_id=2输入两次，然后task_id=1再输入一次。task_id=2最后一次输入在task_id=1之前，则将先输出task_id=2通过<em>timeout_code</em>计算后的内容。</li>
<li><em>timeout</em>上一条需要发生在同一个task_id第一次输入到最后一次输入的时间不超过<em>timeout</em>设置的时间的情况下，如果超过了<em>timeout</em>设置的时间，则会立即执行<em>timeout_code</em>的内容。例如<em>timeout</em>设置5分钟，而task_id=1持续输入（输入间隔不超过<em>inactivity_timeout</em>设置的时间）超过五分钟，则在五分钟的时候这个事件将会被推入输出里。</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;aggregate可以将将属于一个task的多个events的信息聚合在一起，并最终聚合到最后一个events中。使用时要注意一定要将worker数量设置成1（在logstash安装目录下的config目录下，logstash.yml文件，默认的worker数量和cpu的内
    
    </summary>
    
    
      <category term="logstash" scheme="http://yoursite.com/tags/logstash/"/>
    
  </entry>
  
  <entry>
    <title>logstash学习（四）—— 多管道</title>
    <link href="http://yoursite.com/2019/04/10/logstash%E5%A4%9A%E7%AE%A1%E9%81%93/"/>
    <id>http://yoursite.com/2019/04/10/logstash多管道/</id>
    <published>2019-04-10T11:14:07.000Z</published>
    <updated>2019-04-10T11:33:12.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-多管道配置"><a href="#1-多管道配置" class="headerlink" title="1. 多管道配置"></a>1. 多管道配置</h2><p>如果需要在同一个进程运行多个管道（pipeline），可以配置logstash的pipelines.yml文件，该文件在logstash/config目录下。配置文件的结构如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">- pipeline.id: my-pipeline_1</div><div class="line">  path.config: &quot;/etc/path/to/p1.config&quot;</div><div class="line">  pipeline.workers: 3</div><div class="line">- pipeline.id: my-other-pipeline</div><div class="line">  path.config: &quot;/etc/different/path/p2.cfg&quot;</div><div class="line">  queue.type: persisted</div></pre></td></tr></table></figure>
<p>配置文件需要遵循yaml格式列出一组路径，每个路径都描述了一个管道信息，并且需要以键值对的形式对管道进行描述。上述例子展现了两种通过ID和路径信息描述管道的方式。对于第一个管道，pipeline.workers的值被设置为3，而在另一个则启用持久队列特性。管道之间的资源竞争是很重要的，因为默认值是为单个管道调优的，考虑减少每个管道使用的管道工人的数量可以对单个管道调优，因为默认情况下每个管道每个CPU核心将使用1个工人。在pipelines.yml文件中未显式设置值的将使用默认值。</p>
<h2 id="2-启动"><a href="#2-启动" class="headerlink" title="2. 启动"></a>2. 启动</h2><p>在logstash的bin目录下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"># ./logstash</div></pre></td></tr></table></figure>
<p>在没有参数的情况下启动logstash时，它将读取pipelines.yml文件并实例化文件中指定的所有管道，当使用-e或-f时，Logstash会忽略pipelines.yml文件，并记录对此的警告。</p>
<h2 id="3-注意事项"><a href="#3-注意事项" class="headerlink" title="3. 注意事项"></a>3. 注意事项</h2><ul>
<li>当输入使用相同插件的时候，比如都用了s3插件，需要考虑了设置临时存储文件的路径。因为s3插件在没有设置文件下载路径的情况下，默认把文件下载到了“/tmp/logstash”路径下，当有多个管道进行一起下载的时候，如果对命名没有区分，则可能造成处理文件出现异常。解决方法是指定各自管道的临时存储路径，s3的指定方法是设置temporary_directory参数，并在路径下创建指定的目录。</li>
<li>当数据导入出现异常，需要删除数据重新启动的时候，需要先删除对导入数据进度的记录文件，具体做法是将logstash\data\下的文件都删掉。</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;1-多管道配置&quot;&gt;&lt;a href=&quot;#1-多管道配置&quot; class=&quot;headerlink&quot; title=&quot;1. 多管道配置&quot;&gt;&lt;/a&gt;1. 多管道配置&lt;/h2&gt;&lt;p&gt;如果需要在同一个进程运行多个管道（pipeline），可以配置logstash的pipeline
    
    </summary>
    
    
      <category term="logstash" scheme="http://yoursite.com/tags/logstash/"/>
    
  </entry>
  
  <entry>
    <title>logstash学习（三）—— 自动重载配置文件</title>
    <link href="http://yoursite.com/2019/04/10/logstash%E8%87%AA%E5%8A%A8%E9%87%8D%E8%BD%BD%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/"/>
    <id>http://yoursite.com/2019/04/10/logstash自动重载配置文件/</id>
    <published>2019-04-10T11:12:55.000Z</published>
    <updated>2019-04-10T11:21:25.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-自动重启配置"><a href="#1-自动重启配置" class="headerlink" title="1. 自动重启配置"></a>1. 自动重启配置</h2><p>从logstash2.3开始，当配置文件发生更改时，可以设置自动检测和重载配置文件而不用重启logstash。设置自动重载的命令如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"># bin/logstash –f configFile.conf --config.reload.automatic</div></pre></td></tr></table></figure>
<p>注意：</p>
<ul>
<li>当运行命令时（logstash指定-e运行）–config.reload.automatic参数不起作用。</li>
<li>默认是每个3秒检测一次配置文件是否发生变化，当然检测间隔也可以自己设置，格式如下：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"># bin/logstash –f configFile.conf --config.reload.automatic &lt;interval&gt;</div></pre></td></tr></table></figure>
<p>如果已经运行了没有提供自动重启的logstash，可以发送一个挂起命令给logstash重新加载配置文件:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">kill -1 &lt;pid&gt;</div></pre></td></tr></table></figure>
<h2 id="2-工作原理"><a href="#2-工作原理" class="headerlink" title="2. 工作原理"></a>2. 工作原理</h2><p>当检测到配置文件变化则通过停止所有输入来停止当前pipeline。然后用新的配置尝试创建一个新的pipeline。如果检查配置文件语法正确并且所有的输入和输出可以初始化，则使用新的pipeline替换当前的pipeline。如果检查失败,使用旧的继续工作。注意在重载过程中,jvm没有重启。</p>
<h2 id="3-注意事项"><a href="#3-注意事项" class="headerlink" title="3. 注意事项"></a>3. 注意事项</h2><ul>
<li>stdin输入插件不支持自动重启。</li>
<li>syslog作为输入源，当重载配置文件时会崩溃。</li>
</ul>
<p>具体见官网：<a href="https://www.elastic.co/guide/en/logstash/current/reloading-config.html" target="_blank" rel="external">Reloading the Config File</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;1-自动重启配置&quot;&gt;&lt;a href=&quot;#1-自动重启配置&quot; class=&quot;headerlink&quot; title=&quot;1. 自动重启配置&quot;&gt;&lt;/a&gt;1. 自动重启配置&lt;/h2&gt;&lt;p&gt;从logstash2.3开始，当配置文件发生更改时，可以设置自动检测和重载配置文件而不用
    
    </summary>
    
    
      <category term="logstash" scheme="http://yoursite.com/tags/logstash/"/>
    
  </entry>
  
  <entry>
    <title>logstash学习（二）—— 配置文件运行</title>
    <link href="http://yoursite.com/2019/04/10/logstash%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%BF%90%E8%A1%8C/"/>
    <id>http://yoursite.com/2019/04/10/logstash配置文件运行/</id>
    <published>2019-04-10T11:12:29.000Z</published>
    <updated>2020-03-03T13:36:06.277Z</updated>
    
    <content type="html"><![CDATA[<p>在实际生产环境中，logstash常常需要用到复杂的运行规则，使用命令行显然不可取。所以可以自定义规则文件，然后让Logstash根据规则进行工作。</p>
<h2 id="1-配置文件"><a href="#1-配置文件" class="headerlink" title="1. 配置文件"></a>1. 配置文件</h2><p>配置文件结构如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">input&#123;</div><div class="line"></div><div class="line">&#125;</div><div class="line"></div><div class="line">filter&#123;</div><div class="line">    </div><div class="line">&#125;</div><div class="line"></div><div class="line">output&#123;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>配置文件主要有input，output和filter三个大的部分构成。input是数据的输入源，可以是来自文件，网站，redis，s3等。output是数据的输出，可以输出到文件，elasticsearch，邮件，kafka等。filter是过滤规则，根据需要进行内容的过滤。</p>
<p>将配置文件放在logstash下的config目录中。进入logstash的bin目录，运行配置文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"># cd logstash-6.3.2/bin/</div><div class="line"># ./logstash -f /root/logstash-6.5.0/config/test.conf</div></pre></td></tr></table></figure>
<h2 id="2-常用插件"><a href="#2-常用插件" class="headerlink" title="2. 常用插件"></a>2. 常用插件</h2><p>例，如下的配置文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div></pre></td><td class="code"><pre><div class="line">cat UGA-MTN.conf </div><div class="line">input &#123;</div><div class="line">  s3&#123;</div><div class="line">    access_key_id =&gt; &quot;ACCESS_KEY_ID&quot;</div><div class="line">    secret_access_key =&gt; &quot;SECRET_ACCESS_KEY&quot;</div><div class="line">    bucket =&gt; &quot;your_bucket&quot;</div><div class="line">    region =&gt; &quot;your_s3_region&quot;</div><div class="line">    interval =&gt; 600</div><div class="line">    prefix =&gt; &quot;……&quot; </div><div class="line">    exclude_pattern =&gt; &quot;……&quot;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line">filter &#123;</div><div class="line">  urldecode&#123;</div><div class="line">    all_fields =&gt; true</div><div class="line">  &#125;</div><div class="line">  grok&#123;</div><div class="line">    match =&gt; &#123;&quot;message&quot; =&gt; &quot;%&#123;TIMESTAMP_ISO8601:log_date&#125;\s%&#123;NOTSPACE:log_name&#125;\s(?:%&#123;NOTSPACE:request_body&#125;)&quot;&#125;</div><div class="line">    remove_field =&gt; [&quot;log_name&quot;]</div><div class="line">  &#125;</div><div class="line">  json &#123;</div><div class="line">    source =&gt; &quot;request_body&quot;</div><div class="line">    remove_field =&gt; [&quot;request_body&quot;]</div><div class="line">  &#125;</div><div class="line">  mutate&#123;</div><div class="line">    add_field =&gt; &#123;</div><div class="line">      &quot;rack&quot; =&gt; &quot;……&quot;</div><div class="line">    &#125;</div><div class="line">    convert =&gt; &#123;</div><div class="line">      &quot;request_time&quot; =&gt; &quot;integer&quot;</div><div class="line">      &quot;status&quot; =&gt; &quot;integer&quot;</div><div class="line">      &quot;bytes_sent&quot; =&gt; &quot;integer&quot;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line">output &#123;</div><div class="line">  elasticsearch &#123;</div><div class="line">    action =&gt; &quot;index&quot;                                       #The operation on ES</div><div class="line">    hosts =&gt; [&quot;192.168.32.20:9200&quot;]   #ElasticSearch host, can be array.</div><div class="line">    index =&gt; &quot;cache_log_%&#123;+YYYY.MM.dd&#125;&quot;                     #The index to write data to.</div><div class="line">    document_type =&gt; &quot;logs&quot;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="2-1-输入插件s3"><a href="#2-1-输入插件s3" class="headerlink" title="2.1 输入插件s3"></a>2.1 输入插件s3</h3><p>s3插件可以从s3上获取数据。常用参数有：</p>
<ul>
<li>access_key_id: String类型，没有默认值。用于获取s3的验证。</li>
<li>aws_credentials_file：String类型，没有默认值。用于获取s3的验证。当access_key_id和secret_access_key没有设置的时候，才会加载这个文件。</li>
<li>secret_access_key：String类型，默认值是“logstash”，根据实际情况进行修改。</li>
</ul>
<p>注：常用的获取s3验证的方法有：</p>
<ol>
<li>access_key_id和ecret_access_key进行配置；</li>
<li>配置aws_credentials_file从外部文件进行获取；</li>
<li>将AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY配置到环境变量中。</li>
<li>将AMAZON_ACCESS_KEY_ID和AMAZON_SECRET_ACCESS_KEY配置到环境变量中。</li>
<li>IAM实例的证书。</li>
</ol>
<ul>
<li>bucket，String类型必选参数且没有默认值。需要获取数据的s3的桶的名称。</li>
<li>region，String类型，默认值是“us-east-1”，表示AWS所在的位置区域。</li>
<li>interval，number类型，默认值是60，单位是秒。两次获取数据的间隔时间。</li>
<li>prefix，String类型，默认值是nil。表示表示在桶里面需要匹配的文件前缀，不是一个正则表达式。</li>
<li>exclude_pattern，String类型，默认值是nil。正则匹配需要从桶里面排除的文件。</li>
<li>temporary_directory，String类型，默认值是“/tmp/logstash”，用于临时保存从s3下载的文件，可以根据需求进行修改路径。</li>
</ul>
<p>其他参数详见官方文档：<a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-s3.html" target="_blank" rel="external">s3插件说明</a></p>
<h3 id="2-2-输出插件elasticsearch"><a href="#2-2-输出插件elasticsearch" class="headerlink" title="2.2 输出插件elasticsearch"></a>2.2 输出插件elasticsearch</h3><p>elasticsearch只支持HTTP协议，logstash和elasticsearch的交互也优先使用HTTP协议。HTTP协议尽管稍微慢一点，但是很好控制和使用。</p>
<ul>
<li>action，，String类型，默认值是index。表示elasticsearch要进行的操作。有效操作有: index：为文件编索引；delete：根据id删除文件；create：为文件指定创建的索引，如果索引已经存在，则创建失败；update：根据id更新文件信息。</li>
<li>hosts，类型是Uri，默认值是[//127.0.0.1]。设置远程实例的主机，可以在数组里放多个ip设置多台主机。</li>
<li>index，String类型，默认值是logstash-%{+YYYY.MM.dd}。用于设置数据写入的index名称。</li>
</ul>
<p>其他参数详见官方文档：<a href="https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html" target="_blank" rel="external">elasticsearch插件说明</a></p>
<h3 id="2-3-filter插件"><a href="#2-3-filter插件" class="headerlink" title="2.3 filter插件"></a>2.3 filter插件</h3><ul>
<li>urldecode：Nginx作为反向代理服务器，部分日志在传输的过程中是通过“urlencoded”编码的，通过logstash收集上来的日志需要用urldecode将字符串还原为原来的格式。</li>
</ul>
<p>Urldecode常用的配置选项：</p>
<ol>
<li><p>all_fields：对所有的值进行url的解码，默认值为false</p>
</li>
<li><p>Charset：设置解码使用的字符集，默认为“utf-8”。</p>
</li>
</ol>
<ul>
<li>grok是通过系统预定义的正则表达式或者通过自己定义正则表达式来匹配日志中的各个值，在使用时自己可以根据自己的日志格式或者对匹配正则进行调整或者直接调用。如果，自己想在其他目录定义正则规则匹配日志，在使用时需要指定正则的路径。</li>
<li>mutate主要用来修改、替换、删除及重命名字段中的某些插件。</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在实际生产环境中，logstash常常需要用到复杂的运行规则，使用命令行显然不可取。所以可以自定义规则文件，然后让Logstash根据规则进行工作。&lt;/p&gt;
&lt;h2 id=&quot;1-配置文件&quot;&gt;&lt;a href=&quot;#1-配置文件&quot; class=&quot;headerlink&quot; title
    
    </summary>
    
    
      <category term="logstash" scheme="http://yoursite.com/tags/logstash/"/>
    
  </entry>
  
  <entry>
    <title>logstash学习（一）—— 安装配置</title>
    <link href="http://yoursite.com/2019/04/10/logstash%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE/"/>
    <id>http://yoursite.com/2019/04/10/logstash安装配置/</id>
    <published>2019-04-10T11:11:01.000Z</published>
    <updated>2019-04-10T11:32:44.000Z</updated>
    
    <content type="html"><![CDATA[<p>  Logstash是一个开源的服务器端数据处理管道，可以同时从多个源获取数据。面对海量的日志量，rsyslog和sed，awk等日志收集，处理工具已经显的力不从心。logstash是一个整合型的框架，可以用以日志的收集，存储，索引构建。</p>
<h2 id="1-logstash安装"><a href="#1-logstash安装" class="headerlink" title="1. logstash安装"></a>1. logstash安装</h2><h3 id="1-1-安装环境"><a href="#1-1-安装环境" class="headerlink" title="1.1 安装环境"></a>1.1 安装环境</h3><ul>
<li>centos 7.2</li>
<li>JDK：1.8.0_112</li>
</ul>
<h3 id="1-2-安装"><a href="#1-2-安装" class="headerlink" title="1.2 安装"></a>1.2 安装</h3><p>直接下载压缩包，解压运行即可。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"># wget https://artifacts.elastic.co/downloads/logstash/logstash-6.5.0.tar.gz</div><div class="line"># tar -zxvf logstash-6.5.0.tar.gz</div></pre></td></tr></table></figure>
<h2 id="2-验证"><a href="#2-验证" class="headerlink" title="2. 验证"></a>2. 验证</h2><p>进入解压的logstash文件夹目录下的bin目录中，运行以命令行的形式运行logstash：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"># cd logstash-6.5.0/bin/</div><div class="line"># ./logstash -e &apos;input&#123; stdin&#123;&#125; &#125; output&#123; stdout&#123;&#125; &#125;&apos;</div><div class="line">Sending Logstash logs to /root/logstash-6.5.0/logs which is now configured via log4j2.properties</div><div class="line">[2018-11-19T14:30:25,975][INFO ][logstash.setting.writabledirectory] Creating directory &#123;:setting=&gt;&quot;path.queue&quot;, :path=&gt;&quot;/root/logstash-6.5.0/data/queue&quot;&#125;</div><div class="line">[2018-11-19T14:30:26,010][INFO ][logstash.setting.writabledirectory] Creating directory &#123;:setting=&gt;&quot;path.dead_letter_queue&quot;, :path=&gt;&quot;/root/logstash-6.5.0/data/dead_letter_queue&quot;&#125;</div><div class="line">[2018-11-19T14:30:27,642][WARN ][logstash.config.source.multilocal] Ignoring the &apos;pipelines.yml&apos; file because modules or command line options are specified</div><div class="line">[2018-11-19T14:30:27,848][INFO ][logstash.runner          ] Starting Logstash &#123;&quot;logstash.version&quot;=&gt;&quot;6.5.0&quot;&#125;</div><div class="line">[2018-11-19T14:30:28,051][INFO ][logstash.agent           ] No persistent UUID file found. Generating new UUID &#123;:uuid=&gt;&quot;7e16bcaa-b50e-4751-9fac-7acd00dc1773&quot;, :path=&gt;&quot;/root/logstash-6.5.0/data/uuid&quot;&#125;</div><div class="line">[2018-11-19T14:30:49,501][INFO ][logstash.pipeline        ] Starting pipeline &#123;:pipeline_id=&gt;&quot;main&quot;, &quot;pipeline.workers&quot;=&gt;4, &quot;pipeline.batch.size&quot;=&gt;125, &quot;pipeline.batch.delay&quot;=&gt;50&#125;</div><div class="line">[2018-11-19T14:30:50,690][INFO ][logstash.pipeline        ] Pipeline started successfully &#123;:pipeline_id=&gt;&quot;main&quot;, :thread=&gt;&quot;#&lt;Thread:0x2418c105 run&gt;&quot;&#125;</div><div class="line">The stdin plugin is now waiting for input:</div><div class="line">[2018-11-19T14:30:51,118][INFO ][logstash.agent           ] Pipelines running &#123;:count=&gt;1, :running_pipelines=&gt;[:main], :non_running_pipelines=&gt;[]&#125;</div><div class="line">[2018-11-19T14:30:52,429][INFO ][logstash.agent           ] Successfully started Logstash API endpoint &#123;:port=&gt;9600&#125;</div><div class="line">hello world</div><div class="line">&#123;</div><div class="line">      &quot;@version&quot; =&gt; &quot;1&quot;,  # 版本号</div><div class="line">       &quot;message&quot; =&gt; &quot;hello world&quot;, # 输入内容</div><div class="line">          &quot;host&quot; =&gt; &quot;xxxxxx&quot;,  # 主机名，数据是从哪个节点发过来的</div><div class="line">    &quot;@timestamp&quot; =&gt; 2018-11-19T06:31:03.697Z # 自动生成的时间戳</div><div class="line">&#125;</div></pre></td></tr></table></figure>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;  Logstash是一个开源的服务器端数据处理管道，可以同时从多个源获取数据。面对海量的日志量，rsyslog和sed，awk等日志收集，处理工具已经显的力不从心。logstash是一个整合型的框架，可以用以日志的收集，存储，索引构建。&lt;/p&gt;
&lt;h2 id=&quot;1-log
    
    </summary>
    
    
      <category term="logstash" scheme="http://yoursite.com/tags/logstash/"/>
    
  </entry>
  
  <entry>
    <title>微信公众号开发（三）——自定义菜单和响应</title>
    <link href="http://yoursite.com/2018/08/28/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E5%BC%80%E5%8F%91%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%92%8C%E5%93%8D%E5%BA%94/"/>
    <id>http://yoursite.com/2018/08/28/微信公众号开发（三）——自定义菜单和响应/</id>
    <published>2018-08-28T14:15:36.000Z</published>
    <updated>2020-03-03T13:35:08.359Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-测试号申请"><a href="#1-测试号申请" class="headerlink" title="1. 测试号申请"></a>1. 测试号申请</h2><p>不同的公众号类型具备不同的接口权限，一般个人申请的是未认证订阅号，没有自定义菜单的功能，只有企业组织科申请工作好认证公众号。不同类型的公众号的不同接口权限详情见：<a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1433401084" target="_blank" rel="external">公众号接口权限说明</a>。</p>
<p>我们可以申请一个接口测试号，这个测试号几乎开放所有的接口，可以供测试使用， <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421137522" target="_blank" rel="external">接口测试号申请</a>。测试号如下图，可以用测试号的appID和appsecret进行接口调用。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/21.png" alt="image"></p>
<h2 id="2-创建自定义菜单"><a href="#2-创建自定义菜单" class="headerlink" title="2. 创建自定义菜单"></a>2. 创建自定义菜单</h2><h3 id="2-1-获取access-token"><a href="#2-1-获取access-token" class="headerlink" title="2.1 获取access_token"></a>2.1 获取access_token</h3><p>获取access_token的接口为GET：<br><a href="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&amp;appid=APPID" target="_blank" rel="external">https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&amp;appid=APPID</a></p>
<p>创建自定义菜单的接口为POST：<a href="https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESSTOKEN" target="_blank" rel="external">https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESSTOKEN</a> 其中需要传的body详见官网文档： <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421141013" target="_blank" rel="external">自定义菜单创建接口</a>。</p>
<p>可以用postman进行接口调用。这里主要讲通过java程序进行创建。</p>
<p>获取access_token<br>TokenResponseDTO.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TokenResponseDTO</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> Integer errcode;</div><div class="line">    <span class="keyword">private</span> String errmsg;</div><div class="line">    <span class="keyword">private</span> String access_token;</div><div class="line">    <span class="keyword">private</span> String expires_in;</div><div class="line">    <span class="comment">//省略get set</span></div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> String <span class="title">getToken</span><span class="params">(String appId, String appSecret)</span> </span>&#123;</div><div class="line">    String queryTokenUrl = <span class="string">"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&amp;appid=&#123;0&#125;&amp;secret=&#123;1&#125;"</span>;</div><div class="line"></div><div class="line">    String newQueryTokenUrl = MessageFormat.format(queryTokenUrl, appId,appSecret);</div><div class="line">    <span class="keyword">try</span> &#123;</div><div class="line">        ResponseEntity&lt;TokenResponseDTO&gt; response =restTemplate.getForEntity(newQueryTokenUrl, TokenResponseDTO.class);</div><div class="line">        TokenResponseDTO tokenResponseDTO = <span class="keyword">null</span>;</div><div class="line">        <span class="keyword">if</span> (response != <span class="keyword">null</span> &amp;&amp; response.getStatusCode() != <span class="keyword">null</span> &amp;&amp;response.getStatusCode().value() == <span class="number">200</span> &amp;&amp; response.getBody() != <span class="keyword">null</span></div><div class="line">                &amp;&amp; response.getBody().getErrcode() == <span class="keyword">null</span> &amp;&amp;response.getBody().getErrmsg() == <span class="keyword">null</span>) &#123;</div><div class="line">            tokenResponseDTO = response.getBody();</div><div class="line">            LOGGER.info(<span class="string">"get token from weixin, Reponse: "</span> +JsonUtils.encode(response));</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Exception(<span class="string">"getToken error! "</span> +response.getBody().getErrcode().toString() + <span class="string">":"</span> +response.getBody().getErrmsg());</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> tokenResponseDTO.getAccess_token();</div><div class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</div><div class="line">        LOGGER.error(<span class="string">"getToken error!"</span> + e.getMessage());</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(e.getMessage());</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>因为获取接口的access_token每日的调用次数是有上限的，每次token的过期时间有2个小时，所以可以另外搭一个存access_token的服务器，定时更新access_token。</p>
<h3 id="2-2-创建自定义菜单"><a href="#2-2-创建自定义菜单" class="headerlink" title="2.2 创建自定义菜单"></a>2.2 创建自定义菜单</h3><p>组body的json数据，可以通过组对象，然后转换成json格式的字符串，也可以自己组好json字符串，写在程序里。我们这里采用组对象转换json串的方式，组三个三种类型的一级按钮。</p>
<p>MenuDTO.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MenuDTO</span> </span>&#123;</div><div class="line">    List&lt;ButtonDTO&gt; button;</div><div class="line">    <span class="comment">// get set</span></div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>ButtonDTO.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ButtonDTO</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> String type;</div><div class="line">    <span class="keyword">private</span> String name;</div><div class="line">    <span class="keyword">private</span> String key;</div><div class="line">    <span class="keyword">private</span> String url;</div><div class="line">    ButtonDTO()&#123;&#125;</div><div class="line">    ButtonDTO(String type, String name, String key, String url)&#123;</div><div class="line">        <span class="keyword">this</span>.type = type;</div><div class="line">        <span class="keyword">this</span>.name = name;</div><div class="line">        <span class="keyword">this</span>.key = key;</div><div class="line">        <span class="keyword">this</span>.url = url;</div><div class="line">    &#125;</div><div class="line">    <span class="comment">// get set</span></div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">createMenu</span><span class="params">(String appId, String appSecret)</span> </span>&#123;</div><div class="line">    <span class="keyword">int</span> result = <span class="number">0</span>;</div><div class="line">    String accessToken = getToken(appId, appSecret);</div><div class="line">    <span class="comment">// 拼装创建菜单的url</span></div><div class="line">    String menu_create_url =<span class="string">"https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"</span>;</div><div class="line">    String url = menu_create_url.replace(<span class="string">"ACCESS_TOKEN"</span>, accessToken);</div><div class="line">    <span class="comment">// 将菜单对象转换成json字符串</span></div><div class="line">    MenuDTO menuDTO = <span class="keyword">new</span> MenuDTO();</div><div class="line">    ButtonDTO buttonDTO1 = <span class="keyword">new</span> ButtonDTO(<span class="string">"pic_photo_or_album"</span>, <span class="string">"上传照片"</span>, <span class="string">"11"</span>, <span class="keyword">null</span>);</div><div class="line">    ButtonDTO buttonDTO2 = <span class="keyword">new</span> ButtonDTO(<span class="string">"click"</span>, <span class="string">"click"</span>, <span class="string">"12"</span>, <span class="keyword">null</span>);</div><div class="line">    ButtonDTO buttonDTO3 = <span class="keyword">new</span> ButtonDTO(<span class="string">"view"</span>, <span class="string">"view"</span>, <span class="keyword">null</span>, <span class="string">"https://weui.io/#uploader"</span>);</div><div class="line">    List&lt;ButtonDTO&gt; buttonDTOList = <span class="keyword">new</span> ArrayList&lt;&gt;();</div><div class="line">    buttonDTOList.add(buttonDTO1);</div><div class="line">    buttonDTOList.add(buttonDTO2);</div><div class="line">    buttonDTOList.add(buttonDTO3);</div><div class="line">    menuDTO.setButton(buttonDTOList);</div><div class="line">    HttpHeaders headers = <span class="keyword">new</span> HttpHeaders();</div><div class="line">    headers.setContentType(MediaType.APPLICATION_JSON);</div><div class="line">    MultiValueMap&lt;String, String&gt; params = <span class="keyword">new</span> LinkedMultiValueMap&lt;String,String&gt;();</div><div class="line">    params.add(<span class="string">"data"</span>, JsonUtils.encode(menuDTO));</div><div class="line">    System.out.println(JsonUtils.encode(menuDTO));</div><div class="line">    HttpEntity&lt;MultiValueMap&lt;String, String&gt;&gt; requestEntity = newHttpEntity&lt;MultiValueMap&lt;String, String&gt;&gt;(params, headers);</div><div class="line">    RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</div><div class="line">    ResponseEntity&lt;String&gt; response = restTemplate.exchange(url,HttpMethod.POST, requestEntity, String.class);</div><div class="line">    System.out.println(response.getBody());</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>运行方法，如果没有错误信息，则返回：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;errcode&quot;: 0,</div><div class="line">    &quot;errmsg&quot;: &quot;ok&quot;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>打开测试公众号，可以看到我们新加的自定义菜单。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/22.png" alt="image"></p>
<h2 id="菜单响应"><a href="#菜单响应" class="headerlink" title="菜单响应"></a>菜单响应</h2><p>这里的响应主要针对上传照片和click按钮，view按钮会自动跳转到初始化的url页面上。</p>
<p>在web.xml中配一个servlet</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">servlet</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>weixin<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">servlet-class</span>&gt;</span>com.star.ott.tokenmanage.api.business.CoreServlet<span class="tag">&lt;/<span class="name">servlet-class</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">servlet</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;<span class="name">servlet-mapping</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>weixin<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/wx<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">servlet-mapping</span>&gt;</span></div></pre></td></tr></table></figure>
<p>CoreServlet.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CoreServlet</span> <span class="keyword">extends</span> <span class="title">HttpServlet</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String OK = <span class="string">"OK"</span>;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">get</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> OK;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 确认请求来自微信服务器</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doGet</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> ServletException, IOException </span>&#123;</div><div class="line"></div><div class="line">        <span class="comment">// 微信加密签名</span></div><div class="line">        String signature = request.getParameter(<span class="string">"signature"</span>);</div><div class="line">        <span class="comment">// 时间戳</span></div><div class="line">        String timestamp = request.getParameter(<span class="string">"timestamp"</span>);</div><div class="line">        <span class="comment">// 随机数</span></div><div class="line">        String nonce = request.getParameter(<span class="string">"nonce"</span>);</div><div class="line">        <span class="comment">// 随机字符串</span></div><div class="line">        String echostr = request.getParameter(<span class="string">"echostr"</span>);</div><div class="line">        response.setHeader(<span class="string">"content-type"</span>, <span class="string">"text/html;charset=UTF-8"</span>);</div><div class="line">        PrintWriter out = response.getWriter();</div><div class="line">        <span class="comment">// 通过检验signature对请求进行校验，若校验成功则原样返回echostr，表示接入成功，否则接入失败</span></div><div class="line">        <span class="keyword">if</span> (SignUtil.checkSignature(signature, timestamp, nonce)) &#123;</div><div class="line">            out.print(echostr);</div><div class="line">        &#125;</div><div class="line">        out.close();</div><div class="line">        out = <span class="keyword">null</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 处理微信服务器发来的消息</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doPost</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> ServletException, IOException </span>&#123;</div><div class="line">        <span class="comment">// 将请求、响应的编码均设置为UTF-8（防止中文乱码）</span></div><div class="line">        request.setCharacterEncoding(<span class="string">"UTF-8"</span>);</div><div class="line">        response.setCharacterEncoding(<span class="string">"UTF-8"</span>);</div><div class="line"></div><div class="line">        <span class="comment">// 调用核心业务类接收消息、处理消息</span></div><div class="line">        String respMessage = processRequest(request);</div><div class="line"></div><div class="line">        <span class="comment">// 响应消息</span></div><div class="line">        PrintWriter out = response.getWriter();</div><div class="line">        out.print(respMessage);</div><div class="line">        out.close();</div><div class="line"></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">private</span> String <span class="title">processRequest</span><span class="params">(HttpServletRequest request)</span> </span>&#123;</div><div class="line">        String respMessage = <span class="keyword">null</span>;</div><div class="line">        <span class="keyword">try</span> &#123;</div><div class="line">            <span class="comment">// 默认返回的文本消息内容</span></div><div class="line">            String respContent = <span class="string">"请求处理异常，请稍候尝试！"</span>;</div><div class="line"></div><div class="line">            <span class="comment">// xml请求解析</span></div><div class="line">            Map&lt;String, String&gt; requestMap = MessageUtil.parseXml(request);</div><div class="line"></div><div class="line">            <span class="comment">// 发送方帐号（open_id）</span></div><div class="line">            String fromUserName = requestMap.get(<span class="string">"FromUserName"</span>);</div><div class="line">            <span class="comment">// 公众帐号</span></div><div class="line">            String toUserName = requestMap.get(<span class="string">"ToUserName"</span>);</div><div class="line">            <span class="comment">// 消息类型</span></div><div class="line">            String msgType = requestMap.get(<span class="string">"MsgType"</span>);</div><div class="line"></div><div class="line">            <span class="comment">// 回复文本消息</span></div><div class="line">            TextMessage textMessage = <span class="keyword">new</span> TextMessage();</div><div class="line">            textMessage.setToUserName(fromUserName);</div><div class="line">            textMessage.setFromUserName(toUserName);</div><div class="line">            textMessage.setCreateTime(<span class="keyword">new</span> Date().getTime());</div><div class="line">            textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);</div><div class="line">            textMessage.setFuncFlag(<span class="number">0</span>);</div><div class="line"></div><div class="line">            <span class="comment">// 图片消息</span></div><div class="line">            <span class="keyword">if</span> (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) &#123;</div><div class="line">                respContent = <span class="string">"您发送的是图片消息！"</span>;</div><div class="line">            &#125;</div><div class="line">            <span class="comment">// 事件推送</span></div><div class="line">            <span class="keyword">if</span> (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) &#123;</div><div class="line">                <span class="comment">// 事件类型</span></div><div class="line">                String eventType = requestMap.get(<span class="string">"Event"</span>);</div><div class="line">                <span class="keyword">if</span> (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) &#123;</div><div class="line">                    <span class="comment">// 事件KEY值，与创建自定义菜单时指定的KEY值对应</span></div><div class="line">                    String eventKey = requestMap.get(<span class="string">"EventKey"</span>);</div><div class="line"></div><div class="line">                    <span class="keyword">if</span> (eventKey.equals(<span class="string">"12"</span>)) &#123;</div><div class="line">                        respContent = <span class="string">"被点击！"</span>;</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">            textMessage.setContent(respContent);</div><div class="line">            respMessage = MessageUtil.textMessageToXml(textMessage);</div><div class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</div><div class="line">            e.printStackTrace();</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> respMessage;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>SignUtil.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SignUtil</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String token = <span class="string">"weixintest"</span>;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 验证签名</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> signature</div><div class="line">     * <span class="doctag">@param</span> timestamp</div><div class="line">     * <span class="doctag">@param</span> nonce</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">checkSignature</span><span class="params">(String signature, String timestamp, String nonce)</span> </span>&#123;</div><div class="line">        String[] arr = <span class="keyword">new</span> String[]&#123;token, timestamp, nonce&#125;;</div><div class="line">        <span class="comment">// 将token、timestamp、nonce三个参数进行字典序排序</span></div><div class="line">        Arrays.sort(arr);</div><div class="line">        StringBuilder content = <span class="keyword">new</span> StringBuilder();</div><div class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; arr.length; i++) &#123;</div><div class="line">            content.append(arr[i]);</div><div class="line">        &#125;</div><div class="line">        MessageDigest md = <span class="keyword">null</span>;</div><div class="line">        String tmpStr = <span class="keyword">null</span>;</div><div class="line"></div><div class="line">        <span class="keyword">try</span> &#123;</div><div class="line">            md = MessageDigest.getInstance(<span class="string">"SHA-1"</span>);</div><div class="line">            <span class="comment">// 将三个参数字符串拼接成一个字符串进行sha1加密</span></div><div class="line">            <span class="keyword">byte</span>[] digest = md.digest(content.toString().getBytes());</div><div class="line">            tmpStr = byteToStr(digest);</div><div class="line">        &#125; <span class="keyword">catch</span> (NoSuchAlgorithmException e) &#123;</div><div class="line">            e.printStackTrace();</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        content = <span class="keyword">null</span>;</div><div class="line">        <span class="comment">// 将sha1加密后的字符串可与signature对比，标识该请求来源于微信</span></div><div class="line">        <span class="keyword">return</span> tmpStr != <span class="keyword">null</span> ? tmpStr.equals(signature.toUpperCase()) : <span class="keyword">false</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 将字节数组转换为十六进制字符串</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> byteArray</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title">byteToStr</span><span class="params">(<span class="keyword">byte</span>[] byteArray)</span> </span>&#123;</div><div class="line">        String strDigest = <span class="string">""</span>;</div><div class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; byteArray.length; i++) &#123;</div><div class="line">            strDigest += byteToHexStr(byteArray[i]);</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> strDigest;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 将字节转换为十六进制字符串</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> mByte</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title">byteToHexStr</span><span class="params">(<span class="keyword">byte</span> mByte)</span> </span>&#123;</div><div class="line">        <span class="keyword">char</span>[] Digit = &#123;<span class="string">'0'</span>, <span class="string">'1'</span>, <span class="string">'2'</span>, <span class="string">'3'</span>, <span class="string">'4'</span>, <span class="string">'5'</span>, <span class="string">'6'</span>, <span class="string">'7'</span>, <span class="string">'8'</span>, <span class="string">'9'</span>, <span class="string">'A'</span>, <span class="string">'B'</span>, <span class="string">'C'</span>, <span class="string">'D'</span>, <span class="string">'E'</span>, <span class="string">'F'</span>&#125;;</div><div class="line">        <span class="keyword">char</span>[] tempArr = <span class="keyword">new</span> <span class="keyword">char</span>[<span class="number">2</span>];</div><div class="line">        tempArr[<span class="number">0</span>] = Digit[(mByte &gt;&gt;&gt; <span class="number">4</span>) &amp; <span class="number">0X0F</span>];</div><div class="line">        tempArr[<span class="number">1</span>] = Digit[mByte &amp; <span class="number">0X0F</span>];</div><div class="line"></div><div class="line">        String s = <span class="keyword">new</span> String(tempArr);</div><div class="line">        <span class="keyword">return</span> s;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>MessageUtil.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MessageUtil</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 返回消息类型：文本</div><div class="line">     */</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String RESP_MESSAGE_TYPE_TEXT = <span class="string">"text"</span>;</div><div class="line"></div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 请求消息类型：图片</div><div class="line">     */</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String REQ_MESSAGE_TYPE_IMAGE = <span class="string">"image"</span>;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String REQ_MESSAGE_TYPE_EVENT = <span class="string">"event"</span>;</div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 事件类型：CLICK(自定义菜单点击事件)</div><div class="line">     */</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String EVENT_TYPE_CLICK = <span class="string">"CLICK"</span>;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String EVENT_TYPE_PHOTO = <span class="string">"pic_photo_or_album"</span>;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 解析微信发来的请求（XML）</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> request</div><div class="line">     * <span class="doctag">@return</span></div><div class="line">     * <span class="doctag">@throws</span> Exception</div><div class="line">     */</div><div class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Map&lt;String, String&gt; <span class="title">parseXml</span><span class="params">(HttpServletRequest request)</span> <span class="keyword">throws</span> Exception </span>&#123;</div><div class="line">        <span class="comment">// 将解析结果存储在HashMap中</span></div><div class="line">        Map&lt;String, String&gt; map = <span class="keyword">new</span> HashMap&lt;String, String&gt;();</div><div class="line"></div><div class="line">        <span class="comment">// 从request中取得输入流</span></div><div class="line">        InputStream inputStream = request.getInputStream();</div><div class="line">        <span class="comment">// 读取输入流</span></div><div class="line">        SAXReader reader = <span class="keyword">new</span> SAXReader();</div><div class="line">        Document document = reader.read(inputStream);</div><div class="line">        <span class="comment">// 得到xml根元素</span></div><div class="line">        Element root = document.getRootElement();</div><div class="line">        <span class="comment">// 得到根元素的所有子节点</span></div><div class="line">        List&lt;Element&gt; elementList = root.elements();</div><div class="line"></div><div class="line">        <span class="comment">// 遍历所有子节点</span></div><div class="line">        <span class="keyword">for</span> (Element e : elementList)</div><div class="line">            map.put(e.getName(), e.getText());</div><div class="line"></div><div class="line">        <span class="comment">// 释放资源</span></div><div class="line">        inputStream.close();</div><div class="line">        inputStream = <span class="keyword">null</span>;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> map;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 文本消息对象转换成xml</div><div class="line">     * <span class="doctag">@param</span> textMessage 文本消息对象</div><div class="line">     * <span class="doctag">@return</span> xml</div><div class="line">     */</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">textMessageToXml</span><span class="params">(TextMessage textMessage)</span> </span>&#123;</div><div class="line">        xstream.alias(<span class="string">"xml"</span>, textMessage.getClass());</div><div class="line">        <span class="keyword">return</span> xstream.toXML(textMessage);</div><div class="line">    &#125;</div><div class="line"></div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * 扩展xstream，使其支持CDATA块</div><div class="line">     */</div><div class="line">    <span class="keyword">private</span> <span class="keyword">static</span> XStream xstream = <span class="keyword">new</span> XStream(<span class="keyword">new</span> XppDriver() &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> HierarchicalStreamWriter <span class="title">createWriter</span><span class="params">(Writer out)</span> </span>&#123;</div><div class="line">            <span class="keyword">return</span> <span class="keyword">new</span> PrettyPrintWriter(out) &#123;</div><div class="line">                <span class="comment">// 对所有xml节点的转换都增加CDATA标记</span></div><div class="line">                <span class="keyword">boolean</span> cdata = <span class="keyword">true</span>;</div><div class="line"></div><div class="line">                <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</div><div class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startNode</span><span class="params">(String name, Class clazz)</span> </span>&#123;</div><div class="line">                    <span class="keyword">super</span>.startNode(name, clazz);</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">writeText</span><span class="params">(QuickWriter writer, String text)</span> </span>&#123;</div><div class="line">                    <span class="keyword">if</span> (cdata) &#123;</div><div class="line">                        writer.write(<span class="string">"&lt;![CDATA["</span>);</div><div class="line">                        writer.write(text);</div><div class="line">                        writer.write(<span class="string">"]]&gt;"</span>);</div><div class="line">                    &#125; <span class="keyword">else</span> &#123;</div><div class="line">                        writer.write(text);</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line">            &#125;;</div><div class="line">        &#125;</div><div class="line">    &#125;);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>参考文章：<a href="https://blog.csdn.net/lyq8479/article/details/8952173" target="_blank" rel="external">https://blog.csdn.net/lyq8479/article/details/8952173</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;1-测试号申请&quot;&gt;&lt;a href=&quot;#1-测试号申请&quot; class=&quot;headerlink&quot; title=&quot;1. 测试号申请&quot;&gt;&lt;/a&gt;1. 测试号申请&lt;/h2&gt;&lt;p&gt;不同的公众号类型具备不同的接口权限，一般个人申请的是未认证订阅号，没有自定义菜单的功能，只有企业
    
    </summary>
    
    
      <category term="微信公众号" scheme="http://yoursite.com/tags/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7/"/>
    
  </entry>
  
  <entry>
    <title>微信公众号开发（二）——本地调试</title>
    <link href="http://yoursite.com/2018/08/28/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E5%BC%80%E5%8F%91%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%E6%9C%AC%E5%9C%B0%E8%B0%83%E8%AF%95/"/>
    <id>http://yoursite.com/2018/08/28/微信公众号开发（二）——本地调试/</id>
    <published>2018-08-28T14:15:18.000Z</published>
    <updated>2020-03-03T13:35:20.200Z</updated>
    
    <content type="html"><![CDATA[<p>微信用户发送信息到腾讯服务器，然后腾讯服务器把请求以post方式转发到自己的服务器上（需要有外网ip，并且token验证通过的服务器）。然后在自己的服务器上进行处理后把处理结果返回给腾讯服务器，腾讯服务器再把数据返回给用户，整个过程如下图。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/20.png" alt="image"></p>
<p>因为腾讯服务器转发到自己的服务器要求该服务器有外网地址，而我们调试程序的服务器通常可能没有外网地址。因此需要在有外网地址的服务器上做流量转发，转发到本地进行调试。</p>
<p>可以用xshell或其他软件做内网穿透，具体见：<a href="https://my.oschina.net/AbnerLee/blog/348998" target="_blank" rel="external">https://my.oschina.net/AbnerLee/blog/348998</a></p>
<p>这种方法的优势在于方便，有xshell就能实现；但是实际使用时发现并不稳定，连接会经常断掉。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/24.png" alt="image"></p>
<p>如上图所示，在有外网地址的服务器上搭一个流量转发工具（orange），将所有访问443或者80端口的流量（腾讯服务器只发送到这两个端口）转发到本地服务器的特定端口上，这样在本地起服务就能够进行调试了。本地服务器和有外网的服务器需要在同一网段里，才能进行转发。</p>
<p>这种方法进行本地测试比较稳定，缺点在于本地服务和有外网的服务器需要在同一网段内，这个本身如果服务器没有公网ip，也需要做内网穿透。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;微信用户发送信息到腾讯服务器，然后腾讯服务器把请求以post方式转发到自己的服务器上（需要有外网ip，并且token验证通过的服务器）。然后在自己的服务器上进行处理后把处理结果返回给腾讯服务器，腾讯服务器再把数据返回给用户，整个过程如下图。&lt;/p&gt;
&lt;p&gt;&lt;img src=
    
    </summary>
    
    
      <category term="微信公众号" scheme="http://yoursite.com/tags/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7/"/>
    
  </entry>
  
  <entry>
    <title>微信公众号开发（一）——搭建环境</title>
    <link href="http://yoursite.com/2018/08/28/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E5%BC%80%E5%8F%91%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%E6%90%AD%E5%BB%BA%E7%8E%AF%E5%A2%83/"/>
    <id>http://yoursite.com/2018/08/28/微信公众号开发（一）——搭建环境/</id>
    <published>2018-08-28T14:14:53.000Z</published>
    <updated>2020-03-03T13:34:56.997Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-基础环境"><a href="#1-基础环境" class="headerlink" title="1. 基础环境"></a>1. 基础环境</h2><ul>
<li>微信公众号一个（我的是个人订阅号，不同的类型权限不同）</li>
<li>有外网ip的服务器（centos7）</li>
</ul>
<h2 id="2-服务器配置（以官方demo为例，具体见：官方文档）"><a href="#2-服务器配置（以官方demo为例，具体见：官方文档）" class="headerlink" title="2. 服务器配置（以官方demo为例，具体见：官方文档）"></a>2. 服务器配置（以官方demo为例，具体见：<a href="https://mp.weixin.qq.com/advanced/advanced?action=interface&amp;t=advanced/interface&amp;token=501128847&amp;lang=zh_CN" target="_blank" rel="external">官方文档</a>）</h2><p>centos系统自带了python，因此不用再下载，如果没有自带python，需要先下载2.7及其以上版本的python。</p>
<h3 id="2-1-安装依赖库"><a href="#2-1-安装依赖库" class="headerlink" title="2.1 安装依赖库"></a>2.1 安装依赖库</h3><ul>
<li>安装pip</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">yum -y install epel-release</div><div class="line">yum install python-pip</div><div class="line">pip install --upgrade pip</div></pre></td></tr></table></figure>
<ul>
<li>安装 web.py</li>
</ul>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pip install web.py</div></pre></td></tr></table></figure>
<ul>
<li>安装libxml2, libxslt, lxml</li>
</ul>
<p>这些库都是python自带的，如果没有需要安装。</p>
<ul>
<li>编辑代码</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">vi main.py</div><div class="line"></div><div class="line"><span class="comment"># -*- coding: utf-8 -*-</span></div><div class="line"><span class="comment"># filename: main.py</span></div><div class="line">import web</div><div class="line"></div><div class="line">urls = (</div><div class="line">    <span class="string">'/wx'</span>, <span class="string">'Handle'</span>,</div><div class="line">)</div><div class="line"></div><div class="line">class Handle(object):</div><div class="line">    def GET(self):</div><div class="line">        <span class="built_in">return</span> <span class="string">"hello, this is handle view"</span></div><div class="line"></div><div class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</div><div class="line">    app = web.application(urls, globals())</div><div class="line">    app.run()</div></pre></td></tr></table></figure>
<ul>
<li>启动</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">python main.py 80</div></pre></td></tr></table></figure>
<p>打开浏览器，输入服务器ip和路径，可以看到</p>
<p><img src="http://106.13.2.39:8888/blog_pic/15.png" alt="image"></p>
<ul>
<li>添加 handle.py</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line">vi handle.py</div><div class="line"><span class="comment"># -*- coding: utf-8 -*-</span></div><div class="line"><span class="comment"># filename: handle.py</span></div><div class="line"></div><div class="line">import hashlib</div><div class="line">import web</div><div class="line">class Handle(object):</div><div class="line">    def GET(self):</div><div class="line">        try:</div><div class="line">            data = web.input()</div><div class="line">            <span class="keyword">if</span> len(data) == 0:</div><div class="line">                <span class="built_in">return</span> <span class="string">"hello, this is handle view"</span></div><div class="line">            signature = data.signature</div><div class="line">            timestamp = data.timestamp</div><div class="line">            nonce = data.nonce</div><div class="line">            echostr = data.echostr</div><div class="line">            token = <span class="string">"xxxx"</span> <span class="comment">#请按照公众平台官网\基本配置中信息填写</span></div><div class="line"></div><div class="line">            list = [token, timestamp, nonce]</div><div class="line">            list.sort()</div><div class="line">            sha1 = hashlib.sha1()</div><div class="line">            map(sha1.update, list)</div><div class="line">            hashcode = sha1.hexdigest()</div><div class="line">            <span class="built_in">print</span> <span class="string">"handle/GET func: hashcode, signature: "</span>, hashcode, signature</div><div class="line">            <span class="keyword">if</span> hashcode == signature:</div><div class="line">                <span class="built_in">return</span> echostr</div><div class="line">            <span class="keyword">else</span>:</div><div class="line">                <span class="built_in">return</span> <span class="string">""</span></div><div class="line">        except Exception, Argument:</div><div class="line">            <span class="built_in">return</span> Argument</div></pre></td></tr></table></figure>
<ul>
<li>重新启动</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">python main.py 80</div></pre></td></tr></table></figure>
<h3 id="2-微信公众号配置"><a href="#2-微信公众号配置" class="headerlink" title="2. 微信公众号配置"></a>2. 微信公众号配置</h3><p>在开发 &gt; 基本配置中修改配置</p>
<p><img src="http://106.13.2.39:8888/blog_pic/17.png" alt="image"></p>
<p><img src="http://106.13.2.39:8888/blog_pic/16.png" alt="image"></p>
<p>其中的Token需要和服务器handle.py里的token一样。</p>
<p>点击“提交”之后，如果验证成功，则配置成功。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;1-基础环境&quot;&gt;&lt;a href=&quot;#1-基础环境&quot; class=&quot;headerlink&quot; title=&quot;1. 基础环境&quot;&gt;&lt;/a&gt;1. 基础环境&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;微信公众号一个（我的是个人订阅号，不同的类型权限不同）&lt;/li&gt;
&lt;li&gt;有外网ip的服务器
    
    </summary>
    
    
      <category term="微信公众号" scheme="http://yoursite.com/tags/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7/"/>
    
  </entry>
  
  <entry>
    <title>mybatis使用tips</title>
    <link href="http://yoursite.com/2018/08/28/mybatis%E4%BD%BF%E7%94%A8tips/"/>
    <id>http://yoursite.com/2018/08/28/mybatis使用tips/</id>
    <published>2018-08-28T14:00:15.000Z</published>
    <updated>2020-03-03T13:31:40.282Z</updated>
    
    <content type="html"><![CDATA[<p>mybatis作为一种灵活的orm框架，优点有目共睹：灵活！不管多复杂的查询，联表，多对多，联表count。。。只要能把sql语句写出来，就能查出来转成PO。然鹅，在使用的时候也充分暴露了，越是灵活的东西，越容易出bug。比如买个面包机，我只需要按照说明书，往里加面粉，酵母，糖等配料就行了，做出来的东西起码符合食物的最低标准——能吃！但是如果给你一个烤箱做面包，理论上你可以做各种形状各种口味的：牛角面包，吐司，法棍。。然而事实上，可能做出来的东西并不能称之为“食物”。mybatis就是一个烤箱，能做任何事情，又不能做任何事情。</p>
<h2 id="1-搜索条件传入参数类型是Integer，Long"><a href="#1-搜索条件传入参数类型是Integer，Long" class="headerlink" title="1. 搜索条件传入参数类型是Integer，Long"></a>1. 搜索条件传入参数类型是Integer，Long</h2><p>例如枚举类型，前端传入的是枚举的名称，而数据库存的是枚举值对应的数字，我们的做法是，先找到枚举值对应的数字，然后用数字去数据库进行搜索。</p>
<p>例：</p>
<p>接口Mapper.java：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="function">List&lt;TaskApplicationBO&gt; <span class="title">findTaskApplicationsByCondition</span><span class="params">(@Param(<span class="string">"taskApplicationStatus"</span>)</span> Integer taskApplicationStatus)</span>;</div></pre></td></tr></table></figure>
<p>对应的Mapper.xml的搜索：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">select ta.id id, ta.task_application_status task_application_status</div><div class="line">from task_application ta</div><div class="line">where ta.delete_flag = 1</div><div class="line"><span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">"taskApplicationStatus != null and taskApplicationStatus != ''"</span>&gt;</span></div><div class="line">    and ta.task_application_status = #&#123;taskApplicationStatus&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">if</span>&gt;</span></div></pre></td></tr></table></figure>
<p>这个时候，如果传入的taskApplicationStatus值是0，则会搜索出全部。</p>
<p>sql打印如下：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> ta.id <span class="keyword">id</span>, ta.task_application_status task_application_status <span class="keyword">FROM</span> task_application ta <span class="keyword">WHERE</span> ta.delete_flag = <span class="number">1</span></div></pre></td></tr></table></figure>
<p>传入的taskApplicationStatus值是其他值，则会拼接上搜索条件进行搜索。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> ta.id <span class="keyword">id</span>, ta.task_application_status task_application_status <span class="keyword">FROM</span> task_application ta <span class="keyword">WHERE</span> ta.delete_flag = <span class="number">1</span> <span class="keyword">and</span> ta.task_application_status = ?</div><div class="line"><span class="keyword">Parameters</span>: <span class="number">1</span>(<span class="built_in">Integer</span>)</div></pre></td></tr></table></figure>
<p>由打印出的sql可以看出，当taskApplicationStatus为0的时候，没有进入if条件里。</p>
<p>通过Debug MyBatis源找到了IfSqlNode类，该类用来处理动态SQL的<if>节点。</if></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">IfSqlNode</span> <span class="keyword">implements</span> <span class="title">SqlNode</span> </span>&#123;</div><div class="line">  <span class="keyword">private</span> ExpressionEvaluator evaluator;</div><div class="line">  <span class="keyword">private</span> String test;</div><div class="line">  <span class="keyword">private</span> SqlNode contents;</div><div class="line"></div><div class="line">  <span class="function"><span class="keyword">public</span> <span class="title">IfSqlNode</span><span class="params">(SqlNode contents, String test)</span> </span>&#123;</div><div class="line">    <span class="keyword">this</span>.test = test;</div><div class="line">    <span class="keyword">this</span>.contents = contents;</div><div class="line">    <span class="keyword">this</span>.evaluator = <span class="keyword">new</span> ExpressionEvaluator();</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">apply</span><span class="params">(DynamicContext context)</span> </span>&#123;</div><div class="line">    <span class="keyword">if</span> (evaluator.evaluateBoolean(test, context.getBindings())) &#123;</div><div class="line">      contents.apply(context);</div><div class="line">      <span class="keyword">return</span> <span class="keyword">true</span>;</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>其中方法public boolean apply(DynamicContext context)用来构造节点内的SQL语句。if (evaluator.evaluateBoolean(test, context.getBindings())该代码便是解析<if test="status !=null and status !=''">中test内表达式的关键，如果表达式为true则拼接SQL，否则忽略。</if></p>
<p>ExpressionEvaluator 类，通过OgnlCache.getValue(expression, parameterObject);可以看到表达式的值是从缓存中获取的，由此可知MyBatis竟然对表达式做了缓存，以提高性能。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ExpressionEvaluator</span> </span>&#123;  </div><div class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">evaluateBoolean</span><span class="params">(String expression, Object parameterObject)</span> </span>&#123;  </div><div class="line">    Object value = OgnlCache.getValue(expression, parameterObject);  </div><div class="line">    <span class="keyword">if</span> (value <span class="keyword">instanceof</span> Boolean) <span class="keyword">return</span> (Boolean) value;  </div><div class="line">    <span class="keyword">if</span> (value <span class="keyword">instanceof</span> Number) <span class="keyword">return</span> !<span class="keyword">new</span> BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO);  </div><div class="line">    <span class="keyword">return</span> value != <span class="keyword">null</span>;  </div><div class="line">  &#125;</div></pre></td></tr></table></figure></p>
<p>解析表达式的方法private static Object parseExpression(String expression),该方法会先从缓存取值，如果没有便进行解析并放入缓存中，然后调用Ognl.getValue(parseExpression(expression), root)获得表达式的值。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OgnlCache</span> </span>&#123;</div><div class="line"></div><div class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Map&lt;String, ognl.Node&gt; expressionCache = <span class="keyword">new</span> ConcurrentHashMap&lt;String, ognl.Node&gt;();</div><div class="line"></div><div class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">getValue</span><span class="params">(String expression, Object root)</span> <span class="keyword">throws</span> OgnlException </span>&#123;</div><div class="line">    <span class="keyword">return</span> Ognl.getValue(parseExpression(expression), root);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Object <span class="title">parseExpression</span><span class="params">(String expression)</span> <span class="keyword">throws</span> OgnlException </span>&#123;</div><div class="line">    <span class="keyword">try</span> &#123;</div><div class="line">      Node node = expressionCache.get(expression);</div><div class="line">      <span class="keyword">if</span> (node == <span class="keyword">null</span>) &#123;</div><div class="line">        node = <span class="keyword">new</span> OgnlParser(<span class="keyword">new</span> StringReader(expression)).topLevelExpression();</div><div class="line">        expressionCache.put(expression, node);</div><div class="line">      &#125;</div><div class="line">      <span class="keyword">return</span> node;</div><div class="line">    &#125; <span class="keyword">catch</span> (ParseException e) &#123;</div><div class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> ExpressionSyntaxException(expression, e);</div><div class="line">    &#125; <span class="keyword">catch</span> (TokenMgrError e) &#123;</div><div class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> ExpressionSyntaxException(expression, e);</div><div class="line">    &#125;</div><div class="line">  &#125;</div></pre></td></tr></table></figure></p>
<p>MyBatis的表达式是用<a href="https://commons.apache.org/proper/commons-ognl/language-guide.html" target="_blank" rel="external">OGNL</a>处理的，OGNL的官网中对表达式返回Boolean类型有以下解释：</p>
<p><img src="http://106.13.2.39:8888/blog_pic/18.png" alt="image"></p>
<p>如果对象是一个Number类型，值为0时将被解析为false，否则为true，浮点型0.00也是如此。</p>
<p><if test="taskApplicationStatus != null and taskApplicationStatus != ''"><br>    and ta.task_application_status = #{taskApplicationStatus}<br></if><br>时出现的问题了， taskApplicationStatus != ‘’被解析为false,而’’被解析为false，因此taskApplicationStatus!=’’是不成立的，表达式的值为false，导致无法拼接搜索条件。一般只有传入为String类型的时候才需要判断是否为String != ‘’。</p>
<p><strong>解决方法</strong></p>
<ul>
<li>去掉test表达式中的taskApplicationStatus != ‘’内容</li>
<li>增加一个判断条件 or taskApplicationStatus == 0</li>
</ul>
<p>另外如果你有类似于String str =”A”;  <if test="str!= null and str == 'A'">这样的写法时，因为单引号内如果为单个字符时，OGNL将会识别为Java 中的 char类型，显然String 类型与char类型做==运算会返回false，从而导致表达式不成立。解决方法很简单，修改为<if test="str!= null and str == "A"">即可；也可以做toString的转化<if test="str!= null and str == 'A'.toString()"></if></if></if></p>
<h2 id="2-动态拼接sql时的特殊字符"><a href="#2-动态拼接sql时的特殊字符" class="headerlink" title="2. 动态拼接sql时的特殊字符"></a>2. 动态拼接sql时的特殊字符</h2><p>严格来说这是mysql和url编码（通过http发起请求时参数识别）的问题，需要提前做个字符转化，且几乎无法穷举特殊字符，只能转化常见的。</p>
<p>比如“#”和“&amp;”，在发起http请求时，“#”以后的内容会被忽略，而“&amp;”作为参数拼接的符号，其后跟的内容会被认为是传入的参数，而本身作为参数传入时讲无法辨认。需要在前端进行转化。</p>
<p>mysql的特殊字符，比如“%”和“/”等。则是mysql的特殊字符，需要在拼接sql前进行转义。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">value = value.replaceAll(<span class="string">"/"</span>, ESCAPE_CHAR + <span class="string">"/"</span>);</div><div class="line">value = value.replaceAll(<span class="string">"%"</span>, ESCAPE_CHAR + <span class="string">"%"</span>);</div><div class="line">value = value.replaceAll(<span class="string">"_"</span>, ESCAPE_CHAR + <span class="string">"_"</span>);</div><div class="line">value = value.replaceAll(<span class="string">"'"</span>, <span class="string">"''"</span>);</div></pre></td></tr></table></figure>
<h2 id="3-传入List组in语句"><a href="#3-传入List组in语句" class="headerlink" title="3. 传入List组in语句"></a>3. 传入List组in语句</h2><p>其实这也是mysql的问题，传入List，拼接in语句，如果传入的List集合是数字类型（如Integer，Long），查询返回的集合会根据数字大小进行升序排序（如果传入的List是String则不会）。因此如果需要原始顺序，需要对查询字段进行排序，具体如下：</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&lt;foreach collection="ids" item="item" open="(" separator="," close=")"&gt;</div><div class="line">    #&#123;item&#125;</div><div class="line">&lt;/foreach&gt;</div><div class="line">     ORDER BY FIELD</div><div class="line">&lt;foreach collection="ids" item="item" open="(id," separator="," close=")"&gt;</div><div class="line">    #&#123;item&#125;</div><div class="line">&lt;/foreach&gt;</div></pre></td></tr></table></figure>
<h2 id="4-其他"><a href="#4-其他" class="headerlink" title="4. 其他"></a>4. 其他</h2><p>未完待续。。。。。。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;mybatis作为一种灵活的orm框架，优点有目共睹：灵活！不管多复杂的查询，联表，多对多，联表count。。。只要能把sql语句写出来，就能查出来转成PO。然鹅，在使用的时候也充分暴露了，越是灵活的东西，越容易出bug。比如买个面包机，我只需要按照说明书，往里加面粉，酵母
    
    </summary>
    
    
      <category term="mybatis" scheme="http://yoursite.com/tags/mybatis/"/>
    
      <category term="mysql" scheme="http://yoursite.com/tags/mysql/"/>
    
  </entry>
  
  <entry>
    <title>hibernate实体类与数据库表的常见映射</title>
    <link href="http://yoursite.com/2018/07/31/hibernate%E5%AE%9E%E4%BD%93%E7%B1%BB%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%BA%93%E8%A1%A8%E7%9A%84%E5%B8%B8%E8%A7%81%E6%98%A0%E5%B0%84/"/>
    <id>http://yoursite.com/2018/07/31/hibernate实体类与数据库表的常见映射/</id>
    <published>2018-07-31T14:27:10.000Z</published>
    <updated>2018-07-31T14:34:50.000Z</updated>
    
    <content type="html"><![CDATA[<p>Hibernate的核心功能是根据数据库到实体类的映射，自动从数据库绑定数据到实体类。</p>
<p>这使我们操作实体类(Java对象)就能对数据库进行增、删、查、改，而不用调用JDBC API使数据操作变得简单而不繁琐。本文主要总结了实体类的各种属性类型到数据库表的映射以及各种关联关系的映射。</p>
<h3 id="1-基本类型的映射"><a href="#1-基本类型的映射" class="headerlink" title="1. 基本类型的映射"></a>1. 基本类型的映射</h3><p>在业务中常见的基本类型有Integer、Short、Float、Double、Long、Boolean、String。</p>
<h4 id="1-1-Integer"><a href="#1-1-Integer" class="headerlink" title="1.1 Integer"></a>1.1 Integer</h4><p>实体类的属性用Integer可以映射数据的int类型的字段，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;integer_number&quot;)</div><div class="line">private Integer integerNumber;</div></pre></td></tr></table></figure>
<h4 id="1-2-Short"><a href="#1-2-Short" class="headerlink" title="1.2 Short"></a>1.2 Short</h4><p>实体类的属性用Short可以映射数据的tinyint类型的字段，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;short_num&quot;)</div><div class="line">private Short shortNum;</div></pre></td></tr></table></figure>
<h4 id="1-3-Float"><a href="#1-3-Float" class="headerlink" title="1.3 Float"></a>1.3 Float</h4><p>实体类的属性用Float可以映射数据的float类型的字段，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;float_num&quot;)</div><div class="line">private Float floatNum;</div></pre></td></tr></table></figure>
<h4 id="1-3-Double"><a href="#1-3-Double" class="headerlink" title="1.3 Double"></a>1.3 Double</h4><p>实体类的属性用Double可以映射数据的double类型的字段，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;Double_num&quot;)</div><div class="line">private Double doubleNum;</div></pre></td></tr></table></figure>
<h4 id="1-4-Long"><a href="#1-4-Long" class="headerlink" title="1.4 Long"></a>1.4 Long</h4><p>实体类的属性用Long可以映射数据的bigint类型的字段，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;long_num&quot;)</div><div class="line">private Long longNum;</div></pre></td></tr></table></figure>
<h4 id="1-5-Boolean"><a href="#1-5-Boolean" class="headerlink" title="1.5 Boolean"></a>1.5 Boolean</h4><p>实体类的属性用Boolean可以映射数据的tinyint类型的字段，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;boolean_type&quot;)</div><div class="line">private Boolean booleanType;</div></pre></td></tr></table></figure>
<h4 id="1-6-String"><a href="#1-6-String" class="headerlink" title="1.6 String"></a>1.6 String</h4><p>实体类的属性用String可以映射数据的varchar或者char类型的字段，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;string_type&quot;)</div><div class="line">private String stringType;</div></pre></td></tr></table></figure>
<h3 id="2-枚举类型的映射"><a href="#2-枚举类型的映射" class="headerlink" title="2. 枚举类型的映射"></a>2. 枚举类型的映射</h3><p>实体类的属性的枚举类型，在数据库表中可以被映射为int，或者tinyint类型的字段，具体视枚举数量的多少决定，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">@Enumerated(EnumType.ORDINAL)</div><div class="line">@Column(name = &quot;sex&quot;)</div><div class="line">private Sex sex;</div></pre></td></tr></table></figure>
<p>其中Sex为枚举类型：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">public enum Sex &#123;</div><div class="line">    MALE(0,&quot;MALE&quot;),</div><div class="line">    FEMALE(1,&quot;FEMALE&quot;),</div><div class="line">    UNKNOWN(2,&quot;UNKNOWN&quot;);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="3-BigDecimal"><a href="#3-BigDecimal" class="headerlink" title="3. BigDecimal"></a>3. BigDecimal</h3><p>实体类的属性的BigDecimal类型，在数据库表中可以被映射为decimal，可以指定精度，模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;charge&quot;)</div><div class="line">private BigDecimal charge;</div></pre></td></tr></table></figure>
<h3 id="4-时间相关"><a href="#4-时间相关" class="headerlink" title="4. 时间相关"></a>4. 时间相关</h3><p>常用的时间相关的类型是Datetime和Instant，在数据库表中可以被映射为datetime，注意如果不指定datetime的精度，则默认四舍五入到秒，而Instant的默认精度是毫秒，实际值和存储值可能出现1秒的误差。模型为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Column(name = &quot;instant_date&quot;)</div><div class="line">private Instant instantDate;</div></pre></td></tr></table></figure>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Hibernate的核心功能是根据数据库到实体类的映射，自动从数据库绑定数据到实体类。&lt;/p&gt;
&lt;p&gt;这使我们操作实体类(Java对象)就能对数据库进行增、删、查、改，而不用调用JDBC API使数据操作变得简单而不繁琐。本文主要总结了实体类的各种属性类型到数据库表的映射以
    
    </summary>
    
    
      <category term="java" scheme="http://yoursite.com/tags/java/"/>
    
      <category term="hibernate" scheme="http://yoursite.com/tags/hibernate/"/>
    
  </entry>
  
  <entry>
    <title>mybatis pageHelper插件研究</title>
    <link href="http://yoursite.com/2018/07/31/mybatis-pageHelper%E6%8F%92%E4%BB%B6%E7%A0%94%E7%A9%B6/"/>
    <id>http://yoursite.com/2018/07/31/mybatis-pageHelper插件研究/</id>
    <published>2018-07-31T14:26:50.000Z</published>
    <updated>2018-07-31T14:34:01.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-mybatis插件"><a href="#1-mybatis插件" class="headerlink" title="1. mybatis插件"></a>1. mybatis插件</h2><p>在mybatis中使用插件，需要先实现接口Interceptor：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">public interface Interceptor &#123;</div><div class="line"></div><div class="line">  Object intercept(Invocation invocation) throws Throwable;</div><div class="line"></div><div class="line">  Object plugin(Object target);</div><div class="line"></div><div class="line">  void setProperties(Properties properties);</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>Interceptor中有三个方法：</p>
<ul>
<li>intercept方法：是插件的核心方法，它将直接覆盖你所拦截的对象原有的方法。intercept里有个参数Invocation对象，通过它可以反射调度原料对象的方法。</li>
<li>plugin方法：target是被拦截对象，plugin方法的作用是给被拦截对象生成一个代理对象并返回。</li>
<li>setProperties方法：允许在plugin中配置所需要的参数，该方法在插件初始化的时候就被调用一次，然后把插件对象存到配置中，以便后续取出。</li>
</ul>
<h2 id="2-PageHelper"><a href="#2-PageHelper" class="headerlink" title="2. PageHelper"></a>2. PageHelper</h2><p>PageHelper会使用ThreadLocal获取到同一线程中的变量信息，各个线程之间的Threadlocal不会相互干扰，也就是Thread1中的ThreadLocal1之后获取到Tread1中的变量的信息，不会获取到Thread2中的信息所以在多线程环境下，各个Threadlocal之间相互隔离，可以实现，不同thread使用不同的数据源或不同的Thread中执行不同的SQL语句，所以，PageHelper利用这一点通过拦截器获取到同一线程中的预编译好的SQL语句之后将SQL语句包装成具有分页功能的SQL语句，并将其再次赋值给下一步操作，所以实际执行的SQL语句就是有了分页功能的SQL语句。</p>
<p>startPage源码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">/**</div><div class="line"> * 开始分页</div><div class="line"> *</div><div class="line"> * @param pageNum  页码</div><div class="line"> * @param pageSize 每页显示数量</div><div class="line"> * @param count    是否进行count查询</div><div class="line"> */</div><div class="line">public static Page startPage(int pageNum, int pageSize, boolean count) &#123;</div><div class="line">    return startPage(pageNum, pageSize, count, null);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>最终执行的代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">public static Page startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) &#123;</div><div class="line">    Page page = new Page(pageNum, pageSize, count);</div><div class="line">    page.setReasonable(reasonable);</div><div class="line">    page.setPageSizeZero(pageSizeZero);</div><div class="line">    SqlUtil.setLocalPage(page);</div><div class="line">    return page;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>其中SqlUtil.setLocalPage是：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">public static void setLocalPage(Page page) &#123;</div><div class="line">    LOCAL_PAGE.set(page);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>LOCAL_PAGE是：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">private static final ThreadLocal&lt;Page&gt; LOCAL_PAGE = new ThreadLocal&lt;Page&gt;();</div></pre></td></tr></table></figure>
<p>PageHelper就是使用ThreadLocal存储了Page对象，在这个对象中有我们设置的pageNum、pageSize，也会查询返回的pages、total。</p>
<h2 id="3-PageHelper执行过程"><a href="#3-PageHelper执行过程" class="headerlink" title="3. PageHelper执行过程"></a>3. PageHelper执行过程</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Page page = PageHelper.startPage(pageNumber, pageSize, true);</div></pre></td></tr></table></figure>
<p>配置好PageHelper后，在代码中使用PageHelper后，LOCAL_PAGE会存入分页信息。</p>
<p>在PageHelper下的第一个查询的方法会被PageHelper拦截住，然后重新组装sql语句。具体组装的源码为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div></pre></td><td class="code"><pre><div class="line">private Object _processPage(Invocation invocation) throws Throwable &#123;</div><div class="line">    final Object[] args = invocation.getArgs();</div><div class="line">    RowBounds rowBounds = (RowBounds) args[2];</div><div class="line">    if (SqlUtil.getLocalPage() == null &amp;&amp; rowBounds == RowBounds.DEFAULT) &#123;</div><div class="line">        if (OrderByHelper.getOrderBy() != null) &#123;</div><div class="line">            OrderByHelper.processIntercept(invocation);</div><div class="line">        &#125;</div><div class="line">        return invocation.proceed();</div><div class="line">    &#125; else &#123;</div><div class="line">        //获取原始的ms</div><div class="line">        MappedStatement ms = (MappedStatement) args[0];</div><div class="line">        //判断并处理为PageSqlSource</div><div class="line">        if (!isPageSqlSource(ms)) &#123;</div><div class="line">            processMappedStatement(ms, parser);</div><div class="line">        &#125;</div><div class="line">        //忽略RowBounds-否则会进行Mybatis自带的内存分页</div><div class="line">        args[2] = RowBounds.DEFAULT;</div><div class="line">        //分页信息</div><div class="line">        Page page = getPage(rowBounds);</div><div class="line">        //pageSizeZero的判断</div><div class="line">        if ((page.getPageSizeZero() != null &amp;&amp; page.getPageSizeZero()) &amp;&amp; page.getPageSize() == 0) &#123;</div><div class="line">            COUNT.set(null);</div><div class="line">            //执行正常（不分页）查询</div><div class="line">            Object result = invocation.proceed();</div><div class="line">            //得到处理结果</div><div class="line">            page.addAll((List) result);</div><div class="line">            //相当于查询第一页</div><div class="line">            page.setPageNum(1);</div><div class="line">            //这种情况相当于pageSize=total</div><div class="line">            page.setPageSize(page.size());</div><div class="line">            //仍然要设置total</div><div class="line">            page.setTotal(page.size());</div><div class="line">            //返回结果仍然为Page类型 - 便于后面对接收类型的统一处理</div><div class="line">            return page;</div><div class="line">        &#125;</div><div class="line">        //简单的通过total的值来判断是否进行count查询</div><div class="line">        if (page.isCount()) &#123;</div><div class="line">            COUNT.set(Boolean.TRUE);</div><div class="line">            //替换MS</div><div class="line">            args[0] = msCountMap.get(ms.getId());</div><div class="line">            //查询总数</div><div class="line">            Object result = invocation.proceed();</div><div class="line">            //还原ms</div><div class="line">            args[0] = ms;</div><div class="line">            //设置总数</div><div class="line">            page.setTotal((Integer) ((List) result).get(0));</div><div class="line">            if (page.getTotal() == 0) &#123;</div><div class="line">                return page;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        //pageSize&gt;0的时候执行分页查询，pageSize&lt;=0的时候不执行相当于可能只返回了一个count</div><div class="line">        if (page.getPageSize() &gt; 0 &amp;&amp;</div><div class="line">                ((rowBounds == RowBounds.DEFAULT &amp;&amp; page.getPageNum() &gt; 0)</div><div class="line">                        || rowBounds != RowBounds.DEFAULT)) &#123;</div><div class="line">            //将参数中的MappedStatement替换为新的qs</div><div class="line">            COUNT.set(null);</div><div class="line">            BoundSql boundSql = ms.getBoundSql(args[1]);</div><div class="line">            args[1] = parser.setPageParameter(ms, args[1], boundSql, page);</div><div class="line">            COUNT.set(Boolean.FALSE);</div><div class="line">            //执行分页查询</div><div class="line">            Object result = invocation.proceed();</div><div class="line">            //得到处理结果</div><div class="line">            page.addAll((List) result);</div><div class="line">        &#125;</div><div class="line">        //返回结果</div><div class="line">        return page;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>在实际查询中打印出sql为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@25428560]</div><div class="line">==&gt;  Preparing: SELECT count(*) FROM translator t LEFT JOIN account a ON a.translator_id = t.id AND a.delete_flag = 1 WHERE t.delete_flag = 1 AND t.account_status != 0 AND t.account_status != 3 </div><div class="line">==&gt; Parameters: </div><div class="line">&lt;==    Columns: count(*)</div><div class="line">&lt;==        Row: 5</div><div class="line">&lt;==      Total: 1</div><div class="line">ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@25428560]</div><div class="line">==&gt;  Preparing: SELECT a.name as name, a.account_id as account_id, a.nick_name as nick_name, t.sex as sex, a.phone_number as phone_number, t.id as id, t.translator_level as translator_level, t.account_status as account_status FROM translator t LEFT JOIN account a on a.translator_id= t.id AND a.delete_flag = 1 WHERE t.delete_flag = 1 and t.account_status != 0 and t.account_status != 3 limit ?,? </div><div class="line">==&gt; Parameters: 0(Integer), 3(Integer)</div><div class="line">&lt;==    Columns: name, account_id, nick_name, sex, phone_number, id, translator_level, account_status</div><div class="line">&lt;==        Row: 121212@163.com, 1, 王五, 0, 13310101001, 1, null, 4</div><div class="line">&lt;==        Row: 222222@qq.com, 9, 张三, 1, 18811221122, 2, null, 4</div><div class="line">&lt;==        Row: lisi, 12, lisi, 0, 13899988223, 4, 1, 4</div><div class="line">&lt;==      Total: 3</div></pre></td></tr></table></figure>
<p>不加PageHelper打印出的sql为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@30e836ca]</div><div class="line">==&gt;  Preparing: SELECT a.name as name, a.account_id as account_id, a.nick_name as nick_name, t.sex as sex, a.phone_number as phone_number, t.id as id, t.translator_level as translator_level, t.account_status as account_status FROM translator t LEFT JOIN account a on a.translator_id= t.id AND a.delete_flag = 1 WHERE t.delete_flag = 1 and t.account_status != 0 and t.account_status != 3 </div><div class="line">==&gt; Parameters: </div><div class="line">&lt;==    Columns: name, account_id, nick_name, sex, phone_number, id, translator_level, account_status</div><div class="line">&lt;==        Row: 121212@163.com, 1, 王五, 0, 13310101001, 1, null, 4</div><div class="line">&lt;==        Row: 222222@qq.com, 9, 张三, 1, 18811221122, 2, null, 4</div><div class="line">&lt;==        Row: lisi, 12, lisi, 0, 13899988223, 4, 1, 4</div><div class="line">&lt;==        Row: wangmazi, 15, wangmazi, 0, 17722818182, 18, null, 1</div><div class="line">&lt;==        Row: xiaohua, 14, xiaohua, 1, 16322818182, 19, 0, 4</div><div class="line">&lt;==      Total: 5</div></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;1-mybatis插件&quot;&gt;&lt;a href=&quot;#1-mybatis插件&quot; class=&quot;headerlink&quot; title=&quot;1. mybatis插件&quot;&gt;&lt;/a&gt;1. mybatis插件&lt;/h2&gt;&lt;p&gt;在mybatis中使用插件，需要先实现接口Interceptor
    
    </summary>
    
    
      <category term="mybatis" scheme="http://yoursite.com/tags/mybatis/"/>
    
      <category term="PageHelper" scheme="http://yoursite.com/tags/PageHelper/"/>
    
  </entry>
  
  <entry>
    <title>hibernate和mybatis查询对比</title>
    <link href="http://yoursite.com/2018/07/31/hibernate%E5%92%8Cmybatis%E6%9F%A5%E8%AF%A2%E5%AF%B9%E6%AF%94/"/>
    <id>http://yoursite.com/2018/07/31/hibernate和mybatis查询对比/</id>
    <published>2018-07-31T14:25:39.000Z</published>
    <updated>2020-03-03T13:34:40.154Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-实体中没有引用对象"><a href="#1-实体中没有引用对象" class="headerlink" title="1. 实体中没有引用对象"></a>1. 实体中没有引用对象</h3><h4 id="1-1-实体编写实例"><a href="#1-1-实体编写实例" class="headerlink" title="1.1 实体编写实例"></a>1.1 实体编写实例</h4><ol>
<li>hibernate</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">@Entity</div><div class="line">@Table(name = &quot;translator_info_change_log&quot;)</div><div class="line">@NamedQuery(name = &quot;TranslatorInfoChangeLog.findAll&quot;, query = &quot;SELECT o FROM TranslatorInfoChangeLog o&quot;)</div><div class="line">public class TranslatorInfoChangeLog extends AbstractModel &#123;</div><div class="line">    @Id</div><div class="line">    @GeneratedValue(strategy = GenerationType.IDENTITY)</div><div class="line">    private Long id;</div><div class="line">   </div><div class="line">    @Column(name = &quot;translator_id&quot;)</div><div class="line">    private Long translatorId;</div><div class="line">    </div><div class="line">    @Column(name = &quot;details&quot;)</div><div class="line">    private String details;</div><div class="line">    </div><div class="line">    @Enumerated(EnumType.ORDINAL)</div><div class="line">    @Column(name = &quot;account_status&quot;)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>说明：</p>
<ul>
<li>@Entity：标志这是一个数据库实体（pojo）</li>
<li>@Table：对应数据库的表名</li>
<li>@Id：标识此列是数据库中的主键</li>
<li>@GeneratedValue：数据库主键的生成方式，JPA提供的四种标准用法为TABLE（使用一个特定的数据库表格来保存主键），SEQUENCE（根据底层数据库的序列来生成主键，条件是数据库支持序列），IDENTITY（主键由数据库自动生成（主要是自动增长型）），AUTO（主键由程序控制）</li>
<li>@Column：对应数据库中的列名</li>
</ul>
<ol>
<li>mybatis<br>直接使用上面JPA的数据模型就可以，也可以不加上面的所有注解，为了JPA也能使用最好按上面的方式。</li>
</ol>
<h4 id="1-2-查询编写示例"><a href="#1-2-查询编写示例" class="headerlink" title="1.2 查询编写示例"></a>1.2 查询编写示例</h4><ol>
<li>hibernate</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">public interface TranslatorInfoChangeLogRepository extends JpaRepository&lt;TranslatorInfoChangeLog, Long&gt;, JpaSpecificationExecutor&lt;TranslatorInfoChangeLog&gt; &#123;</div><div class="line">    // 使用sql语句查询</div><div class="line">    // @Query(value = &quot;select * from translator_info_change_log t where t.id = ?1&quot;, nativeQuery = true)</div><div class="line">    // 使用hql语句查询</div><div class="line">    @Query(value = &quot;select t from TranslatorInfoChangeLog where t.id = ?1&quot;)</div><div class="line">    TranslatorInfoChangeLog findChangeLog(Long id);</div><div class="line">    </div><div class="line">    @Modifying</div><div class="line">    @Query(&quot;update TranslatorInfoChangeLog p set status=0 where p.id in :ids&quot;)</div><div class="line">    void deleteTranslatorInfo(@Param(&quot;ids&quot;) List&lt;Long&gt; ids);</div><div class="line">    </div><div class="line">    TranslatorInfoChangeLog findById(Long id);</div><div class="line">    </div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>说明：</p>
<ul>
<li>需要继承JpaRepository、JpaSpecificationExecutor</li>
<li>这里只能返回本实体及其父类</li>
<li>findById转化成的SQL语句为：select * from modify_log where id = ? </li>
<li>@Modifying：标识是个更改操作</li>
<li>@Query：要执行的SQL语句，这不是原生的SQL,可以设置参数（nativeQuery = true），这就可以写原生SQL</li>
<li>示例中的findChangeLog和findById作用是一样的，findChangeLog是用sql语句进行查询，findById是jpa根据方法名称进行解析后查询。</li>
</ul>
<ol>
<li>mybatis</li>
</ol>
<ul>
<li>编写查询的接口</li>
<li>直接在查询接口对应的xml文件写sql语句</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">List&lt;TranslatorInfoChangeLog&gt; queryModifyLog(@Param(&quot;id&quot;) Long id);</div></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">&lt;select id=&quot;queryModifyLog&quot; resultType=&quot;TranslatorInfoChangeLog&quot; patameterType=&quot;Long&quot;&gt;</div><div class="line">select * from translator_info_change_log where id = ?</div><div class="line">&lt;/select&gt;</div></pre></td></tr></table></figure>
<p>其中查询语句写在<select>标签里。</select></p>
<h4 id="1-3-建议"><a href="#1-3-建议" class="headerlink" title="1.3 建议"></a>1.3 建议</h4><p>此类查询建议使用JPA完成，开发速度快，只需按规则定义方法名就可以根据不同的字段进行查询，而用mybatis需要编写不同的SQL语句。</p>
<h3 id="2-实体中存在单实体引用对象（一对一或者多对一）"><a href="#2-实体中存在单实体引用对象（一对一或者多对一）" class="headerlink" title="2. 实体中存在单实体引用对象（一对一或者多对一）"></a>2. 实体中存在单实体引用对象（一对一或者多对一）</h3><h4 id="2-1-实体编写实例（以多对一为例）"><a href="#2-1-实体编写实例（以多对一为例）" class="headerlink" title="2.1 实体编写实例（以多对一为例）"></a>2.1 实体编写实例（以多对一为例）</h4><p>台词和人物信息的关系，多个台词可能都属于同一个人物的。在多的一端（台词表）添加外键（人物的id），与人物表相关联。</p>
<ol>
<li>hibernate</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">@ManyToOne(cascade = CascadeType.REFRESH, targetEntity = Character.class, optional = true)</div><div class="line">@JoinColumn(name = &quot;character_person_id&quot;)</div><div class="line">private Character character;</div></pre></td></tr></table></figure>
<p>其中：</p>
<ul>
<li>@ManyToOne：标识关联关系，cascade标识相关级联操作，其中CascadeType.PERSIST：级联新增（又称级联保存，CascadeType.MERGE:级联合并（级联更新），CascadeType.REMOVE:级联删除，CascadeType.REFRESH:级联刷新，CascadeType.ALL:以上四种都是。</li>
<li>targetEntity 定义关系类的类型，默认是该成员属性对应的类类型，所以通常不需要提供定义。</li>
<li>optional标识是否可为空。</li>
<li>@JoinColumn：标识用于关联的字段<br>@ ManyToOne和OneToOne的fetch 属性默认值是FetchType.EAGER</li>
</ul>
<ol>
<li>Mybatis</li>
</ol>
<p>直接使用上面JPA的数据模型就可以，也可以不加上面的所有注解，为了JPA也能使用最好按上面的方式。</p>
<h4 id="2-2-查询编写示例"><a href="#2-2-查询编写示例" class="headerlink" title="2.2 查询编写示例"></a>2.2 查询编写示例</h4><ol>
<li>hibernate</li>
</ol>
<p>正常查询，因为设置了级联查询，会直接查询出来，OneToOne和ManyToOne默认不是延迟加载，所以不用设置就是及时加载.</p>
<ol>
<li>mybatis</li>
</ol>
<ul>
<li>返回模型设置</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">……</div><div class="line">&lt;association property=&quot;character&quot; column=&quot;character_person_id&quot; javaType=&quot;com.star.ott.scriptsTranslation.domain.Character&quot;&gt;</div><div class="line">    &lt;id property=&quot;id&quot; column=&quot;c_id&quot;/&gt;</div><div class="line">    &lt;result property=&quot;name&quot; column=&quot;name&quot;/&gt;</div><div class="line">    &lt;result property=&quot;englishName&quot; column=&quot;english_name&quot;/&gt;</div><div class="line">    &lt;result property=&quot;age&quot; column=&quot;age&quot;/&gt;</div><div class="line">    &lt;result property=&quot;sex&quot; column=&quot;sexjavaType=&quot;com.star.ott.scriptsTranslation.domain.enums.Sex&quot;</div><div class="line">            typeHandler=&quot;org.apache.ibatis.type.EnumOrdinalTypeHandl&quot;/&gt;</div><div class="line">    &lt;result property=&quot;remarks&quot; column=&quot;remarks&quot;/&gt;</div><div class="line">    &lt;result property=&quot;characterType&quot; column=&quot;character_type&quot;</div><div class="line">            javaType=&quot;com.star.ott.scriptsTranslation.domain.enums.CracterType&quot;</div><div class="line">            typeHandler=&quot;org.apache.ibatis.type.EnumOrdinalTypeHandl&quot;/&gt;</div><div class="line">    &lt;result property=&quot;dramaId&quot; column=&quot;dramaId&quot;/&gt;</div><div class="line">&lt;/association&gt;</div><div class="line">……</div></pre></td></tr></table></figure>
<ul>
<li>查询语言需要做关联</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&lt;select id=&quot;&quot; resultMap=&quot;&quot;&gt;</div><div class="line">……</div><div class="line">LEFT JOIN character_person c ON c.id = l.character_person_id AND c.delete_flag = 1</div><div class="line">……</div><div class="line">&lt;/select&gt;</div></pre></td></tr></table></figure>
<ul>
<li>一对一和多对一，实体映射用<association>标签。</association></li>
</ul>
<h4 id="2-3-建议"><a href="#2-3-建议" class="headerlink" title="2.3 建议"></a>2.3 建议</h4><p>此类查询建议使用JPA完成，开发速度快，只需要设置实体的注解，mybatis需要join语句，还需要设置返回的resultMap。</p>
<h3 id="3-实体中存在多实体引用对象（一对多）"><a href="#3-实体中存在多实体引用对象（一对多）" class="headerlink" title="3. 实体中存在多实体引用对象（一对多）"></a>3. 实体中存在多实体引用对象（一对多）</h3><p>例如剧集下存在多个台词，剧集是“一”的一方，台词是“多”的一方，在多的一端（台词表）添加外键（剧集的id），与剧集表相关联。</p>
<h4 id="3-1-实体编写示例"><a href="#3-1-实体编写示例" class="headerlink" title="3.1 实体编写示例"></a>3.1 实体编写示例</h4><ol>
<li>hibernate</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">……</div><div class="line">@OneToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)</div><div class="line">@JoinColumn(name=&quot;episodeid&quot;)</div><div class="line">@Where(clause = &quot;delete_flag = 1&quot;)</div><div class="line">@OrderBy(value = &quot;id asc&quot;)</div><div class="line">private List&lt;Line&gt; lines;</div><div class="line">……</div></pre></td></tr></table></figure>
<p>说明：</p>
<ul>
<li>@OneToMany：标识关联关系，Cascade标识相关级联操作，fetch标识加载方式是懒加载还是及时加载。</li>
<li>@Where：过滤条件，只查询符合条件的级联表里的记录。</li>
<li>@OrderBy：对查询的记录进行按字段排序。</li>
<li>默认是延迟加载。</li>
</ul>
<ol>
<li>mybatis</li>
</ol>
<p>直接使用上面JPA的数据模型就可以，也可以不加上面的所有注解，为了hibernate也能使用最好按上面的方式。</p>
<h4 id="3-2-查询编写示例"><a href="#3-2-查询编写示例" class="headerlink" title="3.2 查询编写示例"></a>3.2 查询编写示例</h4><ol>
<li>hibernate</li>
</ol>
<p>正常查询，因为设置了级联查询，会直接查询出来，OneToMany默认是延迟加载</p>
<ol>
<li>Mybatis</li>
</ol>
<ul>
<li>返回model设置</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">……</div><div class="line">&lt;collection property=&quot;sourceLanguages&quot; column=&quot;line_id&quot; ofType=&quot;com.star.ott.scriptsTranslation.domain.TranslationLine&quot;&gt;</div><div class="line">    &lt;id property=&quot;id&quot; column=&quot;tl_id&quot;/&gt;</div><div class="line">    &lt;result property=&quot;translationTaskId&quot; column=&quot;translation_task_id&quot;/&gt;</div><div class="line">    &lt;result property=&quot;lineId&quot; column=&quot;line_id&quot;/&gt;</div><div class="line">    &lt;result property=&quot;lineStr&quot; column=&quot;line_str&quot;/&gt;</div><div class="line">    &lt;result property=&quot;language&quot; column=&quot;language&quot;</div><div class="line">            javaType=&quot;com.star.ott.scriptsTranslation.domain.enums.Language&quot;</div><div class="line">            typeHandler=&quot;org.apache.ibatis.type.EnumOrdinalTypeHandler&quot;/&gt;</div><div class="line">&lt;/collection&gt;</div><div class="line">……</div></pre></td></tr></table></figure>
<ul>
<li>查询语言需要做关联</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&lt;select id=&quot;&quot; resultMap=&quot;&quot;&gt;</div><div class="line">……</div><div class="line">LEFT JOIN translation_line tl ON tl.line_id = l.id AND tl.delete_flag = 1 AND tl.translation_task_id IS NULL</div><div class="line">……</div><div class="line">&lt;/select&gt;</div></pre></td></tr></table></figure>
<ul>
<li>一对多，实体映射用<collection>标签。</collection></li>
<li><strong>collection和association标签的实体映射需要放在一起，不能交叉放置，否则会出现错误。</strong></li>
</ul>
<h4 id="3-3-建议"><a href="#3-3-建议" class="headerlink" title="3.3 建议"></a>3.3 建议</h4><p>此类查询建议使用hibernate完成，开发速度快，只需要设置实体的注解，mybatis需要join语句，还需要设置返回的resultMap但是执行效率上mybatis会更好一些，如果数据量大，就需要测试后选择。</p>
<h3 id="4-两个实体间存在中间实体关联（多对多）"><a href="#4-两个实体间存在中间实体关联（多对多）" class="headerlink" title="4. 两个实体间存在中间实体关联（多对多）"></a>4. 两个实体间存在中间实体关联（多对多）</h3><h4 id="4-1-两张表与关系表OneToMany级联，关系表与两张表ManyToOne级联。"><a href="#4-1-两张表与关系表OneToMany级联，关系表与两张表ManyToOne级联。" class="headerlink" title="4.1 两张表与关系表OneToMany级联，关系表与两张表ManyToOne级联。"></a>4.1 两张表与关系表OneToMany级联，关系表与两张表ManyToOne级联。</h4><p><strong>例1：</strong> 用户组与权限的关系，一个用户组可以拥有多个权限，一个权限又可包含多个用户组。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/new_15.png" alt="image"></p>
<h5 id="4-1-1-实体编写示例"><a href="#4-1-1-实体编写示例" class="headerlink" title="4.1.1 实体编写示例"></a>4.1.1 实体编写示例</h5><ol>
<li>hibernate</li>
</ol>
<p>Role实体</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">@Entity</div><div class="line">@Table(name=&quot;glw_role&quot;)</div><div class="line">public class Role&#123;</div><div class="line">    @Id</div><div class="line">    @GeneratedValue(strategy=GenerationType.TABLE)</div><div class="line">    private Long id;</div><div class="line">    @Column(length=50)</div><div class="line">    private String name;</div><div class="line">    @OneToMany(mappedBy=&quot;role&quot;,cascade=CascadeType.ALL)</div><div class="line">    private Set&lt;RoleResource&gt; roleResources = new HashSet&lt;RoleResource&gt;();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>Resource实体</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">@Entity</div><div class="line">@Table(name=&quot;glw_resource&quot;)</div><div class="line">public class Resource&#123;</div><div class="line">    @Id</div><div class="line">    @GeneratedValue(strategy=GenerationType.TABLE)</div><div class="line">    private Long id;</div><div class="line">    @Column(length=50)</div><div class="line">    private String name;</div><div class="line">    @OneToMany(mappedBy=&quot;resource&quot;,cascade=CascadeType.ALL)</div><div class="line">    private Set&lt;RoleResource&gt; roleResources = new HashSet&lt;RoleResource&gt;();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>RoleResource辅助实体</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">@Entity</div><div class="line">@Table(name=&quot;glw_role_resource&quot;)</div><div class="line">public class Resource&#123;</div><div class="line">    @Id</div><div class="line">    @GeneratedValue(strategy=GenerationType.TABLE)</div><div class="line">    private Long id;</div><div class="line">    @Column</div><div class="line">    private Integer sort;</div><div class="line">    @ManyToOne(cascade=Cascade.ALL)</div><div class="line">    @JoinColumn(name=&quot;roleId&quot;,nullable=true)</div><div class="line">    private Role role;</div><div class="line">    @ManyToOne(cascade=Cascade.ALL)</div><div class="line">    @JoinColumn(name=&quot;resourceId&quot;,nullable=true)</div><div class="line">    private Resource resource;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ol>
<li>mybatis<br>直接使用上面JPA的数据模型就可以，也可以不加上面的所有注解，为了JPA也能使用最好按上面的方式。</li>
</ol>
<h5 id="4-1-2-查询示例编写"><a href="#4-1-2-查询示例编写" class="headerlink" title="4.1.2 查询示例编写"></a>4.1.2 查询示例编写</h5><ol>
<li><p>hibernate<br>正常查询，因为设置了级联查询，会直接查询出来，（查询不同的Dao就会对应生成不同的实体）。</p>
</li>
<li><p>mybatis</p>
</li>
</ol>
<ul>
<li>返回model设置对应的 association 或 collection</li>
<li>正常sql语句的链表查询</li>
</ul>
<h4 id="4-2-两张表与关系表相对各自独立，没有完整级联"><a href="#4-2-两张表与关系表相对各自独立，没有完整级联" class="headerlink" title="4.2 两张表与关系表相对各自独立，没有完整级联"></a>4.2 两张表与关系表相对各自独立，没有完整级联</h4><p><strong>例2：</strong> 例如在translator中的，译员的母语和语言tag之间是多对多关系，tag是数据字典，初始化值后便不会改变。因为语言tag不仅母语需要使用，擅长语言这个属性也需要使用，所以中间表需要加字段“tag类型”用以区分是tag是被谁在使用。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/23.png" alt="image"></p>
<h5 id="4-2-1-实体编写示例"><a href="#4-2-1-实体编写示例" class="headerlink" title="4.2.1 实体编写示例"></a>4.2.1 实体编写示例</h5><ol>
<li>hibernate</li>
</ol>
<p>translator和中间表之间一对多级联，中间表和数据字典之间不做级联，可以由translator对中间表进行crud操作，中间表和数据字典之间的关系需要手动维护。</p>
<p>translator</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">……</div><div class="line">@OneToMany(cascade = &#123;CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE&#125;, fetch = FetchType.LAZY, orphanRemoval = true)</div><div class="line">@JoinColumn(name = &quot;translator_id&quot;)</div><div class="line">@Where(clause = &quot;delete_flag = 1 and dict_type = 2&quot;)</div><div class="line">private Set&lt;TranslatorDictRelation&gt; motherTongue;</div><div class="line">……</div></pre></td></tr></table></figure>
<p>tag</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">……</div><div class="line">@Id</div><div class="line">@GeneratedValue(strategy = GenerationType.IDENTITY)</div><div class="line">private Long id;</div><div class="line">/**</div><div class="line"> * 数据字典类型</div><div class="line"> */</div><div class="line">@Enumerated(EnumType.ORDINAL)</div><div class="line">@Column(name = &quot;dict_type&quot;)</div><div class="line">private DictType dictType;</div><div class="line">/**</div><div class="line"> * 标签号</div><div class="line"> */</div><div class="line">@Column(name = &quot;tag_num&quot;)</div><div class="line">private Integer tagNum;</div><div class="line">/**</div><div class="line"> * 标签值</div><div class="line"> */</div><div class="line">@Column(name = &quot;tag_value&quot;)</div><div class="line">private String tagValue;</div><div class="line">……</div></pre></td></tr></table></figure>
<p>TranslatorDictRelation</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">……</div><div class="line">@Id</div><div class="line">@GeneratedValue(strategy = GenerationType.IDENTITY)</div><div class="line">private Long id;</div><div class="line">/**</div><div class="line"> * 译员id</div><div class="line"> */</div><div class="line">@Column(name = &quot;translator_id&quot;)</div><div class="line">private Long translatorId;</div><div class="line">/**</div><div class="line"> * tag id</div><div class="line"> */</div><div class="line">@Column(name = &quot;dict_id&quot;)</div><div class="line">private Long dictId;</div><div class="line">/**</div><div class="line"> * 数据字典类型</div><div class="line"> */</div><div class="line">@Enumerated(EnumType.ORDINAL)</div><div class="line">@Column(name = &quot;dict_type&quot;)</div><div class="line">private DictType dictType;</div><div class="line">……</div></pre></td></tr></table></figure>
<ol>
<li>mybatis</li>
</ol>
<p>直接使用上面JPA的数据模型就可以，也可以不加上面的所有注解，为了JPA也能使用最好按上面的方式。</p>
<h5 id="4-2-2-查询示例编写"><a href="#4-2-2-查询示例编写" class="headerlink" title="4.2.2 查询示例编写"></a>4.2.2 查询示例编写</h5><ol>
<li><p>hibernate<br>正常查询，因为设置了级联查询，会直接查询出来，（查询不同的Dao就会对应生成不同的实体）。</p>
</li>
<li><p>mybatis</p>
</li>
</ol>
<ul>
<li>返回model设置对应的 association 或 collection</li>
<li>正常sql语句的链表查询</li>
</ul>
<h4 id="4-3-两张表用ManyToMany级联，实体映射上不与关系表级联"><a href="#4-3-两张表用ManyToMany级联，实体映射上不与关系表级联" class="headerlink" title="4.3 两张表用ManyToMany级联，实体映射上不与关系表级联"></a>4.3 两张表用ManyToMany级联，实体映射上不与关系表级联</h4><p><strong>例3：</strong> 两张多对多表关系均可能出现crud操作，中间表除了作为外键的字段和唯一id，没有其他字段。</p>
<h5 id="4-3-1-实体编写示例"><a href="#4-3-1-实体编写示例" class="headerlink" title="4.3.1 实体编写示例"></a>4.3.1 实体编写示例</h5><p>TranslatorTest<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line">@Entity</div><div class="line">@Table(name = &quot;translator_test&quot;)</div><div class="line">@NamedQuery(name = &quot;TranslatorTest.findAll&quot;, query = &quot;SELECT o FROM TranslatorTest o&quot;)</div><div class="line">public class TranslatorTest &#123;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 主键</div><div class="line">     */</div><div class="line">    @Id</div><div class="line">    @GeneratedValue(strategy = GenerationType.IDENTITY)</div><div class="line">    private Long id;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 证件号</div><div class="line">     */</div><div class="line">    @Column(name = &quot;credential_number&quot;)</div><div class="line">    private String credentialNumber;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 空闲时段</div><div class="line">     */</div><div class="line">    @ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)</div><div class="line">    @JoinTable(name = &quot;translator_dict_relation_test&quot;,</div><div class="line">            joinColumns = &#123;@JoinColumn(name = &quot;translator_id&quot;)&#125;,  // 由哪端进行维护</div><div class="line">            inverseJoinColumns = &#123;@JoinColumn(name = &quot;dict_id&quot;)&#125;) // 可由两张表进行维护关系和查询，用不同的dao就行</div><div class="line">    @OrderBy(&quot;id&quot;)</div><div class="line">    private Set&lt;TagTest&gt; freeTime = new HashSet&lt;&gt;();</div><div class="line">    ……</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>TagTest<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">@Entity</div><div class="line">@Table(name = &quot;tag_test&quot;)</div><div class="line">@NamedQuery(name = &quot;TagTest.findAll&quot;, query = &quot;SELECT o FROM TagTest o&quot;)</div><div class="line">public class TagTest &#123;</div><div class="line">    /**</div><div class="line">     * 主键</div><div class="line">     */</div><div class="line">    @Id</div><div class="line">    @GeneratedValue(strategy = GenerationType.IDENTITY)</div><div class="line">    private Long id;</div><div class="line">    /**</div><div class="line">     * 标签值</div><div class="line">     */</div><div class="line">    @Column(name = &quot;tag_value&quot;)</div><div class="line">    private String tagValue;</div><div class="line"></div><div class="line">    @ManyToMany(mappedBy = &quot;freeTime&quot;, cascade = CascadeType.ALL) // 映射的字段</div><div class="line">    private Set&lt;TranslatorTest&gt; translatorTestSet = new HashSet&lt;&gt;();</div><div class="line">    ……</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>中间关系表不需要映射实体，但是需要创建出来：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">CREATE TABLE `translator_dict_relation_test`(</div><div class="line">  `id`                 bigint(20)       NOT NULL AUTO_INCREMENT               COMMENT &apos;标识&apos;,</div><div class="line">  `translator_id`      bigint(20)                                             COMMENT &apos;译员项标识&apos;,</div><div class="line">  `dict_id`            bigint(20)                                             COMMENT &apos;数据字典标识&apos;,</div><div class="line">  PRIMARY KEY (`id`),</div><div class="line">  KEY `FK_translator_dict_id` (`translator_id`),</div><div class="line">  KEY `FK_dict_translator_id` (`dict_id`),</div><div class="line">  CONSTRAINT `FK_translator_dict_id` FOREIGN KEY (`translator_id`) REFERENCES `translator_test` (`id`),</div><div class="line">  CONSTRAINT `FK_dict_translator_id` FOREIGN KEY (`dict_id`) REFERENCES `tag_test` (`id`)</div><div class="line">)ENGINE=InnoDB</div><div class="line">COMMENT = &apos;translator_dict_relation_test&apos;</div><div class="line">CHARACTER SET utf8 COLLATE utf8_general_ci;</div></pre></td></tr></table></figure>
<h5 id="4-3-2-查询示例编写"><a href="#4-3-2-查询示例编写" class="headerlink" title="4.3.2 查询示例编写"></a>4.3.2 查询示例编写</h5><ol>
<li>JPA<br>正常进行crud操作，JPA会自动对关联的表以及中间表进行相关操作。</li>
<li>mybatis<br>正常sql语句的链表查询。</li>
</ol>
<h4 id="4-4-建议"><a href="#4-4-建议" class="headerlink" title="4.4 建议"></a>4.4 建议</h4><ul>
<li>此类查询建议使用mybatis完成，使用JPA设置关联关系存在限制性，又容易出现循环关联，查询时不灵活等问题。</li>
<li>多对多关系的解除由关系维护端来完成，关系被维护端不能解除关系，但是此处需要注意，不建议用级联修改关系。</li>
<li>多对多关系可以不做表间的外键，而是对单独的表进行增删改查，再维护对应关系表。</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;1-实体中没有引用对象&quot;&gt;&lt;a href=&quot;#1-实体中没有引用对象&quot; class=&quot;headerlink&quot; title=&quot;1. 实体中没有引用对象&quot;&gt;&lt;/a&gt;1. 实体中没有引用对象&lt;/h3&gt;&lt;h4 id=&quot;1-1-实体编写实例&quot;&gt;&lt;a href=&quot;#1-1-实
    
    </summary>
    
    
      <category term="java" scheme="http://yoursite.com/tags/java/"/>
    
      <category term="hibernate" scheme="http://yoursite.com/tags/hibernate/"/>
    
      <category term="mybatis" scheme="http://yoursite.com/tags/mybatis/"/>
    
  </entry>
  
  <entry>
    <title>java BigDecimal比较大小compareTo和equals</title>
    <link href="http://yoursite.com/2018/03/13/java-BigDecimal%E6%AF%94%E8%BE%83%E5%A4%A7%E5%B0%8FcompareTo%E5%92%8Cequals/"/>
    <id>http://yoursite.com/2018/03/13/java-BigDecimal比较大小compareTo和equals/</id>
    <published>2018-03-13T11:41:57.000Z</published>
    <updated>2018-03-13T11:44:20.000Z</updated>
    
    <content type="html"><![CDATA[<p>Java在java.math包中提供的API类BigDecimal，用来对超过16位有效位的数进行精确的运算，常用BigDecimal来存金额。在对金额进行校验的时候发生异常，后来发现是比较大小的方法equals导致的。</p>
<p>BigDecimal的equals方法源码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">public boolean equals(Object x) &#123;</div><div class="line">    if (!(x instanceof BigDecimal))</div><div class="line">        return false;</div><div class="line">    BigDecimal xDec = (BigDecimal) x;</div><div class="line">    if (x == this)</div><div class="line">        return true;</div><div class="line">    if (scale != xDec.scale)</div><div class="line">        return false;</div><div class="line">    long s = this.intCompact;</div><div class="line">    long xs = xDec.intCompact;</div><div class="line">    if (s != INFLATED) &#123;</div><div class="line">        if (xs == INFLATED)</div><div class="line">            xs = compactValFor(xDec.intVal);</div><div class="line">        return xs == s;</div><div class="line">    &#125; else if (xs != INFLATED)</div><div class="line">        return xs == compactValFor(this.intVal);</div><div class="line">    return this.inflated().equals(xDec.inflated());</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>equals方法不仅比较了两个数的大小，还比较了精度是否相等，当出现5和5.0这种，就会被认为是不相等的。</p>
<p>compareTo的源码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">public int compareTo(BigDecimal val) &#123;</div><div class="line">    // Quick path for equal scale and non-inflated case.</div><div class="line">    if (scale == val.scale) &#123;</div><div class="line">        long xs = intCompact;</div><div class="line">        long ys = val.intCompact;</div><div class="line">        if (xs != INFLATED &amp;&amp; ys != INFLATED)</div><div class="line">            return xs != ys ? ((xs &gt; ys) ? 1 : -1) : 0;</div><div class="line">    &#125;</div><div class="line">    int xsign = this.signum();</div><div class="line">    int ysign = val.signum();</div><div class="line">    if (xsign != ysign)</div><div class="line">        return (xsign &gt; ysign) ? 1 : -1;</div><div class="line">    if (xsign == 0)</div><div class="line">        return 0;</div><div class="line">    int cmp = compareMagnitude(val);</div><div class="line">    return (xsign &gt; 0) ? cmp : -cmp;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>compareTo方法只比较了两个数的大小，没有比较精度是否相等，当出现5和5.0这种，就会被认为是相等的。</p>
<p>BigDecimal常用方法：</p>
<ol>
<li>add(BigDecimal)：BigDecimal对象中的值相加，然后返回这个对象。</li>
<li>subtract(BigDecimal)：BigDecimal对象中的值相减，然后返回这个对象。</li>
<li>multiply(BigDecimal)：BigDecimal对象中的值相乘，然后返回这个对象。</li>
<li>divide(BigDecimal)：BigDecimal对象中的值相除，然后返回这个对象。</li>
<li>toString()：将BigDecimal对象的数值转换成字符串。</li>
<li>doubleValue()：将BigDecimal对象中的值以双精度数返回。</li>
<li>floatValue()：将BigDecimal对象中的值以单精度数返回。</li>
<li>longValue()：将BigDecimal对象中的值以长整数返回。</li>
<li>intValue()：将BigDecimal对象中的值以整数返回。</li>
</ol>
<p>相关文章：<a href="http://blog.csdn.net/lisongjia123/article/details/51232657" target="_blank" rel="external">http://blog.csdn.net/lisongjia123/article/details/51232657</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Java在java.math包中提供的API类BigDecimal，用来对超过16位有效位的数进行精确的运算，常用BigDecimal来存金额。在对金额进行校验的时候发生异常，后来发现是比较大小的方法equals导致的。&lt;/p&gt;
&lt;p&gt;BigDecimal的equals方法
    
    </summary>
    
    
      <category term="java" scheme="http://yoursite.com/tags/java/"/>
    
      <category term="BigDecimal" scheme="http://yoursite.com/tags/BigDecimal/"/>
    
  </entry>
  
  <entry>
    <title>jpa联表查询对子表进行crud操作</title>
    <link href="http://yoursite.com/2018/03/13/jpa%E8%81%94%E8%A1%A8%E6%9F%A5%E8%AF%A2%E5%AF%B9%E5%AD%90%E8%A1%A8%E8%BF%9B%E8%A1%8Ccrud%E6%93%8D%E4%BD%9C/"/>
    <id>http://yoursite.com/2018/03/13/jpa联表查询对子表进行crud操作/</id>
    <published>2018-03-13T11:41:05.000Z</published>
    <updated>2019-04-10T11:18:03.000Z</updated>
    
    <content type="html"><![CDATA[<p>jpa使用@OneToMany注解进行两个表之间一对多的关联以方便级联查询。当需要对关联的表进行增删改操作的时候，需要根据实际需求选择合适的方法。</p>
<p>order表的模型，省略get和set。其中order和installment是一对多关系</p>
<p>Order.java<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line">@Entity</div><div class="line">@Table(name = &quot;order&quot;)</div><div class="line">@NamedQuery(name = &quot;Order.findAll&quot;, query = &quot;SELECT o FROM Order o&quot;)</div><div class="line">public class Order extends AbstractModel &#123;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 主键</div><div class="line">     */</div><div class="line">    @Id</div><div class="line">    @GeneratedValue(strategy = GenerationType.IDENTITY)</div><div class="line">    private Long id;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 订单号</div><div class="line">     */</div><div class="line">    @Column(name = &quot;order_number&quot;)</div><div class="line">    private String orderNumber;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 总价</div><div class="line">     */</div><div class="line">    @Column(name = &quot;quoted_price&quot;)</div><div class="line">    private BigDecimal quotedPrice;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 分期信息</div><div class="line">     */</div><div class="line">    @OneToMany(cascade = &#123;CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE&#125;, fetch = FetchType.LAZY, orphanRemoval = true)</div><div class="line">    @JoinColumn(name = &quot;order_id&quot;)</div><div class="line">    @Where(clause = &quot;delete_flag = 1&quot;)</div><div class="line">    private List&lt;Installment&gt; installments;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 删除状态</div><div class="line">     */</div><div class="line">    @Enumerated(EnumType.ORDINAL)</div><div class="line">    @Column(name = &quot;delete_flag&quot;)</div><div class="line">    private Status status;</div><div class="line">    ……</div></pre></td></tr></table></figure></p>
<p>Installment.java</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line">@Entity</div><div class="line">@Table(name = &quot;installment&quot;)</div><div class="line">@NamedQuery(name = &quot;Installment.findAll&quot;, query = &quot;SELECT o FROM Installment o&quot;)</div><div class="line">public class Installment extends AbstractModel &#123;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 分期主键</div><div class="line">     */</div><div class="line">    @Id</div><div class="line">    @GeneratedValue(strategy = GenerationType.IDENTITY)</div><div class="line">    private Long id;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 订单标识</div><div class="line">     */</div><div class="line">    @Column(name = &quot;order_id&quot;)</div><div class="line">    private Long orderId;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 分期金额</div><div class="line">     */</div><div class="line">    @Column(name = &quot;quoted_price&quot;)</div><div class="line">    private BigDecimal quotedPrice;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 删除标识</div><div class="line">     */</div><div class="line">    @Enumerated(EnumType.ORDINAL)</div><div class="line">    @Column(name = &quot;delete_flag&quot;)</div><div class="line">    private Status status;</div><div class="line">    ……</div></pre></td></tr></table></figure>
<h3 id="1-被关联表的id不作为其他表的外键关联"><a href="#1-被关联表的id不作为其他表的外键关联" class="headerlink" title="1. 被关联表的id不作为其他表的外键关联"></a>1. 被关联表的id不作为其他表的外键关联</h3><p>如果被关联的表的id没有被其他表的外键关联，可以进行“全删全加”进行被关联表的进行增删改的操作。</p>
<p>fillingModifyPara</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">……</div><div class="line">order.setOrderNumber(orderDTO.getOrderNumbber());</div><div class="line">order.setQuotedPrice(orderDTO.getQuotedPrice());</div><div class="line">List&lt;InstallmentDTO&gt; installmentDTOs = orderDTO.getInstallments();</div><div class="line">List&lt;Installment&gt; installments = new ArrayList&lt;&gt;();</div><div class="line">for (InstallmentDTO installmentDTO : installmentDTOs) &#123;</div><div class="line">//可以写构造函数进行初始操作</div><div class="line">    Installment installment = new Installment();</div><div class="line">    installment.setOrderId();//修改操作，orderId由前端传入</div><div class="line">    installment.setQuotedPrice(installmentDTO.gettQuotedPrice());</div><div class="line">    installments.add(installment);</div><div class="line">&#125;</div><div class="line">order.getInstallments().clear();</div><div class="line">order.getInstallments().addAll(installments);</div></pre></td></tr></table></figure>
<p>这种方法将原来关联的表的相关内容先物理删除，再将新的内容全部添加上去。优点是方便简单，没有冗余数据；缺点是被关联的表的id不能作为其他表的外键关联。这里注意不要对installment再定义一个对象后进行order.set的操作，这样会出现两个对象而报错，详情可以参看hb源码。</p>
<h3 id="2-被关联表的id可能还关联了一堆乱七八糟的东西"><a href="#2-被关联表的id可能还关联了一堆乱七八糟的东西" class="headerlink" title="2. 被关联表的id可能还关联了一堆乱七八糟的东西"></a>2. 被关联表的id可能还关联了一堆乱七八糟的东西</h3><p>当被关联表的id可能还关联了一堆乱七八糟的东西的时候，就不能进行简单的“全删全加”操作来修改关联表的内容。这个时候的做法是现将从数据库中查出的联表信息全部逻辑删除（status设为无效），然后与传入的进行比对，id相同的将status设置为有效，并修改内容；传入的DTO中没有id的作为新增的加入。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">order.setOrderNumber(orderDTO.getOrderNumbber());</div><div class="line">order.setQuotedPrice(quotationDTO.getQuotedPrice());</div><div class="line">List&lt;Installment&gt; installments = orders.getInstallments();</div><div class="line">List&lt;InstallmentDTO&gt; installmentDTOS = quotationDTO.getInstallmentList();</div><div class="line">List&lt;Installment&gt; newInstallments = new ArrayList&lt;&gt;();</div><div class="line">installments.forEach(installment -&gt; &#123;installment.setStatus(Status.INVALID);&#125;);</div><div class="line">for (InstallmentDTO installmentDTO : installmentDTOS) &#123;</div><div class="line">    for (Installment installment : installments) &#123;</div><div class="line">        if (installment.getId().equals(installmentDTO.getId())) &#123;</div><div class="line">            installment.setStatus(Status.VALID);</div><div class="line">            installment.setQuotedPrice(installmentDTO.getQuotedPrice());</div><div class="line">            installment.setOrderId();</div><div class="line">                &#125;</div><div class="line">        &#125;</div><div class="line">        if (installmentDTO.getId() == null) &#123;</div><div class="line">            Installment installment = new Installment();</div><div class="line">            installment.setOrderId();</div><div class="line">            installment.setQuotedPrice(installmentDTO.gettQuotedPrice());</div><div class="line">            newInstallments.add(newInstallment);</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">installments.addAll(newInstallments);</div><div class="line">orders.setInstallments(installments);</div></pre></td></tr></table></figure>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;jpa使用@OneToMany注解进行两个表之间一对多的关联以方便级联查询。当需要对关联的表进行增删改操作的时候，需要根据实际需求选择合适的方法。&lt;/p&gt;
&lt;p&gt;order表的模型，省略get和set。其中order和installment是一对多关系&lt;/p&gt;
&lt;p&gt;Ord
    
    </summary>
    
    
      <category term="java" scheme="http://yoursite.com/tags/java/"/>
    
      <category term="jpa" scheme="http://yoursite.com/tags/jpa/"/>
    
  </entry>
  
  <entry>
    <title>Python2.7 pytesser提取图片中文字信息</title>
    <link href="http://yoursite.com/2017/11/30/Python2-7-pytesser%E6%8F%90%E5%8F%96%E5%9B%BE%E7%89%87%E4%B8%AD%E6%96%87%E5%AD%97%E4%BF%A1%E6%81%AF/"/>
    <id>http://yoursite.com/2017/11/30/Python2-7-pytesser提取图片中文字信息/</id>
    <published>2017-11-30T12:30:34.000Z</published>
    <updated>2020-03-03T13:27:52.797Z</updated>
    
    <content type="html"><![CDATA[<p>图片中存在大段的文字，用手敲实在是费劲。很多在线转换的工具，速度慢，并且达到一定次数后会收费。贫穷让我自力更生，查到python可以通过安装pytesseract库来进行文字识别。而pytesser调用了tesseract，因此还需要安装Tesseract-OCR软件（OCR：Optical Character Recognition，即光学字符识别技术，专门用于对图片文字进行识别，并获取文本。tesseract-ocr引擎先由HP实验室研发，后来成为一个开源项目，主要由google进行改进优化）。另外还需要用的图像处理库PIL库。</p>
<p>网上相关资料很多，在安装过程中出现了很多问题，特将安装方法和遇到的问题做个记录，方便日后查阅。</p>
<p>运行环境为：OS X Yosemite 10.10.5</p>
<h2 id="1-安装PIL和pytesseract库"><a href="#1-安装PIL和pytesseract库" class="headerlink" title="1.安装PIL和pytesseract库"></a>1.安装PIL和pytesseract库</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">RosyMacBook-Pro:~ Rosy$ pip install pytesseract</div><div class="line">RosyMacBook-Pro:~ Rosy$ pip install PIL</div></pre></td></tr></table></figure>
<h2 id="2-安装Tesseract-OCR"><a href="#2-安装Tesseract-OCR" class="headerlink" title="2.安装Tesseract-OCR"></a>2.安装Tesseract-OCR</h2><h3 id="2-1-安装homebrew"><a href="#2-1-安装homebrew" class="headerlink" title="2.1 安装homebrew"></a>2.1 安装homebrew</h3><p>Homebrew是MacOS上的包管理器，类似于ubuntu中的apt-get，centos中的yum。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">RosyMacBook-Pro:~ Rosy$ ruby -e &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)&quot;</div></pre></td></tr></table></figure>
<p>安装好后，可用 <em>brew -v</em> 查看认是否安装成功。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">RosyMacBook-Pro:~ Rosy$ brew -v</div><div class="line">Homebrew 1.3.8</div><div class="line">Homebrew/homebrew-core (git revision e939; last commit 2017-11-29)</div></pre></td></tr></table></figure>
<h3 id="2-2-安装tesseract"><a href="#2-2-安装tesseract" class="headerlink" title="2.2 安装tesseract"></a>2.2 安装tesseract</h3><p>安装tesseract会提示 <strong><em>tesseract:XQuartz is required to install this formula. X11Requirment unsatiffied!</em></strong> </p>
<p><strong>需要先安装xquartz！</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">// 安装xquartz</div><div class="line">RosyMacBook-Pro:~ Rosy$ brew cask install xquartz</div><div class="line">// 安装tesseract的同时安装训练工具</div><div class="line">RosyMacBook-Pro:~ Rosy$ brew install --with-training-tools tesseract</div></pre></td></tr></table></figure>
<h3 id="2-3-安装Tesseract-OCR语言包配置"><a href="#2-3-安装Tesseract-OCR语言包配置" class="headerlink" title="2.3 安装Tesseract-OCR语言包配置"></a>2.3 安装Tesseract-OCR语言包配置</h3><p>默认安装的是eng英文语言包。如果需要对汉语进行提取，则需要安装汉语语言包。</p>
<p>语言包下载地址：<a href="https://github.com/tesseract-ocr/tessdata" target="_blank" rel="external">https://github.com/tesseract-ocr/tessdata</a></p>
<p>下载chi_sim.traineddata（中文简体），将其拷贝到/usr/local/Cellar/tesseract/3.05.01/share/tessdata下。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/9.jpeg" alt="image"></p>
<p>验证Tesseract-OCR：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">RosyMacBook-Pro:~ Rosy$ tesseract /Users/Rosy/Desktop/test.png /Users/Rosy/Desktop/test -l chi_sim</div><div class="line">Tesseract Open Source OCR Engine v3.05.01 with Leptonica</div><div class="line">RosyMacBook-Pro:~ Rosy$</div></pre></td></tr></table></figure>
<p>其中 <em>-l</em> 表示识别的语言，test是输出到文件txt的名字。在指定路径下能够看到生成的txt文件。</p>
<p>png格式的图片，提前其中的文字信息</p>
<p><img src="http://106.13.2.39:8888/blog_pic/10.png" alt="image"></p>
<p>提取后的效果：<br><img src="http://106.13.2.39:8888/blog_pic/new_8.png" alt="image"></p>
<h2 id="3-python程序提取图片信息"><a href="#3-python程序提取图片信息" class="headerlink" title="3.python程序提取图片信息"></a>3.python程序提取图片信息</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">#coding:utf-8</div><div class="line">import sys </div><div class="line">import pytesseract</div><div class="line">from PIL import Image</div><div class="line"></div><div class="line">reload(sys)</div><div class="line">sys.setdefaultencoding(&apos;utf-8&apos;)</div><div class="line">image = Image.open(&quot;/Users/Rosy/Desktop/test.png&quot;)</div><div class="line">text = pytesseract.image_to_string(image,lang=&apos;chi_sim&apos;) </div><div class="line">with open(&quot;/Users/Rosy/Desktop/output.txt&quot;, &quot;w&quot;) as f:</div><div class="line">    print(text)</div><div class="line">    f.write(str(text))</div></pre></td></tr></table></figure>
<p>提取结果</p>
<p><img src="http://106.13.2.39:8888/blog_pic/new_9.png" alt="image"></p>
<p>其中需要说明：</p>
<ul>
<li>import pytesseract报错如下</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&gt;&gt;&gt; import pytesseract</div><div class="line">Traceback (most recent call last):</div><div class="line">  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;</div><div class="line">  File &quot;pytesseract.py&quot;, line 9, in &lt;module&gt;</div><div class="line">    import Image</div><div class="line">  File &quot;/Library/Python/2.7/site-packages/PIL/Image.py&quot;, line 27, in &lt;module&gt;</div><div class="line">    from . import VERSION, PILLOW_VERSION, _plugins</div><div class="line">ValueError: Attempted relative import in non-package</div></pre></td></tr></table></figure>
<p>说pytesseract.py无法引用Image这个包，在确认了PIL是正常安装且能够正常使用，于是找到了pytesseract.py报错的地方，源码是：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">try:</div><div class="line">    import Image</div><div class="line">except ImportError:</div><div class="line">    from PIL import Image</div></pre></td></tr></table></figure>
<p>我的PIL的Image文件和pytesseract.py不在同一目录下，因此没法直接引用。并且没有走 <em>from PIL import Image</em> 这条语句，说明是异常没有捕获到，改一个范围更大的异常进行捕获就可以了：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">try:</div><div class="line">    import Image</div><div class="line">except Exception:</div><div class="line">    from PIL import Image</div></pre></td></tr></table></figure>
<ul>
<li>python不能够读取中午，加了 <em>#coding:utf-8</em> 也不行；查阅了网上的资料，加上如下三行，搞定！</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">import sys </div><div class="line">reload(sys)</div><div class="line">sys.setdefaultencoding(&apos;utf-8&apos;)</div></pre></td></tr></table></figure>
<p>具体添加的位置见完整代码。</p>
<p>另外，尝试了有背景花纹，颜色和背景接近和非正常字体的图片的文字提取信息。发现正常字体在颜色和背景接近的情况下，能够大致识别出文字信息，而在非正常字体的情况下几乎不能正确提前信息。见下图：</p>
<p><img src="http://106.13.2.39:8888/blog_pic/new_10.png" alt="image"></p>
<p>另外，对图片的处理和对Tesseract-OCR语言包的训练，可以提高识别率，以后可能会继续研究。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;图片中存在大段的文字，用手敲实在是费劲。很多在线转换的工具，速度慢，并且达到一定次数后会收费。贫穷让我自力更生，查到python可以通过安装pytesseract库来进行文字识别。而pytesser调用了tesseract，因此还需要安装Tesseract-OCR软件（OC
    
    </summary>
    
    
      <category term="python" scheme="http://yoursite.com/tags/python/"/>
    
      <category term="pytesseract" scheme="http://yoursite.com/tags/pytesseract/"/>
    
      <category term="Tesseract-OCR" scheme="http://yoursite.com/tags/Tesseract-OCR/"/>
    
  </entry>
  
  <entry>
    <title>docker里用tomcat8下载文件</title>
    <link href="http://yoursite.com/2017/11/30/docker%E9%87%8C%E7%94%A8tomcat8%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6/"/>
    <id>http://yoursite.com/2017/11/30/docker里用tomcat8下载文件/</id>
    <published>2017-11-30T12:30:04.000Z</published>
    <updated>2020-03-03T13:30:16.884Z</updated>
    
    <content type="html"><![CDATA[<p>在docker中启动Tomcat作为下载文件服务器，如果存在中文名的文件，则需要对docker的容器编码格式进行修改，使其能够支持中文，否则将无法在下载中文名文件。</p>
<h2 id="1-修改docker容器编码格式"><a href="#1-修改docker容器编码格式" class="headerlink" title="1. 修改docker容器编码格式"></a>1. 修改docker容器编码格式</h2><p>直接启动docker容器，发现中文文件在里面显示为乱码</p>
<p><img src="http://106.13.2.39:8888/blog_pic/1.png" alt="image"></p>
<p>查看docker容器编码格式，可以看到当前编码格式为POSIX，这种编码格式不支持中文.</p>
<p><img src="http://106.13.2.39:8888/blog_pic/2.png" alt="image"></p>
<p>我们需要自己修改容器的编码格式。</p>
<h3 id="1-1-安装中文语言包"><a href="#1-1-安装中文语言包" class="headerlink" title="1.1 安装中文语言包"></a>1.1 安装中文语言包</h3><p>查看容器所有的语言环境，如果没有中文语言包，则需要自己安装。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/3.png" alt="image"></p>
<p>安装中文语言包：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@e0f34910ee74 /]# yum install glibc-common</div></pre></td></tr></table></figure>
<p>安装好后再次查看语言包信息，出现中文语言包。</p>
<p><img src="http://106.13.2.39:8888/blog_pic/4.png" alt="image"></p>
<h3 id="1-2-docker-build生成新的镜像"><a href="#1-2-docker-build生成新的镜像" class="headerlink" title="1.2 docker build生成新的镜像"></a>1.2 docker build生成新的镜像</h3><p>将下好语言包的容器commit成一个新的镜像</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@host-10-0-251-159 dockerbuild]# docker commit c2eb75865693 webcrawler-service:test</div></pre></td></tr></table></figure>
<p>基于这个新镜像写一个Dockerfile，然后基于Dockerfile生成我们需要的镜像。<br>Dockerfile为</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">[root@host-10-0-251-159 dockerbuild]# cat Dockerfile </div><div class="line">FROM webcrawler-service:test</div><div class="line">ENV LANG zh_CN.UTF-8</div></pre></td></tr></table></figure>
<p>docker build成新的镜像</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@host-10-0-251-159 dockerbuild]# docker build -t webcrawler-service-new .</div></pre></td></tr></table></figure>
<p>基于这个新镜像webcrawler-service-new启动一个容器，进入这个容器，我们可以看到容器编码为我们设置的zh_CN.UTF-8，生成一个新的中文名文件，不再出现乱码。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">[root@host-10-0-251-159 /]# docker run -tid eec4bea /run.sh</div><div class="line">[root@host-10-0-251-159 /]# docker exec -ti eec4bea /bin/bash</div></pre></td></tr></table></figure>
<p><img src="http://106.13.2.39:8888/blog_pic/5.png" alt="image"></p>
<p><img src="http://106.13.2.39:8888/blog_pic/6.png" alt="image"></p>
<h2 id="2-Tomcat8-HTTP文件下载文件"><a href="#2-Tomcat8-HTTP文件下载文件" class="headerlink" title="2. Tomcat8 HTTP文件下载文件"></a>2. Tomcat8 HTTP文件下载文件</h2><h3 id="2-1-修改Tomcat配置文件"><a href="#2-1-修改Tomcat配置文件" class="headerlink" title="2.1 修改Tomcat配置文件"></a>2.1 修改Tomcat配置文件</h3><p>在Tomcat安装目录下，修改web.xml文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">[root@eec4bea40bab conf]# vi /usr/local/tomcat/conf/web.xml</div><div class="line">……</div><div class="line">    &lt;servlet&gt;</div><div class="line">        &lt;servlet-name&gt;default&lt;/servlet-name&gt;</div><div class="line">        &lt;servlet-class&gt;org.apache.catalina.servlets.DefaultServlet&lt;/servlet-class&gt;</div><div class="line">        &lt;init-param&gt;</div><div class="line">            &lt;param-name&gt;debug&lt;/param-name&gt;</div><div class="line">            &lt;param-value&gt;0&lt;/param-value&gt;</div><div class="line">        &lt;/init-param&gt;</div><div class="line">        &lt;init-param&gt;</div><div class="line">            &lt;param-name&gt;listings&lt;/param-name&gt;</div><div class="line">            &lt;param-value&gt;true&lt;/param-value&gt; （把原来的false改为true）</div><div class="line">        &lt;/init-param&gt;</div><div class="line">        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;</div><div class="line">    &lt;/servlet&gt;</div><div class="line">……</div></pre></td></tr></table></figure>
<h3 id="2-2-创建用于下载的xml文件"><a href="#2-2-创建用于下载的xml文件" class="headerlink" title="2.2 创建用于下载的xml文件"></a>2.2 创建用于下载的xml文件</h3><p>在安装目录下的conf/Catalina/localhost/下新建一个xml文件，用于访问下载列表。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">[root@eec4bea40bab conf]# vi /usr/local/tomcat/conf/web.xml </div><div class="line">[root@eec4bea40bab conf]# cat /usr/local/tomcat/conf/Catalina/localhost/download.xml </div><div class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</div><div class="line">&lt;Context  reloadable=&quot;true&quot; docBase=&quot;/usr/download&quot; crossContext=&quot;true&quot;&gt;</div><div class="line">&lt;/Context&gt;</div></pre></td></tr></table></figure>
<p>其中docBase的路径为对外暴露的下载路径，需要存在该路径。<br>docker容器，对外暴露需要访问的端口，浏览器访问，出现下载列表：</p>
<p><img src="http://106.13.2.39:8888/blog_pic/7.png" alt="image"></p>
<p>因为有需求是文件名需要有中文。一开始中文出现乱码，导致无法下载文件。查阅了网上很多资料，都说是因为Tomcat默认的编码方式是iso8859-1，而iso8859-1不属于中文码表，故而出现乱码。建议修改server.xml文件，将编码方式修改为utf-8： <em>URIEncoding=”utf-8”</em>。我进行尝试后发现依然中文乱码。后来发现Tomcat8默认的编码已经是utf-8了，所以不需要修改server.xml文件 <strong>（如果从Tomcat早期版本迁移到Tomcat8需要注意这个问题）</strong>。后来多次测试，发现是系统环境的问题，如果系统本身不支持中文，则修改Tomcat后也不支持中文文件的下载，需要先修改系统环境语言。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在docker中启动Tomcat作为下载文件服务器，如果存在中文名的文件，则需要对docker的容器编码格式进行修改，使其能够支持中文，否则将无法在下载中文名文件。&lt;/p&gt;
&lt;h2 id=&quot;1-修改docker容器编码格式&quot;&gt;&lt;a href=&quot;#1-修改docker容器编码
    
    </summary>
    
    
      <category term="docker" scheme="http://yoursite.com/tags/docker/"/>
    
      <category term="Tomcat8" scheme="http://yoursite.com/tags/Tomcat8/"/>
    
  </entry>
  
  <entry>
    <title>java基本数据类型及其封装类</title>
    <link href="http://yoursite.com/2017/11/30/java%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E5%8F%8A%E5%85%B6%E5%B0%81%E8%A3%85%E7%B1%BB/"/>
    <id>http://yoursite.com/2017/11/30/java基本数据类型及其封装类/</id>
    <published>2017-11-30T12:28:58.000Z</published>
    <updated>2017-11-30T12:33:55.000Z</updated>
    
    <content type="html"><![CDATA[<p>之前刚从python写java，碰到过两个String比较大小，习惯性用了“==”，发现不管我的String赋什么样的值，最后都是false。后来网上搜了一下，发现是封装类的原因。</p>
<h2 id="1-相关说明"><a href="#1-相关说明" class="headerlink" title="1.相关说明"></a>1.相关说明</h2><p>说到封装类，不得不提到栈内存和堆内存。<br>堆内存在Java运行时被使用来为对象和JRE类分配内存。不论什么时候我们创建了对象，它将一直会在堆内存上创建。垃圾回收运行在堆内存上来释放没有任何引用的对象所占的内存，任何在堆上被创建的对象都有一个全局的访问，并且可以在应用的任何位置被引用。</p>
<p>Java的栈内存被用来线程的执行，他们包含生命周期很短的具体值的方法和在堆中使用这个方法对象的引用。栈内存是LIFO（后进先出）序列。当方法被调用的时候，堆内存中一个新的块被创建，保存了本地原始值和在方法中对其他对象的引用。这个方法结束之后，这个块对其他方法就变成可用的了。和堆内存比，栈内存要小的多，因为明确使用了内存分配规则（LIFO），和堆内存相比栈内存非常快。</p>
<h2 id="2-封装类型和基本类型比较"><a href="#2-封装类型和基本类型比较" class="headerlink" title="2. 封装类型和基本类型比较"></a>2. 封装类型和基本类型比较</h2><h3 id="2-1-封装类型和基本类型说明"><a href="#2-1-封装类型和基本类型说明" class="headerlink" title="2.1 封装类型和基本类型说明"></a>2.1 封装类型和基本类型说明</h3><p>Java的基本数据类型：int，double，float，long，byte，char，boolean。<br>对应的封装类：Integer，Double，Float，Long，Byte，Char，Boolean。<br>封装类型和基本类型的内存模型本质上是不一样的。基本数据类型直接存储在内存中的内存栈上的，数据本身的值就是存储在栈空间里面，不存在“引用”的概念。封装类型是按照Java里面存储对象的内存模型来进行数据存储的，使用Java内存堆和内存栈来进行这种类型的数据存储，对象本身的值存储在内存堆上，栈内存只包含原始值变量和堆中对象变量的引用。封装类型和基本类型有不同的默认值：封装类型默认值为null，基本类型的默认值于本身的类型有关。基本数据类型本身就是一个值。</p>
<h3 id="2-2基本数据类型及其封装类的转换"><a href="#2-2基本数据类型及其封装类的转换" class="headerlink" title="2.2基本数据类型及其封装类的转换"></a>2.2基本数据类型及其封装类的转换</h3><p>定义基本数据类型转封装类：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">intnum=3;</div><div class="line">Integerinteger=newInteger(num);</div></pre></td></tr></table></figure>
<p>在添加了自动装拆箱的功能之后:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Integer integer=3;</div></pre></td></tr></table></figure>
<p>封装类转基本数据类型：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">integer integer=newInteger(3);</div><div class="line">int num=integer.intValue();</div></pre></td></tr></table></figure>
<p>当然，也可以直接int num=integer，这里的自动拆箱，其实也是调用了封装类的intValue()方法来实现的。<br>将String类型字符串与基本数据类型进行转换。<br>字符串转基本数据类型：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">String ageString=&quot;23&quot;;</div><div class="line">int age=Integer.parseInt(ageString);</div></pre></td></tr></table></figure>
<p>基本数据类型转字符串：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">String age=23+&quot;&quot;;</div><div class="line">或者Stringage = String.valueOf(23);</div></pre></td></tr></table></figure>
<p>封装类转字符串,直接调用封装类对象的toString()方法即可。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Integer age = 23;</div><div class="line">String ageString=age.toString();</div></pre></td></tr></table></figure>
<h3 id="2-3-封装类型和基本类型数值直接的比较方法（equal和-）"><a href="#2-3-封装类型和基本类型数值直接的比较方法（equal和-）" class="headerlink" title="2.3 封装类型和基本类型数值直接的比较方法（equal和==）"></a>2.3 封装类型和基本类型数值直接的比较方法（equal和==）</h3><p>例如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">int n=3;</div><div class="line">int m=3;</div></pre></td></tr></table></figure>
<p>变量n和变量m都是直接存储的“3”这个数值，所以用==比较的时候结果是true。<br>而对于非基本数据类型的变量，是引用类型的变量，引用类型的变量存储的并不是“值”本身，而是于其关联的对象在内存中的地址。例如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">String str1;</div></pre></td></tr></table></figure>
<p>这句话声明了一个引用类型的变量，此时它并没有和任何对象关联。<br>而通过newString（”hello”）来产生一个对象（也称作为类String的一个实例），并将这个对象和str1、str2进行绑定：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">str1=newString(&quot;hello&quot;);（或者：String str1=&quot;hello&quot;;）</div><div class="line">str2=newString(&quot;hello&quot;);（或者：String str2=&quot;hello&quot;;）</div></pre></td></tr></table></figure>
<p>那么str1指向了一个对象，此时变量str1中存储的是它指向的对象在内存中的存储地址，并不是“值”本身，也就是说并不是直接存储的字符串”hello”。这里面的引用和C/C++中的指针很类似。<br>因此在用==对str1和str2进行第一次比较时，得到的结果是false。因此它们分别指向的是不同的对象，也就是说它们实际存储的内存地址不同。<br>而在第二次比较时，都让str1和str2指向了str指向的对象，那么得到的结果毫无疑问是true。<br>equals方法是基类Object中的方法，因此对于所有的继承于Object的类都会有该方法。<br>在Object类中，equals方法是用来比较两个对象的引用是否相等，即是否指向同一个对象。String类对equals方法进行了重写，用来比较指向的字符串对象所存储的字符串是否相等。</p>
<p>对于封装过之后，每个类型都有对应的缓存：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>缓存情况</th>
</tr>
</thead>
<tbody>
<tr>
<td>Byte</td>
<td>全部缓存</td>
</tr>
<tr>
<td>Boolean</td>
<td>全部缓存</td>
</tr>
<tr>
<td>Integer</td>
<td>-128~127缓存</td>
</tr>
<tr>
<td>Character</td>
<td>&lt;=127缓存</td>
</tr>
<tr>
<td>Short</td>
<td>-128~127缓存</td>
</tr>
<tr>
<td>Long</td>
<td>-128~127缓存</td>
</tr>
<tr>
<td>Float</td>
<td>没有缓存</td>
</tr>
<tr>
<td>Double</td>
<td>没有缓存</td>
</tr>
</tbody>
</table>
<p>例如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">Integer i1=127;// java在编译的时候，被翻译成-&gt;Integer i1=Integer.valueOf(127);</div><div class="line">Integer i2=127;</div><div class="line">System.out.println(i1==i2);// true</div><div class="line">Integer _i1=128;</div><div class="line">Integer _i2=128;</div><div class="line">System.out.println(_i1==_i2);//false</div></pre></td></tr></table></figure>
<p>对于-128到127之间的数，会进行缓存，Integer i1=127时，会将127进行缓存，下次再写Integer i2=127时，就会直接从缓存中取，就不会new了。所以i1 == i2 是true；而_i1 == _i2是false。</p>
<h2 id="3-总结"><a href="#3-总结" class="headerlink" title="3.总结"></a>3.总结</h2><h3 id="3-1基本类型："><a href="#3-1基本类型：" class="headerlink" title="3.1基本类型："></a>3.1基本类型：</h3><p>优点：</p>
<ul>
<li>用于计算效率高</li>
<li>不会由于常量池引起比较大小错误</li>
</ul>
<p>缺点：</p>
<ul>
<li>当数据库中查询出结果之后封装结果集时如果返回的值时null时，如果直接赋值给这个基本类型的字段时，会在运行时报出异常，不能将null赋给一个基本类型，而用Integer就不存在这样都的情况</li>
<li>当用spring的表单对象时如果页面传来的值是只有字段没有值是如果表单对象中有基本类型的值时会抛出异常。需要设置在数据库中为该字段设置默认值，但是这经常会被忽略。</li>
</ul>
<h3 id="3-2封装类型"><a href="#3-2封装类型" class="headerlink" title="3.2封装类型"></a>3.2封装类型</h3><p>优点：</p>
<ul>
<li>可以存放null，从数据库中查出值时可能会有null</li>
<li>表示一个值（不用于计算，只用于保存值时和int类型一样）;</li>
</ul>
<p>缺点</p>
<ul>
<li>不能用于两个Integer对象双等判断两个对象的值相等，会出现错误的。</li>
</ul>
<p>java中Integer，String判断相等与integer的比较大小不适合频繁的用于计算，要用双等判断两个对象的值是否相等时，要调用intValue方法<br>综上所述，在建立实体类时，用封装类型比较好；如果需要频发的计算，则用基本类型效率更高。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;之前刚从python写java，碰到过两个String比较大小，习惯性用了“==”，发现不管我的String赋什么样的值，最后都是false。后来网上搜了一下，发现是封装类的原因。&lt;/p&gt;
&lt;h2 id=&quot;1-相关说明&quot;&gt;&lt;a href=&quot;#1-相关说明&quot; class=&quot;h
    
    </summary>
    
    
      <category term="java" scheme="http://yoursite.com/tags/java/"/>
    
      <category term="基本类" scheme="http://yoursite.com/tags/%E5%9F%BA%E6%9C%AC%E7%B1%BB/"/>
    
      <category term="封装类" scheme="http://yoursite.com/tags/%E5%B0%81%E8%A3%85%E7%B1%BB/"/>
    
  </entry>
  
  <entry>
    <title>JAVA写入word文档（用POI包）</title>
    <link href="http://yoursite.com/2017/11/07/JAVA%E5%86%99%E5%85%A5word%E6%96%87%E6%A1%A3%EF%BC%88%E7%94%A8POI%E5%8C%85%EF%BC%89/"/>
    <id>http://yoursite.com/2017/11/07/JAVA写入word文档（用POI包）/</id>
    <published>2017-11-07T08:14:06.000Z</published>
    <updated>2017-11-07T08:35:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>Apache的POI是一种流行的API，允许程序员创建，修改和使用显示Java程序的MS-Office文件。它是开发和Apache软件基金会的分布式设计或修改使用Java程序MS-Office文件的开源库。它包含的类和方法来将用户输入的数据或文件到MS-Office文档解码。POI对<strong>Excel</strong>、<strong>Word</strong>、<strong>PowerPoint</strong>甚至是<strong>Visio</strong>都有组件。近期工作中有项目需要生成Word，并且有指定样式（字体、字号页眉页脚、水印等），特记录一下POI在Word上的用法。</p>
<p>官网地址：<a href="https://poi.apache.org/" target="_blank" rel="external">https://poi.apache.org/</a>。</p>
<h2 id="1-创建docx文档（写入段落和表格）"><a href="#1-创建docx文档（写入段落和表格）" class="headerlink" title="1. 创建docx文档（写入段落和表格）"></a>1. 创建docx文档（写入段落和表格）</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line">public static void main(String[] args) throws XmlException, IOException</div><div class="line">&#123;</div><div class="line">    //创建一个空白文档</div><div class="line">    XWPFDocument document = new XWPFDocument();</div><div class="line">    </div><div class="line">    //给段落加下边框</div><div class="line">    paragraph.setBorderBottom(Borders.BASIC_BLACK_DASHES);</div><div class="line">    </div><div class="line">    //给段落加左边框</div><div class="line">    paragraph.setBorderLeft(Borders.BASIC_BLACK_DASHES);</div><div class="line">    </div><div class="line">    //给段落加右边框</div><div class="line">    paragraph.setBorderRight(Borders.BASIC_BLACK_DASHES);</div><div class="line">    </div><div class="line">    //给段落加上边框</div><div class="line">    paragraph.setBorderTop(Borders.BASIC_BLACK_DASHES);</div><div class="line">    </div><div class="line">    //创建一个段落</div><div class="line">    XWPFParagraph paragraph = document.createParagraph();</div><div class="line">    XWPFRun run = paragraph.createRun();</div><div class="line">    run.setText(&quot;这是一个word文档，用于测试，用poi写入。  可以有空格，可以改写&quot;);</div><div class="line">    </div><div class="line">    //创建表格</div><div class="line">    XWPFTable table = document.createTable();</div><div class="line">    </div><div class="line">    //创建第一行</div><div class="line">    XWPFTableRow tableRowOne = table.getRow(0);</div><div class="line">    tableRowOne.getCell(0).setText(&quot;col one, row one&quot;);</div><div class="line">    tableRowOne.addNewTableCell().setText(&quot;col two, row one&quot;);</div><div class="line">    tableRowOne.addNewTableCell().setText(&quot;col three, row one&quot;);</div><div class="line">    </div><div class="line">    //创建第二行</div><div class="line">    XWPFTableRow tableRowTwo = table.createRow();</div><div class="line">    tableRowTwo.getCell(0).setText(&quot;col one, row two&quot;);</div><div class="line">    tableRowTwo.getCell(1).setText(&quot;col two, row two&quot;);</div><div class="line">    tableRowTwo.getCell(2).setText(&quot;col three, row two&quot;);</div><div class="line">    </div><div class="line">    //Write the Document in file system</div><div class="line">    FileOutputStream out = new FileOutputStream(new File(&quot;C:\\Users\\dell\\Desktop\\createparagraph.docx&quot;));</div><div class="line">    document.write(out);</div><div class="line">    out.close();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="2-段落对齐和添加样式"><a href="#2-段落对齐和添加样式" class="headerlink" title="2. 段落对齐和添加样式"></a>2. 段落对齐和添加样式</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div></pre></td><td class="code"><pre><div class="line">public static void main(String[] args) throws XmlException, IOException</div><div class="line">&#123;</div><div class="line">    XWPFDocument document = new XWPFDocument();</div><div class="line">    //创建段落</div><div class="line">    XWPFParagraph paragraphone = document.createParagraph();</div><div class="line"></div><div class="line">    //设置段落右对齐样式</div><div class="line">    paragraphone.setAlignment(ParagraphAlignment.RIGHT);</div><div class="line">    XWPFRun runone=paragraphone.createRun();</div><div class="line">    runone.setText(&quot;右对齐右对齐右对齐右对齐右对齐右对齐右对齐右对齐右对齐右对齐.&quot;);</div><div class="line"></div><div class="line">    //创建一个新段落</div><div class="line">    XWPFParagraph paragraphtwo=document.createParagraph();</div><div class="line"></div><div class="line">    //设置段落居中对齐样式</div><div class="line">    paragraphtwo.setAlignment(ParagraphAlignment.CENTER);</div><div class="line">    XWPFRun runtwo=paragraphtwo.createRun();</div><div class="line">    runtwo.setText(&quot;居中对齐居中对齐居中对齐居中对齐居中对齐居中对齐居中对齐居中对齐.&quot;);</div><div class="line"></div><div class="line">    //创建一个新段落</div><div class="line">    XWPFParagraph paragraphthree = document.createParagraph();</div><div class="line">    </div><div class="line">    //设置加粗和斜体</div><div class="line">    XWPFRun paragraphOneRunOne = paragraphthree.createRun();</div><div class="line">    paragraphOneRunOne.setBold(true);</div><div class="line">    paragraphOneRunOne.setItalic(true);</div><div class="line">    paragraphOneRunOne.setText(&quot;Font Style&quot;);</div><div class="line">    paragraphOneRunOne.addBreak();</div><div class="line">    </div><div class="line">    //设置文本位置</div><div class="line">    XWPFRun paragraphOneRunTwo = paragraphthree.createRun();</div><div class="line">    paragraphOneRunTwo.setText(&quot;Font Style two&quot;);</div><div class="line">    paragraphOneRunTwo.setTextPosition(100);</div><div class="line">    </div><div class="line">    //创建一个新段落</div><div class="line">    XWPFRun paragraphOneRunThree = paragraphthree.createRun();</div><div class="line">    </div><div class="line">    //设置文字中划线样式（可能弃用）</div><div class="line">    paragraphOneRunThree.setStrike(true);</div><div class="line">    </div><div class="line">    //设置文字大小</div><div class="line">    paragraphOneRunThree.setFontSize(20);</div><div class="line">    </div><div class="line">    //设置文字段落内对齐，垂直对齐（垂直靠下对齐，垂直居中对齐，垂直向上对齐）</div><div class="line">    paragraphOneRunThree.setSubscript(VerticalAlign.SUBSCRIPT);</div><div class="line">    paragraphOneRunThree.setText(&quot; Different Font Styles&quot;);</div><div class="line">    </div><div class="line">    //Write the Document in file system</div><div class="line">    FileOutputStream out = new FileOutputStream(newFile(&quot;C:\\Users\\dell\\Desktop\\createparagraph.docx&quot;));</div><div class="line">    document.write(out);</div><div class="line">    out.close();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="3-从已有的docx文件中获取信息"><a href="#3-从已有的docx文件中获取信息" class="headerlink" title="3. 从已有的docx文件中获取信息"></a>3. 从已有的docx文件中获取信息</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">public static void main(String[] args) throws XmlException, IOException</div><div class="line">&#123;</div><div class="line">    //传流构建XWPFDocument</div><div class="line">    XWPFDocument docx = new XWPFDocument(new FileInputStream(&quot;C:\\Users\\dell\\Desktop\\createparagraph.docx&quot;));</div><div class="line">    </div><div class="line">    //传路径构建XWPFDocument</div><div class="line">    XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(&quot;C:\\Users\\dell\\Desktop\\createparagraph.docx&quot;));</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="4-处理页眉页脚（由模板文件进行文字替换）"><a href="#4-处理页眉页脚（由模板文件进行文字替换）" class="headerlink" title="4. 处理页眉页脚（由模板文件进行文字替换）"></a>4. 处理页眉页脚（由模板文件进行文字替换）</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div></pre></td><td class="code"><pre><div class="line">public static void replaceFooterAndHeader(XWPFDocument doc)&#123;</div><div class="line">        List&lt;XWPFParagraph&gt; footers = doc.getHeaderFooterPolicy().getDefaultFooter().getParagraphs();</div><div class="line">        List&lt;XWPFParagraph&gt; headers = doc.getHeaderFooterPolicy().getDefaultHeader().getParagraphs();</div><div class="line">       </div><div class="line">        //处理页脚</div><div class="line">        for (XWPFParagraph paragraph : footers) &#123;</div><div class="line">            List&lt;XWPFRun&gt; runs = paragraph.getRuns();</div><div class="line">            for (XWPFRun run : runs) &#123;</div><div class="line">                String text = run.getText(0);</div><div class="line">                if(StringUtils.isNotEmpty(text))&#123;</div><div class="line">                    for(Entry&lt;String, String&gt; entry : params.entrySet())&#123;</div><div class="line">                        String key = entry.getKey();</div><div class="line">                        if(text.indexOf(key) != -1)&#123;</div><div class="line">                            Object value = entry.getValue();</div><div class="line">                            if(value instanceof String)&#123;</div><div class="line">                                text = text.replace(key, value.toString());</div><div class="line">                                run.setText(text,0);</div><div class="line">                            &#125;</div><div class="line">                        &#125;</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">       </div><div class="line">        //处理页眉</div><div class="line">        for (XWPFParagraph paragraph : headers) &#123;</div><div class="line">            List&lt;XWPFRun&gt; runs = paragraph.getRuns();</div><div class="line">            for (XWPFRun run : runs) &#123;</div><div class="line">                String text = run.getText(0);</div><div class="line">                if(StringUtils.isNotEmpty(text))&#123;</div><div class="line">                    for(Entry&lt;String, String&gt; entry : params.entrySet())&#123;</div><div class="line">                        String key = entry.getKey();</div><div class="line">                        if(text.indexOf(key) != -1)&#123;</div><div class="line">                            Object value = entry.getValue();</div><div class="line">                            if(value instanceof String)&#123;</div><div class="line">                                text = text.replace(key, value.toString());</div><div class="line">                                run.setText(text,0);</div><div class="line">                            &#125;</div><div class="line">                        &#125;</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<h2 id="5-获取docx文件中的图片"><a href="#5-获取docx文件中的图片" class="headerlink" title="5.获取docx文件中的图片"></a>5.获取docx文件中的图片</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">public static void main(String[] args) throws XmlException, InvalidFormatException, FileNotFoundException, IOException&#123;</div><div class="line">    String path =&quot;C:\\Users\\dell\\Desktop\\picture.docx&quot;;</div><div class="line">    File file = new File(path);</div><div class="line">    FileInputStream fis = new FileInputStream(file);</div><div class="line">    XWPFDocument document = new XWPFDocument(fis);</div><div class="line">    XWPFWordExtractor xwpfWordExtractor = new XWPFWordExtractor(document);</div><div class="line">    String text = xwpfWordExtractor.getText();</div><div class="line">    System.out.println(text);</div><div class="line">    List&lt;XWPFPictureData&gt; picList = document.getAllPictures();</div><div class="line">    for (XWPFPictureData pic : picList) &#123;</div><div class="line">        System.out.println(pic.getPictureType() + file.separator + pic.suggestFileExtension()+file.separator+pic.getFileName());</div><div class="line">        byte[] bytev = pic.getData();</div><div class="line">        FileOutputStream fos = new FileOutputStream(&quot;d:\\&quot;+pic.getFileName());</div><div class="line">        fos.write(bytev);</div><div class="line">    &#125;</div><div class="line">    fis.close();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="6-POI读取-doc-和-docx"><a href="#6-POI读取-doc-和-docx" class="headerlink" title="6. POI读取.doc 和.docx"></a>6. POI读取.doc 和.docx</h2><h3 id="6-1-HWPF和XWPF"><a href="#6-1-HWPF和XWPF" class="headerlink" title="6.1 HWPF和XWPF"></a>6.1 HWPF和XWPF</h3><p>HWPF：<strong>MS－Word 97-2003(.doc)</strong>，基于BIFF8格式的JAVA接口。只支持.doc文件简单的操作，读写能力有限。本API为POI项目早期开发，很不幸的 是主要负责HWPF模块开发的工程师已经离开Apache组织，现在该模块没有人维护、更新、完善。 </p>
<p>XWPF：<strong>MS－Word 2007+(.docx)</strong>，基于OOXML格式的JAVA接口。较HWPF功能完善。 </p>
<h3 id="6-2-HSSF和XSSF"><a href="#6-2-HSSF和XSSF" class="headerlink" title="6.2 HSSF和XSSF"></a>6.2 HSSF和XSSF</h3><p>HSSF：<strong>MS－Excel 97-2003（.xls）</strong>，基于BIFF8格式的JAVA接口。 </p>
<p>XSSF：<strong>MS－Excel 2007+(.xlsx)</strong>，基于OOXML格式的JAVA接口。 </p>
<p>具体见： <a href="http://blog.csdn.net/qq_20389175/article/details/44058209" target="_blank" rel="external">POI读取.doc 和.docx的区别</a></p>
<p>另外很好的例子地址：<a href="http://www.cnblogs.com/unruly/p/7552998.html" target="_blank" rel="external">http://www.cnblogs.com/unruly/p/7552998.html</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Apache的POI是一种流行的API，允许程序员创建，修改和使用显示Java程序的MS-Office文件。它是开发和Apache软件基金会的分布式设计或修改使用Java程序MS-Office文件的开源库。它包含的类和方法来将用户输入的数据或文件到MS-Office文档解码
    
    </summary>
    
    
      <category term="java" scheme="http://yoursite.com/tags/java/"/>
    
      <category term="poi" scheme="http://yoursite.com/tags/poi/"/>
    
  </entry>
  
  <entry>
    <title>程序中的返回码</title>
    <link href="http://yoursite.com/2017/11/07/%E7%A8%8B%E5%BA%8F%E4%B8%AD%E7%9A%84%E8%BF%94%E5%9B%9E%E7%A0%81/"/>
    <id>http://yoursite.com/2017/11/07/程序中的返回码/</id>
    <published>2017-11-07T08:09:04.000Z</published>
    <updated>2017-11-07T08:34:08.000Z</updated>
    
    <content type="html"><![CDATA[<p>HTTP状态码可以指明请求是否成功，还可以揭示请求失败的确切原因，是前后端进行“交流”的重要语言。在实际开发过程中，存在着对不同状态码应用场景不清楚，如何正确抛出合适的状态码的问题，使得前端开发人员不能很好的获取发起请求后后台的反应。本文旨在通过实际应用对现业务涉及的状态码进行探究记录，为以后开发中遇到类似问题作个参考。</p>
<h2 id="1-查询一个资源"><a href="#1-查询一个资源" class="headerlink" title="1. 查询一个资源"></a>1. 查询一个资源</h2><p>例如：GET：/v1/characters/{id}</p>
<h3 id="1-1-接口定义"><a href="#1-1-接口定义" class="headerlink" title="1.1 接口定义"></a>1.1 接口定义</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">public interface CharacterService &#123;</div><div class="line">    @GET</div><div class="line">    @Path(&quot;&#123;id&#125;&quot;)</div><div class="line">    @ApiOperation(value = &quot;根据人物id查询人物详情&quot;, notes = &quot;根据人物id查询人物详情&quot;, response = CharacterDTO.class, tags = &#123;&quot;CharacterService&quot;,&#125;)</div><div class="line">    @ApiResponses(value = &#123;</div><div class="line">            @ApiResponse(code = 204, message = &quot;没有数据&quot;),</div><div class="line">            @ApiResponse(code = 404, message = &quot;@06404:请求参数错误&quot;),</div><div class="line">            @ApiResponse(code = 500, message = &quot;@06500:服务器异常&quot;)&#125;)</div><div class="line">    CharacterDTO getCharacterById(@ApiParam(value = &quot;人物ID&quot;, required = true) @NotNull(message = &quot;CharacterID must be not null&quot;) @PathParam(&quot;id&quot;) Long id);</div></pre></td></tr></table></figure>
<h3 id="1-2-接口实现"><a href="#1-2-接口实现" class="headerlink" title="1.2 接口实现"></a>1.2 接口实现</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">public CharacterDTO getCharacterById(Long characterId) &#123;</div><div class="line">        CharacterDTO characterDTO = null;</div><div class="line">        try&#123;</div><div class="line">            Character character = characterRepository.findByIdAndStatus(characterId, Status.VALID);</div><div class="line">            if (null == character)&#123;</div><div class="line">                return null;</div><div class="line">               //此处return null表示查询请求正确，但是没有资源，不抛异常，返回码204，表示没有数据。</div><div class="line">               //LOGGER.error(&quot;can not query character from db and character is: &quot; + characterId);</div><div class="line">               // throw new WebApplicationException(ScriptsTranslationReturnCode.NOT_FOUND_MODEL, &quot;can not find character&quot;);</div><div class="line">            &#125;</div><div class="line">            return beanMapper.map(character,CharacterDTO.class);</div><div class="line">        &#125; catch (WebApplicationException we)&#123;</div><div class="line">            LOGGER.error(we.getMessage(),we);</div><div class="line">            throw we;</div><div class="line">        &#125; catch (Exception e)&#123;</div><div class="line">            LOGGER.error(&quot;query character information by id error&quot;,e);</div><div class="line">            throw new WebApplicationException(ScriptsTranslationReturnCode.SERVICE_EXCEPTION, &quot;service exception&quot;);</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<ul>
<li>参数正确请求得到一个资源，返回200；</li>
<li>参数正确资源不存在，返回204，或者404；</li>
<li>参数类型错误（其他应该返回400），返回404，路径斜杠后的参数错误不会返回400.</li>
</ul>
<h2 id="2-查询一个资源，该资源可能关联其他子资源。"><a href="#2-查询一个资源，该资源可能关联其他子资源。" class="headerlink" title="2. 查询一个资源，该资源可能关联其他子资源。"></a>2. 查询一个资源，该资源可能关联其他子资源。</h2><p>例如：GET: {id}/lines-info</p>
<h3 id="2-1-接口定义"><a href="#2-1-接口定义" class="headerlink" title="2.1 接口定义"></a>2.1 接口定义</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">@GET</div><div class="line">    @Path(&quot;&#123;id : \\d+&#125;/device-models&quot;)</div><div class="line">    @ApiOperation(value = &quot;根据镜像id得到终端型号列表&quot;, notes = &quot;根据镜像id得到终端型号列表&quot;, response = SimpleImageContentDTO.class, responseContainer = &quot;List&quot;, tags=&#123; &quot;image content&quot;&#125;)</div><div class="line">    @ApiResponses(value = &#123;</div><div class="line">            @ApiResponse(code = 200, message = &quot;OK&quot;, response = DeviceModelDTO.class, responseContainer = &quot;List&quot;),</div><div class="line">            @ApiResponse(code = 204, message = &quot;没有数据&quot;),</div><div class="line">            @ApiResponse(code = 422, message = &quot;06450：无法处理的镜像资源&quot;, responseContainer = &quot;Object&quot;),</div><div class="line">            @ApiResponse(code = 500, message = &quot;06500：服务异常&quot;, responseContainer = &quot;Object&quot;) &#125;)</div><div class="line">    List&lt;DeviceModelDTO&gt; queryDeviceModelsById(@ApiParam(value = &quot;镜像id&quot;,required = true) @NotNull(message = &quot;The image content id must not be null&quot;) @PathParam(&quot;id&quot;) Long id);</div></pre></td></tr></table></figure>
<h3 id="2-2-接口实现"><a href="#2-2-接口实现" class="headerlink" title="2.2 接口实现"></a>2.2 接口实现</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">@Override</div><div class="line">    public List&lt;DeviceModelDTO&gt; queryDeviceModelsById(Long id) &#123;</div><div class="line">        try&#123;</div><div class="line">            ImageContent imageContent = imageContentRepository.findByIdAndStatus(id, Status.VALID);</div><div class="line">            if (imageContent==null)&#123;</div><div class="line">                throw new UnprocessableEntityException(TerminalDeviceReturnCode.UNPROCESSABLE_IMAGE_CONTENT,&quot;image content not found&quot;);</div><div class="line">            &#125;</div><div class="line">            List&lt;DeviceModel&gt; deviceModelList = deviceModelRepository.queryDeviceModelsByImageContentId(id);</div><div class="line">            if (CollectionUtils.isEmpty(deviceModelList))&#123;</div><div class="line">                return null;</div><div class="line">            &#125;</div><div class="line">            return beanMapper.mapAsList(deviceModelList,DeviceModelDTO.class);</div><div class="line">        &#125; catch (WebApplicationException we) &#123;</div><div class="line">            LOGGER.error(&quot;query device models by Id error!&quot; ,we);</div><div class="line">            throw we;</div><div class="line">        &#125; catch (Exception e) &#123;</div><div class="line">            LOGGER.error(&quot;query device models by Id error!&quot; ,e);</div><div class="line">            throw new WebApplicationException(TerminalDeviceReturnCode.SERVICE_EXCEPTION, &quot;service exception!&quot;);</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>根据A资源查询B资源</p>
<ul>
<li>正确查询，返回200；</li>
<li>根据查不到A资源信息，返回422，抛异常UnprocessableException。</li>
<li>可以查到A资源，但是根据A资源查不到B资源，返回204或者404；</li>
</ul>
<h2 id="3-修改一个资源"><a href="#3-修改一个资源" class="headerlink" title="3. 修改一个资源"></a>3. 修改一个资源</h2><p>例如：PUT:/translation-lines-info/{id}</p>
<h3 id="3-1接口定义"><a href="#3-1接口定义" class="headerlink" title="3.1接口定义"></a>3.1接口定义</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">@PUT</div><div class="line">    @Path(&quot;&#123;id&#125;&quot;)</div><div class="line">    @ApiOperation(value = &quot;修改人物信息&quot;, notes = &quot;修改人物信息&quot;, response = CharacterDTO.class, tags = &#123;&quot;CharacterService&quot;,&#125;)</div><div class="line">    @ApiResponses(value = &#123;</div><div class="line">            @ApiResponse(code = 201, message = &quot;修改成功&quot;),</div><div class="line">            @ApiResponse(code = 404, message = &quot;@06404：人物信息不存在&quot;),</div><div class="line">            @ApiResponse(code = 400, message = &quot;@06400: 请求参数错误&quot;),</div><div class="line">            @ApiResponse(code = 409, message = &quot;@06411:人物名称冲突&quot;),</div><div class="line">            @ApiResponse(code = 500, message = &quot;@06500：服务异常&quot;)&#125;)</div><div class="line">    CharacterDTO modifyCharacter(@ApiParam(value = &quot;人物id&quot;, required = true) @NotNull(message = &quot;parameter must not be null&quot;) @PathParam(&quot;id&quot;) Long id, @NotNull(message = &quot;parameter must be not null&quot;) @ApiParam(value = &quot;人物信息&quot;, required = true) CharacterDTO characterDTO);</div></pre></td></tr></table></figure>
<h3 id="3-2-接口实现"><a href="#3-2-接口实现" class="headerlink" title="3.2 接口实现"></a>3.2 接口实现</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line">public CharacterDTO modifyCharacter(Long id, CharacterDTO characterDTO) &#123;</div><div class="line">        try &#123;</div><div class="line">            Character character = characterRepository.findByIdAndStatus(id,Status.VALID);</div><div class="line">            if(character == null)&#123;</div><div class="line">                throw new WebApplicationException(ScriptsTranslationReturnCode.NOT_FOUND_MODEL, &quot;modify character error! can not found character info exception!&quot;);</div><div class="line">            &#125;</div><div class="line">            String chName = characterDTO.getName();</div><div class="line">            String enName = characterDTO.getEnglishName();</div><div class="line">            Long dramaId = characterDTO.getDramaId();</div><div class="line">            if(org.springframework.util.StringUtils.isEmpty(chName) || org.springframework.util.StringUtils.isEmpty(enName) || dramaId == null)&#123;</div><div class="line">                throw new ClientErrorException(HttpStatus.BAD_REQUEST, ScriptsTranslationReturnCode.PARAM_ERROR, &quot; chname or enname or dramaid is empty!&quot;);</div><div class="line">            &#125;</div><div class="line">            List&lt;Character&gt; chNamesOrEnNames = characterRepository.findOtherIdExistNames(chName,enName,id,dramaId);</div><div class="line">            if(chNamesOrEnNames.size() &gt; 0)&#123;</div><div class="line">                throw new ClientErrorException(HttpStatus.CONFLICT, ScriptsTranslationReturnCode.DRAMA_ADD_OR_UPDATE_CHARACTER_NAME_CONFICT, &quot; chname or enname conflict&quot;);</div><div class="line">            &#125;</div><div class="line">            Character modifyCharacher = fillingModifyParams(characterDTO, character);</div><div class="line">            Character modifyResult = characterRepository.saveAndFlush(modifyCharacher);</div><div class="line">            return beanMapper.map(modifyResult, CharacterDTO.class);</div><div class="line">        &#125;catch (ClientErrorException we)&#123;</div><div class="line">            LOGGER.error(we.getMessage(),we);</div><div class="line">            throw we;</div><div class="line">        &#125; catch (Exception e)&#123;</div><div class="line">            LOGGER.error(&quot;modify character error&quot;,e);</div><div class="line">            throw new WebApplicationException(ScriptsTranslationReturnCode.SERVICE_EXCEPTION, &quot;service exception&quot;);</div><div class="line">        &#125;</div><div class="line"></div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<ul>
<li>修改成功返回201；</li>
<li>根据路径上的id查询人物信息没查到，返回404；</li>
<li>如果路径上的id为非数字类型，返回404；</li>
<li>如果dto里的类型错误，返回400（body和？后的参数类型错误，返回400），抛出BadRequestException；</li>
<li>修改的数据与原数据产生了冲突（根据业务定义），返回409，抛出ClienErrorException。</li>
</ul>
<h2 id="4-总结"><a href="#4-总结" class="headerlink" title="4. 总结"></a>4. 总结</h2><p>现在涉及到的状态码有以下几个：</p>
<ul>
<li>200：用于成功查询到信息。</li>
<li>201：用于修改或添加成功。</li>
<li>204：用于删除成功，也可用于找不到资源。</li>
<li>400：body和“？”后的参数类型错误，抛出BadRequestException。</li>
<li>404：“/”后的参数错误，或者找不到资源。</li>
<li>409：修改的数据与原数据产生了冲突（根据业务定义），返回409，抛出ClienErrorException。</li>
<li>422：当用户针对不存在的资源进行操作时，则返回404，当用户传入无效字段，则返回422，抛出异常UnprocessableException。<br>例如：<br>修改A资源，但A资源不存在，则返回404；<br>根据A资源，添加A与B资源的关系，此时操作主体是A与B资源的关系表，当A和B都不存在时，则是属于创建某个资源，发生了一个验证错误，此时应该返回422。</li>
</ul>
<hr>
<h2 id="附1-常用http状态码"><a href="#附1-常用http状态码" class="headerlink" title="附1. 常用http状态码"></a>附1. 常用http状态码</h2><p>服务器向用户返回的状态码和提示信息，常见的有以下一些（方括号中是该状态码对应的HTTP动词）。</p>
<ul>
<li><blockquote>
<p>200 OK - [<strong>GET</strong>]：服务器成功返回用户请求的数据，该操作是幂等的（Idempotent）（在编程中，一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。）</p>
</blockquote>
</li>
<li><blockquote>
<p>201 CREATED - [<strong>POST/PUT/PATCH</strong>]：用户新建或修改数据成功。</p>
</blockquote>
</li>
<li><blockquote>
<p>202 Accepted - [*]：表示一个请求已经进入后台排队（异步任务）</p>
</blockquote>
</li>
<li><blockquote>
<p>204 NO CONTENT - [DELETE]：用户删除数据成功。</p>
</blockquote>
</li>
<li><blockquote>
<p>400 INVALID REQUEST - [<strong>POST/PUT/PATCH</strong>]：用户请求错误，参数格式不符合调用API规定、资源不存在、语义参数服务端无法处理等</p>
</blockquote>
</li>
<li><blockquote>
<p>401 Unauthorized - [*] ：表示用户<strong>没有权限</strong>（令牌、用户名、密码错误）。</p>
</blockquote>
</li>
<li><blockquote>
<p>403 Forbidden - [*]：表示用户<strong>得到授权</strong>（与401错误相对），但是<strong>访问是被禁止</strong>的。</p>
</blockquote>
</li>
<li><blockquote>
<p>404 NOT FOUND - [*]：用户发出的请求针对的是不存在的记录，<strong>服务器没有进行操作</strong>，该操作是幂等的。</p>
</blockquote>
</li>
<li><blockquote>
<p>406 Not Acceptable - [<strong>GET</strong>]：用户请求的格式不可得（比如用户请求JSON格式，但是只有XML格式）。</p>
</blockquote>
</li>
<li><blockquote>
<p>410 Gone -[<strong>GET</strong>]：用户请求的资源被永久删除，且不会再得到的。</p>
</blockquote>
</li>
<li><blockquote>
<p>422 Unprocesable entity - [<strong>POST/PUT/PATCH</strong>]：<strong>当创建一个对象时，发生一个验证错误。</strong></p>
</blockquote>
</li>
<li><blockquote>
<p>500 INTERNAL SERVER ERROR - [*]：服务器发生错误，用户将无法判断发出的请求是否成功。</p>
</blockquote>
</li>
</ul>
<h2 id="附2-服务接口方法名规"><a href="#附2-服务接口方法名规" class="headerlink" title="附2. 服务接口方法名规"></a>附2. 服务接口方法名规</h2><ul>
<li><blockquote>
<p>add开头方法代表添加数据：add</p>
</blockquote>
</li>
<li><blockquote>
<p>modify开头方法代表修改业务数据：modify</p>
</blockquote>
</li>
<li><blockquote>
<p>query开头方法代表查询多条数据：queryByCondition，queryByIDs</p>
</blockquote>
</li>
<li><blockquote>
<p>get开头方法代表返回一条数据：getByID</p>
</blockquote>
</li>
<li><blockquote>
<p>delete开头方法代表删除数据：deleteByID</p>
</blockquote>
</li>
<li><blockquote>
<p>提供服务的方法不要有重载</p>
</blockquote>
</li>
<li><blockquote>
<p>方法参数不要超过5个，多于5个请使用包装类（DTO）</p>
</blockquote>
</li>
<li><blockquote>
<p>方法参数类型不能使用int/char/double/long/boolean/short/float，必须换成对应Integer/Character/Double/Long/Boolean/Short/Float类型</p>
</blockquote>
</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;HTTP状态码可以指明请求是否成功，还可以揭示请求失败的确切原因，是前后端进行“交流”的重要语言。在实际开发过程中，存在着对不同状态码应用场景不清楚，如何正确抛出合适的状态码的问题，使得前端开发人员不能很好的获取发起请求后后台的反应。本文旨在通过实际应用对现业务涉及的状态码
    
    </summary>
    
    
      <category term="http" scheme="http://yoursite.com/tags/http/"/>
    
  </entry>
  
</feed>
