Spring

SPRING 5 - support for Java 8 and higher. (spring - simplify java enterprise development)

## Goals of Spring
- Lightweight development with Java POJOs(Plain-Old-Java-Objects) (easier than the heavy weight EJBs)
- Dependency Injection to promote loose coupling (via configuration files)
- Declarative programming with Aspect-Oriented-Programming(AOP) (add application wide services declaratively like logging, security, transactions etc.,)
- Minimize boilerplate Java Code

## Big picture of Spring Framework
-----------------------------------------
# Core Container - main part of the framework, for creating beans then making those beans available.
- Beans (Manages how beans are created, it has bean factory for creating the beans, reconfig files, dependency injection)
- Core
- SpEL (Spring Expression Language - to refer to the other beans)
- Context (spring container that holds the beans in memory)

# Infrastructure - creates application vide services like logging, securtiy, transactions, instrumentation, etc...
- AOP (Aspect-Oriented-Programming)
- Aspects
- Instrumentation (Java agents to remotely monitor your app with JMX (Java Management Extension)
- Messaging

# Data Access Layer - Communicating with db like rdbms, noSql also making use of message queues
- JDBC (Spring JDBC helper classes - reduce your jdbc code by 50%)
- ORM (Object to Relational Mapping, integrate with Hibernate/JPA)
- Transactions (support of Transaction manager for making transactions on Methods, database calls and many more.)
- OXM
- JMS (Spring Java Message Service helper classes, for sending async messages to a Message Broker/Message Queues)

# Web Layer - All web related classes (home for the spring mvc framework), Web remoting, Remote Procedure call (rpc) ..
- Servlet
- WebSocket
- Web
- Portlet

# Test Layer - Supports Test-Driven-Development(TDD), Mock objects and out-of-container testing
- Unit
- Integration
- Mock
-----------------------------------------

##Spring - Projects
-----------------------------------------
- Additional spring modules built-on top of the core Spring Framework
- Only use what you need...
Spring Boot, Spring Framework, Spring Cloud, Spring Data
Spring Batch, Spring Security
Spring for Android, Spring Web Flow
Spring Web Services (REST/SOAP), Spring LDAP

-----------------------------------------

##IoC - Inversion of Control
The approach of outsourcing the construction and management of objects.

two types of IoC containers
1) BeanFactory (simple applications, basic support)
2) ApplicationContext (real world applications, event handling, i18n, etc...)

-----------------------------------------

## Spring Developement Process
1) Cofigure your Spring Beans
- XML Configuration (Legacy)
- Java Annotations (New)
- Java Source Code (New)

// it should be under src folder if you are using ApplicationContext
//outside of the src folder and in the root folder we will have this configuration when using BeanFactory,
#configuring through XML - applicationContext.xml
<beans>
<bean id="myCouch" class = "com.xxx.xxx.xxx">
</bean>
<beans>
2) Create a Spring Container (generally known as Application Context) # Spring Object Factory
Specialized implementation
- ClassPathXmlApplicationContext
- AnnotationConfigApplicationContext
- GenericWebApplicationContext
- Others...

# creating Spring Container (2 types)
1) BeanFactory factory = new XmlBeanFactory(new FileSystemResource("spring.xml"));

2) ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

3) Retrieve Beans from Spring Container
Coach theCoach = context.getBean("myCouch", Coach.class);
-----------------------------------------

# What is a Spring Bean?

A "Spring Bean" is simply a Java object.

When Java objects are created by the Spring Container, then Spring refers to them as "Spring Beans".

Spring Beans are created from normal Java classes .... just like Java objects.

-----------------------------------------

Dependency Injection
1) Construction Injection (similar to the TypeScript DI, we pass dependencies in constructor)

<bean id="myCricketCoach" class="com.luv2code.springdemo.CricketCoach">
<constructor-arg ref="myFortuneService"/> // DI service reference
</bean>

2) Setter Injection (here instead of passing dependencies in constructor we pass it in setter method)

<bean id="myCricketCoach" class="com.luv2code.springdemo.CricketCoach">
<property name="fortuneService" ref="myFortuneService"/> // Setter Injection with service reference
<property name="emailAddress" value="thebestcoach@gmail.com"/> // Injecting literal values
</bean>

#how to inject values from a property file using annotations
src/sport.properties
----------------------
foo.email=myeasycoach@luv2code.com
foo.team=Silly Java Coders

