Skip to content

Commit

Permalink
Added support for stored procedures and functions
Browse files Browse the repository at this point in the history
  • Loading branch information
CollinAlpert committed Mar 20, 2020
1 parent 4c59d7a commit 14f3a23
Show file tree
Hide file tree
Showing 16 changed files with 436 additions and 127 deletions.
23 changes: 0 additions & 23 deletions .github/workflows/post-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,6 @@ jobs:
nexus_username: ${{ secrets.nexus_username }}
nexus_password: ${{ secrets.nexus_password }}

github_deploy:
name: Deploy to GitHub Packages
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v1

- name: Setup JDK 13
uses: actions/setup-java@v1
with:
java-version: '13'

- name: Release to GitHub Package Registry
env:
github_token: ${{ secrets.GITHUB_TOKEN }}
gpg_private_key: ${{ secrets.gpg_private_key }}
gpg_passphrase: ${{ secrets.gpg_passphrase }}
run: |
mkdir -p ~/.m2
echo "${gpg_private_key}" | gpg --batch --import
echo "<settings><servers><server><id>github</id><username>CollinAlpert</username><password>${github_token}</password></server></servers></settings>" > ~/.m2/settings.xml
mvn clean deploy -B -e -Dmaven.wagon.http.pool=false -DaltDeploymentRepository=github::default::https://maven.pkg.github.com/CollinAlpert/Java2DB -Dgpg.passphrase=${gpg_passphrase}
github_release:
name: Create GitHub release
runs-on: ubuntu-latest
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Include the Maven artifact:
<dependency>
<groupId>com.github.collinalpert</groupId>
<artifactId>java2db</artifactId>
<version>5.3.0</version>
<version>5.4.0</version>
</dependency>
```
Or include the [JAR](https://github.com/CollinAlpert/Java2DB/releases/latest) in your project.
Expand Down Expand Up @@ -157,6 +157,9 @@ You can also check if a table has at least one row by calling `personService.any
### Duplicate value checking
To check if a column's values are unique in a table, use the `hasDuplicates` method provided by the `BaseService`. It will return `true` if there is at least one duplicate value and false if all the values are unique.

### Programmability
The `DBConnection` class offers the possibility to call a stored procedure and a function. Simply use the `callStoredProcedure` or `callFunction` method, respectively and pass in the class you would like the result mapped to. Using the `@Ignore` annotation will also work with these kinds of calls. Please make sure your class has an empty constructor.

### Asynchronous operations
As of version 4.0 it is possible to execute all of the CRUD operations asynchronously.
To use the asynchronous methods with your service classes, the individual service class should inherit from the
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.collinalpert</groupId>
<artifactId>java2db</artifactId>
<version>5.3.0</version>
<version>5.4.0</version>
<packaging>jar</packaging>

<name>Java2DB</name>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.github.collinalpert.java2db.database;

import com.github.collinalpert.java2db.exceptions.ConnectionFailedException;
import com.github.collinalpert.java2db.mappers.FieldMapper;
import com.github.collinalpert.java2db.queries.Queryable;
import com.github.collinalpert.java2db.queries.StoredProcedureQuery;
import com.github.collinalpert.java2db.queries.async.AsyncQueryable;
import com.github.collinalpert.java2db.queries.async.AsyncStoredProcedureQuery;
import com.mysql.cj.exceptions.CJCommunicationsException;
import com.mysql.cj.jdbc.exceptions.CommunicationsException;

Expand All @@ -10,6 +15,12 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

import static com.github.collinalpert.java2db.utilities.Utilities.supplierHandling;

