2014年1月12日日曜日

Jboss7.1.1 とJPA(eclipselink)

Jboss7.1.1 で eclipselink を使ってみた。

(1) eclipselink をダウンロード
% wget http://ftp.yz.yamagata-u.ac.jp/pub/eclipse//rt/eclipselink/releases/2.4.2/eclipselink-2.4.2.v20130514-5956486.zip
% unzip eclipselink-2.4.2.v20130514-5956486.zip

(2) modules の配置
・解凍したファイルより、 eclipselink.jar を以下の配置にコピーする。
% cd ~/jboss7/modules/org
% tree eclipse
eclipse
└── persistence
    └── main
        ├── eclipselink.jar
        ├── eclipselink.jar.index
        └── module.xml

2 directories, 3 files

(3) module.xml
<module xmlns="urn:jboss:module:1.1" name="org.eclipse.persistence">
  <resources>
    <resource-root path="eclipselink.jar"/>
    <!-- Insert resources here -->
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.persistence.api"/>
    <module name="javax.transaction.api"/>
    <module name="javax.validation.api"/>
    <module name="javax.xml.bind.api"/>
    <module name="org.antlr"/>
    <module name="org.apache.commons.collections"/>
    <module name="org.dom4j"/>
    <module name="org.javassist"/>
    <module name="org.jboss.logging"/>
  </dependencies>
</module>
(4) persistence.xml
・最初、JBoss 用の property を記載しなくて、トランザクションが掛からなくてハマりました。
<?xml version="1.0" encoding="UTF-8" ?>
<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_2_0.xsd"
            version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">

    <persistence-unit name="foods" transaction-type="JTA">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <jta-data-source>java:jboss/datasources/MySqlDS</jta-data-source>
        <class>jpa.foods</class>
        <properties>
            <property name="eclipselink.target-server" value="JBoss" />
<!--          
            <property name="eclipselink.logging.level" value="FINEST" />
-->
        </properties>
    </persistence-unit>

</persistence>

jboss7.1.1 と JPA(jhibernate)

jboss7.1.1 上で JPA(hibernate)を使ってみた。

「JPAのエラーは全てRuntimeExceptionのサブクラスなので例外をキャッチする必要はない」というノリでJboss7.1.1 でEJBと組み合わせてJPAを使ってみましたが、なかなか思うようにはいきませんでした。

簡単なWebアプリケーションを作成してみると同一のレコードを更新しあう競合の問題に出会います。この回避策として version 番号による方法が知られていて、JPAでは@VERSIONアノテーションで簡単に実装できます。しかし、この場合の例外もRuntimeException です。

一方、EJBはトランザクションの自動管理をしてくれますが、自動でロールバックしてくれるのは「ロールバックされる例外はRuntimeExceptionと java.rmi.RemoteExceptionのサブクラス」なのだそうです。

EJBのロールバックの条件とJPAの例外発生の方針から、例外処理については何もしないのが正解に思えてしまいます。しかし、アプリケーションによって競合の場合はその旨を知らせて再度オペレーションを促したり、WEB画面にスタックトレースが表示されるのを避けたいことも多いはずです。また、ビジネスロジックでエラーが発生した場合もロールバックさせたいはずです。

EJBで通常のException系のエラーでEJBにロールバックさせるには、例外オブジェクトの定義に@ApplicationException(rollback = true)を記入することで実現できるそうです。今回は競合の発生時にこのユーザ定義エラーにして throw することにしました。競合の検出には、更新処理内で flush() することにより、OptimisticLockExceptionを拾う方法が一般的なようです。

また、予期せぬ例外でWEB画面にスタックトレースが表示される問題は、 EJBExceptionを拾ってメッセージに置き換えることにしました。実際のアプリケーションはどのように作られているのでしょう。

jboss7.1.1 で JPA

$ tree .
.
├── DATA
│   └── create.sh
├── jpatest
│   ├── WEB-INF
│   │   └── classes
│   │       ├── META-INF
│   │       │   ├── persistence.xml -> persistence.xml.hibernate
│   │       │   ├── persistence.xml.eclipselink
│   │       │   ├── persistence.xml.hibernate
│   │       │   └── persistence.xml.openjpa
│   │       └── jpa
│   │           ├── MyException.class
│   │           ├── Srvtest01.class
│   │           ├── VersionException.class
│   │           ├── foods.class
│   │           ├── foodsdao.class
│   │           └── foodsdaoBean.class
│   └── test01.jsp
├── jpatest.war
└── src
    ├── MyException.java
    ├── Srvtest01.java
    ├── build.xml
    ├── foods.java
    ├── foodsdao.java
    └── foodsdaoBean.java

