GWTでJava Persistence API (Hibernate) をつかう

結構困ったので、記録。


前準備
1. Eclipseをダウンロード
2. EclipseGWTプラグインを入れる

手順
Userを登録してリスト表示するサービスを作ってみます。
1. GWTプロジェクトを作成
2.Service, ServiceAsync, データオブジェクトを作成
2−1. UserServiceインタフェースを作る
package jp.ac.sojou.cis.shiva.gwtjpa.client;

import java.util.List;
import jp.ac.sojou.cis.shiva.gwtjpa.model.User;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("user")
public interface UserService extends RemoteService {

public Long add(User u);
public List list();
}
2−2. Userクラスを作る(仮)
package jp.ac.sojou.cis.shiva.gwtjpa.model;

public class User implements Serializable{
}
modelパッケージを作成。
GWTxJPA.gwt.xmlに次の一行を追加。
  
2−3. UserServiceAsyncインタフェースを作る
(UserServiceでエラーが表示されているので、Ctrl+1から作成)
package jp.ac.sojou.cis.shiva.gwtjpa.client;

import java.util.List;
import jp.ac.sojou.cis.shiva.gwtjpa.model.User;
import com.google.gwt.user.client.rpc.AsyncCallback;

public interface UserServiceAsync {

void add(User u, AsyncCallback callback);
void list(AsyncCallback> callback);
}
2−4.UserServiceImplを作成 (後で実装)
package jp.ac.sojou.cis.shiva.gwtjpa.server;

import java.util.List;
import jp.ac.sojou.cis.shiva.gwtjpa.client.UserService;
import jp.ac.sojou.cis.shiva.gwtjpa.model.User;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class UserServiceImpl extends RemoteServiceServlet implements
UserService {

public Long add(User u) {
return null;
}

public List list() {
return null;
}
}
2−5.web.xmlを変更
次のコードを追加します。


userServlet

jp.ac.sojou.cis.shiva.gwtjpa.server.UserServiceImpl




userServlet
/gwtxjpa/user

3−1. GWTxJPA.htmlのbodyタグ内部を次のように変更します。

User List








3−2. GWTxJPA.javaを実装します。
package jp.ac.sojou.cis.shiva.gwtjpa.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

public class GWTxJPA implements EntryPoint {

private final UserServiceAsync userService = GWT.create(UserService.class);

//UI Widgets
private TextBox nameBox;
private PasswordTextBox passwordBox;
private Button button;
private FlexTable table;

public void onModuleLoad() {
//Create Widgets
nameBox = new TextBox();
passwordBox = new PasswordTextBox();
button = new Button("Add");
table = new FlexTable();

//Layout Widgets
HorizontalPanel hp = new HorizontalPanel();
hp.add(new Label("Name : "));
hp.add(nameBox);
hp.add(new Label(" Password : "));
hp.add(passwordBox);

RootPanel.get("fields").add(hp);
RootPanel.get("button").add(button);
RootPanel.get("list").add(table);
}
}
ここで一度実行して動作を確認します。
4. 永続化設定
Userオブジェクトを永続化します。

4−0. 関連ライブラリの追加
以後必要となるライブラリを追加します。
ビルドパス、WEB-INF/lib配下に図中のjarファイルを追加します。
これが一番面倒でした。Mavenとか使うと楽になるのかな?

ビルドパス

war/WEB-INF/lib配下

