Kotlinを使用したJava EEアプリケーション

1. 概要

Javaとlink:/kotlin[Kotlin]は両方ともlink:/jvm-languages[for the JVM]で設計された言語です。 ただし、Java EEコンテナーでKotlinを使用しようとすると、すぐにいくつかの慣用的な課題に直面します。
このチュートリアルでは、これらの課題と効果的な対処方法を見ていきます。

2. 課題

JavaとKotlinはやや異なる言語です。 構文は異なりますが、それは明らかですが、それは本当の問題ではありません。 *これは言語設計とパラダイムが異なり*、Java EEコンテナ内での使用に関していくつかの問題を引き起こします。 Kotlinを使用してエンタープライズアプリケーションを構築するには、これらの違いを満たす必要があります。
たとえば、* Kotlinクラスはデフォルトでfinalです。*それらを拡張可能にするには、明示的に開く必要があります。 また、https://www.baeldung.com/the-persistence-layer-with-spring-and-jpa [JPA]やlink:/などのフレームワークで使用するためのパラメーターなしのコンストラクターを提供する必要もあります。ジャクソン[ジャクソン]。 Javaではデフォルトで使用可能ですが、Kotlinでは追加の作業が必要です。 link:/arquillian[Arquillian]との統合テストのように、コンテナが頻繁に使用する注入も初期化のために少し注意が必要です。
これらの課題に対処しましょう。 この例では、* Kotlinで簡単なCRUDアプリケーションを構築し、それをJava EEコンテナで実行する方法*を示します。 まず、単純なデータクラスから始め、次にサービスと統合テストを作成します。

3. 依存関係

最初に、https://search.maven.org/search?q = g:javax%20AND%20a:javaee-api [_javaee-api_]依存関係を追加しましょう。
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>8.0.1</version>
    <scope>provided</scope>
</dependency>
コンパイルにのみ必要なため、この依存関係を_provided_に設定していることに注意してください。

4. JPAエンティティ

次に、https://www.baeldung.com/jpa-entities [entity]およびlink:/entity-to-andとして使用する単純な_Student_データクラスを作成しましょう。 -from-dto-for-a-java-spring-application [DTO]同時に。
学生のID、姓、名が必要です:
@Entity
data class Student constructor (

    @SequenceGenerator(name = "student_id_seq", sequenceName = "student_id_seq",
      allocationSize = 1)
    @GeneratedValue(generator = "student_id_seq", strategy = GenerationType.SEQUENCE)
    @Id
    var id: Long?,

    var firstName: String,
    var lastName: String

) {
    constructor() : this(null, "", "")
    constructor(firstName: String, lastName: String) : this(null, firstName, lastName)
}
_Student_をデータクラスとして定義しました。これは、Kotlinでデータを保持するための特別な種類のクラスです。 これらのクラスでは、コンパイラーは、プライマリコンストラクターで宣言されたすべてのプロパティから、_equals()_、_ hashCode()_、および_toString()_ commonなどの一般的な関数を自動的に派生させます。
*関数の自動生成により、データクラスは非常に便利で使いやすくなります。*ただし、データクラスもいくつかのルールを満たす必要があります。 たとえば、_val_または_var_としてマークされた少なくとも1つのパラメーターを持つプライマリコンストラクターが必要です。
データクラスでは、プライマリコンストラクター内ですべてのメンバーを定義しました。
また、2つのセカンダリコンストラクターを定義しました。
  • まず、コンテナーと共通のパラメーターなしのコンストラクターがあります
    JPAやJacksonなどのフレームワークは、クラスをインスタンス化し、データにセッターを設定するために必要です。

  • 第二に、使いたいコンストラクターがあります。
    IDなしで新しいオブジェクトをインスタンス化します。通常は、新しいエンティティをデータベースに保存するときに使用します。

    さらに、標準のJava EEと同様に、標準のJPAアノテーションを使用してJPAエンティティを定義します。

3. ビジネスサービス

_https://www.baeldung.com/hibernate-entitymanager [EntityManager] _でCRUD操作を処理するビジネスサービスが必要です。 シンプルで、Java実装に非常に似ていますが、いくつかの顕著な違いがあります。
@Stateless
open class StudentService {

    @PersistenceContext
    private lateinit var entityManager: EntityManager

    open fun create(student: Student) = entityManager.persist(student)

    open fun read(id: Long): Student? = entityManager.find(Student::class.java, id)

    open fun update(student: Student) = entityManager.merge(student)

