Archive for 4月, 2010

Oracle Welcome Event の資料公開

先日開催された Oracle Welcome Event の資料が公開されたようです。
(こちらから入手)
Oracle のサイトにログイン後、各資料を入手してください。

2010年4月27日 at 2:37 午後

EJB 3.1 の組み込み可能コンテナ

前回のエントリで、EJB 3.1 の新機能概要を紹介しました。今回は EJB 3.1 の新機能の一つである組み込みコンテナを使用し、EJB の単体テストをかんたんに行う方法を紹介します。

ここでは NetBeans 6.8 と GlassFish v3 を利用して説明しますが、NetBeans 6.8 で「 EJB モジュール」のプロジェクトを作成するとデフォルトで GlassFish v3 の組み込みコンテナが利用できるようになっています。ですので、特に面倒な設定は必要なく JUnit のテストコードの記述に集中できるようになっています。今までは EJB の単体テストを行うためにデプロイして Global JNDI 名を利用してテストを行ったりしていたかと思いますが、この方法を利用すると直接アプリケーションサーバにデプロイしなくてもすむため非常に便利です。

それでは実際に EJB 3.1 の組み込みコンテナを利用してみましょう。
NetBeans 6.8 を使用して「EJB モジュール」の新規プロジェクトを作成します。メニューより「新規プロジェクト」を選択してください。すると下記のウィンドウが表示されます。ここで、「Java EE」→「EJB モジュール」を選択し「次へ>」ボタンを押下します。

次に、プロジェクト名、プロジェクトの場所、プロジェクトフォルダ等の項目を入力し「次へ>」ボタンを押下します。

次に、この EJB モジュールを配備するアプリケーションサーバの選択(GlassFish v3)、Java EE のバージョン(Java EE 6)を選択し「完了(F)」ボタンを押下します。

「完了(F)」ボタンを押下すると、NetBeans のプロジェクトに下記のようなプロジェクトが作成されます。ここでプロジェクトのツリーを展開すると「ソースパッケージ」、「テストパッケージ」、「ライブラリ」、「テストライブラリ」等が表示されます。この中で「テストライブラリ」のツリーに注目してください。「GlassFish v3 (埋め込み可能コンテナ)」→「glassfish-embedded-static-shell.jar」というファイルが組み込まれている事を確認できます。このライブラリはとても重要で、これを利用しEJB の組み込みコンテナを利用できるようになります。

それでは、実際に EJB コンポーネントを作成してみましょう。今回はかんたんなサンプルとして、ステートレス Session Bean を1つ作成します。

実装コードは下記を記載します。

EJB コンポーネントを作成しましたので、次に JUnit のテストコードを記載します。NetBeans のメニューより、「新規」→「JUnit テスト …」を選択してください。メニュー中に表示されない場合は、「その他…」を選択した後「JUnit テスト …」を選択してください。

JUnit のテスト用コードを記述するクラス名、パッケージ等を定義します。

次に、JUnit のバージョンを選択し、「選択(S)」ボタンを押下します。

クラスの雛形が自動生成されますので、テスト用のメソッドを実装します。

記載するコードを下記に記載します。

@Test
    public void testSayHello() {
        Map p = new HashMap();
        p.put("<strong>org.glassfish.ejb.embedded.glassfish.instance.root</strong>",
                "<strong>/Applications/GlassFish/glassfishv3-webprofile/glassfish/domains/domain1</strong>");
        <span style="color:blue;"><strong>EJBContainer container = EJBContainer.createEJBContainer(p);</strong></span>
        try{
            Hello hello = (Hello)<strong>container.getContext().lookup("java:global/classes/Hello");</strong>
            System.out.println(hello.sayHello());
        }catch(Exception e){
           e.printStackTrace();
        }
       container.close();
   }

このコード中で重要なポイントは、EJBContainer.createEJBContainer()を呼び出す所です。EJBContainer クラスは、組み込み可能な EJB コンテナ上で EJB コンポーネントを実行するためのクラスです。スタティックメソッドの createEJBContainer() を実行すると組み込み可能 EJB コンテナのインスタンスの作成と初期化を行います。引数 Map 中で設定プロパティを記載する事ができます。
今回、GlassFish の組み込み可能コンテナを利用しますので、org.glassfish.ejb.embedded.glassfish.instance.root に GlassFish のインスタンスのルート(通常 DAS のドメインの場所)を指定して初期化と起動を行います。EJB コンテナの初期化が完了した後、JNDI のルックアップで EJB コンポーネントを取得し適宜評価プログラムを記載することができるようになります。