7 directories, 19 files

(1) foods.java
package jpa;

import java.io.Serializable;
import javax.persistence.*;

@Entity
@Table(name="foods")
public class foods implements Serializable {

  public foods(){};

  @Id
  @Column(name="code")
  private String code;

  @Column(name="name")
  private String name;

  @Column(name="price")
  private Integer price;

  @Version
  @Column(name="version")
  private Integer version;

  public String getCode() {
    return code;
  }

  public void setCode(String code) {
    this.code = code;
  }

  public String getName() {
    return name;
  }

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

  public int getPrice() {
    return price;
  }

  public void setPrice(int price) {
    this.price = price;
  }

  public int getVersion() {
    return version;
  }

  public void setVersion(int version) {
    this.version = version;
  }
}

(2) MyException.java
package jpa;

import javax.ejb.ApplicationException;

@ApplicationException(rollback = true)
public class MyException extends Exception {
}

(3) foodsdao.java
package jpa;

import java.util.*;

public interface foodsdao {
  public List allfoods();
  public foods sltfoods(String code);
  public void  updfoods(foods foods) throws MyException;
}

(4) foodsdaoBean.java
package jpa;

import java.util.*;
import javax.ejb.*;
import javax.persistence.*;

@Stateless
@Remote
public class foodsdaoBean implements foodsdao {
  @PersistenceContext(unitName="foods")
  private EntityManager em;

  public List allfoods() {
    Query query = em.createQuery("select p from foods p");
    List foods = query.getResultList();
    return(foods);
  }

  public foods sltfoods(String code) {
    return(em.find(foods.class,code));
  }

  public void updfoods(foods foods) throws MyException {
    try { 
      foods foods1 = em.find(foods.class, foods.getCode());
      foods foods2 = em.merge(foods);
      em.flush();
    }
    catch(OptimisticLockException e){
      throw new MyException();
    }
  }
}

(4) Srvtest01.java
package jpa;

import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
import javax.ejb.*;
import javax.persistence.*;
import jpa.foodsdao;

@WebServlet(name="Srvtest01", urlPatterns={"/Srvtest01"})
public class Srvtest01 extends HttpServlet {
  @EJB
  private foodsdao foodsdao;

  @Override
  public void doGet(HttpServletRequest req,
                    HttpServletResponse res) throws ServletException,IOException {

    res.setContentType("text/html; charset=UTF-8");
    PrintWriter out = res.getWriter();

    String HTML_TEXT = "</head>"
                     + "<body>"
                     + "<form action='Srvtest01' method='post'>"
                     + "<input type='hidden' name='mode' value='select'/>"
                     + "CODE:"
                     + "<input type='text'   name='code'/>"
                     + "<input type='submit' value='検索'/>"
                     + "</form>"
                     + "</body>"
                     + "</html>";
    out.println(HTML_TEXT);
    out.close();
  }

