- Spring offers a
container
(calledSpring application context
) that creates and manages application components (calledbeans
). This is a pattern calleddependency injection
- How to guide the
Spring application context
to wirebeans
together- XML-based configuration
- Java-based configuration
- automatic configuration
autowiring
: Spring automatically inject the components with the other beans that they depend oncomponent scanning
:Spring can automatically discover components from an application's classpath and create them as beans in theSpring application context
Spring Boot
:an extension of Spring Framework that offer many productivity enhancementsautoconfiguration
: based on entries in the classpath, env variables, and other factors, Spring Boot can make reasonable guess of what components need to be wired and configured together
- start a project
- starter dependencies: DevTools, Thymeleaf, Web
- client send a request with a specific path, Spring will associate the specific path with a specific handler class. The handler class will run all
@MethodAttribute
function to get aorg.springframework.ui.Model
object and run the specific request's function (e.g.@PostMapping
), which will return the view's name and bringorg.springframeworkui.Model
to the view - For request handler,
- return a string without prefix, this string is a filename under the
resources\templates
and the browser will render the view - return a string with prefix
redirect: ...
, it will run the code of...
related handler method
- return a string without prefix, this string is a filename under the
- job of MVC:
- Model (controller's member function): defined in Controller class with
@org.springframework.web.bind.annotation.ModelAttribute
- view (html file under
/static
): render data to HTML using template such as avaServer Pages (JSP), Thymeleaf, FreeMarker, Mustache, and Groovy-based templates- template: HTML with some additional element attributes that guide a template in rendering request data.
- controller (java class): handle HTTP requests
- hand off a request to a view to render HTML (browser-displayed)
- write data directly to the body of a response (RESTful)
- Model (controller's member function): defined in Controller class with
- Controller
- Class-Level annotation
lombok
:@org.springframework.stereotype.Controller
: Spring will discover it through component scanning and automatically create an instance ofDesignTacoController
as a bean in the Spring application context (aka container)@org.springframework.web.bind.annotation.RequestMapping("/design")
: In this case, it specifies thatDesignTacoController
will handle requests whose path begins with/design
org.springframework.web.bind.annotation.SessionAttributes("tacoOrder)
: theTacoOrder
object that is put into themodel attribute
in theDesignTacoController
should be maintained in session
- Method-Level annotation (the methods are
controller
's methods)org.springframework.web.bind.annotation.GetMapping
: whenGET
request happen, the function will activateorg.springframework.web.bind.annotation.PostMapping
: whenPOST
request happen, the function will activate- For
Post
handler method, you could have parameters- Form object parameter (without annotation
@ModelAttribute
) : When the form is submitted, the fields in the form are bound to properties of a Taco object (whose class is shown in the next listing) that’s passed as a parameter into processTaco() - Model Attribute parameter (with annotation
@ModelAttribute
): refer to the object (usually@SessionAttributes(...)
) inside theorg.springframework.ui.Model
container org.springframework.web.bind.support.SessionStatus
: by settingsessionStatus.setComplete()
, we close the session and clear the cached SessionAttributes, for this case,TacoOrder
- Form object parameter (without annotation
- when you want to redirect to another handler, pls return with prefix
redirect:...
, otherwise it will try to locate the file underresources\templates
with filename equal to the return value
- For
@org.springframework.web.bind.annotation.ModelAttribute
: when a request happen, the function will activate and it want to add tuple (data name, data value) toorg.springframework.ui.Model
- add many attributes at same time:
model.addAttribute(type.toString().toLowerCase(), filterByType(ingredients, type))
- add one attribute at one time:
@ModelAttribute(name="tacoOrder")
+ the decorated function return model value
- add many attributes at same time:
- Class-Level annotation
- View
th:object
: (i.e. the name of form-backing bean) the name Spring MVC gives to objects that model a form’s fields and provide getter and setter methods that will be used by the framework for establishing and obtaining the values input by the user at the browser side- [
th:field
]: (could be private) field of the above form object *{...}
: selection expressions, just like variable expressions, except they will be executed on a nearestth:object
instead of the whole context variables map (i.e.Model Attributes
)- [
th:text
]: replace the text value inside the tag - [
th:each="ingredient : ${sauce}"
]: replicate the tag and its children in a for loop. In this case, we create aingredient
variable for the children tag, and it is one element ofModel["sauce"]
- If there is no
action
inside the<form>
, when the form is submitted, the browser will gather all the data in the form and send it to the server in an HTTP POST request to the same path for which aGET
request displayed the form—the/design
path - When the form is submitted, the fields in the form are bound to properties of a
Taco
object (whose class is shown in the next listing and without annotationModelAttribute
) that’s passed as a parameter into@PostMapping processTaco()
- Converter:
- a class
- that implements Spring's
Converter
interface and implements itsconvert()
method to take one value and convert it to another - that is annotated with
@Component
: Spring will automatically use the converter when the conversion of request parameters to bound properties is needed - used when converting
<form>
'sth:field
from typeString
to another type of the form object's field with the same name
- that implements Spring's
- a class
- Validate:
- 1st: import dependencies:
spring-boot-starter-validation
- 2nd: add annotation on domain objects, e.g. (
javax.validation.constraints.NotNull
,javax.validation.constraints.Size
,javax.validation.constraints.NotBlank
)[https://jakarta.ee/specifications/bean-validation/3.0/apidocs/], (org.hibernate.validator.constraints.CreditCardNumber
)[https://docs.jboss.org/hibernate/validator/5.1/api/org/hibernate/validator/constraints/package-summary.html] - 3rd: prefix
@Valid
on POST request handler function's form parameter- normally, you will also add
org.springframework.validation.Errors
parameter
- normally, you will also add
- 4th: in Thymeleaf html, add
<span th:if="${#fields.hasErrors('ccNumber')}" th:errors="*{ccNumber}">cc Num Error</span>
- 1st: import dependencies:
-
Reading and Writing data to JDBC database
- 1st: add
Id
andTimestamp
to domain object - 2nd: add dependencies for
JdbcTemplate
:spring-boot-starter-jdbc
- 3rd: add dependencies for embedded/external database: e.g.
com.h2database
- 4th: rename
resources\application.properties
toresources\application.yml
, and add- spring: datasource: generate-unique-name: false name: tacocloud
- the database URL will be
jdbc:h2:mem:tacocloud
- Spring Boot DevTools enables the database at http://localhost:8080/h2-console
- 5th: create new repo interface
IngredientRepository
- 6th: implement above interface with class
JdbcIngredientRepository
@Repository
will make Spring auto discover it@Autowired
on constructor: enables you to inject the object (jdbcTemplate
) dependency implicitly@Transactional
: any failure causes the entire operation to roll back to its previous state and to re-throw the original exception- READ:
jdbcTemplate.query(...,...)
- UPDATE/INSERT:
jdbcTemplate.update(...)
- 7th: create
src/main/resources/schema.sql
to define schema of table with name same as the domain object- If there’s a file named schema.sql in the root of the application’s classpath, then the SQL in that file will be executed against the database when the application starts
- 8th: create
src/main/resources/data.sql
- Spring Boot will also execute a file named data.sql from the root of the classpath when the application starts
- Here jdbcTemplate is not ORM. The domain object fields do not have relationship with the schema of database If you check the database
Taco
, the schema has no relationship withTaco
class jdbcTemplate
andjdbcOperation
worked as db connection.jdbcTemplate
: callquery(String, ...)
jdbcOperation
: callupdate(PreparedStatementCreator, KeyHolder)
and you can get the automatic generated value for theIdentity
field in sql table
- 1st: add
-
Working with Spring Data JDBC (one of the Spring Data projects - I guess it is ORM. to be confirmed)
- 1st: add Spring Data JDBC to the build
- 2nd: define repository interfaces
- extend
org.springframework.data.repository.CrudRepository< param1, param2 >
param1
: object to be persistedparam2
: type of ID
- extend
- 3rd: annotate the domain object
- annotate the class with
@Table
- annotate the properties with
@Id
- preload data with
CommandLineRunner
- this is a SAM interface and a lambda function could be the interface's object
- How to implement in Spring? As the return value of a function annotated with
@Bean
underTacocloudApplication.java
- How it works? At application start, Spring will wire up (i.e. create) all the Beans. For
@Bean
function, Spring will run it and store its returned value in application context. Then in the application context, it will first run theCommandLineRunner
object'srun
function
- solve issue
Caused by: org.springframework.dao.IncorrectUpdateSemanticsDataAccessException: Failed to update entity [Ingredient(id=FLTO, name=Flour Tortilla, type=WRAP)]; Id [FLTO] not found in database
- annotate the class with
-
Working with Spring Data JPA (no need to write schema.sql)
- add dependencies:
spring-boot-starter-data-jpa
- annotate the domain as entities:
- annotations over domain class
@jakarta.persistence.Entity
@lombok.NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
- force = true: the Lombok-generated constructor setting properties to a default value of null, 0, or false
@lombok.AllArgsConstructor
@lombok.RequiredArgsConstructor
- annotations over ID property:
@jakarta.persistence.GeneratedValue(strategy = GenerationType.AUTO)
@jakarta.persistence.Id
- annotations over List<...> properties:
@ManyToMany()
: declare the relationship between an object (e.g. Taco) and its List property (e.g. List), a Taco can have many Ingredient, and one Ingredient can be in many Taco@OneToMany(cascade=CascadeType.ALL)
: TacoOrder::List, one TacoOrder object can have many Taco objects, but one Taco object belong to only one TacoOrder. For the Cascade, if CascadeType.All, then if you delete one TacoOrder, you will delete all the Tacos related
- declare JPA repositories
- similar to JDBC
- define interface extends
CrudRepository< object type, ID type >
- customize repositories
- naming Convention: < verb, e.g. find >By< field name e.g. deliveryZip >< predicate e.g. Between, IsNotNull... >And< field name >< predicate > (args...)
- [
@Query( {JPA Query} )
] over a customized function name
- annotations over domain class
- add dependencies:
-
Tips:
-
Solved Error:
Failed to transfer...
by link -
You can always using method reference or lambda function to implement SAM(Single Abstract Method) interface
-
you can use
orElse(null)
underOptional
object -
Persist: Data persistence is the collective set of mechanisms that allow you to save ("persist") your data somewhere before it evaporates from memory when you turn the power off
-
we
autowired
(DI) the interface rather than implementation why?
ToDo
- I do not quite understand templates/design.html's
<input th:field="*{ingredients}" type="checkbox" th:value="${ingredient.id}"/>
The part ofth:field
make me confusing. Let me know how it is rendered to html when continuing reading - How to do live reload debug?