Spring Tool Suite で Spring MVC から JPA(Java Persistence API) を利用する
Spring Tool Suite の Spring MVC Project テンプレートから、Spring JDBC を使えるようにして、JDBC を JNDI 経由で使えるように、一歩一歩試してきて、ようやく JPAを試す準備がととのった。
Spring MVC Project を作成し、JDBCをJNDI から使用できる状態にしたプロジェクト に対して、Simple Spring JPA Utility Project で作成される、JPAのサンプルコードを移植してみる。
EclipseLink および依存ライブラリの設定
JPA のエンジンとして、EclipseLink を使用するための設定を Maven の pom.xml に行う。
repositories 要素に、EcliipseLink の リポジトリ設定を追加
<repository>
<id>EclipseLink Repo</id>
<url>http://www.eclipse.org/downloads/download.php?r=1&nf=1&file=/rt/eclipselink/maven.repo</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
依存ライブラリの設定
pom.xml に Simple Spring JPA Utility Project で作成される、pom.xml を参考に、依存ライブラリの設定を行う。具体的には、dependencies 要素に、以下を追加
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.1_3</version>
</dependency>
エンティティクラスの実装
Spring JPA Utility Project で作成される、JPAのサンプルコードから、Orderクラス、Itemクラスをコピーしてくる。
Order.java
package info.typea.mvctest.entity;
import java.util.Collection;
import java.util.LinkedHashSet;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
* An order.
*/
@Entity
@Table(name="T_ORDER")
public class Order {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String customer;
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="ORDER_ID")
private Collection<Item> items = new LinkedHashSet<Item>();
/**
* @return the customer
*/
public String getCustomer() {
return customer;
}
/**
* @param customer the customer to set
*/
public void setCustomer(String customer) {
this.customer = customer;
}
/**
* @return the items
*/
public Collection<Item> getItems() {
return items;
}
/**
* @param items the items to set
*/
public void setItems(Collection<Item> items) {
this.items = items;
}
/**
* @return the id
*/
public Long getId() {
return id;
}
}
Item.java
package info.typea.mvctest.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
/**
* An item in an order
*/
@Entity
public class Item {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private Order order;
private String product;
private double price;
private int quantity;
/**
* @return the order
*/
public Order getOrder() {
return order;
}
/**
* @return the product
*/
public String getProduct() {
return product;
}
/**
* @param product the product to set
*/
public void setProduct(String product) {
this.product = product;
}
/**
* @return the price
*/
public double getPrice() {
return price;
}
/**
* @param price the price to set
*/
public void setPrice(double price) {
this.price = price;
}
/**
* @return the quantity
*/
public int getQuantity() {
return quantity;
}
/**
* @param quantity the quantity to set
*/
public void setQuantity(int quantity) {
this.quantity = quantity;
}
/**
* @return the id
*/
public Long getId() {
return id;
}
}
動作確認のため、適当にDAOクラスを作成
JpaDao.java
package info.typea.mvctest.dao;
import info.typea.mvctest.entity.Order;
import java.util.List;
public interface JpaDao {
public void saveOrder(Order order);
public List<Order> findByCustomerLike(String customer);
}
JpaDaoImpl.java
package info.typea.mvctest.dao;
import info.typea.mvctest.entity.Order;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import org.springframework.orm.jpa.support.JpaDaoSupport;
public class JpaDaoImpl extends JpaDaoSupport implements JpaDao {
public void saveOrder(Order order) {
//EntityManagerFactory emf = Persistence.createEntityManagerFactory("application");
EntityManagerFactory emf = getJpaTemplate().getEntityManagerFactory();
EntityManager em = emf.createEntityManager();
EntityTransaction et = em.getTransaction();
et.begin();
em.persist(order);
et.commit();
em.close();
}
@SuppressWarnings("unchecked")
public List<Order> findByCustomerLike(String customer) {
//return getJpaTemplate().find("select o from Order o where o.customer like ?1", customer);
return getJpaTemplate().find("select o from Order o");
}
}
persistance.xml (永続化設定ファイル) の作成
WEB-INF/classes/META-INF/persistence.xml に作成する。
EJB環境、Webコンポーネント環境、JavaSE環境それぞれで、persistance.xml の置き場所が異なるので、注意。
<persistence 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"
version="1.0">
<persistence-unit name="application" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>info.typea.mvctest.entity.Item</class>
<class>info.typea.mvctest.entity.Order</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="eclipselink.logging.level" value="FINEST"/>
<property name="javax.persistence.jtaDataSource" value="java:comp/env/jdbc/db2"/>
<!-- <property name="eclipselink.ddl-generation" value="create-tables" /> -->
<property name="eclipselink.ddl-generation" value="none" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
</properties>
</persistence-unit>
</persistence>
JDBC を JNDI に登録
- tc Server のインスタンスを作成
- JDBC ドライバを、tc Server の lib に置く
- context.xml に設定を記述
context.xml にJNDIの記述をしても、適宜初期化されてしまう。。。 tc server のテンプレートを別途作成し、そこに JNDI の設定をして、そのテンプレートを元にインスタンスを作成・・・なんて手順を踏めばうまくいくのかなぁと思ったりしているが、未検証。
プロジェクトエクスプローラーから context.xml を編集しておくと、とりあえず消えなさそうだ。
context.xml に、以下の様な、JNDI リソースを登録
<Context docBase="C:\springsource\vfabric-tc-server-developer-2.5.0.RELEASE\tc_server_1\wtpwebapps\mvctest" path="/mvctest" reloadable="true" source="org.eclipse.jst.jee.server:mvctest">
<Resource name="jdbc/db2" auth="Container"
type="javax.sql.DataSource"
driverClassName="com.ibm.db2.jcc.DB2Driver"
url="jdbc:db2://192.168.10.77:50000/sample"
username="db2inst1"
password="xxxxx"
maxActive="20" maxIdle="10"
maxWait="-1"/>
</Context>
WEB-INF/web.xml に以下を追記
<resource-ref>
<description>DB2 Datasource example</description>
<res-ref-name>jdbc/db2</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
各種 Bean の登録
root-context.xml に、以下の Bean を登録
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="application" />
<property name="loadTimeWeaver" ref="loadTimeWeaver" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
<bean id="loadTimeWeaver" class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
<bean id="jpaDao"
class="info.typea.mvctest.dao.JpaDaoImpl">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/db2"/>
<property name="lookupOnStartup" value="false"/>
<property name="cache" value="true"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
Java Agent の登録
InstrumentationLoadTimeWeaver クラスを利用するためには、Java Agent を登録する必要がある。
Java Agent とは、Java のバイトコードを操作する、AOPには欠かせない機能。
Servers ビューから、サーバーをダブルクリックし、エディタから、Open launch configuration を選択する。
Arguments タブから、VM arguments の Variables…ボタンを押下、開いた画面から、Edit Variables… ボタンを押下。
New ボタンを押下し、起動したダイアログに、以下のような内容を設定
| 項目 | 内容 | 例 | 備考 |
| Name | 任意の名前 | javaagent | |
| Value | -javaagent:/エージェントへのパス | -javaagent:/Users/piroto/.m2/repository/org/springframework/spring-instrument/3.0.0.RELEASE/spring-instrument-3.0.0.RELEASE.jar | とりあえず、Mavenのリポジトリ内のjarへのパス ※ Users/piroto はユーザーディレクトリ |
最後に、VM arguments に今設定した変数 ${javaagent} を追記する。
MVC コントローラーに処理を記述
package info.typea.mvctest;
import info.typea.mvctest.dao.JpaDao;
import info.typea.mvctest.dao.Staff;
import info.typea.mvctest.dao.StaffDao;
import info.typea.mvctest.entity.Item;
import info.typea.mvctest.entity.Order;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
private JpaDao jpadao;
public void setJpaDao(JpaDao jpadao) {
this.jpadao = jpadao;
}
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value="/", method=RequestMethod.GET)
public String home() {
logger.info("Welcome home!");
return "home";
}
@RequestMapping(value="/save_order")
public String saveOrder() {
logger.info("insert test data!");
Order order = new Order();
order.setCustomer("customer");
Item item = new Item();
item.setPrice(100d);
item.setProduct("product");
order.getItems().add(item);
this.jpadao.saveOrder(order);
logger.info("ORDER id:" + order.getId());
List<Order> os = this.jpadao.findByCustomerLike("");
for (Order o : os) {
logger.info("o:id " + o.getId());
}
return "home";
}
}
/save_order で、適当なオブジェクトを登録して、登録されているオブジェクトをログに出力する記述。
実行
http://localhost:8080/mvctest/save_order を実行
上記を何度か実行。正しく実行されて、登録されたデータが取得できた。
データベースを確認
データも正しくできている。
いじょ。
JPA の仕組みについては、この本に非常に丁寧な説明があります。
その他の項目についての説明も非常にわかりやすい良書。
