CLOVER🍀

That was when it all began.

ほぼ初めてのJPA

ちょっと思うところがあって、JPAの簡単なサンプルを書いてみました。

JPAは、1.0の頃にちょこっと本を見て書いたことがあるだけで、ほとんど覚えていません。仕事でも使っていませんし。

で、とりあえずJava EEサーバにデプロイなんて考えずに、Java SEの範囲で動かすことを考えます。ただ、書くのはScalaでですが…。

JPAの実装には、Hibernateを選びました。最近触っているのがデータグリッドなので、このあたりにJPAに興味を持ったの理由が見え隠れしてるかも?

Hibernate
http://www.hibernate.org/hibernate

データベースはMySQLです。このために、久々にインストール。

対象のテーブルは、こんな感じ。今回は、ホントに簡単なサンプルなので。

CREATE TABLE user (
 id INT(9),
 first_name VARCHAR(10),
 last_name VARCHAR(10),
 age INT(3),
 version_no INT,
 PRIMARY KEY(id)
);

では、build.sbt。

name := "jpa-example"

version := "0.0.1-SNAPSHOT"

scalaVersion := "2.10.3"

organization := "littlewings"

resolvers += "Public JBoss Group" at "http://repository.jboss.org/nexus/content/groups/public-jboss"

libraryDependencies ++= Seq(
  "org.hibernate" % "hibernate-entitymanager" % "4.2.7.SP1",
  "mysql" % "mysql-connector-java" % "5.1.26" % "runtime"
)

JPAの設定ファイルである、永続ユニットの定義です。
src/main/resources/META-INF/persistence.xml

<?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_2_0.xsd"
             version="2.0">
  <persistence-unit name="jpa.example" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/test?useUnicode=true&amp;characterEncoding=utf8" />
      <property name="javax.persistence.jdbc.user" value="kazuhira" />
      <property name="javax.persistence.jdbc.password" value="password" />
    </properties>
  </persistence-unit>
</persistence>

ユーザ名とかパスワードとか、Hibernateのドキュメントを見ていると固有のプロパティの例ばかりでしたが、JPAっぽいプロパティ名でも動きました。

で、エンティティクラスの定義。

@SerialVersionUID(1L)
@Entity(name = "user")
class User(
  @(Id @beanGetter) @BeanProperty var id: Int,
  @(Column @beanGetter)(name = "first_name") @BeanProperty var firstName: String,
  @(Column @beanGetter)(name = "last_name") @BeanProperty var lastName: String,
  @(Column @beanGetter) @BeanProperty var age: Int,
  @(Column @beanGetter)(name = "version_no") @(Version @beanGetter) @BeanProperty var versionNo: Int = 1
) extends Serializable {
  def this() = this(0, null, null, 0, 1)

  override def toString(): String =
    s"id = $id, firstName = $firstName, lastName = $lastName, age = $age, versionNo = $versionNo"
}

エンティティにはデフォルトコンストラクタが要求されることや、各種アノテーションを付けたフィールドがgetterとかに付かないとまともに動作しなかったので、最初はこんな形に。

Scala的にはコンストラクタで設定して…といきたかったのですが、デフォルトコンストラクタが必須な分だけ、この定義はダサいですね…。

仕方がないので、ここは諦めてこんな定義に。

object User {
  def apply(id: Int, firstName: String, lastName: String, age: Int): User = {
    val user = new User
    user.id = id
    user.firstName = firstName
    user.lastName = lastName
    user.age = age
    user
  }
}

@SerialVersionUID(1L)
@Entity
@Table(name = "user")
class User extends Serializable {
  @Id
  @BeanProperty
  var id: Int = _

  @Column(name = "first_name")
  @BeanProperty
  var firstName: String = _

  @Column(name = "last_name")
  @BeanProperty var lastName: String = _

  @Column
  @BeanProperty
  var age: Int = _

  @Column(name = "version_no")
  @Version
  @BeanProperty
  var versionNo: Int = _

  override def toString(): String =
    s"id = $id, firstName = $firstName, lastName = $lastName, age = $age, versionNo = $versionNo"
}

ここは、コンパニオンオブジェクトを使ってしまおうってことで。

そうそう、今回の例で初めて@beanGetterとか、他の@field、@getterアノテーションとかを知ったんですよね。たまには他のものに手を出してみるのいいですね〜。
*あとで見直したら、なんか@beanGetterとかいらないことがわかりました…

EntityManagerを使ったサンプルは、こちらです。いやまあ、全然大したことしてませんけど。

object JpaExample {
  def main(args: Array[String]): Unit = {
    val entityManagerFactory = Persistence.createEntityManagerFactory("jpa.example")
    val entityManager = entityManagerFactory.createEntityManager()
    val transaction = entityManager.getTransaction

    transaction.begin()

    val query = entityManager.createQuery("SELECT u FROM User u")
    query.getResultList.asScala.foreach {
      case u: User =>
        println(s"User: $u")
        // entityManager.detach(u)
    }

    entityManager.clear()

    entityManager.createQuery("DELETE FROM User").executeUpdate()

    val users = Array(
      User(1, "カツオ", "磯野", 11),
      User(2, "ワカメ", "磯野", 9),
      User(3, "タラオ", "フグ田", 3)
    )

    users.foreach(entityManager.persist)

    transaction.commit()

    entityManager.close()
    entityManagerFactory.close()
  }
}

EntityManagerの永続性コンテキストってやつですか?そのあたりが全然わかってないので、ちょっと本を読みながら勉強してみようと思います。

Beginning Java EE 6~GlassFish 3で始めるエンタープライズJava (Programmer's SELECTION)

Beginning Java EE 6~GlassFish 3で始めるエンタープライズJava (Programmer's SELECTION)

でも、近くデータグリッドと組み合わせるかも?

今回の、サンプルコード全体です。

import scala.collection.JavaConverters._

import scala.beans.BeanProperty

import javax.persistence.{Column, Entity, Id, Persistence, Version}

object JpaExample {
  def main(args: Array[String]): Unit = {
    val entityManagerFactory = Persistence.createEntityManagerFactory("jpa.example")
    val entityManager = entityManagerFactory.createEntityManager()
    val transaction = entityManager.getTransaction

    transaction.begin()

    val query = entityManager.createQuery("SELECT u FROM User u")
    query.getResultList.asScala.foreach {
      case u: User =>
        println(s"User: $u")
        // entityManager.detach(u)
    }

    entityManager.clear()

    entityManager.createQuery("DELETE FROM User").executeUpdate()

    val users = Array(
      User(1, "カツオ", "磯野", 11),
      User(2, "ワカメ", "磯野", 9),
      User(3, "タラオ", "フグ田", 3)
    )

    users.foreach(entityManager.persist)

    transaction.commit()

    entityManager.close()
    entityManagerFactory.close()
  }
}

object User {
  def apply(id: Int, firstName: String, lastName: String, age: Int): User = {
    val user = new User
    user.id = id
    user.firstName = firstName
    user.lastName = lastName
    user.age = age
    user
  }
}

@SerialVersionUID(1L)
@Entity
@Table(name = "user")
class User extends Serializable {
  @Id
  @BeanProperty
  var id: Int = _

  @Column(name = "first_name")
  @BeanProperty
  var firstName: String = _

  @Column(name = "last_name")
  @BeanProperty var lastName: String = _

  @Column
  @BeanProperty
  var age: Int = _

  @Column(name = "version_no")
  @Version
  @BeanProperty
  var versionNo: Int = _

  override def toString(): String =
    s"id = $id, firstName = $firstName, lastName = $lastName, age = $age, versionNo = $versionNo"
}