JavaRush /Java Blog /Random EN /Conquest Spring Boot
Surplus
Level 37
Москва

Conquest Spring Boot

Published in the Random EN group
Good day, dear reader! And it’s nice to meet you, even if the pompous name was the main reason to look into the modest topic about the first acquaintance with Spring Boot development. I would like to share my experience of completing the introductory assignment for an internship on the JavaRush portal, presenting an overview from the side of a completely ordinary technical university student who wants to test the strength of his accumulated knowledge. Conquest Spring Boot - 1In no way do I deny the possible presence of rudeness in the attached code or thinking method, and I welcome constructive criticism, because it is thanks to “bumps and bruises” that it is possible to develop in a professional direction. Moreover, I do not at all pretend to be a “panacea” in solving the given conditions and deliberately omit individual fragments of the program, leaving the key importance of entering into a relatively complex topic without the slightest consequences for the nervous system. It’s true, it’s reckless to deny the obvious: it was hard for me and absolutely nothing was clear until a certain moment. And if you experience similar feelings from the first meeting with the task, then “Welcome!” Let's write a web application in Spring Boot using a simplified analogy of an internship entrance test using a template engine Thymeleafand queryqueries to a local MySQL server to filter the incoming array of information. So let's get started!

Spring Boot. What kind of animal is it and how to cook it?

In short and concisely, it is an excellent tool from Pivotel for saving valuable time in the process of creating an application, eliminating the need to directly connect third-party libraries, write an impressive mapping canvas and servlets. It is enough to use the Spring Initializr builder , integrated into IntelliJ IDEA Ultimate Edition (File - New - Project... - Spring Initializr) or located on the start.spring.io web service , specifying packages to include from a wide range of offers.
Conquest Spring Boot - 2
Following the technical specifications put forward, we will use the gentleman’s set, standard for creating a simple web application using the MySQL database :
  • WEB is the main component for developing a web application, including a local Apache Tomcat server at the standard address localhost:8080 and the universal Spring MVC framework.

  • DevTools - used to quickly restart an application in a hot JVM when changes are detected in the compiled code or templates; Moreover, it frees Thymeleaf from clearing cache if the selected engine is included in the project.

  • JPA is a technology required for working with databases and provides object-relational mapping of Java objects, provides an API ( Hibernate in our case) for managing, saving and retrieving entities.

  • Thymeleaf (Mustache, AngularJS, Vaadin and beyond) - template engine for application visualization; Thanks to my relative familiarity with the principles of html, I chose Thymeleaf, which pushed the language to the cornerstone of the world.

  • MySQL - connects Java Database Connectivity drivers to execute SQL queries against the database.
After the final selection of components and creation, we get an ordinary web application architecture with directories ready for further filling. Fragments for interacting with the visual part, be it CSS graphic styles, standard HTML pages or JavaScript functionality, should be located in “resources”, and the back-end component, accordingly, is meant to be placed in “java”. We should also pay attention to the pom.xml file in the root range, which stores the project structure and dependencies between components. If you want to further expand the functionality with additional packages or remove unnecessary things, you should perform manipulations between tags <dependencies></dependencies>according to a similar method.
Conquest Spring Boot - 3

First steps into a great future

Next, a rather interesting and quite logical question arises: “What to do now? How will this work? The program is built on the principles of Model-View-Controller: it organizes the reading of entities from the connected database (Model) and is displayed in the user interface with controls (View); communication between components and execution of actions according to transmitted requests is carried out thanks to the Controller. It is the creation of key elements that serves as the reference point for continued development. In order to avoid a slippery slope and maintain the respect of your comrades in the work field, you should place the components in the appropriate directories (for example, place the Controller file in the controllers folder in the “java” branch) and carefully keep order in the workplace.

Essence is a small part in a big mechanism

