0% found this document useful (0 votes)
3 views

SpringJPAHibernate

Uploaded by

Suresh
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

SpringJPAHibernate

Uploaded by

Suresh
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 40

Introduction Scores

Objectives

 Explain the need and benefit of ORM


o ORM (Object-Relational Mapping), makes it easier to develop code that interacts with database, abstracts the
database system, transactionality
 ORM Pros and Cons - https://blog.bitsrc.io/what-is-an-orm-and-why-you-should-use-it-b2b6f75f5e2a
 What is ORM? - https://en.wikipedia.org/wiki/Object-relational_mapping

 Demonstrate the need and benefit of Spring Data JPA


o Evolution of ORM solutions, Hibernate XML Configuration, Hibernate Annotation Configuration, Spring Data JPA,
Hibernate benefits, open source, light weight, database independent query
 With H2 in memory database - https://www.mkyong.com/spring-boot/spring-boot-spring-data-jpa/
 With MySQL - https://www.mkyong.com/spring-boot/spring-boot-spring-data-jpa-mysql-example/
 XML Configuration Example -https://www.tutorialspoint.com/hibernate/hibernate_examples.htm
 Hibernate Configuration Example -https://www.tutorialspoint.com/hibernate/hibernate_annotations.htm

 Explain about core objects of hibernate framework


o Session Factory, Session, Transaction Factory, Transaction, Connection Provider
 Hibernate Architecture Reference - https://www.tutorialspoint.com/hibernate/hibernate_architecture.htm

 Explain ORM implementation with Hibernate XML Configuration and Annotation Configuration
o XML Configuration - persistence class, mapping xml, configuration xml, loading hibernate configuration xml file;
Annotation Configuration - persistence class, @Entity, @Table, @Id, @Column, hibernate configuration xml file
Loading hibernate configuration and interacting with database get the session factory, open session, begin
transaction, commit transaction, close session
 XML Configuration Example - https://www.tutorialspoint.com/hibernate/hibernate_examples.htm
 Hibernate Configuration Example - https://www.tutorialspoint.com/hibernate/hibernate_annotations.htm

 Explain the difference between Java Persistence API, Hibernate and Spring Data JPA
o JPA (Java Persistence API), JPA is a specification (JSR 338), JPA does not have implementation, Hibernate is
one of the implementation for JPA, Hibernate is a ORM tool, Spring Data JPA is an abstraction above Hibernate to
remove boiler plate code when persisting data using Hibernate.
 Difference between Spring Data JPA and Hibernate - https://dzone.com/articles/what-is-the-difference-
between-hibernate-and-sprin-1
 Intro to JPA - https://www.javaworld.com/article/3379043/what-is-jpa-introduction-to-the-java-
persistence-api.html

 Demonstrate implementation of DML using Spring Data JPA on a single database table
o Hibernate log configuration and ddl-auto configuration, JpaRepsitory.findById(), defining Query Methods,
JpaRespository.save(), JpaRepository.deleteById()
 Spring Data JPA Ref Repository methods -
https://docs.spring.io/spring-data/jpa/docs/2.2.0.RELEASE/reference/html/#repositories.core-concepts
 Query methods - https://docs.spring.io/spring-data/jpa/docs/2.2.0.RELEASE/reference/html/
#repositories.query-methods

Spring Data JPA - Quick Example


Software Pre-requisites
 MySQL Server 8.0
 MySQL Workbench 8
 Eclipse IDE for Enterprise Java Developers 2019-03 R
 Maven 3.6.2

Create a Eclipse Project using Spring Initializr

 Go to https://start.spring.io/
 Change Group as “com.cognizant”
 Change Artifact Id as “orm-learn”
 In Options > Description enter "Demo project for Spring Data JPA and Hibernate"
 Click on menu and select "Spring Boot DevTools", "Spring Data JPA" and "MySQL Driver"
 Click Generate and download the project as zip
 Extract the zip in root folder to Eclipse Workspace
 Import the project in Eclipse "File > Import > Maven > Existing Maven Projects > Click Browse
and select extracted folder > Finish"
 Create a new schema "ormlearn" in MySQL database. Execute the following commands to open
MySQL client and create schema.

> mysql -u root -p

mysql> create schema ormlearn;

 In orm-learn Eclipse project, open src/main/resources/application.properties and include the


below database and log configuration.

# Spring Framework and application log


logging.level.org.springframework=info
logging.level.com.cognizant=debug

# Hibernate logs for displaying executed SQL, input and output


logging.level.org.hibernate.SQL=trace
logging.level.org.hibernate.type.descriptor.sql=trace

# Log pattern
logging.pattern.console=%d{dd-MM-yy} %d{HH:mm:ss.SSS} %-20.20thread %5p %-25.25logger{25} %25M %4L %m%n

# Database configuration
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ormlearn
spring.datasource.username=root
spring.datasource.password=root

# Hibernate configuration
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
 Build the project using ‘mvn clean package -Dhttp.proxyHost=proxy.cognizant.com -
Dhttp.proxyPort=6050 -Dhttps.proxyHost=proxy.cognizant.com -Dhttps.proxyPort=6050 -
Dhttp.proxyUser=123456’ command in command line
 Include logs for verifying if main() method is called.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger LOGGER = LoggerFactory.getLogger(OrmLearnApplication.class);

public static void main(String[] args) {


SpringApplication.run(OrmLearnApplication.class, args);
LOGGER.info("Inside main");
}

 Execute the OrmLearnApplication and check in log if main method is called.

SME to walk through the following aspects related to the project created:

1. src/main/java - Folder with application code


2. src/main/resources - Folder for application configuration
3. src/test/java - Folder with code for testing the application
4. OrmLearnApplication.java - Walkthrough the main() method.
5. Purpose of @SpringBootApplication annotation
6. pom.xml
1. Walkthrough all the configuration defined in XML file
2. Open 'Dependency Hierarchy' and show the dependency tree.

Country table creation

 Create a new table country with columns for code and name. For sample, let us insert one
country with values 'IN' and 'India' in this table.

create table country(co_code varchar(2) primary key, co_name varchar(50));

 Insert couple of records into the table

insert into country values ('IN', 'India');


insert into country values ('US', 'United States of America');

Persistence Class - com.cognizant.orm-learn.model.Country

 Open Eclipse with orm-learn project


 Create new package com.cognizant.orm-learn.model
 Create Country.java, then generate getters, setters and toString() methods.
 Include @Entity and @Table at class level
 Include @Column annotations in each getter method specifying the column name.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="country")
public class Country {

@Id
@Column(name="code")
private String code;

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

// getters and setters

// toString()

Notes:

 @Entity is an indicator to Spring Data JPA that it is an entity class for the application
 @Table helps in defining the mapping database table
 @Id helps is defining the primary key
 @Column helps in defining the mapping table column

Repository Class - com.cognizant.orm-learn.CountryRepository

 Create new package com.cognizant.orm-learn.repository


 Create new interface named CountryRepository that extends JpaRepository<Country, String>
 Define @Repository annotation at class level

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.cognizant.ormlearn.model.Country;

@Repository
public interface CountryRepository extends JpaRepository<Country, String> {

Service Class - com.cognizant.orm-learn.service.CountryService

 Create new package com.cognizant.orm-learn.service


 Create new class CountryService
 Include @Service annotation at class level
 Autowire CountryRepository in CountryService
 Include new method getAllCountries() method that returns a list of countries.
 Include @Transactional annotation for this method
 In getAllCountries() method invoke countryRepository.findAll() method and return the result

Testing in OrmLearnApplication.java

 Include a static reference to CountryService in OrmLearnApplication class

private static CountryService countryService;

 Define a test method to get all countries from service.

private static void testGetAllCountries() {


LOGGER.info("Start");
List<Country> countries = countryService.getAllCountries();
LOGGER.debug("countries={}", countries);
LOGGER.info("End");
}

 Modify SpringApplication.run() invocation to set the application context and the CountryService
reference from the application context.

ApplicationContext context = SpringApplication.run(OrmLearnApplication.class, args);


countryService = context.getBean(CountryService.class);

testGetAllCountries();

 Execute main method to check if data from ormlearn database is retrieved.

Hibernate XML Config implementation walk through


SME to provide explanation on the sample Hibernate implementation available in the link below:
https://www.tutorialspoint.com/hibernate/hibernate_examples.htm

Explanation Topics

 Explain how object to relational database mapping done in hibernate xml configuration file
 Explain about following aspects of implementing the end to end operations in Hibernate:
o SessionFactory
o Session
o Transaction
o beginTransaction()
o commit()
o rollback()
o session.save()
o session.createQuery().list()
o session.get()
o session.delete()

Hibernate Annotation Config implementation walk through


SME to provide explanation on the sample Hibernate implementation available in the link below:
https://www.tutorialspoint.com/hibernate/hibernate_annotations.htm

Explanation Topics

 Explain how object to relational database mapping done in persistence class file Employee
 Explain about following aspects of implementing the end to end operations in Hibernate:
o @Entity
o @Table
o @Id
o @GeneratedValue
o @Column
o Hibernate Configuration (hibernate.cfg.xml)
 Dialect
 Driver
 Connection URL
 Username
 Password

Difference between JPA, Hibernate and Spring Data JPA


Java Persistence API (JPA)

