Vous êtes sur la page 1sur 76

Apache iBATIS

Persistenz leicht(er) gemacht


Guido Schmutz
guido.schmutz@trivadis.com
1
Speaker
Trivadis
IT solution provider for more than 10 years
Solution portfolio: Application Development, Application Performance
Management, Business Communication, Business Intelligence, Managed
Services, Security, Training
Active in 10 locations in Switzerland and Germany
more than 420 employees, more than 400 clients
about 3300 training participants / year
research budget: CHF 2.6 million / EUR 1.7 million
Guido Schmutz
Working at Trivadis for 9 years
Java Architect
Consultant and Trainer for J2SE, J2EE and Oracle
More than 10 years of software development experience
2
What is iBATIS
4 Basic Steps of using SQL maps
The SQL Map XML File
SQL Map configuration
Spring integration
Apache iBATIS
3
What is iBATIS ?
A popular tool that works at the SQL level but abstracts
the use of JDBC completely
DAO framework and SQL Maps framework
Started by Clinton Begin
part of Apache since February 2005
Version for .NET available since June 2005
Simple idea
Statements are defined in an XML file
Specify a SQL string with parameter placeholders
In case of a query result columns are mapped to a result objects
4
What is iBATIS ?
provides support for caching query results
pluggable cache strategies
does not provide transparent persistence
no change detection for retrieved objects
strictly focus on mapping of input parameters and result values
for SQL statements
When using JavaBeans as values, a low-level kind of O/R
mapping is provided
Nested objects must be dealt with explicitly, no implicit
cascading of update or delete operations
5
What is iBATIS ?
Compared to high-level transparent persistence,
mapping at the SQL level has the following advantages:
Retain the full power of SQL
Set-based updates and deletes
Aggregate functions like AVG, SUM, and COUNT can be used
without any restrictions
Mapping a domain object to fields across multiple tables is
straightforward, allowing any granularity
Works with legacy database schemas
6
What is iBATIS ?
an applications data access code is still responsible
for:
Explicitly updating modified objects, keeping track of
potential modifications, and managing relationships between
objects
Converting application-level values to database-compatible
values
Full blown transparent persistence tools (like
Hibernate) offer a more convenient way for working
with persistent objects
Data-model driven vs. domain model driven
7
Data Mapper Concept
8
Installation
Installing the iBATIS Data Mapper framework is simply a
matter of placing the appropriate JAR files on the
classpath
iBATIS comes with the following JAR files that should
be on the classpath
NO iBATIS Data Access Framework ibatis-dao.jar
YES iBATIS Data Mapper Framework ibatis-sqlmap.jar
YES iBATIS Common Utilities ibatis-common.jar
Required Description File Name
9
What is iBATIS
4 Basic Steps of using SQL maps
The SQL Map XML File
SQL Map configuration
Spring integration
Apache iBATIS
10
4 Basic steps in using iBATIS SQL Map
1. Create Java objects to hold the data moving
from/to the database
2. Create the SQL Map XML File to map the Java
classes to the database
3. Create the SQL Map Configuration File
4. Execute the mapped statement from the
application
11
1. Create the Java Objects
For parameters and results (in case of Query)
JavaBeans, Map implementations, primitive wrapper
types (String, Integer ..) and even XML documents can
be used
public class Customer implements Serializable {
private long customerID;
private String firstName;
private String lastName;
private String companyName;
private java.util.Date dateOfBirth;
public long getCustomerID() {
...
Customer.java
12
2. Create the SQL Map XML File
A SQL Map will be used to specify the SQL statement to
be mapped to the Java object
<sqlMap namespace="Customer">
<typeAlias alias="customer" type="com.trivadis.domain.Customer"/>
<select id="getCustomer" resultClass="customer" parameterClass="long">
SELECT id as CustomerID
, first_name as firstName
, last_name as lastName
, company_name as companyName
, date_of_birth as dateOfBirth
FROM customer_t cust
WHERE cust.id = #value#
</select>
</sqlMap>
Customer.xml
13
3. Create the XML Configuration File
a central XML configuration file is used for configuration
<sqlMapConfig>
<settings
cacheModelsEnabled="true"
enhancementEnabled="true"/>
<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property ...
...
</dataSource>
</transactionManager>
<sqlMap resource="com/trivadis/dao/impl/maps/Customer.xml"/>
</sqlMapConfig>
sql-map-config.xml
14
4. Execute the mapped statement
The SQLMapClient API is simple and minimal
Functionality to configure an SQL Map, Execute an SQL update,
execute a query for single object and for a list of objects
public Customer getCustomer(long id) {
Customer cust = null;
try {
Reader reader = Resources.getResourceAsReader
("com/trivadis/dao/impl/sql-map-config.xml");
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
cust = (Customer)sqlMap.queryForObject("getCustomer", new Long(id));
} catch (Exception e) {
e.printStackTrace();
}
return cust;
}
CustomerDAOImpl.java
15
What is iBATIS
4 Basic Steps of using SQL maps
The SQL Map XML File
SQL Map configuration
Spring integration
Apache iBATIS
16
SQL Map XML File
XML descriptor which maps the JavaBeans, Map implementations,
primitive wrapper types (String, Integer ) and even XML
documents to an SQL statement
You can define and use as many SQL Map files as you like
<sqlMap>
<cacheModel/> *
<typeAlias/> *
<parameterMap/> *
<resultMap/> *
<statement/> *
<select | insert | update | delete | procedure/> *
</sqlMap>
17
Mapped Statements
Data Mapper concept is centered around mapped
statements
Mapped statements can be any SQL statement and can
have parameter maps (input) and result maps (output)
can be configured with a cache model to cache results
in memory
<select id="getAllUsers" resultMap="User" parameterClass="long">
SELECT id, username, password, active
FROM user_t
</select>
18
The <statement> Element
The <statement> element is a general "catch all"
statement that can be used for any type of SQL
statement
Its a good idea to use one of the more specific elements
provide more intuitive XML DTD and sometimes additional
features
<procedure> <update>
<select> <insert>
<delete> <statement>
19
The SQL
can be any SQL that is valid for the database
combination of SQL and XML, so there is the potential for
conflicting special characters
Put the SQL within a XML CDATA section !
<select id="getRegisteredAfter" resultClass="customer"
parameterClass="date">
<![CDATA[
SELECT id as customerID
, ...
FROM customer_t cust
WHERE registration_date > #value#
]]>
</select>
20
The <parameterMap> Element
The <parameterMap> is responsible for mapping
JavaBeans properties to the parameters of a statement
The parameterMap contains any number of parameter
mappings that map directly to the parameters of a statement
<parameterMap id="parameterMapName" [class="someDomainClass"]>
<parameter property="propertyName" [jdbcType="VARCHAR"]
[javaType="string"] [nullValue="-1"] [mode="-1"]/>
<parameter .../>
</parameterMap>
21
A <parameterMap> Example
<parameterMap id="adrParams"
class="com.trivadis.domain.Address">
<parameter property="addressCategory"/>
<parameter property="addressType"/>
<parameter property="city"/>
<parameter property="street"/>
<parameter property="zipCode"/>
<parameter property="country.id" nullValue="0"/>
</parameterMap>
<insert id="insertAddress" parameterMap="adrParams">
INSERT INTO address_t
(id, category_information, address_type, city, street,
zip_code, cd_country, customer_fk)
VALUES (address_psq.NEXTVAL, ?, ?, ?, ?, ?, ?, 1)
</insert>
22
<parameter> Element Attributes
property
Name of the JavaBeans
property of the parameter
object
jdbcType
Explicitly specifies the
database column type of the
parameter to be set by this
property
javaType
Explicitly specifies the Java
property type of the
parameter to be set
nullValue
Used to specify an outgoing
null value replacement
E.g. when the value is
detected, then a NULL will be
written to the database
mode
Used with stored procedure to
specify IN, OUT or INOUT
parameter
typeHandler
define customized processing
before parameters are set and
after values are retrieved
23
Inline Parameter Maps
the syntax for declaring parameterMaps is very verbose
There is a more popular syntax that simplifies the
definition and reduce code
By default, any Mapped Statement that has no explicit
parameterMap specified will be parsed for inline parameters
<insert id="insertAddress" parameterClass="com.trivadis.domain.Address">
INSERT INTO address_t
(id, category_information, address_type, city, street,
zip_code, cd_country, customer_fk)
VALUES (address_psq.NEXTVAL, #addressCategory#, #addressType#,
#city#, #street#, #zipCode#, #country.id#, 1)
</insert>
24
Inline Parameter Maps
Declaring types and null value replacement can also be
accomplished with inline parameters
Note: with a lot of type descriptors and null value replacements,
cleaner code might be achieved by using an external parameterMap
<insert id="insertAddress"
parameterClass="com.trivadis.domain.Address">
INSERT INTO address_t
(id, category_information, address_type, city, street,
zip_code, cd_country, customer_fk)
VALUES (address_psq.NEXTVAL, #addressCategory#, #addressType#,
#city:VARCHAR:NO_CITY#, #street#, #zipCode#,
#country.id:NUMERIC:0#, 1)
</insert>
25
Primitive Type Parameters
It is not always necessary or convenient to write a
JavaBean just to use a parameter
In these cases it is possible to use a primitive wrapper
objects
e.g. String, Integer, Date, etc.
The #value# parameter will be replaced with the value of the
Integer instance
<statement id="getProduct" parameterClass="java.lang.Integer">
SELECT * FROM product_t
WHERE product_id = #value#
</statement>
sqlMapClient.queryForObject("getProduct", new Integer(1));
26
Map Type Parameters
If writing a JavaBean class is not convenient and a
simple parameter wont do (cause there are multiple
parameters), a Map can be used
e.g. HashMap, TreeMap
<statement id="getAddresses" parameterClass="java.util.Map">
SELECT * FROM addresses_t
WHERE customer_fk = #customerDTO.id#
AND category_information = #category#
</statement>
params.put("customerDTO", customerDTO);
params.put("category", "Business");
sqlMapClient.queryForList("getAddresses", params);
27
Result Maps
ResultMaps are an extremely important component of
Data Mapper
The <resultMap> is responsible for mapping JavaBean
properties to the columns of a ResultSet
<resultMap id="resultMapName" class="someDomainClass"
[extends="parent-resultMap"]>
<result property="propertyName" column="COLUMN_NAME"
[columnIndex="1"] [javaType="int"] [jdbcType="NUMERIC"]
[nullVlaue="-1"] [select="someOtherStatement"]/>
<result .../>
</resultMap>
28
A <resultSet> Example
<resultMap id="customerDTO" class="CustomerProfileDTO">
<result property="customerID" column="ID" nullValue="0"/>
<result property="title" column="TITLE"/>
<result property="firstName" column="FIRST_NAME"/>
<result property="lastName" column="LAST_NAME" />
<result property="email" column="EMAIL" />
</resultMap>
<select id="baseSelectCustomer" resultMap="customerDTO">
SELECT cus.id,
title1.value_de title,
cus.first_name,
cus.last_name,
cus.email
FROM customer_t
LEFT OUTER JOIN code_t title1 ON (cus.cd_title = title1.id)
</select>
29
<result> Element - Attributes
property
the name of a JavaBeans
property of the result object that
will be returned by the mapped
statement
column
the name of the column in the
ResultSet
columnIndex
Optional (minimal) performance
enhancement. The index of the
column in the ResultSet
jdbcType
explicitly specifies the database
column type of the ResultSet
column
javaType
explicitly specifies the Java type of
the property to be set
nullValue
Specifies the value to be used in
place of a NULL value in the DB
select
Describes a relationship between
objects and automatically loads
complex property types.
name of another mapped statement
typeHandler
typeHandler that will perform
customized processing before
parameters are set and after values
are retrieved
30
Implicit Result Maps
A query can be processed without defining a resultMap
column names (or aliases) returned match up with the writable
properties of the bean
<select id="getCustomer" parameterClass="long"
resultClass="com.trivadis.domain.Customer">
SELECT id AS customerID
, first_name AS firstName
, registration_date as registrationDate
, email
, ...
FROM customer_t cust
WHERE cust.id = #value#
</select>
public class Customer {
private long customerID;
private String firstName;
private String email;
...
private Date registrationDate;
31
Primitive Results
ResultMaps can populate simple Java type wrapper
such as String, Integer, Booleans
Either by using a resultMap
or simpler by using a result class in a mapped statement (and
using the column alias value)
<resultMap id="get-id-result" resultClass="java.lang.Integer">
<result property="value" column="NUMBER_OF_CUSTOMERS"/>
</resultMap>
<statement id="getCustomerCount" resultClass="int"
SELECT count(*) AS value
FROM customer_t
</statement>
32
Map Results
ResultMaps can populate a Map instance such as
HashMap or TreeMap
Note: Can be queried as a
single object or as a list
<resultMap id="code-result-map" resultClass="java.util.HashMap">
<result property="id" column="ID"/>
<result property="code" column="CODE"/>
<result property="value" column="VALUE_DE"/>
</resultMap>
<select id="getAllCountriesAsMap" resultMap="code-result-map">
SELECT id, code, value_de
FROM code_t
WHERE code_type_code = 'CNTRY'
</select>
Map map = (Map)
sqlmap.queryForList("getAllCountriesAsMap")
{code=CH, value=Schweiz, id=36}
{code=DE, value=Deutschland, id=37}
{code=IT, value=Italien, id=38}
33
Map Results
A map can also be retrieved using an implicit result
map with a Map type
<select id="getAllCountriesAsImplicitMap"
resultClass="java.util.HashMap">
SELECT id, code, value_de value
FROM code_t
WHERE code_type_code = 'CNTRY'
</select>
Map map =
(Map)sqlmap.queryForList("getAllCountriesAsImplicitMap")
{code=CH, value=Schweiz, id=36}
{code=DE, value=Deutschland, id=37}
{code=IT, value=Italien, id=38}
34
Results as maps
result beans can be loaded into a map instead of a list
the map is keyed by the 3rd parameter specified
The value of the map can be the whole result object or just one
property from the object, specified by the 4th parameter
<select id="getAllCountries" resultClass="TitleCode">
SELECT id, code, value_de AS value
FROM code_t
WHERE code_type_code = 'CNTRY'
</select>
// Map of TitleCode objects, keyed by code
sqlmap.queryForMap("getAllCountries", null, "code");
// Map of ids, keyed by code
Sqlmap.queryForMap("getAllCountries", null, "code", "id");
35
Mapping Results to XML document
<select id="getCustomer" resultClass="xml"
xmlResultName="customer"
parameterClass="long">
SELECT id as customerID
, first_name as "firstName"
, last_name as "lastName"
, email as "email"
, date_of_birth as "dateOfBirth"
FROM customer_t cust
WHERE cust.id = #value#
</select>
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<CUSTOMERID>1</CUSTOMERID>
<firstName>Guido</firstName>
<lastName>Schmutz</lastName>
<email>guido.schmutz@trivadis.com</email>
<dateOfBirth>1968-06-13 00:00:00.0</dateOfBirth>
</customer>
36
Customer
- id: l ong
- firstName: Stri ng
- lastName: Stri ng
- companyName: String
- dateOfBirth: java.util.Date
- phoneNumber: String
- faxNumber: Stri ng
- email: Stri ng
- registrati onDate: java.util .Date
Address
- id: l ong
- addressCategory: String
- addressType: String
- street: Stri ng
- zipCode: Stri ng
- city: String
+ fmtPostal Address() : Stri ng
+ toXML() : Stri ng
Wine::Wine
- id: l ong
- productCode: Stri ng
- shortName: String
- longName: String
- price: Double
- percentByVolume: Doubl e
- vintage: Integer
- degustationNote: Stri ng
- caseSize: Integer
- award: String
- pi cture: Byte[]
User::User
- username: String
- password: String
1..* +addresses
1
1
+mainAddress
0..1
0..1
+user
0..*
+favori teWines
0..*
Complex
properties
Its possible to automatically
populate properties of
complex types
in the DB the data is usually
modeled via a 1:1 or 1:M
relationship
Wine::wine_t
Wine::favorite_t
Meta::user_t
Customer::customer_t
Customer::address_t
0..* 1
0..*
1
1 +PK_product_t
0..* +FK_fav_prod
0..*
(id = id)
1
37
Complex properties
Associate a statement with the
mapping for 1:1, 1:m + n:m relations
column id is passed as a parameter to
getUserByCustomerID which returns a user
<resultMap id="customerResult" class="customer">
<! all scalar properties of Customer -->
<result property=id" column="ID"/>
<result ...
<!-- 1:1 relation to User -->
<result property="user" column="ID" select="User.getUserByCustomerID"/>
<!-- 1:m relation to Address -->
<result property="addresses" column="ID"
select="Address.getAddressesByCustomerID"/>
<!-- 1:1 relation to MainAddress -->
<result property="mainAddress" column="ID"
select="Address.getMainAddressByCustomerID"/>
<!-- n:m relation to Wine -->
<result property="favoriteWines" column="ID"
select="Wine.getWinesByCustomerID"/>
</resultMap>
Customer
- i d: l ong
- firstName: Stri ng
- l astName: Stri ng
- companyName: Stri ng
- dateOfBi rth: j ava.uti l.Date
- phoneNumber: String
- faxNumber: Stri ng
- emai l: Stri ng
- regi strati onDate: j ava.uti l.Date
Address
- i d: l ong
- addressCategory: String
- addressType: String
- street: Stri ng
- zipCode: Stri ng
- city: Stri ng
+ fmtPostal Address() : String
+ toXML() : Stri ng
Wine::Wine
- id: long
- productCode: Stri ng
- shortName: Stri ng
- longName: Stri ng
- price: Double
- percentByVol ume: Doubl e
- vi ntage: Integer
- degustati onNote: Stri ng
- caseSize: Integer
- award: Stri ng
- pi cture: Byte[]
User::User
- username: Stri ng
- password: String
1..* +addresses
1
1 +mainAddress
0..1
0..1
+user
0..*
+favori teWines
0..*
38
Complex properties
<select id="getCustomer" resultMap="customerResult"
parameterClass="long">
SELECT id, cd_title, first_name, last_name, company_name,
phone_number, fax_number, email, date_of_birth, registration_date
FROM customer_t
WHERE id = #value#
</select>
<resultMap id="userResult" class="com.trivadis.domain.User">
<result property="username" column="username"/>
<result property="password" column="password"/>
</resultMap>
<select id="getUserByCustomerID" resultMap="userResult"
parameterClass="long">
SELECT username, password
FROM user_t
WHERE id = #value#
</select>
39
Complex properties: N+1 Selects problem
The problem with this solution is, that whenever a
Customer is loaded, 5 SELECTs in total are executed
1 to retrieve the customer details (main query)
4 queries to retrieve the related complex properties
1 for each of the properties user, addresses, mainAddress,
favoriteWines
seems trivial when loading a single Customer
if the query was run to load 10 Customers, 4 additional
queries would be executed for each Customer
4 * 10 = 40 additional queries => N+1 or in this case 40 + 1 = 41
40
Complex properties: Avoiding N+1 Selects
One solution is to use a join and nested property
mappings instead of separate select statements
<resultMap id="customerResult" class="customer" groupBy="id">
<result property="id" column="ID"/>
<result ...
<!-- 1:1 relation to User -->
<result property="user.username" column="USERNAME"/>
<result property="user.password" column="PASSWORD"/>
<!-- 1:m relation to Address -->
<result property="addresses" resultMap="Customer.addressResult"/>
<!-- 1:1 relation to MainAddress -->
<result property="mainAddress.addressType" column="MA_ADDRESS_TYPE"/>
<result property="mainAddress.street" column="MA_STREET"/>
<result property="mainAddress.zipCode" column="MA_ZIP_CODE"/>
<result property="mainAddress.city" column="MA_CITY"/>
<!-- n:m relation to Wine -->
<result property="favoriteWines" column="ID"
select="Wine.getWinesByCustomerID"/>
</resultMap>
41
Complex properties: Avoiding N+1 Selects
with the corresponding mapped statement
<select id="getCustomer" resultMap="customerResult" parameterClass="long">
SELECT cust.id, cust.first_name, cust.last_name, cust.company_name,
cust.phone_number, cust.fax_number, cust.email, cust.date_of_birth,
cust.registration_date,
usr.username, usr.password,
adr.address_type, adr.street, adr.zip_code, adr.city,
mainadr.address_type AS ma_address_type, mainadr.street AS ma_street,
mainadr.zip_code AS ma_zip_code, mainadr.city AS ma_city,
title.id title_id, title.code title_code, title.value_de title_value
FROM customer_t cust RIGHT OUTER JOIN address_t adr
ON (cust.id = adr.customer_fk)
INNER JOIN address_t mainadr
ON (cust.id = adr.customer_fk AND adr.category_information = 'Main')
RIGHT OUTER JOIN user_t usr ON (cust.id = usr.id)
WHERE cust.id = #value#
</select>
42
Lazy Loading vs. Joins
using a join is not always better
If the related object is rarely accessed then it might be
faster to avoid the join
and better to use the sub-select solution with lazy
loading and byte-code enhancement options enabled
Currently lazy loading can only be set on the whole SQL Map config
<sqlMapConfig>
<settings
cacheModelsEnabled="true"
lazyLoadingEnabled="true"
enhancementEnabled="true"/>
sql-map-config.xml
43
Programming with the DataMapper:
The API
The SQLMapClient API is simple and minimal
1. Configuration
Reader reader = Resources.getResourceAsReader
("com/trivadis/dao/impl/sql-map-config.xml");
SqlMapClient sqlMap =
SqlMapClientBuilder.buildSqlMapClient(reader);
44
Programming with the DataMapper:
The API
2. Execute an SQL update
3. Execute a query for a single object
4. Execute a query for a list of objects
sqlMap.update("updateCustomer", cust);
sqlMap.insert("insertCustomer", cust);
sqlMap.delete("deleteAddress", new Long(id));
Customer cust = sqlMap.queryForObject("getCustomer", key);
List list = sqlMap.queryForList("getCustomers", null);
45
Programming with the DataMapper:
Transactions
By default, calling an execute method on SqlMapClient
instance will auto-commit or auto-rollback
The SQLMapClient interface has methods that allow you
to demarcate transactional boundaries
try {
sqlMap.startTransaction();
sqlMap.update("updateCustomer");
sqlMap.insert("favorites");
sqlMap.commitTransaction();
} finally {
sqlMap.endTransaction();
}
46
Programming with the DataMapper:
Batches
Batches allow to execute multiple non-query
statements as a batch
to minimize network traffic
Using batches is simple with the SQL Map API
Upon calling executeBatch(), all batched statements will be
executed through the JDBC driver
sqlMap.startBatch();
// ... Execute statements in between
SqlMap.executeBatch()
47
Reusing SQL-fragments
Use <sql> to avoid duplicate fragments of SQL
<sql id="base-select">
SELECT id as customerID
, ...
FROM customer_t cust
</sql>
<select id="getCustomer" resultClass="cust" parameterClass="long">
<include refid="base-select"/>
WHERE cust.id = #value#
</select>
<select id="getCustomersOrdered" resultClass="cust">
<include refid="base-select"/>
ORDER BY registration_date DESC
</select>
48
Stored Procedures
Stored procedures are supported via the <procedure>
element
<parameterMap id="param" class="map">
<parameter property="crit.wineTypeID" javaType="long"
jdbcType="NUMERIC" nullValue="0" mode="IN" />
<parameter property="crit.wineCategoryID" javaType="long"
jdbcType="NUMERIC" nullValue="0" mode="IN" />
<parameter property="crit.countryID" javaType="long"
jdbcType="NUMERIC" nullValue="0" mode="IN" />
<parameter ... />
</parameterMap>
<procedure id="searchWine" parameterMap="Wine.param">
{call pck_wine.searchWine(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}
</procedure>
49
Caching Mapped Statement Results
The results from a Query Mapped Statement can be
cached simply by specifying the cacheModel parameter
The cache model is configured within the SQL map
using the cacheModel element
<select id="getAllCountries" resultMap="codeResult"
cacheModel="country-cache-lru">
SELECT id, code, value_de
FROM code_t
WHERE code_type_code = 'CNTRY'
</select>
50
Caching Mapped Statement Results
Cache Types
cache model uses a pluggable framework
MEMORY uses reference type to manage the cache behavior
LRU uses a Least Recently Used algorithm
FIFO uses a First In First Out algorithm
OSCACHE uses the OSCache 2.0 caching engine
<cacheModel id="country-cache" type="MEMORY">
<flushInterval hours="24"/>
<flushOnExecute statement="insertCountry"/>
<property name="refrence-type" value="WEAK"/>
</cacheModel>
<cacheModel id="county-cache-lru" type="LRU">
<property name="size" value="1000"/>
</cacheModel>
51
Dynamic Mapped Statements
A very common problem with working directly with
JDBC is dynamic SQL
normally very difficult to work with SQL statements
that change not only the values of parameters
but which parameters and columns are included at all
The typical solution is usually a mess of conditional if-else
statements and string concatenations
The Data Mapper API provides an elegant solution that
can be applied to any mapped statement element
52
Dynamic Mapped Statements
<select id="findCustomers" resultMap="customerResult">
SELECT id, first_name, last_name, company_name, phone_number,
fax_number, email, date_of_birth, registration_date
FROM customer_t cust
WHERE 1 = 1
<dynamic prepend="AND">
<isNotNull prepend="AND" property="firstName">
cust.first_name $operator$ #firstName#
</isNotNull>
<isNotNull prepend="AND" property="lastName">
cust.last_name $operator$ #lastName#
</isNotNull>
</dynamic>
<dynamic open=" cust.id IN (SELECT customer_fk FROM address_t
adr WHERE " close=")" prepend="AND">
<isNotNull prepend="AND" property="street">
adr.street $operator$ #street#
</isNotNull>
<isNotNull prepend="AND" property="city">
adr.city $operator$ #city#
</isNotNull>
</dynamic>
</select>
53
Simple Dynamic SQL Elements
Sometimes only a small piece of the SQL needs to be
dynamic
SQL statements can contain simple dynamic SQL
elements to help implement:
dynamic order by clauses
dynamic select columns
dynamic table names
etc.
The concept works much like parameter maps, but uses
a slightly different syntax
54
Simple Dynamic SQL Elements
<select id="findCustomers" resultMap="customerResult"
parameterClass="map">
SELECT id, first_name, last_name, company_name, phone_number,
fax_number, email, date_of_birth, registration_date
FROM customer_t cust
WHERE cd_language = #language#
AND $whereclause$
ORDER BY $orderlist$
</select>
Map paramMap = new HashMap();
paramMap.put("language", new Long(1));
paramMap.put("whereclause", "1=1");
paramMap.put("orderlist", "registration_date DESC");
return sqlmap.queryForList("Customer.findCustomers", paramMap);
SELECT id, first_name, last_name, company_name, phone_number, fax_number,
email, date_of_birth, registration_date
FROM customer_t cust WHERE cd_language = ? AND 1=1
ORDER BY registration_date DESC
55
Auto-Generated Keys
Data Mapper supports auto-generated keys via the
<selectKey> element of the <insert>.
Both pre-generated (Oracle) and post-generated (MS-SQL
Server) keys are supported
<insert id="insertCustomerAndUser" parameterClass="customer">
<selectKey resultClass="long" keyProperty="customerID">
SELECT customer_psq.NEXTVAL AS customerID FROM DUAL
</selectKey>
INSERT INTO customer_t (id, cd_title, first_name, last_name,
company_name, date_of_birth, phone_number,
fax_number, email, cd_language, registration_date)
VALUES (#customerID#, #title.id#, #firstName#, #lastName#,
#companyName#, #dateOfBirth#, #phoneNumber#,
#faxNumber#, #email#, 1, #registrationDate#)
</insert>
56
TypeHandlerCallback
simple interface for custom type handlers.
a type handler can perform customized processing
before parameters are set on a PreparedStatement
and after values are retrieved from a ResultSet.
By using a custom type handler the framework can be
extended
to handle types that are not supported
handle supported types in a different way
a custom type handler might be used to implement booleans in
the database using "Y" and "N" instead of the more typical 0/1.
57
TypeHandlerCallback - Example
boolean handler that uses "Y" and "N"
public class CharBoolTypeHandlerCallback implements TypeHandlerCallback {
private static final String YES = "Y";
private static final String NO = "N";
public Object getResult(ResultGetter getter) throws SQLException {
String s = getter.getString();
if (YES.equalsIgnoreCase(s)) {
return new Boolean(true);
} else if (NO.equalsIgnoreCase(s)) {
return new Boolean(false);
} else {
throw new SQLException("Unexpected value " + s + " found where "
+ YES + " or " + NO + " was expected.");
}
}
...
58
TypeHandlerCallback Example (cont.)
public void setParameter(ParameterSetter setter, Object parameter)
throws SQLException {
boolean b = ((Boolean) parameter).booleanValue();
if (b) {
setter.setString(YES);
} else {
setter.setString(NO);
}
}
public Object valueOf(java.lang.String s) { ... }
}
<typeAlias alias="charToBool" type="CharBoolTypeHandlerCallback"/>
<resultMap id="userResult" class="User">
<result ...
<result property="active" column="active" typeHandler="charToBool"/>
</resultMap>
<select id="getAllUsers" resultMap="userResult" parameterClass="long">
SELECT id, username, password, active FROM user_t
</select>
59
What is iBATIS
4 Basic Steps of using SQL maps
The SQL Map XML File
SQL Map configuration
Spring integration
Apache iBATIS
60
SQL Map XML Configuration File
Data Mapper is configured using a central XML
configuration file, providing
configuration details for DataSources
other options, like thread management
<sqlMapConfig>
<properties/>
<settings/>
<typeAlias/> *
<transactionManager>
<dataSource/>
</transactionManager>
<sqlMap/> *
</sqlMapConfig>
61
The <properties> Element
The sqlMapConfig can hold a single <properties> element that
allows a standard Java properties file to be associated with the
XML configuration document
By doing this, each named value in the properties file becomes a
variable that can be referred to in the SQL map configuration file
If the properties file contains the following
it can be referenced in the config by using the placeholder syntax
url=jdbc:oracle:thin:@ltgus.trivadis.com:1521:ltgus10
<property name="JDBC.ConnectionURL" value="${url}" />
<properties resource="database.properties"/>
62
The <settings> Element
The <settings> elements allows to configure various options and
optimizations for the SqlMapClient instance
The <settings> element and all its attribute are optional
Always refer to mapped statements by a fully qualified name
(combination of the sqlMap name and the statement name)
useStatementNamespace
Enables runtime bytecode enhancements enhancementEnabled
Globally enables or disables lazy loading lazyLoadingEnabled
Globally enables or disables caching cacheModelsEnabled
Maximum number of threads that can enter a transaction at a time maxTransactions
number of sessions (or clients) that can be active at a given time maxSessions
maximum number of threads that can execute an SQL statement at a
time
maxRequests
63
The <transactionManager> Element
<transactionManager> configures the transaction
management services for a SQL Map
The type attribute indicates which transaction manager
to use
<transactionManager type="JDBC">
<dataSource
Allows you to manage transaction on your own (transactions
will not be committed as part of the framework lifecycle)
EXTERNAL
Uses JTA global transaction such that SQL Map activities can
be included as part of a distributed transaction
JTA
Allows JDBC to control the transaction JDBC
64
The <dataSource> Element
SimpleDataSourceFactory (SIMPLE) based on iBATIS
connection pooling implementation
DbcpDataSourceFactory (DBCP) uses Jakarta DBCP
(Database Connection Pool) to provided connection pooling
services
JndiDataSourceFactory (JNDI) retrieves DataSource from
a JNDI context
<dataSource type="SIMPLE">
<property value="${driver}" name="JDBC.Driver" />
<property value="${url}" name="JDBC.ConnectionURL" />
<property value="${username}" name="JDBC.Username" />
<property value="${password}" name="JDBC.Password" />
</dataSource>
65
The <sqlMap> Element
The <sqlMap> element is used to explicitly include an
SQL Map or another SQL map configuration file
Each Sql Map XML file that is going to be used by this
SQLMapClient instance must be declared
Can be loaded from the classpath
or from a URL
<sqlMap resource="com/trivadis/dao/impl/maps/Customer.xml" />
<sqlMap url="file:///c:/config/Customer.xml" />
66
What is iBATIS
4 Basic Steps of using SQL maps
The SQL Map XML File
SQL Map configuration
Spring integration
Apache iBATIS
67
Core J2EE Patterns - Data Access Object
a Data Access Object (DAO) is used to abstract and
encapsulate all access to the data source.
68
Middle Tier
Presentation
Resource Tier
Business Logic
Client Tier
RDBMS
Consumed Service
BL
I
n
t
e
g
.

P
r

s
.
Rich Client
Java Swing
G
U
I
Remoting
Browser
HTTP
Integration
P
r
o
x
y
Persistent Domain Objects / Core OO Model / DTOs
Mobile Device
G
U
I
Java ME
Remoting
W
e
b

A
c
t
i
o
n
s
R
e
m
o
t
e

S
e
r
v
i
c
e

E
x
p
o
r
t
e
r
s
Remoting
Rich Client
.NET
Framework
G
U
I
O
/
R

M
a
p
p
i
n
g
D
a
t
a

A
c
c
e
s
s

O
b
j
e
c
t
(
D
A
O
)
V
i
e
w
C
o
m
p
o
n
e
n
t
s
JDBC
JDBC
MOM
(Queue)
JMS
Crosscutting Concerns (Transaction, Security )
Email
Unit & IntegrationTesting
A
J
A
X
HTTP
B
u
s
i
n
e
s
s

S
e
r
v
i
c
e
69
Spring DAO Support
The DAO (Data Access Object) support in Spring is
primarily aimed at making it easy to work with different
data access technologies (like JDBC, Hibernate, EJB3 or
iBATIS) in a standardized way.
This allows to switch between them fairly easily and to
code without worrying about catching exceptions that are
specific to each technology.
70
Spring common persistence features
replaces DAOFactory classes with configurations and
dependency injection
easy configuration of DAO using dependency injection
base DaoSupport classes for popular persistence
frameworks
JDBC, iBATIS, Hibernate, Toplink, EJB3,
converts exceptions to a common exception hierarchy
template classes reduce redundant code
opens and closes necessary resources (such as
database connections)
71
Using Spring iBATIS DAO Abstraction
public class UserDAOSqlMap extends SqlMapClientDaoSupport implements UserDAO {
public User getUser(Long id) {
return (User) getSqlMapClientTemplate().queryForObject("getUser", id);
}
...
<sqlMap namespace="User">
<resultMap id="result" class="com.tvd.auction.domain.User">
<result property="id" column="user_id"/>
<result property="firstName" column="first_name"/>
<result property="lastName" column="last_name"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
</resultMap>
<statement id="getUser" resultMap="result" parameterClass="java.lang.Long">
SELECT usr.user_id, usr.first_name, usr.last_name, usr.password, usr.email,
FROM user_t usr WHERE usr.user_id = #id#
</statement>
72
Using Spring iBATIS DAO Abstraction
<bean id="dataSource">...</bean>
<bean id="incrementer">...</bean>
<bean id="sqlMapClient"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">
<value>classpath:/com/tvd/auction/context/sqlmap-config.xml</value>
</property>
</bean>
<bean id="user" class="com.tvd.auction.dao.UserDAOSqlMap">
<property name="dataSource"><ref local="dataSource" /></property>
<property name="sqlMapClient"><ref local="sqlMapClient"/></property>
<property name="incrementer"><ref local="incrementer" /></property>
</bean>
73
On-line Resources
Apache iBATIS Site
http://ibatis.apache.org/
Frequently Asked Questions
http://opensource2.atlassian.com/confluence/oss/display/IBATIS/Fre
quently+Asked+Questions
Article: iBatis Dao
http://www.onjava.com/pub/a/onjava/2005/08/10/ibatisdao.html
Article: Using iBatis SQL Maps for Java Data Access
http://www.developer.com/db/article.php/3346301
Spring Framework
http://www.springframework.org
74
iBATIS SQL Maps is a straightforward data access tool
Externalizes SQL statements
full control over the SQL issued
Can replace direct JDBC coding completely
Do you ever again want to use plain JDBC ?
The SQL Maps approach is particularly suitable for read-
only reference data
A complex read-write domain model is not a good fit !
Good integration with the Spring Framework
Apache iBATIS: Summary
Thanks!
?

Vous aimerez peut-être aussi