  @Override
  public void doPost(HttpServletRequest req,
                    HttpServletResponse res) throws ServletException,IOException {

    req.setCharacterEncoding("UTF-8");
    res.setContentType("text/html; charset=UTF-8");

    String mode = req.getParameter("mode");
    if (mode.equals("select")) {
      foods foods = null;
      try {
        foods = foodsdao.sltfoods(req.getParameter("code"));
      }
      catch(EJBException e) {
        String HTML_TEXT = "</head>"
                         + "<body>"
                         + "検索に失敗しました(EJBException):"
                         + "<a href='Srvtest01'>戻る</a>"
                         + "</body>"
                         + "</html>";
        PrintWriter out = res.getWriter();
        out.println(HTML_TEXT);
        out.close();
        return;
      }
      if (foods != null) {
        String HTML_TEXT = "</head>"
                         + "<body>"
                         + "<form action='Srvtest01' method='post'>"
                         + "<input type='hidden' name='mode' value='update'/>"
                         + "CODE:%s"
                         + "<input type='hidden' name='code' value='%s'/>"
                         + "NAME:"
                         + "<input type='text' name='name' value='%s'/>"
                         + "PRICE:"
                         + "<input type='text' name='price' value='%d'/>"
                         + "<input type='hidden' name='version' value='%d'/>"
                         + "<p>"
                         + "<a href='Srvtest01'>戻る</a>"
                         + "<input type='submit' value='更新'/>"
                         + "</form>"
                         + "</body>"
                         + "</html>";

        PrintWriter out = res.getWriter();
        out.println(String.format(HTML_TEXT,
                                  foods.getCode(),
                                  foods.getCode(),
                                  foods.getName(),
                                  foods.getPrice(),
                                  foods.getVersion()));
        out.close();
      }
      else {
        String HTML_TEXT = "</head>"
                         + "<body>"
                         + "検索に失敗しました(対象なし):"
                         + "<a href='Srvtest01'>戻る</a>"
                         + "</body>"
                         + "</html>";
        PrintWriter out = res.getWriter();
        out.println(HTML_TEXT);
        out.close();
      }
    }
    else if (mode.equals("update")) {

      foods foods = new foods();
      foods.setCode(req.getParameter("code"));
      foods.setName(req.getParameter("name"));
      foods.setPrice(Integer.parseInt(req.getParameter("price")));
      foods.setVersion(Integer.parseInt(req.getParameter("version")));

      try {
        foodsdao.updfoods(foods);
        String HTML_TEXT = "</head>"
                         + "<body>"
                         + "更新完了しました:"
                         + "<a href='Srvtest01'>戻る</a>"
                         + "</body>"
                         + "</html>";
        PrintWriter out = res.getWriter();
        out.println(HTML_TEXT);
        out.close();
      }
      catch(MyException e) {
        String HTML_TEXT = "</head>"
                         + "<body>"
                         + "更新失敗しました(競合):"
                         + "<a href='Srvtest01'>戻る</a><br />"
                         + "</body>"
                         + "</html>";
        PrintWriter out = res.getWriter();
        out.println(HTML_TEXT);
        out.close();
      }
      catch(EJBException e) {
        String HTML_TEXT = "</head>"
                         + "<body>"
                         + "更新失敗しました(EJBException):"
                         + "<a href='Srvtest01'>戻る</a><br />"
                         + "</body>"
                         + "</html>";
        PrintWriter out = res.getWriter();
        out.println(HTML_TEXT);
        out.close();
      }
    }
  }
}

(5) build.xml
<project name="test01" default="default">
  <property name="hibernate" location="/home/ユーザ/jboss7/modules" />

  <target name="default">
    <javac srcdir="." destdir="../jpatest/WEB-INF/classes" >
      <classpath>
        <pathelement path="${hibernate}/javax/persistence/api/main/hibernate-jpa-2.0-api-1.0.1.Final.jar" />
        <pathelement path="${hibernate}/javax/ejb/api/main/jboss-ejb-api_3.1_spec-1.0.1.Final.jar" />
        <pathelement path="${hibernate}/javax/servlet/api/main/jboss-servlet-api_3.0_spec-1.0.0.Final.jar" />
      </classpath>
    </javac>
  </target>
</project>

(6) persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<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_2_0.xsd"
            version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">

    <persistence-unit name="foods" transaction-type="JTA">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:jboss/datasources/MySqlDS</jta-data-source>
        <class>jpa.foods</class>
    </persistence-unit>

</persistence>

(7) create.sh
#! /bin/sh

mysql -uユーザ -ppassword sampledb <<EOF
drop table if exists foods;
create table foods(
  code    varchar(8) primary key, 
  name    varchar(100),
  price   integer,
  version integer
) engine=InnoDB;
 
insert into foods values('000001','りんご',398,0);
insert into foods values('000002','みかん',480,0);
insert into foods values('000003','柿'    ,450,0);

select * from foods;
EOF

(8) コンパイルと配備
% cd src
% ant
% cd ../jpatest
% jar cvf ../jpatest.war .
% rm ~/jboss7/standalone/deployments/jpa*
% cp ../jpatest.war ~/jboss7/standalone/deployments
% cd ../DATA
% ./create.sh
(9) 実行確認

2014年1月2日木曜日

