Spring Boot H2 datasource with Thymeleaf using Gradle

In this example, we will see how to build a simple web application in Spring boot with Thymeleaf as template engine and H2 database as embedded or in-memory database. Gradle has been used as build and dependency management tool.

We will create a sample Student Information System where we can enter Student record in one panel and see the existing records in another panel. Here is a screenshot of the web page for this:

Student Information System

The sample application is developed by the following source files:

  1. build.gradle
  2. SpringBootH2Example.java
  3. StudentController.java
  4. Student.java
  5. StudentRepository.java
  6. StudentService.java
  7. student.html
  8. layout.html
  9. application.properties
The files are placed as per following folder structure:
 1Root
 2│   build.gradle
 3 4└───src
 5    └───main
 6        ├───java
 7        │   └───com
 8        │       └───firstfewlines
 9        │           │   SpringBootH2Example.java
10        │           │
11        │           ├───controller
12        │           │       StudentController.java
13        │           │
14        │           ├───domain
15        │           │       Student.java
16        │           │
17        │           ├───repository
18        │           │       StudentRepository.java
19        │           │
20        │           └───service
21        │                   StudentService.java
2223        └───resources
24            │   application.properties
2526            └───templates
27                │   student.html
2829                └───layout
30                        layout.html
31                
32

Now let us see the content of the files:

1. build.gradle

The build.gradle files defines all the dependencies to Spring boot, Thymeleaf and Hibernate and H2 database library.

 1buildscript {
 2    ext {
 3        springBootVersion = '1.5.3.RELEASE'
 4    }
 5    repositories {
 6        mavenCentral()
 7    }
 8    dependencies {
 9        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
10    }
11}
12
13group 'com.firstfewlines'
14version '1.0.0-SNAPSHOT'
15
16apply plugin: 'java'
17apply plugin: 'idea'
18apply plugin: 'org.springframework.boot'
19
20repositories {
21    mavenCentral()
22}
23
24dependencies {
25    compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
26    compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
27    compile "org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion"
28    compile "org.springframework.boot:spring-boot-devtools:$springBootVersion"
29    runtime "com.h2database:h2"
30}

2. SpringBootH2Example.java

It is being the main Application entry point, initializes the Spring boot application.

 1package com.firstfewlines;
 2
 3import org.springframework.boot.SpringApplication;
 4import org.springframework.boot.autoconfigure.SpringBootApplication;
 5
 6@SpringBootApplication
 7public class SpringBootH2Example {
 8    public static void main(String [] argv){
 9        SpringApplication.run(SpringBootH2Example.class, argv);
10    }
11}

3. StudentController.java

It is the only controller class of the application. A controller class defines the Web Service end points needed in the application.

 1package com.firstfewlines.controller;
 2
 3import com.firstfewlines.domain.Student;
 4import com.firstfewlines.service.StudentService;
 5import org.springframework.beans.factory.annotation.Autowired;
 6import org.springframework.http.HttpStatus;
 7import org.springframework.http.MediaType;
 8import org.springframework.stereotype.Controller;
 9import org.springframework.web.bind.annotation.*;
10import org.springframework.web.servlet.ModelAndView;
11import java.text.DateFormat;
12import java.text.SimpleDateFormat;
13
14
15@Controller
16@RequestMapping("/")
17class StudentController {
18    final static DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
19
20    @Autowired
21    StudentService studentService;
22
23    @RequestMapping(method = RequestMethod.GET)
24    String home() {
25        return "student";
26    }
27
28    @RequestMapping(value = "student", method = RequestMethod.POST, consumes = MediaType.ALL_VALUE)
29    @ResponseStatus(value = HttpStatus.OK)
30    ModelAndView addStudent(@RequestParam Integer rollNo,
31                            @RequestParam String name,
32                            @RequestParam String dateOfBirth ) throws Exception {
33
34        ModelAndView modelAndView = new ModelAndView("student");
35        try {
36            Student student = new Student();
37            student.setRollNo(rollNo);
38            student.setName(name);
39            student.setDateOfBirth(df.parse(dateOfBirth));
40            student = studentService.addStudent(student);
41            modelAndView.addObject("message", "Student added with name: " + student.getName());
42        }
43        catch (Exception ex){
44            modelAndView.addObject("message", "Failed to add student: " + ex.getMessage());
45        }
46        modelAndView.addObject("students", studentService.getStudents());
47        return modelAndView;
48    }
49}

4. Student.java

