Skip to content

Commit

Permalink
Fix connection leak on multi transaction mode (#195)
Browse files Browse the repository at this point in the history
  • Loading branch information
rainbowdashlabs authored Aug 7, 2024
1 parent eb575e4 commit af83eca
Show file tree
Hide file tree
Showing 21 changed files with 532 additions and 155 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ plugins {

publishData {
useEldoNexusRepos(false)
publishingVersion = "2.2.1"
publishingVersion = "2.2.2"
}

group = "de.chojo.sadu"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import de.chojo.sadu.datasource.DataSourceCreator;
import de.chojo.sadu.mariadb.databases.MariaDb;
import de.chojo.sadu.mariadb.mapper.MariaDbMapper;
import de.chojo.sadu.queries.api.configuration.QueryConfiguration;
import de.chojo.sadu.queries.api.query.Query;
import de.chojo.sadu.queries.configuration.QueryConfiguration;
import org.intellij.lang.annotations.Language;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import de.chojo.sadu.datasource.DataSourceCreator;
import de.chojo.sadu.postgresql.databases.PostgreSql;
import de.chojo.sadu.postgresql.mapper.PostgresqlMapper;
import de.chojo.sadu.queries.api.configuration.QueryConfiguration;
import de.chojo.sadu.queries.api.query.Query;
import de.chojo.sadu.queries.configuration.QueryConfiguration;
import org.intellij.lang.annotations.Language;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import de.chojo.sadu.core.base.DataSourceProvider;
import de.chojo.sadu.core.exceptions.ThrowingFunction;
import de.chojo.sadu.queries.configuration.QueryConfiguration;
import de.chojo.sadu.queries.api.configuration.QueryConfiguration;
import de.chojo.sadu.queries.query.QueryImpl;

import javax.sql.DataSource;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* Copyright (C) RainbowDashLabs and Contributor
*/

package de.chojo.sadu.queries.api.configuration;

import de.chojo.sadu.queries.api.configuration.context.QueryContext;

import java.sql.SQLException;

public interface ActiveQueryConfiguration extends QueryConfiguration {
/**
* Handles a SQLException by invoking the exceptionHandler consumer, logging the exception,
* and potentially throwing a WrappedQueryExecutionException.
*
* @param e the SQLException to handle
*/
void handleException(SQLException e);

QueryContext context();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* Copyright (C) RainbowDashLabs and Contributor
*/

package de.chojo.sadu.queries.api.configuration;

import java.sql.Connection;

public interface ConnectedQueryConfiguration extends ActiveQueryConfiguration, AutoCloseable {
Connection connection();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* Copyright (C) RainbowDashLabs and Contributor
*/

package de.chojo.sadu.queries.api.configuration;

import de.chojo.sadu.mapper.RowMapperRegistry;
import de.chojo.sadu.queries.api.query.ParsedQuery;
import de.chojo.sadu.queries.configuration.ConnectedQueryConfigurationImpl;
import de.chojo.sadu.queries.configuration.QueryConfigurationBuilder;
import de.chojo.sadu.queries.api.configuration.context.QueryContext;
import org.intellij.lang.annotations.Language;

import javax.sql.DataSource;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

@SuppressWarnings("removal")
public interface QueryConfiguration extends de.chojo.sadu.queries.configuration.QueryConfiguration{
AtomicReference<QueryConfiguration> DEFAULT = new AtomicReference<>(null);

/**
* Retrieves the default QueryConfiguration.
*
* @return the default QueryConfiguration
*/
static QueryConfiguration getDefault() {
return Objects.requireNonNull(DEFAULT.get(), "You need to configure the configuration first by calling QueryConfiguration.setDefault()");
}

/**
* Sets the default query configuration.
*
* @param configuration the query configuration to set as default
*/
static void setDefault(QueryConfiguration configuration) {
DEFAULT.set(configuration);
}

/**
* Creates a new QueryConfigurationBuilder instance with the given DataSource.
*
* @param source the DataSource to use for QueryConfiguration
* @return a QueryConfigurationBuilder instance
*/
static QueryConfigurationBuilder builder(DataSource source) {
return new QueryConfigurationBuilder(source);
}

/**
* Returns a new QueryConfiguration object with the provided query and other configuration settings.
*
* @param context the query to be associated with the configuration
* @return the new QueryConfiguration object
*/
ActiveQueryConfiguration forQuery(QueryContext context);

/**
* Retrieves the value of the atomic flag.
*
* @return {@code true} if atomic flag is set; {@code false} otherwise.
*/
boolean atomic();

/**
* Retrieves the value of the throwExceptions field.
*
* @return the value of the throwExceptions field
*/
boolean throwExceptions();

/**
* Retrieves the {@link RowMapperRegistry} object from the QueryConfiguration.
*
* @return The {@link RowMapperRegistry} object from the QueryConfiguration.
*/
RowMapperRegistry rowMapperRegistry();

/**
* Returns the DataSource object associated with this QueryConfiguration.
*
* @return the DataSource object
*/
DataSource dataSource();

/**
* Executes a SQL query with the given SQL statement and format arguments.
*
* @param sql the SQL statement to be executed
* @param format the format arguments to be applied to the SQL statement
* @return a parsed query ready for execution
*/
ParsedQuery query(@Language("sql") String sql, Object... format);

/**
* Returns a new instance of the ConnectedQueryConfiguration class with the "single transaction" configuration applied.
* This means that a single transaction will be used for the queries executed using this configuration.
*
* @return A new instance of the ConnectedQueryConfiguration class with the "single transaction" configuration applied.
*/
ConnectedQueryConfigurationImpl withSingleTransaction();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* Copyright (C) RainbowDashLabs and Contributor
*/

package de.chojo.sadu.queries.api.configuration.context;

import de.chojo.sadu.queries.api.exception.ExceptionHolder;

public interface QueryContext extends ExceptionHolder {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* Copyright (C) RainbowDashLabs and Contributor
*/

package de.chojo.sadu.queries.api.exception;

import java.util.List;

public interface ExceptionHolder {
List<Exception> exceptions();

void logException(Exception e);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

package de.chojo.sadu.queries.api.query;

import de.chojo.sadu.queries.configuration.QueryConfiguration;
import de.chojo.sadu.queries.api.configuration.QueryConfiguration;
import de.chojo.sadu.queries.query.ParsedQueryImpl;
import de.chojo.sadu.queries.query.QueryImpl;
import org.intellij.lang.annotations.Language;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* Copyright (C) RainbowDashLabs and Contributor
*/

package de.chojo.sadu.queries.configuration;

import de.chojo.sadu.mapper.RowMapperRegistry;
import de.chojo.sadu.queries.api.configuration.ActiveQueryConfiguration;
import de.chojo.sadu.queries.api.configuration.context.QueryContext;
import de.chojo.sadu.queries.exception.WrappedQueryExecutionException;
import org.jetbrains.annotations.NotNull;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.function.Consumer;

public class ActiveQueryConfigurationImpl extends QueryConfigurationImpl implements ActiveQueryConfiguration {
protected final @NotNull QueryContext context;

public ActiveQueryConfigurationImpl(@NotNull DataSource dataSource, boolean atomic, boolean throwExceptions, Consumer<SQLException> exceptionHandler, RowMapperRegistry rowMapperRegistry, @NotNull QueryContext context) {
super(dataSource, atomic, throwExceptions, exceptionHandler, rowMapperRegistry);
this.context = context;
}

@Override
public void handleException(SQLException e) {
exceptionHandler.accept(e);
context.logException(e);
if (throwExceptions) {
throw (WrappedQueryExecutionException) new WrappedQueryExecutionException(e.getMessage()).initCause(e);
}
}

public @NotNull QueryContext context() {
return context;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* Copyright (C) RainbowDashLabs and Contributor
*/

package de.chojo.sadu.queries.configuration;

import de.chojo.sadu.mapper.RowMapperRegistry;
import de.chojo.sadu.queries.api.configuration.ConnectedQueryConfiguration;
import de.chojo.sadu.queries.api.configuration.context.QueryContext;
import de.chojo.sadu.queries.exception.WrappedQueryExecutionException;
import org.jetbrains.annotations.Nullable;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.function.Consumer;

public class ConnectedQueryConfigurationImpl extends ActiveQueryConfigurationImpl implements ConnectedQueryConfiguration {
private Connection connection;

ConnectedQueryConfigurationImpl(QueryContext context, DataSource dataSource, @Nullable Connection connection, boolean atomic, boolean throwExceptions, Consumer<SQLException> exceptionHandler, RowMapperRegistry rowMapperRegistry) {
super(dataSource, atomic, throwExceptions, exceptionHandler, rowMapperRegistry, context);
this.connection = connection;
}

@Override
public ConnectedQueryQueryConfigurationDelegate forQuery(QueryContext context) {
// TODO: Maybe store the different query instances from the context in the future.
return new ConnectedQueryQueryConfigurationDelegate(this);
}

@Override
public void close() {
if (connection == null) return;
try (var conn = connection) {
if (context.exceptions().isEmpty()) {
if (atomic()) {
conn.commit();
}
}
} catch (SQLException e) {
handleException(e);
}
}

@Override
public Connection connection() {
if (connection == null) {
try {
connection = dataSource.getConnection();
connection.setAutoCommit(!atomic);
} catch (SQLException e) {
handleException(e);
throw (WrappedQueryExecutionException) new WrappedQueryExecutionException(e.getMessage()).initCause(e);
}
}
return connection;
}
}
Loading

0 comments on commit af83eca

Please sign in to comment.