diff -r 6eede6088d41 -r 3f30adba1c63 src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt Mon Dec 21 18:29:34 2020 +0100
@@ -0,0 +1,739 @@
+/*
+ * Copyright 2020 Mike Becker. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package de.uapcore.lightpit.dao
+
+import de.uapcore.lightpit.entities.*
+import de.uapcore.lightpit.filter.*
+import de.uapcore.lightpit.types.WebColor
+import java.sql.Connection
+import java.sql.PreparedStatement
+import java.sql.ResultSet
+
+class PostgresDataAccessObject(private val connection: Connection) : DataAccessObject {
+
+ //
+ private fun selectUserInfo(
+ rs: ResultSet,
+ idColumn: String = "userid",
+ usernameColumn: String = "username",
+ givennameColumn: String = "givenname",
+ lastnameColumn: String = "lastname",
+ mailColumn: String = "mail"
+ ): User? {
+ val idval = rs.getInt(idColumn)
+ return if (rs.wasNull()) null else {
+ User(idval).apply {
+ username = rs.getString(usernameColumn)
+ givenname = rs.getString(givennameColumn)
+ lastname = rs.getString(lastnameColumn)
+ mail = rs.getString(mailColumn)
+ }
+ }
+ }
+
+ private fun selectUsers(stmt: PreparedStatement) = sequence {
+ stmt.executeQuery().use { rs ->
+ while (rs.next()) selectUserInfo(rs)?.let { yield(it) }
+ }
+ }
+
+ //language=SQL
+ private val userQuery = "select userid, username, lastname, givenname, mail from lpit_user"
+
+ private val stmtUsers by lazy {
+ connection.prepareStatement(
+ """${userQuery}
+ where userid > 0
+ order by username
+ """
+ )
+ }
+ private val stmtUserByID by lazy {
+ connection.prepareStatement(
+ """${userQuery}
+ where userid = ?
+ """
+ )
+ }
+ private val stmtUserByName by lazy {
+ connection.prepareStatement(
+ """${userQuery}
+ where lower(username) = lower(?)
+ """
+ )
+ }
+ private val stmtInsertUser by lazy {
+ connection.prepareStatement(
+ "insert into lpit_user (username, lastname, givenname, mail) values (?, ?, ?, ?)"
+ )
+ }
+ private val stmtUpdateUser by lazy {
+ connection.prepareStatement(
+ "update lpit_user set lastname = ?, givenname = ?, mail = ? where userid = ?"
+ )
+ }
+
+ override fun listUsers() = selectUsers(stmtUsers).toList()
+ override fun findUser(id: Int): User? {
+ stmtUserByID.setInt(1, id)
+ return selectUsers(stmtUserByID).firstOrNull()
+ }
+
+ override fun findUserByName(username: String): User? {
+ stmtUserByName.setString(1, username)
+ return selectUsers(stmtUserByName).firstOrNull()
+ }
+
+ override fun insertUser(user: User) {
+ with(user) {
+ stmtInsertUser.setStringSafe(1, username)
+ stmtInsertUser.setStringOrNull(2, lastname)
+ stmtInsertUser.setStringOrNull(3, givenname)
+ stmtInsertUser.setStringOrNull(4, mail)
+ }
+ stmtInsertUser.execute()
+ }
+
+ override fun updateUser(user: User) {
+ with(user) {
+ stmtUpdateUser.setStringOrNull(1, lastname)
+ stmtUpdateUser.setStringOrNull(2, givenname)
+ stmtUpdateUser.setStringOrNull(3, mail)
+ stmtUpdateUser.setInt(4, id)
+ }
+ stmtUpdateUser.execute()
+ }
+ //
+
+ //
+ private fun selectVersions(stmt: PreparedStatement) = sequence {
+ stmt.executeQuery().use { rs ->
+ while (rs.next()) {
+ yield(Version(rs.getInt("versionid"), rs.getInt("project")).apply {
+ name = rs.getString("name")
+ node = rs.getString("node")
+ ordinal = rs.getInt("ordinal")
+ status = rs.getEnum("status")
+ })
+ }
+ }
+ }
+
+ private fun setVersionFields(stmt: PreparedStatement, obj: Version): Int {
+ with(obj) {
+ stmt.setStringSafe(1, name)
+ stmt.setStringSafe(2, node)
+ stmt.setInt(3, ordinal)
+ stmt.setEnum(4, status)
+ }
+ return 5
+ }
+
+ //language=SQL
+ private val versionQuery = "select versionid, project, name, node, ordinal, status from lpit_version"
+
+ private val stmtVersions by lazy {
+ connection.prepareStatement(
+ """${versionQuery}
+ where project = ?
+ order by ordinal desc, lower(name) desc
+ """
+ )
+ }
+ private val stmtVersionByID by lazy {
+ connection.prepareStatement(
+ """${versionQuery}
+ where versionid = ?
+ """
+ )
+ }
+ private val stmtVersionByNode by lazy {
+ connection.prepareStatement(
+ """${versionQuery}
+ where project = ? and node = ?
+ """
+ )
+ }
+ private val stmtInsertVersion by lazy {
+ connection.prepareStatement(
+ """
+ insert into lpit_version (name, node, ordinal, status, project)
+ values (?, ?, ?, ?::version_status, ?)
+ """
+ )
+ }
+ private val stmtUpdateVersion by lazy {
+ connection.prepareStatement(
+ """
+ update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status
+ where versionid = ?
+ """
+ )
+ }
+
+ override fun listVersions(project: Project): List {
+ stmtVersions.setInt(1, project.id)
+ return selectVersions(stmtVersions).toList()
+ }
+
+ override fun findVersion(id: Int): Version? {
+ stmtVersionByID.setInt(1, id)
+ return selectVersions(stmtVersionByID).firstOrNull()
+ }
+
+ override fun findVersionByNode(project: Project, node: String): Version? {
+ stmtVersionByNode.setInt(1, project.id)
+ stmtVersionByNode.setString(2, node)
+ return selectVersions(stmtVersionByNode).firstOrNull()
+ }
+
+ override fun insertVersion(version: Version) {
+ val col = setVersionFields(stmtInsertVersion, version)
+ stmtInsertVersion.setInt(col, version.projectid)
+ stmtInsertVersion.execute()
+ }
+
+ override fun updateVersion(version: Version) {
+ val col = setVersionFields(stmtUpdateVersion, version)
+ stmtUpdateVersion.setInt(col, version.id)
+ stmtUpdateVersion.execute()
+ }
+ //
+
+ //
+ private fun selectComponents(stmt: PreparedStatement) = sequence {
+ stmt.executeQuery().use { rs ->
+ while (rs.next()) {
+ yield(Component(rs.getInt("id"), rs.getInt("project")).apply {
+ name = rs.getString("name")
+ node = rs.getString("node")
+ color = try {
+ WebColor(rs.getString("color"))
+ } catch (ex: IllegalArgumentException) {
+ WebColor("000000")
+ }
+ ordinal = rs.getInt("ordinal")
+ description = rs.getString("description")
+ lead = selectUserInfo(rs)
+ })
+ }
+ }
+ }
+
+ private fun setComponentFields(stmt: PreparedStatement, obj: Component): Int {
+ with(obj) {
+ stmt.setStringSafe(1, name)
+ stmt.setStringSafe(2, node)
+ stmt.setStringSafe(3, color.hex)
+ stmt.setInt(4, ordinal)
+ stmt.setStringOrNull(5, description)
+ stmt.setIntOrNull(6, obj.lead?.id)
+ }
+ return 7
+ }
+
+ //language=SQL
+ private val componentQuery =
+ """
+ select id, project, name, node, color, ordinal, description,
+ userid, username, givenname, lastname, mail
+ from lpit_component
+ left join lpit_user on lead = userid
+ """
+
+ private val stmtComponents by lazy {
+ connection.prepareStatement(
+ """${componentQuery}
+ where project = ?
+ order by ordinal, lower(name)
+ """
+ )
+ }
+ private val stmtComponentById by lazy {
+ connection.prepareStatement(
+ """${componentQuery}
+ where id = ?
+ """
+ )
+ }
+ private val stmtComponentByNode by lazy {
+ connection.prepareStatement(
+ """${componentQuery}
+ where project = ? and node = ?
+ """
+ )
+ }
+ private val stmtInsertComponent by lazy {
+ connection.prepareStatement(
+ """
+ insert into lpit_component (name, node, color, ordinal, description, lead, project)
+ values (?, ?, ?, ?, ?, ?, ?)
+ """
+ )
+ }
+ private val stmtUpdateComponent by lazy {
+ connection.prepareStatement(
+ "update lpit_component set name = ?, node = ?, color = ?, ordinal = ?, description = ?, lead = ? where id = ?"
+ )
+ }
+
+ override fun listComponents(project: Project): List {
+ stmtComponents.setInt(1, project.id)
+ return selectComponents(stmtComponents).toList()
+ }
+
+ override fun findComponent(id: Int): Component? {
+ stmtComponentById.setInt(1, id)
+ return selectComponents(stmtComponentById).firstOrNull()
+ }
+
+ override fun findComponentByNode(project: Project, node: String): Component? {
+ stmtComponentByNode.setInt(1, project.id)
+ stmtComponentByNode.setString(2, node)
+ return selectComponents(stmtComponentByNode).firstOrNull()
+ }
+
+ override fun insertComponent(component: Component) {
+ val col = setComponentFields(stmtInsertComponent, component)
+ stmtInsertComponent.setInt(col, component.projectid)
+ stmtInsertComponent.execute()
+ }
+
+ override fun updateComponent(component: Component) {
+ val col = setComponentFields(stmtUpdateComponent, component)
+ stmtUpdateComponent.setInt(col, component.id)
+ stmtUpdateComponent.execute()
+ }
+
+ //
+
+ //
+
+ private fun selectProjects(stmt: PreparedStatement) = sequence {
+ stmt.executeQuery().use { rs ->
+ while (rs.next()) {
+ yield(Project(rs.getInt("projectid")).apply {
+ name = rs.getString("name")
+ node = rs.getString("node")
+ description = rs.getString("description")
+ repoUrl = rs.getString("repourl")
+ owner = selectUserInfo(rs)
+ })
+ }
+ }
+ }
+
+ private fun setProjectFields(stmt: PreparedStatement, obj: Project): Int {
+ with(obj) {
+ stmt.setStringSafe(1, name)
+ stmt.setStringSafe(2, node)
+ stmt.setStringOrNull(3, description)
+ stmt.setStringOrNull(4, repoUrl)
+ stmt.setIntOrNull(5, owner?.id)
+ }
+ return 6
+ }
+
+ //language=SQL
+ private val projectQuery =
+ """
+ select projectid, name, node, description, repourl,
+ userid, username, lastname, givenname, mail
+ from lpit_project
+ left join lpit_user owner on lpit_project.owner = owner.userid
+ """
+
+ private val stmtProjects by lazy {
+ connection.prepareStatement(
+ """${projectQuery}
+ order by lower(name)
+ """
+ )
+ }
+ private val stmtProjectByID by lazy {
+ connection.prepareStatement(
+ """${projectQuery}
+ where projectid = ?
+ """
+ )
+ }
+ private val stmtProjectByNode by lazy {
+ connection.prepareStatement(
+ """${projectQuery}
+ where node = ?
+ """
+ )
+ }
+ private val stmtInsertProject by lazy {
+ connection.prepareStatement(
+ "insert into lpit_project (name, node, description, repourl, owner) values (?, ?, ?, ?, ?)"
+ )
+ }
+ private val stmtUpdateProject by lazy {
+ connection.prepareStatement(
+ "update lpit_project set name = ?, node = ?, description = ?, repourl = ?, owner = ? where projectid = ?"
+ )
+ }
+ private val stmtIssueSummary by lazy {
+ connection.prepareStatement(
+ """
+ select phase, count(*) as total
+ from lpit_issue
+ join lpit_issue_phases using(status)
+ where project = ?
+ group by phase
+ """
+ )
+ }
+
+ override fun listProjects(): List {
+ return selectProjects(stmtProjects).toList()
+ }
+
+ override fun findProject(id: Int): Project? {
+ stmtProjectByID.setInt(1, id)
+ return selectProjects(stmtProjectByID).firstOrNull()
+ }
+
+ override fun findProjectByNode(node: String): Project? {
+ stmtProjectByNode.setString(1, node)
+ return selectProjects(stmtProjectByNode).firstOrNull()
+ }
+
+ override fun insertProject(project: Project) {
+ setProjectFields(stmtInsertProject, project)
+ stmtInsertProject.execute()
+ }
+
+ override fun updateProject(project: Project) {
+ val col = setProjectFields(stmtUpdateProject, project)
+ stmtUpdateProject.setInt(col, project.id)
+ stmtUpdateProject.execute()
+ }
+
+ override fun collectIssueSummary(project: Project): IssueSummary {
+ stmtIssueSummary.setInt(1, project.id)
+ return stmtIssueSummary.executeQuery().use { rs ->
+ val summary = IssueSummary()
+ while (rs.next()) {
+ val phase = rs.getInt("phase")
+ val total = rs.getInt("total")
+ when (phase) {
+ 0 -> summary.open = total
+ 1 -> summary.active = total
+ 2 -> summary.done = total
+ }
+ }
+ summary
+ }
+ }
+
+ //
+
+ //
+
+ private fun selectIssues(stmt: PreparedStatement) = sequence {
+ stmt.executeQuery().use { rs ->
+ while (rs.next()) {
+ val proj = Project(rs.getInt("project")).apply {
+ name = rs.getString("projectname")
+ node = rs.getString("projectnode")
+ }
+ val comp = rs.getInt("component").let {
+ if (rs.wasNull()) null else
+ Component(it, proj.id).apply {
+ name = rs.getString("componentname")
+ node = rs.getString("componentnode")
+ }
+ }
+ val issue = Issue(rs.getInt("issueid"), proj, comp).apply {
+ component = comp
+ status = rs.getEnum("status")
+ category = rs.getEnum("category")
+ subject = rs.getString("subject")
+ description = rs.getString("description")
+ assignee = selectUserInfo(rs)
+ created = rs.getTimestamp("created")
+ updated = rs.getTimestamp("updated")
+ eta = rs.getDate("eta")
+ }
+ queryAffectedVersions.setInt(1, issue.id)
+ issue.affectedVersions = selectVersions(queryAffectedVersions).toList()
+ queryResolvedVersions.setInt(1, issue.id)
+ issue.resolvedVersions = selectVersions(queryResolvedVersions).toList()
+ yield(issue)
+ }
+ }
+ }
+
+ private fun setIssueFields(stmt: PreparedStatement, obj: Issue): Int {
+ with(obj) {
+ stmt.setIntOrNull(1, component?.id)
+ stmt.setEnum(2, status)
+ stmt.setEnum(3, category)
+ stmt.setStringSafe(4, subject)
+ stmt.setStringOrNull(5, description)
+ stmt.setIntOrNull(6, assignee?.id)
+ stmt.setDateOrNull(7, eta)
+ }
+ return 8
+ }
+
+ //language=SQL
+ private val issueQuery =
+ """
+ select issueid,
+ i.project, p.name as projectname, p.node as projectnode,
+ component, c.name as componentname, c.node as componentnode,
+ status, category, subject, i.description,
+ userid, username, givenname, lastname, mail,
+ created, updated, eta
+ from lpit_issue i
+ join lpit_project p on i.project = projectid
+ left join lpit_component c on component = c.id
+ left join lpit_user on userid = assignee
+ """
+
+ private val queryResolvedVersions by lazy {
+ connection.prepareStatement(
+ """
+ select versionid, project, name, status, ordinal, node
+ from lpit_version v join lpit_issue_resolved_version using (versionid)
+ where issueid = ?
+ order by ordinal, name
+ """
+ )
+ }
+
+ private val queryAffectedVersions by lazy {
+ connection.prepareStatement(
+ """
+ select versionid, project, name, status, ordinal, node
+ from lpit_version join lpit_issue_affected_version using (versionid)
+ where issueid = ?
+ order by ordinal, name
+ """
+ )
+ }
+
+ private val stmtIssues by lazy {
+ connection.prepareStatement(
+ """
+ with issue_version as (
+ select issueid, versionid from lpit_issue_affected_version
+ union select issueid, versionid from lpit_issue_resolved_version
+ ) ${issueQuery} left join issue_version using (issueid)
+ where
+ (not ? or projectid = ?) and
+ (not ? or versionid = ?) and (not ? or versionid is null) and
+ (not ? or component = ?) and (not ? or component is null)
+ """
+ )
+ }
+
+ private val fproj = 1
+ private val projectid = 2
+ private val fversion = 3
+ private val versionid = 4
+ private val nversion = 5
+ private val fcomp = 6
+ private val component = 7
+ private val ncomp = 8
+
+ private fun applyFilter(filter: Filter, fflag: Int, nflag: Int, idcol: Int) {
+ when (filter) {
+ is AllFilter -> {
+ stmtIssues.setBoolean(fflag, false)
+ stmtIssues.setBoolean(nflag, false)
+ stmtIssues.setInt(idcol, 0)
+ }
+ is NoneFilter -> {
+ stmtIssues.setBoolean(fflag, false)
+ stmtIssues.setBoolean(nflag, true)
+ stmtIssues.setInt(idcol, 0)
+ }
+ is SpecificFilter -> {
+ stmtIssues.setBoolean(fflag, true)
+ stmtIssues.setBoolean(nflag, false)
+ stmtIssues.setInt(idcol, filter.obj.id)
+ }
+ else -> {
+ TODO("Implement range filter.")
+ }
+ }
+ }
+
+ override fun listIssues(filter: IssueFilter): List {
+ when (filter.project) {
+ is AllFilter -> {
+ stmtIssues.setBoolean(fproj, false)
+ stmtIssues.setInt(projectid, 0)
+ }
+ is SpecificFilter -> {
+ stmtIssues.setBoolean(fproj, true)
+ stmtIssues.setInt(projectid, filter.project.obj.id)
+ }
+ else -> throw IllegalArgumentException()
+ }
+ applyFilter(filter.version, fversion, nversion, versionid)
+ applyFilter(filter.component, fcomp, ncomp, component)
+
+ return selectIssues(stmtIssues).toList()
+ }
+
+ private val stmtFindIssueByID by lazy {
+ connection.prepareStatement(
+ """${issueQuery}
+ where issueid = ?
+ """
+ )
+ }
+ private val stmtInsertIssue by lazy {
+ connection.prepareStatement(
+ """
+ insert into lpit_issue (component, status, category, subject, description, assignee, eta, project)
+ values (?, ?::issue_status, ?::issue_category, ?, ?, ?, ?, ?)
+ returning issueid
+ """
+ )
+ }
+ private val stmtUpdateIssue by lazy {
+ connection.prepareStatement(
+ """
+ update lpit_issue set updated = now(),
+ component = ?, status = ?::issue_status, category = ?::issue_category, subject = ?,
+ description = ?, assignee = ?, eta = ?
+ where issueid = ?
+ """
+ )
+ }
+ private val stmtInsertAffectedVersion by lazy {
+ connection.prepareStatement(
+ "insert into lpit_issue_affected_version (issueid, versionid) values (?,?)"
+ )
+ }
+ private val stmtInsertResolvedVersion by lazy {
+ connection.prepareStatement(
+ "insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)"
+ )
+ }
+ private val stmtClearAffectedVersions by lazy {
+ connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?")
+ }
+ private val stmtClearResolvedVersions by lazy {
+ connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?")
+ }
+
+ override fun findIssue(id: Int): Issue? {
+ stmtFindIssueByID.setInt(1, id)
+ return selectIssues(stmtFindIssueByID).firstOrNull()
+ }
+
+ private fun insertVersionInfo(issue: Issue) {
+ stmtInsertAffectedVersion.setInt(1, issue.id)
+ stmtInsertResolvedVersion.setInt(1, issue.id)
+ issue.affectedVersions.forEach {
+ stmtInsertAffectedVersion.setInt(2, it.id)
+ stmtInsertAffectedVersion.execute()
+ }
+ issue.resolvedVersions.forEach {
+ stmtInsertResolvedVersion.setInt(2, it.id)
+ stmtInsertResolvedVersion.execute()
+ }
+ }
+
+ override fun insertIssue(issue: Issue) {
+ val col = setIssueFields(stmtInsertIssue, issue)
+ stmtInsertIssue.setInt(col, issue.project.id)
+ stmtInsertIssue.executeQuery().use { rs ->
+ rs.next()
+ issue.id = rs.getInt(1)
+ }
+ insertVersionInfo(issue)
+ }
+
+ override fun updateIssue(issue: Issue) {
+ val col = setIssueFields(stmtUpdateIssue, issue)
+ stmtUpdateIssue.setInt(col, issue.id)
+ // TODO: improve by only inserting / deleting changed version information
+ stmtClearAffectedVersions.setInt(1, issue.id)
+ stmtClearResolvedVersions.setInt(1, issue.id)
+ stmtClearAffectedVersions.execute()
+ stmtClearResolvedVersions.execute()
+ insertVersionInfo(issue)
+ }
+
+ //
+
+ //
+
+ private fun selectComments(stmt: PreparedStatement) = sequence {
+ stmt.executeQuery().use { rs ->
+ while (rs.next()) {
+ yield(IssueComment(rs.getInt("commentid"), rs.getInt("issueid")).apply {
+ created = rs.getTimestamp("created")
+ updated = rs.getTimestamp("updated")
+ updateCount = rs.getInt("updatecount")
+ comment = rs.getString("comment")
+ author = selectUserInfo(rs)
+ })
+ }
+ }
+ }
+
+ private val stmtComments by lazy {
+ connection.prepareStatement(
+ "select * from lpit_issue_comment left join lpit_user using (userid) where issueid = ? order by created"
+ )
+ }
+ private val stmtInsertComment by lazy {
+ connection.prepareStatement(
+ "insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)"
+ )
+ }
+ private val stmtUpdateIssueDate by lazy {
+ connection.prepareStatement(
+ "update lpit_issue set updated = now() where issueid = ?"
+ )
+ }
+
+ override fun listComments(issue: Issue): List {
+ stmtComments.setInt(1, issue.id)
+ return selectComments(stmtComments).toList()
+ }
+
+ override fun insertComment(issueComment: IssueComment) {
+ with(issueComment) {
+ stmtUpdateIssueDate.setInt(1, issueid)
+ stmtInsertComment.setInt(1, issueid)
+ stmtInsertComment.setStringSafe(2, comment)
+ stmtInsertComment.setIntOrNull(3, author?.id)
+ }
+ stmtInsertComment.execute()
+ stmtUpdateIssueDate.execute()
+ }
+ //
+}
\ No newline at end of file