This is the domain class defining the Student table in database.

 1package com.firstfewlines.domain;
 2
 3import javax.persistence.Entity;
 4import javax.persistence.Id;
 5import javax.persistence.Table;
 6import javax.persistence.Transient;
 7import java.text.DateFormat;
 8import java.text.SimpleDateFormat;
 9import java.util.Date;
10
11@Entity
12@Table(name="student_mst", schema = "public")
13public class Student {
14
15    @Transient
16    static final DateFormat df = new SimpleDateFormat("dd-MM-yyyy");
17
18    int rollNo;
19    String name;
20    String stream;
21    Date dateOfBirth;
22    int totalMarks;
23
24    @Id
25    public int getRollNo() {
26        return rollNo;
27    }
28
29    public void setRollNo(int rollNo) {
30        this.rollNo = rollNo;
31    }
32
33    public String getName() {
34        return name;
35    }
36
37    public void setName(String name) {
38        this.name = name;
39    }
40
41    public String getStream() {
42        return stream;
43    }
44
45    public void setStream(String stream) {
46        this.stream = stream;
47    }
48
49    public Date getDateOfBirth() {
50        return dateOfBirth;
51    }
52
53    @Transient
54    public String getDateOfBirthFormatted() {
55        return df.format(dateOfBirth);
56    }
57
58    public void setDateOfBirth(Date dateOfBirth) {
59        this.dateOfBirth = dateOfBirth;
60    }
61
62    public int getTotalMarks() {
63        return totalMarks;
64    }
65
66    public void setTotalMarks(int totalMarks) {
67        this.totalMarks = totalMarks;
68    }
69}

5. StudentRepository.java

This is an interface to save & retrieve Student object to and from database. The spring boot automatically creates concrete implementation of it. Our duty is to just reference it using @Autowired annotation as it is done in StudentService.java.

Notice that, there are no methods in this interface. It only extends from CrudRepository which has defined commonly needed methods for saving and retrieving the domain object to/from database. If we want, we can define our own methods here as well.

 1package com.firstfewlines.repository;
 2
 3import com.firstfewlines.domain.Student;
 4import org.springframework.data.repository.CrudRepository;
 5import org.springframework.stereotype.Repository;
 6
 7@Repository
 8public interface StudentRepository extends CrudRepository<Student, Integer> {
 9
10}

6. StudentService.java

This is the Service class to provide basic services for manipulating and retrieving Student. This works as a wrapper over StudentRepository. Student controller is referencing this service by @Autowired.

This is better practice to use Service classes from Controller classes instead of Controller classes directly handles database operations.

 1package com.firstfewlines.service;
 2
 3import com.firstfewlines.domain.Student;
 4import com.firstfewlines.repository.StudentRepository;
 5import org.springframework.beans.factory.annotation.Autowired;
 6import org.springframework.stereotype.Service;
 7
 8@Service
 9public class StudentService {
10
11    @Autowired
12    StudentRepository studentRepository;
13
14    public Student addStudent(Student student) {
15        studentRepository.save(student);
16        return student;
17    }
18
19    public Iterable<Student> getStudents(){
20        return studentRepository.findAll();
21    }
22}

7. student.html

This HTML contains the form to enter student and display existing students in a table. This is actually a block of HTML using Thymeleaf template engine. In runtime, it is rendered under the template that is defined in layout.html.

 1<!DOCTYPE HTML>
 2<html layout:decorator="layout/layout" xmlns:th="http://www.thymeleaf.org">
 3<body>
 4<th:block layout:fragment="content">
 5
 6    <div class="row">
 7        <div class="col-md-4 col-sm-4">
 8            <div class="panel panel-default">
 9                <div class="panel-heading">Add new Student</div>
10                <div class="panel-body">
11                    <form method="post" class="form-group" action="/student">
12                        <div class="form-group">
13                            <label for="rollNo">Roll No:&nbsp;</label>
14                            <input class="form-control" type="number" name="rollNo" id="rollNo" maxlength="10"/>
15                        </div>
16
17                        <div class="form-group">
18                            <label for="name">Name:&nbsp;</label>
19                            <input class="form-control" type="text" name="name" id="name" maxlength="50"/>
20                        </div>
21
22                        <div class="form-group">
23                            <label for="dateOfBirth">Date Of Birth:&nbsp;</label>
24                            <input class="form-control" type="date" name="dateOfBirth" id="dateOfBirth" maxlength="20"/>
25                        </div>
26
27                        <input class="btn btn-primary" type="submit" value="Save"/>
28
29                    </form>
30                </div>
31                <div class="panel-footer"><span th:text="${message}">Status</span></div>
32            </div>
33        </div>
34
35        <div class="col-md-8 col-sm-8">
36            <div class="panel panel-default">
37                <div class="panel-heading">Student List</div>
38                <div class="panel-body">
39                    <table class="table table-striped">
40                        <thead>
41                            <tr>
42                                <th>Roll No</th>
43                                <th>Name</th>
44                                <th>Date of Birth</th>
45                            </tr>
46                        </thead>
47                        <tbody>
48                            <tr th:each="student : ${students}">
49                                <td th:text="${student.getRollNo()}">Roll No</td>
50                                <td th:text="${student.getName()}">Name</td>
51                                <td th:text="${student.getDateOfBirthFormatted()}">Date of Birth</td>
52                            </tr>
53                        </tbody>
54                    </table>
55                </div>
56            </div>
57        </div>
58    </div>
59</th:block>
60</body>
61</html>

