From 4c983c392a7d3bbb84173a4548966adcc756f118 Mon Sep 17 00:00:00 2001 From: Pekka Helenius Date: Wed, 23 Sep 2020 02:17:33 +0300 Subject: [PATCH] Add JDBC exercise example --- README.md | 11 +- exercises/spring-jdbc-test/pom.xml | 70 +++++++ .../example/sqltest/SqlTestApplication.java | 57 ++++++ .../com/example/sqltest/model/Customer.java | 173 ++++++++++++++++++ .../example/sqltest/model/CustomerDAO.java | 13 ++ .../sqltest/model/CustomerDAOImpl.java | 86 +++++++++ .../example/sqltest/model/CustomerMapper.java | 27 +++ .../sqltest/web/CustomerController.java | 35 ++++ .../src/main/resources/application.properties | 12 ++ .../src/main/resources/schema.sql | 13 ++ .../src/main/resources/static/.gitignore | 0 .../main/resources/templates/customers.html | 38 ++++ 12 files changed, 530 insertions(+), 5 deletions(-) create mode 100644 exercises/spring-jdbc-test/pom.xml create mode 100644 exercises/spring-jdbc-test/src/main/java/com/example/sqltest/SqlTestApplication.java create mode 100644 exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/Customer.java create mode 100644 exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerDAO.java create mode 100644 exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerDAOImpl.java create mode 100644 exercises/spring-jdbc-test/src/main/java/com/example/sqltest/model/CustomerMapper.java create mode 100644 exercises/spring-jdbc-test/src/main/java/com/example/sqltest/web/CustomerController.java create mode 100644 exercises/spring-jdbc-test/src/main/resources/application.properties create mode 100644 exercises/spring-jdbc-test/src/main/resources/schema.sql create mode 100644 exercises/spring-jdbc-test/src/main/resources/static/.gitignore create mode 100644 exercises/spring-jdbc-test/src/main/resources/templates/customers.html 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 NameFirst NameSexLanguageEngagement LevelEmail AddressPhone Number
+ + + \ No newline at end of file