Building an MVC Application with Spring Framework: A Beginner's Tutorial

View all articles

Java is often said to be too complicated and to take too long to build simple applications. Nonetheless, Java provides a stable platform with a very mature ecosystem around it, which makes it a wonderful option for developing robust software.

The Spring Framework, one of the many powerful frameworks in the Java ecosystem, comes with a collection of programming and configuration models with a goal to simplify the development of performant and testable applications in Java.

Spring Framework

In this tutorial, we will take the challenge of building a simple application that will act as a database of software developers using Spring Framework and the Java Persistence API (JPA).

The application follows a standard MVC architecture. It will have a controller (ContractsController class), views (based on Thymeleaf templates), and a model (a Java map object). For the sake of simplicity, we will use an in-memory database behind JPA to persist data while the application is running.

Getting Started with the Spring Framework Tutorial

To build a Spring based application, we will need to use one of the following build tools:

  • Maven
  • Gradle

In this tutorial, we will use Maven. If you are not familiar with either of these tools, an easy way to get started is to download the Spring Tool Suite. The suite is dedicated for Spring Framework, and comes with its own Eclipse based IDE.

In Spring Tool Suite, we create a new project by selecting “Spring Starter Project” from under the “File > New” menu.

Spring Framework Tutorial

Once a new project has been created, we will need to edit the Maven configuration file, “pom.xml”, and add the following dependencies:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-commons</artifactId>
</dependency>

These listed dependencies will load Spring Boot Web, Thymeleaf, JPA, and H2 (which will serve as our in-memory database). All necessary libraries will be pulled automatically.

Entity Classes

To be able to store information about developers and their skills, we will need to define two entity classes: “Developer” and “Skill”.

Both of these are defined as plain Java classes with some annotations. By adding “@Entity” before the classes, we are making their instances available to JPA. This will make it easier to store and retrieve instances from the persistent data store when needed. Additionally, the “@Id” and “@GeneratedValue” annotations allow us to indicate the unique ID field for the entity and have its value generated automatically when stored in the database.

As a developer can have many skills, we can define a simple many-to-many relationship using the “@ManyToMany” annotation.

Developer

@Entity
public class Developer {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private long id;
	private String firstName;
	private String lastName;
	private String email;
	@ManyToMany
	private List<Skill> skills;

	public Developer() {
		super();
	}

	public Developer(String firstName, String lastName, String email,
			List<Skill> skills) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
		this.skills = skills;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	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;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public List<Skill> getSkills() {
		return skills;
	}

	public void setSkills(List<Skill> skills) {
		this.skills = skills;
	}

	public boolean hasSkill(Skill skill) {
		for (Skill containedSkill: getSkills()) {
			if (containedSkill.getId() == skill.getId()) {
				return true;
			}
		}
		return false;
	}

}

Skill

@Entity
public class Skill {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;
    private String label;
    private String description;

    public Skill() {
		super();
    }

    public Skill(String label, String description) {
		super();
		this.label = label;
		this.description = description;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}
    
}

Repositories

With JPA we can define a very useful DeveloperRepository interface and SkillRepository interface, which allow for easy CRUD operations. These interfaces will allow us to access stored developers and skills through simple method calls, such as:

  • “respository.findAll()”: returns all developers
  • “repository.findOne(id)”: returns developer with given ID

To create these interfaces, all we need to do is extend the CrudRepository interface.

Developer Repository

public interface DeveloperRepository extends CrudRepository<Developer, Long> {

}

Skill Repository

public interface SkillRepository extends CrudRepository<Skill, Long> {
	public List<Skill> findByLabel(String label);
}

Functionality for the the additional method “findByLabel “declared here will be provided automatically by JPA.

Controller

Next, we can work on the controller for this application. The controller will map request URIs to view templates and perform all necessary processing in between.

@Controller
public class DevelopersController {

	@Autowired
	DeveloperRepository repository;

	@Autowired
	SkillRepository skillRepository;