8. layout.html

This is the main Thymeleaf layout of the Application. It defines header, menu, footer and includes all the needed JavaScripts and CSSs like Bootstrap.

 1<!DOCTYPE html>
 2<html lang="en" xmlns:th="http://www.thymeleaf.org">
 3<head>
 4    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
 5    <meta charset="utf-8"></meta>
 6    <meta http-equiv="X-UA-Compatible" content="IE=edge"></meta>
 7    <meta name="viewport" content="width=device-width, initial-scale=1"></meta>
 8
 9    <meta name="description" content="Student Information System demo using SpringBoot, ThymeLeaf, H2 database"></meta>
10    <meta name="author" content="www.firstfewlines.com"></meta>
11    <title>Student Information System demo</title>
12
13    <script src="https://code.jquery.com/jquery-3.2.1.min.js"
14            integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
15            crossorigin="anonymous"></script>
16
17    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
18          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"></link>
19
20    <!-- Optional theme -->
21    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"
22          integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"></link>
23
24    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
25            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
26            crossorigin="anonymous"></script>
27
28</head>
29<body>
30<div class="container-fluid">
31    <nav class="navbar navbar-full navbar-inverse">
32        <div class="container-fluid">
33            <div class="navbar-header">
34                <a class="navbar-brand" href="#">Student Information System</a>
35            </div>
36            <ul class="navbar-nav nav">
37                <li class="nav-item active"><a class="nav-link" href="/">Home</a></li>
38                <li class="nav-item"><a class="nav-link" href="http://www.firstfewlines.com">About</a></li>
39            </ul>
40        </div>
41    </nav>
42
43    <div layout:fragment="content"></div>
44
45    <footer style="text-align:center;">
46        <hr/>
47        <p>Copyright © 2017 <a href="http://www.firstfewlines.com">www.firstfewlines.com</a></p>
48    </footer>
49</div>
50</body>
51</html>

9. application.properties

Last but not the least, the application properties. It defines the application settings like database connection, server port etc.

 1server.port=8090
 2
 3spring.datasource.url = jdbc:h2:mem:student
 4spring.datasource.username = sa
 5spring.datasource.password =
 6
 7# JPA/Hibernate properties
 8spring.jpa.show-sql = true
 9
10# Hibernate ddl auto (create, create-drop, update): with "update" the database
11# schema will be automatically updated accordingly to java entities found in project
12spring.jpa.hibernate.ddl-auto = update
13
14spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
15spring.jpa.hibernate.naming.strategy = org.hibernate.cfg.ImprovedNamingStrategy

How to run this Spring boot application?

  • Make sure you have latest version of Gradle installed and PATH is configured.
  • Go to the directory where you unzipped the file i.e. where the build.gradle is there
  • Run gradle clean bootRun command
  • This should run the application with output similar to below
  • Now open your browser and point to http://localhost:8090/

Alternatively, you can use IntelliJ IDEa or Eclipse to import the Gradle project and run from there.

output:
 1Starting a Gradle Daemon, 1 busy and 2 stopped Daemons could not be reused, use --status for details
 2:clean
 3:compileJava
 4:processResources
 5:classes
 6:findMainClass
 7:bootRun
 801:52:08.953 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Included patterns for restart : []
 901:52:08.955 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Excluded patterns for restart : [/spring-boot-starter/target/classes/, /spring-boot-autoconfigure/target/classes/, /spring-boot-starter-[\w-]+/, /spring-boot/target/classes/, /spring-boot-actuator/target/classes/, /spring-boot-devtools/target/classes/]
