2013-01-14

GAE/J+Slim3+Google Cloud SQL+Hibernate+DbUnit(3)

HibernateConfigServiceはこうなっている
HibernateでDBアクセスするにはorg.hibernate.Sessionを取得する必要がある。
そのための手順は、次のようになる。
  1. Configuration configration = new Configuration().configure();
  2. ServiceRegistryBuilder builder = new ServiceRegistryBuilder().applySettings(configration.getProperties());
  3. ServiceRegistry serviceRegistry = builder.buildServiceRegistry();
  4. SessionFactory sessionFactory = configration.buildSessionFactory(serviceRegistry);
  5. Session session = sessionFactory.openSession();
  6. DBアクセス処理
  7. session.close()
これをクラス化して、DbUnitを使用したテストクラスでも使えるようにしたのが、このHibernateConfigServiceです。


HibernateConfigServiceクラス
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;

 */
public class HibernateConfigService {
    private static HibernateConfigService instance = null;
    private Configuration configration = null;
    private SessionFactory sessionFactory = null;
    private ConnectionProvider connProvider = null;

    protected HibernateConfigService() {
        String configFileName = System.getenv("hibernateConfig");
        if (configFileName == null) {
            configration = new Configuration().configure();
        } else {
            configration = new Configuration().configure(configFileName);
        }
    }

    public static HibernateConfigService getInstance() {
        if(instance == null) {
            instance = new HibernateConfigService();
        }
        return instance;
    }

    /**
     * <p>[ユニットテスト用]{@link HibernateConfigService}インスタンスを取得する</p>
     * @param pConnectionProvider {@link ConnectionProvider}インスタンス
     * @return {@link HibernateConfigService}インスタンス
     */
    public static HibernateConfigService getInstance(final ConnectionProvider pConnectionProvider) {
        if(instance == null) {
            instance = new HibernateConfigService();
        }
        instance.setConnProvider(pConnectionProvider);
        return instance;
    }

    @SuppressWarnings("deprecation")
    public SessionFactory getSessionFactory() {
        if(sessionFactory == null) {
            ServiceRegistryBuilder builder = new ServiceRegistryBuilder()
                .applySettings(configration.getProperties());
            if (connProvider != null) {
                builder = builder.addService(ConnectionProvider.class, connProvider);
            }
            ServiceRegistry serviceRegistry = builder.buildServiceRegistry();
            sessionFactory = configration.buildSessionFactory(serviceRegistry);
        }
        return sessionFactory;
    }

    private void setConnProvider(ConnectionProvider pConnProvider) {
        this.connProvider = pConnProvider;
    }

    public Configuration getConfigration() {
        return this.configration;
    }
}

2013-01-11

GAE/J+Slim3+Google Cloud SQL+Hibernate+DbUnit(2)

前回の続きで、一覧表示機能について。

ここで使用しているHibernateConfigServiceクラスは、Hibernateの管理用に作成したクラスで、この内容は次回に。

ListControllerクラス
  • setUp()
    • DBのセッションをセッション・スコープに格納して使いまわす
  • run()
    • データ件数の取得(もっと簡潔な方法ないかな?)
      dbSession.createQuery("select count(*) from User")
          .uniqueResult();
    • データの取得:ページングのために、setFirstResult()で取得開始位置を、setMaxResults()で取得件数を指定している
      dbSession.createCriteria(User.class)
          .setFirstResult((pageNo - 1) * MAX_ITEM_PAGE)
          .setMaxResults(MAX_ITEM_PAGE)
          .list();
import org.hibernate.Session;
import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;

public class ListController {
    private final int MAX_ITEM_PAGE = 20;
    private Session _dbSession = null;

    @Override
    protected Navigation setUp() {
        this._dbSession = sessionScope("dbSession");
        if (this._dbSession == null) {
            HibernateConfigService hibernateConfigService = HibernateConfigService.getInstance();
            this._dbSession = hibernateConfigService.getSessionFactory().openSession();
            sessionScope("dbSession", this._dbSession);
        }
        return super.setUp();
    }

    @Override
    public Navigation run() throws Exception {
        List<String> msgList = new ArrayList<String>();
        Session dbSession = getDbSession();

        Long count = (Long)dbSession.createQuery("select count(*) from User").uniqueResult();
        Integer pageNo = asInteger("pageNo");
        if (pageNo == null) { pageNo = 1; }
        requestScope("pageNo", pageNo);
        long maxPageNo = (count - 1) / MAX_ITEM_PAGE + 1;
        requestScope("maxPageNo", maxPageNo);
        List<User> list = (List<User>)dbSession.createCriteria(User.class)
                .setFirstResult((pageNo - 1) * MAX_ITEM_PAGE)
                .setMaxResults(MAX_ITEM_PAGE)
                .list();
        requestScope("list", list);

        return forward("list.jsp");
    }

    protected Session getDbSession() {
        return this._dbSession;
    }
}

2013-01-06

GAE/J+Slim3+Google Cloud SQL+Hibernate+DbUnit(1)

GAE/J+Slim3からGoogle Cloud SQLを使ってみた。
そのまま使うだけじゃつまらないし、開発効率のことを考えてHibernateを使う方法を調べてみた。またテストにDbUnitを使う方法も一緒に調べてみた。

まずはモデル定義、よくある部署と所属ユーザーでやってみる。
ER図はこんな感じ、
部署テーブル(Division)とユーザーテーブル(User)、部署とユーザーの関係を表すUserDivisionテーブルの3つで、モデルクラスを作成するのはDivisionとUserのみ、ソースは次のようになる。(単純なsetter,getterは省略してある)
長くなるので、とりあえずUserクラスだけ、Divisionクラスは後日。


Userクラス
  • テーブルとの関係付け
    • @Entity
    • @Table: 'name'でテーブル名を指定する
  • 主キー
    • @Id
    • @Column: 'name'でカラム名を指定する
  • 楽観的排他制御
    • @Version
    • @Temporal(TemporalType.TIMESTAMP): カラムの属性を指定する
    • @Column: 'name'でカラム名を指定する
  • テーブル間関係
    • @ManyToMany: 多対多の関係を定義する
    • @JoinTable: 'name'で結合テーブルを指定する。'joinColumns'でUserテーブルとの結合カラムを指定する。'inverseJoinColumns'でDivisionテーブルとの結合カラムを指定する
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;

@Entity
@Table(name = "User")
public class User implements java.io.Serializable {
    private int id;
    private String name;
    private String email;
    private Date timestamp;
    private Set<Division> divisions = new HashSet<Division>();

    public User() {

    }

    public User(String name, Set<Division> divisions) {
        this.name = name;
        this.divisions = divisions;
    }

    /**
     * idを取得する
     * @return id
     */
    @Id
    @Column(name = "id", unique = true, nullable = false)
    public int getId() {
        return this.id;
    }

    /**
     * idを設定する
     * @param id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * timestampを取得する
     * @return timestamp
     */
    @Version
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "timestamp")
    public Date getTimestamp() {
        return this.timestamp;
    }

    /**
     * timestampを設定する
     * @param timestamp
     */
    public void setTimestamp(Date timestamp) {
        this.timestamp = timestamp;
    }

    /**
     * divisionsを取得する
     * @return divisions
     */
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(name = "UserDivision",
        joinColumns = { @JoinColumn(name = "user_id")},
        inverseJoinColumns = {@JoinColumn(name = "division_id")})
    public Set<Division> getDivisions() {
        return this.divisions;
    }

    /**
     * divisionsを設定する
     * @param divisions
     */
    public void setDivisions(Set<Division> divisions) {
        this.divisions = divisions;
    }
}