项目背景:

项目需要对两个服务器上的表进行同步,表的结构可能不一样。比如服务器A上的表i同步数据到服务器B上的表j,i和j的结构可能不一样,当然大部分字段是一样的。项目看起来很简单,网上一搜也是很多,什么利用Oracle的同步工具,利用mybatis拦截器拦截sql语句等等。不好意思,由于种种原因,我们项目都没有办法使用。我们最后讨论的方案就是最传统的暴力解决,一条一条插入或者更新,当然这有一个很重要的前提,就是我们的数据不多,最多也就是几万条,如果数据量很多,比如上千万的这种级别,这种方案估计就有待商榷了。

项目技术:

在我不知道有mybatis generator的时候,我想着,一张表恨不得几百个字段,我一个一个敲要敲到什么时候,而且很容易出错!是的,我曾经经历过这样的痛楚,如果后面表的字段要改的话,我去,代码很多地方也要改,可谓牵一发而动全身,程序出错的机率非常大。而且,sql语句写起来也很麻烦,insert、update、select等等,太多字段,太多条件,简直哭死。后来,我接触到mybatis generator,我终于明白,什么才叫框架!这才是真正的框架,大大简化了开发!这是我目前遇到的最好的一个框架,没有之一!我用这个来开发,完全不需要关注sql语句怎么写,实体类怎么写,只需要关注业务逻辑,真正的解耦!太NB了!

好了,说到这里,我们项目没有用到什么高深的技术,就是mybatis和mybatis generator,语言是Java,所有的业务逻辑代码不到500行。

项目实施:

1.建立maven项目

利用eclipse建立一个maven工程,无需选择webapp类型,就简单的工程就行了,在pom.xml中添加要依赖的项目:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>cn.huifu</groupId>
<artifactId>dataSynTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>dataSynTest</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency> <dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency> <dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency> <!-- 日志框架 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.3</version>
</dependency> <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency> </dependencies> <build>
<finalName>dataSynTest</finalName>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
</plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>application.App</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin> </plugins>
</build> </project>

这里我贴一下所有的xml内容,这很重要,有很多博客,这部分不贴,都不知道他们项目用了哪些依赖,我们根本没有办法复现,即使知道用了哪些依赖,版本不一致的话,也会导致项目失败,因为可能不兼容。不难看到,我们用了mybatis generator和mybatis的相关依赖,同时还用了ojdbc6,因为我们用的是Oracle数据库,所以需要有这个依赖。然后还有一些日志的依赖。

2.mybatis generator的安装和使用

然后需要注意的是plugin中需要添加mybatis generator插件,并且定义配置文件的位置。要让mybatis generator可以在eclipse运行,还需要在eclipse的应用商店中安装mybatis generator插件,查询到了,直接install就行了。

ok,现在工具已经准备好了,下面我们先试一把,我们项目的目录如下,generatorConfig.xml在src/main/resources下面

具体内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 数据库驱动:选择你的本地硬盘上面的数据库驱动包-->
<!-- <classPathEntry location="E:\mysql\mysql-connector-java-5.1.46.jar"/> -->
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--数据库链接URL,用户名、密码 -->
<!-- 数据源配置 -->
<jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@192.168.4.228:1521:testdb" userId="gadbusr" password="gadbusr">
<!-- 数据目标配置 -->
<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@192.168.4.228:1521:testdb" userId="newhl" password="newhl"> --> </jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 生成模型的包名和位置-->
<javaModelGenerator targetPackage="mybatis.model" targetProject="dataSyn/src/main/java">
</javaModelGenerator>
<!-- 生成映射文件的包名和位置-->
<sqlMapGenerator targetPackage="mybatis.mapping" targetProject="dataSyn/src/main/java/">
</sqlMapGenerator>
<!-- 生成DAO的包名和位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="mybatis.mapper" targetProject="dataSyn/src/main/java">
</javaClientGenerator>
<!-- 要生成的表 tableName是数据库中的表名或视图名 domainObjectName是实体类名-->
<!-- <table tableName="people_for_test" domainObjectName="Person" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table> -->
<!-- <table tableName="mer_base_info" domainObjectName="MerBaseInfo"></table> -->
<!-- <table tableName="mer_base_info_syn" domainObjectName="MerBaseInfoSyn"></table> -->
<!-- <table tableName="field_syn_info" domainObjectName="FieldSynInfo"></table> -->
<!-- <table tableName="table_syn_info" domainObjectName="TableSynInfo"></table> -->
<table tableName="usr_info" domainObjectName="UsrInfo" schema="GADBUSR"></table>
<!-- <table tableName="usr_info_syn" domainObjectName="UsrInfoSyn"></table> -->
<!-- <table tableName="mer_trans_info" domainObjectName="MerTransInfo" schema="GADBUSR"></table> -->
<!-- <table tableName="mer_trans_info_syn" domainObjectName="MerTransInfoSyn" schema="NEWHL"></table> --> </context> </generatorConfiguration>