- Load the properties file in the XML config file.
<context:property-placeholder location="classpath:sport.properties"/>

- in xml config file
<bean id="myCricketCoach" class="com.luv2code.springdemo.CricketCoach">
<property name="emailAddress" value="${foo.email}"/> // Injecting literal values
<property name="team" value="${foo.team}"/> // Injecting literal values
</bean>

- Inject the properties values into your Swim Coach: SwimCoach.java
@Value("${foo.email}")
private String email;

@Value("${foo.team}")
private String team;

BeanScope
-----------
Singleton - Create a single shared isntance of the bean. Default scope. <bean scope="singleton"> ... </bean>
Prototype - Creates a new bean instance for each container request. <bean scope="prototype"> ... </bean>
Request - Scoped to an HTTP web request. Only used for web apps.
Session - Scoped to an HTTP web session. Only used for web apps.
Global Session - Scoped to a gloabl HTTP web session. Only used for web apps.

Bean Life Cycle
------------

Container Started -> Bean Instantiated -> Dependency Injected -> Internal Spring Processing -> Your custom Init Method -> Bean is ready for use

Container Shutdown -> Your custom Destryo method -> STOP

Init/destroy method configuration
---------------------------
<beans ...>
<bean id="myCouch" class = "com.xxx.xxx.xxx"
init-method="doMyStartupStuff"
destroy-method="doMyCleanupStuff">
...
</bean>
<beans>

*These Init/Destroy methods can have any access modifier (public/protected/private)
*These methods can have any type of return type. However, "void" is most commonly used, as you will not be able to capture the return value.
*These methods can't accept any arguments, should be no-arg.

There is a subtle point you need to be aware of with "prototype" scoped beans.
For "prototype" scoped beans, Spring does not call the destroy method.  Gasp! 

In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, and otherwise assembles a prototype object, and hands it to the client, with no further record of that prototype instance.

Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are holding.

BeanFactory ---> beanfactorypostprocessor() can be called this method before bean instatiating.


##Annotations##
======================================================
Steps to enable Spring annotations
1)Enable component scanning in Srping config file.
<beans ...>
<context:component-scan base-package="com.luv2code.springdemo"/> // Spring will scan this package (recursively) and register them with the spring container in the background automatically.
</beans>

2) Add the @Component Annotation to your Java classes

@Component("thatSillyCoach") // give bean id, this will be identified as a bean and it will be registred automatically by spring
public class TennisCoach implements Coach {
...
}
Note: if you don't provide the bean id in the @Component, spring will make use class name as bean id ex: TennisCoach -> tennisCoach as bean id
3) Retrive bean from Spring Container
Same code as before, nothing changes.
Coach theCoach = context.getBean("thatSillyCoach", Coach.class);
//if you haven't provided any Bean id in @Component you should access the bean as below
Coach theCoach = context.getBean("tennisCoach", Coach.class);


AutoWiring
---------------------
- For Dependency Injection, Spring can use auto wiring
- Spring will look for a class that matches the property
matches by type: class or interface
- Spring will inject it automatically .. hence it is autowired

Autowiring Injection types
- Constructor Injection
Define the dependency interface and class
Create a constructor in your component class for injections
Configure the dependency injection with @Autowired Annotation
(As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean only defines one constructor to begin with. However, if several constructors are available, at least one must be annotated to teach the container which one to use.)
- Setter Injection
Create setter method(s) in your component class for injections
Configure the dependency injection with @Autowired Annotation
*Autowired can be used on any method not only setter methods.
- Field Injection
Inject dependencies by setting field vlaues on your class directly (even private fields) - Accomplished by using Java Reflection
ex:
@Autowired
private FortuneService fortuneService;

All the 3 types of Autowiring do the samething, you can use any one type through out the project for consistency.

# What if the interface has multiple implementations, how does spring will pick the actual implementation component, actually spring will throught error if it found multiple service implementations, to fix this use @Qualifier annotation as shown below.

@Autowired
@Qualifier("happyFortuneService") // specific component
private FortuneService fortuneService;
 
#for constructors you should write like below;
@Autowired
public TennisCoach(@Qualifier("randomFortuneService") FortuneService theFortuneService) {
fortuneService = theFortuneService;
}

