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

Conquest of Spring Boot

Published in the Random EN group
Good day, dear reader! And it's nice to meet you, even if the main reason to look into the modest topic about the first acquaintance with the development on Spring Boot was the pompous name. I would like to share my experience of completing the introductory task for an internship on the CodeGym portal, presenting a review from a completely ordinary student of a technical university who wants to test the accumulated knowledge for strength. Spring Boot Conquest - 1In no way do I deny the possible presence of rudeness in the attached code or the method of thinking, and I welcome constructive criticism, because it is thanks to “bumps and abrasions” that I manage to develop in a professional direction. Moreover, I do not at all pretend to be a “panacea” in solving the set conditions and I deliberately omit individual fragments of the program, leaving the entry into a relatively complex topic without the slightest consequences for the nervous system as the key value. It’s true, it’s reckless to deny the obvious: it was hard for me and absolutely nothing was clear until a certain point. 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 Thymeleafandquery- requests to the local MySQL server to filter the incoming array of information. So let's get started!

springboot. What kind of animal and how to cook it?

In short and concise, a great tool from Pivotel for saving precious 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 proposals.
Spring Boot Conquest - 2
Following the terms of reference put forward, we will use the gentleman's set, which is standard for creating a simple web application, taking into account the use of the MySQL database :
  • WEB is the main component for developing a web application, including the local Apache Tomcat server at the standard address localhost:8080 and the Spring MVC universal framework.

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

  • JPA - the technology is required to work with databases and provides an 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) - a templating engine for rendering an application; due to relative familiarity with the principles of html, he chose Thymeleaf, which put forward 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 the usual architecture of a web application with directories ready for further filling. Fragments for interacting with the visual part, whether it be CSS graphic styles, standard HTML pages or JavaScript functionality, should be located in “resources”, and the back-end component, respectively, is meant to be placed in “java”. Separately, you should 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 ones, then you should manipulate between tags <dependencies></dependencies>according to a similar method.
Spring Boot Conquest - 3

First steps towards a great future

Then a rather interesting and quite logically justified question arises: “What to do now? How will it work? The program is built on the principles of Model-View-Controller: organizes the reading of entities from the connected database (Model) and is displayed in the user interface with controls (View); communication between components and the execution of actions according to the transmitted requests is carried out thanks to the Controller. It is the creation of key elements that serves as a reference point for continuing development. In order to avoid a slippery slope and maintain the respect of comrades in the labor 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 the workplace tidy.

Essence is a small detail in a big mechanism

Or otherwise - our Model according to the conditions set in the task. Deviating from the topic of discussion and returning to the introductory draft, we can confidently assert that there are minimal differences between the tasks and adhere to the average concept in the further review. Let's say notes in a notebook that include:
  • Identification number to determine the location in the general stream;
  • Text message of a certain number of characters;
  • The date the user added to the general list;
  • Boolean variable to define "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 better understanding of what is happening from a theoretical position. Communication between beans in Spring is defined by annotations, - special pointers in front of objects, each of which performs a certain role in the mechanism and begins with the “@” symbol. The @Entity annotation tells Spring Boot that the subsequent class data belongs to the “Entity”, and @Id and @GeneratedValue set the selected field with an identifier with automatic iterator generation when processing an array of information. I specifically miss the addition of standard Getter and Setter to increase the compactness of the visual format. Further, taking into account the use of the database for storing records, we proceed to the next step in the development of the application: we will create the NoteRepository interface in the “repository” directory, which is a connecting element in the exchange chain, and inherit the repository most suitable for further work, indicating the stored entity and integer iterator to access.
public interface NoteRepository extends JpaRepository<Note, Integer> {
}
Actually, everything. Briefly and concisely. Now Spring Boot will use the created bean to organize interactions with the database. There are relatively many types of legacy repositories with varying potential for possible action. JpaRepository is at the top of the ladder and has the most potential, including downstream CrudRepository and PageAndSortingRepository. We will no longer go deep and deviate from the topic, because some subtleties can be found on the Pivotel website in the technical documentation. Now, after implementing the data image and specifying the communication methods on the application side, you need to pay attention to the creation of the MySQL database in the appropriate external environment “MySQL Workbench”, pre-installed on the desktop platform in the assembly from the official developer with additional packages for creating a local server:
Spring Boot Conquest - 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 schema 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 insistently require attention to successfully achieve the desired result:
  • There is no separate Boolean type as such. Any request handling action 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 marrow of your bones, you will have to limit yourself to only the position in the calendar.