jboss7.1.1 で EJB をJSP,SERVLET,CLIENTから呼び出す。

せっかく、JBoss7.1.1 をインストールしたので、EJBを試してみる。EJBの利点の一つに作成したモジュールの再利用性にあるらしい。WEB用に作ったEJBモジュールをクライアントプログラムからも呼び出し確認してみる。

JBoss7.1.1 で EJB !


(1) ディレクトリ構成
$ tree .
.
├── ejbclient
│   ├── ejbclient.class
│   ├── ejbtest
│   │   └── Greeting.class
│   └── testrun.sh
├── ejbtest
│   ├── WEB-INF
│   │   └── classes
│   │       ├── Srvtest01.class
│   │       └── ejbtest
│   │           ├── Greeting.class
│   │           └── GreetingBean.class
│   └── test01.jsp
├── srcclient
│   ├── Greeting.java
│   ├── build.xml
│   └── ejbclient.java
└── srcejb
    ├── Greeting.java
    ├── GreetingBean.java
    ├── Srvtest01.java
    └── build.xml

8 directories, 14 files

(1) EJB モジュール

・Greeting インタフェース
package ejbtest;

import javax.ejb.Remote;

@Remote
public interface Greeting {
   public String SayHello();
}

・GreetingBean
package ejbtest;

import javax.ejb.Stateless;

@Stateless
public class GreetingBean implements Greeting {

   public GreetingBean() {
   }

   public String SayHello() {
      return("Hello EJB World!");
   }
}

(2) EJBテスト用サーブレットと build.xml

・テスト用サーブレット
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
import javax.ejb.EJB;
import ejbtest.Greeting;

@WebServlet(name="Srvtest01", urlPatterns={"/Srvtest01"})
public class Srvtest01 extends HttpServlet {
  @EJB
  private Greeting greeting;

  @Override
  public void doGet(HttpServletRequest req,
                    HttpServletResponse res) throws ServletException,IOException {

    res.setContentType("text/html; charset=UTF-8");
    PrintWriter out = res.getWriter();

    out.println("");
    out.println("");
    out.println(greeting.SayHello());
    out.println("");
    out.println("");

    out.close();
  }
}

・build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="ejbtest" default="main" basedir=".">

   <property name="build.classes" location="../ejbtest/WEB-INF/classes" />
   <property name="jboss" location="/home/ユーザ/jboss7/modules" />
   <target name="main" depends="compile" />

   <target name="compile">
      <mkdir dir="${build.classes}" />
      <javac destdir="${build.classes}" srcdir=".">
        <classpath>
          <pathelement path="${jboss}/javax/servlet/api/main/jboss-servlet-api_3.0_spec-1.0.0.Final.jar" />
          <pathelement path="${jboss}/javax/transaction/api/main/jboss-transaction-api_1.1_spec-1.0.0.Final.jar" />
          <pathelement path="${jboss}/javax/ejb/api/main/jboss-ejb-api_3.1_spec-1.0.1.Final.jar" />
          <pathelement path="${jboss}/javax/inject/api/main/javax.inject-1.jar" />
        </classpath>
      </javac>
   </target>
</project>

(3) EJB パッケージを配備して動作確認
$ cd srcejb
$ ant
$ cd ../ejbtest
$ jar cvf ../ejbtest.war .
$ cp ../ejbtest.war ~/jboss7/standalone/deployments

(4) JSP からEJBの呼び出し

JSP は@EJBアノテーションは使えないので、jndi名のlookupとする。
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="javax.naming.Context,javax.naming.InitialContext"%>
<%@ page import="ejbtest.Greeting"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSPテスト</title>
</head>

<body>
<%
   Context ctx=new InitialContext();
   Greeting greeting =
      (Greeting) ctx.lookup("java:app/ejbtest/GreetingBean!ejbtest.Greeting");

   out.println(greeting.SayHello());
%>
</body>
</html>

・再配備と実行
$ cd ejbtest
$ jar cvf ../ejbtest.war .
$ rm -rf ~/jboss7/standalone/deployments/ejbtest.*
$ cp ../ejbtest.war ~/jboss7/standalone/deployments

(5) client よりEJBの呼び出し