Or in other words, our Model according to the conditions set in the problem. Departing from the topic of discussion and returning to the introductory project, we can confidently assert that there are minimal differences between the tasks and adhere to the average concept in further review. Let's say, notes in a notebook, including:
  • Identification number to determine location in the general flow;
  • A text message of a certain number of characters;
  • The date the user added it to the general list;
  • A Boolean variable to determine “Done or not done” (“Read or not read”).
Therefore, let's create a Note class in a directory called “entity” and add the appropriate fields:
@Entity
public class Note {

   @Id
   @GeneratedValue
   private int id;
   private String message;
   private Date date;
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}
Another deviation from the topic of discussion for a greater understanding of what is happening from a theoretical position. The connection between components in Spring is specified by annotations - special pointers in front of objects, each of which plays a specific role in the mechanism and begins with the “@” symbol. The @Entity annotation indicates to Spring Boot that subsequent class data belongs to the “Entity”, and @Id and @GeneratedValue specify the selected field as an identifier with automatic generation of an iterator when processing an array of information. I deliberately omit adding standard Getter and Setter to increase the compactness of the visual format. Next, taking into account the use of a database for storing records, we move on to the next step in application development: we will create the NoteRepository interface in the “repository” directory, a connecting element in the exchange chain, and inherit the most suitable repository for further work, indicating the stored entity and integer iterator to access.
public interface NoteRepository extends JpaRepository<Note, Integer> {
}
Actually, that's all. Brief and concise. Now Spring Boot will use the created component to organize interactions with the database. There are relatively many types of legacy repositories with varying potential for action. JpaRepository is at the top of the ladder and has the most potential, including the CrudRepository and PageAndSortingRepository below it. We won’t go any further and deviate from the topic, because some of the subtleties can be found on the Pivotel website in the technical documentation. Now, after implementing the data image and specifying communication methods on the application side, you need to pay attention to creating a MySQL database in the appropriate external environment “MySQL Workbench”, pre-installed on the desktop platform in an assembly from the official developer with additional packages for creating a local server:
Conquest Spring Boot - 4
Next, following the instructions of the environment after clicking on the icon with the current local server in the main window, we create a table diagram according to the fields of our entity (Note) and fill it with the appropriate data. It is necessary to separately clarify the subtleties of the MySQL dialect, which urgently require attention to successfully achieve the desired result:
  • There is no separate Boolean type as such. Any request processing actions will convert “true” or “false” to the bit value “1” or “0”, respectively;
  • The date is stored entirely in the Timestamp type. If you use Date, which is familiar to the core, you will have to limit yourself to only the position in the calendar.
Conquering Spring Boot - 5
After the final completion of the preparatory steps, we indicate “MySQL Workbench” to send data to the local server by clicking on the “lightning” icon on the toolbar. Now, if adding information was completed correctly, we can confidently return to our native IDE to continue development by adding the current database configuration to application.properties (usually located in the “resources” directory):
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
And finally binding the Note entity to MySQL using annotations. @Table indicates the use of a table with the selected name and schema, and @Column indicates that the variables belong to a specific field.
@Entity
@Table(name = "test", schema = "test", catalog = "")
public class Note {

   @Id
   @GeneratedValue
   private int id;
   @Column(name = "message")
   private String message;
   @Column(name = "date")
   private Date date;
   @Column(name = "done")
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}

View or user interface

Alas, we can safely say the following: “Visualization of the application will become the main stumbling block without the slightest theoretical or practical knowledge.” To be frank, the front-end component took up an amazing amount of the total amount of work and confidently frayed my nerves over a long period of time. But thanks to the amazing simplicity of Thymeleaf, it was possible to find a suitable compromise after a series of enchanting defeats. Further discussion will be about the intricacies of using the selected engine, although the general concept adheres to a similar position. The main technique is the ability to use the purest HTML and assemble the final display from individual fragments to avoid multiple repetitions of identical sections. Let's assume the UI architecture consists of a main page consisting of a navigation bar with controls (adding a new entry, returning to the main page) and a dynamic table for displaying entities sorted by the time the note was added in an ascending (ASC) or decrementing (DESC) direction. meanings. Let us take as standard the display of all records in ascending order. According to the hierarchical policy of the selected template engine, the component visualization elements should be located on the “templates” branch in the “resources” directory. Consequently, further manipulations with components take into account the put forward conditions. Let's create a main page with the name “index” (or any other name according to personal preference) on an html5 template. For example:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/head :: head"></head>
<body>
<div class="container">
   <div th:replace="fragments/header :: header"></div>
   <div th:if="${not #lists.isEmpty(notes)}">
       <div th:replace="operations/list :: notebook"></div>
   </div>
   <div th:replace="fragments/footer :: footer"></div>
