Getting Started with SpringBoot in Intellij IDEA Community Edition

We can use Intellij IDEA Community Edition for working with SpringBoot applications as we don’t need support for configuring servers like Tomcat, Wildlfy etc and can simply run the applications by running main() method.

However, there is no provision in Intellij IDEA Community Edition to create SpringBoot application directly, the way it supports in Ultimate Edition.

We can go to http://start.spring.io/ and generate the project and then import into our IDE. But it would be nice to be able to create the SpringBoot project from IDE itself.

One simple alternative I found to circumvent this problem is by using Project Template support in IntellijIDEA.

For the first time you can create the SpringBoot project from http://start.spring.io/ and import it into Intellij IDE.
If you wish you can add any other starters that you commonly use, configure properties like JDBC parameters etc.

Now click on menu Tools -> Save Project as Template. Enter Name and Description for the template.

SBTemplate

Once the template is created we can use that template while creating new projects.

Select File -> New -> Project.

NewProjDlg

In the New Project Dialog, you can see User-defined section and when you click on User-defined you can see all the project templates we created.
Select the SpringBootBasic template we created earlier and provide the project name and click Finish.

Well, we won’t get any fancy Spring support features like Ultimate Edition provides, but it will help us to get started quickly without requiring us to manually create Maven/Gradle project, configure <parent> etc etc.
Of course, it is not specific to SpringBoot only, we can create templates for any of our favorite tech stacks.

Happy coding 🙂

Update on SpringBoot : Learn By Example book

I would like to let you know that I have updated/added the following sections to my SpringBoot : Learn By Example book.

springboot_learn_by_example

Additions to existing chapters:

  • Working with Multiple Databases
  • Exposing JPA entities with bi-directional references through RESTful services

In some of our applications we need to work with multiple databases. For example, we may have a primary database and a reporting database where most the application uses primary database and the application reports will be generated out of reporting database data.

The section Working with Multiple Databases explains how to configure multiple databases while using JPA in a SpringBoot application.

As SpringBoot can’t auto-configure components such as TransactionManagers, EntityManagerFactoryBeans, DataSourceInitializers for multiple databases automatically, this section will show how to turn-off auto-configuration mechanism for a specific AutoConfiguration class and provide the required configuration explicitly.

Added new chapter : Chapter 16: Deploying SpringBoot Applications.

  • Running SpringBoot applications in production mode
  • Deploying SpringBoot application on Heroku
  • Running SpringBoot application on Docker

In this new chapter I have explained how to run the SpringBoot self-contained jar in production and how to override the configuration properties configured in src/main/resources/application-*.properties.

This chapter also covers how we can deploy SpringBoot + JPA + Postgres application on Heroku platform by linking to our GitHub repository. We can enable automatic deployment so that whenever we push our changes to our Github repository the updated application will be automatically deployed on Heroku.

Finally, we will learn how to run a SpringBoot + JPA + Postgres application on Docker container. This chapter explains how to run both Postgres server and application in separate containers and link them. Then, we will also look into running the docker containers using DockerCompose instead of starting them individually.

I hope you will enjoy these new additions. 🙂

My New Book SpringBoot : Learn By Example Published Today

I am happy to announce that my new book SpringBoot : Learn By Example  got published today on Leanpub.

 

springboot_learn_by_example

What is SpringBoot?

Spring is one of the most popular Java frameworks out there to build web and enterprise application.
Spring supports variety of configuration approaches (XML, Annotations, JavaConfig etc) and properly configuring Spring applications become a bit tedious and repetitive process. To avoid these problems Spring team introduced SpringBoot to address the complexity of configuring Spring application.

SpringBoot is an opinionated approach to configure Spring based applications. SpringBoot automatically configures the application components based on various criteria like the jars you have on classpath, beans you have already configured, properties configured etc which makes it very easy to get up and running quickly.

I strongly believe that the best way to learn or teach is by using examples. This book is filled with plenty of examples explaining the concepts  in step by step manner.

