Files
redkale/source.html
Redkale 66e364f204
2017-03-26 15:13:15 +08:00

474 lines
70 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="UTF-8">
<title>Redkale(红菜苔)--基于Java 8全新的微服务开源框架 - Redkale官网</title>
<meta name="keywords" content="Redkale,redkale,java,微服务,架构"/>
<meta name="description" content="Redkale(红菜苔)是基于Java 8全新的微服务开源框架"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="stylesheets/stylesheet.css" media="screen">
</head>
<body>
<section class="page-header">
<a href="index.html" class="project-name">Redkale</a>
<h2 class="project-tagline"></h2>
<a href="redkale.html" class="btn">Redkale 入门</a>
<a href="convert.html" class="btn">Convert 组件</a>
<a href="service.html" class="btn">Service 组件</a>
<a href="source.html" class="btn">Source 组件</a>
<a href="net.html" class="btn">Net 组件</a>
<a href="watch.html" class="btn">Watch 组件</a>
<a href="plugins.html" class="btn">Redkale 插件</a>
<a href="articles.html" class="btn">技术文章</a>
</section>
<section class="main-content">
<h3><a id="source_intro" class="anchor" href="#" aria-hidden="true"></a>Source 组件介绍</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Source 主要为数据源提供简易的操作接口,使底层的具体数据源(传统数据库、文件系统、内存数据库、Memcached/Redis缓存)对上层是透明的。其提供两种类型的数据源DataSource 和 CacheSource。DataSource 为数据库或内存数据库提供类似JPA、Hibernate的接口与功能。CacheSource 为缓存数据提供类似Memcached、Redis的接口和功能。两者也提供了异步接口(基于<a href="service.html#service_remote" target="_blank">远程模式Service</a>)。<br/></p>
<a href="article_regain.html"></a>
<h3><a id="source_datasource" class="anchor" href="#" aria-hidden="true"></a>DataSource 入门</h3>
<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JPA虽已提供了简洁成熟的数据库操作接口但当数据、业务量很庞大的时候就显得捉襟见肘与JPA相比DataSource有以下几个特点<br/>
<a href="article_regain.html"></a>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1、简易的过滤查询接口但仅支持简单的表关联查询。 <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、简化分表分库操作。 <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3、动态更新变更后的数据库连接参数。 <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4、读写分离的简易配置。 <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5、进程间的缓存同步。 <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6、提供异步接口。 <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;数据库操作方面常见的是过滤查询操作JPA规范中的JPQL虽然简化了SQL但是对于动态产生的过滤条件,开发人员还是无法免去组装过滤条件的过程(无论JPQL还是CriteriaQuery), DataSource定义了FilterBean接口可以省略组装条件的过程FilterNode提供了类似CriteriaQuery的功能且这两种对象都可以序列化<a href="service.html#service_remote" target="_blank">远程模式Service</a>提供了基础微服务架构提倡服务之间尽量降低耦合因此DataSource仅支持简单的关联查询复杂的表关联查询或统计应放在数据分析系统中。一个服务通常部署多个进程若用JPA的缓存则进程之间的缓存无法同步而DataSource采用SNCP协议即可方便地达到自动同步缓存功能。JPA无法在主数据库异常时动态切换到备份数据库DataSource会监听<a href="#source_confxml">persistence.xml</a>文件,当文件配置发生变化时自动切换新的数据库配置。<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了降低学习成本DataSource重用了JPA里的部分注解与配置文件使用方法基本相同与JPA用法的区别是注解只能标记于字段不能标记在方法上。
</p>
<table style="margin: auto;">
<tr><th>注解类名</th><th>功能描述</th></tr>
<tr><td style="text-align: left;">javax.persistence.Cacheable</td><td>标记Entity类是否需要缓存与JPA用法一致</td></tr>
<tr><td style="text-align: left;">javax.persistence.Column</td><td>标记字段只使用其name()、insertable()、updatable()属性</td></tr>
<tr><td style="text-align: left;">javax.persistence.Entity</td><td>JPA的Entity类必须标记为@Entity, 而Redkale不强制要求该注解一般较少使用</td></tr>
<tr><td style="text-align: left;">javax.persistence.GeneratedValue</td><td>仅用于标记主键是否为数据库自增长</td></tr>
<tr><td style="text-align: left;">javax.persistence.Id<td>标记主键字段与JPA用法一致</td></tr>
<tr><td style="text-align: left;">javax.persistence.Table<td>标记表的别名与JPA用法一致</td></tr>
<tr><td style="text-align: left;">javax.persistence.Transient<td>标记是否为表对应的字段与JPA用法一致</td></tr>
<tr><td style="text-align: center;font-size: 0.9rem;" colspan="2"><b>以下是Redkale自定义的注解</b></td></tr>
<tr><td style="text-align: left;"><b>@VirtualEntity</b></td><td>用于非数据库表对应的Entity类且仅用于开启缓存模式的DataSource</td></tr>
<tr><td style="text-align: left;"><b>@DistributeTable</b></td><td>标记表进行分表分库存储, 与DistributeTableStrategy接口结合使用</td></tr>
<tr><td style="text-align: left;"><b>@FilterColumn</b></td><td>用于FilterBean过滤类的字段设置</td></tr>
<tr><td style="text-align: left;"><b>@FilterJoinColumn</b></td><td>用于FilterBean过滤类的关联表字段设置</td></tr>
<tr><td style="text-align: left;"><b>@FilterGroup</b></td><td>用于FilterBean过滤类的过滤条件分组设置</td></tr>
</table>
<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;操作数据源主要使用的对象有 DataSource、FilterBean、FilterNode。DataSource 提供的接口分几种系列:
</p>
<table style="margin: auto;">
<tr><th>系列方法</th><th>功能描述</th></tr>
<tr><td style="text-align: left;">insert</td><td>插入数据</td></tr>
<tr><td style="text-align: left;">delete</td><td>删除数据</td></tr>
<tr><td style="text-align: left;">update</td><td>更新数据</td></tr>
<tr><td style="text-align: left;">updateColumn</td><td>更新数据的部分字段</td></tr>
<tr><td style="text-align: left;">getNumberXXX</td><td>统计查询,用于查询字段的总和、最大值、平均值等数据</td></tr>
<tr><td style="text-align: left;">queryColumnXXX<td>单个字段数据查询和字段的统计查询</td></tr>
<tr><td style="text-align: left;">find<td>查找单个对象</td></tr>
<tr><td style="text-align: left;">queryList<td>查询对象的List集合</td></tr>
<tr><td style="text-align: left;">querySheet</td><td>查询对象的Sheet页式集合</td></tr>
<tr><td style="text-align: left;">directXXX<td>直接运行SQL语句用于复杂的关联查询与更新</td></tr>
</table>
<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以上接口除了directXXX其他都有等同的异步接口。insert、delete、update接口与JPA同名接口用法一样。DataSource提供了丰富的查询接口且有独特的翻页查询功能。每以系列的方法主要重载三类 单个字段过滤、FilterBean过滤和FilterNode过滤。<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>带 AsyncHandler 参数的接口均为异步接口</b><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;开发者可以借鉴 <a href="https://github.com/redkale/redkale-demo" target="_blank">Redkale-demo</a> 中的 <a href="https://github.com/redkale/redkale-demo/blob/master/src/org/redkale/demo/base/AutoClassCreator.java" target="_blank"> AutoClassCreator</a>的代码根据数据表自动生成Entity代码。
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<b>过滤条件</b></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FilterBean、FilterNode对象用于构造过滤条件。FilterBean可以转化为FilterNode。FilterBean主要用于接收外界构建的过滤条件而FilterNode为了构建内部的过滤条件且降低过滤条件变化的耦合性FilterNode中name值以#开头的视为虚拟字段不会构建成过滤条件仅供分布分库的DistributeTableStrategy策略使用。</p>
<div class="highlight"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserBean</span> <span class="kd">implements</span> <span class="n">FilterBean</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kt">int</span> <span class="n">userid</span><span class="o">;</span>
<span class="nd">@FilterColumn</span><span class="o">(</span><span class="n">express</span> <span class="o">=</span> <span class="n">FilterExpress</span><span class="o">.</span><span class="na">LIKE</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">username</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">Range</span> <span class="n">age</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">UserBean</span><span class="o">(</span><span class="kt">int</span> <span class="n">userid</span><span class="o">,</span> <span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">Range</span> <span class="n">age</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">userid</span> <span class="o">=</span> <span class="n">userid</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">username</span> <span class="o">=</span> <span class="n">username</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">age</span> <span class="o">=</span> <span class="n">age</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">/**</span> <span class="n">以下省略getter</span> <span class="n">setter方法</span> <span class="o">*/</span>
<span class="o">}</span>
<span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">200001</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">,</span> <span class="k">new</span> <span class="nf">IntRange</span><span class="o">(</span><span class="mi">14</span><span class="o">,</span> <span class="mi">36</span><span class="o">))</span> <span class="o">等价于</span>
<span class="n">FilterNode</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="s">&quot;userid&quot;</span><span class="o">,</span> <span class="mi">200001</span><span class="o">).</span><span class="na">and</span><span class="o">(</span><span class="s">&quot;username&quot;</span><span class="o">,</span> <span class="n">FilterExpress</span><span class="o">.</span><span class="na">LIKE</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">).</span><span class="na">and</span><span class="o">(</span><span class="s">&quot;age&quot;</span><span class="o">,</span> <span class="k">new</span> <span class="n">Range</span><span class="o">.</span><span class="na">IntRange</span><span class="o">(</span><span class="mi">14</span><span class="o">,</span> <span class="mi">36</span><span class="o">))</span>
<span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">200001</span><span class="o">,</span><span class="s">&quot;redkale&quot;</span><span class="o">,</span><span class="k">new</span> <span class="nf">IntRange</span><span class="o">(</span><span class="mi">14</span><span class="o">,</span><span class="mi">36</span><span class="o">))</span> <span class="o">等价于</span> <span class="s">&quot;WHERE userid=200001 AND username LIKE &#39;%redkale%&#39; AND age BETWEEN 14 AND 36&quot;</span>
<span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">200001</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">,</span> <span class="kc">null</span><span class="o">)</span> <span class="o">等价于</span> <span class="s">&quot;WHERE userid = 200001 AND username LIKE &#39;%redkale%&#39;&quot;</span>
<span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">,</span> <span class="kc">null</span><span class="o">)</span> <span class="o">等价于</span> <span class="s">&quot;WHERE username LIKE &#39;%redkale%&#39;&quot;</span>
</pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如上定义UserBean过滤条件当非数值类字段值为null、字符串值为空、数值类字段值小于@FilterColumn.least()值(least的默认值为1)都不会构建成过滤条件。@FilterColumn.express根据字段的类型有不同的默认值若字段类型为Collection子类或数组则express默认为FilterExpress.IN若字段类型为Range的子类则express默认为FilterExpress.BETWEEN其他类型则express默认为FilterExpress.EQUAL。默认字段之间是AND关系若想使用OR关系则需要使用@FilterGroup进行标记</p>
<div class="highlight"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserBean</span> <span class="kd">implements</span> <span class="n">FilterBean</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kt">int</span> <span class="n">userid</span><span class="o">;</span>
<span class="nd">@FilterGroup</span><span class="o">(</span><span class="s">&quot;[OR]a&quot;</span><span class="o">)</span>
<span class="nd">@FilterColumn</span><span class="o">(</span><span class="n">express</span> <span class="o">=</span> <span class="n">FilterExpress</span><span class="o">.</span><span class="na">LIKE</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">username</span><span class="o">;</span>
<span class="nd">@FilterGroup</span><span class="o">(</span><span class="s">&quot;[OR]a&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">Range</span> <span class="n">age</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">UserBean</span><span class="o">(</span><span class="kt">int</span> <span class="n">userid</span><span class="o">,</span> <span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">Range</span> <span class="n">age</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">userid</span> <span class="o">=</span> <span class="n">userid</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">username</span> <span class="o">=</span> <span class="n">username</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">age</span> <span class="o">=</span> <span class="n">age</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">/**</span> <span class="n">以下省略getter</span> <span class="n">setter方法</span> <span class="o">*/</span>
<span class="o">}</span>
<span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">200001</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">,</span> <span class="k">new</span> <span class="nf">IntRange</span><span class="o">(</span><span class="mi">14</span><span class="o">,</span> <span class="mi">36</span><span class="o">))</span> <span class="o">等价于</span>
<span class="n">FilterNode</span> <span class="n">orNode</span> <span class="o">=</span> <span class="n">FilterNode</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="s">&quot;username&quot;</span> <span class="o">,</span> <span class="n">FilterExpress</span><span class="o">.</span><span class="na">LIKE</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">).</span><span class="na">or</span><span class="o">(</span><span class="s">&quot;age&quot;</span><span class="o">,</span> <span class="k">new</span> <span class="n">Range</span><span class="o">.</span><span class="na">IntRange</span><span class="o">(</span><span class="mi">14</span><span class="o">,</span> <span class="mi">36</span><span class="o">));</span>
<span class="n">FilterNode</span> <span class="n">node</span> <span class="o">=</span> <span class="n">FilterNode</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="s">&quot;userid&quot;</span><span class="o">,</span> <span class="mi">200001</span><span class="o">).</span><span class="na">and</span><span class="o">(</span><span class="n">orNode</span><span class="o">);</span>
<span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">200001</span><span class="o">,</span><span class="s">&quot;redkale&quot;</span><span class="o">,</span><span class="k">new</span> <span class="nf">IntRange</span><span class="o">(</span><span class="mi">14</span><span class="o">,</span><span class="mi">36</span><span class="o">))</span> <span class="o">等价于</span> <span class="s">&quot;WHERE userid=200001 AND (username LIKE &#39;%redkale%&#39; OR age BETWEEN 14 AND 36)&quot;</span>
<span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">200001</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">,</span> <span class="kc">null</span><span class="o">)</span> <span class="o">等价于</span> <span class="s">&quot;WHERE userid = 200001 AND username LIKE &#39;%redkale%&#39;&quot;</span>
<span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">,</span> <span class="kc">null</span><span class="o">)</span> <span class="o">等价于</span> <span class="s">&quot;WHERE username LIKE &#39;%redkale%&#39;&quot;</span>
<span class="n">source</span><span class="o">.</span><span class="na">getNumberResult</span><span class="o">(</span><span class="n">User</span><span class="o">.</span><span class="k">class</span><span class="o">,</span> <span class="n">FilterFunc</span><span class="o">.</span><span class="na">COUNT</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="k">new</span> <span class="nf">UserBean</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="s">&quot;redkale&quot;</span><span class="o">,</span> <span class="k">new</span> <span class="nf">IntRange</span><span class="o">(</span><span class="mi">14</span><span class="o">,</span> <span class="mi">36</span><span class="o">))).</span><span class="na">intValue</span><span class="o">()</span> <span class="o">等价于</span>
<span class="s">&quot;SELECT COUNT(*) FROM user WHERE username LIKE &#39;%redkale%&#39; AND age BETWEEN 14 AND 36&quot;</span>
</pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如上@FilterGroup 的value 必须是[OR]或者[AND]开头,没有标记@FilterGroup的字段等价于标记了@FilterGroup(value = "[AND]")。[AND]、[OR]后面的字符串为GROUP_NAME默认的GROUP_NAME为空字符串。如上"[OR]a"可以直接使用"[OR]",有多个[OR]或者[AND]则需要加上不同的NAME。</p>
<p id="source_distribute">&nbsp;&nbsp;&nbsp;&nbsp;<b>分表分库</b></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DataSource提供了单个实体类对应多个数据库表的功能通常流水型的数据量比较大单个数据库无法存储DataSource提供了简单的分表操作同时在接口设计上尽量减少单表操作与分表操作的差异。分表分库只需在实体类上注解@DistributeTable并实现DistributeTableStrategy分表策略即可。</p>
<div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">DistributeTableStrategy</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">{</span>
<span class="cm">/**</span>
<span class="cm"> * 获取对象的表名</span>
<span class="cm"> * 查询单个对象时调用本方法获取表名</span>
<span class="cm"> *</span>
<span class="cm"> * @param table 模板表的表名</span>
<span class="cm"> * @param primary 记录主键</span>
<span class="cm"> *</span>
<span class="cm"> * @return</span>
<span class="cm"> */</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">Serializable</span> <span class="n">primary</span><span class="o">);</span>
<span class="cm">/**</span>
<span class="cm"> * 获取对象的表名</span>
<span class="cm"> * 查询、修改、删除对象时调用本方法获取表名</span>
<span class="cm"> * 注意: 需保证FilterNode过滤的结果集合必须在一个数据库表中</span>
<span class="cm"> *</span>
<span class="cm"> * @param table 模板表的表名</span>
<span class="cm"> * @param node 过滤条件</span>
<span class="cm"> *</span>
<span class="cm"> * @return</span>
<span class="cm"> */</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">FilterNode</span> <span class="n">node</span><span class="o">);</span>
<span class="cm">/**</span>
<span class="cm"> * 获取对象的表名</span>
<span class="cm"> * 新增对象或更新单个对象时调用本方法获取表名</span>
<span class="cm"> *</span>
<span class="cm"> * @param table 模板表的表名</span>
<span class="cm"> * @param bean 实体对象</span>
<span class="cm"> *</span>
<span class="cm"> * @return</span>
<span class="cm"> */</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">T</span> <span class="n">bean</span><span class="o">);</span>
<span class="o">}</span>
</pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DistributeTableStrategy分表策略需要实现三个接口模板表由实体类的@Table注解提供。Redkale默认实现的MySQL数据库的拷贝表结构语句其他数据库类型需要通过指定<a href="#source_confxml">persistence.xml</a> 中的 <b>javax.persistence.tablenotexist.sqlstates</b><b>javax.persistence.tablecopy.sqltemplate</b> 来配置。</p>
<div class="highlight"><pre><span></span><span class="nd">@DistributeTable</span><span class="o">(</span><span class="n">strategy</span> <span class="o">=</span> <span class="n">LoginRecord</span><span class="o">.</span><span class="na">TableStrategy</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">LoginRecord</span> <span class="kd">extends</span> <span class="n">BaseEntity</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;主键ID; 值=UUID+create36time&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">loginid</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//主键ID; 值=UUID+create36time</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;C端用户ID&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">userid</span><span class="o">;</span> <span class="c1">//C端用户ID</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;登录网络类型; wifi/4g/3g&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">netmode</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//登录网络类型; wifi/4g/3g</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;APP版本信息&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">appversion</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//APP版本信息</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;APP操作系统信息&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">appos</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//APP操作系统信息</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;登录时客户端信息&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">loginagent</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//登录时客户端信息</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;登录时的IP&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">loginaddr</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//登录时的IP</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;创建时间&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">createtime</span><span class="o">;</span> <span class="c1">//创建时间</span>
<span class="cm">/** 以下省略getter setter方法 */</span>
<span class="c1">//创建对象</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
<span class="n">LoginRecord</span> <span class="n">record</span> <span class="o">=</span> <span class="k">new</span> <span class="n">LoginRecord</span><span class="o">();</span>
<span class="kt">long</span> <span class="n">now</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span>
<span class="n">record</span><span class="o">.</span><span class="na">setCreatetime</span><span class="o">(</span><span class="n">now</span><span class="o">);</span> <span class="c1">//设置创建时间</span>
<span class="n">String</span> <span class="n">create36time</span> <span class="o">=</span> <span class="n">Long</span><span class="o">.</span><span class="na">toString</span><span class="o">(</span><span class="n">now</span><span class="o">,</span> <span class="mi">36</span><span class="o">);</span> <span class="c1">//时间的36进制</span>
<span class="k">if</span> <span class="o">(</span><span class="n">create36time</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&lt;</span> <span class="mi">9</span><span class="o">)</span> <span class="n">create36time</span> <span class="o">=</span> <span class="s">&quot;0&quot;</span> <span class="o">+</span> <span class="n">create36time</span><span class="o">;</span> <span class="c1">//当前时间值的36进制只可能是8位或9位不足9位填充0</span>
<span class="n">record</span><span class="o">.</span><span class="na">setLoginid</span><span class="o">(</span><span class="n">Utility</span><span class="o">.</span><span class="na">uuid</span><span class="o">()</span> <span class="o">+</span> <span class="n">create36time</span><span class="o">);</span> <span class="c1">//主键的生成规则</span>
<span class="c1">//.... 填充其他字段</span>
<span class="n">source</span><span class="o">.</span><span class="na">insert</span><span class="o">(</span><span class="n">record</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">TableStrategy</span> <span class="kd">implements</span> <span class="n">DistributeTableStrategy</span><span class="o">&lt;</span><span class="n">LoginRecord</span><span class="o">&gt;</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">dayformat</span> <span class="o">=</span> <span class="s">&quot;%1$tY%1$tm%1$td&quot;</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">yearformat</span> <span class="o">=</span> <span class="s">&quot;%1$tY&quot;</span><span class="o">;</span>
<span class="c1">//过滤查询时调用本方法</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">FilterNode</span> <span class="n">node</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Serializable</span> <span class="n">day</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="na">findValue</span><span class="o">(</span><span class="s">&quot;#day&quot;</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">day</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="n">getTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="o">(</span><span class="n">Integer</span><span class="o">)</span> <span class="n">day</span><span class="o">,</span> <span class="mi">0</span><span class="n">L</span><span class="o">);</span> <span class="c1">//存在#day参数则直接使用day值</span>
<span class="n">Serializable</span> <span class="n">time</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="na">findValue</span><span class="o">(</span><span class="s">&quot;#createtime&quot;</span><span class="o">);</span> <span class="c1">//存在createtime则使用最小时间且createtime的范围必须在一天内因为本表以天为单位建表</span>
<span class="k">return</span> <span class="n">getTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="o">(</span><span class="n">time</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">?</span> <span class="mi">0</span><span class="n">L</span> <span class="o">:</span> <span class="o">(</span><span class="n">time</span> <span class="k">instanceof</span> <span class="n">Range</span> <span class="o">?</span> <span class="o">((</span><span class="n">Range</span><span class="o">.</span><span class="na">LongRange</span><span class="o">)</span> <span class="n">time</span><span class="o">).</span><span class="na">getMin</span><span class="o">()</span> <span class="o">:</span> <span class="o">(</span><span class="n">Long</span><span class="o">)</span> <span class="n">time</span><span class="o">)));</span>
<span class="o">}</span>
<span class="c1">//创建或单个查询时调用本方法</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">LoginRecord</span> <span class="n">bean</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">getTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">bean</span><span class="o">.</span><span class="na">getCreatetime</span><span class="o">());</span>
<span class="o">}</span>
<span class="c1">//根据主键ID查询单个记录时调用本方法</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">Serializable</span> <span class="n">primary</span><span class="o">)</span> <span class="o">{</span>
<span class="n">String</span> <span class="n">loginid</span> <span class="o">=</span> <span class="o">(</span><span class="n">String</span><span class="o">)</span> <span class="n">primary</span><span class="o">;</span>
<span class="n">String</span> <span class="n">create36time</span> <span class="o">=</span> <span class="n">loginid</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">loginid</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">-</span> <span class="mi">9</span><span class="o">);</span> <span class="c1">//固定最后9位为创建时间的36进制值</span>
<span class="k">return</span> <span class="n">getTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">Long</span><span class="o">.</span><span class="na">parseLong</span><span class="o">(</span><span class="n">create36time</span><span class="o">,</span> <span class="mi">36</span><span class="o">));</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="kt">int</span> <span class="n">day</span><span class="o">,</span> <span class="kt">long</span> <span class="n">createtime</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">pos</span> <span class="o">=</span> <span class="n">table</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="sc">&#39;.&#39;</span><span class="o">);</span>
<span class="n">String</span> <span class="n">year</span> <span class="o">=</span> <span class="o">(</span><span class="n">day</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">?</span> <span class="s">&quot;&quot;</span> <span class="o">+</span> <span class="n">day</span> <span class="o">/</span> <span class="mi">10000</span> <span class="o">:</span> <span class="n">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="n">yearformat</span><span class="o">,</span> <span class="n">createtime</span><span class="o">));</span>
<span class="k">return</span> <span class="s">&quot;platf_login_&quot;</span> <span class="o">+</span> <span class="n">year</span> <span class="o">+</span> <span class="s">&quot;.&quot;</span> <span class="o">+</span> <span class="n">table</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span> <span class="o">+</span> <span class="s">&quot;_&quot;</span> <span class="o">+</span> <span class="o">(</span><span class="n">day</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">day</span> <span class="o">:</span> <span class="n">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="n">dayformat</span><span class="o">,</span> <span class="n">createtime</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如上范例用户登陆记录的分表分库策略为一年一个库一个库中365张表每天一个表。为了分表策略的三个接口均得到实现需要对主键ID的生成规则进行一定的设计。常见的场景是查询单个用户的登录列表。上面的范例就无法满足查询单个用户的登录信息需求而分表策略又只能根据一种规则生成因此需要按用户维度存在另外一张表中。</p>
<div class="highlight"><pre><span></span><span class="nd">@DistributeTable</span><span class="o">(</span><span class="n">strategy</span> <span class="o">=</span> <span class="n">LoginUserRecord</span><span class="o">.</span><span class="na">TableStrategy</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">LoginUserRecord</span> <span class="kd">extends</span> <span class="n">BaseEntity</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;记录ID; 值=userid+'-'+UUID&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">seqid</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//记录ID; 值=userid+'-'+UUID</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;C端用户ID&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">userid</span><span class="o">;</span> <span class="c1">//C端用户ID</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;LoginRecord主键&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">loginid</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//LoginRecord主键</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;创建时间&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">createtime</span><span class="o">;</span> <span class="c1">//创建时间</span>
<span class="cm">/** 以下省略getter setter方法 */</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">TableStrategy</span> <span class="kd">implements</span> <span class="n">DistributeTableStrategy</span><span class="o">&lt;</span><span class="n">LoginUserRecord</span><span class="o">&gt;</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">LoginUserRecord</span> <span class="n">bean</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">getTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="n">bean</span><span class="o">.</span><span class="na">getUserid</span><span class="o">());</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">FilterNode</span> <span class="n">node</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Serializable</span> <span class="n">id</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="na">findValue</span><span class="o">(</span><span class="s">&quot;userid&quot;</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">id</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="k">return</span> <span class="n">getTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="n">id</span><span class="o">);</span>
<span class="k">return</span> <span class="n">getHashTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="o">(</span><span class="n">Integer</span><span class="o">)</span> <span class="n">node</span><span class="o">.</span><span class="na">findValue</span><span class="o">(</span><span class="s">&quot;#hash&quot;</span><span class="o">));</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">Serializable</span> <span class="n">primary</span><span class="o">)</span> <span class="o">{</span>
<span class="n">String</span> <span class="n">id</span> <span class="o">=</span> <span class="o">(</span><span class="n">String</span><span class="o">)</span> <span class="n">primary</span><span class="o">;</span>
<span class="k">return</span> <span class="n">getHashTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="o">(</span><span class="n">Long</span><span class="o">.</span><span class="na">parseLong</span><span class="o">(</span><span class="n">id</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">id</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="sc">&#39;-&#39;</span><span class="o">)))</span> <span class="o">%</span> <span class="mi">100</span><span class="o">));</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="n">String</span> <span class="nf">getHashTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="kt">int</span> <span class="n">hash</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">pos</span> <span class="o">=</span> <span class="n">table</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="sc">&#39;.&#39;</span><span class="o">);</span>
<span class="k">return</span> <span class="s">&quot;platf_login.&quot;</span> <span class="o">+</span> <span class="n">table</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span> <span class="o">+</span> <span class="s">&quot;_&quot;</span> <span class="o">+</span> <span class="o">(</span><span class="n">hash</span> <span class="o">&gt;</span> <span class="mi">9</span> <span class="o">?</span> <span class="n">hash</span> <span class="o">:</span> <span class="o">(</span><span class="s">&quot;0&quot;</span> <span class="o">+</span> <span class="n">hash</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如上表LoginUserRecord只存储用户ID与登录信息ID的关联关系以用户ID取模100进行hash存储获取用户登录列表时先查询LoginUserRecord一页的数据再根据loginid查询LoginRecord实体。常见的分表策略是时间和主键hash例如用户信息表采用主键hash分表</p>
<div class="highlight"><pre><span></span><span class="nd">@DistributeTable</span><span class="o">(</span><span class="n">strategy</span> <span class="o">=</span> <span class="n">UserDetail</span><span class="o">.</span><span class="na">TableStrategy</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserDetail</span> <span class="kd">extends</span> <span class="n">BaseEntity</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">userid</span><span class="o">;</span> <span class="c1">//用户ID</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">length</span> <span class="o">=</span> <span class="mi">64</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;用户昵称&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">username</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//用户昵称</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">length</span> <span class="o">=</span> <span class="mi">32</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;手机号码&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">mobile</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//手机号码</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">length</span> <span class="o">=</span> <span class="mi">64</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;密码&quot;</span><span class="o">)</span>
<span class="nd">@ConvertColumn</span><span class="o">(</span><span class="n">ignore</span> <span class="o">=</span> <span class="kc">true</span><span class="o">,</span> <span class="n">type</span> <span class="o">=</span> <span class="n">ConvertType</span><span class="o">.</span><span class="na">ALL</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">password</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//密码</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">length</span> <span class="o">=</span> <span class="mi">128</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;备注&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">remark</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">;</span> <span class="c1">//备注</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">,</span> <span class="n">comment</span> <span class="o">=</span> <span class="s">&quot;创建时间&quot;</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">createtime</span><span class="o">;</span> <span class="c1">//创建时间</span>
<span class="cm">/** 以下省略getter setter方法 */</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">TableStrategy</span> <span class="kd">implements</span> <span class="n">DistributeTableStrategy</span><span class="o">&lt;</span><span class="n">UserDetail</span><span class="o">&gt;</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">UserDetail</span> <span class="n">bean</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">getTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="n">bean</span><span class="o">.</span><span class="na">getUserid</span><span class="o">());</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">FilterNode</span> <span class="n">node</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Serializable</span> <span class="n">id</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="na">findValue</span><span class="o">(</span><span class="s">&quot;userid&quot;</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">id</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="k">return</span> <span class="n">getTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="n">id</span><span class="o">);</span>
<span class="k">return</span> <span class="n">getHashTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="o">(</span><span class="n">Integer</span><span class="o">)</span> <span class="n">node</span><span class="o">.</span><span class="na">findValue</span><span class="o">(</span><span class="s">&quot;#hash&quot;</span><span class="o">));</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="n">Serializable</span> <span class="n">userid</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">getHashTable</span><span class="o">(</span><span class="n">table</span><span class="o">,</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="o">(((</span><span class="n">Long</span><span class="o">)</span> <span class="n">userid</span><span class="o">)</span> <span class="o">%</span> <span class="mi">100</span><span class="o">));</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="n">String</span> <span class="nf">getHashTable</span><span class="o">(</span><span class="n">String</span> <span class="n">table</span><span class="o">,</span> <span class="kt">int</span> <span class="n">hash</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">pos</span> <span class="o">=</span> <span class="n">table</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="sc">&#39;.&#39;</span><span class="o">);</span>
<span class="k">return</span> <span class="s">&quot;platf_user.&quot;</span> <span class="o">+</span> <span class="n">table</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span> <span class="o">+</span> <span class="s">&quot;_&quot;</span> <span class="o">+</span> <span class="o">(</span><span class="n">hash</span> <span class="o">&gt;</span> <span class="mi">9</span> <span class="o">?</span> <span class="n">hash</span> <span class="o">:</span> <span class="o">(</span><span class="s">&quot;0&quot;</span> <span class="o">+</span> <span class="n">hash</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></div><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如上用户表以userid取模100进行hash分表若需要提供根据手机号查询单个用户信息则需要另外存在一个用户ID对应手机号码的关系表同样可以以手机号后两位数字为hash存储。</p>
<h3><a id="source_cachesource" class="anchor" href="#" aria-hidden="true"></a>CacheSource 入门</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CacheSource同Memcached类似像一个带有过期功能地Map容器存放key-value数据。常见的使用场景就是存放HTTP的Session信息。Redkale把用户会话信息数据当做业务数据处理而不是接入层的数据。WebSocket的连接态数据也是用CacheSource存储。key为WebSocket的groupidvalue为WebSocket服务端节点的IP地址列表。</p>
<div class="highlight"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserService</span> <span class="kd">implements</span> <span class="n">Service</span> <span class="o">{</span>
<span class="c1">//用户简单信息缓存</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">,</span> <span class="n">UserInfo</span><span class="o">&gt;</span> <span class="n">users</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ConcurrentHashMap</span><span class="o">&lt;&gt;();</span>
<span class="c1">//使用CacheSource必须要指明泛型</span>
<span class="nd">@Resource</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">&quot;usersessions&quot;</span><span class="o">)</span>
<span class="kd">protected</span> <span class="n">CacheSource</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Integer</span><span class="o">&gt;</span> <span class="n">sessions</span><span class="o">;</span>
<span class="c1">//登录</span>
<span class="kd">public</span> <span class="n">RetResult</span><span class="o">&lt;</span><span class="n">UserInfo</span><span class="o">&gt;</span> <span class="nf">login</span><span class="o">(</span><span class="n">LoginBean</span> <span class="n">bean</span><span class="o">)</span> <span class="o">{</span> <span class="c1">//bean.sessionid 在接入层进行赋值</span>
<span class="n">UserInfo</span> <span class="n">user</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="c1">// 登陆逻辑 user = ...</span>
<span class="n">users</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">getUserid</span><span class="o">(),</span> <span class="n">user</span><span class="o">);</span>
<span class="n">sessions</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="mi">600</span><span class="o">,</span> <span class="n">bean</span><span class="o">.</span><span class="na">getSessionid</span><span class="o">(),</span> <span class="n">user</span><span class="o">.</span><span class="na">getUserid</span><span class="o">());</span> <span class="c1">//session过期时间设置为10分钟</span>
<span class="k">return</span> <span class="k">new</span> <span class="n">RetResult</span><span class="o">&lt;&gt;(</span><span class="n">user</span><span class="o">);</span>
<span class="o">}</span>
<span class="c1">//获取当前用户信息</span>
<span class="kd">public</span> <span class="n">UserInfo</span> <span class="nf">current</span><span class="o">(</span><span class="n">String</span> <span class="n">sessionid</span><span class="o">)</span> <span class="o">{</span> <span class="c1">//给HTTP的BaseServlet用</span>
<span class="n">Integer</span> <span class="n">userid</span> <span class="o">=</span> <span class="n">sessions</span><span class="o">.</span><span class="na">getAndRefresh</span><span class="o">(</span><span class="n">sessionid</span><span class="o">);</span>
<span class="k">return</span> <span class="n">userid</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">users</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">userid</span><span class="o">);</span>
<span class="o">}</span>
<span class="c1">//注销</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">logout</span><span class="o">(</span><span class="n">String</span> <span class="n">sessionid</span><span class="o">)</span> <span class="o">{</span>
<span class="n">sessions</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">sessionid</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></pre></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以上是个简单的范例用于用户模块存放sessionid。</p>
<h3><a id="source_confxml" href="#" aria-hidden="true"></a>persistence.xml 配置说明</h3>
<div class="highlight"><pre><span></span><span class="c">&lt;!-- 其配置算是标准的JPA配置文件的缩略版 --&gt;</span>
<span class="p">&lt;</span><span class="nt">persistence</span><span class="p">&gt;</span>
<span class="c">&lt;!-- 系统基本库 --&gt;</span>
<span class="p">&lt;</span><span class="nt">persistence-unit</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;demouser&quot;</span><span class="p">&gt;</span>
<span class="c">&lt;!-- 为NONE表示不启动缓存@Cacheable 失效; 非NONE值(通常用ALL)表示开启缓存。 --&gt;</span>
<span class="p">&lt;</span><span class="nt">shared-cache-mode</span><span class="p">&gt;</span>NONE<span class="p">&lt;/</span><span class="nt">shared-cache-mode</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">properties</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.jdbc.url&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8&quot;</span><span class="p">/&gt;</span>
<span class="c">&lt;!-- </span>
<span class="c"> javax.persistence.jdbc.driver在JPA的值是JDBC驱动Redkale有所不同值应该是javax.sql.DataSource的子类。 </span>
<span class="c"> 为了兼容用户习惯Redkale内置常见JDBC驱动到javax.sql.DataSource的映射关系</span>
<span class="c"> org.mariadb.jdbc.Driver —————— org.mariadb.jdbc.MySQLDataSource</span>
<span class="c"> org.postgresql.Driver —————— org.postgresql.ds.PGConnectionPoolDataSource</span>
<span class="c"> com.mysql.jdbc.Driver —————— com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource</span>
<span class="c"> oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource</span>
<span class="c"> com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource</span>
<span class="c"> 因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource</span>
<span class="c"> 并且如果JDBC驱动是以上几个版本javax.persistence.jdbc.driver属性都可以省略Redkale会根据javax.persistence.jdbc.url的值来识别驱动</span>
<span class="c"> --&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.jdbc.driver&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;com.mysql.jdbc.Driver&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.jdbc.user&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;root&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.jdbc.password&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;123456&quot;</span><span class="p">/&gt;</span>
<span class="c">&lt;!-- 最大连接数默认值CPU数*16 --&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.connections.limit&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;32&quot;</span><span class="p">/&gt;</span>
<span class="c">&lt;!-- 包含的SQL模板相当于反向LIKE不同的JDBC驱动的SQL语句不一样Redkale内置了MySQL的语句 --&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.contain.sqltemplate&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;LOCATE(${keystr}, ${column}) &gt; 0&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.notcontain.sqltemplate&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;LOCATE(${keystr}, ${column}) = 0&quot;</span><span class="p">/&gt;</span>
<span class="c">&lt;!-- 复制表结构的SQL模板Redkale内置了MySQL的语句 --&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.tablenotexist.sqlstates&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;42000;42S02&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.tablecopy.sqltemplate&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;CREATE TABLE ${newtable} LIKE ${oldtable}&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;/</span><span class="nt">properties</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">persistence-unit</span><span class="p">&gt;</span>
<span class="c">&lt;!-- IM消息库 --&gt;</span>
<span class="p">&lt;</span><span class="nt">persistence-unit</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;demoim&quot;</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">shared-cache-mode</span><span class="p">&gt;</span>NONE<span class="p">&lt;/</span><span class="nt">shared-cache-mode</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">properties</span><span class="p">&gt;</span>
<span class="c">&lt;!-- jdbc:mysql://127.0.0.1:3306/dbim?autoReconnect=true&amp;amp;autoReconnectForPools=true&amp;amp;characterEncoding=utf8 --&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.jdbc.url&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;jdbc:mysql://127.0.0.1:3306/dbim?characterEncoding=utf8&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.jdbc.driver&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;com.mysql.jdbc.Driver&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.jdbc.user&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;root&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;</span><span class="nt">property</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;javax.persistence.jdbc.password&quot;</span> <span class="na">value</span><span class="o">=</span><span class="s">&quot;123456&quot;</span><span class="p">/&gt;</span>
<span class="p">&lt;/</span><span class="nt">properties</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">persistence-unit</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">persistence</span><span class="p">&gt;</span>
</pre></div>
<footer class="site-footer">
<span class="site-footer-owner">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;欢迎加入Redkale QQ群: 527523235</span>
</footer>
</section>
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?b4e05d7de8b5f3401dd93e3c6b35ffa5";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</body>
</html>