Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 30, 2025

Adds beginTransaction(), commit(), rollback(), and executeInTransaction(Consumer<Repository>) methods to the Repository interface for atomic multi-operation workflows.

Changes

  • Repository interface: Added four transaction methods with comprehensive Javadoc

    • Manual control via beginTransaction(), commit(), rollback()
    • Convenience method executeInTransaction() with automatic commit/rollback handling
  • SQLRepository: ThreadLocal-based transaction connection management

    • Stores active transaction connection per thread
    • Integrates with HikariDatabase to share connections across operations
  • HikariDatabase: Transaction-aware connection pooling

    • Modified getConnection() to return transaction connection when active
    • Updated all database operation methods to reuse transaction connections
    • Prevents connection pool exhaustion during multi-operation transactions
  • JsonRepository: Throws UnsupportedOperationException for all transaction methods

  • Tests: Added 8 test cases covering commit, rollback, error handling, and isolation

Usage

// Automatic transaction management
repository.executeInTransaction(repo -> {
    repo.save(entity1);
    repo.save(entity2);
    repo.deleteById(oldId);
    // Auto-commits on success, rolls back on exception
});

// Manual transaction control
repository.beginTransaction();
try {
    repository.save(entity1);
    repository.commit();
} catch (Exception e) {
    repository.rollback();
}

SQLite already declares SupportType.TRANSACTION support; JSON databases skip transaction tests via JUnit Assumptions.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • hub.spigotmc.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=384m -XX:&#43;HeapDumpOnOutOfMemoryError -Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/gradle-daemon-main-8.13.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/agents/gradle-instrumentation-agent-8.13.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.13 (dns block)
  • jitpack.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=384m -XX:&#43;HeapDumpOnOutOfMemoryError -Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/gradle-daemon-main-8.13.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/agents/gradle-instrumentation-agent-8.13.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.13 (dns block)
  • s01.oss.sonatype.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=384m -XX:&#43;HeapDumpOnOutOfMemoryError -Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/gradle-daemon-main-8.13.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/agents/gradle-instrumentation-agent-8.13.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.13 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Add transaction management methods:

beginTransaction()
commit()
rollback()
executeInTransaction(Consumer<Repository>)

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 4 commits October 30, 2025 10:29
Co-authored-by: Robotv2 <81443374+Robotv2@users.noreply.github.com>
…entations

Co-authored-by: Robotv2 <81443374+Robotv2@users.noreply.github.com>
… comprehensive transaction tests

Co-authored-by: Robotv2 <81443374+Robotv2@users.noreply.github.com>
…management

Co-authored-by: Robotv2 <81443374+Robotv2@users.noreply.github.com>
Copilot AI changed the title [WIP] Add transaction management methods for database operations Add transaction management to Repository interface Oct 30, 2025
Copilot AI requested a review from Robotv2 October 30, 2025 10:44
@Robotv2 Robotv2 requested a review from Copilot October 30, 2025 11:08
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request adds transaction support to the repository API, enabling atomic operations across multiple database actions. The implementation includes transaction management methods and proper connection handling for pooled database connections.

  • Adds transaction management methods (beginTransaction(), commit(), rollback(), executeInTransaction()) to the Repository interface
  • Implements transaction support in SQLRepository using ThreadLocal connections
  • Updates HikariDatabase to handle transactional and non-transactional connections appropriately
  • Adds comprehensive test coverage for transaction scenarios

Reviewed Changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
gradlew Standard Gradle wrapper script added for build automation
Anchor-api/src/main/java/fr/robotv2/anchor/api/repository/Repository.java Defines transaction management methods in the Repository interface
Anchor-sql/src/main/java/fr/robotv2/anchor/sql/repository/SQLRepository.java Implements transaction support with ThreadLocal connection management
Anchor-sql/src/main/java/fr/robotv2/anchor/sql/database/HikariDatabase.java Updates database operations to support transactional connections
Anchor-json/src/main/java/fr/robotv2/anchor/json/JsonRepository.java Throws UnsupportedOperationException for transaction methods
Anchor-api/src/testFixtures/java/fr/robotv2/anchor/test/AbstractUserLongTest.java Adds comprehensive transaction test cases

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +205 to +224
// Only close the connection if it's a real transaction connection
// For pooled connections managed by HikariDatabase, let the pool handle it
if (!(database instanceof HikariDatabase)) {
connection.close();
}
} catch (SQLException exception) {
logger.log(Level.WARNING, "Failed to reset auto-commit", exception);
}

// Clear the transaction connection from ThreadLocal
transactionConnection.remove();

// If this is a HikariDatabase, clear the transaction connection and close it
if (database instanceof HikariDatabase) {
((HikariDatabase) database).setTransactionConnection(null);
try {
connection.close();
} catch (SQLException exception) {
logger.log(Level.WARNING, "Failed to close connection", exception);
}
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The connection cleanup logic contains contradictory conditional branches. Lines 207-209 skip closing the connection for HikariDatabase, but lines 218-225 then explicitly close it for HikariDatabase. This creates confusing and potentially inefficient logic. Simplify by removing the first conditional check and always handling HikariDatabase connections in the second block, or consolidate the logic into a single conditional structure.

Suggested change
// Only close the connection if it's a real transaction connection
// For pooled connections managed by HikariDatabase, let the pool handle it
if (!(database instanceof HikariDatabase)) {
connection.close();
}
} catch (SQLException exception) {
logger.log(Level.WARNING, "Failed to reset auto-commit", exception);
}
// Clear the transaction connection from ThreadLocal
transactionConnection.remove();
// If this is a HikariDatabase, clear the transaction connection and close it
if (database instanceof HikariDatabase) {
((HikariDatabase) database).setTransactionConnection(null);
try {
connection.close();
} catch (SQLException exception) {
logger.log(Level.WARNING, "Failed to close connection", exception);
}
} catch (SQLException exception) {
logger.log(Level.WARNING, "Failed to reset auto-commit", exception);
}
// Clear the transaction connection from ThreadLocal
transactionConnection.remove();
try {
if (database instanceof HikariDatabase) {
((HikariDatabase) database).setTransactionConnection(null);
}
connection.close();
} catch (SQLException exception) {
logger.log(Level.WARNING, "Failed to close connection", exception);

Copilot uses AI. Check for mistakes.

private void cleanupTransaction(Connection connection) {
try {
connection.setAutoCommit(true);
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If connection.setAutoCommit(true) fails and throws an exception, the ThreadLocal cleanup (line 215) and HikariDatabase cleanup (lines 218-225) will never execute. This can lead to connection leaks and ThreadLocal memory leaks. Move the transactionConnection.remove() and HikariDatabase cleanup outside the try-catch block or use a nested try-finally structure to ensure cleanup always occurs.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants