親子関係のテーブル構造ごと、別のデータベースに転送する X-R マッピング

データベース間のデータ転送をするとき、親子関係のテーブル構造を、そのまま転送する方法。

メッセージングで、データベース間を連携するとき、親子関係のテーブル(たとえば、受注ヘッダと受注明細)を、そのままの構造で転送することがなかなかできずに困っていた。

親レコードと子レコードをばらばらに送ると、非同期なので、受け取った側のデータベースに書き込む時、参照整合性とかやっかいな問題がおきる。

結合して、一つのフラットファイルのイメージで、メッセージとして送る方法もある。これは、受け取った側で、親子関係の分解が面倒。

イデアとしては、親子関係の構造を、そのままXML ドキュメントにマッピングして、転送すれば良いとは思っていたが、記述が複雑になりそうで、避けていた。

今回、どうしても、そういう機能がほしくなり、Mule ESB + groovy スクリプトで、書いてみたら、意外と簡単に実現できた。

Mule ESB のサービス構成

<model name="db-xml-db Sample">

<service name="db to xml">
	<inbound>
		<jdbc:inbound-endpoint pollingFrequency="${jdbc.pollingFrequency}"
			queryKey="requestPolling"/>
	</inbound>

	<script:component>
		<script:script
			name="select and build xml"
			engine="groovy"
			file="script/db-xml.groovy"
		/>
	</script:component>

	<outbound>
		<pass-through-router>
			<vm:outbound-endpoint address="vm://save"/>
		</pass-through-router>
	</outbound>
</service>

<service name="xml to databese">

	<inbound>
		<vm:inbound-endpoint address="vm://save"/>
	</inbound>
	
	<script:component>
		<script:script
			name="parse xml and insert"
			engine="groovy"
			file="script/xml-db.groovy"
		/>
	</script:component>

</service>

</model>

db to xml の groovy スクリプト(抜粋)

//payload から 転送すべき id を取得

id = payload.id

def writer = new StringWriter() 
def xmlDocument = new groovy.xml.MarkupBuilder( writer )

// 親・子・孫のSELECTをしながら XML 生成
//
// xmlDocument.要素名( SELECT結果 ) の形式で、
// その要素名で、xml 要素を生成
// SELECT結果を、その要素の属性に、カラム名="値", ...  形式で記述
//

// 親レコード(XMLルート要素)を一件だけ取得する
// 複数行文字列は、トリプルクォートで記述

def oyaRecord = sql.firstRow( "親レコードSELECT文" )

xmlDocument.oya( oyaRecord ){ // oya要素 生成

  def listOfKo = sql.rows( "子レコードSELECT文" ) ;

	listOfKo.each{ koRecord -> 
		xmlDocument.ko( koRecord ) { // ko要素 生成

			def listOfMago = sql.rows( "孫レコード取得SELECT文" ) ;
			listOfMago.each{ magoRecord -> 
					xmlDocument.mago( magoRecord ) } // mago要素生成
		}
	}
}

xmlMessage = writer.toString() // xml をStringオブジェクトに変換
log.info( "XML送信:$xmlMessage" ) 

return xmlMessage // outbound に xmlMessage を渡す

生成した XML

<oya id='1' name='oya1'>
  <ko id='1' oya_id='1' name='ko1'>
    <mago id='1' ko_id='1' name='mago1' />
    <mago id='2' ko_id='1' name='mago2' />
  </ko>
  <ko id='2' oya_id='1' name='ko2'>
    <mago id='3' ko_id='2' name='mago3' />
    <mago id='4' ko_id='2' name='mago4' />
  </ko>
</oya>

xml to db の groovy スクリプト ( 抜粋 )

// payload から xml 要素を取り出しながら、データベースに出力
// groovy XmlParse 
// xml 要素名を、そのままオブジェクト名として参照できる

def oya = new XmlParser().parseText(payload);

insertOya( oya.attributes() )
oya.each { ko ->
    insertKo( ko.attributes() )
    ko.each { mago ->
       insertMago( mago.attributes() )
   }
}

def insertOya(map){ /* insert 文の実行 */ }
def insertKo(map){ /* insert 文の実行 */ }
def insertMago(map){ /* insert 文の実行 */ }

goovy の xml.MarkupBuilder、 util.XMLParser クラスを使うと、こんな感じで、書けちゃう。

親子関係だけではなく、2階層目に異なる要素(つまり、兄弟関係の要素)の構造でも、同じ感じで、書ける。

X-R マッピングが、こんなに簡単にかければ、いろいろ使えそうです。

参考URL:
実用的な Groovy: XML を作成し、構文解析し、容易に扱う
http://www.liquibase.org/ja/generate-changelog-with-groovy