Who this book is for?

If you are looking for a fast paced guide to learn how to build Spring based applications using SpringBoot this book is for you!!.

If you are a Spring developer but haven’t get your hands on SpringBoot and you feel like I know the drill, just tell me how to do it in SpringBoot way..this books is what you are looking for!!!

If you are just starting to use SpringBoot and it is working great but it all looks magical and you want to understand how SpringBoot works behind the scenes..this book will definitely be helpful to you!!

If you are a seasoned Spring developer with good grasp of Spring framework concepts and you just want a fast-paced guide to learn SpringBoot quickly..sure..this book is definitely for you!!

I see lot of people asking “What is the difference between SpringMVC and SpringBoot?”, “Should I use SpringBoot or SpringMVC for my next web application?” etc etc. See Here, Here and Here . The short answer is SpringBoot is nothing but Spring only..but it helps you a lot to build Spring based applications quickly. The next question would be How?. The best way to explain it is by creating an application WITHOUT SpringBoot and show how quickly you can develop the same application WITH SpringBoot.

This book explains how normally we build Spring application without SpringBoot and shows how we can build the same application using SpringBoot in much easier way so that you can correlate the concepts and see how SpringBoot increases developer productivity.

SpringBoot does so many things behind the scenes to automatically configure the application. This book explains how SpringBoot AutoConfiguration works behind the scenes in very detailed manner and show how we can create our own AutoConfiguration as well.

What this book covers?

The SpringBoot : Learn By Example book covers the concepts that we commonly use in our Spring applications.

  • What is SpringBoot and how to get started with SpringBoot?
  • How SpringBoot AutoConfiguration Works behind the scenes using @Conditional?
  • SpringBoot goodies like Logging, ConfigurationProperties, DeveloperTools
  • Working with Databases using JdbcTemplate, MyBatis, JOOQ, Spring Data JPA
  • Working with MongoDB NoSQL database
  • Developing Web Applications using SpringMVC
    • SpringBoot Web Starter awesomeness
    • Using Thymeleaf Views
    • File Uploading
    • Validation, I18N
  • Developing RESTful Services using SpringMVC and Spring Data REST
  • Consuming SpringBoot REST API from AngularJS application
  • Securing Web applications and REST API
  • Monitoring SpringBoot applications with SpringBoot Actuator
  • Testing SpringBoot applications

To get complete list of topics covered in this book please see the below Table of Contents.



Table of Contents:

1. Introduction to SpringBoot

  • Overview of Spring framework
  • Developing Web Application using SpringMVC and JPA
  • A Quick Taste of SpringBoot

2. Getting Started with SpringBoot

  • What is SpringBoot?
  • Our First SpringBoot Application

3. SpringBoot Behind the scenes

  • Exploring the Power of @Conditional
  • How SpringBoot AutoConfiguration Works?

4. Creating Custom SpringBoot Starter

  • Create twitter4j-spring-boot-starter

5. SpringBoot Essentials

  • Logging
  • Externalizing Configuration Properties
  • Developer Tools

6. Working with JdbcTemplate

  • Using JdbcTemplate without SpringBoot
  • Using JdbcTemplate with SpringBoot
  • Initializing Database
  • How to use other Connection Pooling libraries?

7. Working with MyBatis

  • Using SpringBoot MyBatis Starter

8. Working with JOOQ

  • Using SpringBoot JOOQ Starter

9. Working with JPA

  • Introducing Spring Data JPA
  • Using Spring Data JPA with SpringBoot

10. Working with MongoDB

  • Introducing MongoDB
  • Getting Started with MongoDB using Mongo Shell
  • Talking to MongoDB using Spring Data Mongo

11. Web Applications with SpringBoot

  • Developing Web Application using SpringBoot
  • Using Tomcat, Jetty, Undertow Embedded Servlet Containers
  • Customizing SpringMVC configuration
  • SpringBoot web application as deployable war
  • SpringBoot Supporting View Templates
  • Working with Thymeleaf Forms
  • Data Validation
  • File Uploading
  • Using ResourceBundles for Internationalization (I18N)