主要注意数据库驱动,因为我们已经依赖了ojdbc6,所以这里不用这个,而且这种写死地址的方法也不好,不方便代码迁移。然后就是数据源的配置,你的驱动类型,我们用的是Oracle,所以是Oracle的驱动类型,然后是url,用户名和密码。这些配置好后,就是最重要的3个配置,分别是model、mapping和dao的配置,主要就是注意路径和包名,要与你自己的项目一致,其他默认的就行了,最后,就是表名的配置,根据你数据库的表名配置一下,以及生成实体类的名字。这里特别要注意一下就是schema这个字段,最好加上,我之前做的时候,没有加,导致有些数据库的用户,他会遍历所有的相同表名出来,导致代码重复。比如我代码中gadbusr,如果不加上schema这个字段,它会找出什么newhl.usr_info,gadbusrtmp.usr_info...而如果用newhl,则只会找出newhl.usr_info,估计是newhl和gadbusr在数据库的权限不一致导致的,所以最好加上,以防万一。

这些,确认无误之后,右键->run as->run mybatis generator,就会在相应的目录下生成文件,sql、dao、mapper什么的都生成好,是不是特别神奇!最绝的是,sql这种形式是万能的,你无论增删查改都可以用example的模板,简直太NB!你不需要写一行sql语句,就能实现几乎所有的增删查改,而且基本不会出错,我的天,每次想到这个,就感觉写这个框架的人很了不起,看起来没有什么高大上的东西,但是确实大大方便了开发人员!

3.实现业务逻辑代码

有了上面的这些之后,就可以开始我们的业务逻辑代码编写了,用到的是mybatis,我们没有集成spring,纯Java写的,因为项目很小。

业务逻辑:建立两张表,一张用了存储需要同步的表,一张用来存储字段(根据这个字段可以来找到哪条记录需要同步,其实就是唯一索引字段)。然后到数据库A中去查询出所有的记录,再将这些记录逐一到数据库B中去查找,如果没有找到,则插入,如果找到了,则比较这两条记录,如果记录相等,则不做处理,如果记录不相等,则进行更新。其中判断两条记录用到了反射机制,用反射机制来找到属性值,然后比较,再调用set函数。这里的记录是经过mybatis把数据库的一条记录转换成Java对象,保存在内存中,所以可以相互比较。具体代码,我已经把代码部署到github上了,在文末可以找到。

这里还要注意一下,因为是两个数据库,所以要配置两个数据源,并且需要在代码中创建两个sqlsession。mybatis配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="dataSourceA"> <environment id="dataSourceA">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="JDBC" />
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@192.168.4.228:1521:testdb" />
<property name="username" value="gadbusr" />
<property name="password" value="gadbusr" />
</dataSource>
</environment> <environment id="dataSourceB">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="JDBC" />
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@192.168.4.228:1521:testdb" />
<property name="username" value="newhl" />
<property name="password" value="newhl" />
</dataSource>
</environment> </environments> <mappers>
<mapper resource="mybatis/mapping/FieldSynInfoMapper.xml" />
<mapper resource="mybatis/mapping/MerBaseInfoMapper.xml" />
<mapper resource="mybatis/mapping/MerBaseInfoSynMapper.xml" />
<mapper resource="mybatis/mapping/MerTransInfoMapper.xml" />
<mapper resource="mybatis/mapping/MerTransInfoSynMapper.xml" />
<mapper resource="mybatis/mapping/TableSynInfoMapper.xml" />
<mapper resource="mybatis/mapping/UsrInfoMapper.xml" />
<mapper resource="mybatis/mapping/UsrInfoSynMapper.xml" />
</mappers> </configuration>

可以看到有两个environment,分别代表不同的数据源,“POOLED”表示使用数据库连接池。mappers中添加所有要用到的mapper文件。

项目运行:

可以直接run as。当然,作为同步工具,需要触发程序,所以我们想的是用maven来打包成jar包,然后部署到Linux服务器上,用crontab来设置定时任务。用maven来打包可以我之前写的博客,在pom.xml中也会用这个打包插件,然后执行mvn install,最终会生成一个独立的可以运行的jar包,不需要其他的jar包,因为所有的都在一个独立的jar包中

后续改进:

1.一次读取数据到list中,会不会导致内存爆掉?

2.效率不知道高不高,考虑用多线程来处理。

相关链接:

1.mybatis的官方指导文章,中文的,写得很清楚,很详细,包括多数据源配置,怎么使用mybatis来操作等等,强烈推荐:http://www.mybatis.org/mybatis-3/zh/configuration.html

2.一个java实现的mybatis批量插入数据库,但是没有使用mybatis generator,可以看看:https://www.jb51.net/article/86914.htm

3.Oracle高效数据库同步:http://blog.sina.com.cn/s/blog_828f81c80102wpwo.html

4.阿里巴巴开源的数据库同步方案-otter,人家这个当然是支持大数据量,并且时效性也很高,如果是牛人,当然是参考这个:https://github.com/alibaba/otter 但是它也是有使用场景的,它好像是根据日志来找到sql语句,这个场景估计不是什么项目都能用。哎,虽然很高大上,但是适合自己的才是最好的。

