Spring Boot DATA JPA CRUD Web App

CRUD stands for Create Read Update Delete.

Video guide of the process



01. Add the required dependencies

Ensure that you have the below dependencies in your pom.xml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

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

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

Depending on the database, you are intending to connect to, add the appropriate jdbc-connecter dependency in you pom.xml.

For H2 : use the below dependency
1
2
3
4
5
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

Note: If you generate the project using IDE / Spring Initializer, then you will have an option to choose your database connector from the dependencies dropdown.

02. Define Entity

Create a class Product.java with the below definition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package snmaddula.product.crud.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Data
@Entity
@Table(name = "PRODUCT")
public class Product {

@Id
@GeneratedValue
private Long id;
private String title;
private String description;
private Double price;
}

In case, if your lombok configuration is not working, you can define setters & getters in the above class.

03. Define Repository.

Create an interface ProductRepo.java that extends JpaReository as below.

1
2
3
4
5
6
7
8
9
10
11
package snmaddula.product.crud.repo;

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

import snmaddula.product.crud.entity.Product;

@Repository
public interface ProductRepo extends JpaRepository<Product, Long> {

}

04. Add Service Implementation

Create a service ProductService.java that talks to the repository to perform crud operations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package snmaddula.product.crud.service;

import java.util.List;
import java.util.Optional;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import snmaddula.product.crud.entity.Product;
import snmaddula.product.crud.repo.ProductRepo;

@Service
@RequiredArgsConstructor
public class ProductService {

private final ProductRepo productRepo;

public List<Product> fetchAll() {
return productRepo.findAll();
}

public Optional<Product> fetchById(Long id) {
return productRepo.findById(id);
}

public Product create(Product product) {
return productRepo.save(product);
}

public Product update(Long id, Product product) {
if (productRepo.existsById(id)) {
return productRepo.save(product);
}
return null;
}

public void remove(Long id) {
productRepo.deleteById(id);
}

public void removeAll() {
productRepo.deleteAllInBatch();
}

}

04. Add Controller Implementation

Create a rest controller ProductController.java and add appropriate CRUD mappings for Product.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package snmaddula.product.crud.controller;

import java.util.List;
import java.util.Optional;

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 lombok.RequiredArgsConstructor;
import snmaddula.product.crud.entity.Product;
import snmaddula.product.crud.service.ProductService;

@RestController
@RequiredArgsConstructor
@RequestMapping("/products")
public class ProductController {

private final ProductService productService;

@GetMapping
public List<Product> getAll() {
return productService.fetchAll();
}

@GetMapping("{id}")
public Optional<Product> getById(@PathVariable Long id) {
return productService.fetchById(id);
}

@PostMapping
public Product create(@RequestBody Product product) {
return productService.create(product);
}

@PutMapping("{id}")
public Product update(@PathVariable Long id, @RequestBody Product product) {
return productService.update(id, product);
}

@DeleteMapping("{id}")
public void removeById(@PathVariable Long id) {
productService.remove(id);
}

@DeleteMapping
public void removeAll() {
productService.removeAll();
}

}

05. CONNECT to the DATABASE

If you are using H2 database (in embed mode), then you pretty much need zero configuration for this app to work.

Optionally to view the H2 Console, you can add the below configuration elements.

1
spring.h2.console.enabled: true

With this, you will be able to access the H2 Console at http://localhost:8080/h2-console

Your configuration should look something to similar as show below.

Click Connect, to open up the console.

06. Configuring DataSource

If you are using any other database, then you would need to provide the connection parameters using the below properties.

1
2
3
4
spring.datasource.url=<jdbc-url>
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=

Configure HTTP to HTTPS Redirect in Spring Boot

In the previous post, we have configured HTTPS in Spring Boot application. Today, we shall see how we can accept HTTP requests and redirect them to HTTPS.

Video guide of the process



Step 1 : Configure Redirect Connector.

Configure a connector, that will listen on port 80 for HTTP requests and redirect them to HTTPS on port 443, where HTTPS connector configured by Spring is listening to.

1
2
3
4
5
6
7
8
private Connector redirectConnector() {
return new Connector(Http11NioProtocol.class.getName()) {{
setScheme("http");
setPort(80);
setSecure(false);
setRedirectPort(443);
}};
}

Step 2 : Configure TomcatServletWebServerFactory Bean

Now, we need to configure TomcatServletWebServerFactory, which is used by Spring Boot to configure the embedded tomcat server.

Override the postProcessContext(Context context) method and add the securityConstraints of CONFIDENTIAL for all the requests.

Finally, we need to expose this factory as a Bean, so that Spring Boot uses it to create the embedded tomcat server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Bean
public TomcatServletWebServerFactory servletContainer() {
return new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
{
getAdditionalTomcatConnectors().add(0, redirectConnector());
}
};
}

Step 3 : Update TLS Port (Optional)

Update server.port to 443 in your application properties file as 443 is the default TLS port.

1
server.port=443

