無定義Hibernate

無定義Hibernate

2005年6月27日

1.Hibernate on Rails?

1.1.どんなもの?

Ruby on Railsってのがあって、ほとんど何も書かずにDBからテーブル情報とってきてORマッピングするんですが、それと同じようにHibernateでもマッピングクラスとかマッピング定義とか書かずにORマッピングしようじゃないか、っていうものです。
マッピングクラスのソースとマッピング定義をデータベースから自動生成するなら、それを実行時にやっちゃえばいいんじゃない?ってとこで。
実際、内部で、ファイルに保存すれば普通にマッピング定義として使えるようなhbm.xmlを生成してます。

2.用意するもの

2.1.本体

ここからダウンロードしてください。
http://www.fk.urban.ne.jp/home/kishida/soft/hibernateonrails-preview.zip(16KB)

Java2SE5の文法を使ってるので、Java2SE5.0以降じゃないと動きません。
あと、データベースはPostgreSQLを前提にしてます。自動採番の型を使わなければ問題ないですが。

hibernateonrails.jarをクラスパスに含めます。

2.2.ライブラリ

次のライブラリを使います。
Hibernate3( http://www.hibernate.org/)
 hibernate3.jar
 antlr-2.7.5H3.jar
 asm-attrs.jar
 asm.jar
 cglib-2.1.jar
 commons-collections-2.1.1.jar
 commons-logging-1.0.4.jar
 dom4j-1.6.jar
 ehcache-1.1.jar
 jta.jar
Velocity1.4( http://jakarta.apache.org/velocity/)
 velocity-dep-1.4.jar
Javassist3.0( http://www.csg.is.titech.ac.jp/~chiba/javassist/)
 javassist.jar

3.データベース

3.1.テーブル

今回はデータベースにPostgreSQL8.0を使いました。
テーブルはt_shohinとt_makerのふたつです。

t_makerの定義

craete table t_maker(
  maker_id int4 primary key, -- メーカーコード
  maker_name text
);

t_makerのデータはこんな感じです。
 maker_id | maker_name
----------+------------
        1 | ぱなつく
        2 | そにつく

t_shohinの定義

create table t_shohin(
  shohin_id int4 primary key, -- 商品コード
  shohin_name text,           -- 商品名
  maker_id int4               -- メーカーコード
    constraint cons_t_shohin_maker_id references t_maker
    on update cascade
);

t_shohinのデータはこんな感じです。
 shohin_id |    shohin_name     | maker_id
-----------+--------------------+----------
         1 | うすいパソコン     |        1
         2 | かっこいいパソコン |        2
         3 | イヌ型ロボット     |        2

4.使ってみる

4.1.ためしに

じゃあ、まずはマッピングクラスから。
マッピングクラスいらないって書いておきながら、マッピングクラス必要なんですが、中身が不要なんですね。
t_shohin用。

Shohin.java

package sample;

public class Shohin {
}

で、t_maker用

Maker.java

package sample;

public class Maker {
}

Hibernateの設定。これはきっと、いつまでもどこかに必要です。設定ですから。

hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="connection.driver_class">org.postgresql.Driver</property>
        <property name="connection.url">jdbc:postgresql://localhost/ormtest</property>
        <property name="connection.username">dbuser</property>
        <property name="connection.password"></property>
        <property name="show_sql">false</property>
        <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
     </session-factory>
</hibernate-configuration>

今回はVelocityで表示用文字列を作成するので、Velocityテンプレート。

sample.vm

商品一覧
#foreach($shohin in $shohins)
$shohin.shohinId $shohin.shohinName $shohin.tmaker.MakerName
#end

メーカー一覧
#foreach($maker in $makers)
$maker.makerId $maker.makerName
   このメーカーの商品
#foreach($shohin in $maker.tshohins)
   $shohin.shohinName
#end
#end

で、試してみる。結構長いけど、よくみると、ほとんど接続準備とVelocityの準備。

Sample.java

package sample;

import hibernate.onrails.CreateMapping;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;
import java.util.Properties;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Sample {
    private static final String DB_DRIVER = "org.postgresql.Driver";
    private static final String DB_URL = "jdbc:postgresql://localhost/ormtest";
    private static final String DB_USER = "dbuser";
    private static final String DB_PASS = "";

    public static void main(String[] args) throws Exception{
        Class.forName(DB_DRIVER);
        Connection c = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
        Configuration cfg = new Configuration().configure("/sample/hibernate.cfg.xml");
        SessionFactory sf = new CreateMapping(c, cfg, "sample").
                setupClass(Maker.class, "t_maker").
                setupClass(Shohin.class, "t_shohin").
                getConfiguration().buildSessionFactory();
        c.close();
        Session s = sf.openSession();
        
        Properties p = new Properties();
        p.setProperty("resource.loader", "class");
        p.setProperty("class.resource.loader.class",
                ClasspathResourceLoader.class.getName());
        p.setProperty("input.encoding", "Windows-31J");
        Velocity.init(p);
        VelocityContext vc = new VelocityContext();
        
        List ml = s.createQuery("from dbMaker").list();
        List sl = s.createCriteria("sample.dbShohin").list();
        vc.put("shohins", sl);
        vc.put("makers", ml);

        Template tem = Velocity.getTemplate("sample/sample.vm");
        StringWriter w = new StringWriter();
        tem.merge(vc, w);
        w.close();
        System.out.print(w.toString());     
    }
    
}

そうすると、こんな感じ。One-to-ManyとかMany-to-Oneとかもうまくマッピングできてます。

商品一覧
1 うすいパソコン ぱなつく
2 かっこいいパソコン そにつく
3 イヌ型ロボット そにつく

メーカー一覧
1 ぱなつく
   このメーカーの商品
   うすいパソコン
2 そにつく
   このメーカーの商品
   イヌ型ロボット
   かっこいいパソコン

こんな感じで、マッピングクラスとテーブルを指定しています。
SessionFactory sf = new CreateMapping(c, cfg, "sample").
        setupClass(Maker.class, "t_maker").
        setupClass(Shohin.class, "t_shohin").
        getConfiguration().buildSessionFactory();

指定したクラスを内部で継承して"db"を頭につけたクラスを作って、それをHibernateに登録するようにしています。だから、"db" + 「登録したクラス名」でHQLなどを書きます。ShohinだとdbShohinになります。
List ml = s.createQuery("from dbMaker").list();
List sl = s.createCriteria("sample.dbShohin").list();

基本的に、MiddlegenでHibernateのマッピング作ったときと同じようなプロパティを生成してます。
#foreach($maker in $makers)
$maker.makerId $maker.makerName
   このメーカーの商品
#foreach($shohin in $maker.tshohins)
   $shohin.shohinName
#end
#end

4.2.Javaコードで利用

クラスにマッピングの定義を書いてないので、内部で生成したメソッドをJavaのコードから呼び出すことができません。
だからVelocityとかJSPの式言語からだけ使えるってことになるのですが、それでは不便です。
ってことで、マッピングクラスに必要なアクセッサの定義を書けば、Javaコードから呼べるようになります。
Many-to-Oneの場合、戻り値や引数の型として内部で生成したクラスを指定する必要があるのですが、そういうことができないので、内部でキャストするアクセッサを「db」を追加した名前で生成してます。ということで、Many-to-Oneの場合は、このメソッドを定義します。

Shohin.java

package sample;

public class Shohin {
    public Integer getShohinId(){ return null; }
    public String getShohinName(){ return null; }
    public Maker getDbTmaker(){ return null; }
}

Maker.java

package sample;
import java.util.Set;

public class Maker {
    public Integer getMakerId(){ return null; }
    public String getMakerName(){ return null; }
    public Set<Shohin> getTshohins(){ return null; }
}

Sample.java

package sample;

import hibernate.onrails.CreateMapping;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Sample {
    private static final String DB_DRIVER = "org.postgresql.Driver";
    private static final String DB_URL = "jdbc:postgresql://localhost/ormtest";
    private static final String DB_USER = "dbuser";
    private static final String DB_PASS = "";

    public static void main(String[] args) throws Exception{
        Class.forName(DB_DRIVER);
        Connection c = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
        Configuration cfg = new Configuration().configure("/sample/hibernate.cfg.xml");
        SessionFactory sf = new CreateMapping(c, cfg, "sample").
                setupClass(Maker.class, "t_maker").
                setupClass(Shohin.class, "t_shohin").
                getConfiguration().buildSessionFactory();
        c.close();
        Session s = sf.openSession();
        
        List<Maker> ml = s.createQuery("from dbMaker").list();
        List<Shohin> sl = s.createCriteria("sample.dbShohin").list();
        
        System.out.println("商品一覧");
        for(Shohin shohin : sl){
            System.out.printf("%d %s %s%n", 
                    shohin.getShohinId(), 
                    shohin.getShohinName(), 
                    shohin.getDbTmaker().getMakerName());
        }
        System.out.println("メーカー一覧");
        for(Maker maker : ml){
            System.out.printf("%d %s%n", maker.getMakerId(), maker.getMakerName());
            for(Shohin shohin : maker.getTshohins()){
                System.out.printf("  %s%n", shohin.getShohinName());
            }
        }
    }
    
}


商品一覧
1 うすいパソコン ぱなつく
2 かっこいいパソコン そにつく
3 イヌ型ロボット そにつく
メーカー一覧
1 ぱなつく
  うすいパソコン
2 そにつく
  イヌ型ロボット
  かっこいいパソコン

4.3.interfaceのほうがよくない?

return null;とか書くならabstractメソッドでいいね、ってなりますね。で、結局全部abstractなんだし、インターフェイスのほうがいいんじゃない?ってことで、interfaceで構わないです。

Shohin.java

package sample;

public interface Shohin {
    Integer getShohinId();
    String getShohinName();
    Maker getDbTmaker();
}

Maker.java

package sample;
import java.util.Set;

public class Maker {
    Integer getMakerId();
    String getMakerName();
    Set<Shohin> getTshohins();
}
こうすると、メソッド名や型間違えてしまったときに、実行時に例外がでて教えてくれます。

5.結局どうなの?

結構、いい感じで使えてますね。
マッピング用の定義にクラスを使えるようにしている必然性が感じられませんが、メソッドの中身を定義してたらオーバーライドしないようにするという使い方を考えてます。いまのところ有無をいわさずオーバーライドですけど。
あとはアノテーションで設定情報の補足を与えたりってことをやらないと実際には使えないんですが、そこまでやるならHibernate自体をいじくったほうがよさそうです。

いままでこういう、内部的にメソッドを生成するっていうアプローチは、Javaのコードから使えないから意味ないって思われてたんですが、JSPの式言語やカスタムタグ、Velocityで、リフレクションから動的にメソッドを検出して呼び出す仕組みが普及してきて、Javaのコードから使える必要があまりなくなってきています。
特に、表示用にしか使わないプロパティなんかだと、JSPやVelocityの式言語でしか使わないので、自動生成で構いません。また、このあたりが煩雑な作業になりがちなので、処理に必要ないものを見なくてよくなるというのはいいかもしれません。

まあ、JavaでもRuby on Railsみたいなことはできますよ、ってことです。
<script language="javascript" type="text/javascript"> </script>
Copyright (c) 2001-2005 Naoki Kishida All Rights Reserved.
http://www.fk.urban.ne.jp/home/kishida/
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值