2013年11月8日金曜日

HibernateでLocalSessionFactoryBeanを複数設定した場合の使いわけ

Hibernateで複数のデータベースに接続したい場合、LocalSessionFactoryBeanを複数設定ファイルに書きますが、ソースコードのDAO側でどうやって切り分けるのかが分からない。


    <bean id="sessionFactoryA" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="configLocation">
            <value>/WEB-INF/A.cfg.xml</value>
        </property>
    </bean>

   <bean id="sessionFactoryB" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="configLocation">
            <value>/WEB-INF/B.cfg.xml</value>
        </property>

    </bean>

調べてみると、意外にも簡単で@Qualifierアノテーションでidを指定するだけでOK。

@Repository
public class SomeDaoImpl implements SomeDao
{
    @Autowired
    @Qualifier(value="sessionFactoryA")
    private SessionFactory _sessionFactory;


こんな感じ。簡単簡単。

2013年11月7日木曜日

とりあえずHibernateのエンティティマッピングの書き方をすぐ忘れてしまうので。。。

とりあえずHibernateのエンティティマッピングの書き方をすぐ忘れてしまうので、一番すっきりとしていて分かりやすいお手本のリンクをメモっておくことにした。

http://en.wikibooks.org/wiki/Java_Persistence/OneToOne

Hibernateでカラムのソートの昇順・降順を指定するには@OrderByを使う

@OrderColumnの落とし穴 でHibernateのエンティティで一対多の関係を持ったコレクションのフィールドで@OrderColumnを使用した場合に、java.util.Listがnullオブジェクトを追加してしまうのでjava.util.Setを使う事にしました。

じゃあそのコレクションのソート降順・昇順の指定はどうなるんだ?という事ですが、@OrderByで指定できるようです。中身は@OrderBy("ID ASC")もしくは@OrderBy("ID DESC")のように、ここはシンプルに指定できます。また、なぜか@OrderColumnは指定する必要が無い事に気づきました。なんで???

またもやHibernateの未知なブラックボックスです。どうやって@OrderByと@OrderColumnを使い分けるのだろう??

http://stackoverflow.com/questions/11433195/hibernate-orderby-vs-ordercolumn-to-maintain-the-order-of-a-collection

2013年11月6日水曜日

とりあえずSpring+Hibernateで、DAO(データレイヤー)をJUnit4でテストする上で分かったことをまとめた。

まだ謎が多いので、分かった事をとりあえず一覧で。


  • JUnit4で作ったクラスはAbstractTransactionalJUnit4SpringContextTestsを継承する。なぜ?
  • spring-test.jarをpom.xmlに追加
  • @RunWith(SpringJUnit4ClassRunner.class)をクラスレベルで書く。なぜ必要?
  • @ContextConfiguration(locations={classpath:applicationContext.xml, classpath:hibernate.cfg.xml})をクラスレベルで書いて、設定ファイルを読み込む。この設定ファイルはクラスパスがとおった
  • @TransactionConfiguratioをクラスレベルで書いて、defaultRollback=trueにする。ロールバック機能を使うと、データベースの状態をテスト前の状態にできるから便利。
  • public void setDataSource(DataSource dataSource)をオーバーライドする。@Resource(name="dataSource")で設定ファイルで指定されているdataSourceを指定。
とにかくハマったとこ。。。

