Skip to content

Commit

Permalink
Issue helidon-io#6991 - Blocking DB Client: All simple Statement inte…
Browse files Browse the repository at this point in the history
…gration tests are passing

Signed-off-by: Tomáš Kraus <tomas.kraus@oracle.com>
  • Loading branch information
Tomas-Kraus committed Jun 27, 2023
1 parent 7511994 commit d40d508
Show file tree
Hide file tree
Showing 14 changed files with 1,005 additions and 172 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

class JdbcExecute extends CommonExecute {

// TODO: Move connection allocation to the statement execution
private final Connection connection;

private JdbcExecute(CommonClientContext context, Connection connection) {
Expand All @@ -36,7 +37,7 @@ private JdbcExecute(CommonClientContext context, Connection connection) {

@Override
public DbStatementQuery createNamedQuery(String statementName, String statement) {
return null;
return new JdbcStatementQuery(connection, JdbcExecuteContext.create(statementName, statement, context()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,21 @@ RS execute() {

}

// Abstract ancestor of index processing classes with AutoCloseable interface
static abstract class PrepareCloseable<RS> extends Prepare<RS> implements AutoCloseable {

PrepareCloseable(String statementString) {
super(statementString);
}

// Only DbStatementException exception may be thrown from close method
@Override
public abstract void close() throws DbStatementException;

}

// Indexed parameters to be set in PreparedStatement
static abstract class PrepareIndex<RS> extends Prepare<RS> implements AutoCloseable {
static abstract class PrepareIndex<RS> extends PrepareCloseable<RS> implements AutoCloseable {

private final PreparedStatement statement;

Expand Down Expand Up @@ -473,7 +486,7 @@ public void close() throws DbStatementException {
}

// Named parameters to be set in CallableStatement
static abstract class PrepareName<RS> extends Prepare<RS> implements AutoCloseable {
static abstract class PrepareName<RS> extends PrepareCloseable<RS> implements AutoCloseable {

private final JdbcCallableStatement statement;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import io.helidon.dbclient.DbStatementDml;
import io.helidon.dbclient.DbStatementException;

public class JdbcStatementDml extends JdbcStatement<DbStatementDml, Long, Long> implements DbStatementDml {
public class JdbcStatementDml
extends JdbcStatement<DbStatementDml, Long, Long>
implements DbStatementDml {

JdbcStatementDml(Connection connection, JdbcExecuteContext context) {
super(connection, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import io.helidon.dbclient.DbStatementException;
import io.helidon.dbclient.DbStatementGet;

public class JdbcStatementGet extends JdbcStatement<DbStatementGet, Optional<DbRow>, Optional<DbRow>> implements DbStatementGet {
public class JdbcStatementGet
extends JdbcStatement<DbStatementGet, Optional<DbRow>, Optional<DbRow>>
implements DbStatementGet {

JdbcStatementGet(Connection connection, JdbcExecuteContext context) {
super(connection, context);
Expand Down Expand Up @@ -59,7 +61,7 @@ Optional<DbRow> executeStatement(Connection connection, String statementString)
return Optional.empty();
}
} catch (SQLException ex) {
throw new DbStatementException("Failed to create Statement", statementString, ex);
throw new DbStatementException("Failed to execute Statement", statementString, ex);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.dbclient.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import io.helidon.dbclient.DbRow;
import io.helidon.dbclient.DbStatementException;
import io.helidon.dbclient.DbStatementQuery;

public class JdbcStatementQuery
extends JdbcStatement<DbStatementQuery, Stream<DbRow>, Stream<DbRow>>
implements DbStatementQuery {

JdbcStatementQuery(Connection connection, JdbcExecuteContext context) {
super(connection, context);
}

@Override
public Stream<DbRow> execute() {
return prepare().execute();
}

@Override
PrepareIndex<Stream<DbRow>> createPrepareIndex(PreparedStatement statement) {
return new JdbcStatementQuery.PrepareIndexQuery(context(), statement);
}

@Override
PrepareName<Stream<DbRow>> createPrepareName(JdbcCallableStatement statement) {
return new JdbcStatementQuery.PrepareNameQuery(context(), statement);
}

@Override
Stream<DbRow> executeStatement(Connection connection, String statementString) {
try {
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(statementString);
return StreamSupport.stream(
new DbRowSpliterator(rs, context(), statementString),
false)
.onClose(new CloseResources(rs, statement, statementString));
} catch (SQLException ex) {
throw new DbStatementException("Failed to execute Statement", statementString, ex);
}
}

private static final class PrepareIndexQuery extends PrepareIndex<Stream<DbRow>> {

private final JdbcExecuteContext context;

PrepareIndexQuery(JdbcExecuteContext context, PreparedStatement statement) {
super(context.statement(), statement);
this.context = context;
}

@Override
Stream<DbRow> execute() {
try {
ResultSet rs = preparedStatement().executeQuery();
return StreamSupport.stream(
new DbRowSpliterator(rs, context, statementString()),
false)
.onClose(new ClosePreparedResources(rs, this));
} catch (SQLException ex) {
throw new DbStatementException("Failed to execute PreparedStatement", statementString(), ex);
}
}

}

private static final class PrepareNameQuery extends PrepareName<Stream<DbRow>> {

private final JdbcExecuteContext context;

PrepareNameQuery(JdbcExecuteContext context, JdbcCallableStatement statement) {
super(context.statement(), statement);
this.context = context;
}

@Override
Stream<DbRow> execute() {
try {
ResultSet rs = callableStatement().executeQuery();
return StreamSupport.stream(
new DbRowSpliterator(rs, context, statementString()),
false)
.onClose(new ClosePreparedResources(rs, this));
} catch (SQLException ex) {
throw new DbStatementException("Failed to execute CallableStatement", statementString(), ex);
}
}

}

private static final class DbRowSpliterator extends Spliterators.AbstractSpliterator<DbRow> {

private final ResultSet rs;
private final JdbcExecuteContext context;
private final String statementString;

DbRowSpliterator(ResultSet rs, JdbcExecuteContext context, String statementString) {
super(Long.MAX_VALUE, Spliterator.ORDERED);
this.rs = rs;
this.context = context;
this.statementString = statementString;
}

@Override
public boolean tryAdvance(Consumer<? super DbRow> action) {
try {
if (rs.next()) {
action.accept(JdbcRow.create(rs,
context.dbMapperManager(),
context.mapperManager()));
return true;
} else {
return false;
}
} catch (SQLException ex) {
throw new DbStatementException("Failed to retrieve next row from ResultSet",
statementString,
ex);
}
}

}

private record CloseResources(ResultSet rs, Statement statement, String statementString) implements Runnable {
@Override
public void run() throws DbStatementException {
try {
rs.close();
} catch (SQLException ex) {
throw new DbStatementException("Failed to close ResultSet", statementString, ex);
} finally {
closeStatement();
}
}

// Close Statement and wrap any SQLException with DbStatementException
private void closeStatement() {
try {
statement.close();
} catch (SQLException ex) {
throw new DbStatementException("Failed to close Statement", statementString, ex);
}
}

}

private record ClosePreparedResources(ResultSet rs, PrepareCloseable<Stream<DbRow>> statement) implements Runnable {
@Override
public void run() {
try {
rs.close();
} catch (SQLException ex) {
throw new DbStatementException("Failed to close ResultSet", statement.statementString(), ex);
} finally {
statement.close();
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@
import io.helidon.config.Config;
import io.helidon.config.ConfigSources;
import io.helidon.dbclient.DbClient;
import io.helidon.dbclient.DbExecute;
import io.helidon.dbclient.DbMapper;
import io.helidon.dbclient.DbRow;

import org.junit.jupiter.api.Assertions;

import static io.helidon.tests.integration.dbclient.common.utils.Utils.verifyInsertPokemon;

/**
* Common testing code.
*/
Expand Down Expand Up @@ -135,22 +140,7 @@ public static DbClient initDbClient() {
/**
* Pokemon type POJO.
*/
public static final class Type {
private final int id;
private final String name;

public Type(int id, String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public String getName() {
return name;
}
public record Type(int id, String name) {

@Override
public String toString() {
Expand Down Expand Up @@ -219,6 +209,7 @@ public List<Type> getTypes() {
return types;
}

@SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument")
public Type[] getTypesArray() {
return types.toArray(new Type[types.size()]);
}
Expand Down Expand Up @@ -246,4 +237,15 @@ public String toString() {

}

protected static void addPokemon(Pokemon pokemon) {
POKEMONS.put(pokemon.getId(), pokemon);
long result = -1L;
try (DbExecute exec = DB_CLIENT.execute()) {
result = exec.namedInsert("insert-pokemon", pokemon.getId(), pokemon.getName());
} catch (Exception ex) {
Assertions.fail(ex);
}
verifyInsertPokemon(result, pokemon);
}

}
Loading

0 comments on commit d40d508

Please sign in to comment.