 JSR 338 Specification for persisting, reading and managing data from Java objects
 Does not contain concrete implementation of the specification
 Hibernate is one of the implementation of JPA

Hibernate

 ORM Tool that implements JPA

Spring Data JPA

 Does not have JPA implementation, but reduces boiler plate code
 This is another level of abstraction over JPA implementation provider like Hibernate
 Manages transactions

Refer code snippets below on how the code compares between Hibernate and Spring Data JPA
Hibernate
/* Method to CREATE an employee in the database */
public Integer addEmployee(Employee employee){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;

try {
tx = session.beginTransaction();
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx != null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}

Spring Data JPA


EmployeeRespository.java
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {

EmployeeService.java
@Autowire
private EmployeeRepository employeeRepository;

@Transactional
public void addEmployee(Employee employee) {
employeeRepository.save(employee);
}

Reference Links:
https://dzone.com/articles/what-is-the-difference-between-hibernate-and-sprin-1
https://www.javaworld.com/article/3379043/what-is-jpa-introduction-to-the-java-persistence-api.html

Implement services for managing Country


An application requires for features to be implemented with regards to country. These features needs to
be supported by implementing them as service using Spring Data JPA.

 Find a country based on country code


 Add new country
 Update country
 Delete country
 Find list of countries matching a partial country name

Before starting the implementation of the above features, there are few configuration and data population
that needs to be incorporated. Please refer each topic below and implement the same.

Explanation for Hibernate table creation configuration

 Moreover the ddl-auto defines how hibernate behaves if a specific table or column is not present
in the database.
o create - drops existing tables data and structure, then creates new tables
o validate - check if the table and columns exist or not, throws an exception if a matching
table or column is not found
o update - if a table does not exists, it creates a new table; if a column does not exists, it
creates a new column
o create-drop - creates the table, once all operations are completed, the table is dropped

# Hibernate ddl auto (create, create-drop, update, validate)


spring.jpa.hibernate.ddl-auto=validate

Populate country table

 Delete all the records in Country table and then use the below script to create the actual list of all
countries in our world.

insert into country (co_code, co_name) values ("AF", "Afghanistan");


insert into country (co_code, co_name) values ("AL", "Albania");
insert into country (co_code, co_name) values ("DZ", "Algeria");
insert into country (co_code, co_name) values ("AS", "American Samoa");
insert into country (co_code, co_name) values ("AD", "Andorra");
insert into country (co_code, co_name) values ("AO", "Angola");
insert into country (co_code, co_name) values ("AI", "Anguilla");
insert into country (co_code, co_name) values ("AQ", "Antarctica");
insert into country (co_code, co_name) values ("AG", "Antigua and Barbuda");
insert into country (co_code, co_name) values ("AR", "Argentina");
insert into country (co_code, co_name) values ("AM", "Armenia");
insert into country (co_code, co_name) values ("AW", "Aruba");
insert into country (co_code, co_name) values ("AU", "Australia");
insert into country (co_code, co_name) values ("AT", "Austria");
insert into country (co_code, co_name) values ("AZ", "Azerbaijan");
insert into country (co_code, co_name) values ("BS", "Bahamas");
insert into country (co_code, co_name) values ("BH", "Bahrain");
insert into country (co_code, co_name) values ("BD", "Bangladesh");
insert into country (co_code, co_name) values ("BB", "Barbados");
insert into country (co_code, co_name) values ("BY", "Belarus");
insert into country (co_code, co_name) values ("BE", "Belgium");
insert into country (co_code, co_name) values ("BZ", "Belize");
insert into country (co_code, co_name) values ("BJ", "Benin");
insert into country (co_code, co_name) values ("BM", "Bermuda");
insert into country (co_code, co_name) values ("BT", "Bhutan");
insert into country (co_code, co_name) values ("BO", "Bolivia, Plurinational State of");
insert into country (co_code, co_name) values ("BQ", "Bonaire, Sint Eustatius and Saba");
insert into country (co_code, co_name) values ("BA", "Bosnia and Herzegovina");
insert into country (co_code, co_name) values ("BW", "Botswana");
insert into country (co_code, co_name) values ("BV", "Bouvet Island");
insert into country (co_code, co_name) values ("BR", "Brazil");
insert into country (co_code, co_name) values ("IO", "British Indian Ocean Territory");
insert into country (co_code, co_name) values ("BN", "Brunei Darussalam");
insert into country (co_code, co_name) values ("BG", "Bulgaria");
insert into country (co_code, co_name) values ("BF", "Burkina Faso");
insert into country (co_code, co_name) values ("BI", "Burundi");
insert into country (co_code, co_name) values ("KH", "Cambodia");
insert into country (co_code, co_name) values ("CM", "Cameroon");
insert into country (co_code, co_name) values ("CA", "Canada");
insert into country (co_code, co_name) values ("CV", "Cape Verde");
insert into country (co_code, co_name) values ("KY", "Cayman Islands");
insert into country (co_code, co_name) values ("CF", "Central African Republic");
insert into country (co_code, co_name) values ("TD", "Chad");
insert into country (co_code, co_name) values ("CL", "Chile");
insert into country (co_code, co_name) values ("CN", "China");
insert into country (co_code, co_name) values ("CX", "Christmas Island");
insert into country (co_code, co_name) values ("CC", "Cocos (Keeling) Islands");
insert into country (co_code, co_name) values ("CO", "Colombia");
insert into country (co_code, co_name) values ("KM", "Comoros");
insert into country (co_code, co_name) values ("CG", "Congo");
insert into country (co_code, co_name) values ("CD", "Congo, the Democratic Republic of the");
insert into country (co_code, co_name) values ("CK", "Cook Islands");
insert into country (co_code, co_name) values ("CR", "Costa Rica");
insert into country (co_code, co_name) values ("HR", "Croatia");
insert into country (co_code, co_name) values ("CU", "Cuba");
insert into country (co_code, co_name) values ("CW", "Curaçao");
insert into country (co_code, co_name) values ("CY", "Cyprus");
insert into country (co_code, co_name) values ("CZ", "Czech Republic");
insert into country (co_code, co_name) values ("CI", "Côte d'Ivoire");
insert into country (co_code, co_name) values ("DK", "Denmark");
insert into country (co_code, co_name) values ("DJ", "Djibouti");
insert into country (co_code, co_name) values ("DM", "Dominica");
insert into country (co_code, co_name) values ("DO", "Dominican Republic");
insert into country (co_code, co_name) values ("EC", "Ecuador");
insert into country (co_code, co_name) values ("EG", "Egypt");
insert into country (co_code, co_name) values ("SV", "El Salvador");
insert into country (co_code, co_name) values ("GQ", "Equatorial Guinea");
insert into country (co_code, co_name) values ("ER", "Eritrea");
insert into country (co_code, co_name) values ("EE", "Estonia");
insert into country (co_code, co_name) values ("ET", "Ethiopia");
insert into country (co_code, co_name) values ("FK", "Falkland Islands (Malvinas)");
insert into country (co_code, co_name) values ("FO", "Faroe Islands");
insert into country (co_code, co_name) values ("FJ", "Fiji");
insert into country (co_code, co_name) values ("FI", "Finland");
insert into country (co_code, co_name) values ("FR", "France");
insert into country (co_code, co_name) values ("GF", "French Guiana");
insert into country (co_code, co_name) values ("PF", "French Polynesia");
insert into country (co_code, co_name) values ("TF", "French Southern Territories");
insert into country (co_code, co_name) values ("GA", "Gabon");
insert into country (co_code, co_name) values ("GM", "Gambia");
insert into country (co_code, co_name) values ("GE", "Georgia");
insert into country (co_code, co_name) values ("DE", "Germany");
insert into country (co_code, co_name) values ("GH", "Ghana");
insert into country (co_code, co_name) values ("GI", "Gibraltar");
insert into country (co_code, co_name) values ("GR", "Greece");
insert into country (co_code, co_name) values ("GL", "Greenland");
insert into country (co_code, co_name) values ("GD", "Grenada");
insert into country (co_code, co_name) values ("GP", "Guadeloupe");
insert into country (co_code, co_name) values ("GU", "Guam");
insert into country (co_code, co_name) values ("GT", "Guatemala");
insert into country (co_code, co_name) values ("GG", "Guernsey");
insert into country (co_code, co_name) values ("GN", "Guinea");
insert into country (co_code, co_name) values ("GW", "Guinea-Bissau");
insert into country (co_code, co_name) values ("GY", "Guyana");
insert into country (co_code, co_name) values ("HT", "Haiti");
insert into country (co_code, co_name) values ("HM", "Heard Island and McDonald Islands");
insert into country (co_code, co_name) values ("VA", "Holy See (Vatican City State)");
insert into country (co_code, co_name) values ("HN", "Honduras");
insert into country (co_code, co_name) values ("HK", "Hong Kong");
insert into country (co_code, co_name) values ("HU", "Hungary");
insert into country (co_code, co_name) values ("IS", "Iceland");
insert into country (co_code, co_name) values ("IN", "India");
insert into country (co_code, co_name) values ("ID", "Indonesia");
insert into country (co_code, co_name) values ("IR", "Iran, Islamic Republic of");
insert into country (co_code, co_name) values ("IQ", "Iraq");
insert into country (co_code, co_name) values ("IE", "Ireland");
insert into country (co_code, co_name) values ("IM", "Isle of Man");
insert into country (co_code, co_name) values ("IL", "Israel");
insert into country (co_code, co_name) values ("IT", "Italy");
insert into country (co_code, co_name) values ("JM", "Jamaica");
insert into country (co_code, co_name) values ("JP", "Japan");
insert into country (co_code, co_name) values ("JE", "Jersey");
insert into country (co_code, co_name) values ("JO", "Jordan");
insert into country (co_code, co_name) values ("KZ", "Kazakhstan");
insert into country (co_code, co_name) values ("KE", "Kenya");
insert into country (co_code, co_name) values ("KI", "Kiribati");
insert into country (co_code, co_name) values ("KP", "Democratic People's Republic of Korea");
insert into country (co_code, co_name) values ("KR", "Republic of Korea");
insert into country (co_code, co_name) values ("KW", "Kuwait");
insert into country (co_code, co_name) values ("KG", "Kyrgyzstan");
insert into country (co_code, co_name) values ("LA", "Lao People's Democratic Republic");
insert into country (co_code, co_name) values ("LV", "Latvia");
insert into country (co_code, co_name) values ("LB", "Lebanon");
insert into country (co_code, co_name) values ("LS", "Lesotho");
insert into country (co_code, co_name) values ("LR", "Liberia");
insert into country (co_code, co_name) values ("LY", "Libya");
insert into country (co_code, co_name) values ("LI", "Liechtenstein");
insert into country (co_code, co_name) values ("LT", "Lithuania");
insert into country (co_code, co_name) values ("LU", "Luxembourg");
insert into country (co_code, co_name) values ("MO", "Macao");
insert into country (co_code, co_name) values ("MK", "Macedonia, the Former Yugoslav Republic of");
insert into country (co_code, co_name) values ("MG", "Madagascar");
insert into country (co_code, co_name) values ("MW", "Malawi");
insert into country (co_code, co_name) values ("MY", "Malaysia");
insert into country (co_code, co_name) values ("MV", "Maldives");
insert into country (co_code, co_name) values ("ML", "Mali");
insert into country (co_code, co_name) values ("MT", "Malta");
insert into country (co_code, co_name) values ("MH", "Marshall Islands");
insert into country (co_code, co_name) values ("MQ", "Martinique");
insert into country (co_code, co_name) values ("MR", "Mauritania");
insert into country (co_code, co_name) values ("MU", "Mauritius");
insert into country (co_code, co_name) values ("YT", "Mayotte");
insert into country (co_code, co_name) values ("MX", "Mexico");
insert into country (co_code, co_name) values ("FM", "Micronesia, Federated States of");
insert into country (co_code, co_name) values ("MD", "Moldova, Republic of");
insert into country (co_code, co_name) values ("MC", "Monaco");
insert into country (co_code, co_name) values ("MN", "Mongolia");
insert into country (co_code, co_name) values ("ME", "Montenegro");
insert into country (co_code, co_name) values ("MS", "Montserrat");
insert into country (co_code, co_name) values ("MA", "Morocco");
insert into country (co_code, co_name) values ("MZ", "Mozambique");
insert into country (co_code, co_name) values ("MM", "Myanmar");
insert into country (co_code, co_name) values ("NA", "Namibia");
insert into country (co_code, co_name) values ("NR", "Nauru");
insert into country (co_code, co_name) values ("NP", "Nepal");
insert into country (co_code, co_name) values ("NL", "Netherlands");
insert into country (co_code, co_name) values ("NC", "New Caledonia");
insert into country (co_code, co_name) values ("NZ", "New Zealand");
insert into country (co_code, co_name) values ("NI", "Nicaragua");
insert into country (co_code, co_name) values ("NE", "Niger");
insert into country (co_code, co_name) values ("NG", "Nigeria");
insert into country (co_code, co_name) values ("NU", "Niue");
insert into country (co_code, co_name) values ("NF", "Norfolk Island");
insert into country (co_code, co_name) values ("MP", "Northern Mariana Islands");
insert into country (co_code, co_name) values ("NO", "Norway");
insert into country (co_code, co_name) values ("OM", "Oman");
insert into country (co_code, co_name) values ("PK", "Pakistan");
insert into country (co_code, co_name) values ("PW", "Palau");
insert into country (co_code, co_name) values ("PS", "Palestine, State of");
insert into country (co_code, co_name) values ("PA", "Panama");
insert into country (co_code, co_name) values ("PG", "Papua New Guinea");
insert into country (co_code, co_name) values ("PY", "Paraguay");
insert into country (co_code, co_name) values ("PE", "Peru");
insert into country (co_code, co_name) values ("PH", "Philippines");
insert into country (co_code, co_name) values ("PN", "Pitcairn");
insert into country (co_code, co_name) values ("PL", "Poland");
insert into country (co_code, co_name) values ("PT", "Portugal");
insert into country (co_code, co_name) values ("PR", "Puerto Rico");
insert into country (co_code, co_name) values ("QA", "Qatar");
insert into country (co_code, co_name) values ("RO", "Romania");
insert into country (co_code, co_name) values ("RU", "Russian Federation");
insert into country (co_code, co_name) values ("RW", "Rwanda");
insert into country (co_code, co_name) values ("RE", "Réunion");
insert into country (co_code, co_name) values ("BL", "Saint Barthélemy");
insert into country (co_code, co_name) values ("SH", "Saint Helena, Ascension and Tristan da Cunha");
insert into country (co_code, co_name) values ("KN", "Saint Kitts and Nevis");
insert into country (co_code, co_name) values ("LC", "Saint Lucia");
insert into country (co_code, co_name) values ("MF", "Saint Martin (French part)");
insert into country (co_code, co_name) values ("PM", "Saint Pierre and Miquelon");
insert into country (co_code, co_name) values ("VC", "Saint Vincent and the Grenadines");
insert into country (co_code, co_name) values ("WS", "Samoa");
insert into country (co_code, co_name) values ("SM", "San Marino");
insert into country (co_code, co_name) values ("ST", "Sao Tome and Principe");
insert into country (co_code, co_name) values ("SA", "Saudi Arabia");
insert into country (co_code, co_name) values ("SN", "Senegal");
insert into country (co_code, co_name) values ("RS", "Serbia");
insert into country (co_code, co_name) values ("SC", "Seychelles");
insert into country (co_code, co_name) values ("SL", "Sierra Leone");
insert into country (co_code, co_name) values ("SG", "Singapore");
insert into country (co_code, co_name) values ("SX", "Sint Maarten (Dutch part)");
insert into country (co_code, co_name) values ("SK", "Slovakia");
insert into country (co_code, co_name) values ("SI", "Slovenia");
insert into country (co_code, co_name) values ("SB", "Solomon Islands");
insert into country (co_code, co_name) values ("SO", "Somalia");
insert into country (co_code, co_name) values ("ZA", "South Africa");
insert into country (co_code, co_name) values ("GS", "South Georgia and the South Sandwich Islands");
insert into country (co_code, co_name) values ("SS", "South Sudan");
insert into country (co_code, co_name) values ("ES", "Spain");
insert into country (co_code, co_name) values ("LK", "Sri Lanka");
insert into country (co_code, co_name) values ("SD", "Sudan");
insert into country (co_code, co_name) values ("SR", "Suriname");
insert into country (co_code, co_name) values ("SJ", "Svalbard and Jan Mayen");
insert into country (co_code, co_name) values ("SZ", "Swaziland");
insert into country (co_code, co_name) values ("SE", "Sweden");
insert into country (co_code, co_name) values ("CH", "Switzerland");
insert into country (co_code, co_name) values ("SY", "Syrian Arab Republic");
insert into country (co_code, co_name) values ("TW", "Taiwan, Province of China");
insert into country (co_code, co_name) values ("TJ", "Tajikistan");
insert into country (co_code, co_name) values ("TZ", "Tanzania, United Republic of");
insert into country (co_code, co_name) values ("TH", "Thailand");
insert into country (co_code, co_name) values ("TL", "Timor-Leste");
insert into country (co_code, co_name) values ("TG", "Togo");
insert into country (co_code, co_name) values ("TK", "Tokelau");
insert into country (co_code, co_name) values ("TO", "Tonga");
insert into country (co_code, co_name) values ("TT", "Trinidad and Tobago");
insert into country (co_code, co_name) values ("TN", "Tunisia");
insert into country (co_code, co_name) values ("TR", "Turkey");
insert into country (co_code, co_name) values ("TM", "Turkmenistan");
insert into country (co_code, co_name) values ("TC", "Turks and Caicos Islands");
insert into country (co_code, co_name) values ("TV", "Tuvalu");
insert into country (co_code, co_name) values ("UG", "Uganda");
insert into country (co_code, co_name) values ("UA", "Ukraine");
insert into country (co_code, co_name) values ("AE", "United Arab Emirates");
insert into country (co_code, co_name) values ("GB", "United Kingdom");
insert into country (co_code, co_name) values ("US", "United States");
insert into country (co_code, co_name) values ("UM", "United States Minor Outlying Islands");
insert into country (co_code, co_name) values ("UY", "Uruguay");
insert into country (co_code, co_name) values ("UZ", "Uzbekistan");
insert into country (co_code, co_name) values ("VU", "Vanuatu");
insert into country (co_code, co_name) values ("VE", "Venezuela, Bolivarian Republic of");
insert into country (co_code, co_name) values ("VN", "Viet Nam");
insert into country (co_code, co_name) values ("VG", "Virgin Islands, British");
insert into country (co_code, co_name) values ("VI", "Virgin Islands, U.S.");
insert into country (co_code, co_name) values ("WF", "Wallis and Futuna");
insert into country (co_code, co_name) values ("EH", "Western Sahara");
insert into country (co_code, co_name) values ("YE", "Yemen");
insert into country (co_code, co_name) values ("ZM", "Zambia");
insert into country (co_code, co_name) values ("ZW", "Zimbabwe");
insert into country (co_code, co_name) values ("AX", "Åland Islands");

Refer subsequent hands on exercises to implement the features related to country.

Find a country based on country code

 Create new exception class CountryNotFoundException in com.cognizant.spring-


learn.service.exception
 Create new method findCountryByCode() in CountryService with @Transactional annotation
 In findCountryByCode() method, perform the following steps:
o Method signature

@Transactional
public Country findCountryByCode(String countryCode) throws CountryNotFoundException

 Get the country based on findById() built in method


Optional<Country> result = countryRepository.findById(countryCode);

 From the result, check if a country is found. If not found, throw CountryNotFoundException

if (!result.isPresent())

 Use get() method to return the country fetched.

Country country = result.get();

 Include new test method in OrmLearnApplication to find a country based on country code and
compare the country name to check if it is valid.

private static void getAllCountriesTest() {


LOGGER.info("Start");
Country country = countryService.findCountryByCode("IN");
LOGGER.debug("Country:{}", country);
LOGGER.info("End");
}

 Invoke the above method in main() method and test it.

NOTE: SME to explain the importance of @Transactional annotation. Spring takes care of creating the
Hibernate session and manages the transactionality when executing the service method.

Add a new country

 Create new method in CountryService.

@Transactional
public void addCountry(Country country)

 Invoke save() method of repository to get the country added.

countryRepository.save(country)

 Include new testAddCountry() method in OrmLearnApplication. Perform steps below:


o Create new instance of country with a new code and name
o Call countryService.addCountry() passing the country created in the previous step.
o Invoke countryService.findCountryByCode() passing the same code used when adding a
new country
o Check in the database if the country is added
Update a country based on code

 Create a new method updateCountry() in CountryService with parameters code and name.
Annotate this method with @Transactional. Implement following steps in this method.
o Get the reference of the country using findById() method in repository
o In the country reference obtained, update the name of country using setter method
o Call countryRepository.save() method to update the name
 Include new test method in OrmLearnApplication, which invokes updateCountry() method in
CountryService passing a country's code and different name for the country.
 Check in database table if name is modified.

Delete a country based on code

 Create new method deleteCountry() in CountryService. Annotate this method with


@Transactional.
 In deleteCountry() method call deleteById() method of repository.
 Include new test method in OrmLearnApplication with following steps
o Call the delete method based on the country code during the add country hands on
 Check in database if the country is deleted

Query Methods and O/R Mapping Scores


Objectives

 Demonstrate implementation of Query Methods feature of Spring Data JPA


o Query Methods - Search by containing text, sorting, filter with starting text, fetch between dates, greater than or
lesser than, top
 Query methods - https://docs.spring.io/spring-data/jpa/docs/2.2.0.RELEASE/reference/html/#jpa.query-
methods.query-creation

 Demonstrate implementation of O/R Mapping


o @ManyToOne, @JoinColumn, @OneToMany, FetchType.EAGER, FetchType.LAZY, @ManyToMany,
@JoinTable, mappedBy
 Relationships reference - https://www.baeldung.com/spring-data-rest-relationships

Write queries on country table using Query Methods


Following are the list of queries that is required for an application. Implement these queries using Query
Methods feature of Spring Data JPA. Click here for reference. Include appropriate methods in
OrmLearnApplication and test the same.

 An application has a search text box for searching by country. When typing characters on the text
box, a list of all the matching countries should be displayed. For example, if 'ou' is entered in the
search box the following countries should be displayed. Write a Query Method to achieve this
feature. Implement this method in CountryRepository.

BV Bouvet Island
DJ Djibouti
GP Guadeloupe
GS South Georgia and the South Sandwich Islands
LU Luxembourg
SS South Sudan
TF French Southern Territories
UM United States Minor Outlying Islands
ZA South Africa

 Enhance the above method to return the countries in ascending order. Modify the query method
name defined in the previous problem to achieve this.

BV Bouvet Island
DJ Djibouti
TF French Southern Territories
GP Guadeloupe
LU Luxembourg
ZA South Africa
GS South Georgia and the South Sandwich Islands
SS South Sudan
UM United States Minor Outlying Islands

 To select a country an alphabet index is displayed in a web page, when the user clicks on the
alphabet, all the countries starting that alphabet needs to be displayed. For example if the
alphabet choose is 'Z', then the following countries should be displayed. Write a query method to
get this feature incorporated.

ZM Zambia
ZW Zimbabwe

Test Cases

Write queries on stock table using Query Methods


With one year stock data of Facebook, Google and Netflix, we need to implement Spring Data JPA Query
Methods for the following scenarios:

Clone sample data folder from git:


Sample data for implementing this hands on is available in the git server. Follow steps below to download
the same.

 Identify a convenient folder for saving the same data files


 In Windows File Explorer go to the folder identified
 Right click on the empty space of this folder and select 'Git Bash'
 Execute the following command which downloads all necessary files:
git clone https://code.cognizant.com/genc-fse-java/spring-data-jpa-files.git

Setup stock data

 Create a new table for storing stock details.

CREATE TABLE IF NOT EXISTS `ormlearn`.`stock` (


`st_id` INT NOT NULL AUTO_INCREMENT,
`st_code` varchar(10),
`st_date` date,
`st_open` numeric(10,2),
`st_close` numeric(10,2),
`st_volume` numeric,
PRIMARY KEY (`st_id`)
);

 The file stock-data.csv in spring-data-jpa-files folder contains the stock data of Facebook, Google
and Netflix from 18 Oct 2018 to 17 Oct 2019. This is public data downloaded from
finance.yahoo.com.
 Open stock-data.csv file in Excel
 Include the following formula in F2 cell, this will display the insert script in F2 cell.

=CONCATENATE("insert into stock (st_code, st_date, st_open, st_close, st_volume) values (""", B2, """,
""", YEAR(A2), "-", MONTH(A2), "-", DAY(A2), """, ", C2, ", ", D2, ", ", E2, ");")

 Drag the formula for all the rows of data in F column.


 Copy all data in F column and paste it in Notepad or Notepad++ and save it as a file
named stock-data.sql. Execute this script in mysql command line client which populates data into
ormlearn.stock table. Following command assumes that the git project is available in D:.

mysql>source D:\spring-data-jpa-files\stock-data.sql

 Create new class Stock in orm-learn project and define the required mapping annotations.
 Create StockRepository class to write the Query Methods
 Create methods in OrmLearnApplication to test by autowiring StockRepository directly.

Query Methods required for the following scenarios

 Get all stock details of Facebook in the month of September 2019. Expected data result of Query
Method below.

+---------+------------+---------+----------+-----------+
| st_code | st_date | st_open | st_close | st_volume |
+---------+------------+---------+----------+-----------+
| FB | 2019-09-03 | 184.00 | 182.39 | 9779400 |
| FB | 2019-09-04 | 184.65 | 187.14 | 11308000 |
| FB | 2019-09-05 | 188.53 | 190.90 | 13876700 |
| FB | 2019-09-06 | 190.21 | 187.49 | 15226800 |
| FB | 2019-09-09 | 187.73 | 188.76 | 14722400 |
| FB | 2019-09-10 | 187.44 | 186.17 | 15455900 |
| FB | 2019-09-11 | 186.46 | 188.49 | 11761700 |
| FB | 2019-09-12 | 189.86 | 187.47 | 11419800 |
| FB | 2019-09-13 | 187.33 | 187.19 | 11441100 |
| FB | 2019-09-16 | 186.93 | 186.22 | 8444800 |
| FB | 2019-09-17 | 186.66 | 188.08 | 9671100 |
| FB | 2019-09-18 | 188.09 | 188.14 | 9681900 |
| FB | 2019-09-19 | 188.66 | 190.14 | 10392700 |
| FB | 2019-09-20 | 190.66 | 189.93 | 19934200 |
| FB | 2019-09-23 | 189.34 | 186.82 | 13327600 |
| FB | 2019-09-24 | 187.98 | 181.28 | 18546600 |
| FB | 2019-09-25 | 181.45 | 182.80 | 18068300 |
| FB | 2019-09-26 | 181.33 | 180.11 | 16083300 |
| FB | 2019-09-27 | 180.49 | 177.10 | 14656200 |
+---------+------------+---------+----------+-----------+

 Get all google stock details where the stock price was greater than 1250

+---------+------------+---------+----------+-----------+
| st_code | st_date | st_open | st_close | st_volume |
+---------+------------+---------+----------+-----------+
| GOOGL | 2019-04-22 | 1236.67 | 1253.76 | 954200 |
| GOOGL | 2019-04-23 | 1256.64 | 1270.59 | 1593400 |
| GOOGL | 2019-04-24 | 1270.59 | 1260.05 | 1169800 |
| GOOGL | 2019-04-25 | 1270.30 | 1267.34 | 1567200 |
| GOOGL | 2019-04-26 | 1273.38 | 1277.42 | 1361400 |
| GOOGL | 2019-04-29 | 1280.51 | 1296.20 | 3618400 |
| GOOGL | 2019-10-17 | 1251.40 | 1252.80 | 1047900 |
+---------+------------+---------+----------+-----------+

 Find the top 3 dates which had highest volume of transactions

+---------+------------+---------+----------+-----------+
| st_code | st_date | st_open | st_close | st_volume |
+---------+------------+---------+----------+-----------+
| FB | 2019-01-31 | 165.60 | 166.69 | 77233600 |
| FB | 2018-10-31 | 155.00 | 151.79 | 60101300 |
| FB | 2018-12-19 | 141.21 | 133.24 | 57404900 |
+---------+------------+---------+----------+-----------+

 Identify three dates when Netflix stocks were the lowest

+---------+------------+---------+----------+-----------+
| st_code | st_date | st_open | st_close | st_volume |
+---------+------------+---------+----------+-----------+
| NFLX | 2018-12-24 | 242.00 | 233.88 | 9547600 |
| NFLX | 2018-12-21 | 263.83 | 246.39 | 21397600 |
| NFLX | 2018-12-26 | 233.92 | 253.67 | 14402700 |
+---------+------------+---------+----------+-----------+

Create payroll tables and bean mapping


To demonstrate one to many, many to one and many to many relationships in Hibernate, a schema with
entities employee, department and skill will be used. In this hands on we will setup the tables and data,
which forms the basis for learning the mappings in Hibernate.

Schema Structure

Follow steps below to create necessary tables:

 Open mysql client in command line


 Use the source command to execute the payroll.sql script file available in spring-data-jpa-files
folder. The following command assumes that spring-data-jpa-files folder is in D:.

mysql> source D:\spring-data-jpa-files\stock-data.sql

Define bean mapping

 Open orm-learn project in Eclipse


 Create model classes Employee, Department and Skill in com.cognizant.orm-learn.model
package
 Define each model should have @Entity and @Table annotations.
 Each id field should be have @Id annotation and @GeneratedValue(strategy =
GenerationType.IDENTITY) annotation. @GeneratedValue annotation ensures auto increment of
id creation.
 Define @Column against each field.
 Define getters, setters and toString() methods
 Employee
o private int id;
o private String name;
o private double salary;
o private boolean permanent;
o private Date dateOfBirth;
 Department
o private int id;
o private String name;
 Skill
o private int id;
o private String name;
 Create appropriate repository interfaces EmployeeRepository, DepartmentRepository and
SkillRepository in repository package

Implement many to one relationship between Employee and Department


Follow steps below to defined many to one relationship and perform persistence operations:

Preparation of Service Classes

 Create EmployeeService, DepartmentService and SkillService defined with annotation @Service.


In each of this class autowire respective repository.
 In each of the service class implement two methods one is to get the entity based on id and the
other one is to save the entity. Sample code below provided for EmployeeService, in similar
fashion include the methods for DepartmentService and SkillService.
 EmployeeService - get() method

@Transactional
public Employee get(int id) {
LOGGER.info("Start");
return employeeRepository.findById(id).get();
}

 EmployeeService - save() method

@Transactional
public void save(Employee employee) {
LOGGER.info("Start");
employeeRepository.save(employee);
LOGGER.info("End");
}

 Include static references of EmployeeService, DepartmentService and SkillService in


OrmLearnApplication.
 Assign employeeService, departmentService and skillService from the context in
OrmLearnApplication main() method.

Implementation of @ManyToOne mapping

 Define department in Employee bean with @ManyToOne and @JoinTable annotation. This
defines the relationship between the entities.
@ManyToOne
@JoinColumn(name = "em_dp_id")
private Department department;

 Include setters and getters for instance variable department.

Getting Employee along with Department

 Create new method testGetEmployee() in OrmLearnApplication


 Implement below code in the method.

private static void testGetEmployee() {


LOGGER.info("Start");
Employee employee = employeeService.get(1);
LOGGER.debug("Employee:{}", employee);
LOGGER.debug("Department:{}", employee.getDepartment());
LOGGER.info("End");
}

 The above implementation gets the employee with id 1 and displays the employee details and
department details.
 Include testGetEmployee() method in main and comment the other test method calls.
 Execute the main method and observe the following:
o In the logs check the lines where the query is generated.
o Since the relationship is defined, hibernate fetches department data as well. The query
should look something like the below. Observe the department table join in this query.
The query is formatted for better readability.

select employee0_.em_id as em_id1_2_0_, employee0_.em_date_of_birth as em_date_2_2_0_,


employee0_.em_dp_id as em_dp_id6_2_0_,
employee0_.em_name as em_name3_2_0_, employee0_.em_permanent as em_perma4_2_0_,
employee0_.em_salary as em_salar5_2_0_,
department1_.dp_id as dp_id1_1_1_, department1_.dp_name as dp_name2_1_1_
from employee employee0_ left outer join department department1_
on employee0_.em_dp_id=department1_.dp_id
where employee0_.em_id=?

NOTE: SME to explain the learners about Eager Fetch and Lazy Fetch. As per JPA specification by
default, Eager Fetch is applied For ManyToOne and OneToOne relationships. Hence department details
as well is joined and fetched by Hibernate.

Add Employee

 Create new method testAddEmployee() in OrmLearnApplication and implement the following


steps
o Create a new instance of Employee
o Set the values for the employee using setter method
o Get a department based on department id 1 using departmentService
o Set the department in employee based on the department obtained in the previous step
o Invoke employeeService.save() passing the employee object created
o Log employee object reference in debug mode
 Include testAddEmployee() invocation in main() method and comment other test methods
 Invoke the main method and check the following:
o Log should contain select query to get the department and insert statement to add
employee
o Observe that the employee log after save contains the id. Hibernate inserts the records,
gets the id and set the id instance variable of employee
o Check in database if new employee data is inserted in employee table

Update Employee

 Create new method testUpdateEmployee() in OrmLearnApplication and implement the following


steps
o Get an employee instance based on employee id using employeeService.get() method
o Get a department based on department id using departmentService. Use a different
department id from the one that is fetched.
o Set the department in employee based on the department obtained in the previous step
o Invoke employeeService.save() passing the employee object created
o Log employee object reference in debug mode
 Include testUpdateEmployee() invocation in main() method and comment other test methods
 Invoke the main method and check the following:
o Log should contain select query to get the department and employee. There should be
update statement that updates employee table
o Check in database if department id is modified.

Implement one to many relationship between Employee and Department


Department.java

 Include new instance variable for set of employees and define the OneToMay annotation

@OneToMany(mappedBy = "department")
private Set<Employee> employeeList;

 Include setter and getter for employeeList

OrmLearnApplication.java

 Include new method testGetDepartment()


 In this method, get a department using departmentService.get() passing the id. Select an
department id that has more than one employee.
 Log the returned department and department.getEmployeeList()
 Include testGetDepartment() method invocation in main method and comment the other test
methods.
 Execute the main() method which will fail with LazyInitializationException. This is because the
default fetch type for OneToMany relationship is LAZY, hibernate fetches only department details
and does not get the employee details.
 In order to get the employee list as well, modify the annotation to include the fetch type as
EAGER. Make this change in employeeList annotation definition of Department class.
@OneToMany(mappedBy = "department", fetch = FetchType.EAGER)
private Set<Employee> employeeList;

 After this change try executing the main() method, which will fetch both department and employee

Implement many to many relationship between Employee and Skill


Many to Many mapping defintion

 Include set of skill list in Employee.java with appropriate getter and setter
 Include set of employee list in Skill.java with appropriate getter and setter
 Include ManyToMany definition in Employee.java as specified below:

@ManyToMany
@JoinTable(name = "employee_skill",
joinColumns = @JoinColumn(name = "es_em_id"),
inverseJoinColumns = @JoinColumn(name = "es_sk_id"))
private Set<Skill> skillList;

 Include ManyToMany defintion in Skill.java as specified below:

@ManyToMany(mappedBy = "skillList")
private Set<Employee> employeeList;

Fetching Employee along with Skills

 In testGetEmployee() method of OrmLearnApplication.java include a new line to log employee


skill details after the department log.

LOGGER.debug("Skills:{}", employee.getSkillList());

 Execute the main method which will fail with LazyInitializationException


 Include fetch type as eager in @ManyToMany annotation of Employee.java, which will fetch the
skill details as well.

Add Skill to Employee

 Include a new method testAddSkillToEmployee() in OrmLearnApplication.java


 Implement the following steps in this method:
o Identify an employee id and skill id for which a relationship does not exists
o Get employee based on employee id calling employeeService.get() method
o Get skill based on skill id calling skillService.get() method
o Get the skill list from employee and add the skill obtained in the previous step to the skill
list
o Call save method in employeeService passing the employee reference
 Invoke testAddSkillToEmployee() in main method and comment other test methods
 Execute the main method and check employee_skill table to verify if the skill is added to the
employee.
Hibernate Query Language (HQL) Scores
Objectives

 Demonstrate writing Hibernate Query Language and Native Query


o HQL stands for Hibernate Query Language, JPQL stands for Java Persistence Query Language, Compare HQL
and JPQL, @Query annotation, HQL fetch keyword, aggregate functions in HQL, Native Query, nativeQuery
attribute
 Reference - https://docs.jboss.org/hibernate/orm/4.3/devguide/en-US/html/ch11.html
 Features of JPA Query - https://www.baeldung.com/spring-data-jpa-query

 Explain the need and benefit of Criteria Query


o Scenarios where Criteria Query helps, CriteriaBuilder, Criteria Query, Root, TypedQuery
 Reference - https://docs.oracle.com/javaee/6/tutorial/doc/gjrij.html

Introduction to HQL and JPQL

 HQL stands for Hibernate Query Language


 JPQL stands for Java Persistence Query Language
 Both HQL and JPQL are object focused query language similar to SQL
 JPQL is a subset of HQL
 All JPQL queries are valid HQL query, but the reverse is not true
 Both HQL and JPQL allows SELECT, UPDATE and DELETE
 HQL additionally allows INSERT statement

Reference - https://docs.jboss.org/hibernate/orm/4.3/devguide/en-US/html/ch11.html

Get all permanent employees using HQL


Using HQL get all permanent employees. When retrieving the employee details it should also retrieve
respective department and skill list as well.

HQL Solution

 Include a new method definition in EmployeeRepository with @Query annotation

@Query(value="SELECT e FROM Employee e WHERE e.permanent = 1")


List<Employee> getAllPermanentEmployees();

// NOTE: HQL looks like SQL, instead of table, Java classes and it's
// instance variables are addressed here

 Include appropriate service method


 Include a new test method and Invoke the service method in OrmLearnApplication.java. Refer
test method below that logs all employee details and each employee's skill details.

public static void testGetAllPermanentEmployees() {


LOGGER.info("Start");
List<Employee> employees = employeeService.getAllPermanentEmployees();
LOGGER.debug("Permanent Employees:{}", employees);
employees.forEach(e -> LOGGER.debug("Skills:{}", e.getSkillList()));
LOGGER.info("End");
}

 Check the list of SQL queries executed in the log file. Following queries would have got executed.

select employee0_.em_id as em_id1_2_0_, department1_.dp_id as dp_id1_1_1_, skill3_.sk_id as sk_id1_4_2_,


employee0_.em_date_of_birth as em_date_2_2_0_,
employee0_.em_dp_id as em_dp_id6_2_0_, employee0_.em_name as em_name3_2_0_,
employee0_.em_permanent as em_perma4_2_0_,
employee0_.em_salary as em_salar5_2_0_, department1_.dp_name as dp_name2_1_1_, skill3_.sk_name as
sk_name2_4_2_,
skilllist2_.es_em_id as es_em_id1_3_0__, skilllist2_.es_sk_id as es_sk_id2_3_0__
from employee employee0_ left outer join department department1_ on
employee0_.em_dp_id=department1_.dp_id left outer join employee_skill skilllist2_ on
employee0_.em_id=skilllist2_.es_em_id left outer join skill skill3_ on
skilllist2_.es_sk_id=skill3_.sk_id
where employee0_.em_permanent=1
select employeeli0_.em_dp_id as em_dp_id6_2_0_, employeeli0_.em_id as em_id1_2_0_, employeeli0_.em_id as
em_id1_2_1_,
employeeli0_.em_date_of_birth as em_date_2_2_1_, employeeli0_.em_dp_id as em_dp_id6_2_1_,
employeeli0_.em_name as em_name3_2_1_, employeeli0_.em_permanent as em_perma4_2_1_,
employeeli0_.em_salary as em_salar5_2_1_
from employee employeeli0_ where employeeli0_.em_dp_id=3
select employeeli0_.em_dp_id as em_dp_id6_2_0_, employeeli0_.em_id as em_id1_2_0_, employeeli0_.em_id as
em_id1_2_1_,
employeeli0_.em_date_of_birth as em_date_2_2_1_, employeeli0_.em_dp_id as em_dp_id6_2_1_,
employeeli0_.em_name as em_name3_2_1_, employeeli0_.em_permanent as em_perma4_2_1_,
employeeli0_.em_salary as em_salar5_2_1_
from employee employeeli0_ where employeeli0_.em_dp_id=2
select skilllist0_.es_em_id as es_em_id1_3_0_, skilllist0_.es_sk_id as es_sk_id2_3_0_, skill1_.sk_id as
sk_id1_4_1_, skill1_.sk_name as sk_name2_4_1_
from employee_skill skilllist0_ inner join skill skill1_ on skilllist0_.es_sk_id=skill1_.sk_id
where skilllist0_.es_em_id=2

Optimizing HQL Solution by removing the EAGER fetch configuration

 An optimal solution should not execute multiple queries, we have defined unnecessary fetch
configuration in
 Eager fetch configuration is defined for employeeList in Department.java and skillList of
Employee.java
 Remove these two eager fetch configurations and check the logs. The following queries would
have got executed. It would have failed when getting the skill list. Since we have remove the
eager fetch skill is not retrieved.

select employee0_.em_id as em_id1_2_, employee0_.em_date_of_birth as em_date_2_2_, employee0_.em_dp_id as


em_dp_id6_2_, employee0_.em_name as em_name3_2_,
employee0_.em_permanent as em_perma4_2_, employee0_.em_salary as em_salar5_2_
from employee employee0_
where employee0_.em_permanent=1
select department0_.dp_id as dp_id1_1_0_, department0_.dp_name as dp_name2_1_0_
from department department0_
where department0_.dp_id=2
select department0_.dp_id as dp_id1_1_0_, department0_.dp_name as dp_name2_1_0_
from department department0_
where department0_.dp_id=3

 There are two issues in this approach:


o We did not get the skill details
o Still the query is not optimal as we have three queries executed

Optimizing HQL solution by using 'fetch'

 Change the query in EmployeeRepository.java as specified below:

@Query(value="SELECT e FROM Employee e left join e.department d left join e.skillList WHERE e.permanent =
1")

 The above query still fails to get skill details. Include fetch after each join. Wherever data is
required we can include fetch, which will populate the respective data. Change the query as
specified below:

@Query(value="SELECT e FROM Employee e left join fetch e.department d left join fetch e.skillList WHERE
e.permanent = 1")

 Following the single query generated for the above HQL:

select employee0_.em_id as em_id1_2_0_, department1_.dp_id as dp_id1_1_1_, skill3_.sk_id as sk_id1_4_2_,


employee0_.em_date_of_birth as em_date_2_2_0_, employee0_.em_dp_id as em_dp_id6_2_0_,
employee0_.em_name as em_name3_2_0_,
employee0_.em_permanent as em_perma4_2_0_, employee0_.em_salary as em_salar5_2_0_,
department1_.dp_name as dp_name2_1_1_,
skill3_.sk_name as sk_name2_4_2_, skilllist2_.es_em_id as es_em_id1_3_0__, skilllist2_.es_sk_id as
es_sk_id2_3_0__
from employee employee0_ left outer join department department1_ on
employee0_.em_dp_id=department1_.dp_id left outer join employee_skill skilllist2_ on
employee0_.em_id=skilllist2_.es_em_id left outer join skill skill3_ on
skilllist2_.es_sk_id=skill3_.sk_id
where employee0_.em_permanent=1

IMPORTANT TAKEAWAY: Join keyword links the table, but does not populate
the beans. Fetch ensures that the beans are populated. Based on our need
wherever we need data, we can define fetch. When joining table data is not
needed the fetch can be ignored.

Fetch quiz attempt details using HQL


In a quiz application there is a requirement for admin to view details of a quiz that an user had attempted.
This view should include the following details:

 Username
 Attempted Date
 All questions as part of the attempt
 List of options under each quiz
 The option that is correct answer
 The score for correct answer

Schema Diagram

Notes on Schema:

 Tables user, question and options are self explanatory. They hold the respective master data.
 Tables attempt, attempt_question and attempt_option are used to hold the data of attempts made
by each user.

Follow steps below to setup the schema:

 Go to spring-data-jpa-files folder in windows explorer


 Open file quiz.mwb in MySQL Workbench
 Generate SQL file using File > Export > Forward Engineer SQL CREATE Script
 Click Browse and select the file name and folder for the saving the generated SQL file
 Select the check box "Generate INSERT Statements for Tables"
 Click Next > Next > Finish to generate the SQL file
 Execute the SQL file in ormlearn schema and check the data in the tables

Steps to get this implemented:


 Create necessary entity class for each table defined above
 Define necessary O/R mapping based on the schema defined above
 Create a Repository and Service class:
o AttemptRepository
 public Attempt getAttempt(int userId, int attemptId)
o AttemptService
 public Attempt getAttempt(int userId, int attemptId)
 Modify OrmLearnApplication.java to include a new test method and test
AttemptService.getAttemptDetail() method
 Create HQL that joins the tables in the below order:
o user
o attempt
o attempt_question
o question
o attempt_option
o options
 In the HQL include where class for userId and attemptId
 Include 'fetch' in HQL wherever there is one-to-many or many-to-many relationship
 In OrmLearnApplication.java test method get the attempts details, iterate through the details and
display the data in the following format. The second column in each option denotes the score
from question table. The last column in each option denotes the answer selected by the user.

What is the extension of the hyper text markup language file?


1) .xhtm 0.0 false
2) .ht 0.0 false
3) .html 1.0 true
4) .htmx 0.0 false

What is the maximum level of heading tag can be used in a HTML page?
1) 5 0.0 false
2) 3 0.0 true
3) 4 0.0 false
4) 6 1.0 false

The HTML document itself begins with <html> and ends </html>. State True of False
1) false 0.0 false
2) true 1.0 true

Choose the right option to store text value value in a variable


1) 'John' 0.5 true
2) John 0.0 false
3) "John" 0.5 false
4) /John/ 0.0 false

Get average salary using HQL


Compute the average salary of a department using HQL.

Refer steps below to implement:

 Define HQL in EmployeeRepository


@Query(value="SELECT AVG(e.salary) FROM Employee e")
double getAverageSalary();

 Include new method with above signature in EmployeeService and include test method in
OrmLearnApplication
 The above query does not filter the result based on department id. Modify the query and method
signature as specified below to accept department.

@Query(value="SELECT AVG(e.salary) FROM Employee e where e.department.id = :id")


double getAverageSalary(@Param("id") int id);

NOTES:

 Observe how department id is referred from 'e'


 Make note of the colon (:) used to define a parameter within a query
 @Param annotation helps in binding the input department id with the query parameter
 Similar to AVG(), all other aggregate functions can be used

Get all employees using Native Query


About Native Queries

 Native queries are direct SQL queries to the database instead of using HQL
 Try to avoid Native Queries and make it minimal.
 Avoiding native queries helps in easier portability of database

Follow steps below to implement

 Define a new native query method in EmployeeRepository

@Query(value="SELECT * FROM employee", nativeQuery = true)


List<Employee> getAllEmployeesNative();

 Define relevant method in service and OrmLearnApplication and test it

Criteria Query
Find below a online retail user scenario

 User goes to Amazon


 Searches with keyword "laptop"
 The left hand size contains the following filteria criteria categories:
o Customer review
o Hard Disk Size
o RAM Size
o CPU Speed
o Operating System
o Weight
o CPU
 The user might select options available in one or more of the criteria and try a fresh search.
In the above given scenario, what will be the where clause of the HQL query that you will run on the
product?

The where clause varies based on the criteria selected by the user. We have to dynamically frame the
where clause filters based on the criteria selected by user.

Criteria Query helps in handling this scenario in a better way. The filter criteria can be programmatically
added, rather than fixing the HQL Statement.

Go through the examples in the link below to understand how Criteria Query has to be implemented.
https://howtodoinjava.com/hibernate/hibernate-criteria-queries-tutorial/

REST API Integration Scores


Objectives

 Demonstrate implementation of RESTful Web Service integrated with Spring Data JPA
o @JsonIgnore, @JsonManagedReference, @JsonBackReference, @JsonIdentityInfo, data transfer objects,
persisting data in POST and PUT methods
 Reference to resolve cyclic dependency - https://www.baeldung.com/jackson-bidirectional-
relationships-and-infinite-recursion

REST API to get all countries


Follow steps below to implement:

 The REST API will be implemented in the spring-learn project


 Modify spring-learn application pom.xml to include Spring Data JPA and MySQL Connector
dependencies
 Modify application.properties of spring-learn application to include database connectivity details
and hibernate log configuration details
 Create com.spring-learn.repository and com.spring-learn.service packages
 Copy CountryService and CountryRepository from orm-learn project
 Autowire CountryService in CountryController and modify the respective controller method that
returns all countries to use CountryService
 Run the service
 Using Chrome Browser or curl command or postman to test the REST API and check if it returns
the countries from the database

Implement all REST API for Country

Using the approach defined in the previous hands on implement the following REST API services related
to country:

 Get country by code


o Endpoint - /countries/IN
o Method - GET
o Expected Output

{"code":"IN","name":"India"}
 Get country based on keyword with result sorted based on country name
o Endpoint - /countries?key=ambia
o Method - GET
o Expected Output

[{"code":"GM","name":"Gambia"}, {"code":"ZM","name":"Zambia"}]

 Add Country
o Endpoint - /countries
o Method - POST
o Request Body - New country details in JSON format {"code":"IN","name":"India"}
o Validations
 code is required
 code cannot exceed 2 characters
 name is required
 name cannot exceed 50 characters
o Response - HTTP Status code 200
 Update Country
o Endpoint - /countries
o Method - PUT
o Request Body - New country details in JSON format {"code":"IN","name":"India"}
o Same validations as specified for add country
o Response - HTTP Status code 200
 Delete Country
o Endpoint - /countries/IN
o Method - DELETE
o Response - HTTP Status code 200

Implement get all employees as REST API


Similar to the instructions provided in previous hands on, create respective repository and service class in
spring-learn application to implement the below REST API service.

Get all employees

 Endpoint - /employees
 Method - GET
 Expected Result - Due to cyclic dependency of objects, it goes into a recursive loop and it does
not result in the result that we expect. We will look at possible solutions in the next hands on.
Test Cases

Resolutions of Cyclic Dependency to get all employees


Solution using @JsonIgnore

Include @JsonIgnore annotation in the below given attributes:

 Department.employeeList
 Skill.employeeList

Note: Import JsonIgnore from com.fasterxml.jackson.annotation.

The above changes will resolve the issue and data will be retrieved as expected. But there is a
disadvantage to this approach. If there is a need to get all employees based on Skill, the employee list
cannot be retrieved due to @JsonIgnore.

Solution using @JsonManagedReference and @JsonBackReference

Comment out the @JsonIgnore annotations defined earlier.

Refer implementation available in the link below and check if this solves the cyclic dependency issue.
There is possibility that few employee details are repeated in the result. Hence this might not bean ideal
solution.
https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

Solution using @JsonIdentityInfo

Comment out the @JsonManagedReference and @JsonBackReference annotations and try the
@JsonIdentityInfo feature provided in the above link. This goes for a recursive data retrieval and will not
be in the form that we need.

Solution using Data Transfer Object

A new set of beans for Employee, Department and Skill needs to be created with DTO suffixed. Then
transfer the data from repository into these new DTO objects and return the EmployeeDTO reference
instead of the entity Employee reference. Follow steps below to implement this approach:

 Create new package com.cognizant.spring-learn.dto


 Create three classes in this new package named EmployeeDTO, DepartmentDTO and SkillDTO
 In the new DTO classes, create attribute exactly the same as the ones in model package
 Create a private method in EmployeeController with signature as defined below

private EmployeeDTO[] transformEmployeeToDTO(Employee[] employee)

 In this method create new EmployeeDTO instance of each Employee. For each employee create
necessary DepartmentDTO and SkillDTO instances. Call respective DTO's setter method to set
the data.
 Invoke this method in getAllEmployees() method after getting the employee list from repository.
 Return EmployeeDTO reference returned by the above method as return type for
EmployeeController.getAllEmployees() method.

Persist employee details through REST API


Expose the following RESTful API Services related to employee in spring-learn project.

Update Employee

 Endpoint - /employees
 Method - PUT
 Request Body - Employee details in JSON Format
 Response - Status 200
 Implementation Steps
o Copy necessary repository and service classes
o Implement the REST API in EmployeeController
o Use the existing request availablein Postman to make the PUT call and test if employee
details are getting save
o Kindly note that DTO classes are not required for this implementation. The entity objects
can be directly used.

Add Employee

 Endpoint - /employees
 Method - PUT
 Request Body - Employee details in JSON Format
 Response - Status 200

Verify transaction feature when adding employee


In the add employee REST API implemented in the previous hands on change the department id in the
request JSON with an id that does not exist in the database.

This request will respond with 500 - Internal Server Error.

Observe the following aspects in the log:

 Insert SQL for employee table would have got executed successfully
 After that it would have failed due to non-availability of department id
 If we go to the database and check employee table, the newly inserted record will not be existing
 Due to failure to update the department details, the insertion had got rolled back

Angular Integration and Mockito Scores


Objectives
 Demonstrate implementation of spring security integrated with repository to get user details
o UserDetailsService, UserDetail, loadUserByUsername(), UsernameNotFoundException
 Reference - https://docs.spring.io/spring-security/site/docs/5.2.1.BUILD-SNAPSHOT/reference/
htmlsingle/#tech-userdetailsservice
 Demonstrate implementation of isolated testing of service using Mockito
o Importance of isolated testing, MockitoJUnitRunner, Mockito.mock(), when(), thenReturn()
 Spring Boot with Mockito - https://www.springboottutorial.com/spring-boot-unit-testing-and-mocking-
with-mockito-and-junit
 Demonstrate code coverage report generation on unit test cases executed
o Eclipse, ECL Emma, understanding the color coding
 EMMA Reference - https://www.eclemma.org/
 Demonstrate end to end integration with Angular, RESTful Web Services and Spring Data JPA
o Implement an event triggered in angular that executes a restful web service, which in turn invoke Spring Data JPA
Repository that gets or persists data in database

Integrate Spring Security with database


In "Spring RESTful Web Services" module, we implemented Spring Security with in memory database. In
this hands on we will integrate Spring Security to use Spring Data JPA repository that manages the user
details.

Prepare the Schema and Repository classes

 Create relevant tables for storing user and role in ormlearn schema. (Refer the ER diagram in the
end)
 Create classes User and Role with necessary many-to-many mapping
 Define roleList in User with eager fetch
 Create UserRepository with inclusion of a Query Method 'findByUsername' to get an user based
on username.
 In user table create two users one for user and one for admin
 Make the password
as $2a$10$R/lZJuT9skteNmAku9Y7aeutxbOKstD5xE5bHOf74M2PHZipyt3yK, which is the
bcrypt encoding of the text 'pwd'
 Test if findByUsername() method works fine and retrieves the user and roles

AppUser.java
This new bean class has to be implement UserDetails class of spring security framework, so that spring
framework can read user details from this bean. Refer steps below to implement.
 Create bean class AppUser to hold the user details. This needs to be implement UserDetails
class of spring security framework, so that spring framework can read user details from this bean.
Refer steps below to implement:
 Right click on com.cognizant.spring-learn.security > New > Class
 Enter "Name" as "AppUser"
 In "Interfaces" click "Add.." and select "org.springframework.security.core.userdetails.UserDetails"
 Click Finish
 The above steps will create AppUser class and populate the code with all the methods that needs
to be implemented.
 Include below instance variables in this class:

private User user; // entity reference


private Collection<? extends GrantedAuthority> authorities; // to store role details

 Create a constructor with user as parameter. Obtain the roles from the user and convert them to
authorities. Refer code below for authorities conversion that needs to be included in the
constructor.

this.authorities = user.getRoleList().stream()
.map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());

 The above code converts the list of roles into a list of SimpleGrantedAuthority.
 Return authorities in getAuthorities() method
 Return user.getPassword() in getPassword() method
 Return true in all the below methods:
o isAccountNonExpired()
o isAccountNonLocked()
o isCredentialsNonExpired()
o isEnabled()

AppUserDetailsService.java

 Create a new class AppUserDetailsService in com.cognizant.spring-learn.security package.


 This class has to implement org.springframework.security.core.userdetails.UserDetailsService, so
that spring security framework uses this class to retrieve user details.
 Autowire UserRepository
 Implement loadUserByUsername() method

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException

 Import UsernameNotFoundException from org.springframework.security.core.userdetails;


 Invoke findByUsername() method from UserRepository and get the user.
 If user is null throw UsernameNotFoundException
 If user is not null then create an instance of AppUser passing user obtained from repository.
 Return the AppUser reference.
 Test the execution of loadUserByUsername() to see if user details are retrieved along with roles.

SecurityConfig.java

 Autowire AppUserDetailsService
 In configure() method, comment out the in memory definition and bind spring framework to use
the appUserDetailsService for authentication.

auth.userDetailsService(appUserDetailsService).passwordEncoder(passwordEncoder());

 Test the authentication web service using curl or postman.


 A Unit Test should be self sufficient
As the name "Unit Testing" suggests, it is supposed to test a specific method in isolation without
worrying about the dependencies.

Let us try to understand this with an example. Consider a Service method that calls a method in
Repository. The service method is dependent on repository and the repository is dependent on
database. When we have to test a service method, we should test it without the dependency on
repository.

A good unit test should be isolated. Avoid dependencies such as environment settings, register
values, or databases.

Let us consider the loadUserByUsername() method we implemented recently.

In this code below, find out the lines that has dependency on repository and database.

 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {


 LOGGER.info("Start");
 LOGGER.debug("UserRepository:{}", userRepository);
 User user = userRepository.findByUsername(username);
 LOGGER.debug("User:{}", user);
 if (user == null) {
 throw new UsernameNotFoundException(username);
 }
 LOGGER.info("End");
 return new AppPrincipalUser(user);
 }

 SME to walkthrough each line of the above code and identify the lines that has dependency.

In the subsequent hands on we will identify how to avoid the dependency using mockito.

Implement integrated testing of loadUserByUsername() method

 Open spring-learn application in Eclipse


 Create a new package com.cognizant.spring-learn.service in src/test/java
 Create a new class UserDetailsServiceTest
 Define the following annotations at class level. This ensures that the class has spring application
context.

@RunWith(SpringRunner.class)
@SpringBootTest

 Autowire UserDetailsService and implement the below test method. This method validates if the
password returned is as per what is available in the database.
@Test
public void testLoadByUserName() {
LOGGER.info("Start");
UserDetails user = userDetailsService.loadUserByUsername("usr");
String expected = "$2a$10$R/lZJuT9skteNmAku9Y7aeutxbOKstD5xE5bHOf74M2PHZipyt3yK";
assertEquals(expected, user.getPassword());
LOGGER.info("End");
}

 Execute this as JUnit Test Case by right clicking on this test class and selecting Run > JUnit Test
 Check the logs to see that the service calls repository and it retrieves the data from database
 Ask yourself the following questions:
o Is this method executed in isolation?
o Is this test unit testing?

This kind of testing is integration testing and not unit testing.

In the next hands on we will see how to isolate the testing using Mockito.

Implement isolated testing with Mockito


Refer the code of the method to be tested:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LOGGER.info("Start");
LOGGER.debug("UserRepository:{}", userRepository);
User user = userRepository.findByUsername(username);
LOGGER.debug("User:{}", user);
if (user == null) {
throw new UsernameNotFoundException(username);
}
LOGGER.info("End");
return new AppPrincipalUser(user);
}

Our unit test cases should ensure that all the lines and all possible scenarios are tested.

The above method can be tested in two scenarios:

 The method findByUsername() returns a valid


 The method findByUsername() returns null due to non-availability of user in the database

Implement the steps below to test the first scenario:

 Create a new test class UserDetailsServiceMockTest in src/test/java, com.cognizant.spring-


learn.service package
 Annotate @RunWith with MockitoJUnitRunner.class
(import org.mockito.junit.MockitoJUnitRunner)
 Annotate @SpringBootTest
 Include test method mockTestLoadUserByUsername()
 Implement following steps in the test method.
 Using Mockit.mock() method create a mock of the repository

UserRepository repository = Mockito.mock(UserRepository.class);

 Using below line of code define what happens when findByUsername() method in repository is
called. This is how we are isolating the dependency.

when(repository.findByUsername("usr")).thenReturn(createUser());

 Due to the above defintion the database call will not happen, we need to simulate the return of
User as part of this call. We achieve this by using a new createUser() method that takes care of
creating a user instance with necessary password and role that is expected. Implement this
method within the test class.
 Create a new constructor in AppUserDetailsService that accepts UserRepository as input and
sets the userRepository instance variable.
 Create AppUserDetailsService instance using the mocked repository

AppUserDetailsService service = new AppUserDetailsService(repository); // repository refers to the mock


repository created

 Invoke the method we want to test. When this method is executed, the repository's find method
will not be invoke, but mockito will just return whatever is returned by createUser() method.

UserDetails user = service.loadUserByUsername("usr");

 Include the lines below to assert if the password matches.

String expected = "$2a$10$R/lZJuT9skteNmAku9Y7aeutxbOKstD5xE5bHOf74M2PHZipyt3yK";


assertEquals(expected, user.getPassword());

 Run the class as JUnit test and check if the test passes.

Implement the mockito test for the second scenario when user is returned as null

 Mock the findByUsername() to return null


 Pass the test case if UsernameNotFoundException is thrown. This can be implemented by
asserting true in the catch block of UsernameNotFoundException handling. Include return after
assert.
 Assert it as false after catch block

Code Coverage of JUnit Testing


Following steps below to check the test coverage of JUnit testing:

 Right click on test class


 Select "Coverage As" > "JUnit Test"
 This executes the following actions:
o Executes the unit test cases
o Marks each line as red and green to denote if a line was executed or not. Open various
files of your choice and check the relevance of red and green lines.
o Generates a coverage report that can be seen in a new view "Coverage". Maximize the
"Coverage" view, expand the packages to view the class wise code coverage
percentage.

Integrate employee web services with Spring Data JPA


Hope you all remember the following features implemented as part of angular hands on:

 Display list of employees


 Clicking edit link on an employee will open employee form with populated values
 Clicking save on edit form to persist the employee details
 Clicking delete link on an employee has to delete the employee

Currently the above implementations are done with in memory static data.

Using repository and appropriate beans implement this with repository and service.

Test in the angular application to see if the features work end to end.

Integrate employee web services with Spring Data JPA


Hope you all remember the following features implemented as part of angular hands on:

 Display list of employees


 Clicking edit link on an employee will open employee form with populated values
 Clicking save on edit form to persist the employee details
 Clicking delete link on an employee has to delete the employee

Currently the above implementations are done with in memory static data.

Using repository and appropriate beans implement this with repository and service.

Test in the angular application to see if the features work end to end.

Implement end to end country search feature


Create a new component and route in angular application for searching country.

This component should have a text box and a div in the bottom of the text box. This div will list the
countries that matches with the keyword entered in text box. The countries within the div needs to be
displayed line by line.
Create necessary service in angular application that invokes the respective REST API created for
returning the country list based on keyword.

This REST API needs to be called on the key press event of the search text box.

You might also like