Thu, 19 Nov 2020 13:58:54 +0100
migrates DAO classes
mike@159 | 1 | /* |
mike@159 | 2 | * Copyright 2020 Mike Becker. All rights reserved. |
mike@159 | 3 | * |
mike@159 | 4 | * Redistribution and use in source and binary forms, with or without |
mike@159 | 5 | * modification, are permitted provided that the following conditions are met: |
mike@159 | 6 | * |
mike@159 | 7 | * 1. Redistributions of source code must retain the above copyright |
mike@159 | 8 | * notice, this list of conditions and the following disclaimer. |
mike@159 | 9 | * |
mike@159 | 10 | * 2. Redistributions in binary form must reproduce the above copyright |
mike@159 | 11 | * notice, this list of conditions and the following disclaimer in the |
mike@159 | 12 | * documentation and/or other materials provided with the distribution. |
mike@159 | 13 | * |
mike@159 | 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
mike@159 | 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
mike@159 | 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
mike@159 | 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
mike@159 | 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
mike@159 | 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
mike@159 | 20 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
mike@159 | 21 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
mike@159 | 22 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
mike@159 | 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
mike@159 | 24 | * |
mike@159 | 25 | */ |
mike@159 | 26 | |
mike@159 | 27 | package de.uapcore.lightpit.dao.postgres |
mike@159 | 28 | |
mike@159 | 29 | import de.uapcore.lightpit.dao.AbstractIssueDao |
mike@159 | 30 | import de.uapcore.lightpit.dao.Functions |
mike@159 | 31 | import de.uapcore.lightpit.entities.* |
mike@159 | 32 | import java.sql.Connection |
mike@159 | 33 | import java.sql.PreparedStatement |
mike@159 | 34 | import java.sql.ResultSet |
mike@159 | 35 | import java.sql.Types |
mike@159 | 36 | |
mike@159 | 37 | class PGIssueDao(connection: Connection) : AbstractIssueDao() { |
mike@159 | 38 | |
mike@159 | 39 | private val query = "select issueid, i.project, p.name as projectname, p.node as projectnode, " + |
mike@159 | 40 | "component, c.name as componentname, c.node as componentnode, " + |
mike@159 | 41 | "status, category, subject, i.description, " + |
mike@159 | 42 | "userid, username, givenname, lastname, mail, " + |
mike@159 | 43 | "created, updated, eta " + |
mike@159 | 44 | "from lpit_issue i " + |
mike@159 | 45 | "join lpit_project p on i.project = projectid " + |
mike@159 | 46 | "left join lpit_component c on component = c.id " + |
mike@159 | 47 | "left join lpit_user on userid = assignee " |
mike@159 | 48 | private val list = connection.prepareStatement(query + |
mike@159 | 49 | "where i.project = ? and coalesce(component, -1) = coalesce(?, component, -1)") |
mike@159 | 50 | private val listForVersion = connection.prepareStatement( |
mike@159 | 51 | "with issue_version as ( " + |
mike@159 | 52 | "select issueid, versionid from lpit_issue_affected_version union " + |
mike@159 | 53 | "select issueid, versionid from lpit_issue_resolved_version) " + |
mike@159 | 54 | query + |
mike@159 | 55 | "left join issue_version using (issueid) " + |
mike@159 | 56 | "where i.project = ? " + |
mike@159 | 57 | "and coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)" |
mike@159 | 58 | ) |
mike@159 | 59 | private val find = connection.prepareStatement(query + "where issueid = ? ") |
mike@159 | 60 | private val insert = connection.prepareStatement( |
mike@159 | 61 | "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " + |
mike@159 | 62 | "values (?, ?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid" |
mike@159 | 63 | ) |
mike@159 | 64 | private val update = connection.prepareStatement( |
mike@159 | 65 | "update lpit_issue set " + |
mike@159 | 66 | "updated = now(), component = ?, status = ?::issue_status, category = ?::issue_category, " + |
mike@159 | 67 | "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?" |
mike@159 | 68 | ) |
mike@159 | 69 | private val affectedVersions = connection.prepareStatement( |
mike@159 | 70 | "select versionid, name, status, ordinal, node " + |
mike@159 | 71 | "from lpit_version join lpit_issue_affected_version using (versionid) " + |
mike@159 | 72 | "where issueid = ? " + |
mike@159 | 73 | "order by ordinal, name" |
mike@159 | 74 | ) |
mike@159 | 75 | private val clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?") |
mike@159 | 76 | private val insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)") |
mike@159 | 77 | |
mike@159 | 78 | private val resolvedVersions = connection.prepareStatement( |
mike@159 | 79 | "select versionid, name, status, ordinal, node " + |
mike@159 | 80 | "from lpit_version v join lpit_issue_resolved_version using (versionid) " + |
mike@159 | 81 | "where issueid = ? " + |
mike@159 | 82 | "order by ordinal, name" |
mike@159 | 83 | ) |
mike@159 | 84 | private val clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?") |
mike@159 | 85 | private val insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)") |
mike@159 | 86 | private val insertComment = connection.prepareStatement( |
mike@159 | 87 | "insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)" |
mike@159 | 88 | ) |
mike@159 | 89 | private val updateComment = connection.prepareStatement( |
mike@159 | 90 | "update lpit_issue_comment set comment = ?, updated = now(), updatecount = updatecount+1 where commentid = ?" |
mike@159 | 91 | ) |
mike@159 | 92 | private val listComments = connection.prepareStatement( |
mike@159 | 93 | "select * from lpit_issue_comment left join lpit_user using (userid) where issueid = ? order by created" |
mike@159 | 94 | ) |
mike@159 | 95 | |
mike@159 | 96 | override fun mapResult(rs: ResultSet): Issue { |
mike@159 | 97 | val project = Project(rs.getInt("project")) |
mike@159 | 98 | project.name = rs.getString("projectname") |
mike@159 | 99 | project.node = rs.getString("projectnode") |
mike@159 | 100 | val issue = Issue(rs.getInt("issueid")) |
mike@159 | 101 | issue.project = project |
mike@159 | 102 | issue.component = rs.getInt("component").let { id -> |
mike@159 | 103 | if (rs.wasNull()) { |
mike@159 | 104 | null |
mike@159 | 105 | } else { |
mike@159 | 106 | val component = Component(id) |
mike@159 | 107 | component.name = rs.getString("componentname") |
mike@159 | 108 | component.node = rs.getString("componentnode") |
mike@159 | 109 | component |
mike@159 | 110 | } |
mike@159 | 111 | } |
mike@159 | 112 | issue.status = IssueStatus.valueOf(rs.getString("status")) |
mike@159 | 113 | issue.category = IssueCategory.valueOf(rs.getString("category")) |
mike@159 | 114 | issue.subject = rs.getString("subject") |
mike@159 | 115 | issue.description = rs.getString("description") |
mike@159 | 116 | issue.assignee = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() } |
mike@159 | 117 | issue.created = rs.getTimestamp("created") |
mike@159 | 118 | issue.updated = rs.getTimestamp("updated") |
mike@159 | 119 | issue.eta = rs.getDate("eta") |
mike@159 | 120 | return issue |
mike@159 | 121 | } |
mike@159 | 122 | |
mike@159 | 123 | private fun updateVersionLists(instance: Issue) { |
mike@159 | 124 | clearAffected.setInt(1, instance.id) |
mike@159 | 125 | clearResolved.setInt(1, instance.id) |
mike@159 | 126 | insertAffected.setInt(1, instance.id) |
mike@159 | 127 | insertResolved.setInt(1, instance.id) |
mike@159 | 128 | clearAffected.executeUpdate() |
mike@159 | 129 | clearResolved.executeUpdate() |
mike@159 | 130 | for (v: Version in instance.affectedVersions) { |
mike@159 | 131 | insertAffected.setInt(2, v.id) |
mike@159 | 132 | insertAffected.executeUpdate() |
mike@159 | 133 | } |
mike@159 | 134 | for (v: Version in instance.resolvedVersions) { |
mike@159 | 135 | insertResolved.setInt(2, v.id) |
mike@159 | 136 | insertResolved.executeUpdate() |
mike@159 | 137 | } |
mike@159 | 138 | } |
mike@159 | 139 | |
mike@159 | 140 | private fun setData(stmt: PreparedStatement, column: Int, instance: Issue): Int { |
mike@159 | 141 | var col = column |
mike@159 | 142 | setForeignKeyOrNull(stmt, ++col, instance.component, Component::id) |
mike@159 | 143 | stmt.setString(++col, instance.status.name) |
mike@159 | 144 | stmt.setString(++col, instance.category.name) |
mike@159 | 145 | stmt.setString(++col, instance.subject) |
mike@159 | 146 | Functions.setStringOrNull(stmt, ++col, instance.description) |
mike@159 | 147 | setForeignKeyOrNull(stmt, ++col, instance.assignee, User::id) |
mike@159 | 148 | Functions.setDateOrNull(stmt, ++col, instance.eta) |
mike@159 | 149 | return col |
mike@159 | 150 | } |
mike@159 | 151 | |
mike@159 | 152 | override fun save(instance: Issue, parent: Project) { |
mike@159 | 153 | instance.project = parent |
mike@159 | 154 | var column = 0 |
mike@159 | 155 | insert.setInt(++column, parent.id) |
mike@159 | 156 | setData(insert, column, instance) |
mike@159 | 157 | // insert and retrieve the ID |
mike@159 | 158 | val rs = insert.executeQuery() |
mike@159 | 159 | rs.next() |
mike@159 | 160 | instance.id = rs.getInt(1) |
mike@159 | 161 | updateVersionLists(instance) |
mike@159 | 162 | } |
mike@159 | 163 | |
mike@159 | 164 | override fun update(instance: Issue): Boolean { |
mike@159 | 165 | var column = setData(update, 0, instance) |
mike@159 | 166 | update.setInt(++column, instance.id) |
mike@159 | 167 | return if (update.executeUpdate() > 0) { |
mike@159 | 168 | updateVersionLists(instance) |
mike@159 | 169 | true |
mike@159 | 170 | } else { |
mike@159 | 171 | false |
mike@159 | 172 | } |
mike@159 | 173 | } |
mike@159 | 174 | |
mike@159 | 175 | override fun list(parent: Project): List<Issue> { |
mike@159 | 176 | list.setInt(1, parent.id) |
mike@159 | 177 | list.setNull(2, Types.INTEGER) |
mike@159 | 178 | return super.list(list) |
mike@159 | 179 | } |
mike@159 | 180 | |
mike@159 | 181 | override fun list(project: Project, component: Component?, version: Version?): List<Issue> { |
mike@159 | 182 | listForVersion.setInt(1, project.id) |
mike@159 | 183 | listForVersion.setInt(2, version?.id ?: -1) |
mike@159 | 184 | listForVersion.setInt(3, component?.id ?: -1) |
mike@159 | 185 | return super.list(listForVersion) |
mike@159 | 186 | } |
mike@159 | 187 | |
mike@159 | 188 | override fun list(project: Project, version: Version?): List<Issue> { |
mike@159 | 189 | listForVersion.setInt(1, project.id) |
mike@159 | 190 | listForVersion.setInt(2, version?.id ?: -1) |
mike@159 | 191 | listForVersion.setNull(3, Types.INTEGER) |
mike@159 | 192 | return super.list(listForVersion) |
mike@159 | 193 | } |
mike@159 | 194 | |
mike@159 | 195 | override fun list(project: Project, component: Component?): List<Issue> { |
mike@159 | 196 | list.setInt(1, project.id) |
mike@159 | 197 | list.setInt(2, component?.id ?: -1) |
mike@159 | 198 | return super.list(list) |
mike@159 | 199 | } |
mike@159 | 200 | |
mike@159 | 201 | override fun find(id: Int): Issue? { |
mike@159 | 202 | find.setInt(1, id) |
mike@159 | 203 | return super.find(find) |
mike@159 | 204 | } |
mike@159 | 205 | |
mike@159 | 206 | private fun listVersions(stmt: PreparedStatement, issue: Issue): List<Version> { |
mike@159 | 207 | stmt.setInt(1, issue.id) |
mike@159 | 208 | return sequence { |
mike@159 | 209 | stmt.executeQuery().use { result -> |
mike@159 | 210 | while (result.next()) yield(PGVersionDao.mapResult(result)) |
mike@159 | 211 | } |
mike@159 | 212 | }.toList() |
mike@159 | 213 | } |
mike@159 | 214 | |
mike@159 | 215 | override fun joinVersionInformation(issue: Issue) { |
mike@159 | 216 | issue.affectedVersions = listVersions(affectedVersions, issue) |
mike@159 | 217 | issue.resolvedVersions = listVersions(resolvedVersions, issue) |
mike@159 | 218 | } |
mike@159 | 219 | |
mike@159 | 220 | override fun listComments(issue: Issue): List<IssueComment> { |
mike@159 | 221 | listComments.setInt(1, issue.id) |
mike@159 | 222 | return sequence { |
mike@159 | 223 | listComments.executeQuery().use { rs -> |
mike@159 | 224 | while (rs.next()) { |
mike@159 | 225 | val comment = IssueComment(rs.getInt("commentid")) |
mike@159 | 226 | comment.created = rs.getTimestamp("created") |
mike@159 | 227 | comment.updated = rs.getTimestamp("updated") |
mike@159 | 228 | comment.updateCount = rs.getInt("updatecount") |
mike@159 | 229 | comment.comment = rs.getString("comment") |
mike@159 | 230 | comment.author = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() } |
mike@159 | 231 | yield(comment) |
mike@159 | 232 | } |
mike@159 | 233 | } |
mike@159 | 234 | }.toList() |
mike@159 | 235 | } |
mike@159 | 236 | |
mike@159 | 237 | override fun saveComment(issue: Issue, comment: IssueComment) { |
mike@159 | 238 | if (comment.id >= 0) { |
mike@159 | 239 | updateComment.setString(1, comment.comment) |
mike@159 | 240 | updateComment.setInt(2, comment.id) |
mike@159 | 241 | updateComment.execute() |
mike@159 | 242 | } else { |
mike@159 | 243 | insertComment.setInt(1, issue.id) |
mike@159 | 244 | insertComment.setString(2, comment.comment) |
mike@159 | 245 | setForeignKeyOrNull(insertComment, 3, comment.author, User::id) |
mike@159 | 246 | insertComment.execute() |
mike@159 | 247 | } |
mike@159 | 248 | } |
mike@159 | 249 | } |