Salesforce と kintone 間で顧客データを同期する方法 (Java - JPA使用)

Salesforce と kintone 間で顧客データを同期する方法として、**CDataドライバー**を利用したWindowsフォームアプリケーション(C#/ADO.NET)によるものを別記事でご紹介しました。
CDataドライバーは、ADO.NETのみならず様々なテクノロジーで提供されています。今回は、Salesforce と kintone 間で顧客データを同期するアプリケーションの Java による例をご紹介したいと思います。

![000_構成図.png](https://qiita-image-store.s3.amazonaws.com/0/141193/946bec73-b425-5f62-56a5-bd05c004d6c7.png)

このアプリケーションの概要は次の通りです。

* JDK 8
* JPA を利用
* JavaFX によるデスクトップアプリケーション
* Eclipse 4.6
* Salesforce および kintone からデータを読み込む
* インポートしたい行を選択して、Salesforce から kintone へデータをインポートする

それでは、このサンプルアプリケーションの開発手順を見ていきます。

## ソースコード
このサンプルアプリケーションのソースコードは[こちら](https://github.com/kasoorimethi/Salesforce2KintoneJPA)からどうぞ。

## CData製品のインストール
CData Software をインストールします。今回必要になるのは、次の2つです。

* [Salesforce JDBC Driver](https://www.cdata.com/jp/drivers/salesforce/jdbc/ "Salesforce JDBC Driver")
* [kintone JDBC Driver](https://www.cdata.com/jp/drivers/kintone/jdbc/ "kintone JDBC Driver")

## 開発環境の準備
Eclipse 4.6 を使用します。以下の機能を Eclipse にインストールしておいてください。

* Dali Java 永続化ツール - EclipseLink JPA サポート 
* Dali Java 永続化ツール - JPA サポート
* e(fx)clipse - IDE

※ サイトのロケーションは http://download.eclipse.org/releases/neon 、http://download.eclipse.org/efxclipse/updates-released/2.4.0/site を使用

また、[ウィンドウ]->[設定]->[Java永続化]->[JPA]->[エラー/警告] で、[プロジェクト]のセクションを開き、「複数の永続化単位」の設定を「無視」にしておきます。

## プロジェクト作成
新規プロジェクトを作成します。JavaFX Project を選択します。

![001_プロジェクト作成.png](https://qiita-image-store.s3.amazonaws.com/0/141193/a0e68e96-f9fc-2ec8-092f-de3947aa7c9f.png)

デフォルトのまま順次「次へ」ボタンをクリックしていきます。最後のダイアログでは、「言語」に"FXML"、「Root-Type」に"javax.scene.layout.AnchorPane"、「ファイル名」に"MainPane"、「Controller Name」に"MainPaneController"を指定し、完了ボタンをクリックします。

![002_プロジェクト作成2.png](https://qiita-image-store.s3.amazonaws.com/0/141193/44f51596-8988-0bfc-6de1-2318614e14d7.png)

プロジェクトが作成されたら、プロジェクトを右クリックしてプロパティを開き、プロジェクト・ファセットで「ファセット・フォームへ変換...」をクリックします。プロジェクト・ファセット「JPA」にチェックを入れ、「より詳しい構成が必要...」リンクから次画面へ進みます。

![003_1_プロジェクト作成3_ファセット_より詳しい構成が必要.png](https://qiita-image-store.s3.amazonaws.com/0/141193/edf5fcf9-2400-eb18-037a-44d6c1d08618.png)

JPAファセットのウィザードで、「ライブラリーをダウンロード...」ボタンをクリックします。

![003_2_プロジェクト作成3_ファセット_JPAファセット.png](https://qiita-image-store.s3.amazonaws.com/0/141193/c2854792-49ea-a24a-28de-3887e75120f4.png)

EclipseLink 2.5.2 を選択し、次へボタンをクリックします。

![003_3_プロジェクト作成3_ファセット_ライブラリのダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/141193/00c30f77-5f74-c806-5ac5-0c871ccd3ae6.png)

ライセンス受諾チェックを入れ、完了ボタンをクリックします。
これによりワークスペースに EclipseLink がダウンロードされ、ユーザライブラリとして使用可能になります。

![003_4_プロジェクト作成3_ファセット_ライブラリのダウンロード_ライセンス.png](https://qiita-image-store.s3.amazonaws.com/0/141193/860e52d7-27b0-983d-e251-6245424122b8.png)

[JPA実装]のユーザー・ライブラリで「EclipseLink 2.5.2」を選択し、「注釈付きクラスを自動的に発見」を選択してOKボタンをクリックします。

![003_5_プロジェクト作成3_ファセット_ライブラリのダウンロード後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/43315e89-ff0e-fe47-d5df-d4706a966e88.png)

## JDBCドライバーの登録
Eclipse に JDBC ドライバーを登録します。
[ウィンドウ]->[設定]->[データ管理]->[接続]->[ドライバー定義] を開き、追加ボタンをクリックします。
汎用JDBCドライバーを選択し、ドライバー名を "CData Salesforce Driver" とします。

![008_2_テーブルからエンティティ_SF_接続プロファイル_ドライバー.png](https://qiita-image-store.s3.amazonaws.com/0/141193/81eb0220-812f-626f-5868-38852cb9a9c5.png)

「Jarリスト」タブを開きjarファイルを指定します。CData製品のインストール先/lib/cdata.jdbc.salesforce.jar を選択します。
次に「プロパティー」タブを開き、ドライバークラスと接続URLを設定します。それぞれ以下の値を設定します。

* ドライバークラス：cdata.jdbc.salesforce.SalesforceDriver
* 接続 URL：jdbc:salesforce:User=<ユーザ名>;Password=<パスワード>;Security Token=<セキュリティトークン>;

![011_テーブルからエンティティ_SF_接続プロファイル_ドライバー4.png](https://qiita-image-store.s3.amazonaws.com/0/141193/0c9ecca0-f09b-5ae4-9820-174e824a28a1.png)

kintone のドライバーも同様に登録します。ドライバークラス名や接続URLについては、[マニュアル](http://cdn.cdata.com/help/LKB/jdbc/pg_connectionj.htm "マニュアル")に記載があります。

## データベース接続の作成
先程登録したJDBCドライバーを使用して、データベース接続を作成します。
ビュー「データ・ソース・エクスプローラ」を開きます。データベース接続を右クリックして「新規」を選択します。
接続プロファイル・タイプで汎用JDBCを選択し、名前を "CData Salesforce JDBC" とし、次へボタンをクリックします。

![007_2_テーブルからエンティティ_SF_接続プロファイル.png](https://qiita-image-store.s3.amazonaws.com/0/141193/1f71bb83-f168-ec46-6460-e9479a71499b.png)

ドライバーは、先程作成した CData Salesforce Driver を選択します。
「データベース」はここではSAMPLEのままでかまいません。「ユーザー名」にユーザ名を入力し、完了ボタンをクリックします。

![012_テーブルからエンティティ_SF_接続プロファイル_ドライバー5.png](https://qiita-image-store.s3.amazonaws.com/0/141193/be445c1e-2527-4a82-996f-17207dbe8583.png)

同様にして、kintone のデータベース接続も作成します。

## エンティティクラスの生成
Eclipse の機能を使用して、テーブル情報からエンティティクラスを生成します。

プロジェクトのプロパティからJPAを開き、接続に "CData Salesforce JDBC" を指定し、「接続からデフォルト・カタログを上書き」「接続からデフォルト・スキーマをオーバーライド」にチェックを入れ、それぞれ "CData" と "Salesforce" を選択してOKボタンをクリックします。

![013_テーブルからエンティティ_SF_プロジェクトのプロパティ_デフォルトカタログ.png](https://qiita-image-store.s3.amazonaws.com/0/141193/da42fcaa-e369-4e84-f041-dbc8715d1fa4.png)

次にプロジェクトを右クリック->新規で「その他」を選択し、[JPA]->[テーブルから JPAエンティティー]を選択して次へボタンをクリックします。
カスタム・エンティティーの生成ダイアログが開くので、「接続」で "CData Salesforce JDBC"、「スキーマ」で "Salesforce" を選択します。すると「テーブル」に Salesforce のオブジェクトの一覧が表示されます。
「Account」にチェックを入れ、次へボタンをクリックします。
※なおここで時間がかかる場合がありますが、そのまま待っていれば次の画面が表示されます。

![015_テーブルからエンティティ_SF_エンティティ作成_テーブル選択.png](https://qiita-image-store.s3.amazonaws.com/0/141193/53541a7d-9755-56f9-a0d4-b20f645051e1.png)

「クラス名」が "Account" になっていることを確認し、完了ボタンをクリックします。

![015_テーブルからエンティティ_SF_エンティティ作成_作成.png](https://qiita-image-store.s3.amazonaws.com/0/141193/ddbb0c0f-18c5-e9da-b646-760f965c5b66.png)

これで Account(取引先) のエンティティクラスが生成されました。
同様にして Contact(取引先責任者) のエンティティクラスも生成します。

kintone の顧客リストのエンティティクラスも同様に作成しますが、プロジェクトのJPAプロパティで接続、カタログ、スキーマを kintone のものに設定してから実施します。

![013_2_テーブルからエンティティ_KT_プロジェクトのプロパティ_デフォルトカタログ.png](https://qiita-image-store.s3.amazonaws.com/0/141193/a779589b-bceb-ead0-303e-59153f7c5d62.png)

kintone の顧客リストのエンティティクラスも同様に作成します。

![016_テーブルからエンティティ_KT_エンティティ作成.png](https://qiita-image-store.s3.amazonaws.com/0/141193/7a3bc7c4-ebfe-1e66-55c7-dd5b599b141a.png)

ここまでで3つのエンティティクラスが作成されました。
プロジェクトのJPAプロパティの接続等を未設定に戻しておきます。

![017_テーブルからエンティティ_プロジェクトのプロパティ_戻し.png](https://qiita-image-store.s3.amazonaws.com/0/141193/0d8a3e8a-ed89-2a41-c022-388b814a3369.png)

## 永続化ユニットの定義
META-INF/persistence.xml を開き、以下を記述します。&lt;Salesforceの接続URL> と &lt;kintoneの接続URL> にはそれぞれJDBCドライバーの登録時に設定したものを指定します。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
	<persistence-unit name="salesforce" transaction-type="RESOURCE_LOCAL">
		<class>application.Contact</class>
		<class>application.Account</class>
		<properties>
			<property name="eclipselink.jdbc.native-sql" value="true"/>
			<property name="javax.persistence.jdbc.url" value="<Salesforceの接続URL>"/>
			<property name="javax.persistence.jdbc.driver" value="cdata.jdbc.salesforce.SalesforceDriver"/>
		</properties>
	</persistence-unit>
	<persistence-unit name="kintone" transaction-type="RESOURCE_LOCAL">
		<class>application.顧客リスト</class>
		<properties>
			<property name="eclipselink.jdbc.native-sql" value="true"/>
			<property name="javax.persistence.jdbc.url" value="<kintoneの接続URL>"/>
			<property name="javax.persistence.jdbc.driver" value="cdata.jdbc.kintone.KintoneDriver"/>
		</properties>
	</persistence-unit>
</persistence>
```

## 処理の実装
### エンティティクラス Contact の修正
Salesforce 側はContact(取引先責任者)が処理対象のオブジェクトですが、一部の項目はAccount(取引先)から取得したいものがあるため、Contactクラスを修正してAccountの項目を取得できるようにします。以下のコードを追加します。

```java
	@JoinColumn(name="AccountId", referencedColumnName="Id")
	private Account account;
	
	public String getAccountName() {
		
		if (account == null) {
			return "";
		}
		
		return account.getName();
	}
	
	public String getAccountAddress() {
		
		if (account == null) {
			return "";
		}
		
		return account.getBillingState() + account.getBillingCity() + account.getBillingStreet();
	}
	
	public String getAccountBillingPostalCode() {
		
		if (account == null) {
			return "";
		}
		
		return account.getBillingPostalCode();
	}
```

### MainPaneControllerの初期化
application/MainPaneController.java を開きます。空の実装になっているので、以下の初期化コードを入れます。
※適宜import文を追加します。

```java
public class MainPaneController implements Initializable {

	@FXML
	private TableView<Contact> salesforceTable;
	
	@FXML
	private TableView<顧客リスト> kintoneTable;
	
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		
		salesforceTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
		kintoneTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
		
		EntityManager em = Persistence.createEntityManagerFactory("kintone").createEntityManager();
		kintoneTable.getItems().addAll(em.createNamedQuery("顧客リスト.findAll", 顧客リスト.class).getResultList());

		em = Persistence.createEntityManagerFactory("salesforce").createEntityManager();
		salesforceTable.getItems().addAll(em.createNamedQuery("Contact.findAll", Contact.class).getResultList());
	}
}
```

### インポート処理
インポート処理を実装します。以下のメソッドを追加します。

```java
	@FXML
	private void importOnClick(MouseEvent event) {
		
		EntityManager em = Persistence.createEntityManagerFactory("kintone").createEntityManager();
		
		em.getTransaction().begin();
		
		for (Contact contact : salesforceTable.getSelectionModel().getSelectedItems()) {
			
			顧客リスト kintoneItem = kintoneTable.getItems().stream()
				.filter(o -> o.getメールアドレス().equals(contact.getEmail()))
				.findFirst()
				.orElse(new 顧客リスト());
			
			kintoneItem.setメールアドレス(contact.getEmail());
			kintoneItem.set会社名(contact.getAccountName());
			kintoneItem.set住所(contact.getAccountAddress());
			kintoneItem.set担当者名(contact.getName());
			kintoneItem.set部署名(Optional.ofNullable(contact.getDepartment()).orElse(""));
			kintoneItem.set郵便番号_数字のみ_(Optional.ofNullable(contact.getAccountBillingPostalCode()).orElse(""));
			kintoneItem.setTel_数字のみ_(contact.getPhone());
			kintoneItem.setFax_数字のみ_(contact.getFax());
			kintoneItem.set備考("");
			kintoneItem.set経度("");
			kintoneItem.set緯度("");
			
			if (kintoneItem.getRecordId() == 0) {
				em.persist(kintoneItem);
			}
		}
		
		em.getTransaction().commit();
		
		kintoneTable.getItems().clear();
		kintoneTable.getItems().addAll(em.createNamedQuery("顧客リスト.findAll", 顧客リスト.class).getResultList());
	}
```

c#版と同じく、メールアドレスをキーとして、Salesforce側のデータをkintone側に設定しています。
CDataドライバーを使用することにより、Salesforceおよびkintoneのデータをデータベーステーブルとして扱えるようになるため、このようにJPAを通した管理を行うこともできます。


### 画面の作成
最後に画面を作成します。application/MainPane.fxmlを開き、以下のコードを実装します。

```xml
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="660.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/8.0.102" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainPaneController">
   <children>
      <TableView fx:id="salesforceTable" layoutX="14.0" layoutY="53.0" prefHeight="600.0" prefWidth="485.0">
        <columns>
          <TableColumn prefWidth="150.0" text="担当者名">
            <cellValueFactory><PropertyValueFactory property="name" /></cellValueFactory>
          </TableColumn>
          <TableColumn prefWidth="150.0" text="メールアドレス">
            <cellValueFactory><PropertyValueFactory property="email" /></cellValueFactory>
          </TableColumn>
          <TableColumn prefWidth="150.0" resizable="false" sortable="false" text="会社名">
          	<cellValueFactory><PropertyValueFactory property="accountName" /></cellValueFactory>
          </TableColumn>
          <TableColumn prefWidth="150.0" text="部署名">
            <cellValueFactory><PropertyValueFactory property="department" /></cellValueFactory>
          </TableColumn>
          <TableColumn prefWidth="150.0" text="住所">
            <cellValueFactory><PropertyValueFactory property="accountAddress" /></cellValueFactory>
          </TableColumn>
        </columns>
      </TableView>
      <Button layoutX="542.0" layoutY="263.0" mnemonicParsing="false" onMouseClicked="#importOnClick" prefHeight="35.0" prefWidth="116.0" text="インポート" />
      <TableView fx:id="kintoneTable" layoutX="696.0" layoutY="53.0" prefHeight="600.0" prefWidth="485.0">
        <columns>
          <TableColumn editable="false" prefWidth="75.0" sortable="false" text="レコード番号">
            <cellValueFactory><PropertyValueFactory property="レコード番号" /></cellValueFactory>
          </TableColumn>
          <TableColumn editable="false" prefWidth="150.0" sortable="false" text="担当者名">
            <cellValueFactory><PropertyValueFactory property="担当者名" /></cellValueFactory>
          </TableColumn>
          <TableColumn editable="false" prefWidth="150.0" sortable="false" text="メールアドレス">
            <cellValueFactory><PropertyValueFactory property="メールアドレス" /></cellValueFactory>
          </TableColumn>
          <TableColumn editable="false" prefWidth="150.0" sortable="false" text="会社名">
            <cellValueFactory><PropertyValueFactory property="会社名" /></cellValueFactory>
          </TableColumn>
          <TableColumn editable="false" prefWidth="100.0" sortable="false" text="部署名">
            <cellValueFactory><PropertyValueFactory property="部署名" /></cellValueFactory>
          </TableColumn>
          <TableColumn editable="false" prefWidth="150.0" sortable="false" text="住所">
            <cellValueFactory><PropertyValueFactory property="住所" /></cellValueFactory>
          </TableColumn>
        </columns>
      </TableView>
   </children>
</AnchorPane>
```

ウィンドウのサイズを設定します。application/Main.java を開き、Sceneのサイズを変更します。

```java
Scene scene = new Scene(root, 1200, 660);
```

実装は以上です。

## 実行
実行構成を作成します。[実行]->[実行構成]を開き、「Javaアプリケーション」を右クリックして「新規」を選択します。
クラスパスタブを開き、CDataのJDBCドライバーを2つ指定し、実行ボタンをクリックします。

![018_実行構成.png](https://qiita-image-store.s3.amazonaws.com/0/141193/59ebffed-caf6-e5c0-b8ce-5db80fa6ca3d.png)

実行すると、テーブルビューにSalesforceとkintoneのデータが表示されたウィンドウが立ち上がります。

![019_実行直後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/191ca2c1-a4ee-82ba-9e81-81dee464535b.png)

データ移行したい行を選択して「インポート」ボタンをクリックすると、選択したデータが kintone に登録されます。

![020_移行後.png](https://qiita-image-store.s3.amazonaws.com/0/141193/116c0bc6-5329-7be3-6ac7-59e8007f14de.png)

![021_移行後kintone.png](https://qiita-image-store.s3.amazonaws.com/0/141193/4081afb2-ad53-8945-82cc-79464e1abef7.png)