universe@38: /* universe@38: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@38: * universe@38: * Copyright 2018 Mike Becker. All rights reserved. universe@38: * universe@38: * Redistribution and use in source and binary forms, with or without universe@38: * modification, are permitted provided that the following conditions are met: universe@38: * universe@38: * 1. Redistributions of source code must retain the above copyright universe@38: * notice, this list of conditions and the following disclaimer. universe@38: * universe@38: * 2. Redistributions in binary form must reproduce the above copyright universe@38: * notice, this list of conditions and the following disclaimer in the universe@38: * documentation and/or other materials provided with the distribution. universe@38: * universe@38: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@38: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@38: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@38: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@38: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@38: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@38: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@38: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@38: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@38: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@38: * POSSIBILITY OF SUCH DAMAGE. universe@38: * universe@38: */ universe@38: package de.uapcore.lightpit.dao.postgres; universe@38: universe@62: import de.uapcore.lightpit.dao.IssueDao; universe@62: import de.uapcore.lightpit.entities.*; universe@38: universe@38: import java.sql.Connection; universe@38: import java.sql.PreparedStatement; universe@38: import java.sql.ResultSet; universe@38: import java.sql.SQLException; universe@47: import java.util.ArrayList; universe@47: import java.util.List; universe@38: import java.util.Objects; universe@38: universe@62: import static de.uapcore.lightpit.dao.Functions.*; universe@62: universe@62: public final class PGIssueDao implements IssueDao { universe@38: universe@86: private final PreparedStatement insert, update, list, listForVersion, find; universe@88: private final PreparedStatement affectedVersions, resolvedVersions; universe@88: private final PreparedStatement clearAffected, clearResolved; universe@88: private final PreparedStatement insertAffected, insertResolved; universe@38: universe@62: public PGIssueDao(Connection connection) throws SQLException { universe@47: list = connection.prepareStatement( universe@83: "select issueid, project, p.name as projectname, status, category, subject, i.description, " + universe@75: "userid, username, givenname, lastname, mail, " + universe@75: "created, updated, eta " + universe@83: "from lpit_issue i " + universe@83: "left join lpit_project p on project = projectid " + universe@75: "left join lpit_user on userid = assignee " + universe@86: "where project = ? "+ universe@86: "order by eta asc, updated desc"); universe@86: universe@86: listForVersion = connection.prepareStatement( universe@86: "with issue_version as ( "+ universe@86: "select issueid, versionid from lpit_issue_affected_version union "+ universe@86: "select issueid, versionid from lpit_issue_resolved_version) "+ universe@86: "select issueid, project, p.name as projectname, status, category, subject, i.description, " + universe@86: "userid, username, givenname, lastname, mail, " + universe@86: "created, updated, eta " + universe@86: "from lpit_issue i " + universe@86: "join issue_version using (issueid) "+ universe@86: "left join lpit_project p on project = projectid " + universe@86: "left join lpit_user on userid = assignee " + universe@86: "where versionid = ? "+ universe@86: "order by eta asc, updated desc" universe@86: ); universe@47: universe@47: find = connection.prepareStatement( universe@83: "select issueid, project, p.name as projectname, status, category, subject, i.description, " + universe@75: "userid, username, givenname, lastname, mail, " + universe@75: "created, updated, eta " + universe@83: "from lpit_issue i " + universe@83: "left join lpit_project p on project = projectid " + universe@75: "left join lpit_user on userid = assignee " + universe@75: "where issueid = ? "); universe@38: universe@38: insert = connection.prepareStatement( universe@75: "insert into lpit_issue (project, status, category, subject, description, assignee, eta) " + universe@75: "values (?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid" universe@38: ); universe@38: update = connection.prepareStatement( universe@62: "update lpit_issue set updated = now(), status = ?::issue_status, category = ?::issue_category, " + universe@75: "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?" universe@75: ); universe@75: universe@75: affectedVersions = connection.prepareStatement( universe@83: "select versionid, name, status, ordinal " + universe@83: "from lpit_version join lpit_issue_affected_version using (versionid) " + universe@75: "where issueid = ? " + universe@83: "order by ordinal, name" universe@75: ); universe@83: clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?"); universe@83: insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)"); universe@75: universe@75: resolvedVersions = connection.prepareStatement( universe@83: "select versionid, name, status, ordinal " + universe@75: "from lpit_version v join lpit_issue_resolved_version using (versionid) " + universe@75: "where issueid = ? " + universe@83: "order by ordinal, name" universe@38: ); universe@83: clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?"); universe@83: insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)"); universe@38: } universe@38: universe@75: private User obtainAssignee(ResultSet result) throws SQLException { universe@75: final int id = result.getInt("userid"); universe@75: if (id != 0) { universe@75: final var user = new User(id); universe@75: user.setUsername(result.getString("username")); universe@75: user.setGivenname(result.getString("givenname")); universe@75: user.setLastname(result.getString("lastname")); universe@75: user.setMail(result.getString("mail")); universe@75: return user; universe@62: } else { universe@62: return null; universe@62: } universe@62: } universe@62: universe@75: private Issue mapColumns(ResultSet result) throws SQLException { universe@75: final var project = new Project(result.getInt("project")); universe@83: project.setName(result.getString("projectname")); universe@86: final var issue = new Issue(result.getInt("issueid")); universe@86: issue.setProject(project); universe@75: issue.setStatus(IssueStatus.valueOf(result.getString("status"))); universe@75: issue.setCategory(IssueCategory.valueOf(result.getString("category"))); universe@75: issue.setSubject(result.getString("subject")); universe@75: issue.setDescription(result.getString("description")); universe@75: issue.setAssignee(obtainAssignee(result)); universe@75: issue.setCreated(result.getTimestamp("created")); universe@75: issue.setUpdated(result.getTimestamp("updated")); universe@75: issue.setEta(result.getDate("eta")); universe@62: return issue; universe@38: } universe@38: universe@86: private Version mapVersion(ResultSet result) throws SQLException { universe@86: final var version = new Version(result.getInt("versionid")); universe@83: version.setName(result.getString("name")); universe@83: version.setOrdinal(result.getInt("ordinal")); universe@83: version.setStatus(VersionStatus.valueOf(result.getString("status"))); universe@75: return version; universe@75: } universe@75: universe@83: private void updateVersionLists(Issue instance) throws SQLException { universe@83: clearAffected.setInt(1, instance.getId()); universe@83: clearResolved.setInt(1, instance.getId()); universe@83: insertAffected.setInt(1, instance.getId()); universe@83: insertResolved.setInt(1, instance.getId()); universe@83: clearAffected.executeUpdate(); universe@83: clearResolved.executeUpdate(); universe@83: for (Version v : instance.getAffectedVersions()) { universe@83: insertAffected.setInt(2, v.getId()); universe@83: insertAffected.executeUpdate(); universe@83: } universe@83: for (Version v : instance.getResolvedVersions()) { universe@83: insertResolved.setInt(2, v.getId()); universe@83: insertResolved.executeUpdate(); universe@83: } universe@83: } universe@83: universe@38: @Override universe@62: public void save(Issue instance) throws SQLException { universe@62: Objects.requireNonNull(instance.getSubject()); universe@59: Objects.requireNonNull(instance.getProject()); universe@59: insert.setInt(1, instance.getProject().getId()); universe@62: insert.setString(2, instance.getStatus().name()); universe@62: insert.setString(3, instance.getCategory().name()); universe@62: insert.setString(4, instance.getSubject()); universe@62: setStringOrNull(insert, 5, instance.getDescription()); universe@75: setForeignKeyOrNull(insert, 6, instance.getAssignee(), User::getId); universe@75: setDateOrNull(insert, 7, instance.getEta()); universe@75: // insert and retrieve the ID universe@75: final var rs = insert.executeQuery(); universe@75: rs.next(); universe@75: instance.setId(rs.getInt(1)); universe@83: updateVersionLists(instance); universe@38: } universe@38: universe@38: @Override universe@62: public boolean update(Issue instance) throws SQLException { universe@75: if (instance.getId() < 0) return false; universe@62: Objects.requireNonNull(instance.getSubject()); universe@62: update.setString(1, instance.getStatus().name()); universe@62: update.setString(2, instance.getCategory().name()); universe@62: update.setString(3, instance.getSubject()); universe@62: setStringOrNull(update, 4, instance.getDescription()); universe@75: setForeignKeyOrNull(update, 5, instance.getAssignee(), User::getId); universe@75: setDateOrNull(update, 6, instance.getEta()); universe@75: update.setInt(7, instance.getId()); universe@83: boolean success = update.executeUpdate() > 0; universe@83: if (success) { universe@83: updateVersionLists(instance); universe@83: return true; universe@83: } else { universe@83: return false; universe@83: } universe@38: } universe@47: universe@86: private List list(PreparedStatement query, int arg) throws SQLException { universe@86: query.setInt(1, arg); universe@75: List issues = new ArrayList<>(); universe@86: try (var result = query.executeQuery()) { universe@47: while (result.next()) { universe@75: issues.add(mapColumns(result)); universe@47: } universe@47: } universe@75: return issues; universe@47: } universe@47: universe@47: @Override universe@86: public List list(Project project) throws SQLException { universe@86: return list(list, project.getId()); universe@86: } universe@86: universe@86: @Override universe@86: public List list(Version version) throws SQLException { universe@86: return list(listForVersion, version.getId()); universe@86: } universe@86: universe@86: @Override universe@62: public Issue find(int id) throws SQLException { universe@47: find.setInt(1, id); universe@47: try (var result = find.executeQuery()) { universe@47: if (result.next()) { universe@47: return mapColumns(result); universe@47: } else { universe@47: return null; universe@47: } universe@47: } universe@47: } universe@75: universe@75: private List listVersions(PreparedStatement stmt, Issue issue) throws SQLException { universe@75: stmt.setInt(1, issue.getId()); universe@75: List versions = new ArrayList<>(); universe@75: try (var result = stmt.executeQuery()) { universe@75: while (result.next()) { universe@86: versions.add(mapVersion(result)); universe@75: } universe@75: } universe@75: return versions; universe@75: } universe@75: universe@75: @Override universe@75: public void joinVersionInformation(Issue issue) throws SQLException { universe@75: Objects.requireNonNull(issue.getProject()); universe@75: issue.setAffectedVersions(listVersions(affectedVersions, issue)); universe@75: issue.setResolvedVersions(listVersions(resolvedVersions, issue)); universe@75: } universe@38: }