FSE_SpringMyBatis
FSE_SpringMyBatis
Introduction
SKILL
KNOW-
LEDGE There is a Knowledge Checkpoint for this course
APPLICATION
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
@Override
public String getGreeting() {
return greeting;
}
@Override
public String getName() {
return name;
}
}
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
">
</beans>
Enables bean configuration Scan for annotated Can be a comma-separated list
via annotations beans in this package
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
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
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
</beans>
Get beans defined using XML exactly as with beans defined using annotations
Define the scope of a bean with the <bean> element’s scope attribute
– If omitted, singleton is the default
BeanFactory factory =
new AnnotationConfigApplicationContext(AppConfig.class);
g.greet();
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
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
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
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?
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”): }
}
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) }
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>
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
Can’t create Film first because its constructor needs a fully configured Budget
– Can’t create Budget first because it needs a Film
@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?
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
Use this:
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:beans.xml")
class BusinessServiceSpringTest {
@Autowired
BusinessService service;
Or (classes = ApplicationConfig.class)
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
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
@Component
public class Film {
private List<Actor> cast;
private String title;
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
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
<bean id="dao"
class="com.fidelity.advanced.DepartmentDao"
init-method="init"
destroy-method="close" >
…
</bean>
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
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;
@Value("#{T(java.lang.Double).valueOf(environment['tax_percent']) / 100.0}")
private double taxRate; // 0.07
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
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" />
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
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;
}
}
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
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”
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>
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
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
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
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
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
}
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
Accessing a Session means reconstructing the object graph from these tables
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
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
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
P roduct PRODU CT
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
<!-- 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;
}
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
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
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
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
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
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
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
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>
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>
Configuring a DataSource
Domain Store Design Pattern
Configuring MyBatis with Spring
Querying a Database with MyBatis in Spring
Working with Relationships
Chapter Summary
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
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>
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>
Spring provides support for managing transactions in tests with its TestContext framework
– Remember, in JUnit use @ExtendWith(SpringExtension.class)
The Spring documentation provides more detail on the options that are available
– https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html
@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() {
…
}
}
@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)
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>
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"/>
@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);
http://mybatis.org/spring/sqlsession.html
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;
}
– 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>
Optional
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
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
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
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
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
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
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());
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
Caller of findPerson() forgot to ask, “What should happen if I get a null value?”
Functional Programming
Lambda Expressions
Stream API
Optional Variables
Chapter Summary
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
Course Summary