Spring Boot Conquest - 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 bolt” icon on the toolbar. Now, if the addition of information went well, we can confidently return to our native IDE for further development, adding the configuration of the current database 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 particular 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 be the main stumbling block without the slightest theoretical or practical knowledge.” To be frank, the front-end component took up an astonishing amount of work and was sure to ruffle nerves over a long period of time. But thanks to its surprising simplicity, Thymeleaf managed to find a suitable compromise after a series of enchanting defeats. Further discussion will focus specifically on the intricacies of using the chosen engine, although the general concept adheres to a similar position. The main technique is to be able to use the purest HTML and assemble the final display from separate fragments to avoid multiple repetition of identical sections. Suppose the user interface 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 the direction of increasing (ASC) or decreasing (DESC) value. Let's take as a standard position the display of all records in ascending order. According to the hierarchical policy of the selected template engine, the composite elements of the visualization should be located on the “templates” branch in the “resources” directory. Therefore, further manipulations with the components take into account the conditions put forward. Let's create a main page with the name "index" (or any other according to personal preferences) on the html5 template. For example: consisting of a navigation panel 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 the direction of increasing (ASC) or decreasing (DESC) values. Let's take as a standard position the display of all records in ascending order. According to the hierarchical policy of the selected template engine, the composite elements of the visualization should be located on the “templates” branch in the “resources” directory. Therefore, further manipulations with the components take into account the conditions put forward. Let's create a main page with the name "index" (or any other according to personal preferences) on the html5 template. For example: consisting of a navigation panel 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 the direction of increasing (ASC) or decreasing (DESC) values. Let's take as a standard position the display of all records in ascending order. According to the hierarchical policy of the selected template engine, the composite elements of the visualization should be located on the “templates” branch in the “resources” directory. Therefore, further manipulations with the components take into account the conditions put forward. Let's create a main page with the name "index" (or any other according to personal preferences) on the html5 template. For example: return to the main page) and a dynamic table for displaying entities sorted by the time the note was added in the direction of increasing (ASC) or decreasing (DESC) values. Let's take as a standard position the display of all records in ascending order. According to the hierarchical policy of the selected template engine, the composite elements of the visualization should be located on the “templates” branch in the “resources” directory. Therefore, further manipulations with the components take into account the conditions put forward. Let's create a main page with the name "index" (or any other according to personal preferences) on the html5 template. For example: return to the main page) and a dynamic table for displaying entities sorted by the time the note was added in the direction of increasing (ASC) or decreasing (DESC) values. Let's take as a standard position the display of all records in ascending order. According to the hierarchical policy of the selected template engine, the composite elements of the visualization should be located on the “templates” branch in the “resources” directory. Therefore, further manipulations with the components take into account the conditions put forward. Let's create a main page with the name "index" (or any other according to personal preferences) on the html5 template. For example: the composite elements of the visualization must be located on the “templates” branch in the “resources” directory. Therefore, further manipulations with the components take into account the conditions put forward. Let's create a main page with the name "index" (or any other according to personal preferences) on the html5 template. For example: the composite elements of the visualization must be located on the “templates” branch in the “resources” directory. Therefore, further manipulations with the components take into account the conditions put forward. Let's create a main page with the name "index" (or any other according to personal preferences) on the 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 component of the final application. Thymeleaf uses a separate syntax for specifying the application of procedures and begins with the keyword “th:”, a link to the library with which it is mandatory to link in the opening <html> tag.
<div th:if="${not #lists.isEmpty(notes)}">
The "if" operation is exactly the same as usual and checks the incoming "notes" attribute for objects to display. We should separately mention the topic overlapping using the Controller, taking into account the use of this for organizing the interaction of the model and visualization. Many foggy moments take shape in the future, just go back if you have the desire.
<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 seen in the example. We copy the snippet called “notebook” from “list.html” in the “operations” directory to the <div></div> of the “index” file, completely replacing the content in the final destination. Outgoing 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 revisit the constructive review and go through the Thymeleaf functions used in order, omitting the standard HTML syntax or graphic styles used, and focusing specifically on understanding the templating engine mechanism.
<div th:fragment="notebook">
The "fragment" operation sets the name of the fragment and opens up the possibility of using the contents of the block for the "replace" command. And! By no means excludes multiple use within the same page, again bringing forward the analogy with procedures or functions in programming languages.
<a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}">
The call to the @PostMapping annotation in the Controller is used with the mapping “/sort/{sortDate}”, where {sortDate}, is the outgoing attribute of the sort direction. Something similar can be observed 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 usual use of the for block in Java syntax: the “note” variable takes the current element from the array of the incoming ${notes} attribute - an array with entities - and is used later to change values. To be frank, I could dedicate a separate article to listing the wide range of Thymeleaf features with examples of practical use - the template engine is extremely simple and does not require learning an impressive baggage of additional syntax at all. The functions described above are set out in the technical documentation on the official website of the developers and play a key role in organizing communication with the back-end. Therefore, you can confidently move on to the next and final part. Of course,