jndi名がなぜ、JSPの場合(java:global/ejbtest/GreetingBean!ejbtest.Greeting)で、クライント呼び出しの場合(ejbtest/GreetingBean!ejbtest.Greeting)と java:global/ を付けるとうまくいかないのか謎のままである。
・クライアントソース
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import ejbtest.Greeting;

public class ejbclient {

  public static void main(String[] args) {
    try {
      InitialContext initialContext = getInitialContext();

      Greeting greeting =
      (Greeting) initialContext.lookup("ejbtest/GreetingBean!ejbtest.Greeting");

      System.out.println(greeting.SayHello());

    } catch (NamingException e) {
      e.printStackTrace();
    }
  }

  private static InitialContext getInitialContext() throws NamingException{

    Properties properties = new Properties();

    properties.put(Context.INITIAL_CONTEXT_FACTORY,"org.jboss.naming.remote.client.InitialContextFactory");
    properties.put(Context.PROVIDER_URL, "remote://localhost:4447");
    properties.put("jboss.naming.client.ejb.context", true);
    properties.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
    properties.put(Context.SECURITY_PRINCIPAL, "testuser");
    properties.put(Context.SECURITY_CREDENTIALS, "testpassword");

    return new javax.naming.InitialContext(properties);
  }
}

・build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="ejbtest" default="main" basedir=".">

   <property name="jboss" location="/home/ユーザ/jboss7/modules" />
   <target name="main" depends="compile" />

   <target name="compile">
      <javac destdir="../ejbclient" srcdir=".">
        <classpath>
          <pathelement path="${jboss}/javax/servlet/api/main/jboss-servlet-api_3.0_spec-1.0.0.Final.jar" />
          <pathelement path="${jboss}/javax/transaction/api/main/jboss-transaction-api_1.1_spec-1.0.0.Final.jar" />
          <pathelement path="${jboss}/javax/ejb/api/main/jboss-ejb-api_3.1_spec-1.0.1.Final.jar" />
          <pathelement path="${jboss}/javax/inject/api/main/javax.inject-1.jar" />
        </classpath>
      </javac>
   </target>
</project>

・テストユーザを作成
$ ./add-user.sh

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): b

Enter the details of the new user to add.
Realm (ApplicationRealm) : 
Username : testuser
Password : 
Re-enter Password : 
What roles do you want this user to belong to? (Please enter a comma separated list, or leave blank for none) : 
About to add user 'testuser' for realm 'ApplicationRealm'
Is this correct yes/no? yes
Added user 'testuser' to file '/home/ユーザ/jboss7/standalone/configuration/application-users.properties'
Added user 'testuser' to file '/home/ユーザ/jboss7/domain/configuration/application-users.properties'

・コンパイルと実行
$ cd srcclient
$ ant
$ cd ../ejbclient
$ java -classpath $HOME/jboss7/bin/client/jboss-client.jar:. ejbclient
1 02, 2014 4:24:58 午後 org.xnio.Xnio <clinit>
INFO: XNIO Version 3.0.3.GA
1 02, 2014 4:24:58 午後 org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.0.3.GA
1 02, 2014 4:24:58 午後 org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 3.2.3.GA
1 02, 2014 4:24:58 午後 org.jboss.ejb.client.remoting.VersionReceiver handleMessage
INFO: Received server version 1 and marshalling strategies [river]
1 02, 2014 4:24:58 午後 org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
INFO: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@3e350808, receiver=Remoting connection EJB receiver [connection=Remoting connection <2befdb3>,channel=jboss.ejb,nodename=naomusi]} on channel Channel ID d65d49fe (outbound) of Remoting connection 395e4ad9 to localhost/127.0.0.1:4447
1 02, 2014 4:24:58 午後 org.jboss.ejb.client.remoting.ChannelAssociation$ResponseReceiver handleMessage
WARN: Unsupported message received with header 0xffffffff
Hello EJB World!


2014年1月1日水曜日

JPAを試す(hibernate,eclipselink,openjpa)

JPA といっても規格であってその実装は色々とあるようです。hibernate,openjpa,eclipselink などなど。eclipselink に至っては、eclipse のプラグインか何かだという固定観念が抜けず、JPAだと理解するまでに時間が掛かりました。

