Mastering Spring and MyBatis
Introduction
Mastering Spring and MyBatis
0-1
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Course Description
How is this course valuable to a Full Stack Engineer (FSE)?
Spring supports software development by implementing much of the lower-level work that
would otherwise be the responsibility for programmers to provide.
One of the most important aspects of Spring is that of object (bean) creation and
dependency injection. Based on configuration information, Spring will create objects and
wire them together. This frees the programmer to concentrate more effort on the task of
building software that fulfills the requirements of the project.
MyBatis greatly simplifies the work of communicating with a relational database. Based on
configuration information, which includes the SQL commands to execute, MyBatis takes
care of the details of executing those commands.
Mastering Spring and MyBatis
0-2
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Course Outline
Chapter 1 Introducing the Spring Framework
Chapter 2 Understanding Spring
Chapter 3 Advanced Spring Configuration
Chapter 4 Introduction to MyBatis and Spring
Chapter 5 Working Effectively with MyBatis
Chapter 6 Functional Programming
Mastering Spring and MyBatis
0-3
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Course Objectives
In this course, we will:
Use the Spring framework to build clean, extensible, loosely-coupled enterprise Java
applications
Utilize Spring as an object factory and dependency injection to wire components together
Understand and apply MyBatis to simplify access to relational databases
Explore and apply Spring to simplify the use of MyBatis in an application
Apply transaction strategies via configuration
Mastering Spring and MyBatis
0-4
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Key Deliverables
Course Notes Project Work
SKILL
KNOW-
LEDGE There is a Knowledge Checkpoint for this course
APPLICATION
Mastering Spring and MyBatis
0-5
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mastering Spring and MyBatis
Chapter 1:
Introducing the Spring Framework
Mastering Spring and MyBatis
1-1
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Overview
In this chapter, we will explore:
The Spring Framework and its core areas of functionality
– The Spring Object Factory
– Inversion of Control
– Dependency Injection
Mastering Spring and MyBatis
1-2
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
The Spring Object Factory
XML-Based Factory Configuration
Chapter Summary
Mastering Spring and MyBatis
1-3
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
What Is Spring?
The Spring Framework is an open-source application framework
and inversion of control container for the Java platform*
The major focus of Spring is on simplifying application development
At the core is an object factory—known as the container
Supplemented by extensive support for application development
– Data access
– Web application development—MVC framework
– Aspect-oriented programming
– Transaction control
– Security
– Batch processing
– Much more
*Source: Wikipedia
Mastering Spring and MyBatis
1-4
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
The Spring Object Factory
Spring provides an object factory
– Creates and manages lifecycle of application objects
– Principle is known as Inversion of Control (IoC)
▪ You hand-over control of object creation to Spring
Object factory can also perform Dependency Injection (DI)
– Establish links between objects it creates when dependencies exist
Object factory needs to be configured
– With which objects to create
– Which dependencies to establish
Configuration can be performed with:
– Annotations or
– XML
Mastering Spring and MyBatis
1-5
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Defining Object Dependencies
Consider the following simple plain Java code
– PopupGreeter has a dependency on Visitor
– An interface is used to maintain loose coupling between the greeter and its visitor
visitor field must be initialized before calling greet()
public class PopupGreeter implements Greeter {
private Visitor visitor = …;
How does PopupGreeter
@Override satisfy this dependency?
public void greet() {
String greeting = visitor.getGreeting();
String name = visitor.getName();
JOptionPane.showMessageDialog(null,
greeting + ", " + name);
}
}
Mastering Spring and MyBatis
1-6
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Defining Object Dependencies (continued)
AmarilloVisitor is a JavaBean that public class AmarilloVisitor implements Visitor {
implements Visitor private String name;
private String greeting;
Goal: satisfy PopupGreeter’s dependency
public AmarilloVisitor() {
with an AmarilloVisitor instance this.name = "Tex";
this.greeting = "Howdy";
}
@Override
public String getGreeting() {
return greeting;
}
@Override
public String getName() {
return name;
}
}
Mastering Spring and MyBatis
1-7
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Using Spring’s Object Factory for the PopupGreeter
To use the PopupGreeter class with the AmarilloVisitor, we need to:
1. Create an instance of PopupGreeter
2. Create an instance of the AmarilloVisitor
3. Assign the AmarilloVisitor to the PopupGreeter’s visitor field
Spring’s object factory will perform all these operations for us
– Factory will create the two objects for us – IoC
– Factory will assign the AmarilloVisitor to the visitor field of PopupGreeter – DI
– We never call constructors directly or manage dependencies manually
Objects created by factory are known as Spring-managed beans
To use Spring, we need to:
1. Configure the Spring bean (object) factory
2. Identify Spring-managed beans
3. Instantiate the bean factory
4. Ask the factory for the PopupGreeter
Mastering Spring and MyBatis
1-8
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
1. Configure the Spring Bean Factory
In the XML configuration file, enable annotation-based bean configuration
– <component-scan> element tells Spring which packages to scan for beans
greeter-beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="com.fidelity.greeter" />
</beans>
Enables bean configuration Scan for annotated Can be a comma-separated list
via annotations beans in this package
Mastering Spring and MyBatis
1-9
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
2. Identify Spring-Managed Beans
To identify your Java classes as Spring-managed beans, add annotations to them
@Component is Spring’s primary bean annotation
To identify fields that require dependency injection, add annotations
– @Autowired – Spring-specific
– @Inject – Java EE standard annotation
▪ Requires Java EE JAR file in classpath
– Spring supports DI using either annotation
Mastering Spring and MyBatis
1-10
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Defining Spring-Managed Beans
Annotate Spring-managed beans with @Component
Bean ID is defined by value
passed to @Component Spring matches the type of the bean
@Component("greeter") to the type of the @Autowired field
public class PopupGreeter implements Greeter {
@Autowired
@Component("visitor")
private Visitor visitor;
public class AmarilloVisitor implements Visitor {
...
// etc.
@Autowired tells Spring }
}
to inject a bean in this field
When Spring finds a bean with @Component, it calls its constructor
– Then it looks for @Component beans whose types match fields with @Autowired
– When it finds matching beans, it injects them into the @Autowired fields
– This process continues recursively until all dependencies for all beans are satisfied
Mastering Spring and MyBatis
1-11
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Spring Annotation for Dependency Injection
@Autowired tells Spring where DI is needed
@Autowired can be added to:
@Autowired Inject bean by assigning
– Fields private Visitor visitor; it directly to a field
@Autowired Inject bean by passing it as an
– Setter methods public setVisitor(Visitor v) { … } argument to a setter method
public class PopupGreeter {
– Constructors @Autowired Inject bean by passing it as an
public PopupGreeter(Visitor v) { … } argument to the constructor
Spring will create and inject an object of the right type
– No problem as long as only one Spring-managed bean implements Visitor
Mastering Spring and MyBatis
1-12
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Creating Beans with Bean Factories
Spring provides several factories for managing beans
– All of them implement the BeanFactory interface
▪ org.springframework.beans.factory.BeanFactory
– Configuration for the factory is provided with Java annotations or XML files or both
Any BeanFactory implementation is capable of:
– Instantiating beans (IoC)
– Injecting the bean’s dependencies (DI)
– Providing access to those beans
Many Spring applications often define the factory as ApplicationContext
– org.springframework.context.ApplicationContext
– A subinterface of BeanFactory that adds extra functionality
Each factory is a bean “container”
– Manages the lifecycle of the beans that it creates
Mastering Spring and MyBatis
1-13
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
3. Instantiate the Bean Factory, 4. Get Beans
Instantiate a bean factory by calling a factory constructor
– ClassPathXmlApplicationContext reads configuration from XML file on classpath
– Bean factory creates all beans as soon as it reads its configuration (eager instantiation)
Fetch beans from a factory using its getBean() method
– Requires a bean ID and the bean’s type (class or interface)
Read the configuration file, then scan
for all classes with @Component
BeanFactory factory =
new ClassPathXmlApplicationContext("greeter-beans.xml");
// Use factory to fetch a bean
Greeter g = factory.getBean("greeter", Greeter.class);
g.greet(); ID of bean requested Type of the bean
Use the bean
Mastering Spring and MyBatis
1-14
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
AbstractApplicationContext
For applications deployed in an application server, the bean container shuts down
automatically
– But standalone clients (such as our lab exercises) must close the container explicitly
BeanFactory interface doesn’t define a close() method
– So, we declare the factory as AbstractApplicationContext
– Best practice: use the most general type that provides the required functionality
AbstractApplicationContext factory =
new ClassPathXmlApplicationContext("greeter-beans.xml");
…
Bean factory will clean up all
factory.close(); beans that it is managing
Mastering Spring and MyBatis
1-15
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Other Spring Annotations for Defining Beans
@Component has subinterfaces for beans with specialized roles:
– @Controller – bean associated with the presentation tier or a REST service endpoint
– @Service – bean associated with the business tier (business logic)
– @Repository – bean associated with the integration tier (DAO)
By default, beans are singletons
– Multiple calls to factory.getBean() return a reference to the same bean
Change the bean’s scope with @Scope
– prototype – each call to factory.getBean() returns a reference to a new bean
@Component("paymentProcessor") Default scope is singleton
@Scope("prototype")
public class CreditCardPaymentProcessor implements PaymentProcessor {
Mastering Spring and MyBatis
1-16
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
@Qualifier and @Primary
For DI, there must be only one bean of the required type; otherwise, an exception occurs
– If there are multiple beans of that type, add @Qualifier with a bean ID
@Component("greeter") @Component("delhiVis")
public class PopupGreeter … { public class DehliVisitor implements Visitor {
@Autowired
…
@Qualifier("dehliVis") @Component("dublinVis")
private Visitor visitor; public class DublinVisitor
Inject the Visitor implements Visitor { … }
… with ID dehliVis
Or annotate one of the bean classes with @Primary
Use this bean if there are multiple
@Component("greeter") @Primary Visitor implementations
public class PopupGreeter … { @Component("delhiVis")
@Autowired public class DehliVisitor implements Visitor {
private Visitor visitor;
…
…
No @Qualifier needed
Mastering Spring and MyBatis
1-17
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 1.1: Spring with Annotations
30 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
1-18
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
The Spring Object Factory
XML-Based Factory Configuration
Chapter Summary
Mastering Spring and MyBatis
1-19
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
XML Configuration File
Instead of using annotations, you can define Spring beans in the XML configuration file
Configure a bean with the <bean> element’s attributes
– id – sets the bean’s ID
– class – fully-qualified class name so Spring knows which constructor to call
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
Bean ID passed to
factory’s getBean()
<bean id="greeter" class="com.fidelity.greeter.PopupGreeter" />
<bean id="vis" class="com.fidelity.greeter.AmarilloVisitor" />
</beans>
Mastering Spring and MyBatis
1-20
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Configuring Dependency Injection in XML
To configure DI using JavaBean setter methods, add <property> elements to a bean
– name=property – tells Spring which setter method to call: setProperty(…)
Define setter method arguments using <property> element attributes
– ref – reference to another Spring bean
– value – literal string or number value
<beans …>
<bean id="greeter"
class="com.fidelity.greeter.PopupGreeter"> public class PopupGreeter implements Greeter {
<property name="visitor" ref="vis"/> public void setVisitor(Visitor visitor) { … }
</bean>
Call setVisitor()… … with the bean whose ID is vis as the argument
<bean id="vis"
class="com.fidelity.greeter.AmarilloVisitor"> public class AmarilloVisitor implements Visitor {
<property name="name" value="Joe Bob Springsteen"/> public void setName(String name) { … }
</bean>
Call setName()… … with this string value as the argument
Mastering Spring and MyBatis
1-21
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Using Beans Defined with XML from Factory
Get beans defined using XML exactly as with beans defined using annotations
Read bean configuration file
BeanFactory factory =
new ClassPathXmlApplicationContext("greeter-beans.xml");
// Use factory
Greeter g = factory.getBean("greeter", Greeter.class);
g.greet(); Specify value of <bean>
element’s id attribute
Use the bean
Mastering Spring and MyBatis
1-22
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 1.2: Spring with XML
30 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
1-23
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
How Many Beans Will Be Created?
The getBean() method may:
1. Keep returning the same object each time
– A “singleton”
– One bean per Spring container (BeanFactory instance)
2. Or return a freshly instantiated object each time
– Uses the bean configuration as a “prototype”
– Return a new object for every distinct request made to the factory
The number of beans created is controlled by the scope configuration of the bean
– Available scopes:
We will use only
▪ Singleton and prototype for all Spring applications these two scopes
▪ Request and session for web applications
Mastering Spring and MyBatis
1-24
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Bean Scope Example
Define the scope of a bean with the <bean> element’s scope attribute
– If omitted, singleton is the default
Each call to getBean()
<?xml version="1.0" encoding="UTF-8"?>
creates a new object
<beans …>
<bean id="greeter" class="com.fidelity.greeter.PopupGreeter" scope="prototype">
<property name="visitor" ref="vis"/>
</bean>
<bean id="vis" class="com.fidelity.greeter.AmarilloVisitor">
<property name="name" value="Joe Bob Springsteen"/>
</bean> Default singleton scope
</beans>
Mastering Spring and MyBatis
1-25
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Complete Configuration Using Annotations
You can eliminate the XML configuration file
@Configuration
completely using Java Configuration public class AppConfig {
Define a class with @Configuration @Bean
public Visitor createVisitor(){
– Define factory methods with @Bean return new AmarilloVisitor();
– When Spring needs to inject a bean, it }
examines the return types of your @Bean
@Bean(name="greeter")
methods public Greeter createGreeter(){
– Spring calls the appropriate @Bean method to return new PopupGreeter();
create the bean for injection }
}
Classes of objects returned by @Bean methods
don’t need @Component
– Useful for creating beans from classes you
don’t own
Mastering Spring and MyBatis
1-26
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Annotation-Based Factory Creation
Use your configuration class to create a BeanFactory
– Call AnnotationConfigApplicationContext constructor
– Pass a reference to your configuration class as the constructor argument
Create a bean factory Your configuration class
BeanFactory factory =
new AnnotationConfigApplicationContext(AppConfig.class);
Greeter g = factory.getBean("greeter", Greeter.class);
System.out.println("Got greeter " + g); Fetch beans as usual
g.greet();
Mastering Spring and MyBatis
1-27
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Use Case for Java Configuration: Injecting a Logger
Use Java Configuration to create beans from classes that aren’t Spring beans
– Example: injecting an SLF4J Logger instance BEFORE: Logger isn’t a Spring
bean, so Spring can’t inject it
@Component("greeter") public class PopupGreeter … {
private Logger logger = LoggerFactory.getLogger(getClass());
@Configuration
public class MyJavaConfig { Spring will call this method when
it needs a Logger instance Spring automatically
@Bean
performs injection for the
@Scope("prototype")
@Bean method’s argument
public Logger createLogger(InjectionPoint ip) {
Class<?> classThatWantsALogger = ip.getField().getDeclaringClass();
return LoggerFactory.getLogger(classThatWantsALogger);
} Create a Logger
@Component("greeter") public class PopupGreeter … {
@Autowired
AFTER: Spring calls our @Bean
private Logger logger;
method and injects a Logger
Mastering Spring and MyBatis
1-28
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 1.3: Java Configuration
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
1-29
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
The Spring Object Factory
XML-Based Factory Configuration
Chapter Summary
Mastering Spring and MyBatis
1-30
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Summary
In this chapter, we have explored:
The Spring Framework and its core areas of functionality
– The Spring Object Factory
– Inversion of Control
– Dependency Injection
Mastering Spring and MyBatis
1-31
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Key Points
Spring provides a general-purpose object factory
– Factory performs dependency injection when configured to do so
To use the Spring factory:
1. Configure the factory using annotations or XML or both
2. Create an ApplicationContext
3. Get beans from the ApplicationContext
– Using the id of the bean and its interface type
Mastering Spring and MyBatis
1-32
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mastering Spring and MyBatis
Chapter 2:
Understanding Spring
Mastering Spring and MyBatis
2-1
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Overview
In this chapter, we will explore:
Spring’s dependency injection
Testing with Spring dependency injection
Working with Maps
Mastering Spring and MyBatis
2-2
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Spring and Dependency Injection
Testing with Spring
Working with Maps
Other Dependency Types
Chapter Summary
Mastering Spring and MyBatis
2-3
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Spring Dependency Injection déjà vu
Earlier, we introduced Spring:
Spring provides an object factory
– Creates and manages lifecycle of application objects
– Principle is known as Inversion of Control (IoC)
▪ You hand-over control of object creation to Spring
Object factory can also perform Dependency Injection (DI)
– Establish links between objects it creates when dependencies exist
These features will be illustrated on the following slides
Mastering Spring and MyBatis
2-4
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Injecting Beans Using Annotations
Identify Spring-managed Beans using @Component
– Or the more specific @Controller, @Service, and @Repository
Specify that a dependency needs to be injected
– @Autowired can be on fields, methods, or constructor arguments
Spring will inject an object of the right type
– No problem as long as only one Spring-managed component implements the interface
– Add @Qualifier or @Primary if there could be multiple beans of required type
Can also configure Spring profiles for defining different beans in different environments
▪ Example: different DAO beans for development, test, and production environments
▪ We’ll discuss profiles later
Mastering Spring and MyBatis
2-5
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Setter Injection – What Does Spring Do?
It is helpful to look at a Spring configuration file and think about the equivalent code that
Spring is “writing” behind the scenes
– Example: how does Spring handle the following XML configuration?
<bean id="vis" class="com.fidelity.greeter.AmarilloVisitor" />
<bean id="greeter" class="com.fidelity.greeter.PopupGreeter">
<property name="visitor" ref="vis"/>
</bean>
Triggers call to setVisitor()
This is the code Spring executes: Call constructor to create bean
Visitor vis = new com.fidelity.greeter.AmarilloVisitor();
Greeter g = new com.fidelity.greeter.PopupGreeter(); Call constructor to
create dependent bean
g.setVisitor(vis);
Inject dependency
Mastering Spring and MyBatis
2-6
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Injecting a Simple Value
IoC container can inject many types of values public class AmarilloVisitor implements Visitor {
– Not just other beans private String name;
…
– Although beans are the most common public void setName(String name) {
this.name = name;
Can inject primitives and Strings (“values”): }
}
<bean id="vis" class="com.fidelity.greeter.AmarilloVisitor">
<property name="name" value="Joe Bob Springstein"/>
</bean>
Value injection is available with annotations public class AmarilloVisitor implements Visitor {
– Here, it’s silly: just assign the value instead @Value(value="Joe Bob Springstein")
private String name;
– Later, we’ll see useful examples with the …
Spring Expression Language (SpEL) }
Mastering Spring and MyBatis
2-7
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 2.1: Using Spring as a Factory
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
2-8
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Invoking the Default Constructor
When beans are defined like this:
@Component("greeter")
public class PopupGreeter implements Greeter {
@Autowired
private Visitor visitor;
@Component("visitor")
public PopupGreeter() {} public class DublinVisitor implements Visitor {
…
public DublinVisitor() {}
…
– The BeanFactory uses the default zero-argument constructors to create objects
The DublinVisitor and PopupGreeter classes must have zero-argument constructors
– The fields are set with field injection, not in the constructor
Mastering Spring and MyBatis
2-9
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Defining Beans with Constructor Arguments
The @Autowired annotation also supports constructors
public class PopupGreeter implements Greeter {
private Visitor visitor;
@Autowired No zero-arg constructor
public PopupGreeter(Visitor visitor) {
this.visitor = visitor;
} Spring creates a Visitor first, then
… calls the PopupGreeter constructor
Can apply @Qualifier to the constructor parameters
– To handle parameter conflict or multiple beans with same interface
public class PopupGreeter implements Greeter {
private Visitor visitor;
@Autowired
public PopupGreeter(@Qualifier("amarilloVis") Visitor visitor) {
this.visitor = visitor;
}
…
Mastering Spring and MyBatis
2-10
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Configuring Constructor Arguments with XML
You can also configure constructor arguments in XML
– Use <constructor-arg> element
public class Actor {
<bean id="star" private String name;
class="com.fidelity.dependency.Actor"> private String dailyRateUsd;
<constructor-arg value="Tom Hanks" /> public Actor(String name, int dailyRateUsd) {
<constructor-arg value="192000" /> this.name = name;
this.dailyRateUsd = dailyRateUsd;
</bean> }
<constructor-arg> has attributes ref and value attributes like <property>
If the constructor has multiple arguments, Spring will attempt to match by type
▪ Even if <constructor-arg> elements are out of order
▪ To avoid potentially ambiguous calls, specify the order with the index attribute
<bean id="star" class="com.fidelity.dependency.Actor">
<constructor-arg index="1" value="192000" />
No problem even if
<constructor-arg index="0" value="Tom Hanks" />
arguments are out of order
</bean>
Mastering Spring and MyBatis
2-11
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Fully Configured Beans
Spring will not inject a bean unless the bean is completely configured
– Example: Film bean has a dependency on Budget bean
@Component
public class Film {
private Budget budget;
@Autowired @Component
public Film(Budget budget) { public class Budget {
this.budget = budget; // no reference to a Film
} }
}
Spring analyzes dependencies between beans and creates them in the correct order
– Here, Spring creates the Budget bean first, then passes it to the Film constructor
Mastering Spring and MyBatis
2-12
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Circular Dependency
But what if Budget requires a reference to the Film?
@Component
public class Film {
private Budget budget; @Component
public class Budget {
@Autowired private Film film;
public Film(Budget budget) {
this.budget = budget; @Autowired
} public Budget(Film film) {
} this.film = film;
}
}
Can’t create Film first because its constructor needs a fully configured Budget
– Can’t create Budget first because it needs a Film
Problem: circular dependency
– Spring will throw exception (BeanCurrentlyInCreationException)
Mastering Spring and MyBatis
2-13
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Resolving Circular Dependencies
To avoid circular dependencies:
– Use setter injection or field injection for at least one link in the chain
@Component
public class Film {
private Budget budget;
Constructor injection @Component
public class Budget {
@Autowired @Autowired
public Film(Budget budget) { Field injection
private Film film;
this.budget = budget;
} public Budget() {
} Budget constructor
}
doesn't need a Film
}
Spring will create the Budget bean first, but will postpone autowiring its Film field
– Then Spring passes the Budget bean to the Film constructor
– Finally, Spring injects the initialized Film bean into the Budget field
Mastering Spring and MyBatis
2-14
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Constructor or Setter Injection?
When designing beans, you have three choices
1. Add @Autowired to constructors
2. Add @Autowired to setter methods
3. Add @Autowired to fields
Which one should you choose?
In early releases of Spring, the Spring team recommended using setter injection exclusively
– More flexible, prevents circular dependencies
– Disadvantage: developer may forget to set a required dependency
The Spring team now recommends using constructor injection
– Guarantees all beans are completely initialized
– Disadvantage of constructor injection: potential for circular dependencies
Mastering Spring and MyBatis
2-15
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
When to Use Constructors and Setters
Use constructor injection for:
– “Read-only” objects
▪ Should not be changed after construction
▪ Avoid setters in objects that should be immutable
– Dependencies that are required for bean to function properly
▪ Avoid runtime errors due to misconfiguration
Use setter or field injection for:
– Cases where a circular dependency results
– Mutable objects and dependencies
– Optional dependencies
Decide on a dependency-by-dependency basis
– Some fields can be initialized in a constructor, some in fields, others with setters
Mastering Spring and MyBatis
2-16
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 2.2: Dependency Injection with Constructors
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
2-17
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Spring and Dependency Injection
Testing with Spring
Working with Maps
Other Dependency Types
Chapter Summary
Mastering Spring and MyBatis
2-18
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Unit Testing with Spring
Best practice is to satisfy dependencies manually
– Instantiate objects with new, or
– Create mock dependencies with Mockito and inject with constructor or setters
Spring supplies test support classes to help mimic “Spring-like” behavior in unit tests
– ReflectionTestUtils can set fields or invoke methods (even if private)
BusinessServiceTest.java
class BusinessServiceTest {
BusinessService service;
@Service
public class BusinessService { @BeforeEach Set the field named dao in
@Autowired void setUp() { the testService object
private ProductDao dao; ProductDao testDao = new ProductDaoImpl();
service = new BusinessService();
… ReflectionTestUtils.setField(service, "dao", testDao);
}
Another option is to supply a special testing configuration and use the Spring bean factory
– More suited to integration testing, but sometimes used for unit testing
Mastering Spring and MyBatis
2-19
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Integration Testing Spring with @ExtendWith
Goal: define integration tests of Spring with our application
– Purpose: test the Spring configuration
Spring integration testing setup:
1. Annotate the test class with @ExtendWith(SpringExtension.class)
2. Annotate the test class with the @ContextConfiguration
– Pass in beans.xml file
– If using a @Configuration class, pass it in as the classes parameter
3. Declare beans being tested (typically Service or DAO) as @Autowired fields
In the @Test methods, all beans will be fully configured by Spring
– Autowired beans will be fully initialized
– All dependencies are injected recursively
Mastering Spring and MyBatis
2-20
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Using the Spring TestContext Framework
Instead of this: class BusinessServiceGetBeanTest {
BusinessService service;
AbstractApplicationContext context;
@BeforeEach
void setUp() {
context = new ClassPathXmlApplicationContext("beans.xml");
service = context.getBean(BusinessService.class);
}
@AfterEach
void tearDown() {
context.close();
}
Use this:
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:beans.xml")
class BusinessServiceSpringTest {
@Autowired
BusinessService service;
Or (classes = ApplicationConfig.class)
Mastering Spring and MyBatis
2-21
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 2.3: Integration Testing with Spring
10 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
2-22
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Spring and Dependency Injection
Testing with Spring
Working with Maps
Other Dependency Types
Chapter Summary
Mastering Spring and MyBatis
2-23
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Overview of Collection Framework Interfaces déjà vu
Set
– Holds onto unique values
– Can be used to check for existence of objects
List
– Like arrays, only can grow and shrink
Queue and Deque
– Used to store items for processing, add and remove methods
– Deque allows to add or remove from front and back of container
Maps
– Stores key/value pairs
– Helpful to cache infrequently changed data from files or database
Mastering Spring and MyBatis
2-24
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
HashMap
Each Entry in a Map is a pair
– Key
– Value
HashMap is the most commonly-used map
TreeMap stores data in sorted order
Useful methods of Map:
– put() allows you to add or replace items in the map
– get() allows you to obtain items from the map
▪ A search operation
Maps are commonly used to cache data
– The result of database queries
– Content of files that may be read multiple times
Mastering Spring and MyBatis
2-25
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Using HashMap: An Example
Example: Employee object is the key; Phone object is the value
// set up query etc
Map<Employee, Phone> directory = new HashMap<>();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
…
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
// Create an Employee using rs.getXXXXX()
Employee emp = new Employee(…);
// Create a Phone by the same mechanism
Phone phone = new Phone(…);
directory.put(emp, phone); // elsewhere
} Employee boss = …;
} Phone bossPhone = directory.get(boss);
// etc
Mastering Spring and MyBatis
2-26
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
What Kinds of Objects Are Valid Keys to a Map?
Both Key and Value can be any Object
– String, Employee, Phone, Double, Integer, etc.
– Not primitives such as int, char
▪ Use the corresponding wrapper class: Integer, Char
Keys must not be null
For HashMap, the Key class must override hashCode() and equals()
– The hash code is usually computed from all the fields of an object
For TreeMap, Key class must implement Comparable
– Otherwise, when you create a TreeMap you supply a Comparator that compares Keys
Mastering Spring and MyBatis
2-27
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Converting Map to Other Collections
In a Map<K, V>
– Sometimes we want to work with a whole entry (key and value)
▪ Can use Map.Entry<K, V>
keySet()
– Returns Set<K> containing all the keys (keys must be unique, so this is appropriate)
values()
– Returns Collection<V> containing all the values in the map
– Usually processed as a List, since values may not be unique
entrySet()
– Returns Set<Map.Entry<K,V>> containing the mappings in the map
Mastering Spring and MyBatis
2-28
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 2.4: Create and Access a HashMap
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
2-29
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Spring and Dependency Injection
Testing with Spring
Working with Maps
Other Dependency Types
Chapter Summary
Mastering Spring and MyBatis
2-30
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Injecting Lists and Sets
Can inject a java.util.List of values with XML configuration
– If the Java code uses generics, Spring will ensure type safety
<bean id="no-way" class="com.fidelity.dependency.Film">
<property name="cast">
<list>
<ref bean="chopra" /> public void setCast(List<Actor> cast) {
<ref bean="hanks" /> this.cast = cast;
<ref bean="jing" /> }
</list>
</property>
</bean>
– Can also put value elements in the list
Similarly, the <set> element works for java.util.Set
No precise equivalent in annotations
Mastering Spring and MyBatis
2-31
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Using Properties in @Configuration
You can define property values in an external property file
– Read properties using @PropertySource on your @Configuration bean
@Configuration film.properties
@PropertySource("classpath:film.properties")
public class AppConfig { title = It's A Wonderful Life
}
@Component
public class Film {
private List<Actor> cast;
private String title;
// Set value from properties file
@Value("${title}") Property placeholder
public void setTitle(String title) {
this.title = title;
}
…
}
Mastering Spring and MyBatis
2-32
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Using Properties in beans.xml
Beans defined in XML can also define read external properties file
– Avoids need to edit beans.xml when changing the values
<beans xmlns:context="http://www.springframework.org/schema/context"
… >
<!-- Read the database credentials from the db.properties file -->
<context:property-placeholder location="classpath:db.properties" />
Load the property file
<!-- Define a DataSource for the database -->
<bean id="oracleDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${db.url}" />
<property name="driverClassName" value="${db.driver}" />
<property name="username" value="${db.username}" />
Use property placeholders
<property name="password" value="${db.password}" />
to use values
</bean>
db.url = jdbc:oracle:thin:@localhost:1521:xepdb1
db.driver = oracle.jdbc.driver.OracleDriver
… db.properties
Mastering Spring and MyBatis
2-33
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Spring and Dependency Injection
Testing with Spring
Working with Maps
Other Dependency Types
Chapter Summary
Mastering Spring and MyBatis
2-34
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Summary
In this chapter, we have explored:
Spring’s dependency injection
Testing with Spring dependency injection
Working with Maps
Mastering Spring and MyBatis
2-35
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Key Points
Spring provides a BeanFactory that supports dependency injection
Spring can use either setters or constructors for dependency injection
When testing with Spring, you can:
– Satisfy dependencies manually
– Use the Spring TestContext Framework
Maps can be used to store values that are retrieved by providing a key
Dependencies can be:
– Other beans: use “ref”
– Can be values (String, primitives): use “value”
– Can be list, set, properties
Mastering Spring and MyBatis
2-36
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mastering Spring and MyBatis
Chapter 3:
Advanced Spring Configuration
Mastering Spring and MyBatis
3-1
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Overview
In this chapter, we will explore:
The bean lifecycle
How dependencies are injected
Dynamically evaluating expressions with Spring Expression Language (SpEL)
Managing Spring configuration across multiple files
Debugging Spring configuration problems
Mastering Spring and MyBatis
3-2
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Managing the Bean Lifecycle
Spring Expression Language (SpEL)
More Configuration Options
Debugging Spring Configuration Problems
Chapter Summary
Mastering Spring and MyBatis
3-3
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
PostConstruct and PreDestroy
In our JDBC tests, we used the tearDown()
method to call close() on the DAO
import javax.annotation.*;
– What about in a real-world application? // Java EE 9+: jakarta.annotation.*;
When you configure a bean: @Repository("dao")
– Can specify a pre-destroy method public class DepartmentDao {
▪ Called only for singleton beans @PostConstruct
▪ To dispose of resources the bean itself public void init() {
…
allocated } Called after all
– Can specify a post-construction method @PreDestroy dependencies are injected
▪ If bean requires additional initialization public void close() {
after all dependencies are injected …
}
Called when Spring
container shuts down
Mastering Spring and MyBatis
3-4
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Specifying an Initialization and Destroy Method in XML
Can specify init-method and destroy-method using XML configuration:
<bean id="dao"
class="com.fidelity.advanced.DepartmentDao"
init-method="init"
destroy-method="close" >
…
</bean>
Mastering Spring and MyBatis
3-5
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Ensuring Graceful Shutdown
For desktop Java applications:
– The ApplicationContext needs to register a shutdown hook with JVM
– The method is defined on the AbstractApplicationContext interface
AbstractApplicationContext factory =
new ClassPathXmlApplicationContext(springConfigurationFile);
factory.registerShutdownHook();
Ensure factory is automatically
closed when JVM shuts down
This is not needed in Java EE environments (e.g., web applications and RESTful services)
– A Spring container running in a Java EE server registers its own shutdown hook
Mastering Spring and MyBatis
3-6
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Optional Exercise 3.1: Bean Destroy
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
3-7
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Managing the Bean Lifecycle
Spring Expression Language (SpEL)
More Configuration Options
Debugging Spring Configuration Problems
Chapter Summary
Mastering Spring and MyBatis
3-8
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Spring Expression Language
Use the Spring Expression Language (SpEL) to execute Java code in @Value()
– Syntax: #{ java-expression }
– Note: use #{…} for SpEL expressions, use ${…} for values in .properties files
Example: injecting environment variables and Java system properties into beans
@Value("#{systemProperties['user.country']}")
private String country; systemProperties and environment
@Value("#{environment['SystemRoot']}") are pre-defined SpEL beans
private String rootDir;
Set system properties with java -Dproperty-name=property-value
$ java -Duser.country=India ...
Set environment variables on the command line:
– UNIX/Linux: $ export SystemRoot=/local
– MS Windows: > set SystemRoot=D:\
Mastering Spring and MyBatis
3-9
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Calling Methods and Performing Calculations in SpEL
An SpEL expression can include complex expressions
Example: convert the value of a system property to upper case
@Value("#{systemProperties['currency.code'].toUpperCase()}")
private String currencyCode;
Example: convert the value of the tax_percent environment variable to a double
– Then divide by 100 to yield a tax rate
– To reference a class in the SpEL expression, wrap it in the T() (type) function
$ export tax_percent=7
@Value("#{T(java.lang.Double).valueOf(environment['tax_percent']) / 100.0}")
private double taxRate; // 0.07
Mastering Spring and MyBatis
3-10
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Managing the Bean Lifecycle
Spring Expression Language (SpEL)
More Configuration Options
Debugging Spring Configuration Problems
Chapter Summary
Mastering Spring and MyBatis
3-11
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Motivation for Modularizing Spring Configuration
Putting all Spring configuration in a single file or class can become unmanageable
– Difficult to find individual beans
– Becomes a bottleneck in development
The solution is to divide the configuration according to project-specific criteria
– E.g., per feature, per layer
Example: when using the Spring TestContext Framework, may want a special configuration
– Different dependencies (e.g., replace production dependencies with mock objects)
– Do not want to duplicate unchanged dependencies
– Do not want to “pollute” production configuration with test
Everywhere that accepts a configuration file or class argument also accepts an array or list
– In Java, parameter is varargs, meaning a comma-separated list or an array
– In annotations, an array (use array constructor {…})
– In XML, can be comma-, space-, or semicolon-separated
Mastering Spring and MyBatis
3-12
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Importing Configuration
Usually prefer to have more control than a simple list can provide
– Create a single “entry point” configuration for each situation and import the rest
…
<import resource="classpath:common-beans.xml" />
<bean id="mockStringProvider" class="com.fidelity.services.StringProviderMockImpl" />
…
– Or, using annotations
@Configuration
@Import(ApplicationCommonConfig.class)
public class ApplicationTestConfig {
@Bean
public StringProvider mockStringProvider() {
return new StringProviderMockImpl();
}
…
Mastering Spring and MyBatis
3-13
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
XML and Annotations
It is possible to configure some beans in Java and some in XML
– Could even have some properties in XML and others with annotations
In the case of a conflict, XML configuration takes precedence
Can even combine @Configuration classes and XML
– If using an XML context, must have component-scan
▪ Any classes annotated with @Configuration will be found and processed
▪ Or can be defined with <bean>
– If using an Annotation context, can import XML
@Configuration
@ImportResource("classpath:mixed2-beans.xml")
public class AppConfig {
@Autowired annotations are processed on @Configuration classes
– Processed very early, limit use to simple dependencies
Mastering Spring and MyBatis
3-14
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
@ComponentScan
Can also annotate the class with @ComponentScan
– Will scan current package and all sub-packages for Spring beans
@Configuration
@ComponentScan
public class ApplicationConfig {
…
Can also supply a list of packages in Strings
– Or a list of classes
▪ Spring will scan the packages of those classes
▪ More type-safe since the compiler can check the class names
Mastering Spring and MyBatis
3-15
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
@Bean Methods
If @Configuration classes can scan for annotations, just like XML, why use @Bean?
@Bean methods can have an arbitrary number of parameters (no @Autowired needed)
– Spring matches them just like constructor parameters
– Use for advanced configuration that is not easily done declaratively
@Configuration A suitable bean will automatically
public class ApplicationTestConfig { be injected here
@Bean
public ImportantService importantService(StringProvider sp) {
// have opportunity to interact with StringProvider here
return new ImportantService(sp);
}
…
Use @Bean when you do not own the source code for the class being instantiated
– You cannot add annotations to the class’s source file
Mastering Spring and MyBatis
3-16
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
@Bean Methods for DataSource Configuration
Java configuration is often used in initialize a DataSource
@Configuration
@PropertySource("classpath:db.properties")
@Component
public class SpringJdbcConfiguration {
public class DepartmentDao {
@Autowired
@Autowired
private Environment env;
private DataSource dataSource;
@Bean …
public DataSource createDataSource() { Spring calls the @Bean method
DriverManagerDataSource dataSource = createDataSource() to satisfy
new DriverManagerDataSource(); the DataSource dependency
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
}
Mastering Spring and MyBatis
3-17
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Spring Profiles
During its development lifecycle, your application will run in different environments
– Your development environment, the QA/QC environment, the production environment
Often, beans and property files need to change based on the current environment
– Need configuration for data sources, different URLs for web services, etc.
Spring defines profiles to support configuration for different environments
Bean created only if Bean created only if
@Component active profile is dev
@Component active profile is prod
@Profile("dev") @Profile("prod")
public class DevelopmentDbConfig { … } public class ProductionDbConfig { … }
Any bean that does not specify a profile belongs to the default profile
Set the active profile with a Java system property
java -Dspring.profiles.active=dev … Set active profile to dev
Mastering Spring and MyBatis
3-18
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Property Files for Different Environments
You can load property files customized for different environments with @PropertySource
– Add profile name to file names: app.prod.properties, app.dev.properties
– Specify the property file path using an SpEL expression with a default value
▪ Syntax: ${expression:default-value}
▪ If expression is not null or 0, use it; otherwise, use default-value
@Configuration
@PropertySource("classpath:app.${spring.profiles.active:prod}.properties")
public class AppConfig { }
If spring.profile.active
is not set, value is “prod”
Set the active profile for test classes using @ActiveProfiles
@ActiveProfiles("dev")
public class LibraryDaoTest { … }
Mastering Spring and MyBatis
3-19
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Managing the Bean Lifecycle
Spring Expression Language (SpEL)
More Configuration Options
Debugging Spring Configuration Problems
Chapter Summary
Mastering Spring and MyBatis
3-20
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
The Source of the Problem
The source of many Spring problems is the configuration file (or annotations)
Misconfigurations, including misnamed beans, are often the culprit
– Remember the values of the bean id and ref attributes are case-sensitive
<bean id="teller" class="com.fidelity.fortune.FortuneTeller">
<property name="provider" ref="theprovider" />
</bean>
<bean id="theProvider" class="com.fidelity.fortune.FortuneTellerProvider">
<property name="fortunes">
<list>
<value>Your lucky number is 42.</value>
<value>This is the first day of the rest of your life</value>
<value>Look both ways before crossing the street</value>
</list>
</property>
</bean>
Mastering Spring and MyBatis
3-21
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Using a Logging Framework to Debug
Sometimes, it is necessary to see what Spring is doing when it is attempting to create the
beans defined by your configuration settings
– Use a logging package
▪ Set the rootLogger to DEBUG level
▪ You will see an astounding level of detail
log4j2.properties
status = warn
dest = err
name = PropertiesConfig
appender.console.type = Console
appender.console.name = Console
appender.console.target = SYSTEM_OUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = Props: %d{HH:mm:ss.SSS} [%t] %-5p %c{36} - %m%n
rootLogger.level = debug
rootLogger.appenderRef.stdout.ref = Console
Mastering Spring and MyBatis
3-22
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 3.2: Debugging Spring Configuration Problems
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
3-23
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Managing the Bean Lifecycle
Spring Expression Language (SpEL)
More Configuration Options
Debugging Spring Configuration Problems
Chapter Summary
Mastering Spring and MyBatis
3-24
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Summary
In this chapter, we have explored:
The bean lifecycle
How dependencies are injected
Dynamically evaluating expressions with Spring Expression Language (SpEL)
Managing Spring configuration across multiple files
Debugging Spring configuration problems
Mastering Spring and MyBatis
3-25
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Key Points
The Spring Expression Language allows more complex configuration
Multiple configuration sources can be used together
Debugging Spring configuration problems
– Read the entire error message in the stack trace
– Identify the source of the problem from the stack trace
– Use a logging framework for those difficult problems
Mastering Spring and MyBatis
3-26
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mastering Spring and MyBatis
Chapter 4:
Introduction to MyBatis and Spring
Mastering Spring and MyBatis
4-1
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Overview
In this chapter, we will explore:
The Domain Store design pattern
Persisting Java beans with MyBatis
Configuring and invoking MyBatis from Spring
Managing relationships in Java and MyBatis
Mastering Spring and MyBatis
4-2
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
Mastering Spring and MyBatis
4-3
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
DataSource
DataSource is a Java interface
Spring will create a DataSource instance based on the database properties
The DataSource will create a pool of database connections
– The Connections are connected to the database when the pool is created
– The Connection will be returned to the pool when the close() method is called
We will use this instead of getting a Connection directly from the DriverManager
– Although we will use the aptly named DriverManagerDataSource
– We could replace this with another DataSource implementation
The connection pool provides a win-win situation:
1. The overhead of opening and closing database connections is removed since the connections
are established with the database when the pool is created
2. The connections can be shared by multiple clients. When the client code has completed a
database operation, it can return the connection to the pool. After the connection has been
returned to the pool, another client can obtain it from the DataSource.
Mastering Spring and MyBatis
4-4
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
DataSource with Connection Pool
«interface»
DataSource
+ getConnection(): Connecton
+ getConnection(String, String): Connection
DataSourceImpl «interface»
manages ConnectionPool
+ getConnection(): Connecton Connection
+ getConnection(String, String): Connection 1 1 1 min..max
+ prepareStatement(): PreparedStatement
Mastering Spring and MyBatis
4-5
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Configuring a DataSource with Spring
Spring simplifies the creation of a DataSource
Java Configuration XML Configuration
@Configuration
<context:property-placeholder location="classpath:db.properties" />
@PropertySource("classpath:db.properties")
public class SpringJdbcConfiguration { <bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
@Bean <property name="driverClassName" value="${db.driver}" />
public DataSource createDataSource( <property name="url" value="${db.url}" />
Environment env) { <property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
Spring autowires @Bean method parameters </bean>
DriverManagerDataSource dataSource =
new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url")); db.properties
dataSource.setUsername(env.getProperty("db.username"));
db.url=jdbc:oracle:thin:@localhost:1521:xepdb1
dataSource.setPassword(env.getProperty("db.password"));
db.driver=oracle.jdbc.driver.OracleDriver
return dataSource;
db.username=scott
}
db.password=TIGER
}
Mastering Spring and MyBatis
4-6
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
Mastering Spring and MyBatis
4-7
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Limitations of JDBC-Based DAOs
Even a simple object-oriented design can be difficult to translate to a relational model
– Example: an application that tracks mentoring sessions
– Each Session is attended by a group of Scholars public class Session {
– Each Session is taught by a single Mentor private int id;
private List<Scholar> scholars;
(ignore the subclasses for now) private Mentor mentor;
– Each Session concerns a single Subject Area private DayOfWeek day;
private LocalTime time;
– Must retrieve all of these to retrieve a Session private SubjectArea subjectArea;
…
Mastering Spring and MyBatis
4-8
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
A Complex Object Graph
Now look at Mentor (still ignoring the subclasses)
– Each Mentor covers a collection of public abstract class Mentor {
Subject Areas private int id;
– So, Mentor is also a complex type private String firstName;
private String lastName;
We call this collection of objects an “object graph” private Set<SubjectArea> subjectAreas;
…
How can we represent these in a database?
Mastering Spring and MyBatis
4-9
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
A Complex Object Graph (continued)
Here is one possible representation
– Note the tables resolving many-to-many relationships between:
▪ Mentors and Subject Area
▪ Sessions and Scholars
Accessing a Session means reconstructing the object graph from these tables
Mastering Spring and MyBatis
4-10
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Inheritance in Relational Databases
Look at Mentor again, think about the subclasses
Each Mentor is one of PartTimeMentor or FullTimeMentor
– There are common fields in Mentor
– Specialized fields in PartTimeMentor and FullTimeMentor
How can we represent this in a Relational Database where there is no inheritance?
Mastering Spring and MyBatis
4-11
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Three Common Approaches to Mapping Inheritance
A single table representing all instances of the superclass (Single Table Inheritance*)
– One table: Mentor
– A type column, known as a discriminator, identifies what is held in each row
– The table contains the superset of all columns from all subclasses (many optional)
Separate tables for each concrete subclass (Concrete Table Inheritance*)
– Two tables: FullTimeMentor and PartTimeMentor
– Common fields are repeated as columns on both tables
– Any operation in the database that operates on all Mentors requires a UNION ALL
– Unique key management is difficult across the tables
Separate tables for each class (Class Table Inheritance*)
– Three tables: Mentor, FullTimeMentor, and PartTimeMentor (our choice in this case)
– Each table matches the class, primary key is common
– Retrieving any meaningful data requires joins between these tables
– Discriminator not required, but may be useful in many cases
*These names are from Fowler, Patterns of Enterprise Application Architecture
Mastering Spring and MyBatis
4-12
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Recursive Relationships
Consider the relationship between departments and employees in the HR schema
– A department may be managed by an
employee
– Many employees may work for a
department
What happens if you retrieve an Employee?
– Remember that objects should always
be fully populated
Need to retrieve the Employee’s department
– To retrieve a Department, need to retrieve all the Employees
▪ Including the one we started with
▪ And each Employee has a reference to the Department
Mastering Spring and MyBatis
4-13
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Partial Objects
An object with only some fields loaded is known as a Partial Object
– Every method must check which fields are loaded
– Cannot use a simple null check if NULL is a valid value
– Difficult to prevent this knowledge leaking outside the object
Object-oriented best practice is always to have fully-populated objects
– Avoid Partial Objects
In recursive relationships, it is very expensive to load the whole object graph
– Need a way to work safely with Partial Objects
– Solution is a Proxy that intercepts all method calls and ensures data is present if needed
Mastering Spring and MyBatis
4-14
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Need for a Persistence Framework
The previous slides illustrate some effects of the Object-Relational Impedance Mismatch
– Named for impedance matching in Electrical Engineering
For very large or highly data-driven applications
– DAO code can become extremely large and complex when using JDBC
– Difficult to maintain
– Difficult to extend
For such projects, may need way to deal with entire object graphs easily
– Perhaps load parts of graph only as needed (“lazy loading”)
– Avoid reloading data when database changes rarely (“caching”)
Best practice: use a persistence framework to persist complex object graphs
– Instead of writing custom DAO code
– The Domain Store design pattern
Mastering Spring and MyBatis
4-15
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
Mastering Spring and MyBatis
4-16
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Introducing MyBatis
MyBatis: an open-source SQL mapping framework
– Makes it easier to create Java objects from database queries
– Supports relationships between objects: one-to-one, one-to-many, many-to-many
– Implements advanced features such as lazy loading and caching of query results
Working with MyBatis:
1. Write a SQL SELECT statement
2. Configure MyBatis to map the result set’s columns to Java object properties
3. Tell MyBatis to execute the query
4. MyBatis calls constructors and setter methods to create a graph of Java objects
MyBatis is not a full-fledged Object-Relational Mapping (ORM) framework
– Doesn’t generate SQL queries for you
– Gives only limited help with inserting and updating objects in database tables
But many projects don’t need a heavyweight ORM
– Teams that hand-tune SQL queries for optimal performance
– Projects that use stored procedures for most persistence tasks
Mastering Spring and MyBatis
4-17
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
How MyBatis Works
Map JavaBeans objects to PreparedStatement parameters and ResultSets
The Application view is: Application
– Use standard Java types as parameters Mapper
JavaBeans JavaBeans
– Execute Java interface methods to Primitives
Mapper Interface Methods
Primitives
access data Collections
Mapper SQL
Collections
– Receive results as standard Java types
MyBatis framework will:
– Create a PreparedStatement Statement PreparedStatement
ResultSet
Parameters (or CallableStatement)
based on configuration
– Set parameters on the statement Spring Configuration
– Execute query or update using JDBC MyBatis
or Configuration File
– Convert ResultSet into Java types
(usually objects or a collection of objects)
Mastering Spring and MyBatis
4-18
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Steps to Persist an Object with MyBatis and Spring
Let’s start with a simple example:
– How to create, read, update, delete (CRUD) Product objects
– This section shows best practices (not all the possible ways)
▪ Will examine the actual requirements later in this chapter
P roduct PRODU CT
- productId :String «column»
- categoryId :String DAO using Spring and MyBatis *PK PRODUCTID :varchar(50)
- name :String NAME :varchar(50)
- description :String DESCN :varchar(50)
CATEGORY :varchar(50)
Steps:
1. Configure MyBatis to load mapping files and use a DataSource
2. Write a Java bean to be persisted
3. Create a Java mapping interface that declares the database operations
4. Write an XML mapping file that contains SQL statements
5. Use MyBatis from Service or DAO
Mastering Spring and MyBatis
4-19
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 1: Configure MyBatis
You can configure MyBatis with either XML or Java configuration
– Specify the Java package for MyBatis to scan for mapper interfaces
– MyBatis will automatically generate objects that implement your mapper interface
– Spring will then autowire the mapper objects into your Service beans or DAO beans
XML Configuration
<!-- enable scanning for Spring components and autowiring
(beware that this does not enable scanning for MyBatis mapper interfaces!) -->
<context:component-scan base-package="com.fidelity.service, com.fidelity.integration" />
<!-- Tell MyBatis where to scan for mappers --> Package of Java interface that
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> maps to MyBatis operations
<property name="basePackage" value="com.fidelity.integration" />
</bean> Java Configuration
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.fidelity.integration");
return configurer;
}
Mastering Spring and MyBatis
4-20
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 1: Configure MyBatis (continued)
Wire up the MyBatis SqlSessionFactory in Spring’s configuration file
– SqlSession is the core of MyBatis, though we will rarely access directly
– configLocation – location of the MyBatis config file, if required
– dataSource – data source to use
– mapperLocations – locations of MyBatis XML mapping files
– typeAliasesPackage – default Java package name for classes referenced in MyBatis
XML mapping file
beans.xml
<!-- define the SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:com/fidelity/**/*.xml" />
<property name="configLocation" value="classpath*:mybatis-config.xml" />
<property name="typeAliasesPackage" value="com.fidelity.domain" />
</bean>
Often can omit configLocation
when using MyBatis with Spring
Mastering Spring and MyBatis
4-21
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 1: Configure MyBatis (Java Configuration)
You can also configure SqlSessionFactory with Java configuration
@Configuration Spring automatically
public class MyBatisConfig { injects these parameters
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource,
ResourceLoader resourceLoader) throws Exception {
Resource[] mapperFiles =
ResourcePatternUtils.getResourcePatternResolver(resourceLoader)
.getResources("classpath*:com/fidelity/**/*.xml");
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
Set the session
factoryBean.setDataSource(dataSource); factory’s properties
factoryBean.setMapperLocations(mapperFiles);
factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
factoryBean.setTypeAliasesPackage("com.fidelity.domain");
return factoryBean.getObject();
} Return the session factory
Mastering Spring and MyBatis
4-22
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
MyBatis Configuration File
By default, the MyBatis configuration file is named mybatis-config.xml
When using MyBatis standalone, this defines parameters for SqlSessionFactoryBean:
– Data source
– Mapper locations
– Type aliases
Usually not needed when using MyBatis with Spring, but can be used:
– To set mapper locations and type aliases in addition to those in the bean declaration
– To set global parameters such as caching, lazy loading, automapping, timeouts
Mastering Spring and MyBatis
4-23
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Spring Support for MyBatis
Spring integrates nicely with MyBatis
– MyBatis provides template classes that integrate with Spring
▪ Simplifies getting access to mappers
– Handles SQLException and maps exceptions
Spring simplifies the configuration of MyBatis
– E.g., configure data source from Spring configuration file
Spring also provides integrated transaction management
– Can add transactions to methods using Spring’s Aspect-Oriented Programming (AOP)
– Simply add Spring’s @Transactional to a class or method
▪ Application code does not explicitly begin, commit, or roll back transactions
▪ It is still happening behind the scenes controlled by Spring
Mastering Spring and MyBatis
4-24
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 4.1: Configure MyBatis with Spring
15 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
4-25
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
Mastering Spring and MyBatis
4-26
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 2: Java Object to Persist
1. Configure MyBatis to load mapping files and use a DataSource
2. Write a Java bean to be persisted
3. Create a Java mapping interface that declares the database operations
4. Write an XML mapping file that contains SQL statements
5. Use MyBatis from Service or DAO
MyBatis will use the default constructor and
setter methods, if present
public class Product {
private int productId;
If no zero-argument constructor, MyBatis will
private int categoryId;
inject arguments for another constructor
private String name;
private String description;
If no setter for a field, MyBatis will access
// getters and setters the field directly (even if private)
}
Mastering Spring and MyBatis
4-27
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 3: MyBatis Mapping Interface
1. Configure MyBatis to load mapping files and use a DataSource
2. Write a Java bean to be persisted
3. Create a Java mapping interface that declares the database operations
4. Write an XML mapping file that contains SQL statements
5. Use MyBatis from Service or DAO ProductMapper.java
public interface ProductMapper {
Product getProduct(int productId);
List<Product> getProductListByCategory(int categoryId);
void insertProduct(Product product);
}
Create a Java interface
– Methods declare the database operations for the Java object that will be persisted
Instances of this interface will be created by SqlSession
– You do not have to write a class that implements this interface!!!
Mastering Spring and MyBatis
4-28
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 4: MyBatis XML Mapping File
1. Configure MyBatis to load mapping files and use a DataSource
2. Write a Java bean to be persisted
3. Create a Java mapping interface that declares the database operations
4. Write an XML mapping file that contains SQL statements
5. Use MyBatis from Service or DAO
Mapping file describes how database tables and columns are mapped to classes and fields
Some important things to remember:
– The mapping file must be consistent with the mapping interface
– The database operations defined in the mapping file must correspond to the methods in
the interface
– The operation ids must match the method names
Mastering Spring and MyBatis
4-29
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 4: MyBatis Mapping File Example
By convention, the name <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
of mapping file is "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
ClassNameMapper.xml <mapper namespace="com.fidelity.integration.ProductMapper">
<select id="getProduct" resultType="Product">
SELECT productId, name, descn as description, category as categoryId
FROM product
ProductMapper.xml WHERE productid = #{productId} <select> id must match method
</select> name in mapper interface
public class Product { <select id="getProductListByCategory" resultType="Product">
SELECT productId, name, descn as description, category as categoryId
public int getProductId() { … } FROM product
public String getName() { … } WHERE category = #{value}
public int getCategoryId() { … } If column name doesn't match
</select> property name exactly, define an alias
public String getDescription() { … }
<insert id="insertProduct" parameterType="Product">
INSERT INTO product (productid, name, descn, category)
VALUES (#{productId}, #{name}, #{description}, #{categoryId})
</insert>
</mapper>
Mastering Spring and MyBatis
4-30
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 4: Mapping File Location
Place the XML mapping file in the location defined in the Spring configuration file
– Usually relative to classpath of application
▪ With package qualified name to avoid conflicts
– When using Maven, put it in a subdirectory of src/main/resources
Could place it in the DAO’s package
– If DAO is in com.fidelity.integration, mapping file will be at:
classpath*:/com/fidelity/integration/ProductMapper.xml
– Another common option is to place it in the package of business object
classpath*:/com/fidelity/domain/ProductMapper.xml
By using ** in the configuration file, specify any number of intermediate directories
classpath*:com/fidelity/**/*.xml
Mastering Spring and MyBatis
4-31
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Step 5: Use MyBatis in Service or DAO
1. Configure MyBatis to load mapping files and use a DataSource
2. Write a Java bean to be persisted
3. Create a Java mapping interface that declares the database operations
4. Write an XML mapping file that contains SQL statements
@Primary is required if mapper
5. Use MyBatis from Service or DAO
interface is in same package as DAO
@Primary
@Repository("productDao")
public class ProductDaoMyBatisImpl implements ProductDao {
The DAO class calls on the Mapper class @Autowired
– To interact with the database private ProductMapper mapper;
Notice that the DAO class has Spring @Override
public List<Product> getProducts(int categoryId) {
auto-wire the Mapper bean return mapper.getProductListByCategory(categoryId);
}
}
Easy to see why we often don’t need the DAO when
using MyBatis: treat the mapper as the DAO instead
Mastering Spring and MyBatis
4-32
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 4.2:
Query the Database with MyBatis and Spring
30 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
4-33
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
Mastering Spring and MyBatis
4-34
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
ResultMaps
Best practice: use a ResultMap to define the mapping instead of column aliases
– ResultMaps allow more complex mappings to be defined
<mapper namespace="com.fidelity.integration.ProductMapper">
<resultMap type="Product" id="ProductMap">
<id property="productId" column="PRODUCTID"/> Column names are
<id> identifies <result property="name" column="NAME"/> not case-sensitive
table’s primary key <result property="description" column="DESCN"/>
<result property="categoryId" column="CATEGORY"/> Property names are
</resultMap> case-sensitive
<select id="getProducts" resultType="Product">
SELECT productid, name, descn as description, category as categoryId
FROM product
</select> Without ResultMap, must
<select id="getProduct" resultMap="ProductMap"> define column aliases
SELECT productid, name, descn, category
FROM product
With ResultMap, use
WHERE productid = #{productId}
</select> raw column names
Mastering Spring and MyBatis
4-35
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-One Mapping (DB)
Database: PRODUCT_DETAIL
– PRODUCT_DETAIL table has PRODUCT
«column»
PRODUCTID column that is «column» *pfK PRODUCTID: NUMBER(8)
MANUFACTURER: VARCHAR2(50)
*PK PRODUCTID: NUMBER(8)
foreign key to PRODUCT and NAME: VARCHAR2(50) SKU: VARCHAR2(50)
UPC: VARCHAR2(50)
is also primary key
DESCN: VARCHAR2(50)
CATEGORY: VARCHAR2(50) MINIMUM_AGE: NUMBER(8,2)
«PK» «FK»
Java: + PK_PRODUCT(NUMBER) + FK_PRODUCT_DETAIL_PRODUCT(NUMBER)
«PK»
– Product class has reference + PK+PRODUCT_DETAIL(NUMBER)
to a ProductDetail object
public class Product { public class ProductDetail {
private int productId; private int productId;
private ProductDetail detail; private int manufacturer;
… private String sku;
} …
This field defines the }
one-to-one relationship
Mastering Spring and MyBatis
4-36
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-One Mapping (MyBatis)
Performing the getProductsWithDetail returns fully populated Products
This technique works, but probably is not what we would use in production
– If we already have Product mappings defined, we want to reuse them
<resultMap type="Product" id="ProductWithDetailMap">
<id property="productId" column="PRODUCTID"/>
<result property="name" column="NAME"/>
<result property="description" column="DESCN"/>
<result property="categoryId" column="CATEGORY"/>
<result property="detail.productId" column="PRODUCTID"/>
<result property="detail.manufacturer" column="MANUFACTURER"/>
<result property="detail.sku" column="SKU"/>
<result property="detail.upc" column="UPC"/>
<result property="detail.minimumAge" column="MINIMUM_AGE"/>
</resultMap>
<select id="getProductsWithDetail" resultMap="ProductWithDetailMap">
SELECT p.productid, p.name, p.descn, p.category, d.manufacturer, d.sku, d.upc, d.minimum_age
FROM product p
JOIN product_detail d ON p.productid = d.productid
</select>
Mastering Spring and MyBatis
4-37
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-One Mapping by Extension
One map can extend another <resultMap type="Product" id="ProductWithDetailMap">
<id property="productId" column="PRODUCTID"/>
<result property="name" column="NAME"/>
<result property="description" column="DESCN"/>
<result property="categoryId" column="CATEGORY"/>
<resultMap type="Product" id="ProductMap"> <result property="detail.productId" column="PRODUCTID"/>
<id property="productId" <result
column="PRODUCTID"/> property="detail.manufacturer" column="MANUFACTURER"/>
<result property="name" column="NAME"/> <result property="detail.sku" column="SKU"/>
<result property="description" column="DESCN"/> <result property="detail.upc" column="UPC"/>
<result
<result property="categoryId" column="CATEGORY"/> property="detail.minimumAge" column="MINIMUM_AGE"/>
</resultMap> </resultMap>
<resultMap type="Product" id="ProductWithDetailByExtension" extends="ProductMap">
<result property="detail.productId" column="PRODUCTID"/>
<result property="detail.manufacturer" column="MANUFACTURER"/>
<result property="detail.sku" column="SKU"/>
<result property="detail.upc" column="UPC"/>
<result property="detail.minimumAge" column="MINIMUM_AGE"/>
</resultMap>
But if we need the detail table in other places, we still must repeat the mappings
– We can avoid this by using a nested ResultMap or a nested SELECT
Mastering Spring and MyBatis
4-38
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-One Mapping with Nested ResultMaps
Define a ResultMap for a Product with an <association> element
– The association data will be loaded using a single query
<resultMap type="Product" id="ProductWithNestedDetailMap">
<id property="productId" column="PRODUCTID"/>
<result property="name" column="NAME"/>
<result property="description" column="DESCN"/>
<result property="categoryId" column="CATEGORY"/>
<association> <association property="detail" resultMap="ProductDetailMap" />
means “one-to-one” </resultMap>
<resultMap type="ProductDetail" id="ProductDetailMap"> Reference to another
<id property="productId" column="PRODUCTID"/> <resultMap>
<result property="manufacturer" column="MANUFACTURER"/>
<result property="sku" column="SKU"/>
<result property="upc" column="UPC"/>
<result property="minimumAge" column="MINIMUM_AGE"/>
</resultMap>
<select id="getProductsWithNestedDetail" resultMap="ProductWithNestedDetailMap">
SELECT p.productid, p.name, p.descn, p.category, d.manufacturer,
d.sku, d.upc, d.minimum_age
FROM product p
JOIN product_detail d ON p.productid = d.productid
</select>
Mastering Spring and MyBatis
4-39
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Combine Extension and Nesting
Can combine both techniques to maximize re-use
– This is the best practice for a production solution
<resultMap type="Product" id="ProductWithNestedDetailMap">
<id property="productId" column="PRODUCTID"/>
<result property="name" column="NAME"/>
<result property="description" column="DESCN"/>
<result property="categoryId" column="CATEGORY"/>
<association property="detail" resultMap="ProductDetailMap" />
</resultMap>
Product map without the
<resultMap type="Product" id="ProductMap"> one-to-one relationship
<id property="productId" column="PRODUCTID"/>
<result property="name" column="NAME"/>
<result property="description" column="DESCN"/> Product map with the
<result property="categoryId" column="CATEGORY"/> one-to-one relationship
</resultMap>
<resultMap type="Product" id="ProductWithNestedDetailByExtension" extends="ProductMap">
<association property="detail" resultMap="ProductDetailMap" />
</resultMap>
Mastering Spring and MyBatis
4-40
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-One Mapping with Nested Select
Can define an <association> to use another <select> statement instead of a JOIN
– Set the <association>’s select attribute to the nested <select>
– A separate SELECT will be executed to fetch the associated ProductDetail
<resultMap type="Product" id="ProductWithNestedDetailSelect">
<id property="productId" column="PRODUCTID"/>
<result property="name" column="NAME"/>
<result property="description" column="DESCN"/>
<result property="categoryId" column="CATEGORY"/>
<association property="detail" column="PRODUCTID" select="getProductDetail" />
</resultMap>
<select id="getProductsWithNestedSelect" resultMap="ProductWithNestedDetailSelect">
SELECT productid, name, descn, category
FROM product
</select>
<select id="getProductDetail" parameterType="int" resultMap="ProductDetailMap">
SELECT productid, manufacturer, sku, upc, minimum_age
FROM product_detail
WHERE productid = #{value}
</select>
Mastering Spring and MyBatis
4-41
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-Many Mapping with Nested ResultMap
The relationship between Category and Product is a one-to-many relationship
– Each Product has a Category
– Each Category may have many Products
This field defines the
The <collection> element can be used to define this in MyBatis one-to-many relationship
<resultMap type="Category" id="CategoryWithNestedProductMap">
<id property="categoryId" column="ID"/> public class Category {
<result property="name" column="CAT_NAME"/> private Set<Product> products;
<collection property="products" resultMap="ProductMap" />
</resultMap> public class Product {
<collection> means “one-to-many” private Category category;
<select id="getCategoriesWithNestedProduct" resultMap="CategoryWithNestedProductMap">
SELECT p.productid, p.name, p.descn, p.category, c.id, c.name AS cat_name
FROM category c
LEFT OUTER JOIN
product p These
These two
two columns have the
columns have the same
same value.
value. Since
Since this
this is
is an
an outer
outer join,
join,
ON p.category = c.id must put the id in twice (once from category and
must put the id in twice (once from category and once from once from
</select> product),
product) otherwise, since
- otherwise, since the
the id
id isis not
not null
null (from
from category,
category),
MyBatis
MyBatis will
will create Products even
create Products even when
when all
all the
the other
other details
details are
are null
null
Mastering Spring and MyBatis
4-42
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-Many Mapping with Nested Select
Can define a nested SELECT instead of a JOIN
– May hurt performance: a nested SELECT will be executed for each child row
<resultMap type="Category" id="CategoryWithNestedProductSelect">
<id property="categoryId" column="ID"/>
<result property="name" column="CAT_NAME"/>
<collection property="products" column="ID" select="getProductListByCategory" />
</resultMap>
<select id="getProductListByCategory" resultType="Product">
SELECT productid, name, descn as description, category as categoryId
FROM product
WHERE category = #{value}
</select>
<select id="getCategoriesWithNestedSelect" resultMap="CategoryWithNestedProductSelect">
SELECT id, name AS cat_name
FROM category
</select>
Mastering Spring and MyBatis
4-43
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 4.3: Query for Complex Object Relationships
45 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
4-44
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
Mastering Spring and MyBatis
4-45
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Summary
In this chapter, we have explored:
The Domain Store design pattern
Persisting Java beans with MyBatis
Configuring and invoking MyBatis from Spring
Managing relationships in Java and MyBatis
Mastering Spring and MyBatis
4-46
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Key Points
MyBatis simplifies JDBC code through a mapping file
– Built around prepared statements
To use MyBatis + Spring:
– Configure MyBatis to load mapping files and use a DataSource
– Write a Java bean
– Create a mapping file interface that declares the database operations
– Write mapping file for the Java Bean
– Use MyBatis from Service or DAO (inject mapper)
MyBatis supports relationships
– One-to-one, one-to-many, and many-to-many
Mastering Spring and MyBatis
4-47
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mastering Spring and MyBatis
Chapter 5:
Working Effectively with MyBatis
Mastering Spring and MyBatis
5-1
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Overview
In this chapter, we will explore:
Performing database update operations with MyBatis
Managing transactions in JUnit tests with Spring
Configuring MyBatis with annotations
Using MyBatis custom type handlers and query caching
Using advanced MyBatis features
Mastering Spring and MyBatis
5-2
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
DML Through MyBatis with XML
Transaction Management in Testing
SQL Mappers Using Annotations
Advanced Topics
Chapter Summary
Mastering Spring and MyBatis
5-3
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mapped Statements
MyBatis supports DML operations
– INSERT
– UPDATE
– DELETE
The following slides show examples of each type of statement and how to use them
Mastering Spring and MyBatis
5-4
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
INSERT
An SQL INSERT command is configured with an <insert> element Notice use of Product
property names
<insert id="insertProduct" parameterType="Product">
INSERT INTO product (productid, name, descn, category)
VALUES (#{productId}, #{name}, #{description}, #{categoryId})
</insert>
The INSERT is executed by using the Mapper interface object
– Mapper method may be defined as returning int (number of rows affected) or void
public interface ProductMapper {
int insertProduct(Product product);
…
@Service
public class ProductService {
Returns number @Transactional
of rows inserted public boolean insertProduct(Product product) {
return mapper.insertProduct(product) == 1;
}
Mastering Spring and MyBatis
5-5
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Autogenerated Primary Keys
MyBatis can work with primary keys that are automatically generated by the database
– Available if database supports IDENTITY columns
– Add useGeneratedKeys, keyProperty, and keyColumn to <insert> statement
<insert id="insertProductWithIdentity" parameterType="Product"
useGeneratedKeys="true" keyProperty="productId" keyColumn="productid">
INSERT INTO product2 (name, descn, category)
VALUES (#{name}, #{description}, #{categoryId})
</insert>
MyBatis updates the key value of the object passed in
Initialize the object with a
dummy product id value
Product product = new Product(0,
"Jet Pack", "Personal flight device", 65);
dao.insertProductWithIdentity(product); Product id is now the key value
generated by the database
int generatedId = product.getProductId();
Mastering Spring and MyBatis
5-6
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Primary Keys from Sequences
Older versions of Oracle do not support IDENTITY columns
– Instead, keys were generated by sequences
If the sequence is used in a trigger, useGeneratedKeys works
– Otherwise, add a <selectKey> element to the <insert> statement
<insert id="insertProductWithSequence" parameterType="Product">
<selectKey keyProperty="productId" resultType="int" order="BEFORE">
SELECT product2_seq.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO product2 (productid, name, descn, category)
VALUES (#{productId}, #{name}, #{description}, #{categoryId})
</insert>
As before, MyBatis updates the key value of the object passed in
Mastering Spring and MyBatis
5-7
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
UPDATE
An SQL UPDATE command is configured with an <update> element
<update id="updateProduct" parameterType="Product">
UPDATE product
SET name = #{name}, descn = #{description}, category = #{categoryId}
WHERE productid = #{productId}
</update>
The UPDATE is executed by using the Mapper interface object
public interface ProductMapper {
Returns number int updateProduct(Product product);
of rows updated …
} @Service
public class ProductService {
@Transactional
In this case, the update is by primary key, public boolean updateProduct(Product product) {
but that may not always be the case, so you return mapper.updateProduct(product) == 1;
may prefer to return the row count }
Mastering Spring and MyBatis
5-8
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
DELETE
A SQL DELETE command is configured with a <delete> element
<delete id="deleteProduct" parameterType="int">
DELETE FROM product
WHERE productid = #{value}
</delete>
The DELETE is executed by using the Mapper interface object
public interface ProductMapper {
Returns number int deleteProduct(int productId);
of rows deleted …
} @Override
@Transactional
As before, you may prefer public boolean deleteProduct(int productId) {
to return the row count return mapper.deleteProduct(productId) == 1;
}
Mastering Spring and MyBatis
5-9
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Cascading Inserts
Unlike an ORM framework, MyBatis does not support cascading inserts
– Cascading insert example: when inserting a department, insert all its employees as well
– With MyBatis, you need to call multiple methods explicitly to perform all inserts
@Service
public class Department {
public class DepartmentService {
public Set<Employee> getEmployees() {…}
@Autowired
private EmployeeDao dao; Service class, not the
DAO, defines transactions
@Repository @Transactional
public class EmployeeDao { public void addDepartment(Department dept) {
@Autowired
private EmployeeMapper mapper; Insert the
dao.insertDepartment(dept);
department
public void insertDepartment(Department d) { … } for (Employee emp : dept.getEmployees()) {
public void insertEmployee(Employee e) { … } dao.insertEmployee(emp);
}
}
} Insert each Employee
individually
Mastering Spring and MyBatis
5-10
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
DML Through MyBatis with XML
Transaction Management in Testing
SQL Mappers Using Annotations
Advanced Topics
Chapter Summary
Mastering Spring and MyBatis
5-11
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Transaction Manager
Spring simplifies transactions
– Define a transaction manager to use the DataSource
– The transaction manager will control starting and committing transactions
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${db.url}" />
<property name="driverClassName" value="${db.driver}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- enable transaction demarcation with annotations -->
<tx:annotation-driven />
Mastering Spring and MyBatis
5-12
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Transactions Through Annotations
Use Annotations to apply transaction boundaries
– The Spring transaction manager automatically manages the transaction
▪ Starts the transaction when the method is called
▪ Commits the transaction when the method completes
▪ Or rolls the transaction back if an unchecked exception is thrown
public class ProductBusinessService {
@Transactional
Spring begins a transaction public boolean insertProduct(Product product) {
when this method is called … // validate the Product, etc.
return dao.insertProduct(product);
}
If the method throws a RuntimeException,
Spring rolls back the transaction; if no exception
is thrown, Spring commits the transaction
Mastering Spring and MyBatis
5-13
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Testing and Transactions
Spring provides support for managing transactions in tests with its TestContext framework
– Remember, in JUnit use @ExtendWith(SpringExtension.class)
You must do the following:
– Provide a PlatformTransactionManager bean
▪ For example, in the beans.xml file that is loaded by @ContextConfiguration
– Annotate your JUnit test class with the @Transactional annotation
The TestContext causes all @Transactional test methods to rollback automatically
– If necessary, use @Rollback(false) or @Commit to override this default behavior
The Spring documentation provides more detail on the options that are available
– https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html
Mastering Spring and MyBatis
5-14
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Transaction Management in Tests
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations="classpath:product-beans.xml")
@Transactional
class ProductDaoMyBatisImplTest { Annotate class or methods
@Autowired with @Transactional
private ProductDao dao;
@Test
void testGetProducts() {
…
} These
Thesetest
methods
methods
willwill
roll
rollback
back automatically
automatically
@Test
void testInsertProduct() {
…
}
@Test
void testAnotherThing() {
…
}
}
Mastering Spring and MyBatis
5-15
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Transaction Management in Tests (continued)
@BeforeEach and @AfterEach run inside any transaction for transactional tests
In addition, there are annotations that run outside the transaction for each test
– @BeforeTransaction runs before the transaction starts
– @AfterTransaction runs after the transaction has ended (usually rolled back)
Mastering Spring and MyBatis
5-16
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
@DirtiesContext
The TestContext caches contexts
– Loaded only once per “suite” (Spring interprets this as “JVM instance”)
– All tests run through Maven are run in the same JVM instance and bean factory
– Singleton beans are not re-initialized when a second test case is executed
Sometimes, your tests override one of a bean’s dependencies
– For example, to load a mock version of a dependency
– This action renders the context unreliable for other tests
Use @DirtiesContext to indicate operations that leave the context in an unreliable state
– The @DirtiesContext tells the testing framework to close and recreate the context
for later tests
– Do this as little as possible: contexts are cached for a reason
– Consider gathering such tests together
Mastering Spring and MyBatis
5-17
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 5.1: DML with MyBatis and Spring
60 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
5-18
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
DML Through MyBatis with XML
Transaction Management in Testing
SQL Mappers Using Annotations
Advanced Topics
Chapter Summary
Mastering Spring and MyBatis
5-19
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mapped Statements
MyBatis defines annotations for different statements
– These are used in the Mapper interface method definition
public interface ProductMapper { @Update("""
@Select(""" UPDATE product
SELECT productid, SET name = #{name},
name, descn = #{description},
descn as description, category = #{categoryId}
category as categoryId WHERE productid = #{productId}
FROM product """)
""") int updateProduct(Product product);
List<Product> getProducts();
@Delete("""
@Insert(""" DELETE FROM product
INSERT INTO product WHERE productid = #{value}
(productid, name, descn, category) """)
VALUES (#{productId}, #{name}, int deleteProduct(int productId);
#{description}, #{categoryId})
""")
int insertProduct(Product product);
Mastering Spring and MyBatis
5-20
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Autogenerated Primary Keys with Annotations
To use autogenerated primary keys:
– Use the @Options annotation
▪ With the useGeneratedKeys and keyProperty attributes
@Insert("INSERT INTO product2 (name, descn, category) " +
"VALUES (#{name}, #{description}, #{categoryId})")
@Options(useGeneratedKeys=true, keyProperty="productId", keyColumn="productid")
int insertProductWithIdentity(Product product);
To use Oracle sequences for generating primary keys:
– Use the @SelectKey annotation
▪ With the statement, resultType, before and keyProperty attributes
@Insert("INSERT INTO product2 (productid, name, descn, category) " +
"VALUES (#{productId}, #{name}, #{description}, #{categoryId})")
@SelectKey(statement="SELECT product2_seq.NEXTVAL FROM DUAL", keyProperty="productId",
resultType=int.class, before=true)
int insertProductWithSequence(Product product);
Mastering Spring and MyBatis
5-21
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
ResultMaps
You can map query results to JavaBean properties
– Use the @Result annotation
@Select("SELECT productid, name, descn, category " +
" FROM product " + MyBatis substitutes value of
method parameter
" WHERE productid = #{productId}")
@Result(property="productId", column="PRODUCTID", id=true)
@Result(property="name", column="NAME")
@Result(property="description", column="DESCN")
@Result(property="categoryId", column="CATEGORY")
Product getProduct(int productId);
– You can also use a ResultMap defined in a Mapper XML file
@Select("SELECT productid, name, descn, category " +
" FROM product " +
" WHERE category = #{value}")
@ResultMap("com.fidelity.integration.ProductMapper.ProductMap")
List<Product> getProductListByCategory(int categoryId);
Mastering Spring and MyBatis
5-22
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-One Mapping
To load a one-to-one association, use the @One annotation
– Uses a nested select statement
– Nested ResultMap (join mapping) is not supported—if needed, use @ResultMap
@Select("SELECT productid, name, descn, category " +
" FROM product")
@Result(property="productId", column="PRODUCTID", id=true)
@Result(property="name", column="NAME")
@Result(property="description", column="DESCN") Column value to pass as argument
@Result(property="categoryId", column="CATEGORY") to getProductDetail()
@Result(property="detail", column="PRODUCTID",
one=@One(select="getProductDetail"))
List<Product> getProductsWithNestedSelect(); Name of mapper method
that defines nested SELECT
@Select("SELECT productid, manufacturer, sku, upc, minimum_age " +
" FROM product_detail " +
" WHERE productid = #{value}")
@Result(property="productId", column="PRODUCTID", id=true)
@Result(property="manufacturer", column="MANUFACTURER")
@Result(property="sku", column="SKU")
@Result(property="upc", column="UPC")
@Result(property="minimumAge", column="MINIMUM_AGE")
ProductDetail getProductDetail(int productId);
Mastering Spring and MyBatis
5-23
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
One-to-Many Mapping
To load a one-to-many association, use the @Many annotation
– Uses a nested SELECT statement
▪ Again, nested result (join mapping) is not available through annotations
▪ If needed, use @ResultMap annotation to reference result map in XML mapping file
@Select("SELECT id, name " +
" FROM category")
@Result(property="categoryId", column="ID", id=true)
@Result(property="name", column="NAME") Column value to pass as argument to
@Result(property="products", column="ID", getProductListByCategory()
many=@Many(select="getProductListByCategory"))
List<Category> getCategoriesWithNestedSelect();
@Select("SELECT … FROM product " +
" WHERE category = #{categoryId}")
@Result(…)
…
List<Product> getProductListByCategory(int categoryId);
Mastering Spring and MyBatis
5-24
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 5.2: Using Annotations with MyBatis
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
5-25
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
DML Through MyBatis with XML
Transaction Management in Testing
SQL Mappers Using Annotations
Advanced Topics
Chapter Summary
Mastering Spring and MyBatis
5-26
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Embedded Databases
Spring provides first-class support for embedded databases
– HyperSQL, the default, http://hsqldb.org/
– H2, type="H2", http://www.h2database.com
– Apache Derby, type="DERBY", https://db.apache.org/derby/
pom.xml
▪ A version of Derby ships with the JDK as Java DB <!-- HyperSQL In-memory Database -->
– Others by extension <dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.7.1</version>
<beans … <scope>test</scope>
xmlns:jdbc="http://www.springframework.org/schema/jdbc" </dependency>
xsi:schemaLocation="…
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
…">
<!-- Define a DataSource for an in-memory database -->
Scripts are
<jdbc:embedded-database id="hsqldbDataSource"> executed in order
<jdbc:script location="classpath:products-hsqldb-schema.sql" />
<jdbc:script location="classpath:products-hsqldb-dataload.sql" />
</jdbc:embedded-database>
Mastering Spring and MyBatis
5-27
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Defining Data Sources with Profiles
Use Spring profiles to define different data sources for different environments
– Depending on the active profile, only one bean with id dataSource will be created
db-beans-prod.xml beans.xml
<beans profile="prod">
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- Define a production DataSource for Oracle -->
<property name="dataSource" ref="dataSource" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${db.url}" /> References the correct dataSource
<property name="driverClassName" value="${db.driver}" /> for the current environment
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
</beans> BeanFactory beanFactory =
new ClassPathXmlApplicationContext(
db-beans-dev.xml "beans.xml","db-beans-prod.xml","db-beans-dev.xml");
<beans profile="dev">
<!-- Define a test DataSource for an in-memory database -->
<jdbc:embedded-database id="dataSource"> Load all Set the active profile when
<jdbc:script location="classpath:products-hsqldb-schema.sql" /> config files you run the application
<jdbc:script location="classpath:products-hsqldb-dataload.sql" />
</jdbc:embedded-database>
$ java -Dspring.profiles.active=dev …
</beans>
Mastering Spring and MyBatis
5-28
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Things to Remember When Using Embedded Databases
Different dialects of SQL
– HyperSQL is more standards compliant than Oracle, Oracle has more features
– NUMERIC instead of NUMBER, VARCHAR instead of VARCHAR2, DATE only holds a date
– In general, DML and query behavior is more consistent than DDL
ORDER BY
– Queries only return a defined order if there is an ORDER BY
– Within a given engine, queries may be consistent from run to run with a given dataset
– Switching engine will make queries less consistent in ordering
Performance
– It should be obvious, but this will be radically different, especially for medium datasets
and upwards
Always do a final test on your target database
Mastering Spring and MyBatis
5-29
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Optional Exercise 5.3: Using an Embedded Database
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
5-30
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Handling Enumeration Types by Ordinal
MyBatis supports persisting Java enum types by ordinal value (position within enum)
– An EnumOrdinalTypeHandler can be used, but must be specified explicitly
<resultMap type="Product" id="ProductWithTypeIdMap">
<id property="productId" column="PRODUCTID"/> public enum ProductType {
<result property="name" column="NAME"/> PHYSICAL_MEDIA, DIGITAL_MEDIA, HYBRID_MEDIA
<result property="description" column="DESCN"/> }
<result property="categoryId" column="CATEGORY"/>
<result property="type" column="PRODUCT_TYPE_ID" Ordinal values
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/> 0, 1, 2
</resultMap>
<select id="getProductsWithTypeId" resultMap="ProductWithTypeIdMap">
SELECT productid, name, descn, category, product_type_id public class Product {
FROM product private ProductType type;
WHERE product_type_id IS NOT NULL …
</select> }
<insert id="insertProductWithTypeId" parameterType="Product">
INSERT INTO product (productid, name, descn, category, product_type_id)
VALUES (#{productId}, #{name}, #{description}, #{categoryId},
#{type, typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler})
</insert>
Mastering Spring and MyBatis
5-31
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Custom Type Handler for enum
If enum values in the database are not 0, 1, 2, …, define a MyBatis custom type handler
– Implement org.apache.ibatis.type.TypeHandler
– Or extend org.apache.ibatis.type.BaseTypeHandler
public enum ProductType { Codes assigned
PHYSICAL_MEDIA(12), DIGITAL_MEDIA(35), HYBRID_MEDIA(44); by DBA
private ProductType(int code) { … }
public static ProductType of(int code) { … }
MyBatis calls this method to
public class ProductTypeHandler extends BaseTypeHandler<ProductType> { convert database value
@Override
public ProductType getNullableResult(ResultSet rs, String col) throws SQLException {
return rs.getInt(col) != 0 ? ProductType.of(rs.getInt(col)) : null;
}
… Find the enum value that
matches the database value Configure the custom type
handler for this column
<result property="type" column="PRODUCT_TYPE_ID"
typeHandler="com.roifmr.leap.mybatis.ProductTypeHandler"/>
Mastering Spring and MyBatis
5-32
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Discriminators
A discriminator is a column that <resultMap id="MentorMap" type="Mentor" >
indicates which of a set of types <id property="id" column="mentor_id" />
is in a particular row <result property="firstName" column="mentor_first_name" />
<result property="lastName" column="mentor_last_name" />
– E.g., an inheritance hierarchy <collection … />
<discriminator javaType="int" column="mentor_type">
Acts as a switch statement to <case value="1" resultMap="FullTimeMentorMap" />
<case value="2" resultMap="PartTimeMentorMap" />
choose the ResultMap </discriminator>
</resultMap>
Child ResultMaps usually
<resultMap id="FullTimeMentorMap" type="FullTimeMentor"
extend the parent map extends="MentorMap">
– If so, the map used includes <result property="payPerWeek" column="pay_per_week" />
all parent and child columns </resultMap>
– If not, it only includes child <resultMap id="PartTimeMentorMap" type="PartTimeMentor"
columns extends="MentorMap">
<result property="hoursWorkedPerWeek" column="hours_per_week" />
<result property="hourlyPay" column="pay_per_hour" />
</resultMap>
Mastering Spring and MyBatis
5-33
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Passing Multiple Input Parameters
The parameterType attribute specifies <select id="getProductsByCategoryAndName"
parameterType="java.util.Map"
the type of the input parameter resultMap="ProductMap">
– If there’s only one parameter, SELECT productid, name, descn, category
FROM product
parameterType can be omitted WHERE category = #{categoryId}
– MyBatis will infer the parameter type AND name LIKE #{productName}
</select>
Can pass multiple parameters by name import org.apache.ibatis.annotations.Param;
– In the mapper interface, add @Param to
public interface ProductMapper {
each parameter List<Product> getProductsByCategoryAndName(
– MyBatis will implicitly pass the @Param("categoryId") int catId,
@Param("productName") String name);
parameter names and values in a Map …
– In <select>, parameterType is }
java.util.Map
@Override
public List<Product> getProducts(int catId, String name) {
return mapper.getProductsByCategoryAndName(catId, name + "%");
}
Mastering Spring and MyBatis
5-34
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Passing Multiple Input Parameters by Position
MyBatis also supports passing multiple parameters to a mapped statement by position
– Reference the parameters in the SELECT using #{paramN} syntax (N starts at 1)
– No @Param required
<select id="getProductsByCategoryAndNameParam" resultMap="ProductMap">
SELECT productid, name, descn, category
FROM product
WHERE category = #{param1}
AND name LIKE #{param2} || '%’
</select>
public interface ProductMapper {
…
List<Product> getProductsByCategoryAndNameParam(int categoryId, String name);
…
}
Mastering Spring and MyBatis
5-35
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Paginated ResultSets
Large numbers of records may be returned by some queries
MyBatis supports pagination of large ResultSets
– Using RowBounds
public interface ProductMapper {
▪ With offset (starting position) List<Product> getProducts();
▪ And limit (number of records) List<Product> getProducts(RowBounds bounds);
– Mapper XML does not need to change …
}
▪ Add parameter to interface
@Override
public List<Product> getProductsByBounds(int offset, int limit) {
RowBounds bounds = new RowBounds(offset, limit);
return mapper.getProducts(bounds);
}
// display the third page of 25 records
List<Product> productsCurrentPage = dao.getProductsByBounds(50, 25);
Mastering Spring and MyBatis
5-36
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
SqlSession and Mappers
The core of MyBatis is SqlSession
– Has methods to execute SQL commands (select, selectList, insert, update, etc.)
– Mapper methods are convenience methods mapped to SqlSession
Sometimes it is useful to use SqlSession directly
– When not using Spring, we might write:
try (SqlSession session = sqlSessionFactory.openSession()) {…}
– Do NOT do this in Spring
▪ That SqlSession is not thread-safe and will not participate in Spring transactions
– Instead create a bean from SqlSessionTemplate
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
http://mybatis.org/spring/sqlsession.html
Mastering Spring and MyBatis
5-37
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Using a Custom ResultHandler
There may be a situation where custom processing of query results is necessary
– Define a custom ResultHandler
– The handleResult method will be called for every row returned by the query
E.g., to return a Map with one property as key and another (not the entire object) as value
@Override
public Map<Integer, String> getProductIdNameMap() {
Map<Integer, String> map = new HashMap<>();
session.select("com.fidelity.integration.ProductMapper.getProducts", // query
new ResultHandler<Product>() {
@Override
public void handleResult(ResultContext<? extends Product> context) {
Product product = context.getResultObject();
map.put(product.getProductId(), product.getName());
}
});
return map;
}
Mastering Spring and MyBatis
5-38
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Calling a Stored Procedure with MyBatis
MyBatis can call stored procedures
Stored procedures that do not return results sets are straightforward:
– To pass more than one parameter, use the parameter map method, or a helper class
– The stored procedure can execute one or more INSERT, UPDATE, or DELETE
– Parameter modes: IN (read only), OUT (write only), INOUT (read and write)
– jdbcType is required only for NULLABLE values
<update id="deleteProductsByCategoryProcedure" parameterType="int" statementType="CALLABLE">
{ CALL proc_del_products_by_category( #{categoryId, mode=IN, jdbcType=NUMERIC} ) }
</update>
You can configure a stored procedure call using annotations:
@Update("{ CALL proc_del_products_by_category(#{categoryId, mode=IN, jdbcType=NUMERIC}) }")
@Options(statementType = StatementType.CALLABLE)
void deleteProductsByCategory(@Param("categoryId") int categoryId);
Mastering Spring and MyBatis
5-39
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Stored Procedures That Return Results
Each vendor treats this differently, here are two examples
– Oracle can only return results as an output parameter
<!-- Oracle style procedure returning a SYS_REFCURSOR as second parameter -->
<select id="getProductsByCategoryProcedure" parameterType="java.util.Map" statementType="CALLABLE">
{ CALL proc_products_by_category( #{categoryId, mode=IN, jdbcType=NUMERIC},
#{results, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=ProductMap} ) }
</select>
– MyBatis returns them …
as a member of the Map @SuppressWarnings("unchecked")
List<Product> products = (List<Product>) parameterMap.get("results");
return products;
– HyperSQL can return results directly and MyBatis treats them like any other query
<!-- HyperSQL style procedure returning a CURSOR as a result set -->
<select id="getProductsByCategoryProcedure" parameterType="int" statementType="CALLABLE"
resultMap="ProductMap">
{ CALL proc_products_by_category( #{categoryId, mode=IN, jdbcType=NUMERIC} ) }
</select>
Mastering Spring and MyBatis
5-40
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Optional Exercise 5.4:
Calling a Stored Procedure with MyBatis
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
5-41
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Caching in MyBatis
MyBatis provides support for caching query results
from <select> statements Database
– First-level cache is enabled by default
– Short-lived: managed by a single SqlSession
– When possible, SqlSession retrieves results from
first-level cache instead of executing the same
First-Level Cache
query twice
Global second-level caches can be enabled Session Object Client
– Using the <cache/> element in Mapper XML files
– Results can be cached in memory or written to disk
– May be shared by multiple SqlSessions
– This can also be customized Second-Level Cache
Optional
Mastering Spring and MyBatis
5-42
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Second-Level Caching in MyBatis
Adding <cache/> to a Mapper XML file does the following: <cache />
– All results from <select> statements will be stored in the cache
– All <insert>, <update>, and <delete> statements flush the cache
– The cache uses a Least Recently Used (LRU) policy
– There is no flush interval
– The cache will store up to 1024 references to lists or objects
– The cache is a read/write cache
▪ Retrieved objects are not shared
▪ They can be safely modified by the caller
▪ There will be no interference with other caller’s modifications
Mastering Spring and MyBatis
5-43
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Customizing Second-Level Caching
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
Caching can be customized by setting attributes in the <cache> element
– The eviction attribute sets the eviction policy
▪ LRU, FIFO (First in first out), SOFT (soft reference), WEAK (weak reference)
– The flushInterval attribute
▪ Cache flush interval in milliseconds
– The size attribute
▪ The maximum number of elements stored in the cache
– The readonly attribute
▪ A read-only cache will return the same cached object to all callers
▪ A read-write cache will return a serialized copy of a cached object
MyBatis also integrates with third-party cache libraries
– Like OSCache, Ehcache, Hazelcast
Mastering Spring and MyBatis
5-44
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Optional Exercise 5.5: Caching with MyBatis
30 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
5-45
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
DML Through MyBatis with XML
Transaction Management in Testing
SQL Mappers Using Annotations
Advanced Topics
Chapter Summary
Mastering Spring and MyBatis
5-46
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Summary
In this chapter, we have explored:
Performing database update operations with MyBatis
Managing transactions in JUnit tests with Spring
Configuring MyBatis with annotations
Using MyBatis custom type handlers and query caching
Using advanced MyBatis features
Mastering Spring and MyBatis
5-47
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mastering Spring and MyBatis
Chapter 6:
Functional Programming
Mastering Spring and MyBatis
6-1
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Overview
In this chapter, we will explore:
Functional programming
– A style of structuring a computer program
– Treats computation as the evaluation of mathematical functions
– Avoids the use of mutable data
Java support for functional programming
– Functional interfaces
– Lambda expressions
– Optional variables
– Stream API
Mastering Spring and MyBatis
6-2
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
Mastering Spring and MyBatis
6-3
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Functional Programming
Many languages (Java, C++, C#) are based on imperative programming
– Statements change the running state of the program
But some languages (LISP, Haskell, Erlang) are based on functional programming
– Functions map values to other values
– Use functions and expressions (declarations) instead of statements
In a functional language, output of a function depends only on the input arguments
– Calling the same function twice with the same arguments returns the same value
– No dependence on local or global state
Eliminates side effects
– No change in state that does not depend on the function inputs
– No need to worry about changes that you cannot see
The Java team started adding functional programming features in Java 8
Mastering Spring and MyBatis
6-4
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
Mastering Spring and MyBatis
6-5
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Lambda Expressions
A lambda expression can be thought of as an anonymous function
– Has a list of parameters, a body, and a return type
– Can be passed as an argument to another method
The name “lambda” comes from the lambda calculus
– Developed by mathematician Alonzo Church in the 1930s
– A system developed to study and formalize the concept of functions
Java lambda expression: concise way to define and use a callback method
– Can replace a full class definition
Mastering Spring and MyBatis
6-6
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Lambda Expressions (continued)
A lambda expression can replace an anonymous inner class
Defines the comparison
function as an anonymous
ticketList.sort(new Comparator<Ticket>() { inner class
@Override
public int compare(Ticket t1, Ticket t2) {
return Double.compare(t1.getCost(), t2.getCost());
}
});
– Here is the same operation using a lambda expression
ticketList.sort((t1, t2) -> Double.compare(t1.getCost(), t2.getCost()));
Defines the comparison function as a lambda
A lambda expression can be used anywhere a functional interface is used
– Functional interface: an interface that specifies a single abstract method
Mastering Spring and MyBatis
6-7
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
What Is a Functional Interface?
java.util.function package defines standard functional interfaces
– Example: method with a java.util.function.Function parameter
public List<String> applyFunction(List<String> items, Function<String, String> callback) {
List<String> newItems = new ArrayList<>();
for (String item : items) { Compiler enforces “single
abstract method” rule
String newItem = callback.apply(item);
@FunctionalInterface
newItems.add(newItem); public interface Function<T, R> {
} Call the “function” R apply(T t);
that was passed as }
return newItems; Types of Function’s
} an argument
argument and return value
Pass a lambda as the value of the Function’s callback argument
– Compiler will generate a class that implements the interface
List<String> nobles = List.of("Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon");
List<String> lowerNobles = applyFunction(nobles, s -> s.toLowerCase());
List<String> emphaticNobles = applyFunction(nobles, s -> s.concat("!"));
Mastering Spring and MyBatis
6-8
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Lambda Expressions Syntax
A lambda expression consists of three sections
– Lambda parameters
– Arrow
– Lambda body
ticketList.sort((t1, t2) -> Double.compare(t1.getCost(), t2.getCost()));
Lambda parameters “Arrow” Lambda body
Internally, the Java compiler converts a lambda to an anonymous inner class
Mastering Spring and MyBatis
6-9
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Lambda Expression vs. Inner Class
A lambda expression has some important differences from an inner class
Inner class creates a new scope
– Can overwrite local variables from enclosing scope
▪ Instantiate a new local variable with the same name in the inner class
▪ Use the keyword this in the inner class to refer to its instance
Lambda expressions work with the enclosing scope
– Cannot overwrite variables from enclosing scope in the lambda’s body
– The key word this refers to the enclosing instance
However, both lambdas and anonymous inner classes can only access variables of the
enclosing scope if they are “effectively final” or final
– A local variable that is not changed after initialization
– You may need to add final to variable or parameter declarations
Mastering Spring and MyBatis
6-10
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Lambda Expressions – Prefer Simplest Syntax
Let the compiler determine parameter types
– It will tell you if there is an ambiguity
// prefer this
a -> a.toLowerCase()
Braces and return statements not required for
one-line lambda bodies // to this
(String a) -> { return a.toLowerCase(); }
Parentheses not required for one parameter
Call a helper method if the body is complex
Instead of a complex lambda … … call a helper method
translateInput(s -> { translateInput(s -> translateHelper(s));
String result = …;
… // many lines of code private String translateHelper(String s) {
return result; String result = …;
}); … // many lines of code
return result;
}
Mastering Spring and MyBatis
6-11
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Method References
Many lambdas simply call an existing method
ticketList.forEach(t -> System.out.println(t)); Pass a lambda to forEach()
If a lambda’s only operation is to pass parameters to an existing method, it can be
replaced by a method reference
ticketList.forEach(System.out::println); Pass a method reference to forEach()
Internally, the Java compiler converts a method reference to a lambda
Method references may be used for static methods or instance methods
– Syntax: class-or-object-name::method-name
Mastering Spring and MyBatis
6-12
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Method References (continued)
Example: sorting a list of tickets
Existing method in Ticket class
public class Ticket {
public static int compareByCost(Ticket t1, Ticket t2) {
return Double.compare(t1.getCost(), t2.getCost());
}
…
} Lambda calls existing method
ticketList.sort((t1, t2) -> Ticket.compareByCost(t1, t2));
ticketList.sort(Ticket::compareByCost); Replace lambda with method reference
Method references are often used as arguments to Stream methods
– We’ll see these soon
Mastering Spring and MyBatis
6-13
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 6.1: Lambda Expressions
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
6-14
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
Mastering Spring and MyBatis
6-15
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Streams
Java streams are designed to process collections of values easily and efficiently
The stream library implementation manages the scheduling of the operations
– Supports parallel operations: each stream operation could execute in its own thread
Streams work on the “what, not how” principle
– You describe what needs to be done
– You don’t specify how to carry out the operations
Mastering Spring and MyBatis
6-16
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Streams vs. Iteration
Suppose we have a list of words
– Goal: find all words longer than 10 characters
// count the long words by iterating thru the list
int count = 0;
for (String w : words) {
if (w.length() > 10){
count++;
}
} // count the long words by using streams
long count = words.stream()
.filter(w -> w.length() > 10)
.count();
Best practice: use streams instead of iterating over a collection
– More explicit than using for loops and if statements
Mastering Spring and MyBatis
6-17
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Stream Characteristics
Streams do not store their elements
– They may be stored in a collection
– Or generated on demand
Streams do not modify their source
– They return a new stream with the results
Streams are lazy whenever possible
– May not be executed until the results are needed
– May even process an infinite stream sometimes
Mastering Spring and MyBatis
6-18
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Streams
A typical stream operation has three stages
1. Create the stream The stream() method creates a stream for a list
2. Specify the intermediate operations
The filter() method returns another stream,
a. Transform initial stream into other streams in this case, with words longer than 10 characters
3. Apply a terminal operation to produce a result
The count() method reduces the stream to a
a. This will execute any lazy operations long result (in this case, the number of words
b. Nothing happens until the terminal longer than 10 characters)
operation is called
// count the long words by using streams
long count = words.stream()
.filter(w -> w.length() > 10)
.count();
Mastering Spring and MyBatis
6-19
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Stream Operations with collect()
Task: create a new list by performing the same operation on all items of an existing list
– Problem: stream methods produce new streams, not lists
– Solution: create a list from a stream using a Collector
Pass a Collector object to the stream terminal operation collect()
– Collectors has methods that create collector objects
– Provides support for counting, summing, averaging, grouping, etc.
import java.util.stream.Collectors;
…
// convert strings to lowercase using streams
List<String> names = List.of("LoGan", "kElSEy", "SLOAN");
List<String> lowerNames =
words.stream() Could use a method reference:
.map(String::toLowerCase)
.map(s -> s.toLowerCase())
.collect(Collectors.toList());
Mastering Spring and MyBatis
6-20
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
Mastering Spring and MyBatis
6-21
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
NullPointerException Problem
NullPointerException (NPE) is a very common Java problem
– Any method that returns an object reference might return a null value
– If the caller forgets to check the method’s return value, an NPE may occur
public Person findPerson(String name) {
Person p = dao.queryPerson(name);
DAO method may
return p; return null
}
…
Person person = findPerson("Pat Drie");
No test for null
String email = person.getEmailAddress(); If person is null,
it causes an NPE
Caller of findPerson() forgot to ask, “What should happen if I get a null value?”
Mastering Spring and MyBatis
6-22
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
java.util.Optional Class
Java introduced the Optional class to prevent NPEs
Use Optional as a method’s return value
– A method that returns Optional cannot return null
– Caller of that method must call an Optional method to get the “real” value
public Optional<Person> findPerson(String name) {
Person p = dao.queryPerson(name);
return Optional.ofNullable(p); Method wraps the
Person in an Optional Optional always
} yields a non-null object
...
Person person = findPerson("Pat Drie").orElse(new Person("", ""));
String email = person.getEmailAddress(); NPE is impossible
Optional is a “speed bump”—it forces developers to slow down and think
– “What should happen if I get an Optional with no value?”
Mastering Spring and MyBatis
6-23
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Optional Conditional Methods
Optional can replace explicit tests for null values
Without Optional: Person person = findPerson(name);
if (person != null) {
Explicit test for null
userService.add(person);
} Execute the callback only if
the Optional has a value
With Optional:
findPerson(name).ifPresent(p -> userService.add(p));
findPerson(name).ifPresentOrElse(p -> userService.add(p),
() -> logger.info(name + " not found"));
Execute the second callback if
the Optional has no value
Optional defines methods that may be chained: filter(), map()
Best practice: return Optional from methods that return object references
Mastering Spring and MyBatis
6-24
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Concepts
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
Mastering Spring and MyBatis
6-25
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Exercise 6.2: Working with Streams and Optional
20 min
Follow the directions in your Exercise Manual
Mastering Spring and MyBatis
6-26
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Chapter Summary
In this chapter, we have explored:
Functional programming
– A style of structuring a computer program
– Treats computation as the evaluation of mathematical functions
– Avoids the use of mutable data
Java support for functional programming
– Functional interfaces
– Lambda expressions
– Optional variables
– Stream API
Mastering Spring and MyBatis
6-27
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Key Points
Pattern/Principle Pointers
Lambda expressions • Favor expressions over statements
• Chain lambda expressions instead of growing them
Functional interfaces • Define one abstract method per interface
• Use the @FunctionalInterface annotation
Optional variables • Use Optional as a return value
• Do not use Optional as a field or argument
• Use orElse() instead of get()
Streams • Prefer streams for iterating over collections
• Style favors intention over process
Mastering Spring and MyBatis
6-28
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Mastering Spring and MyBatis
Course Summary
Mastering Spring and MyBatis
7-1
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.
Course Summary
In this course, we have:
Used the Spring framework to build clean, extensible, loosely-coupled enterprise Java
applications
Utilized Spring as an object factory and dependency injection to wire components together
Understood and applied MyBatis to simplify access to relational databases
Explored and applied Spring to simplify the use of MyBatis in an application
Applied transaction strategies via configuration
Mastering Spring and MyBatis
7-2
© 2023 Copyright ROI Training, Inc. All rights reserved. Not to be reproduced without prior written consent.