The Spring Framework is an application framework and inversion of control container for the Java platform. The framework’s core features can be used by any Java application, but there are extensions for building web applications on top of the Java EE platform.
Spring Boot is a Spring framework module which provides RAD (Rapid Application Development) feature to the Spring framework. It is highly dependent on the starter templates feature which is very powerful and works flawlessly.
1. What is starter template?
Spring Boot starters are templates that contain a collection of all the relevant transitive dependencies that are needed to start a particular functionality. For example, If you want to create a Spring WebMVC application then in a traditional setup, you would have included all required dependencies yourself. It leaves the chances of version conflict which ultimately result in more runtime exceptions.
With Spring boot, to create MVC application all you need to import is spring-boot-starter-web dependency.
<!-- Parent pom is mandatory to control versions of child dependencies --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version><relativePath/></parent><!-- Spring web brings all required dependencies to build web application. --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
Above spring-boot-starter-web dependency, internally imports all given dependencies and add to your project. Notice how some dependencies are direct, and some dependencies further refer to other starter templates which transitively downloads more dependencies.
Also, notice that you do not need to provide version information into child dependencies. All versions are resolved in relation to version of parent starter (in our example it’s 2.0.4.RELEASE).
Autoconfiguration is enabled with @EnableAutoConfiguration annotation. Spring boot auto configuration scans the classpath, finds the libraries in the classpath and then attempt to guess the best configuration for them, and finally configure all such beans.
Auto-configuration tries to be as intelligent as possible and will back-away as you define more of your own configuration.
Auto-configuration is always applied after user-defined beans have been registered.
Spring boot auto-configuration logic is implemented in spring-boot-autoconfigure.jar. Yoy can verify the list of packages here.
For example, look at auto-configuration for Spring AOP. It does the followings-
Scan classpath to see if EnableAspectJAutoProxy, Aspect, Advice and AnnotatedElement classes are present.
If classes are not present, no autoconfiguration will be made for Spring AOP.
If classes are found then AOP is configured with Java config annotation @EnableAspectJAutoProxy.
It checks for property spring.aop which value can be true or false.
Based on the value of property, proxyTargetClass attribute is set.
Spring boot applications always include tomcat as embedded server dependency. It means you can run the Spring boot applications from the command prompt without needling complex server infrastructure.
You can exclude tomcat and include any other embedded server if you want. Or you can make exclude server environment altogether. It’s all configuration based.
For example, below configuration exclude tomcat and include jetty as embedded server.
To run the application, we need to use @SpringBootApplication annotation. Behind the scenes, that’s equivalent to @Configuration, @EnableAutoConfiguration, and @ComponentScan together.
It enables the scanning of config classes, files and load them into spring context. In below example, execution start with main() method. It start loading all the config files, configure them and bootstrap the application based on application properties in application.properties file in /resources folder.
### Server port #########server.port=8080### Context root ########server.contextPath=/home
To execute the application, you can run the main() method from IDE such eclipse, or you can build the jar file and execute from command prompt.
$ java -jar spring-boot-demo.jar
5. Advantages of Spring boot
Spring boot helps in resolving dependency conflict. It identifies required dependencies and import them for you.
It has information of compatible version for all dependencies. It minimizes the runtime classloader issues.
It’s “opinionated defaults configuration” approach helps you in configuring most important pieces behind the scene. Override them only when you need. Otherwise everything just works, perfectly. It helps in avoiding boilerplate code, annotations and XML configurations.
It provides embedded HTTP server Tomcat so that you can develop and test quickly.
It has excellent integration with IDEs like eclipse and intelliJ idea.
In this spring boot tutorial, we will learn about spring-boot-starter-parent dependency which is used internally by all spring boot dependencies. We will also learn what all configurations this dependency provides, and how to override them.
What is spring-boot-starter-parent dependency?
The spring-boot-starter-parent dependency is the parent POM providing dependency and plugin management for Spring Boot-based applications. It contains the default versions of Java to use, the default versions of dependencies that Spring Boot uses, and the default configuration of the Maven plugins.
Few important configurations provided by this file are as below. Please refer to this link to read the complete configuration.
<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd;<modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${revision}</version><relativePath>../../spring-boot-dependencies</relativePath></parent><artifactId>spring-boot-starter-parent</artifactId><packaging>pom</packaging><name>Spring Boot Starter Parent</name><description>Parent pom providing dependency and plugin management for applicationsbuilt with Maven</description><properties><java.version>1.8</java.version><resource.delimiter>@</resource.delimiter> <!-- delimiter that doesn't clash with Spring ${} placeholders --><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><maven.compiler.source>${java.version}</maven.compiler.source><maven.compiler.target>${java.version}</maven.compiler.target></properties>...<resource><directory>${basedir}/src/main/resources</directory><filtering>true</filtering><includes><include>**/application*.yml</include><include>**/application*.yaml</include><include>**/application*.properties</include></includes></resource></project>
The spring-boot-starter-parent dependency further inherits from spring-boot-dependencies, which is defined at the top of above POM file at line number : 9.
This file is the actual file which contains the information of default version to use for all libraries. The following code shows the different versions of various dependencies that are configured in spring-boot-dependencies:
Above list is very long and you can read complete list in this link.
How to override default dependency version?
As you see, spring boot has default version to use for most of dependencies. You can override the version of your choice or project need, in properties tag in your project’s pom.xml file.
e.g. Spring boot used default version of google GSON library as 2.8.2.
Now in your eclipse editor, you can see the message as : The managed version is 2.7 The artifact is managed in org.springframework.boot:spring-boot-dependencies:2.0.0.RELEASE.
Spring boot auto configuration scans the classpath, finds the libraries in the classpath and then attempt to guess the best configuration for them, and finally configure all such beans.
Auto-configuration tries to be as intelligent as possible and will back-away as we define more of our own custom configuration. It is always applied after user-defined beans have been registered.
Auto-configuration works with help of @Conditional annotations such as @ConditionalOnBean and @ConditionalOnClass.
For example, look at AopAutoConfiguration class.
If class path scanning finds EnableAspectJAutoProxy, Aspect, Advice and AnnotatedElement classes and spring.aop.auto=false is not present in properties file then Spring boot will configure the Spring AOP module for us.
In this article, you’ll learn how to map a one-to-many database relationship at the object level using JPA and Hibernate.
Consider the following two tables – posts and comments of a Blog database schema where the posts table has a one-to-many relationship with the comments table –
We’ll create a project from scratch and learn how to go about implementing such one-to-many relationship at the object level using JPA and hibernate.
We’ll also write REST APIs to perform CRUD operations on the entities so that you fully understand how to actually use these relationships in the real world.
Creating the Project
If you have Spring Boot CLI installed, then you can type the following command in your terminal to generate the project –
spring init -n=jpa-one-to-many-demo -d=web,jpa,mysql --package-name=com.example.jpa jpa-one-to-many-demo
Alternatively, You can generate the project from Spring Initializr web tool by following the instructions below –
Click Options dropdown to see all the options related to project metadata.
Change Package Name to “com.example.jpa”
Select Web, JPA and Mysql dependencies.
Click Generate to download the project.
Following is the directory structure of the project for your reference –
“Your bootstrapped project won’t have model, controller, repository and exception packages, and all the classes inside these packages at this point. We’ll create them shortly.“
Configuring the Database and Logging
Since we’re using MySQL as our database, we need to configure the database URL, username, and password so that Spring can establish a connection with the database on startup. Open src/main/resources/application.properties file and add the following properties to it –
# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url=jdbc:mysql://localhost:3306/jpa_one_to_many_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=root
# Hibernate
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE
Don’t forget to change the spring.datasource.username and spring.datasource.password as per your MySQL installation. Also, create a database named jpa_one_to_many_demo in MySQL before proceeding to the next section.
You don’t need to create any tables. The tables will automatically be created by hibernate from the Post and Comment entities that we will define shortly. This is made possible by the property spring.jpa.hibernate.ddl-auto = update.
We have also specified the log levels for hibernate so that we can debug all the SQL statements and learn what hibernate does under the hood.
The best way to model a one-to-many relationship in hibernate
I have been working with hibernate for quite some time and I’ve realized that the best way to model a one-to-many relationship is to use just @ManyToOne annotation on the child entity.
The second best way is to define a bidirectional association with a @OneToMany annotation on the parent side of the relationship and a @ManyToOne annotation on the child side of the relationship. The bidirectional mapping has its pros and cons. I’ll demonstrate these pros and cons in the second section of this article. I’ll also tell you when a bidirectional mapping is a good fit.
But let’s first model our one-to-many relationship in the best way possible.
Defining the Domain Models
In this section, we’ll define the domain models of our application – Post and Comment.
Note that both Post and Comment entities contain some common auditing related fields like created_at and updated_at.
We’ll abstract out these common fields in a separate class called AuditModel and extend this class in the Post and Comment entities.
We’ll also use Spring Boot’s JPA Auditing feature to automatically populate the created_at and updated_at fields while persisting the entities.
1. AuditModel
package com.example.jpa.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
value = {"createdAt", "updatedAt"},
allowGetters = true
)
public abstract class AuditModel implements Serializable {
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_at", nullable = false, updatable = false)
@CreatedDate
private Date createdAt;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "updated_at", nullable = false)
@LastModifiedDate
private Date updatedAt;
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}
In the above class, we’re using Spring Boot’s AuditingEntityListener to automatically populate the createdAt and updatedAt fields.
Enabling JPA Auditing
To enable JPA Auditing, you’ll need to add @EnableJpaAuditing annotation to one of your configuration classes. Open the main class JpaOneToManyDemoApplication.java and add the @EnableJpaAuditing to the main class like so –
package com.example.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@SpringBootApplication
@EnableJpaAuditing
public class JpaOneToManyDemoApplication {
public static void main(String[] args) {
SpringApplication.run(JpaOneToManyDemoApplication.class, args);
}
}
2. Post model
package com.example.jpa.model;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Entity
@Table(name = "posts")
public class Post extends AuditModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Size(max = 100)
@Column(unique = true)
private String title;
@NotNull
@Size(max = 250)
private String description;
@NotNull
@Lob
private String content;
// Getters and Setters (Omitted for brevity)
}
The Comment model contains the @ManyToOne annotation to declare that it has a many-to-one relationship with the Post entity. It also uses the @JoinColumn annotation to declare the foreign key column.
Defining the Repositories
Next, We’ll define the repositories for accessing the data from the database. Create a new package called repository inside com.example.jpa package and add the following interfaces inside the repository package –
Writing the REST APIs to perform CRUD operations on the entities
Let’s now write the REST APIs to perform CRUD operations on Post and Comment entities.
All the following controller classes are define inside com.example.jpa.controller package.
1. PostController (APIs to create, retrieve, update, and delete Posts)
package com.example.jpa.controller;
import com.example.jpa.exception.ResourceNotFoundException;
import com.example.jpa.model.Post;
import com.example.jpa.repository.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
public class PostController {
@Autowired
private PostRepository postRepository;
@GetMapping("/posts")
public Page<Post> getAllPosts(Pageable pageable) {
return postRepository.findAll(pageable);
}
@PostMapping("/posts")
public Post createPost(@Valid @RequestBody Post post) {
return postRepository.save(post);
}
@PutMapping("/posts/{postId}")
public Post updatePost(@PathVariable Long postId, @Valid @RequestBody Post postRequest) {
return postRepository.findById(postId).map(post -> {
post.setTitle(postRequest.getTitle());
post.setDescription(postRequest.getDescription());
post.setContent(postRequest.getContent());
return postRepository.save(post);
}).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " not found"));
}
@DeleteMapping("/posts/{postId}")
public ResponseEntity<?> deletePost(@PathVariable Long postId) {
return postRepository.findById(postId).map(post -> {
postRepository.delete(post);
return ResponseEntity.ok().build();
}).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " not found"));
}
}
2. CommentController (APIs to create, retrieve, update, and delete Comments)
package com.example.jpa.controller;
import com.example.jpa.exception.ResourceNotFoundException;
import com.example.jpa.model.Comment;
import com.example.jpa.repository.CommentRepository;
import com.example.jpa.repository.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
public class CommentController {
@Autowired
private CommentRepository commentRepository;
@Autowired
private PostRepository postRepository;
@GetMapping("/posts/{postId}/comments")
public Page<Comment> getAllCommentsByPostId(@PathVariable (value = "postId") Long postId,
Pageable pageable) {
return commentRepository.findByPostId(postId, pageable);
}
@PostMapping("/posts/{postId}/comments")
public Comment createComment(@PathVariable (value = "postId") Long postId,
@Valid @RequestBody Comment comment) {
return postRepository.findById(postId).map(post -> {
comment.setPost(post);
return commentRepository.save(comment);
}).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " not found"));
}
@PutMapping("/posts/{postId}/comments/{commentId}")
public Comment updateComment(@PathVariable (value = "postId") Long postId,
@PathVariable (value = "commentId") Long commentId,
@Valid @RequestBody Comment commentRequest) {
if(!postRepository.existsById(postId)) {
throw new ResourceNotFoundException("PostId " + postId + " not found");
}
return commentRepository.findById(commentId).map(comment -> {
comment.setText(commentRequest.getText());
return commentRepository.save(comment);
}).orElseThrow(() -> new ResourceNotFoundException("CommentId " + commentId + "not found"));
}
@DeleteMapping("/posts/{postId}/comments/{commentId}")
public ResponseEntity<?> deleteComment(@PathVariable (value = "postId") Long postId,
@PathVariable (value = "commentId") Long commentId) {
return commentRepository.findByIdAndPostId(commentId, postId).map(comment -> {
commentRepository.delete(comment);
return ResponseEntity.ok().build();
}).orElseThrow(() -> new ResourceNotFoundException("Comment not found with id " + commentId + " and postId " + postId));
}
}
The ResourceNotFoundException Class
Both the Post and Comment Rest APIs throw ResourceNotFoundException when a post or comment could not be found. Following is the definition of the ResourceNotFoundException.
package com.example.jpa.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
super();
}
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
I have added the @ResponseStatus(HttpStatus.NOT_FOUND) annotation to the above exception class to tell Spring Boot to respond with a 404 status when this exception is thrown.
Running the Application and Testing the APIs via a Postman
You can run the application by typing the following command in the terminal –
mvn spring-boot:run
Let’s now test the APIs via Postman.
Create Post POST /posts
Get paginated Posts GET /posts?page=0&size=2&sort=createdAt,desc
Create Comment POST /posts/{postId}/comments
Get paginated comments GET /posts/{postId}/comments?page=0&size=3&sort=createdAt,desc
You can test other APIs in the same way.
How to define a bidirectional one-to-many mapping and when should you use it
The Internet is flooded with examples of bidirectional one-to-many mapping. But it’s not the best and the most efficient way to model a one-to-many relationship.
Here is the bidirectional version of the one-to-many relationship between the Post and Comment entities –
package com.example.jpa.model;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@Entity
@Table(name = "comments")
public class Comment extends AuditModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Lob
private String text;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id", nullable = false)
private Post post;
// Getters and Setters (Omitted for brevity)
}
The idea with bidirectional one-to-many association is to allow you to keep a collection of child entities in the parent, and enable you to persist and retrieve the child entities via the parent entity. This is made possible via Hibernate’s entity state transitions and dirty checking mechanism.
For example, here is how you could persist comments via post entity in the bidirectional mapping –
// Create a Post
Post post = new Post("post title", "post description", "post content");
// Create Comments
Comment comment1 = new Comment("Great Post!");
comment1.setPost(post);
Comment comment2 = new Comment("Really helpful Post. Thanks a lot!");
comment2.setPost(post);
// Add comments in the Post
post.getComments().add(comment1);
post.getComments().add(comment2);
// Save Post and Comments via the Post entity
postRepository.save(post);
Hibernate automatically issues insert statements and saves the comments added to the post.
Similarly, you could fetch comments via the post entity like so –
// Retrieve Post
Post post = postRepository.findById(postId)
// Get all the comments
Set<Comment> comments = post.getComments()
When you write post.getComments(), hibernate loads all the comments from the database if they are not already loaded.
Problems with bidirectional one-to-many mapping
A bidirectional mapping tightly couples the many-side of the relationship to the one-side.
In our example, If you load comments via the post entity, you won’t be able to limit the number of comments loaded. That essentially means that you won’t be able to paginate.
If you load comments via the post entity, you won’t be able to sort them based on different properties. You can define a default sorting order using @OrderColumn annotation but that will have performance implications.
When can I use a bidirectional one-to-many mapping
A bidirectional one-to-many mapping might be a good idea if the number of child entities is limited.
Moreover, A bidirectional mapping tightly couples the many-side of the relationship to the one-side. Many times, this tight coupling is desired.
For example, Consider a Survey application with a Question and an Option entity exhibiting a one-to-many relationship between each other.
In the survey app, A Question can have a set of Options. Also, every Question is tightly coupled with its Options. When you create a Question, you’ll also provide a set of Options. And, when you retrieve a Question, you will also need to fetch the Options.
Moreover, A Question can have at max 4 or 5 Options. These kind of cases are perfect for bi-directional mappings.
So, To decide between bidirectional and unidirectional mappings, you should think whether the entities have a tight coupling or not.
Conclusion
That’s all folks! In this article, You learned how to map a one-to-many database relationship using JPA and Hibernate.
More Resources
You may wanna check out the following articles by Vlad Mihalcea to learn more about Hibernate and it’s performance –
Thanks for reading. I hope you found the content useful. Consider subscribing to my newsletter if you don’t wanna miss future articles from The Fusebes Blog.
In this article, You’ll learn how to schedule tasks in Spring Boot using @Scheduled annotation. You’ll also learn how to use a custom thread pool for executing all the scheduled tasks.
The @Scheduled annotation is added to a method along with some information about when to execute it, and Spring Boot takes care of the rest.
Spring Boot internally uses the TaskScheduler interface for scheduling the annotated methods for execution.
The purpose of this article is to build a simple project demonstrating all the concepts related to task scheduling.
Create the Project
Let’s use Spring Boot CLI to create the Project. Fire up your terminal and type the following command to generate the project –
$ spring init --name=scheduler-demo scheduler-demo
Alternatively, You can generate the project using Spring Initializer web app. Just go to http://start.spring.io/, enter the Artifact’s value as “scheduler-demo” and click Generate to generate and download the project.
Once the project is generated, import it in your favorite IDE. The project’s directory structure should like this –
Enable Scheduling
You can enable scheduling simply by adding the @EnableScheduling annotation to the main application class or one of the Configuration classes.
Open SchedulerDemoApplication.java and add @EnableScheduling annotation like so –
package com.example.schedulerdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class SchedulerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulerDemoApplication.class, args);
}
}
Scheduling Tasks
Scheduling a task with Spring Boot is as simple as annotating a method with @Scheduled annotation, and providing few parameters that will be used to decide the time at which the task will run.
Before adding tasks, Let’s first create the container for all the scheduled tasks. Create a new class called ScheduledTasks inside com.example.schedulerdemo package with the following contents –
package com.example.schedulerdemo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
@Component
public class ScheduledTasks {
private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);
private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
public void scheduleTaskWithFixedRate() {}
public void scheduleTaskWithFixedDelay() {}
public void scheduleTaskWithInitialDelay() {}
public void scheduleTaskWithCronExpression() {}
}
The class contains four empty methods. We’ll look at the implementation of all the methods one by one.
All the scheduled methods should follow the following two criteria –
The method should have a void return type.
The method should not accept any arguments.
Cool! Let’s now jump into the implementation.
1. Scheduling a Task with Fixed Rate
You can schedule a method to be executed at a fixed interval by using fixedRate parameter in the @Scheduled annotation. In the following example, The annotated method will be executed every 2 seconds.
@Scheduled(fixedRate = 2000)
public void scheduleTaskWithFixedRate() {
logger.info("Fixed Rate Task :: Execution Time - {}", dateTimeFormatter.format(LocalDateTime.now()) );
}
# Sample Output
Fixed Rate Task :: Execution Time - 10:26:58
Fixed Rate Task :: Execution Time - 10:27:00
Fixed Rate Task :: Execution Time - 10:27:02
....
....
The fixedRate task is invoked at the specified interval even if the previous invocation of the task is not finished.
2. Scheduling a Task with Fixed Delay
You can execute a task with a fixed delay between the completion of the last invocation and the start of the next, using fixedDelay parameter.
The fixedDelay parameter counts the delay after the completion of the last invocation.
Consider the following example –
@Scheduled(fixedDelay = 2000)
public void scheduleTaskWithFixedDelay() {
logger.info("Fixed Delay Task :: Execution Time - {}", dateTimeFormatter.format(LocalDateTime.now()));
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException ex) {
logger.error("Ran into an error {}", ex);
throw new IllegalStateException(ex);
}
}
Since the task itself takes 5 seconds to complete and we have specified a delay of 2 seconds between the completion of the last invocation and the start of the next, there will be a delay of 7 seconds between each invocation –
# Sample Output
Fixed Delay Task :: Execution Time - 10:30:01
Fixed Delay Task :: Execution Time - 10:30:08
Fixed Delay Task :: Execution Time - 10:30:15
....
....
3. Scheduling a Task With Fixed Rate and Initial Delay
You can use initialDelay parameter with fixedRate and fixedDelay to delay the first execution of the task with the specified number of milliseconds.
In the following example, the first execution of the task will be delayed by 5 seconds and then it will be executed normally at a fixed interval of 2 seconds –
@Scheduled(fixedRate = 2000, initialDelay = 5000)
public void scheduleTaskWithInitialDelay() {
logger.info("Fixed Rate Task with Initial Delay :: Execution Time - {}", dateTimeFormatter.format(LocalDateTime.now()));
}
# Sample output (Server Started at 10:48:46)
Fixed Rate Task with Initial Delay :: Execution Time - 10:48:51
Fixed Rate Task with Initial Delay :: Execution Time - 10:48:53
Fixed Rate Task with Initial Delay :: Execution Time - 10:48:55
....
....
4. Scheduling a Task using Cron Expression
If the above simple parameters can not fulfill your needs, then you can use cron expressions to schedule the execution of your tasks.
In the following example, I have scheduled the task to be executed every minute –
But hey, You can create your own thread pool and configure Spring to use that thread pool for executing all the scheduled tasks.
Create a new package config inside com.example.schedulerdemo, and then create a new class called SchedulerConfig inside config package with the following contents –
package com.example.schedulerdemo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
That’s all you need to do for configuring Spring to use your own thread pool instead of the default one.
If you log the name of the current thread in the scheduled methods now, you’ll get the output like so –
Current Thread : my-scheduled-task-pool-1
Current Thread : my-scheduled-task-pool-2
# etc...
Conclusion
In this article, you learned how to schedule tasks in Spring Boot using @Scheduled annotation. You also learned how to use a custom thread pool for running these tasks.
The Scheduling abstraction provided by Spring Boot works pretty well for simple use-cases. But if you have more advanced use cases like Persistent Jobs, Clustering, Dynamically adding and triggering new jobs then check out the following article –
Learn to create and configure spring boot jsp view resolver which uses JSP template files to render view layer. This example uses embedded Tomcat server to run the application.
Maven dependencies – pom.xml
This application uses given below dependencies.
<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.fusebes</groupId><artifactId>spring-boot-demo</artifactId><packaging>war</packaging><version>0.0.1-SNAPSHOT</version><name>spring-boot-demo Maven Webapp</name><url>http://maven.apache.org</url><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.1.RELEASE</version></parent><properties><java.version>1.8</java.version></properties><dependencies><!-- Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Tomcat Embed --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency><!-- JSTL --><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId></dependency><!-- To compile JSP files --><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId><scope>provided</scope></dependency></dependencies></project>
Spring Boot Application Initializer
The first step in producing a deployable war file is to provide a SpringBootServletInitializer subclass and override its configure() method. This makes use of Spring Framework’s Servlet 3.0 support and allows you to configure your application when it’s launched by the servlet container.
Controller classes can have methods mapped to specific URLs in the application. In given application, it has two views i.e. “/” and “/next”.
packagecom.fusebes.app.controller;importjava.util.Map;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;@ControllerpublicclassIndexController {@RequestMapping("/")publicString home(Map<String, Object> model) {model.put("message", "Fusebes Reader !!");return"index";}@RequestMapping("/next")publicString next(Map<String, Object> model) {model.put("message", "You are in new page !!");return"next";}}
Spring Boot JSP ViewResolver Configuration
To resolve JSP files location, you can have two approaches.
1) Add entries in application.properties
spring.mvc.view.prefix=/WEB-INF/view/spring.mvc.view.suffix=.jsp//For detailed logging during developmentlogging.level.org.springframework=TRACElogging.level.com=TRACE
2) Configure InternalResourceViewResolver to serve JSP pages
In this article, You’ll learn how to map a collection of basic as well as embeddable types using JPA’s @ElementCollection and @CollectionTable annotations.
Let’s say that the users of your application can have multiple phone numbers and addresses. To map this requirement into the database schema, you need to create separate tables for storing the phone numbers and addresses –
Both the tables user_phone_numbers and user_addresses contain a foreign key to the users table.
You can implement such relationship at the object level using JPA’s one-to-many mapping. But for basic and embeddable types like the one in the above schema, JPA has a simple solution in the form of ElementCollection.
Let’s create a project from scratch and learn how to use an ElementCollection to map the above schema in your application using hibernate.
Creating the Application
If you have Spring Boot CLI installed, then simply type the following command in your terminal to generate the application –
spring init -n=jpa-element-collection-demo -d=web,jpa,mysql --package-name=com.example.jpa jpa-element-collection-demo
Alternatively, You can use Spring Initializr web app to generate the application by following the instructions below –
Click Options dropdown to see all the options related to project metadata.
Change Package Name to “com.example.jpa”
Select Web, JPA and Mysql dependencies.
Click Generate to generate and download the project.
Configuring MySQL database and Hibernate Log levels
Let’s first configure the database URL, username, password, hibernate log levels and other properties.
Open src/main/resources/application.properties and add the following properties to it –
# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url=jdbc:mysql://localhost:3306/jpa_element_collection_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=root
# Hibernate
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE
Please change spring.datasource.username and spring.datasource.password as per your MySQL installation. Also, create a database named jpa_element_collection_demo before proceeding to the next section.
Defining the Entity classes
Next, We’ll define the Entity classes that will be mapped to the database tables we saw earlier.
Before defining the User entity, let’s first define the Address type which will be embedded inside the User entity.
All the domain models will go inside the package com.example.jpa.model.
We use @ElementCollection annotation to declare an element-collection mapping. All the records of the collection are stored in a separate table. The configuration for this table is specified using the @CollectionTable annotation.
The @CollectionTable annotation is used to specify the name of the table that stores all the records of the collection, and the JoinColumn that refers to the primary table.
Moreover, When you’re using an Embeddable type with Element Collection, you can use the @AttributeOverrides and @AttributeOverride annotations to override/customize the fields of the embeddable type.
Defining the Repository
Next, Let’s create the repository for accessing the user’s data from the database. You need to create a new package called repository inside com.example.jpa package and add the following interface inside the repository package –
Finally, Let’s write the code to test our setup in the main class JpaElementCollectionDemoApplication.java –
package com.example.jpa;
import com.example.jpa.model.Address;
import com.example.jpa.model.User;
import com.example.jpa.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.HashSet;
import java.util.Set;
@SpringBootApplication
public class JpaElementCollectionDemoApplication implements CommandLineRunner {
@Autowired
private UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(JpaElementCollectionDemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
// Cleanup database tables.
userRepository.deleteAll();
// Insert a user with multiple phone numbers and addresses.
Set<String> phoneNumbers = new HashSet<>();
phoneNumbers.add("+91-9999999999");
phoneNumbers.add("+91-9898989898");
Set<Address> addresses = new HashSet<>();
addresses.add(new Address("747", "Golf View Road", "Bangalore",
"Karnataka", "India", "560008"));
addresses.add(new Address("Plot No 44", "Electronic City", "Bangalore",
"Karnataka", "India", "560001"));
User user = new User("Yaniv Levy", "yaniv@fusebes.com",
phoneNumbers, addresses);
userRepository.save(user);
}
}
The main class implements the CommandLineRunner interface and provides the implementation of its run() method.
The run() method is executed post application startup. In the run() method, we first cleanup all the tables and then insert a new user with multiple phone numbers and addresses.
You can run the application by typing mvn spring-boot:run from the root directory of the project.
After running the application, Go ahead and check all the tables in MySQL. The users table will have a new entry, the user_phone_numbers and user_addresses table will have two new entries –
mysql> select * from users;
+----+-----------------------+--------------------+
| id | email | name |
+----+-----------------------+--------------------+
| 3 | yaniv@fusebes.com | Yaniv Levy |
+----+-----------------------+--------------------+
1 row in set (0.01 sec)
mysql> select * from user_phone_numbers;
+---------+----------------+
| user_id | phone_number |
+---------+----------------+
| 3 | +91-9898989898 |
| 3 | +91-9999999999 |
+---------+----------------+
2 rows in set (0.00 sec)
mysql> select * from user_addresses;
+---------+--------------+-----------------+-----------+---------+-----------+----------+
| user_id | house_number | street | city | country | state | zip_code |
+---------+--------------+-----------------+-----------+---------+-----------+----------+
| 3 | 747 | Golf View Road | Bangalore | India | Karnataka | 560008 |
| 3 | Plot No 44 | Electronic City | Bangalore | India | Karnataka | 560001 |
+---------+--------------+-----------------+-----------+---------+-----------+----------+
2 rows in set (0.00 sec)
Conclusion
That’s all in this article Folks. I hope you learned how to use hibernate’s element-collection with Spring Boot.
Spring Boot is powerful yet flexible. It tries to auto-configure most of the stuff for you so that you can get up and running quickly with your application.
It uses sensible defaults during auto-configuration but also gives you the flexibility to change those defaults by just tweaking a few things.
In this article, you’ll learn about the most common configuration tweaks that might need to do in your application.
Changing the embedded server in Spring Boot
Spring Boot uses Tomcat as the default embedded server. If you wanna use some other popular server like Jetty or Undertow then you just need to exclude tomcat dependency and add the other server dependency.
1. Using Jetty as the embedded server in Spring Boot
Changing the default server port and context path in Spring Boot
By default, Spring Boot runs your application on port 8080 with the context path /.
If you wanna change the default port and context path, then it’s just a matter of specifying the corresponding values in the application.properties file –
# HTTP Server port
server.port=8080
# Make the application accessible on the given context path (http://localhost:8080/myapp)
server.servlet.context-path=/myapp
Enabling GZip compression in Spring Boot
GZip compression is a very simple and effective way to save bandwidth and improve the speed of your website.
It reduces the response time of your website by compressing the resources and then sending it over to the clients. It saves bandwidth by at least 50%.
GZip compression is disabled by default in Spring Boot. To enable it, add the following properties to your application.properties file –
# Enable response compression
server.compression.enabled=true
# The comma-separated list of mime types that should be compressed
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
# Compress the response only if the response size is at least 1KB
server.compression.min-response-size=1024
Note that, GZip compression has a small overhead. Therefore I’ve added a min-response-size property to tell spring boot server to compress the response only if the size is more than the given value.
Enabling HTTP/2 support in Spring Boot
HTTP/2 is an improvement over the HTTP1 protocol. It improves the page load speed of your website by employing several mechanisms like data compression, server push, multiplexing of multiple requests over a single TCP connection etc.
You can enable HTTP2 in spring boot with the following property, if the server has support for it –
# Enable HTTP/2 support, if the current environment supports it
server.http2.enabled=true
Enabling browser caching of static resources in Spring Boot
Browser caching is another way to improve the page load speed of your website. You can set cache-control headers to tell browsers to cache static resources until a certain period of time.
Browser caching is disabled by default in Spring Boot. You can enable caching by setting the following properties in the application.properties file.
# Maximum time the response should be cached (in seconds)
spring.resources.cache.cachecontrol.max-age=120
# The cache must re-validate stale resources with the server. Any expired resources must not be used without re-validating.
spring.resources.cache.cachecontrol.must-revalidate=true
Following are few other cache related properties that you should be aware of –
# The resources are private and intended for a single user. They must not be stored by a shared cache (e.g CDN).
spring.resources.cache.cachecontrol.cache-private= # set a boolean value true/false
# The resources are public and any cache may store the response.
spring.resources.cache.cachecontrol.cache-public= # set a boolean value true/false
Configuring multipart file uploads in Spring Boot
Multipart file uploads are enabled by default in Spring Boot with the following property –
spring.servlet.multipart.enabled=true
But there are few other default multipart properties that you might need to change.
By default, Spring Boot allows you to upload a file with a maximum size of 1MB. You might need to change this to the desired value as per your requirements.
Following is the complete set of properties –
# Write files to disk if the file size is more than 2KB.
spring.servlet.multipart.file-size-threshold=2KB
# The intermediate disk location where the uploaded files are written
spring.servlet.multipart.location=/tmp
# Maximum file size that can be uploaded
spring.servlet.multipart.max-file-size=50MB
# Maximum allowed multipart request size
spring.servlet.multipart.max-request-size=75MB
Conclusion
In this short article, you learned how to tweak Spring Boot’s default configurations as per your needs. You can find a full index of common application properties on the following official Spring Boot page –
Whenever you need to configure anything in your spring boot application, the first thing you should do is – check the above common application properties page. Most probably, you’ll find a property for configuring the stuff that you’re looking for.
I hope you enjoyed this article. As always, Thanks for reading.
Nothing is perfect and complete when it is built the first time. The database schema of your brand new application is no exception. It is bound to change over time when you try to fit new requirements or add new features.
Flyway is a tool that lets you version control incremental changes to your database so that you can migrate it to a new version easily and confidently.
In this article, you’ll learn how to use Flyway in Spring Boot applications to manage changes to your database.
We’ll build a simple Spring Boot application with MySQL Database & Spring Data JPA, and learn how to integrate Flyway in the app.
Let’s get started!
Creating the Application
Let’s use Spring Boot CLI to generate the application. Fire up your terminal and type the following command to generate the project.
spring init --name=flyway-demo --dependencies=web,mysql,data-jpa,flyway flyway-demo
Once the project is generated, import it into your favorite IDE. The directory structure of the application would look like this –
Configuring MySQL and Hibernate
First create a new MySQL database named flyway_demo.
Then Open src/main/application.properties file and add the following properties to it –
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://localhost:3306/flyway_demo?useSSL=false
spring.datasource.username = root
spring.datasource.password = root
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
## This is important
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = validate
Please change spring.datasource.username and spring.datasource.password as per your MySQL installation.
The property spring.jpa.hibernate.ddl-auto is important. It tries to validate the database schema according to the entities that you have created in the application and throws an error if the schema doesn’t match the entity specifications.
Creating a Domain Entity
Let’s create a simple Entity in our application so that we can create and test flyway migration for this entity.
First, Create a new package called domain inside com.example.flywaydemo package. Then, create the following User.java file inside com.example.flywaydemo.domain package –
package com.example.flywaydemo.domain;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
@Column(unique = true)
@Size(min = 1, max = 100)
private String username;
@NotBlank
@Size(max = 50)
private String firstName;
@Size(max = 50)
private String lastName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Creating a Flyway Migration script
Flyway tries to read database migration scripts from classpath:db/migration folder by default.
All the migration scripts must follow a particular naming convention – V<VERSION_NUMBER>__<NAME>.sql. Checkout the Official Flyway documentation to learn more about naming convention.
Let’s create our very first database migration script. First, Create the db/migration folder inside src/main/resources directory –
mkdir -p src/main/resources/db/migration
Now, create a new file named V1__init.sql inside src/main/resources/db/migration directory and add the following sql scripts –
CREATE TABLE users (
id bigint(20) NOT NULL AUTO_INCREMENT,
username varchar(100) NOT NULL,
first_name varchar(50) NOT NULL,
last_name varchar(50) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY UK_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Running the Application
When you run the application, flyway will automatically check the current database version and apply any pending migrations.
Run the app by typing the following command in terminal –
mvn spring-boot:run
When running the app for the first time, you’ll see the following logs pertaining to Flyway, which says that it has migrated the schema to version 1 – init.
How does Flyway manage migrations?
Flyway creates a table called flyway_schema_history when it runs the migration for the first time and stores all the meta-data required for versioning the migrations in this table.
mysql> show tables;
+-----------------------+
| Tables_in_flyway_demo |
+-----------------------+
| flyway_schema_history |
| users |
+-----------------------+
2 rows in set (0.00 sec)
You can check the contents of flyway_schema_history table in your mysql database –
It stores the current migration’s version, script file name, and checksum among other details in the table.
When you run the app, Flyway first validates the already applied migration scripts by calculating their checksum and matching it with the checksum stored in the meta-data table.
So, If you change V1__init.sql after the migration is applied, Flyway will throw an error saying that the checksum doesn’t match.
Therefore, for doing any changes to the schema, you need to create another migration script file with a new version V2__<NAME>.sql and let Flyway apply that when you run the app.
Adding multiple migrations
Let’s create another migration script and see how flyway migrates the database to the new version automatically.
Create a new script V2__testdata.sql inside src/main/resources/db/migration with the following contents –
INSERT INTO users(username, first_name, last_name)
VALUES('fusebes', 'Yaniv', 'Levy');
INSERT INTO users(username, first_name, last_name) VALUES('flywaytest', 'Flyway', 'Test');
If you run the app now, flyway will detect the new migration script, and migrate the database to this version.
Open mysql and check the users table. You’ll see that the above two entries are automatically created in the users table –
mysql> select * from users;
+----+------------+-----------+------------+
| id | first_name | last_name | username |
+----+------------+-----------+------------+
| 4 | Yaniv | Levy | fusebes |
| 5 | Flyway | Test | flywaytest |
+----+------------+-----------+------------+
2 rows in set (0.01 sec)
Also, Flyway stores this new schema version in its meta-data table –