項目背景:
項目需要對兩個服務器上的表進行同步,表的結構可能不一樣。比如服務器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