From 9040610606df46cfa57238ababe90a4e3824d6fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Wilhelmst=C3=B6tter?= Date: Sun, 8 Jan 2017 00:59:11 +0100 Subject: [PATCH] #1: Code improvements. --- .../main/java/io/jenetics/jpx/jdbc/DAO.java | 42 +++- .../java/io/jenetics/jpx/jdbc/LinkDAO.java | 18 +- .../io/jenetics/jpx/jdbc/MetadataDAO.java | 190 ++++++++++++++++++ .../java/io/jenetics/jpx/jdbc/PersonDAO.java | 26 ++- .../io/jenetics/jpx/jdbc/PreparedSQL.java | 10 +- .../java/io/jenetics/jpx/jdbc/Results.java | 27 +++ .../java/io/jenetics/jpx/jdbc/RowParser.java | 2 +- .../main/java/io/jenetics/jpx/jdbc/SQL.java | 22 ++ .../java/io/jenetics/jpx/jdbc/SQLQuery.java | 2 +- jpx.jdbc/src/main/resources/model-mysql.sql | 2 +- 10 files changed, 317 insertions(+), 24 deletions(-) create mode 100644 jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/MetadataDAO.java 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 f160cf17..77a2edc3 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 @@ -19,6 +19,7 @@ */ package io.jenetics.jpx.jdbc; +import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; @@ -35,6 +36,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import io.jenetics.jpx.jdbc.SQL.ListMapper; +import io.jenetics.jpx.jdbc.SQL.OptionMapper; + /** * Abstract DAO class * @@ -55,7 +59,7 @@ abstract class DAO { _conn = conn; } - T dao(final Function create) { + T with(final Function create) { return create.apply(_conn); } @@ -118,6 +122,31 @@ static List> put( return result; } + static Map set( + final List values, + final ListMapper mapper, + final SQL.Function, List>> set + ) + throws SQLException + { + final List mapped = values.stream() + .flatMap(v -> mapper.apply(v).stream()) + .collect(Collectors.toList()); + + return set.apply(mapped).stream() + .collect(toMap(Stored::value, Stored::id, (a, b) -> b)); + } + + static Map set( + final List values, + final OptionMapper mapper, + final SQL.Function, List>> set + ) + throws SQLException + { + return set(values, mapper.toListMapper(), set); + } + /** * Reads the auto increment id from the previously inserted record. * @@ -142,12 +171,21 @@ static List map(final List values, final Function mapper) { } static List flatMap( + final List values, + final Function> mapper + ) { + return values.stream() + .flatMap(v -> mapper.apply(v).stream()) + .collect(toList()); + } + + static List flatMapOption( final List values, final Function> mapper ) { return values.stream() .flatMap(v -> mapper.apply(v).map(Stream::of).orElse(Stream.empty())) - .collect(Collectors.toList()); + .collect(toList()); } } 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 05177b84..05b1ba88 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 @@ -27,6 +27,8 @@ import java.sql.SQLException; import java.util.Collections; import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; import io.jenetics.jpx.Link; @@ -148,12 +150,26 @@ public List> put(final List links) throws SQLException { : DAO.put( links, Link::getHref, - list -> selectByHrefs(map(list, Link::getHref)), + //list -> selectByHrefs(map(list, Link::getHref)), + values -> select(values, Link::getHref, this::selectByHrefs), this::insert, this::update ); } + private List> select( + final List values, + final Function mapper, + final SQL.Function, List>> select + ) + throws SQLException + { + final List keys = values.stream() + .map(mapper) + .collect(Collectors.toList()); + + return select.apply(keys); + } /* ************************************************************************* * SELECT queries diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/MetadataDAO.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/MetadataDAO.java new file mode 100644 index 00000000..9a2d6b49 --- /dev/null +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/MetadataDAO.java @@ -0,0 +1,190 @@ +/* + * Java Genetic Algorithm Library (@__identifier__@). + * Copyright (c) @__year__@ Franz Wilhelmstötter + * + * 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. + * + * Author: + * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) + */ +package io.jenetics.jpx.jdbc; + +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toMap; + +import java.sql.Connection; +import java.sql.SQLException; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; + +import io.jenetics.jpx.Bounds; +import io.jenetics.jpx.Copyright; +import io.jenetics.jpx.Link; +import io.jenetics.jpx.Metadata; +import io.jenetics.jpx.Person; + +/** + * @author Franz Wilhelmstötter + * @version !__version__! + * @since !__version__! + */ +public class MetadataDAO extends DAO { + + /** + * Represents a row in the "metadata" tables. + */ + private static final class Row { + final String name; + final String description; + final Long personID; + final Long copyrightID; + final ZonedDateTime time; + final String keywords; + final Long boundsID; + + Row( + final String name, + final String description, + final Long personID, + final Long copyrightID, + final ZonedDateTime time, + final String keywords, + final Long boundsID + ) { + this.name = name; + this.description = description; + this.personID = personID; + this.copyrightID = copyrightID; + this.time = time; + this.keywords = keywords; + this.boundsID = boundsID; + } + } + + + public MetadataDAO(final Connection connection) { + super(connection); + } + + /** + * The link row parser which creates a {@link Link} object from a given DB + * row. + */ + private static final RowParser> RowParser = rs -> Stored.of( + rs.getLong("id"), + new Row( + rs.getString("name"), + rs.getString("desc"), + rs.get(Long.class, "person_id"), + rs.get(Long.class, "copyright_id"), + rs.getZonedDateTime("time"), + rs.getString("keywords"), + rs.get(Long.class, "bounds_id") + ) + ); + + /* +CREATE TABLE metadata( + id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(255), + `desc` TEXT, + person_id BIGINT REFERENCES person(id), + copyright_id BIGINT, + time TIMESTAMP, + keywords VARCHAR(255), + bounds_id BIGINT REFERENCES bounds(id) +); +CREATE INDEX i_metadata_name ON metadata(name); +CREATE INDEX i_metadata_keywords ON metadata(keywords); + +CREATE TABLE metadata_link( + metadata_id BIGINT NOT NULL REFERENCES metadata(id) ON DELETE CASCADE, + link_id BIGINT NOT NULL REFERENCES link(id), + + CONSTRAINT c_metadata_link_metadata_id_link_id UNIQUE (metadata_id, link_id) +); + */ + + /* ************************************************************************* + * INSERT queries + **************************************************************************/ + + /** + * Insert the given person list into the DB. + * + * @param metadata the persons to insert + * @return return the stored persons + * @throws SQLException if inserting fails + */ + public List> insert(final List metadata) + throws SQLException + { + final Map persons = DAO + .set(metadata, Metadata::getAuthor, with(PersonDAO::new)::put); + + final Map copyrights = DAO + .set(metadata, Metadata::getCopyright, with(CopyrightDAO::new)::put); + + final Map bounds = DAO + .set(metadata, Metadata::getBounds, with(BoundsDAO::new)::insert); + + final String query = + "INSERT INTO person(name, email, link_id) " + + "VALUES({name}, {email}, {link_id});"; + + final List> inserted = + Batch(query).insert(metadata, md -> asList( + Param.value("name", md.getName()), + Param.value("desc", md.getDescription()), + Param.value("person_id", md.getAuthor().map(persons::get)), + Param.value("copyright_id", md.getCopyright().map(copyrights::get)), + Param.value("time", md.getTime()), + Param.value("keywords", md.getKeywords()), + Param.value("bounds_id", md.getBounds().map(bounds::get)) + )); + + final Map links = DAO + .set(metadata, Metadata::getLinks, with(LinkDAO::new)::put); + + return null; + } + + /* ************************************************************************* + * SELECT queries + **************************************************************************/ + + /** + * Select all available copyrights. + * + * @return all stored copyrights + * @throws SQLException if the select fails + */ + /* + public List> select() throws SQLException { + final String query = + "SELECT id, " + + "name, " + + "description, " + + "person.name AS person_name, " + + "person.email AS person_email, " + + "link.href AS link_href, " + + "link.text AS link_text, " + + "link.type AS link_type " + + "person."; + + return SQL(query).as(RowParser.list()); + } + */ + +} 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 46774b55..7999da32 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 @@ -21,7 +21,6 @@ import static java.util.Arrays.asList; import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.toMap; import java.sql.Connection; import java.sql.SQLException; @@ -75,7 +74,8 @@ public PersonDAO(final Connection conn) { public List> insert(final List persons) throws SQLException { - final Map links = putLinks(persons); + final Map links = DAO + .set(persons, Person::getLink, with(LinkDAO::new)::put); final String query = "INSERT INTO person(name, email, link_id) " + @@ -88,16 +88,6 @@ public List> insert(final List persons) )); } - private Map putLinks(final List persons) - throws SQLException - { - final List> links = dao(LinkDAO::new) - .put(flatMap(persons, Person::getLink)); - - return links.stream() - .collect(toMap(Stored::value, Stored::id, (a, b) -> a)); - } - /** * Insert the given person into the DB. * @@ -124,7 +114,11 @@ public Stored insert(final Person person) throws SQLException { public List> update(final List> persons) throws SQLException { - final Map links = putLinks(map(persons, Stored::value)); + final Map links = DAO.set( + persons, + (Stored p) -> p.value().getLink(), + with(LinkDAO::new)::put + ); final String query = "UPDATE person " + @@ -168,12 +162,16 @@ public List> put(final List persons) return DAO.put( persons, Person::getName, - list -> selectByNames(flatMap(list, Person::getName)), + list -> selectByNames(flatMapOption(list, Person::getName)), this::insert, this::update ); } + public Stored put(final Person person) throws SQLException { + return put(singletonList(person)).get(0); + } + /* ************************************************************************* * SELECT queries **************************************************************************/ diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PreparedSQL.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PreparedSQL.java index 80b1d3c1..fdf24e71 100644 --- a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PreparedSQL.java +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/PreparedSQL.java @@ -28,7 +28,9 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Timestamp; import java.time.Year; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -103,12 +105,12 @@ private static Object toSQLValue(final Object value) { } if (result instanceof URI) { result = result.toString(); - } - if (result instanceof URL) { + } else if (result instanceof URL) { result = result.toString(); - } - if (result instanceof Year) { + } else if (result instanceof Year) { result = ((Year)result).getValue(); + } else if (result instanceof ZonedDateTime) { + result = Timestamp.from(((ZonedDateTime)result).toInstant()); } return result; diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/Results.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/Results.java index d25db49c..62eccecb 100644 --- a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/Results.java +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/Results.java @@ -19,6 +19,7 @@ */ package io.jenetics.jpx.jdbc; +import static java.time.ZoneOffset.UTC; import static java.util.Objects.requireNonNull; import java.io.InputStream; @@ -41,6 +42,8 @@ import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; +import java.time.Instant; +import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Map; @@ -57,6 +60,26 @@ private Results(final ResultSet rs) { _rs = requireNonNull(rs); } + + public T get(final Class type, final String columnName) + throws SQLException + { + return type.cast(getObject(columnName)); + } + + public ZonedDateTime getZonedDateTime(final String columnName) + throws SQLException + { + final Timestamp ts = getTimestamp(columnName); + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), UTC); + } + + + + /* ************************************************************************* + * ResultSet delegate methods. + **************************************************************************/ + @Override public boolean next() throws SQLException { return _rs.next(); @@ -1371,4 +1394,8 @@ public boolean isWrapperFor(final Class iface) throws SQLException { return _rs.isWrapperFor(iface); } + static Results of(final ResultSet rs) { + return new Results(rs); + } + } diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/RowParser.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/RowParser.java index 77e2525a..2d57f59b 100644 --- a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/RowParser.java +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/RowParser.java @@ -46,7 +46,7 @@ interface RowParser { * @return the stored data object * @throws SQLException if reading of the current row fails */ - T parse(final ResultSet rs) throws SQLException; + T parse(final Results rs) throws SQLException; /** * Return a new parser which expects at least one result. 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 60041e36..baad1e26 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 @@ -19,7 +19,12 @@ */ package io.jenetics.jpx.jdbc; +import static java.util.Collections.emptyList; + import java.sql.SQLException; +import java.util.Collections; +import java.util.List; +import java.util.Optional; /** * Contains implementations of existing functional classes, which allow to throw @@ -73,4 +78,21 @@ public static interface Consumer { public void accept(final T value) throws SQLException; } + @FunctionalInterface + public static interface OptionMapper + extends java.util.function.Function> + { + public default ListMapper toListMapper() { + return t -> apply(t) + .map(Collections::singletonList) + .orElse(emptyList()); + } + } + + @FunctionalInterface + public static interface ListMapper + extends java.util.function.Function> + { + } + } diff --git a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQLQuery.java b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQLQuery.java index 0fdc6ded..501cb829 100644 --- a/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQLQuery.java +++ b/jpx.jdbc/src/main/java/io/jenetics/jpx/jdbc/SQLQuery.java @@ -53,7 +53,7 @@ public T as(final RowParser parser) throws SQLException { try (PreparedStatement stmt = PreparedSQL.prepare(_sql, _params, _conn); ResultSet rs = stmt.executeQuery()) { - return parser.parse(rs); + return parser.parse(Results.of(rs)); } } diff --git a/jpx.jdbc/src/main/resources/model-mysql.sql b/jpx.jdbc/src/main/resources/model-mysql.sql index ee8864f7..cef719f8 100644 --- a/jpx.jdbc/src/main/resources/model-mysql.sql +++ b/jpx.jdbc/src/main/resources/model-mysql.sql @@ -57,7 +57,7 @@ CREATE TABLE bounds( CREATE TABLE metadata( id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), - description TEXT, + `desc` TEXT, person_id BIGINT REFERENCES person(id), copyright_id BIGINT, time TIMESTAMP,