テスト用のコードを記載した後、テストを実行してみましょう。プロジェクトのメニューより「テスト」を選択実行してください。

テストを実行すると下記のようなテスト結果が得られます。

さて、上記でかんたんなテストは完了しましたが、もう一つ JPA-EJB を使った DB連携の EJBコンポーネントを作成しテストをしてみましょう。まず、メニューより「新規」→「データベースからのエンティティクラス…」を選択して Entity Bean を作成しましょう。

データソース(jdbc/sample)を選択し、「使用可能な表(T)」から DB 上に存在するテーブルを選択し、追加します。ここでは自分で作成した Person テービルを利用しています。

次に、ワーニングが表示されている箇所に注目してください。現在のプロジェクトには持続性ユニットが存在しないため作成してくださいとメッセージが表示されています。そこで「持続性ユニットを作成…」ボタンを押下して作成します。

「持続性ユニット名」、「持続性プロバイダ」、「データソース」を選択し「作成」ボタンを押下します。

持続性ユニットを作成すると、ワーニングが消えますので「次へ>」ボタンを押下します。

最後に、データベースのテーブルと Entity Bean のマッピングに関する設定を行い「完了(F)」ボタンを押下します。

上記で、DB のPERSON テーブルから、ファイル名 Person.java の Entity Bean が自動生成されます。自動生成されるコードは下記のようなコードです。

package test;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name = "PERSON")
@NamedQueries({
    @NamedQuery(name = "Person.findAll", query = "SELECT p FROM Person p"),
    @NamedQuery(name = "Person.findById", query = "SELECT p FROM Person p WHERE p.id = :id"),
    @NamedQuery(name = "Person.findByName", query = "SELECT p FROM Person p WHERE p.name = :name"),
    @NamedQuery(name = "Person.findByAge", query = "SELECT p FROM Person p WHERE p.age = :age"),
    @NamedQuery(name = "Person.findByAddress1", query = "SELECT p FROM Person p WHERE p.address1 = :address1"),
    @NamedQuery(name = "Person.findByTelephone", query = "SELECT p FROM Person p WHERE p.telephone = :telephone")})
public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "ID")
    private Long id;
    @Basic(optional = false)
    @Column(name = "NAME")
    private String name;
    @Basic(optional = false)
   @Column(name = "AGE")
    private long age;
    @Basic(optional = false)
    @Column(name = "ADDRESS1")
    private String address1;
    @Basic(optional = false)
    @Column(name = "TELEPHONE")
    private String telephone;
    public Person() {
    }
    public Person(Long id) {
        this.id = id;
    }

    public Person(Long id, String name, long age, String address1, String telephone) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address1 = address1;
        this.telephone = telephone;
    }
    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 long getAge() {
       return age;
    }

    public void setAge(long age) {
        this.age = age;
    }

    public String getAddress1() {
        return address1;
    }

    public void setAddress1(String address1) {
        this.address1 = address1;
    }

    public String getTelephone() {
        return telephone;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

     @Override
     public boolean equals(Object object) {
         // TODO: Warning - this method won't work in the case the id fields are not set
         if (!(object instanceof Person)) {
             return false;
         }
         Person other = (Person) object;
         if ((this.id == null &amp;&amp; other.id != null) || (this.id != null &amp;&amp; !this.id.equals(other.id))) {
             return false;
         }
         return true;
     }

     @Override
     public String toString() {
         return "test.Person[id=" + id + "]";
     }

}

Entity Bean を作成しましたのでこの Entity Bean を扱う Session Bean を作成します。メニューより「セッション Bean…」を 選択してください。

今回はステートレス Session Bean である PersonController を作成します。「EJB 名(N)」、「プロジェクト名」、「場所」、「パッケージ」、「セッションのタイプ」、「インタフェースを作成」それぞれを入力/選択し「完了(F)」ボタンを押下します。