/**
* @author Collin Alpert
Expand Down Expand Up @@ -130,7 +141,7 @@ public long update(String query) throws SQLException {
var statement = this.connection.createStatement();
log(query);
statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
return updateHelper(statement);
return updateInternal(statement);
}

/**
Expand All @@ -149,17 +160,7 @@ public long update(String query, Object... params) throws SQLException {

log(query);
statement.executeUpdate();
return updateHelper(statement);
}

private long updateHelper(Statement statement) throws SQLException {
statement.closeOnCompletion();
var set = statement.getGeneratedKeys();
if (set.next()) {
return set.getLong(1);
}

return -1;
return updateInternal(statement);
}

/**
Expand Down Expand Up @@ -194,14 +195,47 @@ public void close() {
}
}

public <T> Optional<T> callFunction(Class<T> returnType, String functionName, Object... arguments) throws SQLException {
var joiner = new StringJoiner(",");
for (int i = 0; i < arguments.length; i++) {
joiner.add("?");
}

try (var set = execute(String.format("select %s(%s);", functionName, joiner.toString()), arguments)) {
return new FieldMapper<>(returnType).map(set);
}
}

public <T> CompletableFuture<Optional<T>> callFunctionAsync(Consumer<SQLException> exceptionHandler, Class<T> returnType, String functionName, Object... arguments) {
return CompletableFuture.supplyAsync(supplierHandling(() -> this.callFunction(returnType, functionName, arguments), exceptionHandler));
}

public <T> Queryable<T> callStoredProcedure(Class<T> returnType, String storedProcedureName, Object... arguments) {
return new StoredProcedureQuery<>(returnType, this, storedProcedureName, arguments);
}

public <T> AsyncQueryable<T> callStoredProcedureAsync(Class<T> returnType, String storedProcedureName, Object... arguments) {
return new AsyncStoredProcedureQuery<>(returnType, this, storedProcedureName, arguments);
}

/**
* Prints queries to the console, while considering the {@link DBConnection#LOG_QUERIES} constant.
*
* @param text The message to print.
*/
private void log(String text) {
if (DBConnection.LOG_QUERIES) {
if (LOG_QUERIES) {
System.out.println(text);
}
}

private long updateInternal(Statement statement) throws SQLException {
statement.closeOnCompletion();
var set = statement.getGeneratedKeys();
if (set.next()) {
return set.getLong(1);
}

return -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.lang.reflect.Field;

/**
* Describes a foreign key reference to a specific table. This represents a field marked with the {@link com.github.collinalpert.java2db.annotations.ForeignKeyEntity} attribute.
* Describes a foreign key reference to a specific table. This represents a field marked with the {@link ForeignKeyEntity} attribute.
*
* @author Collin Alpert
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
*
* @author Collin Alpert
*/
public class BaseMapper<E extends BaseEntity> implements Mappable<E> {
public class EntityMapper<E extends BaseEntity> implements Mappable<E> {

private static final TableModule tableModule;

Expand All @@ -45,9 +45,11 @@ public class BaseMapper<E extends BaseEntity> implements Mappable<E> {
}

private Class<E> clazz;
private final Map<String, String> aliases;

public BaseMapper(Class<E> clazz) {
public EntityMapper(Class<E> clazz) {
this.clazz = clazz;
this.aliases = FieldModule.getInstance().getAliases(clazz);
}

/**
Expand All @@ -58,13 +60,13 @@ public BaseMapper(Class<E> clazz) {
* @throws SQLException if the {@link ResultSet#next()} call does not work as expected or if the entity fields cannot be set.
*/
@Override
public Optional<E> map(ResultSet set, Map<String, String> aliases) throws SQLException {
public Optional<E> map(ResultSet set) throws SQLException {
E entity = IoC.createInstance(this.clazz);
if (!set.next()) {
return Optional.empty();
}

setFields(set, entity, aliases);
setFields(set, entity);
set.close();
return Optional.of(entity);
}
Expand All @@ -77,9 +79,9 @@ public Optional<E> map(ResultSet set, Map<String, String> aliases) throws SQLExc
* @throws SQLException if the {@link ResultSet#next()} call does not work as expected or if the entity fields cannot be set.
*/
@Override
public List<E> mapToList(ResultSet set, Map<String, String> aliases) throws SQLException {
public List<E> mapToList(ResultSet set) throws SQLException {
var list = new ArrayList<E>();
mapInternal(set, list::add, aliases);
mapInternal(set, list::add);
return list;
}

Expand All @@ -91,9 +93,9 @@ public List<E> mapToList(ResultSet set, Map<String, String> aliases) throws SQLE
* @throws SQLException if the {@link ResultSet#next()} call does not work as expected or if the entity fields cannot be set.
*/
@Override
public Stream<E> mapToStream(ResultSet set, Map<String, String> aliases) throws SQLException {
public Stream<E> mapToStream(ResultSet set) throws SQLException {
var builder = Stream.<E>builder();
mapInternal(set, builder::add, aliases);
mapInternal(set, builder::add);
return builder.build();
}

Expand All @@ -105,9 +107,9 @@ public Stream<E> mapToStream(ResultSet set, Map<String, String> aliases) throws
* @throws SQLException if the {@link ResultSet#next()} call does not work as expected or if the entity fields cannot be set.
*/
@Override
public E[] mapToArray(ResultSet set, Map<String, String> aliases) throws SQLException {
public E[] mapToArray(ResultSet set) throws SQLException {
var module = new ArrayModule<>(this.clazz, 20);
mapInternal(set, module::addElement, aliases);
mapInternal(set, module::addElement);
return module.getArray();
}

Expand All @@ -117,16 +119,15 @@ public E[] mapToArray(ResultSet set, Map<String, String> aliases) throws SQLExce
* @param set The {@code ResultSet} to get the data from.
* @param keyMapping The key function of the map.
* @param valueMapping The value function of the map.
* @param aliases A map of column aliases needed to retrieve column data from the {@code ResultSet}.
* @param <K> The type of the keys in the map.
* @param <V> The type of the values in the map.
* @return A {@code Map} containing the {@code ResultSet}s data.
* @throws SQLException In case the {@code ResultSet} can't be read.
*/
@Override
public <K, V> Map<K, V> mapToMap(ResultSet set, Function<E, K> keyMapping, Function<E, V> valueMapping, Map<String, String> aliases) throws SQLException {
public <K, V> Map<K, V> mapToMap(ResultSet set, Function<E, K> keyMapping, Function<E, V> valueMapping) throws SQLException {
var map = new HashMap<K, V>();
mapInternal(set, x -> map.put(keyMapping.apply(x), valueMapping.apply(x)), aliases);
mapInternal(set, x -> map.put(keyMapping.apply(x), valueMapping.apply(x)));
return map;
}

Expand All @@ -137,10 +138,10 @@ public <K, V> Map<K, V> mapToMap(ResultSet set, Function<E, K> keyMapping, Funct
* @param handling The action to apply at each iteration of the given {@code ResultSet}.
* @throws SQLException Handling a {@code ResultSet} can possibly result in this exception being thrown.
*/
private void mapInternal(ResultSet set, Consumer<E> handling, Map<String, String> aliases) throws SQLException {
private void mapInternal(ResultSet set, Consumer<E> handling) throws SQLException {
while (set.next()) {
var entity = IoC.createInstance(this.clazz);
setFields(set, entity, aliases);
setFields(set, entity);
handling.accept(entity);
}

Expand All @@ -153,8 +154,8 @@ private void mapInternal(ResultSet set, Consumer<E> handling, Map<String, String
* @param set The {@link ResultSet} to get the data from.
* @param entity The Java entity to fill.
*/
private <TEntity extends BaseEntity> void setFields(ResultSet set, TEntity entity, Map<String, String> aliases) throws SQLException {
setFields(set, entity, tableModule.getTableName(entity.getClass()), aliases);
private <TEntity extends BaseEntity> void setFields(ResultSet set, TEntity entity) throws SQLException {
setFields(set, entity, tableModule.getTableName(entity.getClass()));
}

/**
Expand All @@ -164,7 +165,7 @@ private <TEntity extends BaseEntity> void setFields(ResultSet set, TEntity entit
* @param identifier The alias set for a certain entity used as a nested property.
* @param entity The Java entity to fill.
*/
private <TEntity extends BaseEntity> void setFields(ResultSet set, TEntity entity, String identifier, Map<String, String> aliases) throws SQLException {
private <TEntity extends BaseEntity> void setFields(ResultSet set, TEntity entity, String identifier) throws SQLException {
var fieldModule = FieldModule.getInstance();
var fields = fieldModule.getEntityFields(entity.getClass(), true);
for (var field : fields) {
Expand Down Expand Up @@ -204,7 +205,7 @@ private <TEntity extends BaseEntity> void setFields(ResultSet set, TEntity entit

@SuppressWarnings("unchecked")
var foreignKeyObject = IoC.createInstance((Class<? extends BaseEntity>) field.getType());
setFields(set, foreignKeyObject, aliases.get(field.getDeclaringClass().getSimpleName().toLowerCase() + "_" + field.getName()), aliases);
setFields(set, foreignKeyObject, this.aliases.get(field.getDeclaringClass().getSimpleName().toLowerCase() + "_" + field.getName()));
tryAction(() -> field.set(entity, foreignKeyObject));

continue;
Expand Down
Loading

0 comments on commit 14f3a23

Please sign in to comment.