  1. Maven+Eclipse+WTPのファイル構造で設定ファイルをsrc/main/webapp/WEB-INF/は、クラスパス上にはならないので、src/test/resourceにテスト用の設定ファイルをtest-applicationContext.xml, test-hibernate.cfg.xmlのようにコピーした。もっと賢いやり方ないのかな。。。?ただ、WEB-INFから設定ファイルは動かしたくない。
  2. setDataSourceでtest-hibernate.cfg.xmlからDBのコネクション情報を指定してあげないといけないのだが、<session-factory></session-factory>の中に一括して書いてあるので、コネクション上に関してだけ抜き出して、applicationContext.xmlでdataSourceとして書きだした。
多くに共通してるとこは、設定の仕方が多い事が仇になっているということ。それがSpringとかHibernateの分かりにくいとこなのかなと実感。


参考にしたサイト:

http://stackoverflow.com/questions/2377763/abstracttransactionaljunit4springcontexttests-cant-get-the-dao-to-find-inserte
http://zakato.sblo.jp/article/50155918.html
http://java-cauldron.blogspot.com/2012/07/writing-junit4-tests-spring-30.html
http://mvnrepository.com/artifact/org.springframework/spring-test/3.2.4.RELEASE
http://kinoushi.blogspot.com/2008/08/abstracttransactionaljunit4springcontex.html
http://stackoverflow.com/questions/17220432/failed-to-load-applicationcontext-for-junit-test-of-spring-controller
http://junit.sourceforge.net/javadoc/org/junit/Assert.html
http://intink.blogspot.com/2012/12/hibernate4spring-framework3.html
http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/testing.html#testcontext-fixture-di
http://forum.spring.io/forum/spring-projects/container/38874-transactional-junit4-controller-testing-with-multiple-datasources
http://forum.spring.io/forum/spring-projects/container/49585-how-to-get-contextconfiguration-using-web-inf-applicationcontext-xml
http://forum.spring.io/forum/spring-projects/web/97105-testing-problem-junit4-7-spring3-0-5-hibernate-3-3-2



@OrderColumnの落とし穴

@OrderColumnにハマった。。。Stackoverflowで書いてある状況と全く同じで、一対多の関連性のあるエンティティで多の方のエンティティにListを使っていると、何故かデータベースのテーブルに無いレコードがNullとしてListに入っている。????ここにハマった。

要するにListと使って多エンティティを保持すると、保持される順序を指定しないといけないので@OrderColumnを使うのだが、ここの仕様がややこしくてブラックボックス。

The order column must be of integral type. The persistence provider maintains a contiguous (non-sparse) ordering of the values of the order column when updating the association or element collection. The order column value for the first element is 0.
※contiguous=切れ目の無い

と、JavaDocを読んでててイマイチ不明なのだが、要するに@OrderColumnで指定するカラムがintの主キーで、400が一番若いIDだとすると、0~399まではテーブルには存在しないのだが、わざわざご丁寧に0~399までをnullオブジェクトとしてListの保存するわけだ。これが、contiguous (non-sparse) ordering. なんとお節介な機能だ。。。

そして、これを回避するにはList型でなくSet型を使って使うとnullオブジェクトが保持されなくなったのだが、どうもここはまだまだ奥が深そう。。。こういうところがHibernateの分かりにくいところ。。。

http://stackoverflow.com/questions/13307849/hibernate-returns-list-with-null-values-onetomany-annotation-with-list-type
http://docs.oracle.com/javaee/6/api/javax/persistence/OrderColumn.html

HibernateのFetchTypeがLAZYとEAGERの違い


2つのエンティティ(テーブル)が一対多の関連性を持つ場合、どのタイミングで多のエンティティをロードしてあげるか、FetchTypeで2通りのやり方がある。

@OneToMany(fetch=FetchType.EAGER)
@OneToMany(fetch=FetchType.LAZY)

例えば、大学(Univeristy)というエンティティと学生(Student)というエンティティが一対多で関連付けられているとすると、Listのstudentsにデータをロードするタイミングとして、他のフィールド(id, name, address)と同じタイミングでロードするEAGERと、getStudents()などのオンディマンドでゲッターを呼ぶ場合にロードするLAZYのやり方がある。

public class University {
 private String id;
 private String name;
 private String address;
 private List<Student> students;

 // setters and getters
}

使い分けるポイントとしては、studentsにどれだけのデータがロードされるか、一度に全てロードする事が得策かどうかを考える必要がある。

http://stackoverflow.com/questions/2990799/difference-between-fetchtype-lazy-and-eager-in-java-persistence