diff --git a/README.md b/README.md
index 295fc55..2b59824 100644
--- a/README.md
+++ b/README.md
@@ -10,11 +10,12 @@ This repository contains various project exercises, known as `chapters`. Externa
### Chapters
-| Chapter | Description |
-|----------------------------------|------------------|
-| [Chapter 0](exercises/chapter_0) | Hello World |
-| [Chapter 1](exercises/chapter_1) | HTTP Requests |
-| [Chapter 2](exercises/chapter_2) | Thymeleaf basics |
+| Chapter | Description |
+|-----------------------------------------|------------------|
+| [Chapter 0](exercises/chapter_0) | Hello World |
+| [Chapter 1](exercises/chapter_1) | HTTP Requests |
+| [Chapter 2](exercises/chapter_2) | Thymeleaf basics |
+| [JDBC](exercises/spring-jdbc-test) | JDBC Tests |
### Project work
diff --git a/exercises/spring-jdbc-test/pom.xml b/exercises/spring-jdbc-test/pom.xml
new file mode 100644
index 0000000..61782af
--- /dev/null
+++ b/exercises/spring-jdbc-test/pom.xml
@@ -0,0 +1,70 @@
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.4.RELEASE
+
+ com.example.sql-test
+ SQLTest
+ 0.0.1-SNAPSHOT
+ SQL Test
+ Spring SQL connection test
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ ${project.artifactId}
+
+
+
\ No newline at end of file
diff --git a/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/SqlTestApplication.java b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/SqlTestApplication.java
new file mode 100644
index 0000000..f9eed94
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/SqlTestApplication.java
@@ -0,0 +1,57 @@
+package com.example.sqltest;
+
+import java.math.BigDecimal;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Bean;
+
+import com.example.sqltest.model.Customer;
+import com.example.sqltest.model.CustomerDAOImpl;
+
+@SpringBootApplication
+public class SqlTestApplication extends SpringBootServletInitializer {
+ private static final Logger logger = LoggerFactory.getLogger(SqlTestApplication.class);
+
+
+ public static void main(String[] args) {
+ SpringApplication.run(SqlTestApplication.class, args);
+ }
+
+ @Bean
+ public CommandLineRunner CustomerRunner(CustomerDAOImpl customerDAO) {
+
+ return (args) -> {
+
+ logger.info("Deleting old database table entries");
+ customerDAO.dropAll();
+
+ logger.info("Creating new database table entries");
+
+ customerDAO.save(new Customer(
+ "Daniel", "Thyssenlauf",
+ "man", "English",
+ new BigDecimal("0.45"),
+ "danthyf@gmail.com", null
+ ));
+
+ customerDAO.save(new Customer(
+ "Janina", "Riikanen",
+ "woman", "Finnish",
+ new BigDecimal("1.74"),
+ "janskuuu@yahoo.com", "+358405341242"
+ ));
+
+ logger.info("Created a new database table with the following values");
+ for (Customer customer : customerDAO.findAll()) {
+ logger.info("CUSTOMER table: {}", customer.toString());
+ }
+
+ };
+
+ }
+}
diff --git a/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/Customer.java b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/Customer.java
new file mode 100644
index 0000000..7efc739
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/Customer.java
@@ -0,0 +1,173 @@
+package com.example.sqltest.model;
+
+import java.math.BigDecimal;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+//@Entity
+public class Customer {
+
+ private Long id;
+
+ private String firstName;
+ private String lastName;
+
+ private String sex;
+
+ private String language;
+
+ @Min((long) 0.00)
+ @Max((long) 10.00)
+ private BigDecimal engagement;
+
+ private String email;
+ private String phone; //Yes, phone number can have other symbols than numbers. Do regex check for input validation
+ // TODO add street address table
+ // TODO add invoices data table (requires a joining table between CUSTOMER & INVOICE tables)
+
+ // Setters
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public void setSex(String sex) {
+ this.sex = sex;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public void setEngagement(BigDecimal engagement) {
+ this.engagement = engagement;
+ }
+
+ public void setPhone(String phone) {
+ this.phone = phone;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ // Getters
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public String getSex() {
+ return sex;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public BigDecimal getEngagement() {
+ return engagement;
+ }
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ // Constructors
+
+ public Customer() {
+ this.id = (long) 0;
+ this.firstName = null;
+ this.lastName = null;
+
+ this.sex = null;
+ this.language = null;
+ this.engagement = null;
+ this.email = null;
+ this.phone = null;
+ }
+
+ public Customer(
+ Long id,
+ String firstName,
+ String lastName,
+
+ String sex,
+ String language,
+ BigDecimal engagement,
+ String email,
+ String phone
+ ) {
+
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+
+ this.sex = sex;
+ this.language = language;
+ this.engagement = engagement;
+ this.email = email;
+ this.phone = phone;
+ }
+
+ public Customer(
+ String firstName,
+ String lastName,
+
+ String sex,
+ String language,
+ BigDecimal engagement,
+ String email,
+ String phone
+ ) {
+
+ this.id = (long) 0;
+ this.firstName = firstName;
+ this.lastName = lastName;
+
+ this.sex = sex;
+ this.language = language;
+ this.engagement = engagement;
+ this.email = email;
+ this.phone = phone;
+ }
+
+ // Overrides
+
+ @Override
+ public String toString() {
+ return "[" +
+ "id: " + this.id + ", " +
+ "firstname: " + this.firstName + ", " +
+ "lastname: " + this.lastName + ", " +
+ "sex: " + this.sex + ", " +
+ "language: " + this.language + ", " +
+ "engagement: " + this.engagement + ", " +
+ "email: " + this.email + ", " +
+ "phone: " + this.phone +
+ "]";
+
+ }
+
+}
\ No newline at end of file
diff --git a/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerDAO.java b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerDAO.java
new file mode 100644
index 0000000..cab6316
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerDAO.java
@@ -0,0 +1,13 @@
+package com.example.sqltest.model;
+
+import java.util.List;
+
+public interface CustomerDAO {
+
+ public void save(Customer customer);
+
+ public Customer findById(Long id);
+ public List findAll();
+ public void dropAll();
+
+}
\ No newline at end of file
diff --git a/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerDAOImpl.java b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerDAOImpl.java
new file mode 100644
index 0000000..eda8629
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerDAOImpl.java
@@ -0,0 +1,86 @@
+package com.example.sqltest.model;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class CustomerDAOImpl implements CustomerDAO {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ // Setters
+
+ public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
+ // Getters
+ public JdbcTemplate getJdbcTemplate() {
+ return this.jdbcTemplate;
+ }
+
+ // Abstract methods
+
+ @Override
+ public void save(Customer customer) {
+ String sqlQuery = "INSERT INTO CUSTOMER(" +
+ "firstname" + "," +
+ "lastname" + "," +
+ "sex" + "," +
+ "language" + "," +
+ "engagement" + "," +
+ "email" + "," +
+ "phone" +
+ ") VALUES (?,?,?,?,?,?,?)";
+
+ Object[] sqlData = new Object[] {
+ customer.getFirstName(),
+ customer.getLastName(),
+
+ customer.getSex(),
+ customer.getLanguage(),
+ customer.getEngagement(),
+ customer.getEmail(),
+ customer.getPhone()
+ };
+
+ jdbcTemplate.update(sqlQuery, sqlData);
+ }
+
+ @Override
+ public Customer findById(Long id) {
+
+ String sqlQuery = "SELECT * FROM CUSTOMER WHERE ID = ?";
+ Object[] queryData = new Object[] { id };
+
+ RowMapper mapper = new CustomerMapper();
+
+ Customer customer = jdbcTemplate.queryForObject(sqlQuery, queryData, mapper);
+
+ return customer;
+ }
+
+ @Override
+ public List findAll() {
+
+ String sqlQuery = "SELECT * FROM CUSTOMER";
+ RowMapper mapper = new CustomerMapper();
+ List customers = jdbcTemplate.query(sqlQuery, mapper);
+
+ return customers;
+ }
+
+ @Override
+ public void dropAll() {
+ String sqlQueryDelete = "DELETE FROM CUSTOMER";
+ String sqlQueryResetId = "ALTER TABLE CUSTOMER AUTO_INCREMENT = 1";
+ jdbcTemplate.execute(sqlQueryDelete);
+ jdbcTemplate.execute(sqlQueryResetId);
+ }
+
+}
\ No newline at end of file
diff --git a/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerMapper.java b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerMapper.java
new file mode 100644
index 0000000..8d986d3
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerMapper.java
@@ -0,0 +1,27 @@
+package com.example.sqltest.model;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.springframework.jdbc.core.RowMapper;
+
+public class CustomerMapper implements RowMapper {
+
+ @Override
+ public Customer mapRow(ResultSet resultSet, int rowNum) throws SQLException {
+
+ Customer customer = new Customer();
+ customer.setId(resultSet.getLong("id"));
+ customer.setFirstName(resultSet.getString("firstname"));
+ customer.setLastName(resultSet.getString("lastname"));
+
+ customer.setSex(resultSet.getString("sex"));
+ customer.setLanguage(resultSet.getString("language"));
+ customer.setEngagement(resultSet.getBigDecimal("engagement"));
+ customer.setEmail(resultSet.getNString("email"));
+ customer.setPhone(resultSet.getNString("phone"));
+
+ return customer;
+ }
+
+}
\ No newline at end of file
diff --git a/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/web/CustomerController.java b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/web/CustomerController.java
new file mode 100644
index 0000000..64eae46
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/java/com/example/sqltest/web/CustomerController.java
@@ -0,0 +1,35 @@
+package com.example.sqltest.web;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.example.sqltest.model.Customer;
+import com.example.sqltest.model.CustomerDAO;
+
+@Controller
+public class CustomerController {
+
+ @Autowired
+ private CustomerDAO customerDAO;
+
+ @RequestMapping(
+ value = "/customers",
+ method = RequestMethod.GET
+ )
+ public String customerWebFormGet(Model dataModel) {
+
+ List customers = customerDAO.findAll();
+ dataModel.addAttribute("customers", customers);
+ return "customers";
+ }
+
+ @RequestMapping("*")
+ public String redirectWebForm() {
+ return "redirect:/customers";
+ }
+}
\ No newline at end of file
diff --git a/exercises/spring-jdbc-test/src/main/resources/application.properties b/exercises/spring-jdbc-test/src/main/resources/application.properties
new file mode 100644
index 0000000..bc522d5
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/resources/application.properties
@@ -0,0 +1,12 @@
+server.port = 8080
+
+# This MySQL database connection is established to a remote SQL server via SSH tunnel.
+
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.datasource.url=jdbc:mysql://127.0.0.1:3306/MYSQL_DATABASE?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
+spring.datasource.username=MYSQL_USERNAME
+spring.datasource.password=MYSQL_PASSWORD
+spring.datasource.initialization-mode=always
+
+logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG
+logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE
diff --git a/exercises/spring-jdbc-test/src/main/resources/schema.sql b/exercises/spring-jdbc-test/src/main/resources/schema.sql
new file mode 100644
index 0000000..79bb68a
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/resources/schema.sql
@@ -0,0 +1,13 @@
+CREATE TABLE IF NOT EXISTS CUSTOMER (
+ id INT(4) NOT NULL UNIQUE AUTO_INCREMENT,
+ firstname NVARCHAR(30) DEFAULT NULL,
+ lastname NVARCHAR(30) DEFAULT NULL,
+ sex VARCHAR(10) DEFAULT NULL,
+ language VARCHAR(20) DEFAULT NULL,
+ engagement DECIMAL(5,2) DEFAULT NULL,
+ email NVARCHAR(50) DEFAULT NULL,
+ phone VARCHAR(25) DEFAULT NULL,
+ PRIMARY KEY (id)
+ );
+
+CREATE UNIQUE INDEX IF NOT EXISTS `CUSTOMER.id.IDX_UNIQUE` ON CUSTOMER (id ASC);
diff --git a/exercises/spring-jdbc-test/src/main/resources/static/.gitignore b/exercises/spring-jdbc-test/src/main/resources/static/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/exercises/spring-jdbc-test/src/main/resources/templates/customers.html b/exercises/spring-jdbc-test/src/main/resources/templates/customers.html
new file mode 100644
index 0000000..58ababa
--- /dev/null
+++ b/exercises/spring-jdbc-test/src/main/resources/templates/customers.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+ Customer SQL data dump
+
+
+
+
+ Customer SQL data dump
+
+
+
+
+ First Name |
+ First Name |
+ Sex |
+ Language |
+ Engagement Level |
+ Email Address |
+ Phone Number |
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
\ No newline at end of file