	@RequestMapping("/developer/{id}")
	public String developer(@PathVariable Long id, Model model) {
		model.addAttribute("developer", repository.findOne(id));
		model.addAttribute("skills", skillRepository.findAll());
		return "developer";
	}

	@RequestMapping(value="/developers",method=RequestMethod.GET)
	public String developersList(Model model) {
		model.addAttribute("developers", repository.findAll());
		return "developers";
	}

	@RequestMapping(value="/developers",method=RequestMethod.POST)
	public String developersAdd(@RequestParam String email, 
						@RequestParam String firstName, @RequestParam String lastName, Model model) {
		Developer newDeveloper = new Developer();
		newDeveloper.setEmail(email);
		newDeveloper.setFirstName(firstName);
		newDeveloper.setLastName(lastName);
		repository.save(newDeveloper);

		model.addAttribute("developer", newDeveloper);
		model.addAttribute("skills", skillRepository.findAll());
		return "redirect:/developer/" + newDeveloper.getId();
	}

	@RequestMapping(value="/developer/{id}/skills", method=RequestMethod.POST)
	public String developersAddSkill(@PathVariable Long id, @RequestParam Long skillId, Model model) {
		Skill skill = skillRepository.findOne(skillId);
		Developer developer = repository.findOne(id);

		if (developer != null) {
			if (!developer.hasSkill(skill)) {
				developer.getSkills().add(skill);
			}
			repository.save(developer);
			model.addAttribute("developer", repository.findOne(id));
			model.addAttribute("skills", skillRepository.findAll());
			return "redirect:/developer/" + developer.getId();
		}

		model.addAttribute("developers", repository.findAll());
		return "redirect:/developers";
	}

}

Mapping of URIs to methods is done via simple “@RequestMapping” annotations. In this case, every method of the controller is mapped to a URI.

The model parameter of these methods allows data to be passed to the view. In essence, these are simple maps of keys to values.

Each controller method either returns the name of the Thymeleaf template to be used as view, or a URL in a specific pattern (“redirect:”) to redirect to. For example, the methods “developer” and “_developersList_” returns the name of a template, while “developersAdd” and “developersAddSkill” return URLs to redirect to.

Within the controller, the “@Autowired” annotations automatically assigns a valid instance of our defined repository in the corresponding field. This allows access to relevant data from within the controller without having to deal with a lot of boilerplate code.

Views

Finally, we need to define some templates for the views to be generated. For this we are using Thymeleaf, a simple templating engine. The model we used in controller methods is available directly within the templates, i.e. when we enter a contract into “contract” key in a model, we will be able to access the name field as “contract.name” from within the template.

Thymeleaf contains some special elements and attributes that control generation of HTML. They are very intuitive and straightforward. For example, to populate the contents of a span element with the name of a skill, all you need to do is define the following attribute (assuming that the key “skill” is defined in the model):

<span th:text="${skill.label}"></span>

Similarly to set the “href” attribute of an anchor element, the special attribute “th:href” can be used.

In our application, we will need two simple templates. For clarity, we will skip all style and class attributes (namely Bootstrap ones) here in the embedded template code.

Developer List

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head> 
	<title>Developers database</title> 
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
	<h1>Developers</h1>
	<table>
		<tr>
			<th>Name</th>
			<th>Skills</th>
			<th></th>
		</tr>
		<tr th:each="developer : ${developers}">
			<td th:text="${developer.firstName + ' ' + developer.lastName}"></td>
			<td>
				<span th:each="skill,iterStat : ${developer.skills}">
					<span th:text="${skill.label}"/><th:block th:if="${!iterStat.last}">,</th:block>
				</span>
			</td>
			<td>
				<a th:href="@{/developer/{id}(id=${developer.id})}">view</a>
			</td>
		</tr>
	</table>
	<hr/>
	<form th:action="@{/developers}" method="post" enctype="multipart/form-data">
		<div>
			First name: <input name="firstName" />
		</div>
		<div>
			Last name: <input name="lastName" />
		</div>
		<div>
			Email: <input name="email" />
		</div>
		<div>
			<input type="submit" value="Create developer" name="button"/>
		</div>
	</form>