With this, the redirect connector configuration is done and you can verify by accessing the application using HTTP endpoint.

Go to your browser, type localhost and hit enter.

This will be redirected to https://localhost and you will see the Hello TLS / SSL response.

Enable HTTPS in Spring Boot Application

Video guide of the process



Step 1 : Generate SSL Certificate

To enable HTTPS, we would require a SSL certificate. This certificate can be obtained from a Certificate Authority.
Also, we can generate a self-signed certificate using java keytool utility.

1
2
3
4
keytool -genkey -noprompt -alias chocolate \
-storetype PKCS12 -keyalg RSA -keysize 2048 -validity 365 \
-keystore chocolate.p12 -keypass chocolate -storepass chocolate \
-dname "CN=snmaddula, OU=security, O=snmaddula, L=waverock, S=Hyderabad, C=IN"

This will generate a PKCS12 keystore file as chocolate.p12 with the newly generated certificate.

Step 2 : Configure SSL connector for Server

To enable HTTPS in our application, we need to configure few ssl properties binding the application server with the keystore that was generated in the previous step.

Add the below configuration to your application.yml

1
2
3
4
5
6
7
8
server:
port: 8443

ssl:
key-store: classpath:chocolate.p12
key-alias: chocolate
key-store-password: chocolate
key-store-type: PKCS12

That’s all you need to enable HTTPS in a spring boot application.

You can now access the application with the endpoint: https://localhost:8443

You would need to add the Security Certificate Exception in this was a self-signed certificate.

Spring Boot File Download via Angular7 PART 02

Preparing the Angular Client (Angular 7)

Video guide of the angular-client process



1. Create Angular App

Use the below command to create a new angular app with name client.

1
ng new client --minimal=true --routing=false --style=css

In case if you see any vulnerabilities reported after the app was created, switch to the client directory and run the below command to let npm fix those issues.

1
npm audit fix

2. Add File Saver dependency

Add file-saver dependency to enable saving files to the file-system on the client side.

1
npm i -s file-saver

3. Create File Download Service

Use the below command to generate file download service.

1
ng g s service/file-download

4. Create File Download Component

Use the below command to generate file download component.

1
ng g c component/file-download

5. Add HttpClientModule to the application.

To be able to use the HttpClient service within your components we first need to include the HttpClientModule in the Angular application.
First we need to import HttpClientModule in the application’s root module in the file app.module.ts by adding the below import statement.

1
import { HttpClientModule } from '@angular/common/http';
6. Implement File Download Service

Update file-download.service.ts with the below implementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';

@Injectable({
providedIn: 'root'
})
export class FileDownloadService {

constructor(private http:HttpClient) { }

downloadFile(data) {
const REQUEST_PARAMS = new HttpParams().set('fileName', data.fileName);
const REQUEST_URL = '/server/download'
return this.http.get(REQUEST_URL, {
params: REQUEST_PARAMS,
responseType: 'arraybuffer'
});
}
}

7. Implement File Download Component

    i. Update file-download.component.ts with the below implementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { Component, OnInit } from '@angular/core';
import { FileDownloadService } from '../../service/file-download.service';
import { saveAs } from 'file-saver';

const MIME_TYPES = {
pdf : 'application/pdf',
xls : 'application/vnd.ms-excel',
xlsx : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
};

@Component({
selector: 'app-file-download',
templateUrl: './file-download.component.html',
styleUrls: ['./file-download.component.css']
})
export class FileDownloadComponent implements OnInit {

constructor(private service:FileDownloadService) { }

ngOnInit() {
}

downloadFile(fileName) {
console.log("fileName : " + fileName);
const extension = fileName.substr(fileName.lastIndexOf('.') + 1)
this.service.downloadFile({'fileName': fileName})
.subscribe(data => {
saveAs(new Blob([data], { type: MIME_TYPES[extension] }), fileName);
})
}
}

    ii. Update file-download.component.html with the below implementation.

1
2
3
4
5
6
7
8
9
<h3>Choose File </h3>

<select #selectedFile>
<option value="">- select -</option>
<option value="alpha.xls">alpha.xls</option>
<option value="alpha.xlsx">alpha.xlsx</option>
</select>

<button style="margin-left: 30px;" (click)="downloadFile(selectedFile.value)">Download</button>

    iii. Update app.component.html with the below implementation.

1
2
3
4
5
6
<div style="text-align:center">
<h1>
File Download Demo
</h1>
<app-file-download></app-file-download>
</div>
8. Add Proxy Configuration to reach Spring Boot Service.

In the root directory of the angular app, create a file proxy.config.json with the below contents.

1
2
3
4
5
6
7
8
{
"/server":{
"target":"http://localhost:8080",
"pathRewrite":{
"^/server":""
}
}
}

The last change we need to make is to tell the Angular server to use the new proxy.
Open package.json file and update the start script as below.

1
"start": "ng serve --proxy-config proxy.config.json"

Now start the app using npm start and navigate to http://localhost:4200.
You’ll see a UI similar to the one in the below image.