Começando com o snorql
- O que esperar deste post?
- Conheça suas ferramentas
- Dropwizard
- Kotlin
- Maven - Configurando sua máquina local
- Configurando seu aplicativo
- Criando um novo projeto
- Adicionando dependências
- Construir e compilar
- Estrutura do projeto
- Código - Crie e execute o aplicativo
- Próximos passos
- perguntas frequentes
Bem, a situação também não era tão diferente em Udaan e foi aí que alguns de nós decidimos resolver o problema com nossas próprias mãos e criar uma ferramenta que ajudaria a resolver esses problemas preventivamente.
Digitar …
O que é snorql?
snorql é um projeto de código aberto desenvolvido na Udaan com o objetivo de monitorar e diagnosticar problemas relacionados ao banco de dados usando métricas SQL.
Ele fornece visibilidade sobre o desempenho de cada banco de dados, para que você possa parar de jogar o jogo de adivinhação e começar a otimizar seus bancos de dados com facilidade. E deixe-me dizer-lhe, esta visibilidade adicional tem sido um divisor de águas para a nossa organização. Isso aumentou a responsabilidade de cada equipe.
Os resultados falam por si. Graças ao snorql,
- Vimos maior desempenho em mais de 50 bancos de dados.
- Custos de nuvem reduzidos em mais de 60%.
- Maior produtividade do desenvolvedor
- Tempo de depuração reduzido
Se meu preâmbulo despertou sua empolgação e você está ansioso para começar, você veio ao lugar certo. Mas caso você precise de mais convencimento, não deixe de conferir o fantástico artigo intitulado snorql — diagnosticando bancos de dados como um chefe .
O que esperar deste post?
O Snorql expõe várias métricas, como consultas ativas, consultas bloqueadas, estatísticas de índice, crescimento do banco de dados, etc... lista completa aqui . Para esta postagem, selecionaremos consultas ativas. O Active Queries é uma ferramenta valiosa para usuários do SQL Server que precisam monitorar e otimizar o desempenho de suas consultas em tempo real.
Nesta postagem, mostrarei como integrar snorqlem um aplicativo Kotlin Dropwizard simples ( você também pode replicar o mesmo em qualquer outro Java Framework ). No final deste tutorial, seu aplicativo exporá uma API RESTful usando a qual você poderá descobrir as estatísticas de consulta ao vivo para qualquer banco de dados.
Conheça suas ferramentas
Para aqueles que já estão familiarizados com Kotlin, Dropwizard e Maven, sinta-se à vontade para pular esta seção.
Dropwizard
Dropwizard é um framework Java leve para construir serviços web RESTful. O Dropwizard reúne bibliotecas estáveis, como Jersey, Jackson e JDBI, em um pacote leve. Para mais detalhes visitehttps://www.dropwizard.io/en/latest/.
Curiosidade: Dropwizard foi inspirado em Ruby on Rails, um popular framework de aplicações web na linguagem de programação Ruby. Os criadores do Dropwizard queriam trazer o mesmo nível de produtividade e facilidade de uso para o ecossistema Java.
KotlinGenericName
Kotlin é uma linguagem de programação que suporta programação funcional e orientada a objetos criada pela JetBrains, uma empresa de desenvolvimento de software com sede na Rússia. A sintaxe e os conceitos de Kotlin são semelhantes a C#, Scala e Java. Para mais detalhes visitehttps://kotlinlang.org/
Um fato engraçado sobre Kotlin é que seu nome é derivado de uma ilha perto de São Petersburgo, na Rússia, chamada Kotlin Island. Os criadores de Kotlin escolheram esse nome porque muitos deles vivem em São Petersburgo ou nos arredores e a ilha é um marco bem conhecido na área.
Especialista
Maven é uma ferramenta de gerenciamento e automação de compilação para Java. Com o Maven, fica fácil definir a construção do software e as dependências.
Curiosidade: Maven foi criado por Jason van Zyl em 2002 enquanto esperava um voo no Aeroporto O'Hare de Chicago.
Configurando sua máquina local
Você encontrará muitos artigos e vídeos do YouTube para o mesmo, no entanto, anexei algumas referências rápidas
- Instalando e Configurando o maven —https://www.freecodecamp.org/news/how-to-get-started-with-maven-36851d8cfd96/
- Instalando o Kotlin —https://www.oreilly.com/library/view/kotlin-cookbook/9781492046660/ch01.html
- Instalando o IntelliJ —https://www.jetbrains.com/help/idea/installation-guide.html
Esta seção pressupõe que você já configurou Maven, Kotlin e IntelliJ (ou qualquer outro IDE com suporte para projetos maven).
Criando um novo projeto
- Clique em Arquivo –> Novo –> Projeto –> Maven. Você verá o seguinte pop-up
3. Seu IDE abrirá o arquivo pom.xml com o projeto com a seguinte estrutura.
Adicionando dependências
Antes de iniciar a implementação, precisamos adicionar dependências específicas necessárias para o projeto no pom.xmlarquivo. Vamos fazê-lo juntos.
Começamos definindo as versões do Kotlin & Dropwizard na tag de propriedades.
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<kotlin.version>1.4.10</kotlin.version>
<kotlin.compiler.languageVersion>1.4</kotlin.compiler.languageVersion>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
<dropwizard.version>2.0.13</dropwizard.version>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
jdbi3
Vamos precisar dessa dependência para fazer conexões com o banco de dados. Estou usando o jdbi3 aqui. Isso funcionará com qualquer versão do JDBC/JDBI.
Propriedades:
<jdbi.version>3.37.1</jdbi.version>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-core</artifactId>
<version>${jdbi.version}</version>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-sqlobject</artifactId>
<version>${jdbi.version}</version>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-kotlin-sqlobject</artifactId>
<version>${jdbi.version}</version>
</dependency>
Precisamos adicioná-lo para que o aplicativo possa se conectar com êxito ao banco de dados do servidor SQL. Omitir isso lhe dará a java.sql.SQLException: No suitable driver foundexceção infame. Aqui, estarei usando o snorql-extensionsmódulo que nos fornece métricas pré-definidas correspondentes ao servidor SQL, portanto, vamos com este driver.
Existe outro módulo, snorql-frameworkque é a estrutura básica da ferramenta subjacente. Vou explorar isso em um próximo artigo, onde saberemos como aproveitar esse framework para diferentes bancos de dados como Postgres, MySQL, etc…
Propriedades:
<mssql.version>9.2.1.jre8</mssql.version>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>${mssql.version}</version>
</dependency>
Bem, não posso omitir isso!
Propriedades:
<snorql.version>0.5.6</snorql.version>
<dependency>
<groupId>com.udaan.snorql</groupId>
<artifactId>snorql-extensions</artifactId>
<version>${snorql.version}</version>
</dependency>
Construir & Compilar
A próxima etapa é especificar os diretórios de origem em <sourceDirectory>e <testSourceDirectory>para compilar o código-fonte. Também configuraremos o plug-in Kotlin necessário para compilar nosso código-fonte.
Adicione o código pom.xmlconforme mostrado após a seção de dependências
<dependencies>
.....
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Assim que você fizer isso, verá o kotlin destacado em vermelho. Isso ocorre porque o caminho não existe no momento, o que nos leva à próxima seção → estrutura do projeto
Estrutura do Projeto
Esta é a estrutura do meu projeto. Você pode usar o mesmo para começar e depois personalizar de acordo com sua necessidade.
Código
Passo 1: Começamos criando um config.ymlarquivo dentro de src/main/resources. Podemos definir parâmetros neste arquivo de configuração que serão desserializados para uma instância de nossa classe de configuração. Precisamos adicionar pelo menos um parâmetro, caso contrário, o framework Dropwizard lançará um erro durante a compilação. Vamos em frente e adicionar isto:
environment: local
package com.hawkeye.configuration
import com.fasterxml.jackson.annotation.JsonProperty
import io.dropwizard.Configuration
class AppConfig(@JsonProperty("environment") val environment: String): Configuration()
Etapa 4: Em seguida, criamos BootstrapSnorqlApplicationo arquivo .kt dentro do caminho src/main/kotlin/com/hawkeyee adicionamos o seguinte código:
package com.hawkeye.core
import org.jdbi.v3.core.Jdbi
import org.jdbi.v3.core.kotlin.KotlinPlugin
import org.jdbi.v3.sqlobject.SqlObjectPlugin
import org.jdbi.v3.sqlobject.kotlin.KotlinSqlObjectPlugin
class JdbiFactory {
/**
* Return instance of JDBI with all plugins installed
* @param dbName Name of the DB to connect
*/
fun getInstance(dbName: String): Jdbi {
return Jdbi.create(getConnectionStringByDatabaseName(dbName))
.installPlugin(SqlObjectPlugin())
.installPlugin(KotlinPlugin())
.installPlugin(KotlinSqlObjectPlugin())
}
private fun getConnectionStringByDatabaseName(dbName: String): String {
return when(dbName) {
"db-prod" -> "jdbc:sqlserver://<server-name>:<port>;database=$dbName;user=<username>;password=<pwd>;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;"
else -> ""
}
}
}
Passo 5: Agora precisamos implementar a Connectioninterface que snorql-frameworkexpõe. Vamos chamá-lo SqlServerConnectione ele conterá os métodos necessários para snorqlinteragir com o banco de dados do usuário. Para este post, implementar o run()método sozinho seria suficiente. Exploraremos os outros em uma postagem de blog detalhada separada.
Crie um SqlServerConnection.ktarquivo no caminho src/main/kotlin/com/hawkeye/coree adicione o código abaixo.
package com.hawkeye.core
import com.udaan.snorql.framework.metric.Connection
import com.udaan.snorql.framework.models.HistoricalDatabaseResult
import javax.inject.Inject
class SqlServerConnection @Inject constructor(private val jdbi3Factory: JdbiFactory) : Connection {
override fun <T> run(databaseName:String, query: String, mapClass: Class<T>,params: Map<String, *>): List<T> {
return if(params.isNotEmpty()){
jdbi3Factory.getInstance(databaseName).withHandle<List<T>, Exception> { handle ->
handle.createQuery(query).bindMap(params).mapTo(mapClass).toList()
}
} else{
jdbi3Factory.getInstance(databaseName).withHandle<List<T>, Exception> { handle ->
handle.createQuery(query).mapTo(mapClass).toList() as List<T>
}
}
}
override fun storeData(storageBucketId: String, columns: List<String>, rows: List<List<Any>>) {
TODO("Not yet implemented")
}
override fun getHistoricalData(
storageBucketId: String,
metricId: String,
databaseName: String,
columns: List<String>,
paginationParams: Map<String, *>,
params: Map<String, *>
): HistoricalDatabaseResult {
TODO("Not yet implemented")
}
}
O snorql expõe muitas métricas SQL úteis que podem ser integradas e usadas com seu aplicativo. Para esta postagem, escolheremos a métrica mais relevante para quem já usou um banco de dados — Active Queries.
Consultas ativas, também conhecidas como estatísticas de consulta ao vivo, são um recurso do SQL Server que permite aos usuários monitorar o andamento de suas consultas em tempo real. Ele fornece informações sobre o status de uma consulta, como a porcentagem de conclusão, o tempo decorrido e o tempo restante estimado, bem como o plano de execução usado pelo SQL Server para processar a consulta.
Para isso, crie um SnorqlMetricsResource.ktarquivo dentro src/main/kotlin/com/hawkeye/resourcese adicione o código abaixo.
package com.hawkeye.resources
import com.fasterxml.jackson.annotation.JsonProperty
import com.udaan.snorql.extensions.performance.PerformanceEnums
import com.udaan.snorql.extensions.performance.models.ActiveQueryInput
import com.udaan.snorql.extensions.performance.models.ActiveQueryResult
import com.udaan.snorql.framework.metric.SqlMetricManager
import com.udaan.snorql.framework.models.IMetricRecommendation
import com.udaan.snorql.framework.models.MetricPeriod
import javax.ws.rs.Consumes
import javax.ws.rs.POST
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.container.AsyncResponse
import javax.ws.rs.container.Suspended
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response
@Path("/snorql")
class SnorqlMetricsResource {
/*
* Fetches list of active-queries metric for a [activeQueryMetricInput]
*/
@POST
@Path("activeQueries")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
fun getActiveQueryMetric(
input: Input,
@Suspended asyncResponse: AsyncResponse
) {
val activeQueryInput = ActiveQueryInput(
databaseName = input.databaseName,
metricPeriod = input.metricPeriod,
metricId = input.metricId
)
try {
val response = SqlMetricManager.getMetric<ActiveQueryInput, ActiveQueryResult, IMetricRecommendation>(
PerformanceEnums.ACTIVE_QUERIES.getId(),
activeQueryInput
)
asyncResponse.resume(Response.ok(response).build())
} catch (e: Exception) {
println("Something went wrong. Error : ${e.message as String}")
Response.status(500).entity(e.message as String).build()
}
}
}
data class Input (
@JsonProperty("metricId")
val metricId: String = PerformanceEnums.ACTIVE_QUERIES.getId(),
@JsonProperty("metricPeriod")
val metricPeriod: MetricPeriod,
@JsonProperty("databaseName")
val databaseName: String
)
package com.hawkeye
import com.google.inject.AbstractModule
import com.google.inject.Guice
import com.hawkeye.configuration.AppConfig
import com.hawkeye.core.SqlServerConnection
import com.hawkeye.resources.SnorqlMetricsResource
import com.udaan.snorql.extensions.SQLCommonMetrics
import com.udaan.snorql.framework.metric.SqlMetricManager
import io.dropwizard.Application
import io.dropwizard.setup.Environment
class HawkeyeApplication: Application<AppConfig>() {
companion object {
@JvmStatic fun main(args: Array<String>) = HawkeyeApplication().run(*args)
}
override fun run(appConfig: AppConfig, environment: Environment) {
environment.jersey().register(SnorqlMetricsResource::class.java)
registerSQLMetrics()
}
private fun registerSQLMetrics() {
val sqlServerConnectionInstance =
Guice.createInjector(SqlClientModule()).getInstance(SqlServerConnection::class.java)
SqlMetricManager.setConnection(sqlServerConnectionInstance)
SQLCommonMetrics.initialize()
}
}
class SqlClientModule : AbstractModule()
Crie e execute o aplicativo
Estamos prontos para executar nossoHawkeyeApplication
Para fazer isso, adicione a configuração conforme mostrado abaixo:
Observe que adicionamos server src/main/resources/config.ymlos argumentos do programa. Isso informa ao aplicativo Dropwizard para ser executado como um servidor e no local do arquivo de configuração fornecido.
Se tudo correr bem, você deve ver algo assim no terminal. Se não e você está gritando com alguns erros, não se preocupe, eu também cobri isso. Vá para a seção de perguntas frequentes e verifique os erros/resoluções que enfrentei.
Acesse o Postman ou qualquer outra ferramenta semelhante e teste a API. Clique em Importar -> Texto bruto -> Copie e cole a chamada curl fornecida abaixo -> Clique em Continuar -> Clique em Enviar
curl --location --request POST 'localhost:8080/snorql/activeQueries' \
--header 'Content-Type: application/json' \
--data-raw '{
"databaseName": "db-prod",
"metricId": "performance_activeQueries",
"metricPeriod": "REAL_TIME",
"recommendationRequired": true
}'
Aqui, consultamos para ver todas as consultas ativas do banco de dados db-prod. A resposta é dividida em 3 partes:
metricInput
Isso retorna a entrada fornecida pelo usuário com valores padrão em campos opcionais.
"metricInput": {
"from": null,
"to": null,
"recommendationRequired": false,
"metricId": "performance_activeQueries",
"metricPeriod": "REAL_TIME",
"databaseName": "db-prod"
}
Isso fornece a lista de consultas em execução neste momento em seu banco de dados, juntamente com todos os parâmetros relevantes associados a elas. Isso pode ser útil nos seguintes cenários:
- Quando você está tentando depurar o aumento da latência em seu banco de dados e deseja verificar se há alguma consulta de execução longa que está sobrecarregando sua CPU, observando arquivos
elapsedTime. Essas consultas de execução longa podem ser causadas por vários problemas, como planos de consulta ineficientes, indexação abaixo do ideal ou consultas mal escritas. Além disso, também podemos usar isso para analisar a distribuição dos tempos de execução da consulta no banco de dados e tomar medidas proativas para evitar problemas antes que eles ocorram. - Quando você está em um estado em que o banco de dados parece travado ou um processo não está prosseguindo, verificar o bloqueio do banco de dados faz muito sentido e você pode descobrir o sessionId exato que bloqueia essa consulta com a ajuda de
blockedBy. - O
openTransactionCountparâmetro é usado para monitorar o número de transações abertas em um banco de dados em um determinado momento. Se essa contagem for consistentemente alta, isso pode indicar que há transações que não estão sendo confirmadas ou revertidas em tempo hábil. Isso pode levar a problemas como contenção de recursos, maior bloqueio e bloqueios, que podem afetar negativamente o desempenho e a disponibilidade do banco de dados. Ao usar oopenTransactionCountparâmetro em conjunto com outras métricas de desempenho, como tempo de execução da consulta e tempo de espera da consulta, podemos identificar corretamente as consultas que precisam de nossa atenção.
"metricOutput": {
"result": {
"queryList": [
{
"sessionId": 627,
"status": "running",
"blockedBy": 0,
"waitType": null,
"waitResource": "",
"percentComplete": 0,
"waitTime": "00:00:00",
"cpuTime": 2,
"logicalReads": 19,
"reads": 0,
"writes": 0,
"elapsedTime": "0d 00:00:00",
"queryText": "<?query -- \\n\\nSELECT DISTINCT ffi.shipment_group_id, so.extra_data, so.order_type, cac.amount_in_paisa as consumed_amount_in_paisa FROM fulfillment_line_items ffi JOIN seller_order_line ol on ol.order_line_id = ffi.order_line_id JOIN seller_order so on so.order_id = ol.seller_order_id LEFT OUTER JOIN collected_advance_consumable cac on cac.consumed_for = ffi.shipment_group_id WHERE ffi.shipment_group_id in (@P0)\\n\\n--?>",
"storedProc": "",
"command": "SELECT",
"loginName": "udaan",
"hostName": "order-processing-daemons-6fdfb74677-86xtx",
"programName": "order-processing-daemons-6fdfb74677-86xtx/order-mgmt",
"hostProcessId": 0,
"lastRequestEndTime": "2023-02-24 10:36:18.283",
"loginTime": "2023-02-24 10:32:52.12",
"openTransactionCount": 0
}
]
},
"recommendation": null
}
Esta seção nos dá detalhes sobre as consultas usadas pelo snorql para gerar a saída acima. Isso também vem com uma breve descrição e referência rápida à documentação oficial da Microsoft.
O supportsHistorical& minimumRepeatIntervalfaz parte de um recurso em que o snorql nos ajuda a registrar e armazenar o conjunto de resultados de uma saída de consulta em um determinado intervalo, permitindo revisitar os dados anteriores. Isso ajudará em autópsias, triagem de problemas e localização de anomalias. Esse recurso exige uma postagem detalhada separada.
"metadata": {
"underlyingQueries": [
"SELECT sessionId=s.session_id\\n ,status=r.STATUS\\n ,blockedBy=r.blocking_session_id\\n ,waitType=r.wait_type\\n, waitResource=r.wait_resource\\n, waitTime=CONVERT(VARCHAR, DATEADD(ms, r.wait_time, 0), 8)\\n, cpuTime=r.cpu_time\\n, logicalReads=r.logical_reads\\n, r.reads\\n ,r.writes,\\n r.percent_complete as percentComplete\\n,elapsedTime=CONVERT(varchar, (r.total_elapsed_time/1000 / 86400))+ 'd ' +\\n CONVERT(VARCHAR, DATEADD(ms, r.total_elapsed_time, 0), 8)\\n ,queryText=CAST((\\n '<?query -- ' + CHAR(13) + CHAR(13) + Substring(st.TEXT, (r.statement_start_offset / 2) + 1, (\\n (\\n CASE r.statement_end_offset\\n WHEN - 1\\n THEN Datalength(st.TEXT)\\n ELSE r.statement_end_offset\\n END - r.statement_start_offset\\n) / 2\\n) + 1) + CHAR(13) + CHAR(13) + '--?>'\\n ) AS XML)\\n, storedProc=COALESCE(\\n QUOTENAME(DB_NAME(st.dbid)) + N'.' + \\n QUOTENAME(OBJECT_SCHEMA_NAME(st.objectid, st.dbid)) + N'.' +\\n QUOTENAME(OBJECT_NAME(st.objectid, st.dbid)), ''\\n)\\n --,qp.query_plan AS 'xml_plan' -- uncomment (1) if you want to see plan\\n, r.command\\n ,loginName=s.login_name\\n ,hostName=s.host_name\\n ,programName=s.program_name\\n ,hostProcessId=s.host_process_id\\n ,lastRequestEndTime=s.last_request_end_time\\n ,loginTime=s.login_time\\n ,openTransactionCount=r.open_transaction_count\\n FROM sys.dm_exec_sessions AS s\\n INNER JOIN sys.dm_exec_requests AS r ON r.session_id = s.session_id\\n CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS st\\n --OUTER APPLY sys.dm_exec_query_plan(r.plan_handle) AS qp -- uncomment (2) if you want to see plan \\n ORDER BY r.blocking_session_id"
],
"referenceDocumentation": [],
"description": "",
"supportsHistorical": true,
"minimumRepeatInterval": "10"
}
Se você quiser conferir o código completo, visite aqui emhttps://github.com/lakshmi-udaan/hawkeye
Próximos passos
O que fizemos?
Tendo obtido os dados necessários, partimos para criar uma poderosa ferramenta de visualização. Para esse fim, aumentamos as APIs com uma camada de interface do usuário amigável e produzimos o SQL Dashboard, que fornece uma visão panorâmica abrangente de todos os bancos de dados e visualizações detalhadas e granulares de cada um. Os desenvolvedores de nossa organização dependem muito dele para tarefas relacionadas a SQL. Infelizmente, atualmente não é de código aberto, mas se você estiver interessado, ficarei feliz em discutir mais. Aqui está uma prévia do mesmo.
O que você pode fazer?
Agora que você sabe como usar o snorql, é apenas uma questão de tempo antes de liberar todo o seu potencial e começar a aumentar exponencialmente o desempenho do seu banco de dados.
Você pode encontrar a lista de todas as métricas disponíveis aqui . Você pode obter essas métricas simplesmente alterando os parâmetros que estão sendo passados internamente, SqlMetricManager.getMetric()como fizemos ao criar o endpoint da API para Active Queries aqui .
Em meu próximo post, vamos nos aprofundar em como podemos usar algumas dessas métricas para aumentar a produtividade do seu banco de dados em 10x.
Obrigado por ler!
perguntas frequentes
- Não foi possível encontrar o artefato com.udaan.snorql:snorql-framework:pom:0.5.6 em xyz
Solução: faça um arquivomvn clean install. Isso limpará tudo o que foi criado a partir da compilação anterior e, em seguida, criará seu projeto e adicionará os jars ao seu repositório maven local (.m2\repository) - Se você for solicitado com este erro Solução: Você precisa carregar arquivos . Você perdeu a adição
java.sql.SQLException: No suitable driver foundjdbc drivermssql-jdbc
https://udaan-com.github.io/snorql/
https:///engineering-udaan/snorql-diagnosing-databases-like-a-boss-54f2e8fbf57a





































![O que é uma lista vinculada, afinal? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)