Trabalhando com Kotlin e JPA
1. Introdução
Uma das características do Kotlin são as bibliotecasinteroperability with Java, e JPA é certamente uma delas.
Neste tutorial, exploraremos como usarKotlin Data Classes como entidades JPA.
2. Dependências
Para simplificar, usaremos o Hibernate como nossa implementação de JPA; precisamos adicionar as seguintes dependências ao nosso projeto Maven:
org.hibernate
hibernate-core
5.2.15.Final
org.hibernate
hibernate-testing
5.2.15.Final
test
Também usaremos um banco de dados H2 integrado para executar nossos testes:
com.h2database
h2
1.4.196
test
Para Kotlin, usaremos o seguinte:
org.jetbrains.kotlin
kotlin-stdlib-jdk8
1.2.30
3. Plugins do compilador (plugin jpa)
Para usar o JPA, as classes de entidade precisam de um construtor sem parâmetros.
Por padrão, as classes de dados Kotlin não têm e, para gerá-los, precisaremos usarjpa-plugin:
kotlin-maven-plugin
org.jetbrains.kotlin
1.2.30
jpa
1.8
org.jetbrains.kotlin
kotlin-maven-noarg
1.2.30
4. JPA com classes de dados Kotlin
Depois que a configuração anterior for concluída, estamos prontos para usar JPA com classes de dados.
Vamos começar criando uma classe de dadosPerson com dois atributos -idename, assim:
@Entity
data class Person(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int,
@Column(nullable = false)
val name: String
)
Como podemos ver, podemos usar livremente as anotações do JPA como@Entity, @Columne@Id.
Para ver nossa entidade em ação, criaremos o seguinte teste:
@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)
})
}
Depois de executar o teste com o log ativado, podemos ver os seguintes resultados:
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=?
Isso é um indicador de que tudo está indo bem. É importante notar 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
Agora, vamos testar novamente com os valoresnull. Para fazer isso, vamos estender nossa entidadePerson com um novo atributoemail e um relacionamento@OneToMany:
//...
@Column(nullable = true)
val email: String? = null,
@Column(nullable = true)
@OneToMany(cascade = [CascadeType.ALL])
val phoneNumbers: List? = null
Também podemos ver que os camposemailephoneNumbers são anuláveis, portanto, são declarados com o ponto de interrogação.
A entidadePhoneNumber tem dois atributos -idenumber:
@Entity
data class PhoneNumber(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int,
@Column(nullable = false)
val number: String
)
Vamos verificar isso com um teste:
@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)
})
}
Desta vez, obteremos uma instrução de inserção:
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=?
Vamos testar mais uma vez, mas sem os dados denull para verificar a saída:
@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)
})
}
E, como podemos ver, agora temos três instruções de inserção:
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. Conclusão
Neste artigo rápido, vimos um exemplo de como integrar classes de dados Kotlin ao JPA usando o jpa-plugin e o Hibernate.
Como sempre, o código-fonte está disponívelover on GitHub.