(This are my notes taken from the book Spring in Action 5th Edition)
Spring parts
Spring core
Provides 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 boot
Starter dependencies and autoconfiguration.Spring data
Provides the ability to define your application’s data repository as Java interfaces. Works with relational (JPA), document (Mongo) and graph (Neo4j) databases.Spring security
Authentication, authorization and API security.Spring integration & Batch
helps integrate with other applicationsSpring cloud
helps 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.cmd
maven wrapper scripts. Used to build the project even without maven installed on your computerstatic
folder to place images, stylesheets, js resources to serve to the browsertemplates
folder to place template files that will be used to render content to the browserschema.sql
&data.sql
they 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
@SpringBootConfiguration
Designates the class as a configuration one. is a specialized form of@Configuration
@EnableAutoConfiguration
Enables Spring to auto-configure any components that it thinks you may need@ComponentScan
It lets you use another annotations such as@Component
to 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@GetMapping
handlesHTTP GET
request@PostMapping
handlesHTTP POST
request@PutMapping
handles …@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
@NotNull
declare what’s wrong and when an Error should be thrown. - At the controller with the line
@Valid @ModelAttribute("design") final Taco design
we 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.
Thisname
is a field of the model we gave. At this exampleTaco
has aprivate String name
field 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();