12. Building REST API using SpringBoot

  • Introduction to RESTful WebServices
  • REST API using SpringMVC
  • REST API using Spring Data REST
  • Consuming REST API from AngularJS

13. Securing Web Applications using SpringBoot

  • Spring Security in SpringBoot Web Application
  • Method Level Security
  • Securing REST API using SpringSecurity

14. SpringBoot Actuator

  • Exploring Actuator Endpoints
  • Customizing Actuator Endpoints
  • Securing Actuator Endpoints
  • CORS support for Actuator Endpoints
  • Monitoring and management over JMX
  • Monitoring and management using a remote shell

15. Testing SpringBoot Applications

  • Testing Support in SpringBoot
  • Testing RESTful Services using RestTemplate
  • Testing Secured Controller/Service Methods
  • Testing with Mockito

Thank You Manuel Jordan

I would like to thank Manuel Jordan (@dr_pompeii) for reviewing the book and providing feedback which helped me a lot to improve the quality of the book content.

If  you wish please do me a favour by spreading the word on Twitter, Facebook, LinkedIn etc.
I hope you will enjoy the book 🙂

Creating Custom SpringBoot Starter for Twitter4j

SpringBoot provides lot of starter modules to get up and running quickly. SpringBoot’s auto-configure mechanism takes care of configuring SpringBeans on our behalf based on various criteria.

In addition to the springboot starters that comes out-of-the-box provided by Core Spring Team, we can also create our own starter modules.

In this post we will look into how to create a custom SpringBoot starter. To demonstrate it we are going to create twitter4j-spring-boot-starter which will auto-configure Twitter4J beans.

To accomplish this, we are going to create:

  1.  twitter4j-spring-boot-autoconfigure module which contains Twitter4J AutoConfiguration bean definitions 
  2. twitter4j-spring-boot-starter module which pulls in twitter4j-spring-boot-autoconfigure and twitter4j-core dependencies 
  3. Sample application which uses twitter4j-spring-boot-starter 

Create Parent Module spring-boot-starter-twitter4j

First we are going to create a parent pom type module to define dependency versions and sub-modules.