</div>
</body>
</html>
And so, let’s break down the key components of the final application. Thymeleaf uses a separate syntax to indicate the use of procedures and begins with the keyword “th:”, the link to the library with which is necessarily included in the opening <html> tag.
<div th:if="${not #lists.isEmpty(notes)}">
The “if” operation is completely no different from the usual way of doing things and checks the incoming “notes” attribute for the presence of objects for further display. It is worth mentioning separately the overlap of the topic with the use of Controller, taking into account its use for organizing the interaction of the model and visualization. Many vague moments take shape in the future, just go back if you want.
<head th:replace="operations/list :: notebook"></head>
The “replace” operation indicates the replacement of a “stub” or an active block with a selected fragment from the current or separate page - the latter case is clearly observed in the example. We copy the fragment called “notebook” from “list.html” of the “operations” directory into the <div></div> of the “index” file, completely replacing the content in the final destination. The outgoing one has the following content:
<!DOCTYPE html>
<!--suppress ALL -->
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">

<div th:fragment="notebook">
   <table class="table table-bordered table-hover horizontal-align">
       <thead>
       <tr>
           <th style="width: 5%">#</th>
           <th style="width: 60%">Message</th>
           <th class="dropdown" style="width: 20%">Date
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}"><i class="fa fa-chevron-circle-up"></i></a>
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'DESC')}"><i class="fa fa-chevron-circle-down"></i></a>
           </th>
           <th style="width: 5%">Done</th>
           <th style="width: 5%">Edit</th>
           <th style="width: 5%">Delete</th>
       </tr>
       </thead>
       <tbody>
       <tr th:each="note : ${notes}">
           <td th:text="${note.id}" style="text-align: center">#</td>
           <td th:text="${note.message}">Message</td>
           <td th:text="${#dates.format(note.date, 'EEE, d MMM yyyy HH:mm')}" style="text-align: center">Date</td>
           <td style="text-align: center">
               <i th:if="${note.done} == true" class="fa fa-plus-square-o" style="font-size:20px;color:#337ab7"></i>
               <i th:if="${note.done} == false" class="fa fa-minus-square-o" style="font-size:20px;color:#337ab7"></i>
           </td>
           <td style="text-align: center"><a th:href="@{'/edit/{id}'(id=${note.id})}"><i class="fa fa-edit" style="font-size:20px"></i></a></td>
           <td style="text-align: center"><a th:href="@{'/delete/{id}'(id=${note.id})}"><i class="fa fa-trash" style="font-size:20px"></i></a></td>
       </tr>
       </tbody>
   </table>
</div>
</html>
Let's return to the constructive overview and go through the Thymeleaf functions used in order, omitting the standard HTML syntax or graphic styles used, and focusing specifically on understanding the template engine mechanism.
<div th:fragment="notebook">
The “fragment” operation specifies the name of the fragment and makes it possible to use the contents of the block for the “replace” command. Moreover! Multiple uses within a single page are in no way excluded, again bringing forward the analogy with procedures or functions in programming languages.
<a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}">
A call to the @PostMapping annotation is used in the Controller with the mapping “/sort/{sortDate}”, where {sortDate} is the outgoing sort direction attribute. Something similar can be seen in the following block, which adds a dynamic change depending on the position of the user-selected element in the iteration loop:
<a th:href="@{'/edit/{id}'(id=${note.id})}">
<tr th:each="note : ${notes}">
Enumerating values ​​is very similar to the familiar use of a for block in Java syntax: the variable “note” takes the current element from the input attribute array ${notes}—an array of entities—and is used to change the values ​​later. To be frank, we could dedicate a separate article to listing the wide range of Thymeleaf capabilities with examples of practical application - the template engine is extremely simple and does not require learning at all an impressive baggage of additional syntax. The functions described above are outlined in the technical documentation on the official website of the developers and are of key importance in organizing communication with the back-end. Therefore, you can confidently move on to the next and final part. Of course, by attaching the remaining components of the visualization in a link to the finished application at the end of the article.