Controller, administrator in a small company

“The cornerstone in the architecture of a web application” - perhaps there is no way to better describe the importance of the Controller component in the organization of the program: most of the operations are carried out by the connecting element between the model and the view. Thanks to the Spring Boot action mechanics, you can confidently use mapping and GET / POST request methods without the slightest problem, automatically connect the data repository. Let's create the NoteController class in a separate file in the "controllers" directory, again referring to the application 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 close look can see an important change in the principles of application architecture, associated with the addition of a service to isolate business logic from work with the database management service. The actions performed are required to increase the versatility of the finished product and provide ample scope for changing the functionality of the user interface without the need for changes in the methods of communication with the database. The standard view is in no way distinguished from the general mass 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 the overview of the controller and analyze the intricacies of organizing work using Spring Boot methods. The @Autowired annotation indicates the need to automatically bind the service to the specified variable of the appropriate type and establishes a connection with the database. More attention should be paid to the way of communicating with the view, which is annotated with the @GetMapping("/") annotation and returns a page named "index" when it receives a call to localhost:8080. You can take a different approach, either by specifying the @RequestMapping(value = "/", method = RequestMethod.GET) extended description, or by replacing the return type with a ready-made ModelAndView. However, according to the current state of practical experience, I do not notice any cardinal differences in the final result and use the usual version. Let's expand the controller by adding new elements using an additional tab. After the user clicks on the navigation bar element, @GetMapping("/new") is called and redirected to the “new” page from the “operations” directory, returning a parameter with the name “message” when confirming the entered data using the button and redirecting to the main block. A separate mention requires the need for a complete match of the name of the variable in the input window with the name of the transferred value. returning a parameter with the name "message" when confirming the entered data using the button and redirecting to the main block. A separate mention requires the need for a complete match of the name of the variable in the input window with the name of the transferred value. returning a parameter with the name "message" when confirming the entered data using the button and redirecting to the main block. A separate mention requires the need for a complete match of the name of the variable in the input window with the name of the transferred value.
<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 is passed from the url string, the “note” attribute is added with a record for further editing. @RequestParam(value = "done", required = false) boolean done) specifying a specific value is key to using the checkbox when using the Thymeleaf templating 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:/";
}
Deleting items from the database is extremely simple and requires no fancy manipulation at all, calling the appropriate service function with the value passed in:
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
   service.deleteNote(id);
   return "redirect:/";
}
Now let's make small adjustments to the ready-made fragments and move on to an exciting conversation with MySQL using query queries in Spring Data JPA, separately adding a simple filtering control function 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;
}

Such a small, but such an important Query.

It is embarrassing to admit that the filtering of values, contrary to expectations, turned out to be another stumbling block in the fulfillment of the terms of reference, confidently overcoming the complexity threshold set by pagination - splitting the data array into separate pages of a certain dimension for further display. Most likely, accumulated fatigue affected, but ... insight descended after a completely random collision with Query queries.
public interface NoteRepository extends JpaRepository<Note, Integer> {
   List<Note> findAllByOrderByDateAsc();
   List<Note> findAllByOrderByDateDesc();
}
Spring Data JPA provides the ability to create custom database queries that eliminate the need to sort information upon retrieval and have a wide range of applications. For example:
List<Note> findAllByOrderByDateAsc();
The method will be converted into an SQL query and display all records (findAll) sorted (byOrder) by date (byDate) in ascending order (Asc). Moreover, you can make complex combinations and select many fields for a single requirement. Let's 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 appropriate icon. Spring Boot will build the program on Apache Maven and install the local Apache Tomcat server at localhost:8080. Now you just need to follow the link in any browser.
Spring Boot Conquest - 6
And, of course, develop a methodology to meet the rest of the business requirements. The potential of the application is limited by the effort, resourceful initiative and imagination of the developer.
Spring Boot Conquest - 7
Being frank and paying attention to the path traveled, again and again I am convinced of the correctness of the chosen direction and I realize the benefits of learning on the CodeGym educational portal. Thanks to many practical tasks, it was possible to return the alluring interest in the study of programming, which was finally clogged in an outdated and surprisingly boring program of a higher educational institution of a similar direction. Four months of active study of material in the back-end stack of technologies invested much more knowledge compared to 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 thanks to overcoming obstacles that we become better and develop professionally and personally. Hope, a little story contributed to the discovery of fresh ideas for using an amazing tool called SpringBoot. PSGithub .
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION