Travailler avec Kotlin et JPA

Travailler avec Kotlin et JPA

1. introduction

L’une des caractéristiques de Kotlin est les bibliothèquesinteroperability with Java, et JPA en fait certainement partie.

Dans ce didacticiel, nous allons découvrir comment utiliserKotlin Data Classes en tant qu'entités JPA.

2. Les dépendances

Pour simplifier les choses, nous utiliserons Hibernate comme implémentation de notre JPA; nous devrons ajouter les dépendances suivantes à notre projet Maven:


    org.hibernate
    hibernate-core
    5.2.15.Final


    org.hibernate
    hibernate-testing
    5.2.15.Final
    test

Nous utiliserons également une base de données intégrée H2 pour exécuter nos tests:


    com.h2database
    h2
    1.4.196
    test

Pour Kotlin, nous utiliserons les éléments suivants:


    org.jetbrains.kotlin
    kotlin-stdlib-jdk8
    1.2.30

Bien entendu, les versions les plus récentes deHibernate,H2 etKotlin se trouvent dans Maven Central.

3. Plugins du compilateur (jpa-plugin)

Pour utiliser JPA, les classes d'entité ont besoin d'un constructeur sans paramètres.

Par défaut, les classes de données Kotlin ne l'ont pas, et pour les générer, nous devons utiliser lesjpa-plugin:


    kotlin-maven-plugin
    org.jetbrains.kotlin
    1.2.30
    
        
        jpa
        
        1.8
    
    
        
        org.jetbrains.kotlin
        kotlin-maven-noarg
        1.2.30
        
    
    

4. JPA avec classes de données Kotlin

Une fois la configuration précédente terminée, nous sommes prêts à utiliser JPA avec des classes de données.

Commençons par créer une classe de donnéesPerson avec deux attributs -id etname, comme ceci:

@Entity
data class Person(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int,

    @Column(nullable = false)
    val name: String
)

Comme nous pouvons le voir, nous pouvons utiliser librement les annotations de JPA comme@Entity, @Column et@Id.

Pour voir notre entité en action, nous allons créer le test suivant:

@Test
fun givenPerson_whenSaved_thenFound() {
    doInHibernate(({ this.sessionFactory() }), { session ->
        val personToSave = Person(0, "John")
        session.persist(personToSave)
        val personFound = session.find(Person::class.java, personToSave.id)
        session.refresh(personFound)

        assertTrue(personToSave == personFound)
    })
}

Après l'exécution du test avec la journalisation activée, nous pouvons voir les résultats suivants:

Hibernate: insert into Person (id, name) values (null, ?)
Hibernate: select person0_.id as id1_0_0_, person0_.name as name2_0_0_ from Person person0_ where person0_.id=?

C'est un indicateur que tout va bien. Il est important de noter queif we don’t use the jpa-plugin in runtime, we are going to get an InstantiationException, due to the lack of default constructor:

javax.persistence.PersistenceException: org.hibernate.InstantiationException: No default constructor for entity: : com.example.entity.Person

Maintenant, nous allons tester à nouveau avec les valeursnull. Pour ce faire, étendons notre entitéPerson avec un nouvel attributemail et une relation@OneToMany:

    //...
    @Column(nullable = true)
    val email: String? = null,

    @Column(nullable = true)
    @OneToMany(cascade = [CascadeType.ALL])
    val phoneNumbers: List? = null

Nous pouvons également voir que les champsemail etphoneNumbers sont nullables, donc sont déclarés avec le point d'interrogation.

L'entitéPhoneNumber a deux attributs -id etnumber:

@Entity
data class PhoneNumber(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int,

    @Column(nullable = false)
    val number: String
)

Vérifions cela avec un test:

@Test
fun givenPersonWithNullFields_whenSaved_thenFound() {
    doInHibernate(({ this.sessionFactory() }), { session ->
        val personToSave = Person(0, "John", null, null)
        session.persist(personToSave)
        val personFound = session.find(Person::class.java, personToSave.id)
        session.refresh(personFound)

        assertTrue(personToSave == personFound)
    })
}

Cette fois, nous obtiendrons une instruction d'insertion:

Hibernate: insert into Person (id, email, name) values (null, ?, ?)
Hibernate: select person0_.id as id1_0_1_, person0_.email as email2_0_1_, person0_.name as name3_0_1_, phonenumbe1_.Person_id as Person_i1_1_3_, phonenumbe2_.id as phoneNum2_1_3_, phonenumbe2_.id as id1_2_0_, phonenumbe2_.number as number2_2_0_ from Person person0_ left outer join Person_PhoneNumber phonenumbe1_ on person0_.id=phonenumbe1_.Person_id left outer join PhoneNumber phonenumbe2_ on phonenumbe1_.phoneNumbers_id=phonenumbe2_.id where person0_.id=?

Testons encore une fois mais sans les donnéesnull pour vérifier la sortie:

@Test
fun givenPersonWithFullData_whenSaved_thenFound() {
    doInHibernate(({ this.sessionFactory() }), { session ->
        val personToSave = Person(
          0,
          "John",
          "[email protected]",
          Arrays.asList(PhoneNumber(0, "202-555-0171"), PhoneNumber(0, "202-555-0102")))
        session.persist(personToSave)
        val personFound = session.find(Person::class.java, personToSave.id)
        session.refresh(personFound)

        assertTrue(personToSave == personFound)
    })
}

Et, comme nous pouvons le constater, nous avons maintenant trois déclarations d’insertion:

Hibernate: insert into Person (id, email, name) values (null, ?, ?)
Hibernate: insert into PhoneNumber (id, number) values (null, ?)
Hibernate: insert into PhoneNumber (id, number) values (null, ?)

5. Conclusion

Dans cet article rapide, nous avons vu comment intégrer des classes de données Kotlin à JPA à l'aide du plug-in jpa et d'Hibernate.

Comme toujours, le code source est disponibleover on GitHub.