</body>
</html>

Developer Details

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<title>Developer</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
	<h1>Developer</h1>
	Name: <b th:text="${developer.firstName}" /> <b th:text="${developer.lastName}" /><br/>
	Email: <span th:text="${developer.email}" /><br/>
	Skills:
		<span th:each="skill : ${developer.skills}">
			<br/>&nbsp;&nbsp;<span th:text="${skill.label}" /> - <span th:text="${skill.description}" />
		</span>
	<form th:action="@{/developer/{id}/skills(id=${developer.id})}" method="post" enctype="multipart/form-data" >
		<select name="skillId">
			<option th:each="skill : ${skills}" 
				th:value="${skill.id}" 
				th:text="${skill.description}">Skill</option>
		</select>
		<input type="submit" value="Add skill"/>
	</form>
</body>
</html>

Running the Server

Spring contains a boot module. This allows us to start the server easily from command line as a command line Java application:

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    DeveloperRepository developerRepository;

    @Autowired
    SkillRepository skillRepository;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Since we are using an in-memory database, it makes sense to bootstrap the database with some predefined data at launch. That way we will have at least some data in the database when the server is up and running.

@Override
public void run(String... args) throws Exception {
	Skill javascript = new Skill("javascript", "Javascript language skill");
	Skill ruby = new Skill("ruby", "Ruby language skill");
	Skill emberjs = new Skill("emberjs", "Emberjs framework");
	Skill angularjs = new Skill("angularjs", "Angularjs framework");

	skillRepository.save(javascript);
	skillRepository.save(ruby);
	skillRepository.save(emberjs);
	skillRepository.save(angularjs);

	List<Developer> developers = new LinkedList<Developer>();
	developers.add(new Developer("John", "Smith", "john.smith@example.com", 
			Arrays.asList(new Skill[] { javascript, ruby })));
	developers.add(new Developer("Mark", "Johnson", "mjohnson@example.com", 
			Arrays.asList(new Skill[] { emberjs, ruby })));
	developers.add(new Developer("Michael", "Williams", "michael.williams@example.com", 
			Arrays.asList(new Skill[] { angularjs, ruby })));
	developers.add(new Developer("Fred", "Miller", "f.miller@example.com", 
			Arrays.asList(new Skill[] { emberjs, angularjs, javascript })));
	developers.add(new Developer("Bob", "Brown", "brown@example.com", 
			Arrays.asList(new Skill[] { emberjs })));
	developerRepository.save(developers);
}

Conclusion

Spring is a versatile framework that allows building MVC applications. Building a simple application with Spring is quick and transparent. The application can also be integrated with a database easily using JPA.

The source code of this entire project is available on GitHub.

About the author

Stefan Varga, Slovakia
member since October 29, 2014
Stefan is a qualified software developer with a computer science degree and nearly a decade of experience in the tech industry. He has founded startups, worked on eCommerce and B2B projects, and more. He is an efficient, reliable individual, and will be an asset to any development team. [click to continue...]
Hiring? Meet the Top 10 Freelance Spring Developers for Hire in September 2016

Comments