PersonController には下記のコードを記載してください。

package test;
import javax.ejb.Stateless;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
public class PersonController {

     @PersistenceContext(unitName="EmbeddedEJBPU")
     private EntityManager em;
     public List&lt;Person&gt; findAllPersons(){
         //Entity(Person)クラスの @NamedQuery の記載に基づき検索
         Query query = em.createNamedQuery("Person.findAll");
         return (List&lt;Person&gt;) query.getResultList();
     }
     public Person createPerson(Person person){
         em.persist(person);
         return person;
     }
}

Session Bean を作成しましたので、最後に JUnit 用のテストコードを記述してみましょう。先ほどと同様に EJBContainer.creaateEJBContainer() を呼び出し、PersonContoroller のオブジェクトを取得した後、PersonController オブジェクト内のメソッドを呼び出してみましょう。

ここで、実装するコードは下記のようになります。

@Test
public void testGetPersonCount() {
       Map p = new HashMap();
       p.put("org.glassfish.ejb.embedded.glassfish.instance.root",
               "/Applications/GlassFish/glassfishv3-webprofile/glassfish/domains/domain1");
       EJBContainer container = EJBContainer.createEJBContainer(p);
       try{
           PersonController pController = (PersonController)container.getContext().
                   lookup("java:global/classes/PersonController");
            List persons = pController.findAllPersons();
           Iterator iterator = persons.iterator();
            while (iterator.hasNext()) {
                Person person = (Person)iterator.next();
                System.out.println("Person name: " + person.getName());
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }

テストコードを記載した後、「テスト」を実行してください。

先ほどと同様、DB アクセス後、テーブルを参照し情報を取得する事も問題なくできました。後は個々のアプリケーションに応じて詳細なテストコードを記載して頂くことができます。

以上のように EJB 3.1 で提供された組み込みコンテナ機能を使うと EJB コンポーネントに対する単体テストがとてもかんたんになります。今後はこの組み込みコンテナの機能を使い EJB のテストを楽に行ってください。

※補足:
実行した際に吐き出されるコンソールのメッセージを下記に表示します。

2010/04/08 17:01:13 com.sun.enterprise.transaction.JavaEETransactionManagerSimplified initDelegates
情報: Using com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate as the delegate
2010/04/08 17:01:13 com.sun.common.util.logging.LoggingConfigImpl openPropFile
情報: Cannot read logging.properties file.
2010/04/08 17:01:13 com.sun.enterprise.web.WebContainer configureHost
致命的: WEB0355: network-listener [http-listener-2] referenced by virtual server [server] does not exist
2010/04/08 17:01:13 com.sun.enterprise.web.WebContainer configureHost
致命的: WEB0355: network-listener [http-listener-1] referenced by virtual server [server] does not exist
2010/04/08 17:01:13 com.sun.enterprise.web.WebContainer createHosts
情報: Created virtual server server
2010/04/08 17:01:13 com.sun.enterprise.web.WebContainer configureHost
致命的: WEB0355: network-listener [admin-listener] referenced by virtual server [__asadmin] does not exist
2010/04/08 17:01:13 com.sun.enterprise.web.WebContainer createHosts
情報: Created virtual server __asadmin
2010/04/08 17:01:14 com.sun.enterprise.web.WebContainer loadSystemDefaultWebModules
情報: Virtual server server loaded system default web module
2010/04/08 17:01:17 com.sun.enterprise.v3.services.impl.WebContainerStarter startWebContainer
情報: Done with starting web container
2010/04/08 17:01:17 com.sun.enterprise.v3.server.AppServerStartup run
情報: GlassFish v3 (74.2) startup time : Embedded(1291ms) startup services(4389ms) total(5680ms)
2010/04/08 17:01:17 org.glassfish.admin.mbeanserver.JMXStartupService$JMXConnectorsStarterThread run
情報: JMXStartupService: JMXConnector system is disabled, skipping.
2010/04/08 17:01:17 AppServerStartup run
情報: [Thread[GlassFish Kernel Main Thread,5,main]] started
2010/04/08 17:01:19 com.sun.enterprise.security.SecurityLifecycle &lt;init&gt;
情報: security.secmgroff
2010/04/08 17:01:19 com.sun.enterprise.security.ssl.SSLUtils checkCertificateDates
致命的: java_security.expired_certificate
2010/04/08 17:01:19 com.sun.enterprise.security.SecurityLifecycle onInitialization
情報: Security startup service called
2010/04/08 17:01:19 com.sun.enterprise.security.PolicyLoader loadPolicy
情報: policy.loading
2010/04/08 17:01:19 com.sun.enterprise.security.auth.realm.Realm doInstantiate
情報: Realm admin-realm of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
2010/04/08 17:01:19 com.sun.enterprise.security.auth.realm.Realm doInstantiate
情報: Realm file of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
2010/04/08 17:01:19 com.sun.enterprise.security.auth.realm.Realm doInstantiate
情報: Realm certificate of classtype com.sun.enterprise.security.auth.realm.certificate.CertificateRealm successfully created.
2010/04/08 17:01:19 com.sun.enterprise.security.SecurityLifecycle onInitialization
情報: Security service(s) started successfully….
2010/04/08 17:01:20 org.hibernate.validator.util.Version &lt;clinit&gt;
情報: Hibernate Validator bean-validator-3.0-JBoss-4.0.2
2010/04/08 17:01:20 org.hibernate.validator.engine.resolver.DefaultTraversableResolver detectJPA
情報: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
2010/04/08 17:01:21 com.sun.ejb.containers.BaseContainer initializeHome
情報: Portable JNDI names for EJB PersonController : [java:global/classes/PersonController, java:global/classes/PersonController!test.PersonController]
2010/04/08 17:01:21 com.sun.ejb.containers.BaseContainer initializeHome
情報: Portable JNDI names for EJB Hello : [java:global/classes/Hello, java:global/classes/Hello!test.Hello]
2010/04/08 17:01:21 org.jboss.weld.bootstrap.WeldBootstrap &lt;clinit&gt;
情報: WELD-000900 1.0.0 (SP4)
2010/04/08 17:01:21 org.hibernate.validator.engine.resolver.DefaultTraversableResolver detectJPA
情報: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
nullID: /Users/yt133043/NetBeansProjects/EmbeddedableTest/build/classes/ CLASSES: [class test.exceptions.IllegalOrphanException, class test.exceptions.NonexistentEntityException, class test.exceptions.PreexistingEntityException, class test.exceptions.RollbackFailureException, class test.Hello, class test.Person, class test.PersonController]

Hello Embedded TEST
2010/04/08 17:01:22 com.sun.enterprise.connectors.service.ResourceAdapterAdminServiceImpl sendStopToResourceAdapter
情報: ra.stop-successful
2010/04/08 17:01:22 org.glassfish.admin.mbeanserver.JMXStartupService shutdown
情報: JMXStartupService and JMXConnectors have been shut down.
2010/04/08 17:01:22 com.sun.enterprise.v3.server.AppServerStartup stop
情報: Shutdown procedure finished
2010/04/08 17:01:22 AppServerStartup run
情報: [Thread[GlassFish Kernel Main Thread,5,main]] exiting
2010/04/08 17:01:23 com.sun.enterprise.module.impl.HK2Factory initialize
警告: Singleton already initialized as com.sun.enterprise.module.impl.HK2Factory@66e1f8f
2010/04/08 17:01:23 com.sun.enterprise.transaction.JavaEETransactionManagerSimplified initDelegates
情報: Using com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate as the delegate
2010/04/08 17:01:23 com.sun.common.util.logging.LoggingConfigImpl openPropFile
情報: Cannot read logging.properties file.
2010/04/08 17:01:23 com.sun.enterprise.web.WebContainer configureHost
致命的: WEB0355: network-listener [http-listener-2] referenced by virtual server [server] does not exist
2010/04/08 17:01:23 com.sun.enterprise.web.WebContainer configureHost
致命的: WEB0355: network-listener [http-listener-1] referenced by virtual server [server] does not exist
2010/04/08 17:01:23 com.sun.enterprise.web.WebContainer createHosts
情報: Created virtual server server
2010/04/08 17:01:23 com.sun.enterprise.web.WebContainer configureHost
致命的: WEB0355: network-listener [admin-listener] referenced by virtual server [__asadmin] does not exist
2010/04/08 17:01:23 com.sun.enterprise.web.WebContainer createHosts
情報: Created virtual server __asadmin
2010/04/08 17:01:23 com.sun.enterprise.web.WebContainer loadSystemDefaultWebModules
情報: Virtual server server loaded system default web module
2010/04/08 17:01:23 com.sun.enterprise.v3.services.impl.WebContainerStarter startWebContainer
情報: Done with starting web container
2010/04/08 17:01:23 com.sun.enterprise.v3.server.AppServerStartup run
情報: GlassFish v3 (74.2) startup time : Embedded(747ms) startup services(315ms) total(1062ms)
2010/04/08 17:01:23 org.glassfish.admin.mbeanserver.JMXStartupService$JMXConnectorsStarterThread run
情報: JMXStartupService: JMXConnector system is disabled, skipping.
2010/04/08 17:01:23 AppServerStartup run
情報: [Thread[GlassFish Kernel Main Thread,5,main]] started
2010/04/08 17:01:24 com.sun.enterprise.security.SecurityLifecycle &lt;init&gt;
情報: security.secmgroff
2010/04/08 17:01:24 com.sun.enterprise.security.ssl.SSLUtils checkCertificateDates
致命的: java_security.expired_certificate
2010/04/08 17:01:24 com.sun.enterprise.security.SecurityLifecycle onInitialization
情報: Security startup service called
2010/04/08 17:01:24 com.sun.enterprise.security.SecurityLifecycle onInitialization
情報: Security service(s) started successfully….
2010/04/08 17:01:25 com.sun.ejb.containers.BaseContainer initializeHome
情報: Portable JNDI names for EJB PersonController : [java:global/classes/PersonController, java:global/classes/PersonController!test.PersonController]
2010/04/08 17:01:25 com.sun.ejb.containers.BaseContainer initializeHome
情報: Portable JNDI names for EJB Hello : [java:global/classes/Hello, java:global/classes/Hello!test.Hello]
2010/04/08 17:01:25 org.hibernate.validator.engine.resolver.DefaultTraversableResolver detectJPA
情報: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
nullID: /Users/yt133043/NetBeansProjects/EmbeddedableTest/build/classes/ CLASSES: [class test.exceptions.IllegalOrphanException, class test.exceptions.NonexistentEntityException, class test.exceptions.PreexistingEntityException, class test.exceptions.RollbackFailureException, class test.Hello, class test.Person, class test.PersonController]

2010/04/08 17:01:25 org.hibernate.validator.engine.resolver.DefaultTraversableResolver detectJPA
情報: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
2010/04/08 17:01:25 org.eclipse.persistence.session.file:/Users/yt133043/NetBeansProjects/EmbeddedableTest/build/classes/_EmbeddedableTestPU
情報: EclipseLink, version: Eclipse Persistence Services – 2.0.0.v20091127-r5931
2010/04/08 17:01:27 org.eclipse.persistence.session.file:/Users/yt133043/NetBeansProjects/EmbeddedableTest/build/classes/_EmbeddedableTestPU
情報: file:/Users/yt133043/NetBeansProjects/EmbeddedableTest/build/classes/_EmbeddedableTestPU login successful
Person name: 山田 太郎
Person name: 山田 花子
Person name: 坂本 龍馬
Person name: 徳川 家康
Person name: 織田 信長
Person name: 豊臣 秀吉
Person name: 聖徳太子

2010/04/08 17:01:28 org.eclipse.persistence.session.file:/Users/yt133043/NetBeansProjects/EmbeddedableTest/build/classes/_EmbeddedableTestPU
情報: file:/Users/yt133043/NetBeansProjects/EmbeddedableTest/build/classes/_EmbeddedableTestPU logout successful
2010/04/08 17:01:28 com.sun.enterprise.connectors.service.ResourceAdapterAdminServiceImpl sendStopToResourceAdapter
情報: ra.stop-successful
2010/04/08 17:01:28 org.glassfish.admin.mbeanserver.JMXStartupService shutdown
情報: JMXStartupService and JMXConnectors have been shut down.
2010/04/08 17:01:28 com.sun.enterprise.v3.server.AppServerStartup stop
情報: Shutdown procedure finished
2010/04/08 17:01:28 AppServerStartup run
情報: [Thread[GlassFish Kernel Main Thread,5,main]] exiting

2010年4月8日 at 5:20 午後

EJB 3.1 の新機能概要

EJB 3.1 の新機能をプレゼン形式でまとめてみました。
かんたんに概要を紹介します。

EJB 3.0 から EJB 3.1 になり、かんたん開発に向けて多くの改善が施されています。まず、パッケージの簡略化が挙げられます。今までアプリケーションの種類に応じて .ear, .war 等のアーカイブにまとめる必要がありましたが、EJB コンポーネントも .war に含めることができるようになったため開発時の手間が大幅に削減されます。また、EJB 3.1 Lite が提供されフル Java EE の機能の一部の機能だけを利用できるようになったため、フル Java EE の機能が必要ないお客様にとっては不要なメモリリソースを消費せずに運用できる等のメリットがあります。

次のポイントはローカルビジネスインタフェースの実装が必要なくなった点です。これは開発者の生産性が向上するだけではなく、修正の手間も大幅に削減してくれます。例えば、過去に実装したクラスにおいて、特定のメソッドに対する引数や返り値の変更が必要になった場合を想定してください。今まではインタフェースのメソッド定義と実装クラスのメソッド定義を両方変更しなければなりませんでしたが、インタフェースの定義が必要がなくなったため、実装クラスのメソッド定義を変更するだけでよくなります。

次に、移植可能な Global JNDI 名では、今までベンダー独自に実装していた Global の JNDI 名が仕様の中で標準化されましたので、今後アプリケーションを他のアプリケーションサーバへ移行したいような場合、Global JNDI 名を変更する必要がなくなり移植性が高まります。

また、Java SE に組み込み可能な EJB コンテナ は開発時におけるテストの手間を大幅に削減してくれるようになります。今までは EJB のアプリケーションをテストする際、アプリケーションサーバにデプロイして Global 経由でアクセスする等の必要がありました。これは別の Java VM のプロセスとして稼働するためデバッグ等も困難でしたが、EJB 3.1 では Java SE のプロセス内に EJB コンテナをロードして実行する事ができるようになるため、同一 Java プロセス内で処理ができる他、EJB の単体テスト等がしやすくなります。

その他の追加機能として、Singleton Session Beans が追加された事が挙げられます。これはアプリケーション間で同一の情報を扱いたい場合に有効です。今までは独自に Singleton のクラスを作成して運用しなければなりませんでしたが、クラスタ環境のように複数台で実現するためには苦労していたかと思います。なぜなら異なる JVM プロセスの間で Sigleton のインスタンスを共有させる事は面倒だったからです。しかしEJB 3.1で提供する Singleton Session Bean はコンテナを跨ぐ環境においても唯一のインスタンスが保障されるため、こういった実装の手間が必要なく、アノテーションの追加だけで利用できるようになります。その他、タイマーサービスや非同期処理もそれぞれアノテーションを利用して簡単に実装できるようになっています。

最後に、
EJB といえば実装が難しい、設定も面倒という時代は今や過去の事で、今では実装がとてもかんたんになっています。また便利な機能をデフォルトで数多く持っている便利なフレームワークですので、今一度EJB を見直し利用をご検討ください。



























2010年4月7日 at 8:32 午後

友人の結婚式と桜


先週の土曜日に、友人の結婚式がありました。彼とは前の部署にいた時に知り合い、私の結婚式にも来てくれたり入社以来とても仲良くさせて頂いている大切な友人の一人です。彼の結婚を祝福するように天気にも恵まれとても素晴らしい結婚式でした。披露宴が終わった後、二次会まで少し時間があったので、友人と共に千鳥ヶ淵を少し散歩してきました。桜も満開でとても多くの人でにぎわっていました。
このような良い日に結婚式があげられて本当に良かったですね。末永くお幸せに!!


2010年4月5日 at 1:33 午後


Java Champion & Evangelist

Translate

ご注意

このエントリは個人の見解であり、所属する会社の公式見解ではありません

カレンダー

2010年4月
 1234
567891011
12131415161718
19202122232425
2627282930  

カテゴリー

clustermap

ブログ統計情報

  • 1,288,405 hits

Feeds

アーカイブ