<?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"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.sivalabs</groupId>
    <artifactId>spring-boot-starter-twitter4j</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>spring-boot-starter-twitter4j</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <twitter4j.version>4.0.3</twitter4j.version>
        <spring-boot.version>1.3.2.RELEASE</spring-boot.version>
    </properties>

    <modules>
        <module>twitter4j-spring-boot-autoconfigure</module>
        <module>twitter4j-spring-boot-starter</module>
        <module>twitter4j-spring-boot-sample</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.twitter4j</groupId>
                <artifactId>twitter4j-core</artifactId>
                <version>${twitter4j.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

In this pom.xml we are defining the SpringBoot and Twitter4j versions in section so that we don’t need to specify versions all over the places. 


Create twitter4j-spring-boot-autoconfigure module 

Create a child module with name twitter4j-spring-boot-autoconfigure in our parent maven module spring-boot-starter-twitter4j

Add the maven dependencies such as spring-boot, spring-boot-autoconfigure, twitter4j-core and junit as follows:

<?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"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
               http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sivalabs</groupId>
    <artifactId>twitter4j-spring-boot-autoconfigure</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>com.sivalabs</groupId>
        <artifactId>spring-boot-starter-twitter4j</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.twitter4j</groupId>
            <artifactId>twitter4j-core</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

Note that we have specified twitter4j-core as optional dependency because twitter4j-core should be added to the project only when twitter4j-spring-boot-starter is added to the project.

Create Twitter4jProperties to hold the Twitter4J config parameters 

Create Twitter4jProperties.java to hold the Twitter4J OAuth config parameters.
package com.sivalabs.spring.boot.autoconfigure;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

@ConfigurationProperties(prefix= Twitter4jProperties.TWITTER4J_PREFIX)
public class Twitter4jProperties {

    public static final String TWITTER4J_PREFIX = "twitter4j";

    private Boolean debug = false;

    @NestedConfigurationProperty
    private OAuth oauth = new OAuth();

    public Boolean getDebug() {
        return debug;
    }

    public void setDebug(Boolean debug) {
        this.debug = debug;
    }

    public OAuth getOauth() {
        return oauth;
    }

    public void setOauth(OAuth oauth) {
        this.oauth = oauth;
    }

    public static class OAuth {

        private String consumerKey;
        private String consumerSecret;
        private String accessToken;
        private String accessTokenSecret;

        public String getConsumerKey() {
            return consumerKey;
        }
        public void setConsumerKey(String consumerKey) {
            this.consumerKey = consumerKey;
        }
        public String getConsumerSecret() {
            return consumerSecret;
        }
        public void setConsumerSecret(String consumerSecret) {
            this.consumerSecret = consumerSecret;
        }
        public String getAccessToken() {
            return accessToken;
        }
        public void setAccessToken(String accessToken) {
            this.accessToken = accessToken;
        }
        public String getAccessTokenSecret() {
            return accessTokenSecret;
        }
        public void setAccessTokenSecret(String accessTokenSecret) {
            this.accessTokenSecret = accessTokenSecret;
        }
    }
}

With this configuration object we can configure the twitter4j properties in application.properties as follows:

 

twitter4j.debug=true
twitter4j.oauth.consumer-key=your-consumer-key-here
twitter4j.oauth.consumer-secret=your-consumer-secret-here
twitter4j.oauth.access-token=your-access-token-here
twitter4j.oauth.access-token-secret=your-access-token-secret-here

Create Twitter4jAutoConfiguration to auto-configure Twitter4J 

Here comes the key part of our starter. Twitter4jAutoConfiguration configuration class contains the bean definitions that will be automatically configured based on some criteria. 
What is that criteria? 
  • If twitter4j.TwitterFactory.class is on classpath 
  • If TwitterFactory bean is not already defined explicitly 
So, the Twitter4jAutoConfiguration goes like this.
package com.sivalabs.spring.boot.autoconfigure;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;

@Configuration
@ConditionalOnClass({ TwitterFactory.class, Twitter.class })
@EnableConfigurationProperties(Twitter4jProperties.class)
public class Twitter4jAutoConfiguration {

    private static Log log = LogFactory.getLog(Twitter4jAutoConfiguration.class);

    @Autowired
    private Twitter4jProperties properties;

    @Bean
    @ConditionalOnMissingBean
    public TwitterFactory twitterFactory(){

        if (this.properties.getOauth().getConsumerKey() == null
            || this.properties.getOauth().getConsumerSecret() == null
            || this.properties.getOauth().getAccessToken() == null
            || this.properties.getOauth().getAccessTokenSecret() == null)
        {
            String msg = "Twitter4j properties not configured properly." + 
                         " Please check twitter4j.* properties settings in configuration file.";
            log.error(msg);
            throw new RuntimeException(msg);
        }

        ConfigurationBuilder cb = new ConfigurationBuilder();
        cb.setDebugEnabled(properties.getDebug())
          .setOAuthConsumerKey(properties.getOauth().getConsumerKey())
          .setOAuthConsumerSecret(properties.getOauth().getConsumerSecret())
          .setOAuthAccessToken(properties.getOauth().getAccessToken())
          .setOAuthAccessTokenSecret(properties.getOauth().getAccessTokenSecret());
        TwitterFactory tf = new TwitterFactory(cb.build());
        return tf;
    }

    @Bean
    @ConditionalOnMissingBean
    public Twitter twitter(TwitterFactory twitterFactory){
        return twitterFactory.getInstance();
    }

}

We have used @ConditionalOnClass({ TwitterFactory.class, Twitter.class }) to specify that this auto configuration should take place only when TwitterFactory.class, Twitter.class classes are present. 

We have also used @ConditionalOnMissingBean on bean definition methods to specify consider this bean definition only if TwitterFactory/Twitter beans are not already defined explicitly. 
Also note that we have annotated with @EnableConfigurationProperties(Twitter4jProperties.class) to enable support for ConfigurationProperties and injected Twitter4jProperties bean. 
Now we need to configure our custom Twitter4jAutoConfiguration in src/main/resources/META-INF/spring.factories file as follows:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.sivalabs.spring.boot.autoconfigure.Twitter4jAutoConfiguration 

Create twitter4j-spring-boot-starter module 

Create a child module with name twitter4j-spring-boot-starter in our parent maven module spring-boot-starter-twitter4j.
<?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"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sivalabs</groupId>
    <artifactId>twitter4j-spring-boot-starter</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>com.sivalabs</groupId>
        <artifactId>spring-boot-starter-twitter4j</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.sivalabs</groupId>
            <artifactId>twitter4j-spring-boot-autoconfigure</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.twitter4j</groupId>
            <artifactId>twitter4j-core</artifactId>
        </dependency>

    </dependencies>

</project>

Note that in this maven module we are actually pulling in twitter4j-core dependency. 

We don’t need to add any code in this module, but optionally we can specify what are the dependencies we are going to provide through this starter in src/main/resources/META-INF/spring.provides file as follows: 
provides: twitter4j-core 
That’s all for our starter. 
Let us create a sample using our brand new starter twitter4j-spring-boot-starter

Create twitter4j-spring-boot-sample sample application 

Let us create a simple SpringBoot application and add our twitter4j-spring-boot-starter dependency.
<?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"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sivalabs</groupId>
    <artifactId>twitter4j-spring-boot-sample</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.2.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <dependencies>

        <dependency>
            <groupId>com.sivalabs</groupId>
            <artifactId>twitter4j-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Create the entry-point class SpringbootTwitter4jDemoApplication as follows:

 

package com.sivalabs.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootTwitter4jDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootTwitter4jDemoApplication.class, args);
    }
}

