diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/DAO.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/DAO.java index 381044ac..09fbec37 100644 --- a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/DAO.java +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/DAO.java @@ -37,8 +37,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import io.jenetics.jpx.jdbc.SQL.Supplier; - /** * Abstract DAO class * @@ -85,8 +83,10 @@ public default RowParser single() { * * @return a new parser which parses a single selection result */ - public default RowParser> singleOpt() { - return rs -> rs.next() ? Optional.of(parse(rs)) : Optional.empty(); + public default RowParser> singleOpt() { + return rs -> rs.next() + ? SQL.Option.of(parse(rs)) + : SQL.Option.empty(); } /** @@ -138,6 +138,7 @@ void eval() throws SQLException { * * @return the parameter value. */ + @SuppressWarnings({"raw", "unchecked"}) public Object getValue() throws SQLException { Object value = _value.get(); if (value instanceof Optional) { @@ -270,7 +271,7 @@ public SQLQuery(final Connection conn, final String query) { } public SQLQuery on(final String name, final Object value) { - _params.add(Param.insert(name, () -> value)); + _params.add(Param.value(name, value)); return this; } @@ -391,21 +392,19 @@ public Batch batch(final String query) { return new Batch(_conn, query); } - public static Stored insertOrUpdate( + public static Stored put( final T value, - final SQL.Function>> select, + final SQL.Function>> select, final SQL.Function> insert, final SQL.Consumer> update ) throws SQLException { - final Optional> stored = select.apply(value); - if (stored.isPresent()) { - update.accept(stored.get()); - return stored.get().copy(value); - } else { - return insert.apply(value); - } + return select.apply(value) + .map(stored -> { + update.accept(stored); + return stored.copy(value); }) + .orElseGet(() -> insert.apply(value)); } /** diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/LinkDAO.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/LinkDAO.java index d4b55c5a..2ddf4cfb 100644 --- a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/LinkDAO.java +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/LinkDAO.java @@ -19,14 +19,14 @@ */ package io.jenetics.jpx.jdbc; +import static java.lang.String.format; +import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import java.net.URI; import java.sql.Connection; import java.sql.SQLException; -import java.util.Arrays; import java.util.List; -import java.util.Optional; import io.jenetics.jpx.Link; @@ -56,6 +56,9 @@ public LinkDAO(final Connection connection) { ) ); + static final class Href {} + static final class ID {} + /** * Insert the given link list into the DB. * @@ -67,9 +70,10 @@ public List> insert(final List links) throws SQLException { final String query = - "INSERT INTO link(href, text, type) VALUES({href}, {text}, {type});"; + "INSERT INTO link(href, text, type) " + + "VALUES({href}, {text}, {type});"; - return batch(query).insert(links, link -> Arrays.asList( + return batch(query).insert(links, link -> asList( Param.value("href", link.getHref().toString()), Param.value("text", link.getText()), Param.value("type", link.getType()) @@ -100,9 +104,10 @@ public List> update(final List> links) throws SQLException { final String query = - "UPDATE link SET text = {text}, type = {type} WHERE id = {id}"; + "UPDATE link SET text = {text}, type = {type} " + + "WHERE id = {id}"; - batch(query).update(links, link -> Arrays.asList( + batch(query).update(links, link -> asList( Param.value("text", link.map(Link::getText)), Param.value("type", link.map(Link::getType)), Param.value("id", link.getID()) @@ -122,10 +127,10 @@ public Stored update(final Stored link) throws SQLException { return update(singletonList(link)).get(0); } - public Stored insertOrUpdate(final Link link) throws SQLException { - return DAO.insertOrUpdate( + public Stored put(final Link link) throws SQLException { + return DAO.put( link, - l -> selectByHRef(l.getHref()), + l -> selectByHref(l.getHref()), this::insert, this::update ); @@ -142,6 +147,15 @@ public List> select() throws SQLException { .as(RowParser.list()); } + public SQL.Option> selectBy(final Href href) throws SQLException { + final String query = + "SELECT id, href, text, type " + + "FROM link " + + "WHERE href = {href}"; + + return null; + } + /** * Select a link by its DB ID. * @@ -149,7 +163,7 @@ public List> select() throws SQLException { * @return the selected link, if available * @throws SQLException if the select fails */ - public Optional> selectByID(final long id) + public SQL.Option> selectByID(final long id) throws SQLException { return sql("SELECT id, href, text, type FROM link WHERE id = {id};") @@ -164,7 +178,7 @@ public Optional> selectByID(final long id) * @return the selected links * @throws SQLException if the select fails */ - public Optional> selectByHRef(final URI href) + public SQL.Option> selectByHref(final URI href) throws SQLException { return sql("SELECT id, href, text, type FROM link WHERE href = {href}") diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PersonDAO.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PersonDAO.java index 1eb7d8a4..fe3f626a 100644 --- a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PersonDAO.java +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PersonDAO.java @@ -23,6 +23,7 @@ import java.sql.SQLException; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; import io.jenetics.jpx.Email; @@ -78,7 +79,7 @@ public List> insert(final List persons) private Long insertOrUpdate(final Optional link) throws SQLException { return link.isPresent() - ? dao(LinkDAO::of).insertOrUpdate(link.get()).getID() + ? dao(LinkDAO::of).put(link.get()).getID() : null; } diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQL.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQL.java index 42522b96..9241d83d 100644 --- a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQL.java +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQL.java @@ -22,32 +22,191 @@ import static java.util.Objects.requireNonNull; import java.sql.SQLException; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; /** + * Contains implementations of existing functional classes, which allow to throw + * a {@link SQLException}. + * * @author Franz Wilhelmstötter * @version !__version__! * @since !__version__! */ public final class SQL { + private SQL() { } + /** + * Represents a supplier of results. + * + * @see java.util.function.Supplier + * + * @param the result type + */ @FunctionalInterface public static interface Supplier { public T get() throws SQLException; } + /** + * Represents a function that accepts one argument and produces a result. + * + * @see java.util.function.Function + * + * @param the argument type + * @param the result type + */ @FunctionalInterface public static interface Function { public R apply(final T value) throws SQLException; } + /** + * Represents an operation that accepts a single input argument and returns + * no result. Unlike most other functional interfaces, Consumer is expected + * to operate via side-effects. + * + * @see java.util.function.Consumer + * + * @param the argument type + */ @FunctionalInterface public static interface Consumer { public void accept(final T value) throws SQLException; } - public static final class Lazy implements Supplier { + /** + * A container object which may or may not contain a non-null value. + * + * @see java.util.Optional + * + * @param the element type + */ + public static final class Option { + + private static final Option EMPTY = new Option<>(); + + private final T _value; + + private Option() { + _value = null; + } + + private Option(final T value) { + _value = requireNonNull(value); + } + + @SuppressWarnings("unchecked") + public static Option empty() { + return (Option)EMPTY; + } + + public static Option of(final T value) { + return value == null ? empty() : new Option<>(value); + } + + public Optional toOptional() { + return Optional.ofNullable(_value); + } + + public T get() { + if (_value == null) { + throw new NoSuchElementException("No value present"); + } + return _value; + } + + public boolean isPresent() { + return _value != null; + } + + public void ifPresent(final Consumer consumer) + throws SQLException + { + if (_value != null) { + consumer.accept(_value); + } + } + + public Option filter(final Predicate predicate) { + requireNonNull(predicate); + return !isPresent() + ? this + : predicate.test(_value) + ? this + : empty(); + } + + public Option map(final Function mapper) + throws SQLException + { + requireNonNull(mapper); + return !isPresent() + ? empty() + : Option.of(mapper.apply(_value)); + } + + public Option flatMap(final Function> mapper) + throws SQLException + { + requireNonNull(mapper); + return !isPresent() + ? empty() + : requireNonNull(mapper.apply(_value)); + } + + public T orElse(final T other) { + return _value != null ? _value : other; + } + + public T orElseGet(final Supplier other) + throws SQLException + { + return _value != null ? _value : other.get(); + } + + public T orElseThrow( + final java.util.function.Supplier exceptionSupplier + ) + throws X + { + if (_value != null) { + return _value; + } else { + throw exceptionSupplier.get(); + } + } + + @Override + public boolean equals(final Object obj) { + return obj instanceof Option && + Objects.equals(((Option)obj)._value, _value); + } + + @Override + public int hashCode() { + return Objects.hashCode(_value); + } + + @Override + public String toString() { + return _value != null + ? String.format("Option[%s]", _value) + : "Option.empty"; + } + } + + + /** + * Class for lazy value initialization. + * + * @param the result type + */ + static final class Lazy implements Supplier { private final transient Supplier _supplier; private T _value; @@ -109,8 +268,6 @@ public static Lazy of(final Supplier supplier) { * method allows to create a lazy object with the given * {@code value}. * - * @since 3.7 - * * @param value the value this {@code Lazy} object is initialized with * @param the value type * @return return a new lazy value with the given value