diff --git a/core-groovy/README.md b/core-groovy/README.md
new file mode 100644
index 0000000000..5954a8ab7e
--- /dev/null
+++ b/core-groovy/README.md
@@ -0,0 +1,4 @@
+# Groovy
+
+## Relevant articles:
+
diff --git a/core-groovy/pom.xml b/core-groovy/pom.xml
new file mode 100644
index 0000000000..965670e78e
--- /dev/null
+++ b/core-groovy/pom.xml
@@ -0,0 +1,118 @@
+
+
+ 4.0.0
+
+ core-groovy
+ 1.0-SNAPSHOT
+ jar
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ central
+ http://jcenter.bintray.com
+
+
+
+
+
+ org.codehaus.groovy
+ groovy
+ 2.4.13
+
+
+ org.codehaus.groovy
+ groovy-sql
+ 2.4.13
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.jupiter.version}
+ test
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+ test
+
+
+ org.hsqldb
+ hsqldb
+ 2.4.0
+ test
+
+
+
+
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+ 1.6
+
+
+
+ addSources
+ addTestSources
+ compile
+ compileTests
+
+
+
+
+
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+
+ maven-failsafe-plugin
+ 2.19.1
+
+
+ org.junit.platform
+ junit-platform-surefire-provider
+ ${junit.platform.version}
+
+
+
+
+ junit5
+
+ integration-test
+ verify
+
+
+
+ **/*Test5.java
+
+
+
+
+
+
+
+
+
+ UTF-8
+ 1.1.2
+ 1.1.2
+ 1.1.2
+ 1.1.2
+ 0.15
+ 1.5.0
+
+ 5.0.0
+ 1.0.0
+ 4.12.0
+ 4.12
+
+
+
diff --git a/core-groovy/src/test/groovy/com/baeldung/groovy/sql/SqlTest.groovy b/core-groovy/src/test/groovy/com/baeldung/groovy/sql/SqlTest.groovy
new file mode 100644
index 0000000000..ac96a55773
--- /dev/null
+++ b/core-groovy/src/test/groovy/com/baeldung/groovy/sql/SqlTest.groovy
@@ -0,0 +1,229 @@
+package com.baeldung.groovy.sql
+
+import groovy.sql.GroovyResultSet
+import groovy.sql.GroovyRowResult
+import groovy.sql.Sql
+import groovy.transform.CompileStatic
+import static org.junit.Assert.*
+import org.junit.Test
+
+class SqlTest {
+
+ final Map dbConnParams = [url: 'jdbc:hsqldb:mem:testDB', user: 'sa', password: '', driver: 'org.hsqldb.jdbc.JDBCDriver']
+
+ @Test
+ void whenNewSqlInstance_thenDbIsAccessed() {
+ def sql = Sql.newInstance(dbConnParams)
+ sql.close()
+ sql.close()
+ }
+
+ @Test
+ void whenTableDoesNotExist_thenSelectFails() {
+ try {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.eachRow('select * from PROJECT') {}
+ }
+
+ fail("An exception should have been thrown")
+ } catch (ignored) {
+ //Ok
+ }
+ }
+
+ @Test
+ void whenTableCreated_thenSelectIsPossible() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ def result = sql.execute 'create table PROJECT_1 (id integer not null, name varchar(50), url varchar(100))'
+
+ assertEquals(0, sql.updateCount)
+ assertFalse(result)
+
+ result = sql.execute('select * from PROJECT_1')
+
+ assertTrue(result)
+ }
+ }
+
+ @Test
+ void whenIdentityColumn_thenInsertReturnsNewId() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.execute 'create table PROJECT_2 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ def ids = sql.executeInsert("INSERT INTO PROJECT_2 (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials')")
+
+ assertEquals(0, ids[0][0])
+
+ ids = sql.executeInsert("INSERT INTO PROJECT_2 (NAME, URL) VALUES ('REST with Spring', 'github.com/eugenp/REST-With-Spring')")
+
+ assertEquals(1, ids[0][0])
+ }
+ }
+
+ @Test
+ void whenUpdate_thenNumberOfAffectedRows() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.execute 'create table PROJECT_3 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.executeInsert("INSERT INTO PROJECT_3 (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials')")
+ sql.executeInsert("INSERT INTO PROJECT_3 (NAME, URL) VALUES ('REST with Spring', 'github.com/eugenp/REST-With-Spring')")
+ def count = sql.executeUpdate("UPDATE PROJECT_3 SET URL = 'https://' + URL")
+
+ assertEquals(2, count)
+ }
+ }
+
+ @Test
+ void whenEachRow_thenResultSetHasProperties() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.execute 'create table PROJECT_4 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.executeInsert("INSERT INTO PROJECT_4 (NAME, URL) VALUES ('tutorials', 'https://github.com/eugenp/tutorials')")
+ sql.executeInsert("INSERT INTO PROJECT_4 (NAME, URL) VALUES ('REST with Spring', 'https://github.com/eugenp/REST-With-Spring')")
+
+ sql.eachRow("SELECT * FROM PROJECT_4") { GroovyResultSet rs ->
+ assertNotNull(rs.name)
+ assertNotNull(rs.url)
+ assertNotNull(rs[0])
+ assertNotNull(rs[1])
+ assertNotNull(rs[2])
+ assertEquals(rs.name, rs['name'])
+ assertEquals(rs.url, rs['url'])
+ }
+ }
+ }
+
+ @Test
+ void whenPagination_thenSubsetIsReturned() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.execute 'create table PROJECT_5 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.executeInsert("INSERT INTO PROJECT_5 (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials')")
+ sql.executeInsert("INSERT INTO PROJECT_5 (NAME, URL) VALUES ('REST with Spring', 'github.com/eugenp/REST-With-Spring')")
+ def rows = sql.rows('SELECT * FROM PROJECT_5 ORDER BY NAME', 1, 1)
+
+ assertEquals(1, rows.size())
+ assertEquals('REST with Spring', rows[0].name)
+ }
+ }
+
+ @Test
+ void whenParameters_thenReplacement() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.execute 'create table PROJECT_6 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.execute('INSERT INTO PROJECT_6 (NAME, URL) VALUES (?, ?)', 'tutorials', 'github.com/eugenp/tutorials')
+ sql.execute("INSERT INTO PROJECT_6 (NAME, URL) VALUES (:name, :url)", [name: 'REST with Spring', url: 'github.com/eugenp/REST-With-Spring'])
+
+ def rows = sql.rows("SELECT * FROM PROJECT_6 WHERE NAME = 'tutorials'")
+
+ assertEquals(1, rows.size())
+
+ rows = sql.rows("SELECT * FROM PROJECT_6 WHERE NAME = 'REST with Spring'")
+
+ assertEquals(1, rows.size())
+ }
+ }
+
+ @Test
+ void whenParametersInGString_thenReplacement() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.execute 'create table PROJECT_7 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.execute "INSERT INTO PROJECT_7 (NAME, URL) VALUES (${'tutorials'}, ${'github.com/eugenp/tutorials'})"
+ def name = 'REST with Spring'
+ def url = 'github.com/eugenp/REST-With-Spring'
+ sql.execute "INSERT INTO PROJECT_7 (NAME, URL) VALUES (${name}, ${url})"
+
+ def rows = sql.rows("SELECT * FROM PROJECT_7 WHERE NAME = 'tutorials'")
+
+ assertEquals(1, rows.size())
+
+ rows = sql.rows("SELECT * FROM PROJECT_7 WHERE NAME = 'REST with Spring'")
+
+ assertEquals(1, rows.size())
+ }
+ }
+
+ @Test
+ void whenTransactionRollback_thenNoDataInserted() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.withTransaction {
+ sql.execute 'create table PROJECT_8 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.executeInsert("INSERT INTO PROJECT_8 (NAME, URL) VALUES ('tutorials', 'https://github.com/eugenp/tutorials')")
+ sql.executeInsert("INSERT INTO PROJECT_8 (NAME, URL) VALUES ('REST with Spring', 'https://github.com/eugenp/REST-With-Spring')")
+ sql.rollback()
+
+ def rows = sql.rows("SELECT * FROM PROJECT_8")
+
+ assertEquals(0, rows.size())
+ }
+ }
+ }
+
+ @Test
+ void whenTransactionRollbackThenCommit_thenOnlyLastInserted() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.withTransaction {
+ sql.execute 'create table PROJECT_9 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.executeInsert("INSERT INTO PROJECT_9 (NAME, URL) VALUES ('tutorials', 'https://github.com/eugenp/tutorials')")
+ sql.rollback()
+ sql.executeInsert("INSERT INTO PROJECT_9 (NAME, URL) VALUES ('REST with Spring', 'https://github.com/eugenp/REST-With-Spring')")
+ sql.rollback()
+ sql.executeInsert("INSERT INTO PROJECT_9 (NAME, URL) VALUES ('tutorials', 'https://github.com/eugenp/tutorials')")
+ sql.executeInsert("INSERT INTO PROJECT_9 (NAME, URL) VALUES ('REST with Spring', 'https://github.com/eugenp/REST-With-Spring')")
+ }
+
+ def rows = sql.rows("SELECT * FROM PROJECT_9")
+
+ assertEquals(2, rows.size())
+ }
+ }
+
+ @Test
+ void whenException_thenTransactionIsRolledBack() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ try {
+ sql.withTransaction {
+ sql.execute 'create table PROJECT_10 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.executeInsert("INSERT INTO PROJECT_10 (NAME, URL) VALUES ('tutorials', 'https://github.com/eugenp/tutorials')")
+ throw new Exception('rollback')
+ }
+ } catch (ignored) {}
+
+ def rows = sql.rows("SELECT * FROM PROJECT_10")
+
+ assertEquals(0, rows.size())
+ }
+ }
+
+ @Test
+ void givenCachedConnection_whenException_thenDataIsPersisted() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ try {
+ sql.cacheConnection {
+ sql.execute 'create table PROJECT_11 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.executeInsert("INSERT INTO PROJECT_11 (NAME, URL) VALUES ('tutorials', 'https://github.com/eugenp/tutorials')")
+ throw new Exception('This does not rollback')
+ }
+ } catch (ignored) {}
+
+ def rows = sql.rows("SELECT * FROM PROJECT_11")
+
+ assertEquals(1, rows.size())
+ }
+ }
+
+ /*@Test
+ void whenModifyResultSet_thenDataIsChanged() {
+ Sql.withInstance(dbConnParams) { Sql sql ->
+ sql.execute 'create table PROJECT_5 (ID IDENTITY, NAME VARCHAR (50), URL VARCHAR (100))'
+ sql.executeInsert("INSERT INTO PROJECT_5 (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials')")
+ sql.executeInsert("INSERT INTO PROJECT_5 (NAME, URL) VALUES ('REST with Spring', 'github.com/eugenp/REST-With-Spring')")
+
+ sql.eachRow("SELECT * FROM PROJECT_5 FOR UPDATE ") { GroovyResultSet rs ->
+ rs['name'] = "The great ${rs.name}!" as String
+ rs.updateRow()
+ }
+
+ sql.eachRow("SELECT * FROM PROJECT_5") { GroovyResultSet rs ->
+ assertTrue(rs.name.startsWith('The great '))
+ }
+ }
+ }*/
+
+}
diff --git a/pom.xml b/pom.xml
index fc0c8f8ba7..d29f441a7e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
core-java
core-java-io
core-java-8
+ core-groovy