Create TweetService as follows:

 

package com.sivalabs.demo;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;

@Service
public class TweetService {

    @Autowired
    private Twitter twitter;

    public List<String> getLatestTweets(){
        List<String> tweets = new ArrayList<>();
        try {
            ResponseList<Status> homeTimeline = twitter.getHomeTimeline();
            for (Status status : homeTimeline) {
                tweets.add(status.getText());
            }
        } catch (TwitterException e) {
            throw new RuntimeException(e);
        }
        return tweets;
    }
}

Now create a Test to verify our Twitter4j AutoConfigutation. 

Before that make sure you have set your twitter4j oauth configuration parameter to your actual values. You can get them from https://apps.twitter.com/

 

package com.sivalabs.demo;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import twitter4j.TwitterException;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(SpringbootTwitter4jDemoApplication.class)
public class SpringbootTwitter4jDemoApplicationTest  {


    @Autowired
    private TweetService tweetService;

    @Test
    public void testGetTweets() throws TwitterException {
        List<String> tweets = tweetService.getLatestTweets();
        for (String tweet : tweets) {
            System.err.println(tweet);
        }
    }

}

Now you should be able to see the latest tweets on your console output. 

SpringBoot : Working with JOOQ

In my previous article SpringBoot : Working with MyBatis  we have learned how to use SpringBoot MyBatis Starter to quickly get up and running with Spring and MyBatis. In this article we are going to learn about how to use SpringBoot JOOQ Starter.

JOOQ (Java Object Oriented Querying) is a persistence framework which embraces SQL.

JOOQ provides the following features:

  • Building Typesafe SQL using DSL API 
  • Typesafe database object referencing using Code Generation 
  • Easy to use API for Querying and Data fetching 
  • SQL logging and debugging
    etc etc 

SpringBoot provides a starter, spring-boot-starter-jooq, to be able to quickly integrate with JOOQ. 

