
org.nuiton.topia.persistence.support.TopiaSqlQuery Maven / Gradle / Ivy
package org.nuiton.topia.persistence.support;
/*
* #%L
* ToPIA Extension :: API
* %%
* Copyright (C) 2018 - 2022 Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* Wrap a Sql query some object.
*
* Implements the {@link #prepareResult(ResultSet)} to transform a result set
* row to an object.
*
* You can also do some stuff on the result set just after the query was
* executed using method {@link #afterExecuteQuery(ResultSet)}.
*
* @param the type of result data
* @since 2.5
*/
public abstract class TopiaSqlQuery {
/**
* Prepare the statement used to do the sql query.
*
* @param connection jdbc connection to use
* @return the statement containing the query to execute
* @throws SQLException if any problem
*/
public abstract PreparedStatement prepareQuery(Connection connection) throws SQLException;
/**
* given a result set, extract the data.
*
* @param set the result set
* @return the data extracted from the current set, or {@code null}
* @throws SQLException if any prob
*/
public abstract O prepareResult(ResultSet set) throws SQLException;
/**
* A hook to obtain the result set just after the query execute.
*
* @param set the result set just obtained
* @throws SQLException if any prob
* @since 2.6.4
*/
public void afterExecuteQuery(ResultSet set) throws SQLException {
// by default do nothing
}
/**
* Obtain the column names of a given result set using his metadata.
*
* @param set the result set to inspect
* @return the column names of the result set
* @throws SQLException if any pb
* @since 2.6.4
*/
protected String[] getColumnNames(ResultSet set) throws SQLException {
ResultSetMetaData metaData = set.getMetaData();
int columnCount = metaData.getColumnCount();
String[] result = new String[columnCount];
for (int i = 0; i < columnCount; i++) {
result[i] = metaData.getColumnName(i + 1);
}
return result;
}
/**
* From a given result set, let's count his number of row.
*
* Note: the result set must be scrollable to go back to
* before first row.
*
* @param set the result set to inspect
* @return the number of row of the given result set
* @throws SQLException if any pb
* @since 2.6.4
*/
protected long getNbRows(ResultSet set) throws SQLException {
//FIXME tchemit-2012-11-22 use set.last()
long nbRows = 0;
while (set.next()) {
nbRows++;
}
// go back before first row (be ware the resultset must be scrollable)
set.beforeFirst();
return nbRows;
}
/**
* Given the column names of the result set, transform the row of the
* result set to a map with column name as key.
*
* @param columnNames column names of the result set
* @param set the set to inspect
* @return the map for the given row of the result set
* @throws SQLException if any pb
* @since 2.6.4
*/
protected Map getRowAsMap(String[] columnNames,
ResultSet set) throws SQLException {
Map result = new LinkedHashMap<>();
int length = columnNames.length;
for (int i = 0; i < length; i++) {
String name = columnNames[i];
Object value = set.getObject(i + 1);
result.put(name, value);
}
return result;
}
/**
* Let's you easily create a {@code TopiaSqlQuery} using functional code-style.
*
* @param preparer used to create the {@link PreparedStatement}. See {@link TopiaSqlQuery#prepareQuery(Connection)}.
* @param transformer used to transform the {@link ResultSet} to the expected format. See {@link TopiaSqlQuery#prepareResult(ResultSet)}.
* @param the expected return type
* @return a new TopiaSqlQuery instance using the given interfaces
* @see TopiaSqlQuery#prepareQuery(Connection)
* @see TopiaSqlQuery#prepareResult(ResultSet)
*/
public static TopiaSqlQuery wrap(
SqlFunction preparer,
SqlFunction transformer
) {
return new TopiaSqlQuery<>() {
@Override
public PreparedStatement prepareQuery(Connection connection) throws SQLException {
return preparer.apply(connection);
}
@Override
public T prepareResult(ResultSet set) throws SQLException {
return transformer.apply(set);
}
};
}
/**
* Let's you easily create a {@code TopiaSqlQuery} using functional code-style.
*
* @param sqlQuery an SQL query used to create the {@link PreparedStatement}. See {@link TopiaSqlQuery#prepareQuery(Connection)}.
* @param transformer used to transform the {@link ResultSet} to the expected format. See {@link TopiaSqlQuery#prepareResult(ResultSet)}.
* @param the expected return type
* @return a new TopiaSqlQuery instance using the given interfaces
* @see TopiaSqlQuery#prepareQuery(Connection)
* @see TopiaSqlQuery#prepareResult(ResultSet)
*/
public static TopiaSqlQuery wrap(
String sqlQuery,
SqlFunction transformer
) {
return new TopiaSqlQuery<>() {
@Override
public PreparedStatement prepareQuery(Connection connection) throws SQLException {
return connection.prepareStatement(sqlQuery);
}
@Override
public T prepareResult(ResultSet set) throws SQLException {
return transformer.apply(set);
}
@Override
public Optional getSqlQuery() {
return Optional.of(sqlQuery);
}
};
}
/**
* This is optional : the instance may be capable to provide an SQL query. Otherwise this method will return an
* empty optional.
*
* @return the SQL query used or {@link Optional#empty()} if the SQL query is not available
*/
public Optional getSqlQuery() {
return Optional.empty();
}
/**
* This is optional : the instance may be capable to provide an args used for the SQL query. Otherwise this method
* will return an empty optional.
*
* @return the args as map or {@link Optional#empty()} if the SQL args are not available
*/
public Optional> getSqlArgs() {
return Optional.empty();
}
}