5.本项目的代码地址:https://github.com/fxlnjfu/MybatisSyn

利用mybatis generator实现数据库之间的表同步的更多相关文章

  1. 利用navcat为mysql数据库单独的表赋权限及表结构同步

    为mysql数据库单独的表赋权限 场景:考勤系统需要拿OA数据库td_oa中的flow_run和flow_run_data表中的数据做考勤计算 考勤系统只需要读取这两张表的数据,所以只需要开通一个单独 ...

  2. SQL Server 跨服务器 不同数据库之间复制表的数据

    不同数据库之间复制表的数据的方法: 当表目标表存在时: insert into 目的数据库..表 select * from 源数据库..表 当目标表不存在时: select * into 目的数据库 ...

  3. Mybatis分页-利用Mybatis Generator插件生成基于数据库方言的分页语句,统计记录总数 (转)

    众所周知,Mybatis本身没有提供基于数据库方言的分页功能,而是基于JDBC的游标分页,很容易出现性能问题.网上有很多分页的解决方案,不外乎是基于Mybatis本机的插件机制,通过拦截Sql做分页. ...

  4. MyBatis实现Mysql数据库分库分表操作和总结

    前言 作为一个数据库,作为数据库中的一张表,随着用户的增多随着时间的推移,总有一天,数据量会大到一个难以处理的地步.这时仅仅一张表的数据就已经超过了千万,无论是查询还是修改,对于它的操作都会很耗时,这 ...

  5. Mybatis Generator生成数据库自带的中文注释

    1.相关jar包 <!-- mybatis生成 jar包 --> <dependency> <groupId>org.mybatis.generator</g ...

  6. MyBatis Generator 生成数据库自带中文注释

    1. maven依赖 <!-- mybatis生成 jar包 --> <dependency> <groupId>org.mybatis.generator< ...

  7. 利用SQL语句查询数据库中所有表

    Oracle: SELECT * FROM ALL_TABLES;系统里有权限的表 SELECT * FROM DBA_TABLES; 系统表 SELECT * FROM USER_TABLES; 当 ...

  8. 【转】sqlserver数据库之间的表的复制

    以下以数据库t1和test为例.  1.复制表结构及资料 select * into 数据库名.dbo.表名 from 源表(全部数据)     如:select * into t1.dbo.YS1 ...

  9. SQLite与MySQL、SQLServer等异构数据库之间的数据同步

    SQLite DBSync是开源嵌入式数据库SQLite的数据同步引擎,实现了SQLite与SQLite数据库之间以及SQLite与异构数据库(Oracle.MySQL.SQLServer)之间的增量 ...

随机推荐

  1. tongweb安装后无法启动问题

    安装后执行bin下的启动文件,但是管理界面依然无法打开 1.确认破解的dat文件是否在跟目录下,是否过期 2.如果jdk64位试试下面的方法:JAVA_HOME/jre/lib/ext/下缺少包 su ...

  2. 前端 js加密 后台java 解密 RSA

    前端代码 : $.ajax({ type:"GET", url:"http://localhost:8084/getPulbicKey", dataType:& ...

  3. python2 with open(path,"",) as f:

    python2 with open 没有 encoding 这个参数 会报错, 可以 import io with io.open(path,"") as f: 这样就ok 或者是 ...

  4. API手册(2017)

    本页存放内容: API手册. 少量教程 [旧的工具页面] (是一些旧的页面.不放在外面了,这里做个索引) http://www.cnblogs.com/qq21270/p/3538677.html 常 ...

  5. 如何使用cloudflare的CDN加速网站隐藏网站IP

    原文:http://www.safecdn.cn/contact-zh/2018/12/cloudflare-cdn/1146.html 高防CDN:https://www.safeidc.cn/cd ...

  6. 关于JWTtoken的管理问题

    JWT简介: Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准.因为网络上有很多关于jwt的详细介绍了,所以我这里就不再赘述.但是JWT的大 ...

  7. Linux修改用户所在组方法

    Linux修改用户所在组方法 usermod -g 用户组 用户名强行设置某个用户所在组usermod -G 用户组 用户名把某个用户改为 group(s) usermod -a -G 用户组 用户名 ...

  8. Ubuntu16.04 导入tensorflow报错

    错误1:Traceback (most recent call last):  File "/home/lwc/anaconda3/lib/python3.6/site-packages/t ...

  9. R数据导入导出(一): read.table()和read.csv()的区别

    之前也参考过一些资料,虽然是这么简单的两个buildin,还是仔细对比了一下,我有两张txt,都是从cube中导出的,就意味着每一列的列数是不一样的.R语言官方文档中有这样一句话不知道大家注意到了没有 ...

  10. tabpanel如何隐藏页签表头以及基本用法总结

    tabpanel是extjs中一种比较常用的布局容器控件,也比较简单. ///1:相关的插件, var tabScrollerMenu = Ext.create("Ext.ux.TabScr ...