In this article we will see how to use spring-boot-starter-jooq using step by step approach.

Step 1: Create SpringBoot Maven Project 

Create a SpringBoot maven based project and configure spring-boot-starter-jooq dependency.

<?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"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sivalabs</groupId>
    <artifactId>springboot-jooq-demo</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jooq</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
</project>

We are going to use H2 in-memory database first, later we will see how to use MySQL.

Step 2: Create the database initialization scripts. 

We are going to create a simple database with 2 tables.
src/main/resources/schema.sql

DROP TABLE IF EXISTS POSTS;

CREATE TABLE POSTS (
  ID int(11) NOT NULL AUTO_INCREMENT,
  TITLE varchar(200) NOT NULL,
  CONTENT LONGTEXT DEFAULT NULL,
  CREATED_ON datetime DEFAULT NULL,
  PRIMARY KEY (ID)
);

DROP TABLE IF EXISTS COMMENTS;

CREATE TABLE COMMENTS (
  ID int(11) NOT NULL AUTO_INCREMENT,
  POST_ID int(11) NOT NULL, 
  NAME varchar(200) NOT NULL,
  EMAIL varchar(200) NOT NULL,
  CONTENT LONGTEXT DEFAULT NULL,
  CREATED_ON datetime DEFAULT NULL,
  PRIMARY KEY (ID),
  FOREIGN KEY (POST_ID) REFERENCES POSTS(ID)
);

We will populate some sample data using data.sql script.

 src/main/resources/data.sql

insert into posts(id, title, content, created_on) values(1, 'Post 1', 'This is post 1', '2016-01-03');
insert into posts(id, title, content, created_on) values(2, 'Post 2', 'This is post 2', '2016-01-05');
insert into posts(id, title, content, created_on) values(3, 'Post 3', 'This is post 3', '2016-01-07');

insert into comments(id, post_id, name, email, content, created_on) 
values(1, 1, 'User1', 'user1@gmail.com', 'This is comment 1 on post 1', '2016-01-07');

insert into comments(id, post_id, name, email, content, created_on) 
values(2, 1, 'User2', 'user2@gmail.com', 'This is comment 2 on post 1', '2016-01-07');

insert into comments(id, post_id, name, email, content, created_on) 
values(3, 2, 'User1', 'user1@gmail.com', 'This is comment 1 on post 2', '2016-01-07');

Step 3: Configure JOOQ Maven Codegen Plugin to generate database artifacts 

We will use Maven profiles to configure the jooq-codegen-maven configuration properties based on database type.

<profiles>
    <profile>
        <id>h2</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.jooq</groupId>
                    <artifactId>jooq-codegen-maven</artifactId>
                    <executions>
                        <execution>
                            <goals>
                                <goal>generate</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>com.h2database</groupId>
                            <artifactId>h2</artifactId>
                            <version>${h2.version}</version>
                        </dependency>
                    </dependencies>
                    <configuration>
                        <jdbc>
                            <driver>org.h2.Driver</driver>
                            <url>jdbc:h2:~/springbootjooq</url>
                        </jdbc>
                        <generator>
                            <name>org.jooq.util.DefaultGenerator</name>
                            <database>
                                <name>org.jooq.util.h2.H2Database</name>
                                <includes>.*</includes>
                                <excludes />
                                <inputSchema>PUBLIC</inputSchema>
                            </database>
                            <target>
                                <packageName>com.sivalabs.demo.jooq.domain</packageName>
                                <directory>gensrc/main/java</directory>
                            </target>
                        </generator>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
    <profile>
        <id>mysql</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.jooq</groupId>
                    <artifactId>jooq-codegen-maven</artifactId>
                    <executions>
                        <execution>
                            <goals>
                                <goal>generate</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>mysql</groupId>
                            <artifactId>mysql-connector-java</artifactId>
                            <version>${mysql.version}</version>
                        </dependency>
                    </dependencies>
                    <configuration>
                        <jdbc>
                            <driver>com.mysql.jdbc.Driver</driver>
                            <url>jdbc:mysql://localhost:3306/test</url>
                            <user>root</user>
                            <password>admin</password>
                        </jdbc>
                        <generator>
                            <name>org.jooq.util.DefaultGenerator</name>
                            <database>
                                <name>org.jooq.util.mysql.MySQLDatabase</name>
                                <includes>.*</includes>
                                <excludes />
                                <inputSchema>test</inputSchema>
                            </database>
                            <target>
                                <packageName>com.sivalabs.demo.jooq.domain</packageName>
                                <directory>gensrc/main/java</directory>
                            </target>
                        </generator>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