JPA関連のクラスを使って例外処理を特に記述しなくてもコンパイルエラーで叱られない理由が良くわからなかったのですが、「JPAの例外はすべてRuntimeExceptionのサブクラスになっています」らしく、「RuntimeExceptionとその子孫クラスは、Java APIがプログラムのバグを検出したときにスローされる特殊な例外であり、キャッチする必要はありませんし、故にJava文法上でもthrows句に列挙しなくてよいことになっています」ということで納得しました。

また、今回からビルドは ant を使ってみることにしました。ツールなのでまずは楽できること主眼として最低限の記載ですませています。後は、実行時に persistence.xml を切り替えることでJPA製品を切り替えて起動確認しています。

JPAを試す(hibernate,eclipse,openjpa)


(1) ディレクトリ構成
$ tree .
.
├── jpatest
│   ├── META-INF
│   │   ├── persistence.xml -> persistence.xml.hibernate
│   │   ├── persistence.xml.eclipselink
│   │   ├── persistence.xml.hibernate
│   │   └── persistence.xml.openjpa
│   ├── foods.class
│   ├── test01.class
│   └── testrun.sh
└── src
    ├── build.xml
    ├── foods.java
    └── test01.java

3 directories, 10 files

(2) foods.java
import java.util.Date;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="foods")
public class foods implements Serializable {

  public foods(){};

  @Id
  @Column(name="code")
  private String code;

  @Column(name="name")
  private String name;

  @Column(name="price")
  private Integer price;

  public String getCode() {
    return code;
  }

  public void setCode(String code) {
    this.code = code;
  }

  public String getName() {
    return name;
  }

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

  public int getPrice() {
    return price;
  }

  public void setPrice(int price) {
    this.price = price;
  }
}

(3) test01.java
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.EntityManager;

public class test01 {
  public static void main(String args[]) {
    EntityManagerFactory emf =
    Persistence.createEntityManagerFactory("foods");
    EntityManager em = emf.createEntityManager();

    EntityTransaction tran = em.getTransaction();
    tran.begin();

    foods foods1 = em.find(foods.class, "000001");

    System.out.println("コード(変更前):" + foods1.getCode());
    System.out.println("名前(変更前):"   + foods1.getName());
    System.out.println("価格(変更前):"   + foods1.getPrice());

    foods1.setPrice(800);

    foods foods2 = new foods();
    foods2.setCode("000004");
    foods2.setName("巨峰");
    foods2.setPrice(1000);

    em.persist(foods2);

    tran.commit();
    em.close();
    emf.close();
  }
}

(4) build.xml
<?xml version="1.0"?>
<project name="test01" default="default">
  <property name="hibernate" location="/home/naoki/work/java/lib/hibernate/lib" />

  <target name="default">
    <javac srcdir="." destdir="../jpatest" >
      <classpath>
        <pathelement path="${hibernate}/jpa/hibernate-entitymanager-4.3.0.Final.jar" />
        <pathelement path="${hibernate}/required/hibernate-jpa-2.1-api-1.0.0.Final.jar" />
      </classpath>
    </javac>
  </target>
</project>

(5) persistence.xml
・hibernate用
<?xml version="1.0" encoding="UTF-8"?>
<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="foods" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.user" value="naoki" />
            <property name="javax.persistence.jdbc.password" value="naoki" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/sampledb" />
<!--
            <property name="hibernate.show_sql" value="true" />
-->
        </properties>
    </persistence-unit>
</persistence>

・eclipselink用
<?xml version="1.0" encoding="UTF-8" ?>
<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_2_0.xsd"
            version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">

    <persistence-unit name="foods" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>foods</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/sampledb" />
            <property name="javax.persistence.jdbc.user" value="naoki" />
            <property name="javax.persistence.jdbc.password" value="naoki" />

            <!-- EclipseLink should create the database schema automatically -->
<!--            <property name="eclipselink.ddl-generation" value="create-tables" /> -->
<!--            <property name="eclipselink.ddl-generation.output-mode" value="database" />  -->
        </properties>
    </persistence-unit>

</persistence>

・openjpa用
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
    <!-- For DB connectivity -->
    <persistence-unit name="foods" transaction-type="RESOURCE_LOCAL">
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <class>foods</class>
        <properties>
            <property name="openjpa.ConnectionURL" value="jdbc:mysql://localhost:3306/sampledb" />
            <property name="openjpa.ConnectionDriverName" value="com.mysql.jdbc.Driver" />
            <property name="openjpa.ConnectionUserName" value="naoki" />
            <property name="openjpa.ConnectionPassword" value="naoki" />
            <property name="openjpa.jdbc.DBDictionary" value="mysql(SimulateLocking=true)" />
            <property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO" />
        </properties>
    </persistence-unit>