    open fun delete(id: Long) = entityManager.remove(read(id))
}
Kotlinのすべてのクラスはfinalなので、クラスを明示的に開いて拡張機能を有効にする必要があります。 Java EEコンテナは、サービスクラスからプロキシを作成し、必要な場所に挿入するため、拡張機能が必要です。 パブリックメソッドも開く必要があります。そうしないと、プロキシを作成できません。 したがって、*クラスとそのすべてのパブリックメソッドで_open_キーワードを使用します。*
_EntityManager_は、Javaの場合と同様の方法でPersist_ @ PersistenceContext_アノテーションとともに使用されます。 追加のlink:/kotlin-lazy-initialization[_lateinit_ keyword]を使用して、プライベートメンバーとして定義します。 このキーワードは、最初はこの変数が_null_であるが、最初に使用する前に初期化されることをコンパイラーに伝えます。 これにより、不要な_null_チェックがなくなり、コンテナの挿入に完全に適合します。 _ @ Inject_アノテーションを使用するたびに使用します。

4. RESTサービス

最後に、アプリケーションのRESTサービスエンドポイントを定義する必要があります。 そのためには、リソースクラスを登録する必要があります。
@ApplicationPath("/")
class ApplicationConfig : Application() {
    override fun getClasses() = setOf(StudentResource::class.java)
}
次に、リソースクラスを定義します。 Kotlin固有の変更をいくつか加えて、Javaで行うことに非常に近いものになります。
@Path("/student")
open class StudentResource {

    @Inject
    private lateinit var service: StudentService

    @POST
    open fun create(student: Student): Response {
        service.create(student)
        return Response.ok().build()
    }

    @GET
    @Path("/{id}")
    open fun read(@PathParam("id") id: Long): Response {
        val student  = service.read(id)
        return Response.ok(student, MediaType.APPLICATION_JSON_TYPE).build()
    }

    @PUT
    @Path("/{id}")
    open fun update(@PathParam("id") id: Long, student: Student): Response {
        service.update(student)
        return Response.ok(student, MediaType.APPLICATION_JSON_TYPE).build()
    }

    @DELETE
    @Path("/{id}")
    open fun delete(@PathParam("id") id: Long): Response {
        service.delete(id)
        return Response.noContent().build()
    }

}
前のビジネスサービスの例のように、リソースクラスとすべてのパブリックメソッドを明示的に開きました。 また、今回はビジネスサービスをリソースクラスに注入するために、_lateinit_キーワードを再度使用しました。

*5. Arquillian *を使用したテスト

ここで、アプリケーションとArquillianおよびShrinkwrapの統合テストを実装します。
Arquillianでアプリケーションをテストするには、ShrinkWrapでテストコンテナーへのパッケージ化と展開をセットアップする必要があります。
@RunWith(Arquillian.class)
public class StudentResourceIntegrationTest {

    @Deployment
    public static WebArchive createDeployment() {
        JavaArchive[] kotlinRuntime = Maven.configureResolver()
          .workOffline()
          .withMavenCentralRepo(true)
          .withClassPathResolution(true)
          .loadPomFromFile("pom.xml")
          .resolve("org.jetbrains.kotlin:kotlin-stdlib")
          .withTransitivity()
          .as(JavaArchive.class);

        return ShrinkWrap.create(WebArchive.class, "kotlin.war")
          .addPackages(true, Filters.exclude(".*Test*"), "com.baeldung.jeekotlin")
          .addAsLibraries(kotlinRuntime)
          .addAsResource("META-INF/persistence.xml")
          .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    }
    // more code
}
デプロイメントは、統合テストの一般的なJavaデプロイメントに似ていますが、追加の構成がいくつか含まれています。 ここでは、Shrinkwrap Mavenリゾルバーを使用して、Mavenリポジトリから_kotlin-stdlib_を取得します。 次に、それをライブラリとしてWARにアーカイブします。
その後、_HttpClient_を使用して、REST APIに対してCRUDリクエストを実行します。
@Test
@RunAsClient
public void when_post__then_return_ok(@ArquillianResource URL url)
  throws URISyntaxException, JsonProcessingException {
    String student = new ObjectMapper()
      .writeValueAsString(new Student("firstName", "lastName"));
      WebTarget webTarget = ClientBuilder.newClient().target(url.toURI());

    Response response = webTarget
      .path("/student")
      .request(MediaType.APPLICATION_JSON)
      .post(Entity.json(student));

    assertEquals(200, response.getStatus());
}
この例では、_ @ ArquillianResource_を使用してAPIのURLを提供し、_Student_オブジェクトをシリアル化してAPIにPOSTします。 すべてが正常で、オブジェクトがデータベースに作成されている場合、応答ステータスは200 OKであり、テストの終了時にアサートします。

6. 結論

この記事では、KotlinでCRUD REST JPAアプリケーションを構築する方法、デプロイする方法、Java EEコンテナーで実行する方法、Arquillianでテストする方法を示しました。 ご覧のとおり、KotlinとJavaはうまく連携していますが、それを実現するには追加の作業が必要です。
この例の完全なソースコードは、https://github.com/eugenp/tutorials/tree/master/jee-kotlin [on GitHub]で入手できます。