Controller, administrator in a small company

“The cornerstone in the architecture of a web application” - perhaps there is no way to find a more accurate description of the importance of the Controller component in organizing the work of the program: most operations are carried out precisely by the connecting element between the model and the view. Thanks to the action mechanics of Spring Boot, you can confidently use mapping and GET/POST request methods without the slightest problem, and automatically connect the data repository. Let's create the NoteController class in a separate file in the “controllers” directory, again referring to the use of the appropriate annotation:
@Controller
public class NoteController {

   private NoteService service;

   @Autowired
   public void setNoteService(NoteService service) {
       this.service = service;
   }

   @GetMapping("/")
   public String list(Model model) {
       return "index";
   }
}
A careful eye may notice an important change in the design of the application architecture associated with the addition of a service to isolate business logic from working with the database management service. The completed actions are required to increase the versatility of the finished product and provide wide scope for changing the functionality of the user interface without the need for changes in methods of communication with the database. The standard representation in no way stands out from the crowd of similar ones: the interface is located in a separate directory and is implemented by a class with the @Service annotation for Spring Boot detection:
public interface NoteService {
   Note getNoteById(Integer id);
   void saveNote(Note note);
   void updateNote(Integer id, String message, boolean done);
   void deleteNote(Integer id);
   List<Note> findAll();
}

@Service
public class NoteServiceImpl implements NoteService{

   private NoteRepository repository;

   @Autowired
   public void setProductRepository(NoteRepository repository) {
       this.repository = repository;
   }

   @Override
   public Note getNoteById(Integer id) {
       return repository.findOne(id);
   }

   @Override
   public void saveNote(Note note) {
       repository.save(note);
   }

   @Override
   public void updateNote(Integer id, String message, boolean done) {
       Note updated = repository.findOne(id);
       updated.setDone(done);
       updated.setMessage(message);
       repository.save(updated);
   }

   @Override
   public void deleteNote(Integer id) {
       repository.delete(id);
   }

   @Override
   public List<Note> findAll() {
       return repository.findAll();
   }
}
Let's return to reviewing the controller and look at the intricacies of organizing work using Spring Boot methods. The @Autowired annotation indicates the need to automatically bind a service to a specified variable of the appropriate type and establish a connection with the database. More attention should be paid to the way the view communicates, indicated by the @GetMapping("/") annotation, which returns a page named “index” when receiving a call to localhost:8080. You can use a different approach, specifying the extended description @RequestMapping(value = "/", method = RequestMethod.GET) or replacing the return type with a ready-made ModelAndView. However, according to the current state of experience in practical application, I do not notice any fundamental differences in the final result and use the usual option. Let's expand the controller by adding new elements using an additional tab. After the user clicks on a navigation bar element, @GetMapping("/new") is called and redirected to the “new” page from the “operations” directory, returning a parameter named “message” when confirming the entered data using the button and redirecting to the main block. The need for a complete match of the variable name in the input window with the name of the transferred value requires special mention.
<input type="text" class="form-control" id="message" th:name="message" placeholder="Enter your note." maxlength="100"/>
@GetMapping("/new")
public String newNote() {
   return "operations/new";
}