Carlos De Luna Saenz
You are mixing two concepts (patterns) in order to create a good example of MVC app, but a bad app... DAO is not a Model object... you should make the example with the @Service annotation in order to create the Business Model and that Services Object to access to the DAO entity, convert them to a DTO and use them in the view... that's the right way to do it. That's IMHO... #ICanAlwaysBeWorng
Langley
He is missing a Service Object (Business Layer), but that's out of the scope of the post, as the tittle says its only meant to talk about MVC, missing that can let you focus on what you really want to know when you are reading about MVC. A couple of observations about your comments: he is not using DAOs as Models, he doesn't even have DAOs, Repositories are Spring's attempt to get rid of the DAOs and he is not using Repositories as Model Objects, he's loading entities from the repository and settings those as the value of one property of the model, you can see this model object as the DTO you are looking for, you just don't have a class defined for it. DTOs are not required everytime, just when you do too many calls to the server, sometimes its counter effective, sometimes you really require one value from a request and that's it.
Carlos De Luna Saenz
DAO = Data Access Objects... every Entity object that access data from a database, a repository or, in this case, a "in-memory database" is a DAO. I think is important to get the Serervice Object as the "scope of this post" since MVC includes the concept of MODEL and model has to do with the business target of the implementation (that's why is called business model)... but agin #ICanAlwaysBeWrong, if thw porpouse was to show Dependency Injection instead the MVC framework such a concept... then is an excelent post. IMHO
Langley
There's a Business Logic ( Business Layer usually implemented as Services objects ), and there's the Business Model, this model is in the Data Layer in the Backend. And there's another model in the front end, which is the model MVC refers to. This model can be the same as the one in the backend or it can be a different one. Say that you are building a Contacts Front End app which grabs contacts from all your accounts, the back end model of facebooks refers to contacts as friends, the backend model of twitter refers to contacts as followers or w/e, but you might only want one type of contact in your app so you map them all to the same type "Contact". They are different Layers and they can or cannot share the model, none of them is the best option, it depends on the type of app you are doing. The DTOs are usually used in a Rest Architecture and the whole MVC is been done in the client side. Instead of building out the model of one view doing 10 requests for different kind of entities, you ask for all the info in one single DTO and you build the model out of that DTO. Technologies like JSP, JSF, Thymeleaf etc have the MVC logic in the server side, they compile the view and they respond with plain html, so any calls done to the services are done from the server to the server, no need to use DTOs (Data Transfer Object, Client <--> Server ). http://martinfowler.com/eaaCatalog/dataTransferObject.html
HERLANI JUNIOR
A running and good sample of Spring + Hibernate + GenericDAO implementation and Freemaker is available @ https://github.com/herlanijunior/sample-java-spring-genericdao
Carlos De Luna Saenz
Now i'm pretty sure you have a "Patterns missconception" ... i invite you to read the GoF book http://www.uml.org.cn/c++/pdf/DesignPatterns.pdf (the parent of all patterns) and review http://corej2eepatterns.com/ to get the full map of JEE patterns... TO (Original name of DTO) has nothing to do with "REST Services" (Or at least not "only") TO is a good practice for ANY solution no matter the size of it... if after reviewing that documentation you still believe i'm wrong... then #IamGuilty... #IRestMyCase
Langley
Very damn Good recommendation, I have that book in physical and I'm almost done through it =) From the same j2ee link you sent me: Transfer Object Assembler "You want to minimize the network calls to remote objects when building a data representation of the business-tier object model. You want to create a complex model to hand over to the client for presentation purposes." Server <--> Client I did not say it was only for Rest communication, I said it was common in that Architecture. Another good example is when you create a Java server app , and separate you have a java client server app that consumes the server one. The client app, needs to request the REMOTE server app for information, you can reduce the number of requests by having the server reduce lots of information about different entities in one single DTO. Again, in the example shown in this post the server is building the page before sending it to the client. model.addAttribute("developer", repository.findOne(id)); model.addAttribute("skills", skillRepository.findAll()); In the former lines he's getting different kind of resources and inserting them into the Model object. This model object is used to compile the thymeleaf template into an HTML page that will be sent to the client, the client will never know about the business logic used to build the page (which is not a bad thing).
Stefan Varga
Hello, you are right, the example does not follow jee patterns. We can discuss where it is necessary to use DAOs and where it is not, but I wanted to show another point. That is - you can create an application quickly in java as well as others do in other languages, languages more popular in startup world. Many people complain that coding in java is cumbersome, you have to write a lot of 'unnecessary' code, even simple change in an attribute involves other changes etc.. but maybe it does not have to be this way, especially when you are coding an early version of an app for a startup. Later you can reuse a lot of code in a more robust version.
Stefan Varga
True. As much as I remember, very simplified, the DTOs are good for being easy to store and restore. When you decouple server into front-end and back-end. Running them on different servers etc.. completely unnecessary for small sites.
Carlos De Luna Saenz
Transfer Object Assembler != Data Transfer Object... as i mention, pattern missconception.
Langley
Transfer Object Assembler is the pattern that builds Data Transfer Objects.... they are used together must of the time, but if you want to you can read the Transfer Object instead and you'll find the exact same: "You want to reduce remote requests across the network. You want to avoid network performance degradation caused by chattier applications that have high network traffic." That already is telling you when you want to use them, there is no such thing as "a good practice for ANY solution". He is not doing REMOTE requests for each of the entities, the server is building the whole model himself.......
Carlos De Luna Saenz
DTOs (or TOs) had to be with the simplicity of sharing data among tiers, layers or to decouple any part of the app... the point of using them has to be with lot of things that has to be with security (no one outside DAOs changes the persisted or that the data sended or received are just the one needed with any other complexity than the attributes)... i must say that is completly necessary on small sites as well in order to have an scalable wel architected and secure solution. A good architecture will have to be at least on 3 different layers and none of them should be able to "talk" to any non adjacent layer, to share data among layers TOs (DTOs) should be used...
Carlos De Luna Saenz
I must agree that you CAN create things quickly... but mention (may be just mention) about good practices to create more robust solutions should be always in mind... one of the main problems with "other platforms" such as .NET or PHP are not "technology related" but architectural related... when you "compact" application layers and you don't decouple Business Logic in a completly independent layer (no matter if is Java, .NET, PHP or any other) you will have to refactor you solution sooner or later and in most of the cases you will have to make a complete rewritten from scratch. And many people will blame the technologie used instead of the use of antipatters.
Carlos De Luna Saenz
"That already is telling you when you want to use them, there is no such thing as "a good practice for ANY solution"." After that... there's nothing more to discuss....
Langley
Yea I don't think the discussion will lead anywhere, but I'll return the favor you did to me and recommend you a good book: http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420 That's like the one you showed me but with Patterns for Enterprise Applications, its written by Martin Fowler, an eminence in the programming world, and he talks about the DTO which is a Pattern used in Enterprise Applications like the title says, btw not every app is an Enterprise Application.
Raphael Ribeiro
The Add Skills button takes you in a loop when will the part after the if condition get executed in the developerAddSkill function
Sandro Agboka
Hi Stefan! My first question will be this one. Is it necessary to have the spring-data-commons dependency? If yes why ?
Sandro Agboka
Hi Stefan!!! I have another question if you mind. I can seet that you use @ManyToMany annotation on the Developer class but you have not used any on the Skill class; but you say that you are modeling a ManyToMany relationship. Why is that? Can you explain me the reason of this choice.
taruni nandu
Thanks for the post... Useful for everyone who wants to meet their success in it. We IT Hub Online Training are good in giving the <a href="http://www.ithubonlinetraining.com/spring-online-training/">Spring Online Training</a>.
Олег Абражаев
Dependencies should be with versions <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>1.3.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>1.3.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>1.3.3.RELEASE</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.191</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> <version>1.11.4.RELEASE</version> </dependency>
Abhisek Jana
Created a tutorial on Spring boot. Check it out here. http://www.adeveloperdiary.com/java/spring-boot/an-introduction-to-spring-boot/
Дмитрий Первухин
H2 is autowired by Spring Boot without any additional configuration. If you prefer to use another database, remove H2 from pom, add your database dependency, and make standart Spring config - see Boot docs on spring.io for details.
comments powered by Disqus
Subscribe
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering and design posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant technologies
About the author
Stefan Varga
JavaScript Developer
Stefan is a qualified software developer with a computer science degree and nearly a decade of experience in the tech industry. He has founded startups, worked on eCommerce and B2B projects, and more. He is an efficient, reliable individual, and will be an asset to any development team.