(This are my notes taken from the book Spring in Action 5th Edition)
Spring parts
Spring coreProvides the core container, dependency injection framework, Spring MVC (Rest APIs), Spring’s web framework and support for template-based JDBC and reactive programming with Spring WebFlux.Spring bootStarter dependencies and autoconfiguration.Spring dataProvides the ability to define your application’s data repository as Java interfaces. Works with relational (JPA), document (Mongo) and graph (Neo4j) databases.Spring securityAuthentication, authorization and API security.Spring integration & Batchhelps integrate with other applicationsSpring cloudhelps with microservices
Project structure
src/main/java
ApplicationStarter.java
src/test/java
ApplicationStarterTest.java
src/main/resources
static
templates
application.properties
schema.sql
data.sql
mvnw.cmd
pom.xml
mvnw.cmdmaven wrapper scripts. Used to build the project even without maven installed on your computerstaticfolder to place images, stylesheets, js resources to serve to the browsertemplatesfolder to place template files that will be used to render content to the browserschema.sql&data.sqlthey will be executed on start. Useful to create the structure of databases and fill them with data.
Main’s config
@SpringBootApplication is used at the main jar’s Starter as in
@SpringBootApplication
public class ApplicationStarter {
// main
}
It’s a composition Tag which contains another three
@SpringBootConfigurationDesignates the class as a configuration one. is a specialized form of@Configuration@EnableAutoConfigurationEnables Spring to auto-configure any components that it thinks you may need@ComponentScanIt lets you use another annotations such as@Componentto declare Beans
Web requests
The basic structure is an application which will be started with the embedded tomcat. A controller marked with @Controller tag and a @GetMapping("/") tag, which returns the name of the Thymeleaf view to give to the browser.
Controller class. It handles HTTP Requests and either gives it to a view to return HTML or writes data directly to the body (RESTful)
basic controller
@Controller
@RequestMapping("/design")
public class MyController {
@GetMapping("/")
public String home() {
return "home"; // view name
}
}
Thymeleaf will automatically search for /templates/home.html and return it as the view if it exists.
view controller
For a @Controller which is used just to redirect a path to a view and it doesn’t adds any kind of input to the Model, we may implement it with just a single a line at a WebConfig file.
The example implemented on top would be better as the following
@Configuration
public class WebConfig implements WebMvcConfig {
@Override
public void addViewControllers(final
ViewControllerRegistry registry) {
registry.addViewController("/")
.setViewName("home");
}
}
request mapping tags
@RequestMapping(method = RequestMethod.GET)general tag prior to Spring 4.3@GetMappinghandlesHTTP GETrequest@PostMappinghandlesHTTP POSTrequest@PutMappinghandles …@DeleteMapping@PatchMapping
thymeleaf
(See thymeleaf notes)
Full Validation Example
Java’s validation API works with Spring and Hibernate, this last adds a bunch of annotations to use and may work together between Model, View and Controller.
declare validation rules
This is done at the model.
@Data
public class Taco {
@NotNull
@Size(min=5,
message="must be at least 5 chars long")
private String name;
@Size(min=1,
message="must choose at least 1 ing")
private List<String> ingredients;
}
We also have other tags such as
@NotBlank(message="name is required")
private String name;
@CreditCardNumber(message="card not valid")
private String ccNumber;
@Pattern(regexp="here comes the regex",
message="must be MM/YY")
private String ccExpiration;
@Digits(integer=3, fraction=0,
message="invalid CVV")
private String ccCVV;
activate validation
This is done at the controller w. @Valid.
@PostMapping
public String process(@Valid
@ModelAttribute("design") final Taco design,
final Errors errors)
{
if(errors.hasErrors()) {
return "design";
}
// do something
return "viewX";
}
If an error is found, the details of this error will be loaded into the Errors object
access the errors
This is done at the view.
<form method="POST" th:object="${design}">
<div th:if="${#fields.hasErrors()}">
<p class="validationError">
Please, correct following problems
</p>
</div>
<div>
<h3>Name your Taco creation:</h3>
<span class="validationError"
th:if="${#fields.hasErrors('name')}"
th:errors="*{name}">Placeholder</span>
<br/>
<input type="text" th:field="*{name}"/>
<button>Submit your taco</button>
</div>
</form>
all broken together
- The tags
@NotNulldeclare what’s wrong and when an Error should be thrown. - At the controller with the line
@Valid @ModelAttribute("design") final Taco designwe declare what the model is and we assign it anid = design. - At the view form:
- we access this model with
th:object="${design}" - we check if the form has any error(s) with
th:if="${#fields.hasErrors()}"to show a general error - we check a specific field with
th:if="${#fields.hasErrors('name')}"and access the error message withth:errors="*{name}"to overwrite the placeholder.
Thisnameis a field of the model we gave. At this exampleTacohas aprivate String namefield which is the one we’re checking here.
- we access this model with
## Store Model attributes in a HTTP session (cache) This is used to store values which have to be used in between requests.
The @SessionAttributes is used in a @Controller class and whose value matches the one in @ModelAttribute.
When a request comes in it checks if there’s an existing value in the HTTP session, if it does, it retrieves it directly without calling the method again.
@Controller
@RequestMapping("/design")
@SessionAttributes("ingredients")
public class Controller {
@ModelAttribute("ingredients")
public List<Ingredient> addIng(final Model model) {
final List<Ingredient> ings = // ...
// first time this will be executed
// second time it will retrieve the value from the cache
return ings;
}
}
Databases
JDBC (Java DataBase Connection)
(This is the old way without Spring JPA. Do not implement this).
Its support is rooted at JdbcTemplate.java. It provides a mean for developers to perform SQL operations against a relation database without that much boilerplate code.
Working with JdbcTemplate
Dependencies to add:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>
spring-boot-starter-jdbc
</artifactId>
</dependency>
For every Bean to support, we should have an interface and an implementation of it, which will delegate all the operations to JDBC.
public interface TacoRepository {
public Taco save(final Taco taco);
}
@Repository
public class JdbcTacoRepository
implements TacoRepository {
@Autowired
private final JdbcTemplate jdbc;
@Override
public Taco save(final Taco taco) {
// ...
final PreparedStatementCreator psc = new
PreparedStatementCreatorFactory(
"insert into Taco (name, createdAt)
values (?, ?)",
Types.VARCHAR, Types.TIMESTAMP
).newPreparedStatementCreator(
Arrays.asList(taco.getName(),
new Timestamp(taco.getCreatedAt().getTime())));
final KeyHolder kh =
new GeneratedKeyHolder();
this.jdbc.update(psc, kh);
return keyHolder.getKey().longValue();
}
}
The code is hard to read, and it still requires quite an amount of boilerplate for just an operation.
Spring Data JPA
Spring Data JPA is not a JPA Provider. It allows us to automatically create repositories removing the most boilerplate possible, as opposed to using only a JPA Provider (Hibernate). It adds an extra layer of abstraction on top of it.
It’s comprised of several subsprojects
- Spring data JPA - JPA persistence to a relational database
- Spring Data MongoDB - against a Mongo document DBB
- Spring Data Redis - against a Redis key-value store
(etc)
Annotations
A JPA entity needs the tags @Entity and @Id.
Then, @GeneratedValue marks we rely on the database to generate the id.
@ManyToMany indicates relationship between Entities.
@PrePersist calls a method just before it’s persisted.
@Table specifies the database table name where our object’s information will be saved.
@Entity
@Table(name="TacoCustom")
public class Taco {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Date createdAt;
// ...
@ManyToMany(targetEntity = Ingredient.class)
@Size(min = 1,
message = "Must select at least 1 ingredient")
private List<Ingredient> ingredients;
@PrePersist
private void createdAt() {
this.createdAt =
Date.valueOf(LocalDate.now());
}
}
Repository
The main advantage of using Spring JPA vs JDBC, is we don’t need to implement repositories’ methods. We just create an interface, which extends CrudRepository<Object,ID> and Spring will write the implementation at runtime.
This is the whole implementation for a Taco repository and we may use directly its parent’s methods.
public interface TacoRepository
extends CrudRepository<Taco, Long> {
List<Taco> findAllByCreatedAt(
final Date createdAt);
}
Repository methods
Spring is able to parse the name of the previous method, to generate the SQL statement, which will retrieve the info we need. Another example of this would be
List<Order> findByDeliveryZipAndPlacedAtBetween(deliveryZip,
date1, date2);
There’s a list of valid operators at page 82.
The alternative to this for larger queries would be to use @Query tag which may use SQL
@Query("Order o WHERE o.deliveryCity='Atlanta'")
List<Order> findWhatever();