We have configured two profile (h2 and mysql) with appropriate JDBC configuration parameters.

We have specified to generate the code artifacts and place it in com.sivalabs.demo.jooq.domain package within gensrc/main/java directory.

We can run the maven build activating h2 or mysql profile as follows:
 mvn clean install -P h2 (or) mvn clean install -P mysql 

Step 4: Configure Maven build-helper-maven-plugin Plugin to add the generated source as sources folder

We will configure the build-helper-maven-plugin plugin such that maven will add the JOOQ generated code resides in gensrc/main/java directory as source folder.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>gensrc/main/java</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>

Step 5: Create domain objects. 

We can use these domain object to pass data across the layer and JOOQ generated database artifacts to talk to database.

public class Post
{
    private Integer id;
    private String title;
    private String content;
    private Timestamp createdOn;
    private List<Comment> comments = new ArrayList<>();
    //setters & getters

}
public class Comment
{
    private Integer id;
    private Post post;
    private String name;
    private String email;
    private String content;
    private Timestamp createdOn;
    //setters & getters
}

Step 6: Implement the data persistence methods using JOOQ as follows.

package com.sivalabs.demo;

import static com.sivalabs.demo.jooq.domain.tables.Posts.POSTS;
import static com.sivalabs.demo.jooq.domain.tables.Comments.COMMENTS;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sivalabs.demo.entities.Comment;
import com.sivalabs.demo.entities.Post;
import com.sivalabs.demo.jooq.domain.tables.records.CommentsRecord;
import com.sivalabs.demo.jooq.domain.tables.records.PostsRecord;

@Service
@Transactional
public class BlogService
{
    @Autowired
    private DSLContext dsl;
    
    public Post createPost(Post post){
        PostsRecord postsRecord = dsl.insertInto(POSTS)
                .set(POSTS.TITLE, post.getTitle())
                .set(POSTS.CONTENT, post.getContent())
                .set(POSTS.CREATED_ON, post.getCreatedOn())
                .returning(POSTS.ID)
                .fetchOne();
            
        post.setId(postsRecord.getId());
        return post;
    }
    
    public List<Post> getAllPosts(){        
        List<Post> posts = new ArrayList<>();       
        Result<Record> result = dsl.select().from(POSTS).fetch();
        for (Record r : result) {
            posts.add(getPostEntity(r));
        }
        return posts ;
    }

    public Post getPost(Integer postId){
        Record record = dsl.select().
                                from(POSTS)
                                .where(POSTS.ID.eq(postId))
                                .fetchOne();
        if(record != null)
        {
            Post post = getPostEntity(record);
            
            Result<Record> commentRecords = dsl.select().
                                        from(COMMENTS)
                                        .where(COMMENTS.POST_ID.eq(postId))
                                        .fetch();
            
            for (Record r : commentRecords) {
                post.addComment(getCommentEntity(r));
            }
            return post;
        }
        return null;
    }
    
    
    public Comment createComment(Comment comment){
        CommentsRecord commentsRecord = dsl.insertInto(COMMENTS)
                .set(COMMENTS.POST_ID, comment.getPost().getId())
                .set(COMMENTS.NAME, comment.getName())
                .set(COMMENTS.EMAIL, comment.getEmail())
                .set(COMMENTS.CONTENT, comment.getContent())
                .set(COMMENTS.CREATED_ON, comment.getCreatedOn())
                .returning(COMMENTS.ID)
                .fetchOne();
            
        comment.setId(commentsRecord.getId());
        return comment;
    }
    