@PostMapping("/save")
public String updateNote(@RequestParam String message) {
   service.saveNote(new Note(message));
   return "redirect:/";
}
A similar technique is used to update a record. After clicking on the control, the @GetMapping("/edit/{id}") mapping is called and the identifier from the URL string is transferred, the “note” attribute is added with an entry for further editing. @RequestParam(value = "done", required = false) boolean done) specifying a specific value plays a key role in the use of the checkbox when using the Thymeleaf template engine and is set to “false” by default.
@GetMapping("/edit/{id}")
public String edit(@PathVariable Integer id, Model model) {
   Note note = service.getNoteById(id);
   model.addAttribute("note", note);
   return "operations/edit";
}

@PostMapping("/update")
public String saveNote(@RequestParam Integer id, @RequestParam String message,
                      @RequestParam(value = "done", required = false) boolean done) {
   service.updateNote(id, message, done);
   return "redirect:/";
}
Removing items from the database is extremely simple and does not require any significant manipulation by calling the appropriate service function using the passed value:
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
   service.deleteNote(id);
   return "redirect:/";
}
Now let’s make small adjustments to the finished fragments and move on to exciting communication with MySQL using query queries in Spring Data JPA, separately adding a function for managing simple filtering before closing the Controller.
@Controller
public class NoteController {

   private String sortDateMethod = "ASC";

   @GetMapping("/")
   public String list(Model model) {
       List<Note> notebook = filterAndSort();
       model.addAttribute("notes", notebook);
       model.addAttribute("sort", sortDateMethod);
       return "index";
   }

private List<Note> filterAndSort() {
   List<Note> notebook = null;
   switch (sortDateMethod) {
       case "ASC":
           notebook = service.findAllByOrderByDateAsc();
           break;
       case "DESC":
           notebook = service.findAllByOrderByDateDesc();
           break;
   }
   return notebook;
}

So small, but so important Query.

It’s embarrassing to admit, filtering values, contrary to expectations, turned out to be another stumbling block in fulfilling the technical task, confidently overcoming the complexity threshold established by pagination - breaking up the data array into separate pages of a certain size for further display. Most likely, accumulated fatigue was taking its toll, but... inspiration came after a completely accidental encounter with Query queries.
public interface NoteRepository extends JpaRepository<Note, Integer> {
   List<Note> findAllByOrderByDateAsc();
   List<Note> findAllByOrderByDateDesc();
}
Spring Data JPA provides the ability to create highly granular database queries that eliminate the need to sort information once it is received and have a wide range of application potential. For example:
List<Note> findAllByOrderByDateAsc();
The method will be converted to an SQL query and display all records (findAll) sorted (byOrder) by date (byDate) in ascending order (Asc). Moreover, you can create complex combinations and sample across many fields with a single requirement. Say, select all (findAll) completed (byDoneTrue) records in order (byOrder) decreasing (Decs) by date value (byDate):
Page<Note> findAllByDoneTrueOrderByDateDesc(Pageable pageable);

Conclusion or another confession of a novice programmer

All! You can safely launch the web application using the Shift+F10 combination or by clicking on the corresponding icon. Spring Boot will build the program on Apache Maven and install a local Apache Tomcat server at localhost:8080. Now you just need to follow the link in any browser.
Conquest Spring Boot - 6
And, of course, develop a methodology to fulfill other business requirements. The application's potential is limited by the effort, resourcefulness, and imagination of the developer.
Conquest Spring Boot - 7
Being frank and paying attention to the path traveled, I am again and again convinced of the correctness of the chosen direction and realize the benefits of studying on the JavaRush educational portal. Thanks to a variety of practical tasks, it was possible to return the alluring interest in learning programming, which had been completely suppressed in the outdated and surprisingly boring program of a higher educational institution of a similar direction. Four months of actively studying material in the back-end technology stack invested much more knowledge compared to entire years of attending lectures and laboratory classes. Believe it or not. I wish you not to give in to the difficulties of entering complex material, because it is through overcoming obstacles that we become better and develop professionally and personally. I hope this little story has helped me discover some new ideas for using the amazing tool called SpringBoot. PS Github .
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION