- 必要なライブラリ
Hibernate Shardsを用いるにはHibernate Coreが必要なので、
- Core本体
- Coreが依存するlib
- Shardsのlib
を入れる。試すJDBCドライバーも含める。
+lib
antlr.jar
cglib.jar
asm.jar
asm-attrs.jars
commons-collections.jar
commons-logging.jar
hibernate3.jar
hibernate-shards.jar
jta.jar
dom4j.jar
log4j.jar
JDBCドライバーのjar
- RDBMSの設定
Hibernate Shardsは複数のRDBMSに対してデータを分散して登録する。そのためにhibernateの設定ファイルをRDBMS分用意する。
db1.hibernate.cfg.xml
<hibernate-configuration>
<session-factory name="sf0">
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost:9001</property>
<property name="hibernate.connection.shard_id">0</property>
<property name="hibernate.shard.enable_cross_shard_relationship_checks">true</property>
<property name="hbm2ddl.auto">create</property>
・・・
</session-factory>
</hibernate-configuration>
db2.hibernate.cfg.xml
<hibernate-configuration>
<session-factory name="sf1">
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost:9002</property>
<property name="hibernate.connection.shard_id">1</property>
<property name="hibernate.shard.enable_cross_shard_relationship_checks">true</property>
<property name="hbm2ddl.auto">create</property>
・・・
</session-factory>
</hibernate-configuration>
hibernate.connection.shard_idは、複数のRDBを識別する識別子となるので、かぶらないように別々の番号を振る。session-factoryのnameもかぶらないようにつけておく必要があるっぽい。
- エンティティとマッピングファイル
エンティティを用意する。簡単に試したいのでidとvalueのみの単純なエンティティを使う。
package net.grandnature.example.invoice.entity;
public class Example {
private Long id;
private String value;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
マッピングファイルはこんな感じ。
example.hbm.xml
<hibernate-mapping package="net.grandnature.example.invoice.entity">
<class name="Example">
<id name="id" type="long">
<generator class="org.hibernate.shards.id.ShardedTableHiLoGenerator"/>
</id>
<property name="value"/>
</class>
</hibernate-mapping>
ShardedTableHiLoGeneratorを用いると複数のRDBMSでHiloの値が重複しないようになる。identityなどで行いたい場合は、ひとつめのDBは0からはじまるように、ふたつめのDBでは1000からはじまるように、など、データ量に応じてIDが重複しないように自分でうまく調節する必要がある模様。
ShardedTableHiLoGeneratorをHibernate Annotationでgeneratorとして指定するにはどうすればいいのかなあ。
- データを登録してみる
public class ShardsExample {
public static void main(String[] args) {
new ShardsExample().insert();
}
void insert() {
Session session = createSessionFactory().openSession();
Transaction tx = session.beginTransaction();
for (int i = 0; i < 10; i++) {
Example example1 = new Example();
example1.setValue(i + "番目");
session.save(example1);
}
tx.commit();
session.close();
}
SessionFactory createSessionFactory() {
Configuration prototype = new Configuration().configure("db1.hibernate.cfg.xml");
prototype.addResource("example.hbm.xml");
List shard = new ArrayList();
shard.add(new Configuration().configure("db1.hibernate.cfg.xml"));
shard.add(new Configuration().configure("db2.hibernate.cfg.xml"));
ShardStrategyFactory shardStrategyFactory = buildShardStrategyFactory();
ShardedConfiguration shardedConfig = new ShardedConfiguration(
prototype, shard, shardStrategyFactory);
return shardedConfig.buildShardedSessionFactory();
}
ShardStrategyFactory buildShardStrategyFactory() {
ShardStrategyFactory shardStrategyFactory = new ShardStrategyFactory() {
public ShardStrategy newShardStrategy(List shardIds) {
RoundRobinShardLoadBalancer loadBalancer = new RoundRobinShardLoadBalancer(
shardIds);
ShardSelectionStrategy pss = new RoundRobinShardSelectionStrategy(
loadBalancer);
ShardResolutionStrategy prs = new AllShardsShardResolutionStrategy(
shardIds);
ShardAccessStrategy pas = new SequentialShardAccessStrategy();
return new ShardStrategyImpl(pss, prs, pas);
}
};
return shardStrategyFactory;
}
}
これを実行すると、db1.hibernate.cfg.xmlにて定義したRDBに
ID:1, VALUE:0番目
ID:2, VALUE:2番目
ID:3, VALUE:4番目
ID:4, VALUE:6番目
ID:5, VALUE:8番目
が入り、db2.hibernate.cfg.xmlにて定義したRDBに
ID:32768, VALUE:1番目
ID:32769, VALUE:3番目
ID:32770, VALUE:5番目
ID:32771, VALUE:7番目
ID:32772, VALUE:9番目
が入る。(例えばの話)
- データを取得してみる
void select() {
SessionFactory sessionfactory = createSessionFactory();
Session session = sessionfactory.openSession();
List list = session.createQuery(" FROM Example ").list();
for (Example example : (List) list) {
System.out.println("ID:" + example.getId() + ", VALUE:" + example.getValue());
}
}
単一のRDBを操作する場合と変わらない。複数のRDBであることを意識することなく取得できる。
- ShardAccessStrategyを別のものに換えてみる
ShardAccessStrategyとは、データベースへのオペレーションを複数のRDBに対してどのように適用するかを定めたもの。現在の例はSequentialShardAccessStrategyを使っている。SequentialShardAccessStrategyは、複数のRDBに対してひとつずつ順番に処理を実行していく。これをParallelShardAccessStrategyに変更してみる。ParallelShardAccessStrategyはスレッドを生成し、複数のRDBに対して並列に処理を行う。
変更したbuildShardStrategyFactory
ShardStrategyFactory buildShardStrategyFactory() {
ShardStrategyFactory shardStrategyFactory = new ShardStrategyFactory() {
public ShardStrategy newShardStrategy(List shardIds) {
RoundRobinShardLoadBalancer loadBalancer = new RoundRobinShardLoadBalancer(shardIds);
ShardSelectionStrategy pss = new RoundRobinShardSelectionStrategy(loadBalancer);
ShardResolutionStrategy prs = new AllShardsShardResolutionStrategy(shardIds);
ThreadFactory factory = new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
}
};
ThreadPoolExecutor exec = new ThreadPoolExecutor(10, 50, 60,TimeUnit.MICROSECONDS,
new SynchronousQueue(), factory);
ShardAccessStrategy pas = new ParallelShardAccessStrategy(exec);
return new ShardStrategyImpl(pss, prs, pas);
}
};
return shardStrategyFactory;
}