Groovy SQL Example
1. Groovy SQL – Introduction
Apache Groovy (Groovy) is an object-oriented dynamic programming language for the Java platform. It is dynamically compiled to the Java Virtual Machine (JVM) bytecode, and inter-operates with other Java source codes and libraries. Groovy is written in Java and was first released in 2007. Groovy SQL module provides a higher-level abstraction on JDBC technology.
The Groovy SQL API supports a wide variety of databases: HSQLDB, Oracle, SQL Server, MySQL, MongoDB. In this example, I will create a Spring boot application which utilizes Groovy SQL to manage data in an H2 database.
2. Technologies Used
The example code in this article was built and run using:
- Java 1.8.101 (1.8.x will do fine)
- Maven 3.3.9 (3.3.x will do fine)
- Eclipse Mars (Any Java IDE would work)
- Spring boot 1.5.10.RELEASE
- Groovy 2.4
3. Spring-boot Application
Spring is the greatest tool to manage resources. In this example, we use Spring to manage database connections and transactions.
The easiest way to generate a Spring-boot application is via the Spring starter tool. Please check my other article here for more details. A maven project will be generated and downloaded to your workstation. Import it into your Eclipse work space. You should have no errors on building and running it. The generated project has a Java source folder. We will add two folders: src/main/groovy and src/test/groovy. Then we will include them as the build resources.
3.1 Dependencies
We will add Groovy library as a dependency in the generated pom.xml.
pom.xml
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <modelVersion>4.0.0</modelVersion> <groupId>jcg.zheng.demo</groupId> <artifactId>groovysql</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>groovysql</name> <description>Demo project for Groovy SQL</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> </resource> <resource> <directory>src/main/groovy</directory> </resource> <resource> <directory>src/test/groovy</directory> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project> |
3.2 Groovy Spring Application
We will add several Spring beans in the generated GroovySpringApplication class to manage the data resource:
dataSource– In-memory H2 databasejpaVendorAdapter–HibernateJpaVendorAdapterwithGenerateDdlenabledtransactionManager– The database transaction managerentityManagerFactory–LocalContainerEntityManagerFactoryBeanto scanEntityclasses injcg.zheng.demo.groovysqlpackage
GroovySpringApplication.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | package jcg.zheng.demo.groovysql;import javax.persistence.EntityManagerFactory;import javax.sql.DataSource;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.Bean;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;import org.springframework.orm.jpa.JpaTransactionManager;import org.springframework.orm.jpa.JpaVendorAdapter;import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;import org.springframework.orm.jpa.vendor.Database;import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;@SpringBootApplicationpublic class GroovySpringApplication { DataSource dataSource() { return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build(); } @Bean public JpaVendorAdapter jpaVendorAdapter() { HibernateJpaVendorAdapter bean = new HibernateJpaVendorAdapter(); bean.setDatabase(Database.H2); bean.setGenerateDdl(true); return bean; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) { LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean(); bean.setDataSource(dataSource); bean.setJpaVendorAdapter(jpaVendorAdapter); bean.setPackagesToScan("jcg.zheng.demo.groovysql"); return bean; } @Bean public JpaTransactionManager transactionManager(EntityManagerFactory emf) { return new JpaTransactionManager(emf); } public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(GroovySpringApplication.class, args); }} |
4. Groovy SQL
Groovy SQL provides adequate APIs to manage the data in the database. In this step, we will demonstrate the usages of several common APIs:
eachRow– Perform the SQL query with the givenClosure. It will hold the database transaction until the operations inside of theClosurecompletes.rows– Return rows of results set for the given SQL query. The connection is released when the rows return.firstRow– Return the first row of the result set for the SQL query.execute– Execute the SQL statement.executeInsert– Execute theINSERTSQL statement and return any auto-generated value.executeUpdate– Execute theUPDATESQL statement and return the updated row count.withBatch– Perform theClosurewithin a batch for a better performance.
4.1 Entities
We will create a User class and annotate it with @Entity which has attributes: ID, name, and autoGeneratedId. The autoGeneratedId is used to demonstrate the executeInsert method later.
User.groovy
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package jcg.zheng.demo.groovysql.entityimport javax.persistence.CascadeTypeimport javax.persistence.Entityimport javax.persistence.FetchTypeimport javax.persistence.GeneratedValueimport javax.persistence.GenerationTypeimport javax.persistence.Idimport javax.persistence.OneToManyimport javax.persistence.Tableimport groovy.transform.Canonical@Canonical@Entity@Table(name="USER")class User { String id String name @Id @GeneratedValue(strategy=GenerationType.IDENTITY) Long autoGeneratedId } |
4.2 CRUD Operations
We will build a Spring bean UserDao to create, update, delete, and find a user via Groovy SQL module. We will use parameterized query to avoid the SQL injection security vulnerability.
UserDao.groovy
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | package jcg.zheng.demo.groovysql.componentimport javax.annotation.PostConstructimport javax.annotation.PreDestroyimport javax.sql.DataSourceimport org.springframework.beans.factory.annotation.Autowiredimport org.springframework.stereotype.Componentimport org.springframework.transaction.annotation.Transactionalimport groovy.sql.Sqlimport jcg.zheng.demo.groovysql.entity.User@Component@Transactionalclass UserDao { private Sql userSql @Autowired DataSource datasoure def selectNamedSqlStr = "SELECT * FROM User WHERE id=:id " def insertNamedSqlstr = "INSERT INTO User (ID, name) VALUES ( :id, :name ) " def deleteNamedSqlStr = "DELETE FROM User WHERE ID = :id " def updateNamedSqlStr = "UPDATE User set name = :name WHERE ID = :id " def searchNamedSqlStr = "SELECT * FROM User WHERE name like :name " @PostConstruct void setUp(){ userSql = Sql.newInstance(datasoure) } @PreDestroy void cleanup(){ userSql.close() } List search(String name){ List values = [] Map params = [name:"%" + name + "%"] userSql.rows(searchNamedSqlStr, params).each { row -> values.add( mapUser(row) ) } return values } List findAll(){ List values = [] userSql.eachRow("SELECT * from User") { row -> values.add(mapUser(row)) } return values } User mapUser(def row){ User user = new User(id: row.id, name: row.name, autoGeneratedId: row.autoGeneratedId) } void deleteAll(){ userSql.execute("DELETE from User ") } User findById(String userId){ User ret = null Map params = [id:userId] def row = userSql.firstRow(selectNamedSqlStr, params) if( row != null) { ret = new User(id: row.id, name: row.name, autoGeneratedId: row.autoGeneratedId ) } return ret } void create(User user){ Map params = [id:user.id, name:user.name] def ret = userSql.executeInsert(insertNamedSqlstr, params) user.setAutoGeneratedId(ret[0][0]) } int delete(String userId){ Map params = [id:userId] userSql.execute(deleteNamedSqlStr, params) return userSql.updateCount } int update(User user){ Map params = [id:user.id, name:user.name] userSql.executeUpdate(updateNamedSqlStr, params) }} |
- line 43:
rowsusage - line 52:
eachRowusage - line 64, 88:
executeusages - line 71 :
firstRowusage - line 81, 83:
executeInsertusage - line 94:
executeUpdateusage
4.3 Batch Operations
We will create a Spring Bean UserBatchDao to create and delete a list of users in a batch model for better performance.
UserBatchDao.groovy
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package jcg.zheng.demo.groovysql.componentimport java.util.Listimport javax.annotation.PostConstructimport javax.annotation.PreDestroyimport javax.sql.DataSourceimport org.springframework.beans.factory.annotation.Autowiredimport org.springframework.beans.factory.annotation.Valueimport org.springframework.stereotype.Componentimport org.springframework.transaction.annotation.Transactionalimport groovy.sql.Sqlimport jcg.zheng.demo.groovysql.entity.User@Component@Transactionalclass UserBatchDao { private Sql userSql @Autowired DataSource datasoure; @PostConstruct void setUp(){ userSql = Sql.newInstance(datasoure) } @PreDestroy void cleanup(){ userSql.close() } void batchInsert(List users){ def prepareParameterizedQuery = 'INSERT INTO User (ID, name) VALUES (?, ?)' userSql.withBatch(5, prepareParameterizedQuery) { preparedQuery -> users.each{ user-> preparedQuery.addBatch(user.id, user.name) } } } void batchDelete(List users){ userSql.withBatch( { query -> users.each{ user-> query.addBatch("DELETE FROM User WHERE ID = '${user.id}' ") } }) } } |
Note: line 37, 39, 45, 47: withBatch usages
5. Unit Test Classes
We will demonstrate the Groovy SQL APIs via the unit test classes.
5.1 BaseTest
We will create a base test class for the common data and methods.
BaseTest.groovy
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | package jcg.zheng.demo.groovysql.componentimport static org.junit.Assert.*import java.util.Listimport jcg.zheng.demo.groovysql.entity.Userimport org.junit.Afterimport org.junit.Beforeimport org.springframework.beans.factory.annotation.Autowiredclass BaseTest { List users @Autowired UserDao userDao @Before public void setup(){ users =[ new User("1", "AMary"), new User("2", "AShan"), new User("3", "AZheng"), new User("4", "AZhang"), new User("5", "ALee"), new User("6", "AJohnson"), new User("7", "AShan1"), new User("8", "AZheng1"), new User("9", "AZhang1"), new User("10", "ALee1") ] for( int i = 0; i < 100; i++){ User user = new User("${i}", "dummyName{${i}") users.add(user) } } @After public void cleanup(){ userDao.deleteAll() } } |
5.2 UserDaoTest
We will create a test class to create, read, update, and delete users.
UserDaoTest.groovy
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | package jcg.zheng.demo.groovysql.componentimport static org.junit.Assert.*import org.junit.Testimport org.junit.runner.RunWithimport org.springframework.boot.test.context.SpringBootTestimport org.springframework.test.context.junit4.SpringRunnerimport org.springframework.transaction.annotation.Transactionalimport jcg.zheng.demo.groovyspring.model.*import jcg.zheng.demo.groovysql.entity.User@RunWith(SpringRunner.class)@SpringBootTest@Transactionalclass UserDaoTest extends BaseTest { @Test public void test_get() { List users = userDao.findAll() assertTrue(users.empty) } @Test void test_create_get() { User user0 = new User("id001", "Mary") userDao.create(user0) assertTrue(user0.autoGeneratedId > 0) User user = userDao.findById("id001") assertNotNull(user) assertEquals(user0, user) } @Test void test_create_delete() { User user0 = new User("id001", "Mary") userDao.create(user0) int deletedCount = userDao.delete(user0.id) assertEquals(1, deletedCount) User user = userDao.findById(user0.id) assertNull(user) } @Test public void test_create_update_get() { User userBefore = new User("id001", "Mary") userDao.create(userBefore) userBefore.setName("Mary2") def updateCount = userDao.update(userBefore) assertEquals(1, updateCount) User userAfter = userDao.findById("id001") assertEquals("Mary2", userAfter.name) }} |
5.3 UserBatchDaoTest
We will create a test class to compare the performance between a batch insert of 110 users to non-batch inserts. We will also compare the performance between rows and eachRow on retrieving data.
UserBatchDaoTest.groovy
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | package jcg.zheng.demo.groovysql.componentimport static org.junit.Assert.*import org.junit.Afterimport org.junit.Beforeimport org.junit.Testimport org.junit.runner.RunWithimport org.springframework.beans.factory.annotation.Autowiredimport org.springframework.boot.test.context.SpringBootTestimport org.springframework.test.context.junit4.SpringRunnerimport jcg.zheng.demo.groovyspring.model.*import jcg.zheng.demo.groovysql.entity.User@RunWith(SpringRunner.class)@SpringBootTestclass UserBatchDaoTest extends BaseTest { @Autowired private UserBatchDao batchDao @Test public void test_batchInsert() { batchDao.batchInsert(users) List users = userDao.findAll() assertFalse(users.empty) assertEquals(110, users.size()) } @Test public void test_inserts(){ users.each({user -> userDao.create(user)}) List users = userDao.findAll() assertFalse(users.empty) assertEquals(110, users.size()) } @Test public void test_batchDelete() { batchDao.batchInsert(users) batchDao.batchDelete(users) List users = userDao.findAll() assertTrue(users.empty) } @Test public void test_search_found() { batchDao.batchInsert(users) List foundUser = userDao.search("Zh") assertEquals(4, foundUser.size()) } @Test public void test_search_not_found() { batchDao.batchInsert(users) List foundUser = userDao.search("bad") assertEquals(0, foundUser.size()) } @Test public void test_all_rows() { batchDao.batchInsert(users) List foundUser = userDao.search("A") assertEquals(10, foundUser.size()) } @Test public void test_all_eachrow() { batchDao.batchInsert(users) List foundUser = userDao.findAll() assertEquals(110, foundUser.size()) } } |
As you see from the test results below, the batch to insert users took 47 milliseconds and the insert one user at a time took 94 milliseconds. The batch operation is faster than the non-batch operation. There is a tiny performance difference between rows and eachRow.
6. Summary
In this example, we built a Spring Boot application to demonstrate how to use Groovy SQL to manage the data in H2 database. Groovy SQL API is very simple to use. There are other JDBC tools available. Please check out my other articles for Spring data JPA and Quesydsl.
7. Download the Source Code
This example consists of a Spring boot application which demonstrates how to use Groovy SQL API to manage data stored in a H2 database.
You can download the full source code of this example here: Groovy SQL Example



