『OGG 02』Win7 配置 Oracle GoldenGate Adapter Java 踩坑指南
上一文章 《__Win7 配置OGG(Oracle GoldenGate).docx》定下了 两个目标:
- 目标1:
给安装的Oracle_11g 创建 两个用户 admin 和 root 。
admin 对应了 ADMIN 结构,创建了一个 TB_ TEST表。
root 对应了 ROOT 结构,也创建一个 TB_ TEST (表结构一摸一样)。
当 admin.TBTEST 中的表数据 变化时,root.TB TEST 的表数据 自动同步(备份)
PS. 实际的 容灾备份,肯定是 两个 Oracle 服务器,通过网络传输 备份数据 —— 各位可以想象 admin 和 root 在两台不同服务器 上。
- 目标2:
当 admin.TB_ TEST 表数据发生变化时,把这种变化 传递给 Java、C# 程序。
当 Java、C# 程序得到 数据变化消息时,把 数据的变化 记录到日志中(或者其他操作)。
其中,目标1 已经完成,我们开始尝试 目标2
下载 OGG Adapter Java:
http://www.oracle.com/technetwork/cn/middleware/goldengate/downloads/index.html
开始配置 Java运行环境:
我开始后悔了,我的系统是 64位Win7。
我后悔自己安装的是 32位的 Oracle。
我后悔自己安装了两套 JDK: C:\Program Files\Java\jdk1.8.0121 和 C:\Program Files (x86)\Java\jdk1.8.0121。
我后悔自己的OGG目录 取了这么长的 一个名字: D:\oracle\product\11.1.0\x86_ogg4oracle\
接下来的文章:这些坑将导致各种诡异的问题 —— 填坑指南,作者带着你 一起踩坑一起填,在失败中积累厚重经验、在填坑中收获绝对稳定。
配置环境变量:
我的电脑 64位Win7,安装了 64位、32位 位两种 JDK。Oracle_11g 32位,OGG 32位。
进行如下操作:
- 找到 64位 JDK目录 —— 右键,“360强力删除” —— 没错,强力粉碎,这源自我踩坑踩出的仇恨,粉碎,粉碎。 桌面>计算机>右键>属性>高级系统设置>“高级”选项卡>环境变量。
CLASSPATH : .;%JAVAHOME%\lib\tools.jar;%JAVAHOME%\lib\dt.jar;%JAVA_HOME%\bin;
JAVAHOME : C:\Program Files (x86)\Java\jdk1.8.0121
JREHOME : C:\Program Files (x86)\Java\jre1.8.0121
Path : %JAVA_HOME%\bin;
验证环境变量:
打开cmd (不详述了,cmd 都打不开,这篇文章你也不用看了)
建议重启电脑,好像有时候,JAVA的环境变量 要重启后 才生效 —— 重启保平安。
开始考验 JAVA基础的时候了:
打开 eclipse,新建一个 hello world 项目。导出 jar 包。
再次验证 java 运行环境:
你以为 这个 hello.jar 有啥用? —— 没用!
玄学懂不懂?
安装SQL Server 你不去上个厕所,你还想安装成功?【上厕所,减少人为磁盘操作,SQLServer安装程序更稳定】
你不写个 hello word,你还想把OGG Adapter Java 配成?【就是为了验证 java 运行环境,配置者的java基础】
开始配置 OGG Adapter Java 【正式代码在下面,我们要先验证 OGG-Java 的配置环境】
回顾一下:针对 ADMIN.TB_TEST 表 —— 当数据变化时,我们的Java插件要能够 捕获到数据变化。
生成表结构 定义文件。【目标:得到一个 .def 后缀的文件(过程中的其他文件都可以删除)】
在 D:\Temp\ 文件夹创建一个 文本文件 source.prm 内容如下:
运行 cmd,通过 source.prm 生成 source.def 文件。
找到 D:\oracle\product\11.1.0\x86ogg4oracletarget\ 【目标端】双击运行 ggsci.exe 程序。 添加 javaue
配置 javaue
javaue.prm如下:
Extract JAVAUE
SourceDefs dirprm/source.def
getEnv (JAVA_HOME)
-- getEnv (LDLIBRARYPATH)
getEnv (PATH)
CUserExit ggjava_ue.dll CUSEREXIT PassThru IncludeUpdateBefores
GetUpdateBefores
Table ADMIN.*;
javaue.properties 如下:
gg.handlerlist=sample.SampleHandler
java.naming.provider.url=tcp://localhost:61616
java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
gg.handler.sample.type=sample.SampleHandler
goldengate.userexit.timestamp=utc
goldengate.userexit.nochkpt=true
尝试启动 javaue
PS.
如果出现 “不出错”的错误(rpt 文件没有任何异常信息),但 start javaue 窗口一闪而过,死活无法启动。
记住如下方式:用命令行 参数模式 启动 javaue,可以显示 “不出错”错误。
extract paramfile D:\oracle\product\11.1.0\x86_ogg4oracle_target\dirprm\javaue.prm
正式编写 OGG Adapter Java 插件代码
新建项目 custom,添加 jar包 引用。
创建 Java文件 SampleHandler,源码如下:
注意:OGG 12 和 OGG 11 的代码是完全不同的。
//---------------------------------------------------------------------------------------------------
package sample; //包名称 sample,类名称 SampleHandler —— 有没有让你想到什么? javaue.properties
import java.io.*;
//这是 OGG 11 的 import
import com.goldengate.atg.datasource.AbstractHandler;
import com.goldengate.atg.datasource.DsConfiguration;
import com.goldengate.atg.datasource.DsEvent;
import com.goldengate.atg.datasource.GGDataSource.Status;
import com.goldengate.atg.datasource.meta.DsMetaData;
import com.goldengate.atg.datasource.test.DsTestUtils.Logger;
import com.goldengate.atg.datasource.meta.*;
import com.goldengate.atg.datasource.*;
//这是 OGG 12 的 import
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import oracle.goldengate.datasource.AbstractHandler;
//import oracle.goldengate.datasource.DsConfiguration;
//import oracle.goldengate.datasource.DsEvent;
//import oracle.goldengate.datasource.DsTransaction;
//import oracle.goldengate.datasource.conf.DsHandler.Handler.Status;
//import oracle.goldengate.datasource.meta.DsMetaData;
//import oracle.goldengate.datasource.GGDataSource;
public class SampleHandler extends AbstractHandler {
//OGG 11 和 OGG 12 的 日志好像不一样, OGG 12 似乎要写 LoggerFactory.getLogger(SampleHandler.class);
private final Logger logger
= com.goldengate.atg.datasource.test.DsTestUtils.Log4jLogger.getLogger(SampleHandler.class);
@Override
public void init(DsConfiguration conf, DsMetaData metaData) {
super.init(conf, metaData);
logger.info("init!");
WriteStringToFile("D:\\SampleHandler。log", "SampleHandler.init(*)");
}
@Override
public Status transactionCommit(DsEvent e, DsTransaction tx) {
logger.info("transactionCommit!");
WriteStringToFile("D:\\SampleHandler。log", "SampleHandler.transactionCommit(*)");
return Status.OK;
}
@Override
public void destroy() {
super.destroy();
WriteStringToFile("D:\\SampleHandler。log", "SampleHandler.destroy(*)");
logger.info("destroy!");
}
@Override
public String reportStatus() {
WriteStringToFile("D:\\SampleHandler。log", "SampleHandler.reportStatus(*)");
return "reportStatus";
}
public void WriteStringToFile(String filePath, String text) {
try {
System.out.println(text);
File file = new File(filePath);
PrintStream ps = new PrintStream(new FileOutputStream(file));
//ps.println(text);// 往文件里写入字符串
ps.append(text);// 在已有的基础上添加字符串
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
//---------------------------------------------------------------------------------------------------
编译通过,导出 jar包:
再次启动 javaue:
窗口再次一闪而过,启动失败,继续查错。
参考:https://blog.csdn.net/catoop/article/details/49300853
重装 Java环境,重新修改 4个环境变量,验证 java 安装环境。
java -jar D:\hello.jar
重启电脑(或者关掉所有 cmd, ggsci 窗口),重新打开 \x86ogg4oralcetarget\ggsci.exe 输入命令: start mgr start javaue
现代诗:《OGG的迷惘》
那一刻,
晶莹的泪水滑过脸庞,
我似乎,
看到了成功的希望。
蹉跎与挣扎,
纠结与迷惘。
在折腾中,我终究能走向想要的远方。
—— 再给我一次机会,让我再配置一次OGG。
—— 不!我选择 死亡(骂娘)~
打油诗:《神坑OGG》
神坑最多OGG,
坑得劳资没脾气。
坑得劳资没脾气~
神坑最多OGG~
开始测试 javaue 插件是否成功:
程序这次崩溃了,对比一下 两种配置:
在下面的配置(崩溃)中,我们看到了一个路径 dirlib/custom.jar。
我们尝试将 eclipse 导出的 jar 文件 D:\Temp\custom.jar,复制到 \x86ogg4oracletarget\dirlib\ 文件夹(dirlib 要手动创建)
再次运行,还是崩溃,还是相同的错误信息:
虽然不报错了,但为什么 我在 insert 时,插件代码没有执行呢???
我们删除 javaue,然后重建一下:
再次启动 start javaue —— 记住我的ID,再不成功,我直播倒立写代码 ~
结果我又失望了:插件似乎没有执行 —— 插件的代码会生成一个 D:\ SampleHandler.log 的日志文件的,但结果没有。
我似乎又打脸了 —— 啪啪的疼~
我整个人都不好了,日子本已如此艰难,真心快要过不下去了。
就在我一筹莫展时,我有事没事的 翻看 \x86ogg4oracletarget\ 的几个 6个字母的 文件夹。
意外发现了一个文件,我好奇的将文件打开。
我看到了 一个路径:没错啊!就是我配置的 数据文件夹 路径。
确实没错啊!难道非要验证一下 路径么?我虽然吃路径的亏 上了 5次当了。
—— 但这个路径确实没错啊,还不信,我复制给你看看。
整个人都不好了,文本配置最大的弊端就是:你敲几百几千个字母,一点手误都不能有(尼玛路径错了还没提示)
再次重建 javaue,插入数据 —— 我这次也没抱啥希望了,被折磨哭了都,不想被打脸了。
我看到了 一个 路径: \dirdat\r1000000 —— 感觉很奇怪:
源端 ext1 >>> 目标端 rep1,这种数据文件 都已经到了 r1000005 了 —— 为什么 javaue 还在找 r1000000
—— 要不,在源端 重建一个 ext2,使用 r2,让 javaue 从 r2 找数据?
打开源端 OGG 目录:新建 ext2
源端 添加、启动 ext2,启动成功(我都已经驾轻就熟了) :
打开目标端,重建 javaue,使用 r2:
这次,我们看到了一段之前没有出现过的代码:
打开 dirdat 目录:
再次执行 insert 脚本,插入数据的那一刻,javaue 崩溃了:
一脸懵逼,整个人都不好了。 就在我手足无措时,我意外看到 D:\ SampleHandler.log 文件被创建了 —— 虽然程序崩溃了,但是Java代码执行了。
FlagFile 意外配置成功了(本文不多说了,就截了个图)
心累了,又折腾了一天时间。直接说结果吧:
OGG Adapter Java,只和 /dirdat/r2000000 这种数据有关【和 版本无关、和 x86 x64 无关,和 源端、目标端 无关。】
整个OGG 所有涉及到的所有路径,都必须使用 反斜杠 /。【如果写成 D:\AAA\x86\ 程序可能会识别为乱码而诡异崩溃。】
尤其注意:添加 javaue 时,路径一定要用 反斜杠 /
其实直到现在,
OGG Adapter Java 11 x86 版本 每次都会崩溃。就是前两个步骤的BUG截图:数据变化捕获到了,Java代码执行了,但是程序崩溃了。
OGG Adapter Java 12 x64 版本,我把 /dirprm/javaue.prm 和 /dirprm/javaue.properties 和 /dirprm/source.def 三个文件 从 11 拷贝到 12, 执行 add extract javaue —— 程序却能正常运行,捕获到数据。
OGG 源端 必须和 Oracle 版本、32\64 位保持一致。
但是 OGG Adapter 没那么多限制。OGG Adapter 只针对 /dirdat/r1 或 /dirdat/r2 这类 Trail 文件。
最终方案如下:
仔细看 右下角: 我增加了一个 OGG 12 x64 —— 由这个 OGG 启动 javaue。
而 javaue 只只要对接 磁盘文件 dirdat\r2000001
虽然 有两个 OGG 11 x86,但是 好在 磁盘 Trail文件 是互通的。
放一个成功运行的截图:
我写了一个 C#程序,每隔一秒 就向 ADMIN.TB_TEST 写入一行记录。
理论上 ROOT.TB_TEST 会同步这些数据。
理论上 javaue 能捕获到 数据变化。
至此,第二目标完成。
最后放出 最终成功的配置:
或者参见 下一篇文章 《OGG Adapter Java一次性成功》
dirprm 文件夹:
javaue.prm 配置:
javaue.properties 配置:
Source.def 是生成的(生成方式看文章前面),手写无效:
custom.jar 是编译后 导出的 jar包:
//---------------------------------------------------------------------------------------
package sample;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
//这是 OGG 11 的 import
//import com.goldengate.atg.datasource.AbstractHandler;
//import com.goldengate.atg.datasource.DsConfiguration;
//import com.goldengate.atg.datasource.DsEvent;
//import com.goldengate.atg.datasource.GGDataSource.Status;
//import com.goldengate.atg.datasource.handler.*;
//import com.goldengate.atg.datasource.meta.DsMetaData;
//import com.goldengate.atg.datasource.test.DsTestUtils.Logger;
//import com.goldengate.atg.datasource.meta.*;
//import com.goldengate.atg.datasource.*;
//这是 OGG 12 的 import
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oracle.goldengate.datasource.AbstractHandler;
import oracle.goldengate.datasource.DsColumn;
import oracle.goldengate.datasource.DsConfiguration;
import oracle.goldengate.datasource.DsEvent;
import oracle.goldengate.datasource.DsOperation;
import oracle.goldengate.datasource.DsOperation.OpType;
import oracle.goldengate.datasource.DsTransaction;
import oracle.goldengate.datasource.GGTranID;
import oracle.goldengate.datasource.meta.ColumnMetaData;
import oracle.goldengate.datasource.meta.DsMetaData;
import oracle.goldengate.datasource.meta.TableMetaData;
import oracle.goldengate.datasource.meta.TableName;
import oracle.goldengate.datasource.GGDataSource.Status;
public class SampleHandler extends AbstractHandler {
//OGG 11 和 OGG 12 的 日志好像不一样, OGG 12 似乎要写 LoggerFactory.getLogger(SampleHandler.class);
//private final Logger logger = Log4jLogger.getLogger(SampleHandler.class);
private final Logger logger = LoggerFactory.getLogger(SampleHandler.class);
@Override
public void init(DsConfiguration conf, DsMetaData metaData) {
super.init(conf, metaData);
logger.info("init!");
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.init(*)");
}
@Override
public Status transactionCommit(DsEvent e, DsTransaction tx) {
// DsMetaData meta = e.getMetaData();
// //System.out.println(meta);
//
// Set<TableName> tableNames = meta.getTableNames();
// for(TableName tableName : tableNames){
// String tableStr = "";
// tableStr = tableStr + tableName+" : ";
//
// TableMetaData metaData = meta.getTableMetaData(tableName);
// ArrayList<ColumnMetaData> columns = metaData.getColumnMetaData();
// for(ColumnMetaData column : columns){
// tableStr = tableStr + "\r\n " + column.getColumnName() + " | "+column.getDataType().toString();
// }
//
// System.out.println(tableStr+"\r\n");
// //System.out.println(metaData);
// }
//
// //GGTranID tranId = e.getTargetCheckpointInfo();
// //System.out.println(tranId);
//
// System.out.println(tx.getSize());
// System.out.println(tx.getTotalOps());
// System.out.println(tx.getReadTime());
// System.out.println(tx.getTransactionBeginTime());
//
//
// Object eventSource = e.getEventSource();
// System.out.println(eventSource);
//
// DsOperation lastOp = tx.getLastOperation();
// System.out.println(lastOp);
//
//
// List<DsOperation> listOp = tx.getOperations();
// System.out.println(listOp.size());
// //System.out.println(listOp.get(0).getColumn(0).getBeforeValue());
// for(DsOperation op : listOp){
// System.out.println(op.getTableName());
// }
Status superResult = super.transactionCommit(e, tx);
logger.info("transactionCommit!");
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.transactionCommit(*) => "+superResult);
return superResult;
}
@Override
public Status operationAdded(DsEvent e, DsTransaction tx, DsOperation dsOperation) {
DsMetaData meta = e.getMetaData();
//System.out.println(meta);
// Set<TableName> tableNames = meta.getTableNames();
// for(TableName tableName : tableNames){
// String tableStr = "";
// tableStr = tableStr + tableName+" : ";
//
// TableMetaData metaData = meta.getTableMetaData(tableName);
// ArrayList<ColumnMetaData> columns = metaData.getColumnMetaData();
// for(ColumnMetaData column : columns){
// tableStr = tableStr + "\r\n " + column.getColumnName() + " | "+column.getDataType().toString();
// }
//
// System.out.println(tableStr+"\r\n");
// //System.out.println(metaData);
// }
System.out.println("---------------------------------------");
OpType opType = dsOperation.getOperationType();
System.out.println(opType);
TableName tableName = dsOperation.getTableName();
System.out.println(tableName.getFullName());
TableMetaData metaData = meta.getTableMetaData(tableName);
ArrayList<ColumnMetaData> columnMetas = metaData.getColumnMetaData();
List<DsColumn> columns = dsOperation.getColumns();
for(int i=0; i<columnMetas.size(); i++){
ColumnMetaData columnMeta = columnMetas.get(i);
DsColumn column = columns.get(i);
System.out.println(" " + columnMeta.getColumnName() + "\t | \t"+ column.getAfterValue() + (column.isChanged()? "\t >>> \t" +column.getAfterValue():""));
}
System.out.println("---------------------------------------");
Status superResult = super.operationAdded(e, tx, dsOperation);
logger.info("operationAdded!");
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.operationAdded(*) => "+superResult);
return superResult;
}
//@Override
//public DataSourceListener.State getState() {
// return super.getState();
//}
@Override
public void destroy() {
super.destroy();
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.destroy(*)");
logger.info("destroy!");
}
@Override
public String reportStatus() {
String superResult = "OK"; //super.reportStatus(); 调用父类函数,程序就会崩溃。
WriteStringToFile("D:\\SampleHandler.log", "SampleHandler.reportStatus(*) => "+superResult);
return superResult;
//return "status report...===";
}
public static void WriteStringToFile(String filePath, String text) {
try {
System.out.println("AAAAAAAAAAAAAAAAA :" + text);
FileWriter writer = new FileWriter(filePath, true);
writer.write("\r\n"+text);
writer.close();
} catch (IOException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
//---------------------------------------------------------------------------------------
至此,OGG 两大目标 全部完成。几乎把能踩的坑都踩了一遍。
虽然,我会再写一篇文章 《OGG Adapter Java一次性成功》
—— 但是,不要想太多:你还是得乖乖的回到 这篇文章,学习如何从坑中逃出来。
—— 此篇OGG踩坑文章,踩的各种坑 不下十几种,从配置到代码、从编码格式到反斜杠,从版本到位数,从环境变量到笔误。
—— 作者着实不容易。
最后还是以一首打油诗结尾:
现代诗:《OGG的迷惘》
那一刻,
晶莹的泪水滑过脸庞,
我似乎,
看到了成功的希望。
蹉跎与挣扎,
纠结与迷惘。
在折腾中,我终究能走向想要的远方。
—— 再给我一次机会,让我再配置一次OGG。
—— 不!我选择 死亡(骂娘)~
打油诗:《神坑OGG》
神坑最多OGG,
坑得劳资没脾气。
坑得劳资没脾气~
神坑最多OGG~
两首诗把 OGG 批了一顿,还能怎样呢?当然是选择 原谅他啊。
舒小龙 2018-05-31 20:02
『OGG 02』Win7 配置 Oracle GoldenGate Adapter Java 踩坑指南的更多相关文章
- 『OGG 03』Win7 配置 Oracle GoldenGate 一次性成功(包括Adapter Java)
安装Oracle: 安装 Oracle_11g 32位[Oracle 32位的话,OGG 也必须是 32位,否则会有0xc000007b无法正常启动 错误] 安装目录为 D:\oracle\produ ...
- 『OGG 01』Win7 配置 Oracle GoldenGate 踩坑指南
安装 Oracle 安装 Oracle11g 32位[Oracle 32位的话,OGG 也必须是 32位,否则会有0xc000007b无法正常启动 错误] 安装目录为 D:\oracle\produc ...
- 配置Oracle GoldenGate安全性
本章介绍如何配置Oracle GoldenGate安全性. 本章包括以下部分: Overview of Oracle GoldenGate Security Options Encrypting Da ...
- mybatis 使用oracle merge into 语句踩坑实录
由于需求涉及oracle的clob类型字段,在mybatis的mapper xml文件中编写merge into语句时总是失败. 附上错误代码 <insert id="mergeInt ...
- 阿里云轻量应用服务器——配置MySQL远程连接(踩坑,LAMP+CentOS)
说在前面 本文讲解清晰,从0开始 如不能用Navicat等数据库软件远程登陆,请先检查:安全>防火墙中 是否添加了MYSQL的3306端口(ECS服务器请检查 安全组)如未添加,先点右上角“添加 ...
- spring零配置AOP踩坑指南
今天照着书,试着配了AOP(全注解),结果踩了各种坑,后来参考书附带的源码,终于走出来了,现在总结一下 除了spring的jar包以外,还需要导入以下包: 1.Spring核心配置文件beans.xm ...
- oracle+mybatis批量插入踩坑记
最近在项目中需要使用oracle+mybatis批量插入数据,因为自增主键,遇到问题,现记录如下: 一.常用的两种sql写法报错 1.insert ... values ... <insert ...
- 『调错』OGG Error opening module ggjava_ue.dll
Win7 配置 OGG (GoldenGate) Adapter Java 时, 报错: ERROR OGG-01122 Oracle GoldenGate Capture, javaue.prm: ...
- Oracle GoldenGate实现数据库同步
前言:最近刚好在弄数据库同步,网上查了些资料再加上自己整理了一些,做个分享! 一.GoldenGate的安装 1.安装包准备 数据库版本:Oracle Database 11g Release 2(1 ...
随机推荐
- webpack4.x版本splitChunksPlugin的配置项详解与实际应用场景
在工程化地使用webpack时,公共代码抽离是不可或缺的,4.x版本之后,commonsChunkPlugin已经被去掉,splitChunksPlugins登上舞台,并且优化了很多配置选项,集体课件 ...
- .net Core 微服务框架 surging 使用
surging 是一个分布式微服务框架,提供高性能RPC远程服务调用,采用Zookeeper.Consul作为surging服务的注册中心, 集成了哈希,随机,轮询作为负载均衡的算法,RPC集成采用的 ...
- Python_网页爬虫
import sys import multiprocessing import re import os import urllib.request as lib def craw_links( u ...
- GPU渲染流水线的简单概括
GPU流水线 主要分为两个阶段:几何阶段和光栅化阶段 几何阶段 顶点着色器 --> 曲面细分着色器(可选)----->几何着色器(可选)----->裁剪-->屏幕 ...
- PAT1012:The Best Rank
1012. The Best Rank (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue To eval ...
- PAT1088:Rational Arithmetic
1088. Rational Arithmetic (20) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue F ...
- Spring BeanWrapper分析
最近在读DispatcherServlet 源代码,看到父级类org.springframework.web.servlet.HttpServletBean中关于BeanWrapper的一段代码, 继 ...
- mybatis一对一映射配置详解
听说mybatis一对一有三种写法,今天我试了一下. 数据库表准备 为了偷懒,我直接就拿用户权限菜单里的菜单表和菜单与权限的中间表做实现,他们原来是多对多的关系,这边我假设这两张表是一对一. 表 g ...
- Spring Batch 专题
如今微服务架构讨论的如火如荼.但在企业架构里除了大量的OLTP交易外,还存在海量的批处理交易.在诸如银行的金融机构中,每天有3-4万笔的批处理作业需要处理.针对OLTP,业界有大量的开源框架.优秀的架 ...
- canvas绘制形状
栅格 之前简单模板中有个宽/高150px的canvas元素.如下图所示,canvas元素默认被网格所覆盖.通常来说网格中的一个单元相当于canvas元素中的一像素.栅格的起点为左上角(坐标为(0,0) ...