1001:52:08.955 [main] DEBUG org.springframework.boot.devtools.restart.ChangeableUrls - Matching URLs for reloading : [file:/D:/WS/postsrc/spring-boot-h2-datasource-thymeleaf-gradle-example/build/classes/main/, file:/D:/WS/postsrc/spring-boot-h2-datasource-thymeleaf-gradle-example/build/resources/main/]
11
12  .   ____          _            __ _ _
13 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
14( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
15 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
16  '  |____| .__|_| |_|_| |_\__, | / / / /
17 =========|_|==============|___/=/_/_/_/
18 :: Spring Boot ::        (v1.5.3.RELEASE)
19
202017-05-30 01:52:09.202  INFO 7088 --- [  restartedMain] com.firstfewlines.SpringBootH2Example    : Starting SpringBootH2Example on hdaswin8h with PID 7088 (D:\WS\postsrc\spring-boot-h2-datasource-thymeleaf-gradle-example\build\classes\main started by hdas in D:\WS\postsrc\spring-boot-h2-datasource-thymeleaf-gradle-example)
212017-05-30 01:52:09.203  INFO 7088 --- [  restartedMain] com.firstfewlines.SpringBootH2Example    : No active profile set, falling back to default profiles: default
222017-05-30 01:52:09.366  INFO 7088 --- [  restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1c71ce69: startup date [Tue May 30 01:52:09 IST 2017]; root of context hierarchy
232017-05-30 01:52:10.785  INFO 7088 --- [  restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8090 (http)
242017-05-30 01:52:10.795  INFO 7088 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service Tomcat
252017-05-30 01:52:10.796  INFO 7088 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.14
262017-05-30 01:52:10.880  INFO 7088 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
272017-05-30 01:52:10.880  INFO 7088 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1516 ms
282017-05-30 01:52:11.006  INFO 7088 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
292017-05-30 01:52:11.008  INFO 7088 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'webServlet' to [/h2-console/*]
302017-05-30 01:52:11.010  INFO 7088 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
312017-05-30 01:52:11.011  INFO 7088 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
322017-05-30 01:52:11.011  INFO 7088 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
332017-05-30 01:52:11.011  INFO 7088 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
342017-05-30 01:52:11.387  INFO 7088 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
352017-05-30 01:52:11.400  INFO 7088 --- [  restartedMain] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
36        name: default
37        ...]
382017-05-30 01:52:11.539  INFO 7088 --- [  restartedMain] org.hibernate.Version                    : HHH000412: Hibernate Core {5.0.12.Final}
392017-05-30 01:52:11.540  INFO 7088 --- [  restartedMain] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
402017-05-30 01:52:11.541  INFO 7088 --- [  restartedMain] org.hibernate.cfg.Environment            : HHH000021: Bytecode provider name : javassist
412017-05-30 01:52:11.567  INFO 7088 --- [  restartedMain] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
422017-05-30 01:52:11.641  INFO 7088 --- [  restartedMain] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
432017-05-30 01:52:11.932  INFO 7088 --- [  restartedMain] org.hibernate.tool.hbm2ddl.SchemaUpdate  : HHH000228: Running hbm2ddl schema update
442017-05-30 01:52:11.943  INFO 7088 --- [  restartedMain] rmationExtractorJdbcDatabaseMetaDataImpl : HHH000262: Table not found: student_mst
452017-05-30 01:52:11.971  INFO 7088 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
462017-05-30 01:52:12.364  INFO 7088 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1c71ce69: startup date [Tue May 30 01:52:09 IST 2017]; root of context hierarchy
472017-05-30 01:52:12.420  INFO 7088 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/],methods=[GET]}" onto java.lang.String com.firstfewlines.controller.StudentController.home()
482017-05-30 01:52:12.422  INFO 7088 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/student],methods=[POST],consumes=[*/*]}" onto org.springframework.web.servlet.ModelAndView com.firstfewlines.controller.StudentController.addStudent(java.lang.Integer,java.lang.String,java.lang.String) throws java.lang.Exception
492017-05-30 01:52:12.423  INFO 7088 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
502017-05-30 01:52:12.423  INFO 7088 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
512017-05-30 01:52:12.448  INFO 7088 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
522017-05-30 01:52:12.448  INFO 7088 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
532017-05-30 01:52:12.495  INFO 7088 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
542017-05-30 01:52:12.947  INFO 7088 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
552017-05-30 01:52:12.985  INFO 7088 --- [  restartedMain] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
562017-05-30 01:52:13.044  INFO 7088 --- [  restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8090 (http)
572017-05-30 01:52:13.048  INFO 7088 --- [  restartedMain] com.firstfewlines.SpringBootH2Example    : Started SpringBootH2Example in 4.08 seconds (JVM running for 4.419)

References:


Download source:


comments powered by Disqus