    public void deleteComment(Integer commentId){
        dsl.deleteFrom(COMMENTS)
                .where(COMMENTS.ID.equal(commentId))
                .execute();
    }
    
    private Post getPostEntity(Record r){
        Integer id = r.getValue(POSTS.ID, Integer.class);
        String title = r.getValue(POSTS.TITLE, String.class);
        String content = r.getValue(POSTS.CONTENT, String.class);
        Timestamp createdOn = r.getValue(POSTS.CREATED_ON, Timestamp.class);
        return new Post(id, title, content, createdOn);
    }
    
    private Comment getCommentEntity(Record r) {
        Integer id = r.getValue(COMMENTS.ID, Integer.class);
        Integer postId = r.getValue(COMMENTS.POST_ID, Integer.class);
        String name = r.getValue(COMMENTS.NAME, String.class);
        String email = r.getValue(COMMENTS.EMAIL, String.class);
        String content = r.getValue(COMMENTS.CONTENT, String.class);
        Timestamp createdOn = r.getValue(COMMENTS.CREATED_ON, Timestamp.class);
        return new Comment(id, postId, name, email, content, createdOn);
    }
}

Observe that we are auto-wiring DSLContext instance into our Spring Bean and using it to build the TypeSafe queries.

Step 7: Create Entry point class and JUnit test.

@SpringBootApplication
public class SpringbootJooqDemoApplication
{
    public static void main(String[] args) {
        SpringApplication.run(SpringbootJooqDemoApplication.class, args);
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(SpringbootJooqDemoApplication.class)
public class SpringbootJooqDemoApplicationTests
{

    @Autowired
    private BlogService blogService;
    
    @Test
    public void findAllPosts()  {
        List<Post> posts = blogService.getAllPosts();
        assertNotNull(posts);
        assertTrue(!posts.isEmpty());
        for (Post post : posts)
        {
            System.err.println(post);
        }
    }
    
    @Test
    public void findPostById()  {
        Post post = blogService.getPost(1);
        assertNotNull(post);
        System.out.println(post);
        List<Comment> comments = post.getComments();
        System.out.println(comments);
        
    }
    
    @Test
    public void createPost() {
        Post post = new Post(0, "My new Post", 
                            "This is my new test post", 
                            new Timestamp(System.currentTimeMillis()));
        Post savedPost = blogService.createPost(post);
        Post newPost = blogService.getPost(savedPost.getId());
        assertEquals("My new Post", newPost.getTitle());
        assertEquals("This is my new test post", newPost.getContent());
    }
    
    @Test
    public void createComment() {
        Integer postId = 1;
        Comment comment = new Comment(0, postId, "User4", 
                                "user4@gmail.com", "This is my new comment on post1", 
                                new Timestamp(System.currentTimeMillis()));
        Comment savedComment = blogService.createComment(comment);
        Post post = blogService.getPost(postId);
        List<Comment> comments = post.getComments();
        assertNotNull(comments);
        for (Comment comm : comments)
        {
            if(savedComment.getId() == comm.getId()){
                assertEquals("User4", comm.getName());
                assertEquals("user4@gmail.com", comm.getEmail());
                assertEquals("This is my new comment on post1", comm.getContent());
            }
        }
        
    }
    
}

Assuming you have generated code using H2 profile, we can run the JUnit test with out any further configuration.

But if you have generated code using mysql profile then you will have to configure the following properties in application.properties.

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=admin

spring.jooq.sql-dialect=MYSQL

Note that we should use correct SqlDialect for the database otherwise you may get SQL syntax errors at runtime. 

You can find the source code of this article at my Github repository https://github.com/sivaprasadreddy/springboot-tutorials/tree/master/springboot-jooq-demo

 For more info on JOOQ you can look at http://www.jooq.org/learn/