</persistence>

(5) コンパイル
% cd src
% ant

(6) 実行シェル
#! /bin/sh 

HIBERNATE_LIST="
/home/naoki/work/java/lib/hibernate/lib/required/hibernate-jpa-2.1-api-1.0.0.Final.jar
/home/naoki/work/java/lib/hibernate/lib/jpa/hibernate-entitymanager-4.3.0.Final.jar
/home/naoki/work/java/lib/hibernate/lib/required/jboss-logging-3.1.3.GA.jar
/home/naoki/work/java/lib/hibernate/lib/required/hibernate-core-4.3.0.Final.jar
/home/naoki/work/java/lib/hibernate/lib/required/jboss-transaction-api_1.2_spec-1.0.0.Final.jar
/home/naoki/work/java/lib/hibernate/lib/required/jandex-1.1.0.Final.jar
/home/naoki/work/java/lib/hibernate/lib/required/javassist-3.18.1-GA.jar
/home/naoki/work/java/lib/hibernate/lib/required/dom4j-1.6.1.jar
/home/naoki/work/java/lib/hibernate/lib/required/hibernate-commons-annotations-4.0.4.Final.jar
/home/naoki/work/java/lib/hibernate/lib/required/antlr-2.7.7.jar
/home/naoki/ダウンロード/mysql-connector-java-5.1.27/mysql-connector-java-5.1.27-bin.jar
.
"

ECLIPSELINK_LIST="
/home/naoki/work/java/lib/eclipselink/utils/dbws/org.eclipse.persistence.oracleddlparser.source_1.0.0.v20121122.jar
/home/naoki/work/java/lib/eclipselink/utils/dbws/javax.wsdl_1.6.2.v201012040545.jar
/home/naoki/work/java/lib/eclipselink/utils/dbws/eclipselink-dbwsutils.jar
/home/naoki/work/java/lib/eclipselink/utils/dbws/javax.servlet_2.4.0.v200806031604.jar
/home/naoki/work/java/lib/eclipselink/utils/dbws/org.eclipse.persistence.oracleddlparser_1.0.0.v20121122.jar
/home/naoki/work/java/lib/eclipselink/utils/rename/package-rename.jar
/home/naoki/work/java/lib/eclipselink/utils/workbench/jlib/xercesImpl.jar
/home/naoki/work/java/lib/eclipselink/utils/workbench/jlib/connector.jar
/home/naoki/work/java/lib/eclipselink/utils/workbench/jlib/elmwcore.jar
/home/naoki/work/java/lib/eclipselink/utils/workbench/jlib/eclipselinkmw.jar
/home/naoki/work/java/lib/eclipselink/jlib/jpa/eclipselink-jpa-modelgen_2.4.2.v20130514-5956486.jar
/home/naoki/work/java/lib/eclipselink/jlib/jpa/javax.persistence_2.0.5.v201212031355.jar
/home/naoki/work/java/lib/eclipselink/jlib/jpa/javax.persistence_1.0.0.jar
/home/naoki/work/java/lib/eclipselink/jlib/jpa/org.eclipse.persistence.jpars_2.4.2.v20130514-5956486.jar
/home/naoki/work/java/lib/eclipselink/jlib/jpa/org.eclipse.persistence.jpars.source_2.4.2.v20130514-5956486.jar
/home/naoki/work/java/lib/eclipselink/jlib/eclipselink.jar
/home/naoki/work/java/lib/eclipselink/jlib/sdo/commonj.sdo_2.1.1.v201112051852.jar
/home/naoki/work/java/lib/eclipselink/jlib/moxy/com.sun.tools.xjc_2.2.0.jar
/home/naoki/work/java/lib/eclipselink/jlib/moxy/javax.xml.bind_2.2.0.v201105210648.jar
/home/naoki/work/java/lib/eclipselink/jlib/moxy/javax.mail_1.4.0.v201005080615.jar
/home/naoki/work/java/lib/eclipselink/jlib/moxy/javax.activation_1.1.0.v201108011116.jar
/home/naoki/work/java/lib/eclipselink/jlib/moxy/com.sun.xml.bind_2.2.0.v201004141950.jar
/home/naoki/work/java/lib/eclipselink/jlib/moxy/javax.xml.stream_1.0.1.v201004272200.jar
/home/naoki/ダウンロード/mysql-connector-java-5.1.27/mysql-connector-java-5.1.27-bin.jar
. 
"