3 ways of configuring Spring Container
-----------------------------------------
1) Full XML Configuration
<beans ...>
  <bean ...>
  </bean>
  <bean ...>
  </bean>
</beans>

2) XML Component Scan (we use Annotations), which will scan the application
<content:component-scan base-package="com.luv2code.springdemo" />

3) Java Configuration Class (no XML)
@Configuration
@ComponentScan("com.luv2code.springdemo")
public class SportConfig{
...
}

Java configuration
------------------
1) Create a Java class and annotate as @Configuration
2) Add component scanning support: @ComponentScan(optional)
3) Read Spring Java configuration class

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SportConfig.class);

4) Retrieve bean from Spring container

instead of component scan we can create beans with java configuration as below

@Bean //creating bean component manually (default scope singleton as we are not provided the scope)
public FortuneService getFurtuneService(){
return new FortuneService();
}
@Bean
public Coach swimCoach() { // "swimCoach" will be the bean id
 SwimCoach mySwimCoach = new SwimCoach(getFurtuneService()); // dependency injection 
 return mySwimCoach;
}

Injecting values from property files using java config
----------------------------------------------------------
in java config file add the below annotation
@PropertySource("classpath:sport.properties") // load properties file

in bean class make use of the property values as below
@Value("${foo.email}") // field level injection
private String email; // the value of email will be injected here

Hibernate
===========
A framework for persisting / saving Java Objects in a database
# Hibernate uses JDBC in the background for all the database communications

benefints of Hibernate
-----------------------
- Hibernate handles all of the low-level SQL
- Minimizes the amount of JDBC code you have to develop
- Hibernate provides the Object-to-Relational Mapping(ORM)

ORM
- the developer defines mapping between Java call and database table

Java Class                                         Database Table
------------------                             ---------------------
Stundent                                            student

id : int                                            id INT
firstName : String --> HIBERNATE --> first_name VARCHAR(45)
lastName : String <--           <-- last_name VARCHAR(45)
email : String email VARCHAR(45)


// create Java object
Studen theStudent = new Student("John", "Doe", "john@gmail.com");

//save it to database
int theId = (Integer) session.save(theStudent); // session in special Hibernate object

// save or update
session.saveOrUpdate(theStudent); // if primaryKey/id empty (null/0) then INSERT new customer else UPDATE existing customer.

// new retrieve from database using the primary key
Studen myStudent = session.get(Student.class, theId); //Hibernate will query the student table for given id

// fetch all the records from student table
Query query = session.createQuery("from Student");
List<Student> students = query.list(); // returns a list of Student objects from the database


connecting to jdbc postgres
----------------------------------------------------
public static void main(String[] args) {
try (Connection conn = DriverManager.getConnection(
                "jdbc:postgresql://127.0.0.1:5432/hb_student_tracker1", "postgres", "postgresql")) {

            if (conn != null) {
                System.out.println("Connected to the database!");
            } else {
                System.out.println("Failed to make connection!");
            }

        } catch (SQLException e) {
            System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }

}

Steps to connect postgress using hibernate
---------------------------------------------

Hibernate have 2 key players

SessionFactory Class
- Reads the hibernate config file
- Creates Session objects
- Heavy-weight object
- Only create once in your app

Session Class
- Wraps a JDBC connection
- Main object used to save/retrieve objects
- Short-lived object
- Retrieved from SessionFactory

1) Add hibernate config file

hibernate config xml file for connecting to postgresql -> hibernate.cfg.xml (default config file name)
-------------------------------------------------------------
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<!-- JDBC Database connection settings -->
<property name="connection.driver_class">org.postgresql.Driver</property>
<property name="connection.url">jdbc:postgresql://127.0.0.1:5432/hb_student_tracker1</property>
<property name="connection.username">postgres</property>
<property name="connection.password">postgresql</property>

<!-- JDBC connection pool settings ... using built-in test pool -->
<property name="connection.pool_size">1</property>

<!-- Select our SQL dialect -->
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>

<!-- Echo the SQL to stdout -->
<property name="show_sql">true</property>

<!-- Set the current session context -->
<property name="current_session_context_class">thread</property>

</session-factory>

</hibernate-configuration>

2) Annotate java class ( Entity class - Java class that is mapped to a database table (regular java class with setters & getters with annotations))

Two options for mapping
- XML config file (legacy)
- Java Annotations (modern, preferred)

Map class to database table and Map fields to database columns

@Entity
@Table(name="student")
public class Student {
@Id // primary key
@GeneratedValue(strategy=GenerationType.IDENTITY) // auto increment generation strategy
@Column(name="id")
private int id;

@Column(name="first_name")
private String first_name;

...
}

3) Develop Java code to perform database operations

public static void main(String[] args) {
SessionFactory factory = new Configuration()
.configure("hibernate.cfg.xml") // if you have the default config name passing xml file name is optional
.addAnnotatedClass(Student.class)
.buildSessionFactory();

Session session = factory.getCurrentSession();

try{
// insert a new student record
Student tempStudent = new Student("Paul", "Wall", "paul@luv2code.com");
session.beginTransaction();
session.save(tempStudent);
session.getTransaction().commit();

// retreive the above student record from db
session = factory.getCurrentSession();
session.beginTransaction();
Student myStudent = session.get(Student.class, tempStudent1.getId());
System.out.println("student : " + myStudent);
session.getTransaction().commit();

// get all the students list from db
session = factory.getCurrentSession();
session.beginTransaction();
List<Student> students = session.createQuery("from Student").getResultList();
for(Student theStudent: students){
System.out.println("student : " + theStudent);
}
session.getTransaction().commit();


// update student first name
int studentId = 1;
session = factory.getCurrentSession();
session.beginTransaction();
Student myStudent = session.get(Student.class, studentId);
myStudent.setFirstName("Scooby");
session.getTransaction().commit();

// update email of all the records
session = factory.getCurrentSession();
session.beginTransaction();
session.createQuery("update Student set email='foo@gmail.com'").executeUpdate();
session.getTransaction().commit();

// Delete Student by id
int studentId = 1;
session = factory.getCurrentSession();
session.beginTransaction();
Student myStudent = session.get(Student.class, studentId);
session.delete(myStudent);
session.getTransaction().commit();

// or you can delete it without retrieving the student
session.createQuery("delete from Student where id=2").executeUpdate();

} finally {
factory.close();
}
}

Entity Lifecycle
---------------
Detach - If entity is detached, it is not associated with a Hibernate sesssion
Merge - If instance is detached from session, then merge will reattach to session
Persist - Transactions new instances to managed state. Next flush / commit will save in db.
Remove - Transitions managed entity to be removed. Next flush / commit will delete from db.
Refresh - Reload / sync object with data from db. Prevens stale data.



HQL select queries
---------------
theStudents = session.createQuery("from Student s where s.last_name='Doe'").getResultList();;

theStudents = session.createQuery("from Student s where s.last_name='Doe' OR s.first_name='Daffy'").getResultList();;

theStudents = session.createQuery("from Student s where s.email LIKE '%gmail.com'").getResultList();;


Advanced Mapping
-----------------------
One-to-One (Student -> Student details)
One-to-Many Many-to-one (Student -> Course, Course, Course)
Many-to-Many -> (Student, Student -> Course, Course)

Cascade
----------
Applying the same operation to related entities

Ex: If we save instructor it should also save instructor-details table (Primary Key & Foreign Key relationship)
If we delete an instructor, we should also delete their instructor-detail, this is known as "CASCADE DELETE"

Fetch types: Eager vs Lazy Loading
-----------------------------------
When we fetch / retrieve data, should we retrieve EVERYTHING?

- Eager will retrive everything
- Lazy will retrieve on request

Ex: when I fetch instructor object should I get all the courses for that instructor immediately? or should I grab the course objects only on request?

So, EAGER will retrieve all the objects in oneshot, LAZY will retreive the data on request.

Uni-Directional
-------------------
One way relationship
Instructor --> Instructor Detail

You load the instructor and access the instructor details

Bi-Directional
-------------------
Instructor --> Instructor Detail

You load the instructor-details and access the instructor


@One-to-One
=================

@OneToOne - Cascade Types
----------------------------
PERSIST - if entity is persisted / saved, related entity will also be persisted
REMOVE - If entity is removed / deleted, related entity will also be deleted
REFRESH - If entity is refreshed, related entity will also by refreshed
DETACH - If entity is detached (not associated with session), then related entity will aslo be detached
MERGE   - If entity is merged, then related entity will also be merged
ALL - All of above cascade types

@Entity
@Table(name="instructor")
public class Instructor {
@Column
...

@OneToOne(cascade=CascadeType.ALL) // cascade={CascadeType.DETACH, CascadeType.MERGE, ...}
@JoinColumn(name="instructor_detail_id")
private InstructorDetail instructorDetail;

...
// constructors
// setters & getters
}

MAVEN
===============
Maven is a Project Management tool
Most popular use of Maven is for build management and dependencies

- Tell Maven the projects you are working with (dependencies like spring, hibernate...)
- Maven will go out and download the JAR files for the projects for you
- And maven will make those JAR files available during compile/run time

Maven - How it works
-----------------------------
1) Read Project Config file
2) Check Maven local Repository (your computer)
3) if not available get from Maven Remote Repository
4) Save in local Repository
5) Build and Run
- Maven will handle class / build path for you
- Based on config file, Maven will add JAR files accordingly

Advantages of Maven
---------------------------
- Dependency management
. Maven will find JAR file for you
. No more missing JARs

- Building and Running your project
. No more build path / classpath issues

- Standard directory structure

POM file - pom.xml
---------------------
- Project Object Model file: POM file - pom.xml
- Configuration file for your project - info about the dependencies
- Located in the root of your Maven Project

pom.xml
   |
   | -> project meta data (Project name, version etc, Output file type: JAR, WAR...
   | -> dependencies (List of projects we depend on Spring, Hibernate, etc...)
   | -> plugins (Additional custom tasks to run: generally JUnit test reports etc..)
   |

Maven Archetypes (Starter Project)
----------------
- Archetypes can be used to create a new Maven Project
- Contains template files for a given Maven Project (Java Project, Web Project, etc.)

// quickstart basic java project, in project folder run the below command
mvn archetype:generate -DgroupId=com.luv2code -DartifactId=mycoolapp -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false

// to build
mvn package

// to run the App
java -cp target/mycoolapp-1.0-SNAPSHOT.jar com.luv2code.App

 
SPRING REST
===============

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.9</version>
  <type>bundle</type>
</dependency>

Jackson package is used to convert JSON to POJO & POJO to JSON

 - Convert JSON to POJO -> call setter methods on POJO (Jackson will do the work)
 {
"id" : 4 --> call setId(4)
 }
 - Convert POJO to JSON -> call getter methods on POJO (Jackson will do the work)

JSON to JavaPOJO
----------------
import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();
// converting JSON to POJO
Student myStudent = mapper.readValue(new File("data/sample.json"), Student.class);
System.out.println("First Name : " + myStudent.getFirstName());
System.out.println("Last Name : " + myStudent.getLastName());

// converting POJO to Json
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.writeValue(new File("data/output.json"), myStudent);


When building Spring REST applications
---------------------------------------------------------------
- Spring will automatically handle Jackson Integration
- JSON data being passed to REST controller is converted to POJO (in the background automatically)
- Java object being returned from REST controller is converted to JSON (in the background automatically)

Ignore unknown properties from Json while converting to POJO
-------------------------------------------------------------
@JsonIgnoreProperties(ignoreUnknown=true) // this is from Jackson package
public class Student{
...
}

REST over HTTP
-------------------

API Design process
------------------
- Review API requirements
- Identify main resource/entity
- Use HTTP methods to assign action on resource

HttpMethod  -> Endpoint
---------------------------
POST -> /api/customers -> Create a new entity
GET -> /api/customers -> Read a list of entities
GET -> /api/customers/{customerId} -> Read a single entity
PUT -> /api/customers -> Update an existing entity
DELETE -> /api/customers/{customerId} -> Delete an existing entity

*Don't include actions in the endpoint like below
 x /api/customerList
 x /api/deleteCustomer
 x /api/addCustomer
 x /api/updateCustomer

- instead, use HTTP methods to assign actions


new annotation @RESTController extention of @Controller
- handles REST Requests and Responses

REST project developement process
-------------------------------------
1) Add Maven dependency for Spring MVC (spring-webmvc), Jackson project (jackson-databind) and Servlet support (javax.servlet-api)
2) Add code for All Java Config: @Configuration
DemoAppConfig.java

@Configuration
@EnableWebMvc
@ComponentScan(basePackages="com.luv2code.springdemo")
public class DemoAppconfig {
...
}

3) Add code for All Java Config: Servlet Initializer

- Spring MVC provides support for web app initialization
- Makes sure your code is automatically detected
- Your code is used to initialize the servlet container

