Usando a Biblioteca Exposed
Tags: kotlin, backend, database, braziliandevs
Published: 2021-09-27 00:33:42

## Kotlinautas

Esse conteúdo é oferecido e distribuído pela comunidade [Kotlinautas](https://twitter.com/kotlinautas/), uma comunidade brasileira que busca oferecer conteúdo gratuito sobre a linguagem Kotlin em um espaço plural.

![capa Kotlinautas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jreawpvk6whxigcpmctf.png)

## O quê é um ORM?

ORM (Mapeamento de Objeto Relacional) é uma maneira de mapear dados de um banco de dados, em objetos de uma aplicação. Como se fosse uma ponte entre classes da nossa aplicação, e o banco de dados da aplicação.

ORM's servem principalmente para facilitar a comunicação entre uma aplicação e seu banco de dados. Facilitando processos de leitura, escrita, atualização e remoção de dados.

## O quê é Exposed?

[Exposed](https://github.com/JetBrains/Exposed) é a biblioteca oficial de ORM para Kotlin, criada pela própria JetBrains. Uma de suas principais vantagens é poder usar diferentes bancos de dados, e poder trocar qual banco você está usando de maneira fácil. A lista de bancos de dados atualmente suportada pelo Exposed é:

- H2
- MySQL
- MariaDB
- Oracle
- PostgreSQL
- SQL Server
- SQLite

## O quê vamos criar?

Vamos criar um sistema de cadastro de planetas. Onde poderemos inserir informações sobre um planeta, e essas informações serão inseridas em um banco de dados. Com isso, poderemos usar a Exposed para conectar a nossa aplicação, á um banco de dados que desejarmos.

## Criando o projeto

Abra seu IntelliJ no menu inicial e clique em *New Project*:

![botão New Project no menu inicial do IntelliJ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e3xauc7y38mqtdej31ka.png)

Depois, selecione a opção **Kotlin DSL build script**, selecione também a opção **Kotlin/JVM**, e opicionalmente remova a primeira opção **Java**. Essa opção não vai mudar em nada, pois ela dá suporte do Gradle á linguagem Java, mas apenas iremos usar Kotlin.

Após isso, clique em **Next** e escreva o nome do projeto e a localização na sua máquina. Essas duas opção são completamente pessoais, caso não tenha nenhuma ideia, coloque algo como **exposed** apenas como identificação.

Agora, com o projeto aberto, vá ao aquivo `build.gradle.kts` e adicione as dependências da Exposed, com a seção `dependencies` ficando assim:

```kotlin
dependencies {
    implementation(kotlin("stdlib"))
    implementation("org.jetbrains.exposed:exposed-core:0.33.1")
    implementation("org.jetbrains.exposed:exposed-dao:0.33.1")
    implementation("org.jetbrains.exposed:exposed-jdbc:0.33.1")
    implementation("com.h2database:h2:1.4.199")
    implementation("org.slf4j:slf4j-api:1.7.5")
    implementation("org.slf4j:slf4j-simple:1.6.4")
}
```

Agora, clique no elefante no canto superior direito para carregar as alterações no Gradle.

![Elefante do Gradle no canto superior direito](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25316fmqf0v5omh9q6fi.png)

Após isso, poderemos começar a programar. Você pode criar um arquivo em `src/main/kotlin/` chamado `main.kt` para ser o arquivo principal da aplicação.

Mas com qualquer nome de arquivo, como você irá usar a Exposed, sempre se lembre de importar a Exposed no começo do arquivo:

```kotlin
import org.jetbrains.exposed.sql.*
```

## Usando a Exposed

Primeiro, vamos precisar criar um objeto que irá representar uma tabela em nosso banco de dados, e dentro desse objeto inserir as colunas do nosso banco (como propriedades). Isso pode ser feito da seguinte maneira:

```kotlin
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction

object Planetas: Table(){
	val nome = varchar("nome", 30)
	val massa = double("massa")
	val solido = bool("solido")
}
```

- Usamos a palavra `object` para criar o objeto, colocamos o nome da tabela `Planetas`, e o tipo da variável, que é um tipo da Exposed, Sendo `Table`;
- Dentro do objeto, criamos três variáveis, sendo `nome`, `massa`, e `solido`. `nome` irá guardar o nome do planeta, `massa` irá guardar a massa do planeta comparada á terra e `solido` irá determinar se o planeta é sólido ou não;
- Para determinar essas variáveis, usamos tipos de dados de um banco de dados, como `varchar`, `double`, e `bool`;

Após isso, podemos criar a nossa função `main`. Dentro dela, vamos primeiramente conectar ao banco de dados. Isso pode ser feito com a função `Database.connect` dessa maneira:

```kotlin
...

fun main(){
	Database.connect("jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;", "org.h2.Driver")
}
```

- Estamos usando o banco de dados H2. Um banco de dados Java, que pode funcionar apenas na memória. Vamos usar esse banco de dados pois a cada vez que rodarmos o programa, teremos um novo banco totalmente zerado. Com isso você ficará livre para testar o banco, criando, lendo, editando e removendo dados;
- Para fazer essa conexão, tivemos que passar um texto que representa esse banco. Caso você queira mudar o banco, você apenas irá precisar mudar esse texto;

Agora precisamos inserir a tabela `Planetas` que criamos no banco. Isso pode ser feito de maneira completamente automática usando a função `SchemaUtils.create`. Essa função pode ser usada da seguinte maneira:

```kotlin
...

fun main(){
	Database.connect("jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;", "org.h2.Driver")

	transaction {
		SchemaUtils.create(Planetas)
	}
}
```

- Usamos um bloco `transaction` pois toda conexão ao banco (escrita, leitura, edição e remoção) precisa estar dentro de um bloco `transaction`;
- Passamos ao `SchemaUtils.create` o nosso objeto `Planetas`. Com isso, essa tabela será criada no banco de dados;

Agora, vamos criar um bloco `while (true)` onde iremos executar um mesmo trecho de código eternamente, onde iremos ler o nome, massa e se o planeta é sólido ou não, vamos enviar esses dados ao banco de dados, e também mostrar todas as linhas do banco na tela.

```kotlin
...

fun main(){
	Database.connect("jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;", "org.h2.Driver")

	transaction {
		SchemaUtils.create(Planetas)
	}

  while (true){

  }
}
```

Agora vamos usar a função `readLine` para pegar os dados da linha de comando, mostrando na tela pergunta por pergunta:

```kotlin
...

fun main(){
	Database.connect("jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;", "org.h2.Driver")

	transaction {
		SchemaUtils.create(Planetas)
	}

  while (true){
		print("Insira o nome do planeta: ")
		val nomePlaneta = readLine().toString()

		print("Insira a massa do planeta: ")
		val massaPlaneta = readLine().toString().toDouble()

		print("O planeta é sólido? [sim/não]: ")
		val solidoPlaneta = readLine() == "sim"
  }
}
```

- na variável `nomePlaneta` apenas transformamos o texto escrito na linha de comando em `String` com `.toString()`;
- na variável `massaPlaneta` primeiro transformamos o texto escrito em `String`, e depois transformamos essa `String` em `Double`. Para assim obtermos o número decimal da massa do planeta em relação á terra;
- na variável `solidoPlaneta` testamos se o texto escrito é igual á "sim". se essa condição for verdadeira, o planeta é sólido. Se não, o planeta não é sólido;

Agora vamos executar a transação para inserirmos esses dados no banco:

```kotlin
...

fun main(){
	Database.connect("jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;", "org.h2.Driver")

	transaction {
		SchemaUtils.create(Planetas)
	}

  while (true){
		print("Insira o nome do planeta: ")
		val nomePlaneta = readLine().toString()

		print("Insira a massa do planeta: ")
		val massaPlaneta = readLine().toString().toDouble()

		print("O planeta é sólido? [sim/não]: ")
		val solidoPlaneta = readLine() == "sim"

    transaction {
      Planetas.insert {
        it[nome] = nomePlaneta
        it[massa] = massaPlaneta
        it[solido] = solidoPlaneta
      }
    }
  }

}
```

- Para inserirmos esses dados, podemos usar a função `Planetas.insert`, informando o valor de cada coluna do banco com `it[COLUNA] = valor`.

Agora já conseguimos escrever dados no banco. Por fim, vamos pegar todos os dados do banco, e mostrar na tela. Mostrando que realmente conseguimos escrever os dados. Primeiro, vamos pegar todas as linhas da tabela `Planetas`:

```kotlin
...

fun main(){
	Database.connect("jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;", "org.h2.Driver")

	transaction {
		SchemaUtils.create(Planetas)
	}

  while (true){
		print("Insira o nome do planeta: ")
		val nomePlaneta = readLine().toString()

		print("Insira a massa do planeta: ")
		val massaPlaneta = readLine().toString().toDouble()

		print("O planeta é sólido? [sim/não]: ")
		val solidoPlaneta = readLine() == "sim"

    transaction {
      Planetas.insert {
        it[nome] = nomePlaneta
        it[massa] = massaPlaneta
        it[solido] = solidoPlaneta
      }
    }

    transaction {
      val planetasSalvos = Planetas.selectAll()
    }
  }
}
```

Agora vamos percorrer por todo essa lista de dados, e mostrar na tela planeta por planeta usando um `.map`:

```kotlin
...

fun main(){
  Database.connect("jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;", "org.h2.Driver")

    transaction {
      SchemaUtils.create(Planetas)
    }

  while (true){
    print("Insira o nome do planeta: ")
    val nomePlaneta = readLine().toString()

    print("Insira a massa do planeta: ")
    val massaPlaneta = readLine().toString().toDouble()

    print("O planeta é sólido? [sim/não]: ")
    val solidoPlaneta = readLine() == "sim"

    transaction {
      Planetas.insert {
        it[nome] = nomePlaneta
          it[massa] = massaPlaneta
          it[solido] = solidoPlaneta
      }
    }

    transaction {
      val planetasSalvos = Planetas.selectAll()

      planetasSalvos.map {
        if (it[Planetas.solido]){
          println("Planeta ${it[Planetas.nome]} tem ${it[Planetas.massa]} massas terrestres e é sólido")
        }else{
          println("Planeta ${it[Planetas.nome]} tem ${it[Planetas.massa]} massas terrestres e não é sólido")
        }
      }
    }
  }
}
```

- Dentro de um `map`, percorrermos elemento por elemento, e para nos referirmos ao elemento atual, podemos usar a variável `it`. e dentro desse `it` podemos pegar os valores de cada linha, podendo assim mostrar na tela;

Agora, executando esse programa, poderemos ver na prática como criamos um código que pode inserir e ler dados de maneira dinâmica:

```
Insira o nome do planeta: Mercúrio
Insira a massa do planeta: 0.1
O planeta é sólido? [sim/não]: sim
Planeta Mercúrio tem 0.1 massas terrestres e é sólido
Insira o nome do planeta: Vênus
Insira a massa do planeta: 0.9
O planeta é sólido? [sim/não]: sim
Planeta Mercúrio tem 0.1 massas terrestres e é sólido
Planeta Vênus tem 0.9 massas terrestres e é sólido
```

### Update e Delete

Duas operações que não usamos foram a **Update**e **Delete**. Essas duas são muito parecidas em seu uso na Exposed. Pois nas duas é (quase sempre) necessário usar um parâmetro `WHERE` para definir quais linhas desejamos mudar/remover. Por exemplo, podemos fazer um Update com WHERE colocando a condição como argumento da função, e a alteração em si no corpo. Dessa maneira:

```kotlin
transaction {
  Planetas.update({ Planetas.nome eq "teste" }){
    it[Planetas.nome] = "Vênus"
  }
}
```

- Colocamos a condição entre `{}` como argumento da função, e no corpo alteramos a linha diretamente;

E para o Delete podemos fazer a mesma coisa, mas agora apenas precisamos colocar a condição:

```kotlin
transaction {
  Planetas.deleteWhere { Planetas.nome eq "teste" }
}
```

### Usando outros bancos de dados

Caso você queira usar os outros bancos de dados que a Exposed suporta, você pode ver a lista de URL's de conexão de diferentes bancos. Essa lista pode ser acessada [clicando aqui](https://github.com/JetBrains/Exposed/wiki/DataBase-and-DataSource)

## Finalização

Nesse artigo você aprendeu como usar de forma básica a biblioteca [Exposed](https://github.com/JetBrains/Exposed), para escrever, ler, editar e remover dados de um banco.

Muito obrigada por ler ❤️🏳️‍⚧️ e me segue nas redes, é tudo [@lissatransborda](https://twitter.com/lissatransborda) 👀