4−1. Userクラスを変更
package jp.ac.sojou.cis.shiva.gwtjpa.model;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class User implements Serializable {

@Id
@GeneratedValue
private Long id;
private String name;
private String password;

public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

4−2. JPA設定ファイルを追加
ソースフォルダのMETA-INF配下に persistence.xml, orm.xmlを配置

persistence.xml

xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

org.hibernate.ejb.HibernatePersistence









orm.xml

xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

データベースにはHSQLDBを使用しています。

4−3. UserDAOクラスを作成
package jp.ac.sojou.cis.shiva.gwtjpa.server;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import jp.ac.sojou.cis.shiva.gwtjpa.model.User;

public class UserDAO {

private EntityManager manager;

public void setManager(EntityManager manager) {
this.manager = manager;
}

public User add(User u) {
EntityTransaction t = manager.getTransaction();
t.begin();
manager.persist(u);
t.commit();

return u;
}

@SuppressWarnings("unchecked")
public List list() {
Query q = manager.createQuery("select u from User as u");
return (List) q.getResultList();
}
}
4−4. UserServiceImplを実装
package jp.ac.sojou.cis.shiva.gwtjpa.server;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import jp.ac.sojou.cis.shiva.gwtjpa.client.UserService;
import jp.ac.sojou.cis.shiva.gwtjpa.model.User;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class UserServiceImpl extends RemoteServiceServlet implements
UserService {

private UserDAO userDAO;

public UserServiceImpl() {
EntityManagerFactory f = Persistence
.createEntityManagerFactory("GWTxJPA");
EntityManager manager = f.createEntityManager();
userDAO = new UserDAO();
userDAO.setManager(manager);
}

public Long add(User u) {
return userDAO.add(u).getId();
}

public List list() {
return userDAO.list();
}
}
4−5. GWTxJPA.javaを実装
ボタンを押すとユーザが登録され、リストが更新されるように実装します。
package jp.ac.sojou.cis.shiva.gwtjpa.client;

import java.util.List;
import jp.ac.sojou.cis.shiva.gwtjpa.model.User;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

public class GWTxJPA implements EntryPoint {

private final UserServiceAsync userService = GWT.create(UserService.class);

// UI Widgets
private TextBox nameBox;
private PasswordTextBox passwordBox;
private Button button;
private FlexTable table;

public void onModuleLoad() {
// Create Widgets
nameBox = new TextBox();
passwordBox = new PasswordTextBox();
button = new Button("Add");
table = new FlexTable();
updateTable();

// event handler for the button
button.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
buttonClicked();
}
});
updateTable();

// Layout Widgets
HorizontalPanel hp = new HorizontalPanel();
hp.add(new Label("Name : "));
hp.add(nameBox);
hp.add(new Label(" Password : "));
hp.add(passwordBox);

RootPanel.get("fields").add(hp);
RootPanel.get("button").add(button);
RootPanel.get("list").add(table);
}

private void buttonClicked() {
if (nameBox.getText().trim().isEmpty()
|| nameBox.getText().trim().isEmpty()) {
return;
}
User u = new User();
u.setName(nameBox.getText());
u.setPassword(passwordBox.getText());
AsyncCallback callback = new AsyncCallback() {

public void onSuccess(Long result) {
updateTable();
}
public void onFailure(Throwable caught) {}
};
userService.add(u, callback);
}

private void updateTable() {
AsyncCallback> callback = new AsyncCallback>() {

public void onSuccess(List result) {
table.clear();
int row = 0;
for (User user : result) {
table.setText(row, 0, user.getId().toString());
table.setText(row, 1, user.getName());
table.setText(row, 2, user.getPassword());
}
}
public void onFailure(Throwable caught) {}
};
userService.list(callback);
}
}
5. Gileadを使う
ここまでの作業で、完成しているように思えますが、
Hibernateによって内部的に変更されたクラスは
GWT RPCで上手く送る事が出来ないため、エラーが発生します。
Gileadはこの問題をきれいに解決してくれるライブラリです。

5−1. GWTxJPA.gwt.xmlにInheritを追加
5−2. Userクラスを変更
net.sf.gilead.pojo.gwt.LightEntityクラスを継承します。
public class User implements Serializable {
public class User extends LightEntity implements Serializable {
5−3. UserServiceImplクラスを変更
net.sf.gilead.gwt.PersistentRemoteServiceクラスを継承するように変更します。
public class UserServiceImpl extends RemoteServiceServlet implements UserService {
public class UserServiceImpl extends PersistentRemoteService implements UserService {
デフォルトコンストラクタを次のように変更します。
 public UserServiceImpl() {
EntityManagerFactory f = Persistence
.createEntityManagerFactory("GWTxJPA");
EntityManager manager = f.createEntityManager();

//Initialize Persistent Bean Manager
HibernateJpaUtil u = new HibernateJpaUtil(f);
PersistentBeanManager persistentBeanManager = GwtConfigurationHelper
.initGwtStatelessBeanManager(u);
setBeanManager(persistentBeanManager);

userDAO = new UserDAO();
userDAO.setManager(manager);
}
6.動作確認