However, the steps involved in using a JDBC are always the same—obtain a connection,create a Statement, execute a query, run it through ResultSet, and release the resources.The following
Trang 3Just Spring Data Access
Madhusudhan Konda
Trang 4Just Spring Data Access
by Madhusudhan Konda
Copyright © 2012 Madhusudhan Konda All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472 O’Reilly books may be purchased for educational, business, or sales promotional use Online editions are also available for most titles (http://my.safaribooksonline.com) For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.
Editors: Mike Loukides and Meghan Blanchette
Production Editor: Iris Febres
Copyeditor: Gillian McGarvey
Proofreader: Iris Febres
Interior Designer: David Futato
Illustrator: Robert Romano
Revision History for the First Edition:
2012-06-01 First release
See http://oreilly.com/catalog/errata.csp?isbn=9781449328382 for release details.
Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of
O’Reilly Media, Inc Just Spring Data Access, the image of the channel-billed cuckoo, and related trade
dress are trademarks of O’Reilly Media, Inc.
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks Where those designations appear in this book, and O’Reilly Media, Inc., was aware of a trademark claim, the designations have been printed in caps or initial caps.
While every precaution has been taken in the preparation of this book, the publisher and authors assume
no responsibility for errors or omissions, or for damages resulting from the use of the information tained herein.
con-ISBN: 978-1-449-32838-2
Trang 5Table of Contents
Foreword v Preface vii
Trang 6Using Spring Hibernate 31
Trang 7Reading headlines like “Facebook moves 30-petabyte Hadoop cluster to new data ter” shows that one of the biggest struggles we are facing today is Big Data and itsmanagement Data centric applications, mobile front ends to complex data structures,and serving millions of clients accessing our datasets while handling billions of trans-actions a day shows that keeping data management simple and easy to handle is a firstclass problem in modern application development
cen-Thankfully, tools like Spring Data and it’s many utilities make it easy to access thesedata sets using whichever flavor of standards best fits our team’s skills and needs WhileJava blazed the trail by offering the flexible but consistent JDBC standard, it was thepower of Spring that cut out the tedious amounts of boilerplate afforded to us by his-torical SQL paradigms This has empowered developers to focus on business logic,scaling requirements, mobile platform support, and other numerous requirementswhile allowing Spring to handle the chores of managing connections and interactingwith various data management technologies This is analogous to migrating from themanual memory management of coding in C to Java’s sophisticated garbage collection,which removed a whole host of bugs we as developers used to face every day By re-ducing the total amount of code we must write to access our own Big Data, we quicklycut out a huge number of potential bugs on Day One of our own projects
It is refreshing to read a slim and trim book like Just Spring Data Access, which avoids
the ever popular thick-as-possible approach and instead tries to be as clear and point as possible For the fledgling developer that has just joined a team that uses SpringData, this book provides a fantastic means to “catch up” over the weekend and be ready
to-the-to dive in on Monday For an architect trying to-the-to choose which standard to-the-to use for anew system, it also provides a quick read, allowing him or her to start their evaluationwith something more concrete than some cobbled together opinions Finally, for themore seasoned developer, it provides a good reference to look back and polish up skills
in the arena of data management and the options provided by competing Java ards None of us are experts on everything, and having a tightly focused book is oftenjust what we need to hone in and solve the problems we have
stand-—Greg Turnquist, Senior Software Engineer at SpringSource, a division of VMware,
and author of Spring Python 1.1
Trang 9There are two different worlds: the world where none other than objects are known,and the world where data is represented in a traditional row-column format Bringingthese two worlds together is always a cumbersome task, and many times is asking fortrouble However, we have no option: they must work together!
We have JDBC to some extent, but the intricacies and complexities of persistence ofJava objects to a relational databases was still a greater challenge The Object RelationalMapping frameworks—Hibernate being the most popular open source framework—has taken away a lot of pain and grief from the developer Spring framework has goneone more step further to simplify the usage even further
This book in an attempt in bringing the framework closer to the developer With simpleand plain language, along with easy to understand examples, this book covers just therequired bits for data access in a Java world
This book covers JDBC, Hibernate, JPA, and JDO, as well as Spring’s take on thesetechnologies
My goal is to deliver simple, straight-to-the-point explanations with intuitive, driven, engaging books! If you pick up the book, you should finish it in a day or two
pre-If you are in London, ping me (and perhaps buy me a coffee) for a meetup Additionally,
I am easily accessible via email (madhusudhan@madhusudhan.com) or via Twitter(@mkonda007)
Trang 10Conventions Used in This Book
The following typographical conventions are used in this book:
Constant width bold
Shows commands or other text that should be typed literally by the user
Constant width italic
Shows text that should be replaced with user-supplied values or by values mined by context
deter-This icon signifies a tip, suggestion, or general note.
This icon indicates a warning or caution.
Using Code Examples
This book is here to help you get your job done In general, you may use the code inthis book in your programs and documentation You do not need to contact us forpermission unless you’re reproducing a significant portion of the code For example,writing a program that uses several chunks of code from this book does not requirepermission Selling or distributing a CD-ROM of examples from O’Reilly books doesrequire permission Answering a question by citing this book and quoting examplecode does not require permission Incorporating a significant amount of example codefrom this book into your product’s documentation does require permission
We appreciate, but do not require, attribution An attribution usually includes the title,
author, publisher, and ISBN For example: “Just Spring Data Access by Madhusudhan
Konda (O’Reilly) Copyright 2012 Madhusudhan Konda, 978-1-449-32838-2.”
If you feel your use of code examples falls outside fair use or the permission given above,feel free to contact us at permissions@oreilly.com
Trang 11Safari® Books Online
Safari Books Online (www.safaribooksonline.com) is an on-demand digitallibrary that delivers expert content in both book and video form from theworld’s leading authors in technology and business
Technology professionals, software developers, web designers, and business and ative professionals use Safari Books Online as their primary resource for research,problem solving, learning, and certification training
cre-Safari Books Online offers a range of product mixes and pricing programs for zations, government agencies, and individuals Subscribers have access to thousands
organi-of books, training videos, and prepublication manuscripts in one fully searchable tabase from publishers like O’Reilly Media, Prentice Hall Professional, Addison-WesleyProfessional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, JohnWiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, Adobe Press, FTPress, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett, Course Tech-nology, and dozens more For more information about Safari Books Online, please visit
Find us on Facebook: http://facebook.com/oreilly
Follow us on Twitter: http://twitter.com/oreillymedia
Watch us on YouTube: http://www.youtube.com/oreillymedia
Trang 12I also thank my family in India for their wonderful support and love.
This book was written in memory of my loving Dad—we all miss you, Dad!
Trang 13CHAPTER 1 Basics
Persistence of data is a challenging task for developers There are many things that could
go wrong The introduction of JDBC has given the developer community a bit of joy
by taking away painstakingly cumbersome database access in Java applications ever, there are a few wrinkles that come with JDBC, such as having to write boilerplatecode, finding out a clue from the SQLExcetion stacktrace, resource management, and
How-so on
Spring has gone further in simplifying the data access by providing a simple andstraightforward framework This chapter discusses Spring’s take on JDBC, and howSpring simplified the JDBC programming model; it did so by employing simple yetpowerful mechanisms, such as Dependency Injection, Templates, and other patterns
Using Plain JDBC
With the advent of JDBC, accessing data from a Java application has become relativelyeasy Not only do we have independence from database vendor lock-in, but we alsohave a standard API to access multitude of databases
However, the steps involved in using a JDBC are always the same—obtain a connection,create a Statement, execute a query, run it through ResultSet, and release the resources.The following code demonstrates a simple example of selecting the TRADES data usingplain JDBC:
public class JdbcPlainTest {
private String DB_URL="jdbc:mysql://localhost:3306/JSDATA";
private final String USER_NAME = "XXXX";
private final String PASSWORD = "XXXX";
private Connection createConnection() {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn =
Trang 14public static void main(String args[]) {
JdbcPlainTest t = new JdbcPlainTest();
• The SQLException must be caught in both the creation and destruction processes
• The actual business logic is not more than a couple of lines; unfortunately, code iscluttered with lot of JDBC API statements and calls
We can create a home-grown framework with callbacks and handlers to resolve theseissues Although it does work, creating your own framework leads to several issues—maintenance, extending to suit newer requirements, extensive testing, and others
Trang 15If there’s already a framework that does this work, why reinvent the wheel?
The Spring data access framework is specifically created to address these problems It
is a beautiful framework that promotes Dependency Injection principles and carries
multiple features
Spring Data Access
The Spring data access framework has made the developer’s job very easy!
It creates a rich framework in which, or from which to access databases by decoupling
our code from the access mechanisms As always, the framework heavily uses ency Injection patterns, so decoupling of our code really comes to life The components
Depend-using framework’s API are easily testable, too Moreover, there’s no exceptions that weshould have to catch when using the APIs!
The access logic revolves around Template patterns and Support classes These patterns
hide away all the boilerplate code and allows the developer to concentrate solely onbusiness logic
Templates
From the previous example, we can see that there is a lot of code that’s not central tobusiness function It would be ideal to wrap up the non-critical code away from ourbusiness code in a separate class Spring’s JdbcTemplate class does exactly that.This class wraps up all the access logic so users only need to concentrate on the heart
of the application If you understand the workings of JdbcTemplate, I would say you’veconquered most of Spring’s data access workings
In addition to the standard JdbcTemplate, there are two other variations of the Templateclass: SimpleJdbcTemplate and NamedParameterJdbcTemplate These two varieties arenothing but wrappers around JdbcTemplate that are used for special cases We willdiscuss all of these in the coming sections Before we work out examples, we have tocarry out some prerequisites such as creating a database schema and prepopulating testdata
If you already have a database in place, you can skip this section without any concern
MySQL Database Scripts
I am using MySQL as the database for all of the examples provided in this book Setting
up the database is easy if you follow the instructions from the provider carefully.Once you have MySQL set up, make sure you run the SQL scripts provided by thebook’s source code These scripts will create a database called JSDATA and create nec-essary tables such as ACCOUNTS, TRADES, PRICES, and others If you are working with some
Trang 16other database, you should be able to run the scripts without any issues; personally, Ihave not tested them.
The next important thing is to create a DataSource The DataSource encapsulates thedatabase provider information and hence acts like a connection factory by fetchingconnections to talk to the database It should be created by driver information such asURL, username, password, and other information Make sure that you supply the nec-essary provider (driver) information to construct a DataSource if you are using any otherdatabases
The datasource-beans.xml file shown below creates a DataSource for MySQL database:
<bean id="mySqlDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/JSDATA" />
</bean>
The class attribute points to an implementor of the DataSource interface; in the abovesnippet, it is a BasicDataSource class from Apache Common’s DBCP project ThedriverClassName points to a class that will be specific to a database
We will see the full definition in a minute
Throughout the book, we will use DBCP datasource, which can be downloaded fromthe site: http://commons.apache.org/dbcp/ If you are using Maven, add the snippet to
your pom.xml file (check out the full pom.xml provided with the book’s source code)
to include DBCP and MySQL connector jars:
Trang 17resources, which leads to lots of issues JdbcTemplate comes to our rescue in doing thehouse cleaning job for us!
Before we work with JdbcTemplate, we must set the DataSource first This is a mandatoryrequirement that JdbcTemplate be configured with a DataSource object so the templatewill be able to create connections and statements behind the scenes
Configuring a DataSource
As we have already seen, the javax.sql.DataSource is an interface that determines theconnection details for a particular provider Each provider will have their own imple-mentation of the class, usually provided in a Jar file The MySQL driver class is defined
by the com.mysql.jdbc.Driver class, for example
The following configuration shows how to set up a data source for MySQL:
<bean id="mySqlDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/JSDATA"/>
<property name="username" value="jsuser"/>
<property name="password" value="jsuser"/>
</bean>
</beans>
The above snippet will create a bean named mySqlDataSource that points to a MySQLdatabase running on localhost, directed by the url property If we are using other pro-viders, we need to create another bean with the same properties, but with appropriatevalues relevant to our provider
Trang 18Let’s see an example of instantiating JdbcTemplate with a preconfigured DataSource.
public class JdbcTemplateTest {
private ApplicationContext ctx = null;
private JdbcTemplate template = null;
private DataSource datasource = null;
// Instantiate the template with the datasource
template = new JdbcTemplate(datasource);
}
public static void main(String[] args) {
JdbcTemplateTest t = new JdbcTemplateTest();
// execute the data access methods from here
}
}
The steps are simple:
• Load and fetch the context from a config file that consists of datasources (in our
case, it’s the datasouces-beans.xml)
• Create the JdbcTemplate using the new operator providing the datasource bean toits constructor
Once you have the JdbcTemplate fully configured and functional, you are ready to use
it to access our databast tables The JdbcTemplate has a lot of functionality that requires
a bit of detail study
Working with JdbcTemplate
The JdbcTemplate has more than 100 methods that give varied access to data sets!For example, you may wish to execute straight queries such as inserting data or creatingtables You can use the execute() method exposed on the JdbcTemplate for such actions.Likewise, if you wish to query for single or multiple data rows, you should be usingqueryForXXX methods There are lots of other methods, some of them are self explana-tory and others are easy to follow using JavaDoc We will cover the most important ofall of these methods in the coming sections
Let’s say our requirement is to find out the number ofrows present in the TRADES table
Querying for Single and Multiple Rows.
Trang 19The following snippet shows the usage of JdbcTemplate in its simplest form—for ing the number of TRADES in the table:
fetch-public int getTradesCount(){
You can also rewrite the above example by using the more generic queryForObjectmethod However, the method takes a second parameter, which basically describes thereturn value’s data type In our example, because count(*) will return an integer, wepass the Integer class to the method call
This is illustrated below:
public int getTradesCount(){
int numOfTrades =
template.queryForObject("select count(*) from TRADES",Integer.class);
return numOfTrades;
}
// Another example of get the max id of the
// trade using queryForObject method
public int getTradeMaxId(){
int maxId =
template.queryForObject("select max(id) from TRADES", Integer.class);
return maxId;
}
The above snippet also provides another example of using the queryForObject method
to query for a Trade that has a maximum id The queryForLong and queryForStringfollow the same pattern, returning a Long and String value, respectively
The queryForMap returns a single row in a Map<String,Object> format as shown below:
public Map<String,Object> getTradeAsMap(){
// note that we have hardcoded ID here!
Trang 20//The output to the console is:
Trades Map:{ID=1, ACCOUNT=1234AAA, SECURITY=MDMD,
QUANTITY=100000, STATUS=NEW, DIRECTION=BUY}
As you can see, each column name is the key represented by String while the value isrepresented by the Object in the Map<String,Object> declaration
However, the queryForList is a bit different to others in that it can return multiple rows.The rows are returned as a List of Map<String,Object> format
Let’s see this at work The getAllTrades() method fetches all of the trades and printsout to the console:
public List<Map<String,Object>> getAllTrades(){
{ID=5, ACCOUNT=452SEVE, STATUS=NEW, DIRECTION=SELL}]
The queries that we used in the above examples are fairly simple We can also writecomplex queries that can be executed in the same fashion We often use where clausesand other SQL constructs to execute complex queries However, the where clause re-quires input variables to be set How can we parameterize these bind variables?
Bind variables help to create a dynamic SQL query If our requirement is
to fetch records based on various conditions, we usually use the where clause in ourSQL script Bind variables are the preferred option as opposed to using inline variablesbecause they protect our application against SQL injection attacks
For example, if we have to get the STATUS of a Trade whose id is 5, we need to write theSQL as follows:
public String getTradeStatus(int id){
String status =
template.queryForObject("select STATUS from TRADES where id= ?",
new Object[]{id}, String.class);
return status;
}
Bind Variables.
Trang 21The ? will be an indication to the framework to substitute the value with the secondparameter of the method, which in the above case is the id The way to do this is tocreate an array of Object with your incoming id value The third parameter is the type
of value the method query is expected to return; in this case, the STATUS is a String type
We can provide more than a one bind variable, no restriction on the number
In the following snippet, the overloaded getTradeStatus() method has two conditions
in the where clause and accordingly, we provide a second value via a second parameter,Object array:
public String getTradeStatus(int id, String security){
We know that each row in the TRADES table is represented
by our Trade domain object Although we have seen fetching the Trades from the table,
we have not yet seen how we create a Trade object from each row of the record
In order to do this, we need to use a RowMapper callback provided by the framework.The RowMapper interface has one method—mapRow—where you need to map the in-coming row to the domain object You can create the RowMapper as an anonymous class
or you can have your own class implementing the RowMapper interface separately.Let’s take a look at each one separately
First, we create a TradeMapper class that implements the RowMapper interface and definesits single method:
private static final class TradeMapper implements RowMapper<Trade>{
@Override
public Trade mapRow(ResultSet rs, int rowNum) throws SQLException {
Trade t = new Trade();
// set the values by use ResultSet's getXXX methods
As we now have our RowMapper implementation ready, we give it to the overloadedqueryForObject method to retrieve all the trades from the table:
Mapping Rows to Domain Objects.
Trang 22public Trade getMappedTrade(int id){
Trade trade = template.queryForObject("select * from TRADES where id = ?",
There’s an alternative way of using RowMapper—we can also use an anonymous class tocreate a RowMapper instead of creating a separate instance as we have seen above Theway to do so is illustrated below:
public Trade getTrade(int id){
Trade trade = template.queryForObject("select * from TRADES where id= ?",
new Object[]{id},
new RowMapper<Trade>(){
@Override
public Trade mapRow(ResultSet rs, int row) throws SQLException {
Trade t = new Trade();
Creating the RowMapper class anonymously has a limited scope—it can’t be used where else in the application Unless you have a strong case to use the anonymous class,
any-go with a separate class like TradeMapper and reuse it Reusability scores good marks!Note that both the JdbcTemplate and RowMapper classes are thread safe You can sharethem and use them across threads without having to worry about state corruption
Now that we know how to fetch a single record and map to a main object, let’s see how to get the list of all rows mapped to domain objects Actually,
do-it is qudo-ite straight forward now that you have a RowMapper class already designed
Fetching List of Trades.
Trang 23The following snippet is used to fetch such a list Note that the only change was usingquery() method rather than queryForXXX method:
public List<Trade> getAllMappedTrades(){
Inserting, Deleting, and Updating Rows
We also use the JdbcTemplate to do the updates We use JdbcTemplate.update() variants
to execute the appropriate statements The following snippet shows inserting a Tradeinto TRADES table:
private void insertTrade() {
There is a way You can use the another variant of the update method that takes varargs:
private void updateTrade(String status, int id) {
int rowsUpdated =
template.update("update TRADES set status=? where id=?",status, id);
Trang 24System.out.println("Rows Updated:"+rowsUpdated);
}
There’s another overloaded method that sets the bind variables using an Object array(which we have already seen in our query examples earlier) and java.sql.Types array.The types array will provide the necessary framework tools to typecast the variables
In the following updateTradeUsingTypes method, we are using the types array to let theframework know the bind values type However, as the status and id are already knowntypes, perhaps using the types array might not be needed except for the compiler’s sake
private void updateTradeUsingTypes(String status, int id) {
int rowsUpdated = template.update(
"update TRADES set status=? where id=?",
new Object[] { status, id },
new int[] { java.sql.Types.VARCHAR, java.sql.Types.INTEGER });
System.out.println("Rows Updated:" + rowsUpdated);
int rowsUpdated = template.update(
"update TRADES set status=? where id=?",
new Object[] { "UNKNOWN","6" },
new int[] { java.sql.Types.VARCHAR,java.sql.Types.INTEGER });
System.out.println("Rows Updated:" + rowsUpdated);
}
You can also invoke a Stored Procedure using the update method, as shown here:
private void replayTradesUsingSP(List tradeIds) {
public void createAndDropPersonTable(){
template.execute("create table PERSON
(FIRST_NAME varchar(50) not null, LAST_NAME varchar(50) not null)");
// drop the table
template.execute("drop table PERSON");
Trang 25up the unnecessary boilerplate code into templates We have seen the fundamentalclass of the framework—JdbcTemplate—in action We learned how to utilize the classusing simple examples.
The next chapter will discuss the additional templates along with advanced SpringJDBC usage using Support classes and callbacks
Trang 27CHAPTER 2 Advanced Concepts
The Spring framework has provided extensive APIs to work with the database We’vecovered the basics of the framework in the last chapter, especially using the versatileJdbcTemplate class This chapter elaborates on advanced concepts, including othertemplates, callbacks, and batch operations
NamedParameterJdbcTemplate
In our queries, we define the bind variables using a ? operator as shown in the followingsnippet:
select count(*) from TRADES
where account = ? and security = ?
If we have a handful of these parameters, it would be an eyesore to read a querywith ? all over the place Spring has defined a new NamedParameterJdbcTemplate classthat comes handy in eliminating these placeholder variables This class basically en-capsulates the JdbcTemplate by providing the enhanced functionality of declaring thebind variables using named parameters
The same query can be tweaked as shown below using appropriate names instead
of ? variables:
select count(*) from TRADES
where account = :account and security = :security
The :account and :security names indicate that these variables will be passed in bysome means Note the colon (:) before the variable; this is the syntax you must follow.There are two ways of setting these variables One is to use a simple Map with the vari-ables as the keys, and the other is to use SqlParameterSource, a utility provided by theframework
Trang 28Using Map
The following snippet shows how to set the variables by using a map with keys:
public int getTradesCount(String s, String a){
Map bindValues = new HashMap();
bindValues.put("status", s);
bindValues.put("account", a);
int numOfTrades = template.queryForInt
("select count(*) from TRADES where account=:account and status=:status",
bindValues);
System.out.println("Number of Trades: "+numOfTrades);
return numOfTrades;
}
In the above example, we populate the Map with the provided arguments setting them
to the status and account keys Then we pass this Map to the query Did you noticethe names in the query? The names declared in the query must match the keys of ourMap
Using SqlParameterSource
The second way of setting the bind variables is by using framework’s helper interface,SqlParameterSource There’s an out-of-box implementation available in the form ofMapSqlParameterSource which acts as a wrapper around Map The way to set values is
to use the addValue() method and chain them as shown below:
public int getTradesCountUsingSqlParameterSource(String s, String a){
SqlParameterSource bindValues =
new MapSqlParameterSource().addValue("status", s).addValue("account", a);
int numOfTrades = template.queryForInt
("select count(*) from TRADES
where account=:account and status=:status", bindValues);
.
}
There is another implementation of the same interface which works on extracting thevalues from a Java object that complies to JavaBean standards The BeanPropertySql ParameterSource takes an instance and finds the values of the properties See the fol-lowing example to understand the usage:
public int getTradesCountUsingBeanSqlParameterSource(Trade t){
SqlParameterSource bindValues = new BeanPropertySqlParameterSource(t);
int numOfTrades = template.queryForInt
("select count(*) from TRADES where account=:account and status=:status", bindValues);
}
The class browses through the Trade object to find the respective properties—in ourcase, status and account The following snippet shows the way to call the method Thequery will use the account 1234AAAA and status NEW as the arguments retrieved from theTrade bean
Trang 29public static void main(String[] args) {
NamedParameterJdbcTemplateTest t = new NamedParameterJdbcTemplateTest();
Trade trade = new Trade();
replac-Jdbc Batching
Executing lots of updates or inserts one after another is a cumbersome operation If
your JDBC Driver supports, batching is an excellent strategy for improving mance For example, if you wish to insert 10,000 trades, you can insert them in a batchsize of 100, 500, 1,000, or any other number The JDBC statement will be re-used whenexecuting the batch instead of creating new statements every time you execute a call to
perfor-a dperfor-atperfor-abperfor-ase operperfor-ation Another perfor-advperfor-antperfor-age is thperfor-at the round trips to the dperfor-atperfor-abperfor-ase (usuperfor-allyover the network) are drastically reduced
Both JdbcTemplate and NamedParameterJdbcTemplate provide support for batch tions Again, there are couple of ways of doing this: using the framework’s utility classSqlParameterSourceUtils or extending the framework’s BatchPreparedStatementSet ter interface
execu-Using SqlParameterSourceUtils
The SqlParameterSourceUtils is a handy utility class the converts the List to an array
of SqlParameterSource instances
The snippet shown below shows how we use it in action:
private int[] insertTradesList(final List<Trade> trades) {
SqlParameterSource[] tradesList =
SqlParameterSourceUtils.createBatch(trades.toArray());
int[] updatesCount = namedTemplate.batchUpdate(
"insert into TRADES values
(:id,:account,:security,:quantity,:status,:direction)",
tradesList);
return updatesCount;
}
Trang 30You pass in a List of Trades that will be converted to an array of SqlParameterSource bythe SqlParameterSourceUtils utility class This array is then passed to our template forexecution, which returns the rows that were inserted into database.
private int[] insertTrades(final List<Trade> trades) {
int[] updatesCount = template.batchUpdate(
"insert into TRADES values(?,?,?,?,?,?)",
In the above example, the setValues() method will be invoked ten times This method
is similar to the one used for getBatchSize(), which utilizes the samePreparedStatement for all the calls This is an improvement
How would you go about updating larger bucket of trades, such as 100,000 of them?Instead of using the above strategy of calling the insertTrades with different batch sizes,Spring provides you with another version of the batchUpdate method In this instance,
it takes a batch size—say you wish to batch those hundred thousand trades into a batch
of 1,000—which is passed in to the method as a second parameter The addition is aParameterizedPreparedStatementSetter class that implements the setValues() methodsetting values from each Trade on the PreparedStatement
Trang 31public void setValues(PreparedStatement ps, Trade t) throws SQLException { ps.setInt(1, t.getId());
state-Let’s see them at work
SimpleJDBCInsert Class
The SimpleJDBCInsert class is used to execute inserts with minimalistic configuration.The first thing you need to do is to instantiate the class with a DataSource and set thetable name by invoking the method withTableName()
The following example shows this:
private SimpleJdbcInsert jdbcInsert = null;
// Get the datasource
datasource = ctx.getBean("mySqlDataSource", DataSource.class);
//Create the instance associating with the table
jdbcInsert = new SimpleJdbcInsert(datasource).withTableName("TRADES");
Now that the object is ready, we invoke the execute() method that takes a Trade Thevalues of the trade are set against the key values of a Map and passed to theSimpleJdbcInsert class:
public void insertTrade(Trade t) {
Map tradeMap = new HashMap();
Trang 32Make sure the keys match the column names That’s it—no SQL statements and nomore placeholders!
Should you wish to insert only one or two columns, you can set the column namesusing the usingColumns() method as shown below:
The following snippet summarizes it in this context:
public void insertTradeUsingSqlParameterSource(Trade t) {
// create an instance passing our Trade object
SqlParameterSource source = new BeanPropertySqlParameterSource(t);
The trade_by_quantity is a Stored Procedure that fetches a big trade whose quantity
is provided by the client It takes the quantity as IN parameter and spits out ID andACCOUNT values of the row as OUT parameters for the matching big trade
CREATE PROCEDURE trade_by_quantity
(IN in_qty INTEGER, OUT big_trade_id INTEGER)
in-jdbcCall = new SimpleJdbcCall(datasource)
withProcedureName("trade_by_quantity");
Trang 33Now that we have our class configured, we need to work on the method that invokesthis class.
The method shown below does exactly that: it creates an instance of SqlParameter Source with quantity as the bind value and invokes the execute method on the class
public Trade getBigTradeUsingSimpleJdbcCall(String quantity){
SqlParameterSource inValues =
new MapSqlParameterSource().addValue("quantity", quantity);
Map bigTrades = jdbcCall.execute(inValues);
Trade t = new Trade();
t.setId((Integer)bigTrades.get("id"));
t.setAccount((String)bigTrades.get("account"));
return t;
}
The execute method returns a Map with the declared OUT parameters as the keys What
we do is to fetch them from the map based on the OUT parameters and set them on tothe Trade object
The above program returns only one Trade You can also use SimpleJdbcCall to invoke
a StoredProc that returns a ResultSet Create the class as shown below, passing aRowMapper implementation—ParameterizedBeanPropertyRowMapper in this case
jdbcCall = new SimpleJdbcCall(datasource)
withProcedureName("big_trades")
returningResultSet("trades", new ParameterizedBeanPropertyRowMapper());
Map bigTrades = jdbcCall.execute();
develop-to Derby, it carries a fundamental difference: Oracle (Java provider) supports andmaintains the Java DB while Derby is maintained outside the Java ecosystem by Apachegroup
The inner workings of these databases are similar to the Relational Database ment Systems (RDBMS) that we know We can see how Spring helps us in developingaccess to Java DB
Trang 34Manage-The first thing we need to do is to create a DataSource It follows the same lines tonormal DataSource definitions—make sure you provide the right driver class and URLs.Take the following, for example:
<property name="username" value="" />
<property name="password" value="" />
</bean>
As we see in the above snippet, defining an in-memory DataSource is no different toother types of RDBMS DataSources The url points to the local file system path wherethe database will be created The appending create=true string at the end of the urlindicates if the database (in this case JSDATA) should be created if it does not exist.Ideally, the first time you start your Java DB, leave this as true (and turn it to false later
on or else the driver throws warnings!) so the schema will be created for us
The next step is to create our JdbcTemplate using this injected DataSource as we havealready seen in many earlier cases The following snippet shows this test scenario:
public class JavaDBTest {
private ApplicationContext ctx = null;
private JdbcTemplate template = null;
private DataSource datasource = null;
// Create our template using JavaDB DataSource
template = new JdbcTemplate(datasource);
}
//Insert a trade
private void insertTrade() {
int rowsUpdated = template.update(
"insert into TRADES values(?,?,?,?,?,?)", 33, "JSDATA", "REV",500000, "NEW",
"SELL");
System.out.println("Rows Updated:" + rowsUpdated);
}
public static void main(String[] args) {
JavaDBTest test = new JavaDBTest();
test.insertTrade();
}
}
Trang 35In-memory databases are quite useful in development mode; it really gears up the ductivity to a certain degree We do not have to change our SQL scripts when we releaseour code to production; only the DataSource definition needs to be changed Addition-ally, it will not point to a production-RDBMS.
pro-Callbacks
As you may have already noticed, there are two parts in any JDBC client program: onepart gets the connection, creates the statement, and deals with resource management,which is more or less a boilerplate code The other part is the heart of the applicationwhere we code the business logic
The framework hides away the nonbusiness code efficiently in the templates whileallowing us to concentrate on the business logic It does this by providing valuablecallbacks where we can deal with writing the business logic
We have already seen a RowMapper callback in action earlier Let’s see the rest of them,such as RowCallbackHandler, PreparedStatementCallback, and CallableStatementCall back here
PreparedStatement Callback
Should you have a requirement to run the code in your own PreparedStatement, rely
on this callback
The framework will ideally create one and hands over to you via the callback
There are two ways to get the handle to the PreparedStatement: one is by creating yourown implementation class and the other is by creating an anonymous class
Let’s say we need to extract a Map of Trade id and Trade account from our TRADES tableusing the PreparedStatement route To do this, we must create a class and implementthe framework’s PreparedStatementCallback interface to override the doInPrepared Statement method
The following is the code snippet for the callback that executes the query in a givenPrepatedStatement, which results in a ResultSet object The Id and Account columnvalues are extracted from the resultSet into the Map object
private class PSCallback implements
PreparedStatementCallback<Map<Integer, Object>> {
Map<Integer, Object> tradeIdAccountsMap =
new HashMap<Integer, Object>();
@Override
public Map<Integer, Object> doInPreparedStatement(PreparedStatement ps)
throws SQLException, DataAccessException {
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Trang 36int tradeId = rs.getInt(1);
String account = rs.getString(2);
public void getTradesMapViaPSCallback() throws Exception {
Map<Integer, Object> tradeIdAccountsMap =
template.execute("select * from TRADES", new PSCallback());
System.out.println("ID-ACCOUNTS map:" + tradeIdAccountsMap);
}
Alternatively, you can also declare your callback inline:
public void getTradePSCallbackAsAnonymous() throws Exception {
Trade t = template.execute("select * from TRADES where id=1",
new PreparedStatementCallback<Trade>() {
private Trade t = new Trade();
@Override
public Trade doInPreparedStatement(PreparedStatement ps)
throws SQLException, DataAccessException {
As you can see, we extracted one Trade from the ResultSet and returned it
I mentioned earlier that the PreparedStatement that was given to our implementors werealready pre-configured with a SQL statement
What if we wish to create the SQL query somewhere else? It turns out that Spring hasthought of this and hence provides PreparedStatementCreator interface This meansthe implementation will create a statement from the connection provided
The following snippet is the implementor of this interface The example shows that wewere given a Connection object to create a PreparedStatement using the SQL query
class PSCreator implements PreparedStatementCreator {
@Override
public PreparedStatement createPreparedStatement(Connection con)
throws SQLException {
Trang 37// Provide the creator and callback
Map<Integer, Object> tradeIdAccountsMap =
template.execute(new PSCreator(), new PSCallback());
System.out.println("ID-ACCOUNTS map:" + tradeIdAccountsMap);
}
Callable Statement Callbacks
The framework provides a couple of callbacks for CallableStatements: the CallableS tatementCallback and CallableStatementCreator classes
Fortunately, they follow exactly the same footsteps as PreparedStatementXXX For ample, you have to implement CallableStatementCallback to define the doInCalla bleStatement method, wherein you would be given CallableStatement instead:
ex-public void testCSCallbackViaCreator() throws Exception {
Map<Integer, Object> tradeIdAccountsMap = template.execute(
"select * from TRADES", new CSCallback());
System.out.println("ID-ACCOUNTS map:" + tradeIdAccountsMap);
}
private class CSCallback implements CallableStatementCallback {
@Override
public Object doInCallableStatement(CallableStatement ps)
throws SQLException, DataAccessException {
Trang 38The following snippet shows this:
class BigTradeCounter implements RowCallbackHandler {
The following code shows the invocation:
private void bigTradeCountHanlder(double quantity) {
BigTradeCounter counter = new BigTradeCounter(quantity);
template.query("select * from TRADES", counter);
System.out.println("Big Trades" + counter.getBigTradeCount());
}
You can also define the same class as an anonymous inline class
Note that the RowMapper class that we learned about in the first chapter does the samething The RowCallbackHandler implementation holds onto state and hence can’t be re-used unless a new instance is created