Spring Tool Suite で Spring MVC から JPA(Java Persistence API) を利用する

Spring Tool Suite の Spring MVC Project テンプレートから、Spring JDBC を使えるようにして、JDBC を JNDI 経由で使えるように、一歩一歩試してきて、ようやく JPAを試す準備がととのった。

sts_jpa01

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&amp;nf=1&amp;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クラスをコピーしてくる。

sts_jpa02

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 に作成する。

sts_jpa03

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 の設定をして、そのテンプレートを元にインスタンスを作成・・・なんて手順を踏めばうまくいくのかなぁと思ったりしているが、未検証。

sts_contextxml

プロジェクトエクスプローラーから 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 を登録する必要がある。

jpa_07

Java Agent とは、Java のバイトコードを操作する、AOPには欠かせない機能

Servers ビューから、サーバーをダブルクリックし、エディタから、Open launch configuration を選択する。

jpa_08

Arguments タブから、VM arguments の Variables…ボタンを押下、開いた画面から、Edit Variables… ボタンを押下。

jpa_09

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} を追記する。

jpa_10

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 を実行

sts_jpa06 

上記を何度か実行。正しく実行されて、登録されたデータが取得できた。

データベースを確認

sts_jpa07

sts_jpa08

sts_jpa09

データも正しくできている。

いじょ。

 

JPA の仕組みについては、この本に非常に丁寧な説明があります。

その他の項目についての説明も非常にわかりやすい良書。

 

Follow me!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です