Más contenido relacionado
La actualidad más candente (19)
Similar a WEB開発を加速させる。アジャイル開発に最適なデータ構造とORマッパの形 (20)
Más de Yusaku Watanabe (11)
WEB開発を加速させる。アジャイル開発に最適なデータ構造とORマッパの形
- 3. UserData.java
@DataBaseId(id = 1)
@Persistable(name = "user_data")
public class UserData {
@PrimaryKey
private String userName;
@PrimaryKey
private String age;
@Indexed
private Date date;
private String address;
private Family family;
@CompositeIndexed(name={"userName", "age", "address"})
public void composite_index0() {}
public static class Family {
private List<String> familyNames;
public List<String> getFamilyNames() {
return familyNames;
}
public void setFamilyNames(List<String> familyNames) {
this.familyNames = familyNames;
}
}
public Family getFamily() {
return family;
}
public void setFamily(Family family) {
this.family = family;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
- 5. }
}
上記のPOJOをjson_persister.save()すると以下のJavaオブジェクトが下記のようなjsonに変換される
{
"address": "setagaya",
"age": "33",
"date": 1341914556082,
"family": {
"familyNames": [
"masami",
"akari",
"sakutarou"
]
},
"userName": "yuhsaku"
}
主要なインターフェース
JsonPersister.save
javaオブジェクトを保存します
Sample.java
UserData userDataQuery = new UserData();
userDataQuery.setUserName("yuhsaku"); //@PrimaryKey(必須)
userDataQuery.setAge(32);
//@PrimaryKey(必須)
userDataQuery.setDate(new Date()); //@Indexedは(必須)
userDataQuery.setAddress("setagaya");
jsonPersister.save(userDataQuery);
JsonPersister.delete
@PrimaryKey指定で一意なjavaオブジェクトを物理削除します
Sample.java
UserData userDataQuery = new UserData();
userDataQuery.setUserName("yuhsaku"); //@PrimaryKey(必須)
userDataQuery.setAge(32);
//@PrimaryKey(必須)
jsonPersister.delete(userDataQuery);
JsonPersister.load
@PrimaryKey指定で一意なjavaオブジェクトを取得します
- 6. Sample.java
UserData userDataQuery = new UserData();
userDataQuery.setUserName("yuhsaku");
userDataQuery.setAge(32);
//第三引数は取得するデータの最新性を担保するかどうかです。(falseを指定するとmysqlの場合はslaveからデ
ータを取得します。masterから取得したい場合はtrueを渡してください)
UserData result_yuhsaku = jsonPersister.load(userDataQuery, UserData.class, false);
JsonPersister.list
javaオブジェクトを指定した条件でリスト取得します
Sample.java
//32才のUserDataオブジェクトをリスト取得します(5番目から10件をuserNameで降順で取得)
UserData userDataQuery = new UserData();
userDataQuery.setAge(32);
Criteria<UserData> criteria =
Criteria.createCriteria(UserData.class).andEquals(userDataQuery).offset(5).limit(10).orderBy("userName",
Criteria.ORDER.DESC);
//第三引数は取得するデータの最新性を担保するかどうかです。(falseを指定するとmysqlの場合はslaveからデ
ータを取得します。masterから取得したい場合はtrueを渡してください)
//Criteriaでlimitの指定がない場合は100件がデフォルトです(nullを明示的に指定すればlimitは指定されませ
ん = 全件取得になります)
List<UserData> resultList = jsonPersister.list(criteria, true);
Criteriaには他にもLesserThanやGreaterThanなどの比較条件のメソッドも用意されています。
JsonPersister.createTable
テーブルを作成します
Sample.java
jsonPersister.createTable(UserData.class);
JsonPersister.dropTable
テーブルをdropします
Sample.java
jsonPersister.dropTable(UserData.class);
高度な使い方
- 7. 複合インデックス
以下のようなダミーメソッドを作成して@CompositeIndexedを指定するとcreateTable時に複合インデックスが作成されます
@CompositeIndexed(name={"userName", "age", "address"})
public void composite_index0() {}
nameに指定したプロパティ名の順番で複合インデックスが作成されます
暗号化
以下のようにフィールドに@Encryptoアノテーションをつけることによりそのフィールドが暗号化されて永続化されます。(String型のみ
対応しています)
@Encrypto(algorithm="AES", keyLength=256)
private String address;
暗号化を有効化するためにはそのクラスはAbstractEncryptoableを継承する必要があります。
以下サンプル
SecureInfo.java
@DataBaseId(id=0)
@Persistable(name="user_secure_info")
public class SecureInfo extends AbstractEncryptoable{
@PrimaryKey
private String userId;
@Encrypto(algorithm="AES", keyLength=256)
private String mailAddress;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getMailAddress() {
return mailAddress;
}
public void setMailAddress(String mailAddress) {
this.mailAddress = mailAddress;
}
}
保存、取得する際は暗号化キーをsetする必要があります
- 8. //保存
SecureInfo secureInfo = new SecureInfo();
secureInfo.setUserId("hogehoge");
secureInfo.setMailAddress("hogehoge@co.jp");
secureInfo.setCryptoSeed("hogehoge_key"); //暗号化、復号化する際のキー文字列
jsonPersister.save(secureInfo);
//取得
SecureInfo secureInfoQuery = new SecureInfo();
secureInfoQuery.setUserId("hogehoge");
secureInfoQuery.setCryptoSeed("hogehoge_key"); //暗号化、復号化する際のキー文字列
SecureInfo result = jsonPersister.load(secureInfoQuery, SecureInfo.class, false);
@EncryptoアノテーションにはDESやAESなどjavaでサポートされているアルゴリズムを文字列で指定できます。
※AES-256はJavaランタイムに無制限強度の管轄ポリシーの設定追加が必要です。
javaのインストールディレクトリ配下のjre/lib/securiy/以下に
http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
からダウンロードしたzipに入っているlocal_policy.jarとUS_export_policy.jarを上書きコピーしてください
キャッシュ
classに@Cacheableを指定するヒープに指定した時間キャッシュすることができます
@Cacheable(expire=5000)
@DataBaseId(id = 1)
@Persistable(name = "user_data")
public class UserData {
以下略
各モジュールについて
json_persister_mysql
データ保存形式
jsonに変換された後以下のテーブルに保存される(テーブルは事前にjson_persister.CreateTableExecuteインターフェースを通じて作成し
ておく必要がある)
-------------------------------------------------------------------------------------CREATE TABLE `user_data` (
`userName_index` text NOT NULL,
`age_index` text NOT NULL,
`date_index` datetime DEFAULT NULL,
`address_index` text,
`data` text NOT NULL,
PRIMARY KEY (`userName_index`(255),`age_index`(255)),
KEY `date_idx` (`date_index`),
KEY `composite_index0_idx` (`userName_index`(255),`age_index`(255),`address_index`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
--------------------------------------------------------------------------------------
QuickStart
- 9. DIコンテナを利用しない場合
ドキュメント整備中
springでの利用
springの設定方法は以下の通り
<bean id="perser" class="jp.co.cyberagent.persister.parser.JaksonPerser" />
<context:component-scan base-package="jp.co.cyberagent.persister" />
<bean id="masterConnectionHolder" class="jp.co.cyberagent.persister.mysql.SingleConnectionHolder" >
<property name="dataSource" ref="masterDataSourceHolder" />
</bean>
<bean id="slaveConnectionHolder" class="jp.co.cyberagent.persister.mysql.SingleConnectionHolder" >
<property name="dataSource" ref="slaveDataSourceHolder" />
</bean>
<bean id="masterDataSourceHolder" class="jp.co.cyberagent.persister.mysql.SingleConnectionHolder" >
<property name="dataSource">
<bean class="org.apache.commons.dbcp.datasources.SharedPoolDataSource">
<property name="connectionPoolDataSource">
<bean class="org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/ameba"/>
<property name="user" value="root"/>
<property name="password" value=""/>
</bean>
</property>
<property name="defaultAutoCommit" value="true"/>
<property name="maxActive" value="3"/>
<property name="maxIdle" value="1"/>
<property name="maxWait" value="500"/>
</bean>
</property>
</bean>
<bean id="slaveDataSourceHolder" class="jp.co.cyberagent.persister.mysql.SingleConnectionHolder" >
<property name="dataSource">
<bean class="org.apache.commons.dbcp.datasources.SharedPoolDataSource">
<property name="connectionPoolDataSource">
<bean class="org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3316/ameba"/>
<property name="user" value="root"/>
<property name="password" value=""/>
</bean>
</property>
<property name="defaultAutoCommit" value="true"/>
<property name="maxActive" value="3"/>
<property name="maxIdle" value="1"/>
<property name="maxWait" value="500"/>
</bean>
</property>
</bean>
json_persister_mongo
ドキュメント整備中
- 11. Sample.java
Lock lock = Hazelcast.getLock(myLockedObject);
lock.lock();
try {
// データ1をsave
// データ2をsave
} finally {
lock.unlock();
}
みたいな。
残念ながらhazelcastを使ってもロールバックはできませんが、mysqlのバックアップやバイナリログからの復旧ができるので障害時などは
運用でカバーできる範囲かと思っています。
トレードオフですが、個人的にはここらへんの機能を削ってでも得られる開発スピードのメリットを推しています。
MySQL実装を使うメリットはなんですか?
MySQLはamebaでも非常に実績のあるRDBMSです。
それをデータストアに使うことによって、安定性や運用ノウハウをそのまま利用することができます。
O/Rマッパー経由でMySQLを使うことはできますが、テーブル正規化や仕様変更によるテーブル構成変更はamebaの多くのWEBサービス
において運用ボトルネックになりやすいので
スキーマレスにデータを保存できるという点でjsonPersisterは優れています。
まとめ
現在進めているプロジェクトでの開発で実際に利用していますが、MyBatisなどを利用する場合に比べて非常に柔軟な開発が可能です。
PrimaryKeyだけは最初に決めておく必要がありますが、それ以外のデータは開発途中での要件変更やデータ構造の変更が容易だからです
。
Scrumなどアジャイルに開発しているプロジェクトではなおさら高い効率を出せるかと思います。
MyBatisなどのORマッパを使うぐらいならこれを利用するほうが断然がシンプルだと感じました。
参考文献
FriendFeed では MySQL を使いどのようにスキーマレスのデータを保存しているのかhttp://www.hyuki.com/yukiwiki/wiki.cgi?HowFri
endFeedUsesMySqlToStoreSchemaLessData