Web App Initializer
. Extend 'AbstractAnnotationConfigDispatcherServletInitializer' abstract base class
. Override required methods
. Specify servlet mapping and location of your app config

4) Create Spring REST Service using @RestController

package com.naresh.test.rest;

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

import javax.annotation.PostConstruct;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.naresh.entity.Student;


@RestController
@RequestMapping("/test")
public class StudentRestController {

private List<Student> myStudents;

@PostConstruct
public void loadData() {
myStudents = new ArrayList<>();
myStudents.add(new Student("naresh", "kumar"));
myStudents.add(new Student("suresh", "rao4"));
myStudents.add(new Student("ramesh", "reddy"));
}

@GetMapping("/students")
public List<Student> getStudents(){
return myStudents;
}

@GetMapping("/students/{studentId}")
public Student getStudentById(@PathVariable int studentId){

if(studentId >= myStudents.size() || studentId < 0) {
throw new StudentNotFoundException("Student id not found: " + studentId);
}

return myStudents.get(studentId);
}

@ExceptionHandler
public ResponseEntity<StudentErrorResponse> handleException(StudentNotFoundException exc){
StudentErrorResponse error = new StudentErrorResponse();
error.setStatus(HttpStatus.NOT_FOUND.value());
error.setMessage(exc.getMessage());
error.setTimeStamp(System.currentTimeMillis());
return new ResponseEntity<StudentErrorResponse>(error, HttpStatus.NOT_FOUND);
}

@ExceptionHandler
public ResponseEntity<StudentErrorResponse> handleException(Exception exc){
StudentErrorResponse error = new StudentErrorResponse();
error.setStatus(HttpStatus.BAD_REQUEST.value());
error.setMessage(exc.getMessage());
error.setTimeStamp(System.currentTimeMillis());
return new ResponseEntity<StudentErrorResponse>(error, HttpStatus.BAD_REQUEST);
}
}
-------------
@PostConstruct - this annotation will be called only once when the bean is constructed (usefull for data initialization/fetching from db)

@PathVariable - to access the request parameter variable

@ExceptionHandler - to handle rest errors

@ControllerAdvice - perfect for global exception handling
- is similar to an interceptor/filter
- Pre-process requests to controllers
- Post-process responses to handle exceptions
- Perfect for global exception handling

@RequestBody - to access the request body as a POJO

----------------------------------------------------------------------------
package com.luv2code.springdemo.rest;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.luv2code.springdemo.entity.Customer;
import com.luv2code.springdemo.service.CustomerService;

@RestController
@RequestMapping("/api")
public class CustomerRestController {

// autowire the CustomerService (Dependency Injection)
@Autowired
private CustomerService customerService;

// add mapping for GET /customers
@GetMapping("/customers")
public List<Customer> getCustomers() {
return customerService.getCustomers();
}

// add mapping for GET /customers/{customerId}
@GetMapping("/customers/{customerId}")
public Customer getCustomer(@PathVariable int customerId) {
Customer theCustomer = customerService.getCustomer(customerId);
if (theCustomer == null) {
throw new CustomerNotFoundException("Customer id not found - " + customerId);
}
return theCustomer;
}

// add mapping for POST /customers  - add new customer
@PostMapping("/customers")
public Customer addCustomer(@RequestBody Customer theCustomer) {
// also just in case the pass an id in JSON ... set id to 0
// this is force a save of new item ... instead of update
theCustomer.setId(0);
customerService.saveCustomer(theCustomer);
return theCustomer;
}

// add mapping for PUT /customers - update existing customer
@PutMapping("/customers")
public Customer updateCustomer(@RequestBody Customer theCustomer) {
customerService.saveCustomer(theCustomer);
return theCustomer;
}

// add mapping for DELETE /customers/{customerId} - delete customer
@DeleteMapping("/customers/{customerId}")
public String deleteCustomer(@PathVariable int customerId) {
Customer tempCustomer = customerService.getCustomer(customerId);
// throw exception if null
if (tempCustomer == null) {
throw new CustomerNotFoundException("Customer id not found - " + customerId);
}
customerService.deleteCustomer(customerId);
return "Deleted customer id - " + customerId;
}
}
----------------------------------------------------------------------------

SPRING BOOT
==============

