Skip to content

Commit

Permalink
#1: Some JDBC experiments.
Browse files Browse the repository at this point in the history
  • Loading branch information
jenetics committed Jan 5, 2017
1 parent 1888d10 commit 03c3689
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 29 deletions.
27 changes: 13 additions & 14 deletions jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/DAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import io.jenetics.jpx.jdbc.SQL.Supplier;

/**
* Abstract DAO class
*
Expand Down Expand Up @@ -85,8 +83,10 @@ public default RowParser<T> single() {
*
* @return a new parser which parses a single selection result
*/
public default RowParser<Optional<T>> singleOpt() {
return rs -> rs.next() ? Optional.of(parse(rs)) : Optional.empty();
public default RowParser<SQL.Option<T>> singleOpt() {
return rs -> rs.next()
? SQL.Option.of(parse(rs))
: SQL.Option.empty();
}

/**
Expand Down Expand Up @@ -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<?>) {
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -391,21 +392,19 @@ public Batch batch(final String query) {
return new Batch(_conn, query);
}

public static <T> Stored<T> insertOrUpdate(
public static <T> Stored<T> put(
final T value,
final SQL.Function<T, Optional<Stored<T>>> select,
final SQL.Function<T, SQL.Option<Stored<T>>> select,
final SQL.Function<T, Stored<T>> insert,
final SQL.Consumer<Stored<T>> update
)
throws SQLException
{
final Optional<Stored<T>> 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));
}

/**
Expand Down
36 changes: 25 additions & 11 deletions jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/LinkDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
*
Expand All @@ -67,9 +70,10 @@ public List<Stored<Link>> insert(final List<Link> 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())
Expand Down Expand Up @@ -100,9 +104,10 @@ public List<Stored<Link>> update(final List<Stored<Link>> 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())
Expand All @@ -122,10 +127,10 @@ public Stored<Link> update(final Stored<Link> link) throws SQLException {
return update(singletonList(link)).get(0);
}

public Stored<Link> insertOrUpdate(final Link link) throws SQLException {
return DAO.insertOrUpdate(
public Stored<Link> put(final Link link) throws SQLException {
return DAO.put(
link,
l -> selectByHRef(l.getHref()),
l -> selectByHref(l.getHref()),
this::insert,
this::update
);
Expand All @@ -142,14 +147,23 @@ public List<Stored<Link>> select() throws SQLException {
.as(RowParser.list());
}

public SQL.Option<Stored<Link>> 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.
*
* @param id the link DB ID
* @return the selected link, if available
* @throws SQLException if the select fails
*/
public Optional<Stored<Link>> selectByID(final long id)
public SQL.Option<Stored<Link>> selectByID(final long id)
throws SQLException
{
return sql("SELECT id, href, text, type FROM link WHERE id = {id};")
Expand All @@ -164,7 +178,7 @@ public Optional<Stored<Link>> selectByID(final long id)
* @return the selected links
* @throws SQLException if the select fails
*/
public Optional<Stored<Link>> selectByHRef(final URI href)
public SQL.Option<Stored<Link>> selectByHref(final URI href)
throws SQLException
{
return sql("SELECT id, href, text, type FROM link WHERE href = {href}")
Expand Down
3 changes: 2 additions & 1 deletion jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PersonDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -78,7 +79,7 @@ public List<Stored<Person>> insert(final List<Person> persons)

private Long insertOrUpdate(final Optional<Link> link) throws SQLException {
return link.isPresent()
? dao(LinkDAO::of).insertOrUpdate(link.get()).getID()
? dao(LinkDAO::of).put(link.get()).getID()
: null;
}

Expand Down
163 changes: 160 additions & 3 deletions jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
* @version !__version__!
* @since !__version__!
*/
public final class SQL {

private SQL() {
}

/**
* Represents a supplier of results.
*
* @see java.util.function.Supplier
*
* @param <T> the result type
*/
@FunctionalInterface
public static interface Supplier<T> {
public T get() throws SQLException;
}

/**
* Represents a function that accepts one argument and produces a result.
*
* @see java.util.function.Function
*
* @param <T> the argument type
* @param <R> the result type
*/
@FunctionalInterface
public static interface Function<T, R> {
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 <T> the argument type
*/
@FunctionalInterface
public static interface Consumer<T> {
public void accept(final T value) throws SQLException;
}

public static final class Lazy<T> implements Supplier<T> {
/**
* A container object which may or may not contain a non-null value.
*
* @see java.util.Optional
*
* @param <T> the element type
*/
public static final class Option<T> {

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<T> Option<T> empty() {
return (Option<T>)EMPTY;
}

public static <T> Option<T> of(final T value) {
return value == null ? empty() : new Option<>(value);
}

public Optional<T> 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<? super T> consumer)
throws SQLException
{
if (_value != null) {
consumer.accept(_value);
}
}

public Option<T> filter(final Predicate<? super T> predicate) {
requireNonNull(predicate);
return !isPresent()
? this
: predicate.test(_value)
? this
: empty();
}

public<U> Option<U> map(final Function<? super T, ? extends U> mapper)
throws SQLException
{
requireNonNull(mapper);
return !isPresent()
? empty()
: Option.of(mapper.apply(_value));
}

public<U> Option<U> flatMap(final Function<? super T, Option<U>> 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<? extends T> other)
throws SQLException
{
return _value != null ? _value : other.get();
}

public <X extends Throwable> T orElseThrow(
final java.util.function.Supplier<? extends X> 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 <T> the result type
*/
static final class Lazy<T> implements Supplier<T> {
private final transient Supplier<T> _supplier;

private T _value;
Expand Down Expand Up @@ -109,8 +268,6 @@ public static <T> Lazy<T> of(final Supplier<T> supplier) {
* method allows to create a <em>lazy</em> object with the given
* {@code value}.
*
* @since 3.7
*
* @param value the value this {@code Lazy} object is initialized with
* @param <T> the value type
* @return return a new lazy value with the given value
Expand Down

0 comments on commit 03c3689

Please sign in to comment.