OPENJPA_LIST="
/home/naoki/work/java/lib/openjpa/lib/geronimo-jpa_2.0_spec-1.1.jar
/home/naoki/work/java/lib/openjpa/lib/org.apache.bval.bundle-0.3-incubating.jar
/home/naoki/work/java/lib/openjpa/lib/geronimo-jms_1.1_spec-1.1.1.jar
/home/naoki/work/java/lib/openjpa/lib/commons-beanutils-1.8.3.jar
/home/naoki/work/java/lib/openjpa/lib/commons-logging-1.0.4.jar
/home/naoki/work/java/lib/openjpa/lib/geronimo-jta_1.1_spec-1.1.1.jar
/home/naoki/work/java/lib/openjpa/lib/serp-1.14.1.jar
/home/naoki/work/java/lib/openjpa/lib/geronimo-validation_1.0_spec-1.1.jar
/home/naoki/work/java/lib/openjpa/lib/asm-3.2.jar
/home/naoki/work/java/lib/openjpa/lib/derby-10.8.2.2.jar
/home/naoki/work/java/lib/openjpa/lib/commons-dbcp-1.4.jar
/home/naoki/work/java/lib/openjpa/lib/commons-pool-1.5.4.jar
/home/naoki/work/java/lib/openjpa/lib/commons-lang-2.4.jar
/home/naoki/work/java/lib/openjpa/lib/commons-collections-3.2.1.jar
/home/naoki/work/java/lib/openjpa/openjpa-all-2.2.2.jar
/home/naoki/ダウンロード/mysql-connector-java-5.1.27/mysql-connector-java-5.1.27-bin.jar
.
"

#
rm META-INF/persistence.xml
(cd META-INF;ln -s persistence.xml.hibernate persistence.xml)
CLASSPATH=`echo $HIBERNATE_LIST | sed 's/ /:/g'`
java -cp $CLASSPATH test01

#
#rm META-INF/persistence.xml
#(cd META-INF;ln -s persistence.xml.eclipselink persistence.xml)
#CLASSPATH=`echo $ECLIPSELINK_LIST | sed 's/ /:/g'`
#java -cp $CLASSPATH test01

#
#rm META-INF/persistence.xml
#(cd META-INF;ln -s persistence.xml.openjpa persistence.xml)
#CLASSPATH=`echo $OPENJPA_LIST | sed 's/ /:/g'`
#java -cp $CLASSPATH test01

(7) 実行
$ cd jpatest
$ ./testrun.sh
1 01, 2014 2:01:21 午前 org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead.
1 01, 2014 2:01:21 午前 org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead.
1 01, 2014 2:01:21 午前 org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead.
1 01, 2014 2:01:21 午前 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
 name: foods
 ...]
1 01, 2014 2:01:21 午前 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.3.0.Final}
1 01, 2014 2:01:21 午前 org.hibernate.cfg.Environment 
INFO: HHH000206: hibernate.properties not found
1 01, 2014 2:01:21 午前 org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
1 01, 2014 2:01:21 午前 org.hibernate.annotations.common.reflection.java.JavaReflectionManager 
INFO: HCANN000001: Hibernate Commons Annotations {4.0.4.Final}
1 01, 2014 2:01:21 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
1 01, 2014 2:01:21 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/sampledb]
1 01, 2014 2:01:21 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000046: Connection properties: {user=naoki, password=****}
1 01, 2014 2:01:21 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000006: Autocommit mode: false
1 01, 2014 2:01:21 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
1 01, 2014 2:01:22 午前 org.hibernate.dialect.Dialect 
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
1 01, 2014 2:01:22 午前 org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory 
INFO: HHH000397: Using ASTQueryTranslatorFactory
コード(変更前):000001
名前(変更前):りんご(袋詰)
価格(変更前):800
1 01, 2014 2:01:22 午前 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/sampledb]