Problem with Spring
-----------------------
Building a Spring application is really HARD!!!
. Tons of configuration
. What Maven achetype to use?
. Which Maven dependencies do I need?
. How do I set up configuration (xml/java)?
. How do I install the server? (Tomcat, JBoss etc.,)

Spring Boot solution
------------------------
. Make it easier to get started with Spring development
. Minimize the amount of manual configuration
- Perform auto-configuration based on the props files and JAR classpath
. Help to resolve dependency conflicts
. Provide an embedded HTTP server so you can get started quickly
- Tomcat, Jetty, Udertow, ...

Spring Initializer
------------------
. Quickly create a starter Spring project
. Select your dependencies
. Creates a Maven/Gradle project
. Import the project into your IDE

Spring Boot directory structure
---------------------------------
Spring Boot uses Maven directory structure

src/main/java -> your java source code
src/main/resources -> Properties / config files used by your app
src/test/java -> Unit testing source code

Maven wrapper files
----------------------
- mvnw allows you to run a Maven project
. no need to have Maven installed or present on your path
. If correct version of Maven is Not found on your computer
. Automatically downloads correct version and runs Maven

- Two files are provided
. mvnw.cmd for MS windows
. mvnw.sh for Linux/Mac

@SpringBootApplication
--------------------------------
this annotation enables
- Auto configuration
- Component scanning
- Additional configuration

