diff --git a/README.md b/README.md
index 05e5cc8..5e1bcc2 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,3 @@
# Spring Boot Web Application
+##Part 3
This repository has the project files for a tutorial series on Spring Boot available from by website at [Spring Framework Guru](https://springframework.guru)
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 575dff6..58d9bce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,13 +14,13 @@
org.springframework.boot
spring-boot-starter-parent
- 1.2.4.RELEASE
+ 2.2.2.RELEASE
UTF-8
- 1.8
+ 11
@@ -40,11 +40,22 @@
org.springframework.boot
spring-boot-starter-web
-
+
+
+
+ org.webjars
+ bootstrap
+ 3.3.4
+
+
+ org.webjars
+ jquery
+ 2.1.4
+
+
com.h2database
h2
- runtime
org.springframework.boot
diff --git a/src/main/java/guru/springframework/bootstrap/ProductLoader.java b/src/main/java/guru/springframework/bootstrap/ProductLoader.java
new file mode 100644
index 0000000..ad77bf0
--- /dev/null
+++ b/src/main/java/guru/springframework/bootstrap/ProductLoader.java
@@ -0,0 +1,46 @@
+package guru.springframework.bootstrap;
+
+import guru.springframework.domain.Product;
+import guru.springframework.repositories.ProductRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+
+@Component
+public class ProductLoader implements ApplicationListener {
+
+ private ProductRepository productRepository;
+
+ private Logger log = LoggerFactory.getLogger(ProductLoader.class);
+
+ @Autowired
+ public void setProductRepository(ProductRepository productRepository) {
+ this.productRepository = productRepository;
+ }
+
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent event) {
+
+ Product shirt = new Product();
+ shirt.setDescription("Spring Framework Guru Shirt");
+ shirt.setPrice(new BigDecimal("18.95"));
+ shirt.setImageUrl("https://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg");
+ shirt.setProductId("235268845711068308");
+ productRepository.save(shirt);
+
+ log.info("Saved Shirt - id: " + shirt.getId());
+
+ Product mug = new Product();
+ mug.setDescription("Spring Framework Guru Mug");
+ mug.setImageUrl("https://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_coffee_mug-r11e7694903c348e1a667dfd2f1474d95_x7j54_8byvr_512.jpg");
+ mug.setProductId("168639393495335947");
+ productRepository.save(mug);
+
+ log.info("Saved Mug - id:" + mug.getId());
+ }
+}
diff --git a/src/main/java/guru/springframework/configuration/SecurityConfiguration.java b/src/main/java/guru/springframework/configuration/SecurityConfiguration.java
new file mode 100644
index 0000000..87c00b7
--- /dev/null
+++ b/src/main/java/guru/springframework/configuration/SecurityConfiguration.java
@@ -0,0 +1,19 @@
+package guru.springframework.configuration;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity httpSecurity) throws Exception {
+ httpSecurity.authorizeRequests().antMatchers("/").permitAll().and()
+ .authorizeRequests().antMatchers("/console/**").permitAll();
+
+ httpSecurity.csrf().disable();
+ httpSecurity.headers().frameOptions().disable();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/guru/springframework/configuration/WebConfiguration.java b/src/main/java/guru/springframework/configuration/WebConfiguration.java
new file mode 100644
index 0000000..1872322
--- /dev/null
+++ b/src/main/java/guru/springframework/configuration/WebConfiguration.java
@@ -0,0 +1,16 @@
+package guru.springframework.configuration;
+
+import org.h2.server.web.WebServlet;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class WebConfiguration {
+ @Bean
+ ServletRegistrationBean h2servletRegistration(){
+ ServletRegistrationBean registrationBean = new ServletRegistrationBean( new WebServlet());
+ registrationBean.addUrlMappings("/console/*");
+ return registrationBean;
+ }
+}
diff --git a/src/main/java/guru/springframework/controllers/IndexController.java b/src/main/java/guru/springframework/controllers/IndexController.java
new file mode 100644
index 0000000..95280b6
--- /dev/null
+++ b/src/main/java/guru/springframework/controllers/IndexController.java
@@ -0,0 +1,12 @@
+package guru.springframework.controllers;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+public class IndexController {
+ @RequestMapping("/")
+ String index(){
+ return "index";
+ }
+}
diff --git a/src/main/java/guru/springframework/domain/Product.java b/src/main/java/guru/springframework/domain/Product.java
new file mode 100644
index 0000000..a40c704
--- /dev/null
+++ b/src/main/java/guru/springframework/domain/Product.java
@@ -0,0 +1,67 @@
+package guru.springframework.domain;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+
+@Entity
+public class Product {
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Integer id;
+
+ @Version
+ private Integer version;
+
+ private String productId;
+ private String description;
+ private String imageUrl;
+ private BigDecimal price;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getVersion() {
+ return version;
+ }
+
+ public void setVersion(Integer version) {
+ this.version = version;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getProductId() {
+ return productId;
+ }
+
+ public void setProductId(String productId) {
+ this.productId = productId;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+
+ public BigDecimal getPrice() {
+ return price;
+ }
+
+ public void setPrice(BigDecimal price) {
+ this.price = price;
+ }
+}
diff --git a/src/main/java/guru/springframework/repositories/ProductRepository.java b/src/main/java/guru/springframework/repositories/ProductRepository.java
new file mode 100644
index 0000000..820f086
--- /dev/null
+++ b/src/main/java/guru/springframework/repositories/ProductRepository.java
@@ -0,0 +1,7 @@
+package guru.springframework.repositories;
+
+import guru.springframework.domain.Product;
+import org.springframework.data.repository.CrudRepository;
+
+public interface ProductRepository extends CrudRepository{
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index e69de29..8681379 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -0,0 +1 @@
+#logging.level.org.h2.server: DEBUG
diff --git a/src/main/resources/static/css/guru.css b/src/main/resources/static/css/guru.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/resources/static/images/FBcover1200x628.png b/src/main/resources/static/images/FBcover1200x628.png
new file mode 100644
index 0000000..d28620a
Binary files /dev/null and b/src/main/resources/static/images/FBcover1200x628.png differ
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
new file mode 100644
index 0000000..7b3ef86
--- /dev/null
+++ b/src/main/resources/templates/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+ Spring Framework Guru
+
+
+
+
+
+
+
+
+
+
+
+

+
Hello
+
+
Fellow Spring Framework Gurus!!!
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/guru/springframework/SpringBootWebApplicationTests.java b/src/test/java/guru/springframework/SpringBootWebApplicationTests.java
index a1db764..fab8c48 100644
--- a/src/test/java/guru/springframework/SpringBootWebApplicationTests.java
+++ b/src/test/java/guru/springframework/SpringBootWebApplicationTests.java
@@ -2,12 +2,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
-import org.springframework.boot.test.SpringApplicationConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-@RunWith(SpringJUnit4ClassRunner.class)
-@SpringApplicationConfiguration(classes = SpringBootWebApplication.class)
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = SpringBootWebApplication.class)
@WebAppConfiguration
public class SpringBootWebApplicationTests {
diff --git a/src/test/java/guru/springframework/configuration/RepositoryConfiguration.java b/src/test/java/guru/springframework/configuration/RepositoryConfiguration.java
new file mode 100644
index 0000000..4f8aad2
--- /dev/null
+++ b/src/test/java/guru/springframework/configuration/RepositoryConfiguration.java
@@ -0,0 +1,15 @@
+package guru.springframework.configuration;
+
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+@Configuration
+@EnableAutoConfiguration
+@EntityScan(basePackages = {"guru.springframework.domain"})
+@EnableJpaRepositories(basePackages = {"guru.springframework.repositories"})
+@EnableTransactionManagement
+public class RepositoryConfiguration {
+}
diff --git a/src/test/java/guru/springframework/repositories/ProductRepositoryTest.java b/src/test/java/guru/springframework/repositories/ProductRepositoryTest.java
new file mode 100644
index 0000000..fdea2ae
--- /dev/null
+++ b/src/test/java/guru/springframework/repositories/ProductRepositoryTest.java
@@ -0,0 +1,70 @@
+package guru.springframework.repositories;
+
+import guru.springframework.domain.Product;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.math.BigDecimal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+@ExtendWith(SpringExtension.class)
+@DataJpaTest
+public class ProductRepositoryTest {
+
+ @Autowired
+ private ProductRepository productRepository;
+
+ @Test
+ public void testSaveProduct(){
+ //setup product
+ Product product = new Product();
+ product.setDescription("Spring Framework Guru Shirt");
+ product.setPrice(new BigDecimal("18.95"));
+ product.setProductId("1234");
+
+ //save product, verify has ID value after save
+ assertNull(product.getId()); //null before save
+ productRepository.save(product);
+ assertNotNull(product.getId()); //not null after save
+
+ //fetch from DB
+ Product fetchedProduct = productRepository.findById(product.getId()).orElse(null);
+
+ //should not be null
+ assertNotNull(fetchedProduct);
+
+ //should equal
+ assertEquals(product.getId(), fetchedProduct.getId());
+ assertEquals(product.getDescription(), fetchedProduct.getDescription());
+
+ //update description and save
+ fetchedProduct.setDescription("New Description");
+ productRepository.save(fetchedProduct);
+
+ //get from DB, should be updated
+ Product fetchedUpdatedProduct = productRepository.findById(fetchedProduct.getId()).orElse(null);
+ assertEquals(fetchedProduct.getDescription(), fetchedUpdatedProduct.getDescription());
+
+ //verify count of products in DB
+ long productCount = productRepository.count();
+ assertEquals(productCount, 1);
+
+ //get all products, list should only have one
+ Iterable products = productRepository.findAll();
+
+ int count = 0;
+
+ for(Product p : products){
+ count++;
+ }
+
+ assertEquals(count, 1);
+ }
+}