@SpringBootApplication is composed of the following annotations
- @EnableAutoConfiguration (Enables Spring Boot's auto-configuration support)
- @ComponentScan (Enables component scanning of current pacakage also recursively scans sub-packages)
- @Configuration (Able to register extra beans with @Bean or import other configuration classes)

Behind the scenes
- it will create application context and registers all beans
- Starts the embedded server Tomcat etc...

Explicitly listing other packages that are not under the Spring Boot Application package
---------------
@SpringBootApplication(scanBasePackages={"com.luv2code.springboot.demo.mycoolapp", "org.acme.iot.utils", "edu.cmu.wean"})

Application Properties
------------------------
By default Spring Boot will load properties from: application.properties file

This file will be automatically created by spring boot and placed it under the  src/main/resources folder, by default it will be empty.

You can add Spring Boot poperties in appliation.properties file.
ex: server.port=8585 (changing the default port 8080, if you give 0 spring boot will take any available port)
coach.name=Mickey Mouse
team.name=The Mouse Crew

You can inject the application.properties values in your controller as below;

@RestController
public class FunRestController {
@Value("${coach.name}")
private String coachName;

...
}

Benefits of the Spring Boot Starter Parent
--------------------------------------------------
. Default Maven configuration: java version, UTF-encoding etc.,
- you can override the configuration with properties
<parent>
...
<artifactId>spring-boot-starter-parent</artifactId>
...
</parent>
<properties>
<java.version>9</java.version>
</properties>

. Depenedency Management
- use version on parent only
- spring-boot-starter-* dependencies inherit version from parent

. Default configuration of Spring Boot plugins

Spring Boot Dev Tools
-------------------------
. spring-boot-devtools automatically restarts your application when code is updated.

. adding the dependency to your POM file

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>

Spring Boot Actuator
---------------------
. Exposes endpoints to monitor and manage your application, checks application health/stats

. You easily get DevOps functionality out-of-the-box
. Simply add the dependency to your POM file
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

. REST endpoints are automatically added to your application

Endpoints are prefixed with: /actuator (10+ endpoints available)
--------------------------------------------------------------------
Name Description
/health Health information about your application (up/down)

/info Information about your project
(udpate application.properties with your app info)
> src/main/resources/application.properties
info.app.name=My Super Cool App
info.app.description=A crazy app.
info.app.version=1.0.0

localhost:8080/acuator/info
----------------------------
{
"app": {
"name": "My Super Cool App",
...
}
}

/auditevents Audit events for your application
/beans List of all beans registered in the Spring application context
/mappings List of all @RequestMapping paths

- By defualt, only /health and /info are exposed, to expose all actuator endpoints over HTTP

> appliation.properties
//to show all the endpoints
management.endpoints.web.exposure.include=*
//to exclude the given endpoints
management.endpoints.web.exposure.exclude=health,info

Spring Boot Security
------------------------
//Automatically secure REST endpoints
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

- Once your restart the application, in the console it will give you the generated security password with the username as 'user'

- you can also override default user name and generated password
> application.properties
spring.security.user.name=scott
spring.security.user.password=tiger

Run spring boot from the command-line
--------------------------------------
two options for running the app
1 - Use java -jar (give the jar file)
> java -jar mycoolapp.jar

2 - User Spring Boot Maven plugin
mvnw spring-boot:run

Spring Boot Properties in application.properties
-------------------------------
Spring Boot can be configured in the application.properties file
- Server port, context path, actuator, security etc..
- Spring Boot has 1000+ properties, they are roughly grouped in the following categories
Core, Web, Security, Data
Actuator, Integration, DevTools, Testing


Application Architecture for creating Employees REST service
-----------------------------------------------------------------

Employee REST Controller <====> Employee Service <====> Employee DAO (Data Access Object) <====> DB
|
|-> DI (EntityManager) // to get the session object
|
v
Employee DAO <---> Session Factory <---> Data Source <---> DB

EmployeeDAO example
----------------------
package com.luv2code.springboot.cruddemo.dao;
import java.util.List;

import javax.persistence.EntityManager;

import org.hibernate.Session;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.luv2code.springboot.cruddemo.entity.Employee;

@Repository
public class EmployeeDAOHibernateImpl implements EmployeeDAO {

private EntityManager entityManager;

@Autowired
public EmployeeDAOHibernateImpl(EntityManager theEntityManager) {
entityManager = theEntityManager;
}

@Override
@Transactional
public List<Employee> findAll() {

Session currentSession = entityManager.unwrap(Session.class); // gets current session

Query<Employee> theQuery =
currentSession.createQuery("from Employee", Employee.class);

List<Employee> employees = theQuery.getResultList();

return employees;
}

}

JPA DAO Example
--------------------------

Action Native Hibernate method JPA method
------------------------------------------------------------------------------------------------------
Create/save new entity session.save(...) entityManager.persist(...)
Retrieve entity by id session.get(...) / load(...)    entityManager.find(...)
Retrive list of entitites session.createQuery(...)    entityManager.createQuery(...)
Save or update entity session.saveOrUpdate(...) entityManager.merge(...)
Delete entity session.delete(...) entityManager.remove(...)

this is the basic comparision, other options depending on context will change.


Spring DAO Example
----------------------------
using spring DAO you can get all the crud methods, you can directly use without writing any code

. Spring Data JPA provides the interface: JpaRepository
. Exposes methods (some by inheritance from parents)

- Advanced Features
. Extending and adding custom queries with JPQL
. Query Domain Specific Language (Query DSL)
. Defining custom methods (low-level coding)

// Spring DAO implementation
public interface EmployeeRepository extends JpaRepository<Employee, Integer> { // here we are mentioning the entity type and primary key type
// that's it no need to write any code (findAll, findById, save, deleteById ... methods will be avaialable automatically no need of any implementation for curd operations)
}

Spring Data REST
-------------------------
For Spring Data REST, you only need 3 items

1. Your entity: Employee
2. JpaRepository: EmployeeRepository
3. Maven POM dependency for: spring-boot-starter-data-rest


Create REST controllers automatically without any code, based on the given Entity, all the endpoints will be created automatically

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

Spring Data Rest - Configuration, Pagination and Sorting
------------------------------------------
##Path Configuration
//you can also set the base path for the REST end points in application.properties
spring.data.rest.base-path=/magic-api

- By default, Spring Data REST will create endpoints based on entity type
- Simple Pluralized form
. First character of Entity type is lowercase
. Then just adds an "s" to the entity.
ex: Employee -> '/employees'

// you can also specify plural name/path with an annotation
@RepositoryRestResource(path="members") // http://localhost:8080/members
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
}

##Pagination
- By default, Spring Data REST will retun the first 20 elements
. Page size = 20

- You can navigate to the different pages of data using query param

http://localhost:8080/employees?page=0
http://localhost:8080/employees?page=1

properties available
------------------
spring.data.rest.base-path // Base path used to expose repository resources
spring.data.rest.default-page-size // Default size of pages
spring.data.rest.max-page-size // Maximum size of pages
...

sorting
------------
sort by last name (ascending is default)
http://localhost:8080/employees?sort=lastName

sort by first name, desending
http://localhost:8080/employees?sort=firstName,desc

sort by last name, then first name, ascending
http://localhost:8080/employees?sort=lastName,firstName,asc