jdbc recipes a problem-solution approach (problem-solution approach)[2005]

665

Upload: raja-kumar

Post on 17-Jul-2015

163 views

Category:

Education


2 download

TRANSCRIPT

Page 1: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]
Page 2: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Mahmoud Parsian

JDBC RecipesA Problem-Solution Approach

5203ch00_FM.qxd 8/12/05 12:17 PM Page i

Page 3: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

JDBC Recipes: A Problem-Solution Approach

Copyright © 2005 by Mahmoud Parsian

All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means,electronic or mechanical, including photocopying, recording, or by any information storage or retrievalsystem, without the prior written permission of the copyright owner and the publisher.

ISBN: 1-59059-520-3

Printed and bound in the United States of America 9 8 7 6 5 4 3 2 1

Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrenceof a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademarkowner, with no intention of infringement of the trademark.

Lead Editor: Steve AnglinDevelopment Editor: Jim SumserTechnical Reviewer: Sumit PalEditorial Board: Steve Anglin, Dan Appleman, Ewan Buckingham, Gary Cornell, Tony Davis, Jason Gilmore,

Jonathan Hassell, Chris Mills, Dominic Shakeshaft, Jim SumserAssociate Publisher: Grace WongProject Manager: Beckie StonesCopy Edit Manager: Nicole LeClercCopy Editor: Kim WimpsettAssistant Production Director: Kari Brooks-CoponyProduction Editor: Katie StenceCompositor and Artist: Kinetic Publishing Services, LLCProofreaders: Liz Welch and Lori BringIndexer: Tim TateInterior Designer: Van Winkle Design GroupCover Designer: Kurt KramesManufacturing Director: Tom Debolski

Distributed to the book trade worldwide by Springer-Verlag New York, Inc., 233 Spring Street, 6th Floor,New York, NY 10013. Phone 1-800-SPRINGER, fax 201-348-4505, e-mail [email protected], orvisit http://www.springeronline.com.

For information on translations, please contact Apress directly at 2560 Ninth Street, Suite 219, Berkeley,CA 94710. Phone 510-549-5930, fax 510-549-5939, e-mail [email protected], or visit http://www.apress.com.

The information in this book is distributed on an “as is” basis, without warranty. Although every precautionhas been taken in the preparation of this work, neither the author(s) nor Apress shall have any liability toany person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectlyby the information contained in this work.

The source code for this book is available to readers at http://www.apress.com in the Source Code section.

5203ch00_FM.qxd 8/12/05 12:17 PM Page ii

Page 4: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

This book is dedicated tomy love, Behnaz;

my gozal daughter, Maral;my gibldiz son, Yaseen;

my mother, Monireh, and the memory of my father, Bagher.

5203ch00_FM.qxd 8/12/05 12:17 PM Page iii

Page 5: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch00_FM.qxd 8/12/05 12:17 PM Page iv

Page 6: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Contents at a Glance

About the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix

About the Technical Reviewer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi

Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii

Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxv

■CHAPTER 1 Introducing JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

■CHAPTER 2 Exploring JDBC’s Novel Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

■CHAPTER 3 Making Database Connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

■CHAPTER 4 Making Database Connections Using DataSource . . . . . . . . . . . . . . . . . . 153

■CHAPTER 5 Exploring the ResultSet Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169

■CHAPTER 6 Working with Scrollable and Updatable ResultSet Objects . . . . . . . . . . 209

■CHAPTER 7 Reading and Writing BLOBs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

■CHAPTER 8 Reading and Writing CLOBs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257

■CHAPTER 9 Working with Date, Time, and Timestamp in JDBC . . . . . . . . . . . . . . . . . . 307

■CHAPTER 10 Handling Exceptions in JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341

■CHAPTER 11 Exploring the Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381

■CHAPTER 12 Working with the PreparedStatement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431

■CHAPTER 13 Passing Input Parameters to PreparedStatement . . . . . . . . . . . . . . . . . . 493

■CHAPTER 14 Exploring JDBC Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569

■INDEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615

v

5203ch00_FM.qxd 8/12/05 12:17 PM Page v

Page 7: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch00_FM.qxd 8/12/05 12:17 PM Page vi

Page 8: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Contents

About the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix

About the Technical Reviewer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxi

Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii

Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxv

■CHAPTER 1 Introducing JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1-1. What Is JDBC? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11-2. What Is JDBC’s High-Level Architecture?. . . . . . . . . . . . . . . . . . . . . . . . . . . . 31-3. What Is JDBC’s Detailed Architecture?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31-4. What Is a Relational Database? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61-5. What Is ODBC? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61-6. What Is a JDBC-ODBC Bridge?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81-7. What Is SQL? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131-8. What Is JDBC Programming? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151-9. What Is the JDBC API (in a Nutshell)?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261-10. What Are the Core JDBC Classes/Interfaces? . . . . . . . . . . . . . . . . . . . . . . 261-11. What Is a JDBC Driver? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271-12. How Do You Load a JDBC Driver? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291-13. How Do You Test a JDBC Driver Installation? . . . . . . . . . . . . . . . . . . . . . . . 321-14. Where Can You Obtain a JDBC Driver for Your Database

System? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341-15. What Is a JDBC Driver Type? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341-16. What Types of JDBC Drivers Exist? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351-17. What Are the Selection Criteria for JDBC Drivers?. . . . . . . . . . . . . . . . . . . 371-18. What Is the URL Syntax for Connecting to a Database? . . . . . . . . . . . . . . 381-19. What Is the Mapping Between Java to JDBC SQL Types? . . . . . . . . . . . . . 391-20. How Do You Handle JDBC Errors/Exceptions? . . . . . . . . . . . . . . . . . . . . . . 42

■CHAPTER 2 Exploring JDBC’s Novel Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

2-1. What Is Database Metadata? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452-2. What Is a Transaction? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462-3. How Do You Turn On Autocommit Mode?. . . . . . . . . . . . . . . . . . . . . . . . . . . 482-4. How Do You Turn Off Autocommit Mode?. . . . . . . . . . . . . . . . . . . . . . . . . . . 482-5. How Do You Roll Back a Transaction? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

vii

5203ch00_FM.qxd 8/12/05 12:17 PM Page vii

Page 9: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

2-6. How Do You Start and End a Transaction in JDBC? . . . . . . . . . . . . . . . . . . . 482-7. What Is Connection Pool Management?. . . . . . . . . . . . . . . . . . . . . . . . . . . . 492-8. How Do You Improve the Performance of JDBC Applications? . . . . . . . . . . 512-9. What Are Oracle’s JDBC Drivers? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632-10. How Do You Connect with Oracle’s JDBC Thin Driver? . . . . . . . . . . . . . . . 642-11. How Do You Connect with Oracle’s JDBC OCI Driver? . . . . . . . . . . . . . . . . 652-12. How Do You Connect with Oracle’s KPRB Driver? . . . . . . . . . . . . . . . . . . . 652-13. What Are MySQL’s JDBC Drivers? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652-14. How Do You Register the MySQL Driver with the Driver

Manager? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662-15. How Do You Get a MySQL Connection from the Driver

Manager? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662-16. What Are the Key JDBC Concepts for an Oracle Database? . . . . . . . . . . . 692-17. What Are the Key JDBC Concepts for a MySQL Database? . . . . . . . . . . . . 722-18. How Do You Set Your Environment for JDBC? . . . . . . . . . . . . . . . . . . . . . . 732-19. What Are Some JDBC Resources? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742-20. How Do You Debug Problems Related to the JDBC API? . . . . . . . . . . . . . . 752-21. Does MySQL Support Transactions? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762-22. Does Oracle Support Transactions? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 772-23. What Do the Different Versions of JDBC Offer? . . . . . . . . . . . . . . . . . . . . . 782-24. What Is the Core Functionality of a JDBC Driver? . . . . . . . . . . . . . . . . . . . 792-25. Where Can You Find Information and Pointers for Writing

a JDBC Driver? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822-26. What Is the JDBC Driver Certification Program? . . . . . . . . . . . . . . . . . . . . 822-27. What Is a Two-Tier Model for JDBC? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832-28. What Is a Three-Tier Model for JDBC?. . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

■CHAPTER 3 Making Database Connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

3-1. What Is a Connection Object?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883-2. What Is the Relationship of Connection to Other Objects?. . . . . . . . . . . . . . 883-3. What Are the Connection Creation Options? . . . . . . . . . . . . . . . . . . . . . . . . 893-4. What Is the Function of the DriverManager Class? . . . . . . . . . . . . . . . . . . . 903-5. How Do You Create Connection(s) Using the DriverManager

Class? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 913-6. How Do You Get a List of Loaded Drivers? . . . . . . . . . . . . . . . . . . . . . . . . . . 953-7. How Do You Connect to an Oracle Database? . . . . . . . . . . . . . . . . . . . . . . . 973-8. How Do You Connect to a MySQL Database?. . . . . . . . . . . . . . . . . . . . . . . 1003-9. How Do You Get a List of All Available Parameters for Creating

a JDBC Connection? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1033-10. How Do You Create Connection(s) Using the Driver Interface? . . . . . . . . 1063-11. What Is Basic Connection Management?. . . . . . . . . . . . . . . . . . . . . . . . . 1083-12. How Do You Determine If a Database Supports Transactions? . . . . . . . . 113

■CONTENTSviii

5203ch00_FM.qxd 8/12/05 12:17 PM Page viii

Page 10: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

3-13. How Do You Limit the Number of Rows Returned from a SQL Query? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

3-14. How Do You Get the Driver of a Connection? . . . . . . . . . . . . . . . . . . . . . . 1173-15. How Do You Commit and Roll Back Updates to a Database? . . . . . . . . . 1183-16. How Do You Determine If a SQL Warning Occurs?. . . . . . . . . . . . . . . . . . 1193-17. What Are the MySQL Connection Properties? . . . . . . . . . . . . . . . . . . . . . 1213-18. What Are the Oracle Connection Properties? . . . . . . . . . . . . . . . . . . . . . . 1233-19. Can a JDBC Application Connect to More Than One

Database? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1243-20. How Do You Test to See If Your Connection Is Alive? . . . . . . . . . . . . . . . . 1253-21. How Do You Keep the Connection Alive in a Production

Environment? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1303-22. How Do You Disconnect from a Database? . . . . . . . . . . . . . . . . . . . . . . . 1313-23. What Are the Rules for Connection’s Autocommit? . . . . . . . . . . . . . . . . . 1323-24. How Do You Create a New Type Map? . . . . . . . . . . . . . . . . . . . . . . . . . . . 1343-25. How Do You Create a SQL to Java Type Map Entry? . . . . . . . . . . . . . . . . 1353-26. Is There Any Limit on the Number of Connections for JDBC? . . . . . . . . . 1453-27. How Do You Connect As SYSDBA or SYSOPER to an Oracle

Database? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1473-28. How Do You Check MySQL’s/Oracle’s JDBC Installation? . . . . . . . . . . . . 148

■CHAPTER 4 Making Database Connections Using DataSource . . . . . . . . . 153

4-1. How Do You Create Connection Using a DataSource Object? . . . . . . . . . . 1534-2. How Do You Create a DataSource Object? . . . . . . . . . . . . . . . . . . . . . . . . . 1564-3. How Do You Create a DataSource Object Using Oracle? . . . . . . . . . . . . . . 1564-4. How Do You Create a DataSource Object Using MySQL? . . . . . . . . . . . . . 1564-5. How Do You Create a DataSource Object Using a Relational

Database (Oracle/MySQL)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1574-6. How Do You Create a DataSource Object Using a DataSource

Factory?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604-7. What Are the DataSource Properties? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604-8. How Do You Deploy/Register a DataSource? . . . . . . . . . . . . . . . . . . . . . . . 1614-9. How Do You Use a File-Based System for Registering a

DataSource Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1624-10. What Is the Problem with File-Based DataSource Objects? . . . . . . . . . . 1634-11. How Do You Retrieve a Deployed/Registered DataSource? . . . . . . . . . . . 1644-12. How Do You Obtain a Connection with the DataSource

Without Using JNDI? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1654-13. How Do You Obtain a Connection with the DataSource

Using JNDI?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

■CONTENTS ix

5203ch00_FM.qxd 8/12/05 12:17 PM Page ix

Page 11: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

■CHAPTER 5 Exploring the ResultSet Interface . . . . . . . . . . . . . . . . . . . . . . . . . . 169

5-1. What Is a ResultSet?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1695-2. What Is the Relationship of ResultSet to Other

Classes/Interfaces?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1745-3. How Does the JDK Define a ResultSet? . . . . . . . . . . . . . . . . . . . . . . . . . . . 1755-4. What Kinds of ResultSet Objects Exist? . . . . . . . . . . . . . . . . . . . . . . . . . . . 1755-5. How Do You Set a ResultSet Type?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1765-6. How Do You Get a ResultSet Type? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1785-7. Which ResultSet Types Are Supported by Databases?. . . . . . . . . . . . . . . . 1785-8. What Is a ResultSet Concurrency? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1805-9. How Do You Set ResultSet Concurrency?. . . . . . . . . . . . . . . . . . . . . . . . . . 1815-10. How Do You Get ResultSet Concurrency? . . . . . . . . . . . . . . . . . . . . . . . . 1815-11. What Is ResultSet Holdability? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1815-12. How Do You Set ResultSet Holdability? . . . . . . . . . . . . . . . . . . . . . . . . . . 1825-13. How Do You Set ResultSet Holdability Using the

Connection Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1825-14. How Do You Check ResultSet Holdability? . . . . . . . . . . . . . . . . . . . . . . . . 1825-15. How Do You Get ResultSet Holdability? . . . . . . . . . . . . . . . . . . . . . . . . . . 1835-16. How Do You Create and Manipulate ResultSet Objects? . . . . . . . . . . . . . 1835-17. How Do You Get Rows from a Database Table? . . . . . . . . . . . . . . . . . . . . 1835-18. How Do You Get Data from a ResultSet? . . . . . . . . . . . . . . . . . . . . . . . . . 1855-19. How Do You Get Rows from a Database Table? . . . . . . . . . . . . . . . . . . . . 1865-20. How Do You Get Data from a ResultSet? . . . . . . . . . . . . . . . . . . . . . . . . . 1875-21. How Do You Determine If a Fetched Value Is NULL? . . . . . . . . . . . . . . . . 1915-22. How Do You Get the Column Names in a Result Set? . . . . . . . . . . . . . . . 1915-23. How Do You Get the Number of Rows in a Database Table? . . . . . . . . . . 1935-24. How Do You Get BLOB Data from a Database Table? . . . . . . . . . . . . . . . 1945-25. How Do You Get CLOB Data from a Database Table? . . . . . . . . . . . . . . . 1995-26. How Do You Match Using Wildcards in a SQL Statement? . . . . . . . . . . . 2005-27. How Do You Read/Extract Data from a Microsoft Excel

Spreadsheet File? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2005-28. How Do You Write Data to a Microsoft Excel Spreadsheet

File?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2035-29. Which Is the Preferred Collection Class to Use for Storing

Database Result Sets? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2055-30. How Do You Retrieve a Whole Row/Record of Data at Once

Instead of Calling an Individual ResultSet.getXXX() Method for Each Column? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208

■CONTENTSx

5203ch00_FM.qxd 8/12/05 12:17 PM Page x

Page 12: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

■CHAPTER 6 Working with Scrollable and Updatable ResultSet Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209

6-1. What Is a Scrollable ResultSet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2096-2. How Do You Determine If a Database Supports Scrollable

ResultSets? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2126-3. How Do You Create a Scrollable ResultSet?. . . . . . . . . . . . . . . . . . . . . . . . 2126-4. How Do You Determine If a ResultSet Is Scrollable? . . . . . . . . . . . . . . . . . 2146-5. How Do You Move the Cursor in a Scrollable ResultSet?. . . . . . . . . . . . . . 2146-6. How Do You Get the Cursor Position in a Scrollable Result Set? . . . . . . . . 2166-7. How Do You Get the Number of Rows in a Table Using a

Scrollable ResultSet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2176-8. How Do You Determine If a Database Supports Updatable

ResultSets? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2186-9. How Do You Create an Updatable ResultSet? . . . . . . . . . . . . . . . . . . . . . . 2196-10. How Do You Determine If a ResultSet Is Updatable? . . . . . . . . . . . . . . . . 2196-11. How Do You Update a Row in a Database Table Using an

Updatable Result Set?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2206-12. How Do You Cancel Updates to an Updatable ResultSet? . . . . . . . . . . . . 2216-13. How Do You Insert a Row into a Database Table Using an

Updatable ResultSet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2226-14. How Do You Delete a Row from a Database Table Using an

Updatable ResultSet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2266-15. How Do You Refresh a Row in an Updatable ResultSet? . . . . . . . . . . . . . 229

■CHAPTER 7 Reading and Writing BLOBs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

7-1. What Is a BLOB? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2317-2. How Do You Define a BLOB Data Type in a Table? . . . . . . . . . . . . . . . . . . . 2337-3. What Are the Restrictions for Using BLOBs? . . . . . . . . . . . . . . . . . . . . . . . 2347-4. How Do You Create a java.sql.Blob Object? . . . . . . . . . . . . . . . . . . . . . . . . 2347-5. How Do You Materialize BLOB Data? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2357-6. How Do You Insert a New Record with a BLOB? . . . . . . . . . . . . . . . . . . . . 2367-7. How Do You Select and Display a BLOB in a JFrame?. . . . . . . . . . . . . . . . 2407-8. How Do You Delete an Existing BLOB from the Oracle

Database? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2437-9. How Do You Delete an Existing BLOB from the MySQL

Database? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2457-10. How Do You Serialize a Java Object to the Oracle Database? . . . . . . . . . 2467-11. How Do You Serialize a Java Object to the MySQL Database? . . . . . . . . 2527-12. Should You Use byte[] or java.sql.Blob? Which Has the Best

Performance? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256

■CONTENTS xi

5203ch00_FM.qxd 8/12/05 12:17 PM Page xi

Page 13: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

■CHAPTER 8 Reading and Writing CLOBs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257

8-1. What Is a CLOB? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2578-2. How Do You Define a CLOB Data Type in a Table? . . . . . . . . . . . . . . . . . . . 2598-3. What Are the Restrictions for Using CLOBs? . . . . . . . . . . . . . . . . . . . . . . . 2618-4. How Do You Create a java.sql.Clob Object? . . . . . . . . . . . . . . . . . . . . . . . . 2618-5. How Do You Materialize CLOB Data? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2628-6. How Do You Insert a New Record with a CLOB? . . . . . . . . . . . . . . . . . . . . 2658-7. How Do You Select and Display a CLOB in a JFrame?. . . . . . . . . . . . . . . . 2708-8. How Do You Select and Display an Oracle CLOB Using

a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2748-9. How Do You Select and Display a MySQL CLOB Using

a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2778-10. How Do You Select and Display an Oracle CLOB (As a URL)

Using a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2808-11. How Do You Select and Display a MySQL CLOB (As a URL)

Using a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2848-12. How Do You Insert a CLOB into an Oracle Database Using

a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2858-13. How Do You Insert a CLOB into a MySQL Database Using

a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2908-14. How Do You Update an Existing CLOB of an Oracle Database

Using a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2938-15. How Do You Update an Existing CLOB of a MySQL Database

Using a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2978-16. How Do You Delete an Existing CLOB of an OracleDatabase

Using a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3008-17. How Do You Delete an Existing CLOB of an MySQL Database

Using a Servlet? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3038-18. Should You Use java.lang.String or java.sql.Clob?

Which Has the Best Performance? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305

■CHAPTER 9 Working with Date, Time, and Timestamp in JDBC . . . . . . . . 307

9-1. What Is the Mapping of Date-Related SQL and Java Types?. . . . . . . . . . . 3089-2. How Do You Retrieve Date, Time, and Timestamp from

a Database? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3099-3. How Does MySQL Handle Date, Time, and Timestamp? . . . . . . . . . . . . . . 3109-4. How Does Oracle Handle Date, Time, and Timestamp?. . . . . . . . . . . . . . . 3109-5. How Do You Get the Current Date As a java.util.Date Object? . . . . . . . . . . 3149-6. How Do You Create a java.sql.Date Object?. . . . . . . . . . . . . . . . . . . . . . . . 3149-7. How Do You Get the Current Timestamp As a java.sql.Timestamp

Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316

■CONTENTSxii

5203ch00_FM.qxd 8/12/05 12:17 PM Page xii

Page 14: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

9-8. How Do You Get the Current Timestamp As a java.sql.Time Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316

9-9. How Do You Convert from a java.util.Date Object to a java.sql.Date Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316

9-10. How Do You Convert a String Date Such As 2003/01/10 into a java.util.Date Object?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317

9-11. How Do You Create Yesterday’s Date from a Date in the String Format of MM/DD/YYYY? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317

9-12. How Do You Create a java.util.Date Object from a Year, Month,Day Format? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318

9-13. How Do You Convert a String Date Such As 2003/01/10 to a java.sql.Date Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319

9-14. How Do You Get a Timestamp Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . 3209-15. How Do You Create a java.sql.Time Object? . . . . . . . . . . . . . . . . . . . . . . 3209-16. How Do You Convert the Current Time to a java.sql.Date

Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3229-17. How Do You Determine the Day of the Week from Today’s

Date?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3229-18. How Do You Determine the Day of the Week from a Given

java.util.Date Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3229-19. How Do You Convert java.sql.Date to java.util.Date?. . . . . . . . . . . . . . . . 3229-20. What Is java.text.SimpleDateFormat? . . . . . . . . . . . . . . . . . . . . . . . . . . . 3239-21. How Do You Convert java.util.Date to a Date String in the

Format MM/DD/YYYY? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3239-22. How Do You Create a Time String in the Format HH:MM:SS

from an Hour, Minute, Second Format? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3249-23. How Do You Convert a java.util.Date Object to a Time String

in the Format HH:MM:SS? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3259-24. How Do You Check for a Leap Year? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3259-25. How Do You Convert Between Different Java Date Classes? . . . . . . . . . 3269-26. How Do You Add/Subtract Days for a Given Date

(Given As a String)?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3279-27. How Do You Find the Difference Between Two Given Dates? . . . . . . . . . 3289-28. How Do You Convert a Timestamp to Month-Day-Year? . . . . . . . . . . . . . 3319-29. How Do You Determine the Validity of a Format Pattern for

SimpleDateFormat? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3329-30. How Do You Get a Date Label from a java.sql.Timestamp

Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3339-31. How Do You Convert a java.sql.Timestamp Object to

a java.util.Date Object?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3359-32. What Does Normalization Mean for java.sql.Date and

java.sql.Time? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335

■CONTENTS xiii

5203ch00_FM.qxd 8/12/05 12:17 PM Page xiii

Page 15: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

9-33. Does MySQL/Oracle JDBC Driver Normalize java.sql.Date and java.sql.TimeObjects? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336

9-34. How Do You Make a java.sql.Timestamp Object for a Given Year, Month, Day, Hour, and So On? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339

9-35. How Do You Get a Date for a Specific Time Zone? . . . . . . . . . . . . . . . . . 340

■CHAPTER 10 Handling Exceptions in JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341

10-1. What Is an Exception? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34210-2. What Is java.lang.Exception? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34210-3. What Are the Components of an Exception? . . . . . . . . . . . . . . . . . . . . . . 34410-4. What Is the Definition of SQLException? . . . . . . . . . . . . . . . . . . . . . . . . . 34410-5. Is SQLException a “Checked” Exception? . . . . . . . . . . . . . . . . . . . . . . . . 34610-6. What Is the Relationship of SQLException to Other Classes? . . . . . . . . . 34610-7. What Is an Example of Using SQLException?. . . . . . . . . . . . . . . . . . . . . . 34810-8. How Do You Get the Details of a SQLException? . . . . . . . . . . . . . . . . . . . 34910-9. What Is SQLException Chaining? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35010-10. How Do You Get All SQLException Instances? . . . . . . . . . . . . . . . . . . . . 35110-11. What Is a SQLWarning? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35210-12. How Do You Get All SQLWarning Instances? . . . . . . . . . . . . . . . . . . . . . 35310-13. How Do You Determine Whether a SQL Warning Has

Occurred? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35310-14. How Do You Create and Traverse a Custom SQLWarning

Structure? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35510-15. How Do You Wrap Database Exceptions? . . . . . . . . . . . . . . . . . . . . . . . 35710-16. What Are the SQLState Codes Returned by

SQLException.getSQLState()? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35810-17. What Is a BatchUpdateException? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36410-18. What Is a DataTruncation Class? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37310-19. How Do You Use DataTruncation? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37510-20. How Do You Use DataTruncation for ResultSet? . . . . . . . . . . . . . . . . . . 379

■CHAPTER 11 Exploring the Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381

11-1. How Do You Represent a SQL Statement Object? . . . . . . . . . . . . . . . . . . 38111-2. How Do You Create Statement Objects? . . . . . . . . . . . . . . . . . . . . . . . . . 38211-3. How Do You Create a Scrollable ResultSet?. . . . . . . . . . . . . . . . . . . . . . . 38311-4. How Do You Create an Updatable ResultSet? . . . . . . . . . . . . . . . . . . . . . 38511-5. How Do You Execute SQL Statements Using Statement

Objects? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38611-6. How Do You Create a Database Table Using a Statement? . . . . . . . . . . . 38711-7. How Do You Drop a Database Table Using a Statement? . . . . . . . . . . . . 38811-8. How Do You Retrieve Automatically Generated Keys Using

a Statement (MySQL)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388

■CONTENTSxiv

5203ch00_FM.qxd 8/12/05 12:17 PM Page xiv

Page 16: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

11-9. How Do You Retrieve Automatically Generated Keys Using a Statement (Oracle)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392

11-10. How Do You Determine Whether a SQL Warning Occurred (Using Statement Objects)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395

11-11. How Do You Set the Number of Rows to Prefetch (Using Statement Objects)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395

11-12. How Do You Create a MySQL Table to Store Java Types (Using Statement Objects)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396

11-13. How Do You Create an Oracle Table to Store Java Types (Using Statement Objects)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397

11-14. How Do You Get Rows from a Database Table Using a Statement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398

11-15. How Do You Insert a Row into a Database Table Using a Statement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399

11-16. How Do You Update a Row in a Database Table Using a Statement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399

11-17. How Do You Delete All Rows from a Database Table Using a Statement?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400

11-18. How Do You Get the Number of Rows for a Database Table Using a Statement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401

11-19. How Do You Insert Binary Data into a Database Table Using a Statement?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401

11-20. How Do You Get Binary Data from a Database Table?. . . . . . . . . . . . . . 40211-21. How Do You Execute a Batch of SQL Statements in a

Database Using a Statement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40311-22. How Do You Create an OBJECT Type in an Oracle

Database? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40811-23. How Do You Insert an OBJECT Value into an Oracle Table? . . . . . . . . . . 41011-24. How Do You Get an Oracle’s OBJECT Value from an

Oracle Table?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41111-25. How Do You Delete an OBJECT Type from an Oracle Table? . . . . . . . . . 41311-26. How Does JDBC Support Batch Updates? . . . . . . . . . . . . . . . . . . . . . . . 41411-27. How Do You Map Trees into SQL Using Batch Updates? . . . . . . . . . . . . 415

■CHAPTER 12 Working with the PreparedStatement . . . . . . . . . . . . . . . . . . . . . . 431

12-1. What Is a PreparedStatement Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . 43312-2. How Do You Create a PreparedStatement Object? . . . . . . . . . . . . . . . . . 43312-3. What Are the Differences Between Statement and

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43812-4. What Type Conversions Are Supported by MySQL? . . . . . . . . . . . . . . . . . 43912-5. What Type Conversions Are Supported by Oracle? . . . . . . . . . . . . . . . . . 43912-6. How Do You Use Batch Multiple Updates with

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441

■CONTENTS xv

5203ch00_FM.qxd 8/12/05 12:17 PM Page xv

Page 17: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

12-7. How Do You Execute a SQL Statement Using a PreparedStatement Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445

12-8. How Do You Retrieve Automatically Generated Keys UsingPreparedStatement (MySQL)?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448

12-9. How Do You Retrieve Automatically Generated Keys UsingPreparedStatement (Oracle)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451

12-10. How Do You Check for a SQL Warning UsingPreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453

12-11. How Does PreparedStatement Support ScrollableResultSets? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454

12-12. What Are Updatability, Scrollability, and Holdability for ResultSet Objects? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456

12-13. How Do You Create a Scrollable and Updatable ResultSet Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457

12-14. How Do You Create a Scrollable ResultSet Object? . . . . . . . . . . . . . . . . 45912-15. How Do You Create an Updatable ResultSet Object?. . . . . . . . . . . . . . . 46112-16. How Do You Create a Table Using PreparedStatement? . . . . . . . . . . . . 46512-17. How Do You Drop a Table Using PreparedStatement? . . . . . . . . . . . . . . 46612-18. How Do You Set the Number of Rows to Prefetch Using

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46712-19. How Do You Create a MySQL Table to Store Java Types

Using PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46812-20. How Do You Create an Oracle Table to Store Java Types

Using PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47012-21. How Do You Get Rows/Records from a Table Using

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47212-22. What Are the Steps for Inserting a New Record Using

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47512-23. How Do You Insert a New Record into a Table Using

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47712-23. How Do You Update an Existing Record Using

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48012-24. How Do You Delete All Rows from a Table Using

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48312-25. How Do You Get the Number of Rows for a Table Using

PreparedStatement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48512-26. What Is the Caching of PreparedStatement Objects? . . . . . . . . . . . . . . 48812-27. What Is the Pooling of PreparedStatement Objects? . . . . . . . . . . . . . . . 489

■CONTENTSxvi

5203ch00_FM.qxd 8/12/05 12:17 PM Page xvi

Page 18: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

■CHAPTER 13 Passing Input Parameters to PreparedStatement. . . . . . . . . . 493

13-1. How Do You Pass Input Parameters to a PreparedStatement Object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493

13-2. How Do You Use PreparedStatement.setArray()?. . . . . . . . . . . . . . . . . . . 49513-3. How Do You Use PreparedStatement.setAsciiStream()? . . . . . . . . . . . . . 49813-4. How Do You Use PreparedStatement.setBigDecimal()? . . . . . . . . . . . . . . 50213-5. How Do You Use PreparedStatement.setBinaryStream()? . . . . . . . . . . . . 50613-6. How Do You Use PreparedStatement.setBlob()? . . . . . . . . . . . . . . . . . . . 51313-7. How Do You Use PreparedStatement.setBoolean()? . . . . . . . . . . . . . . . . 51913-8. How Do You Use PreparedStatement’s setByte(), setShort(),

setInt(), and setLong()? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52213-9. How Do You Use PreparedStatement.setBytes()? . . . . . . . . . . . . . . . . . . 52613-10. How Do You Use PreparedStatement.setCharacterStream()? . . . . . . . . 53013-11. How Do You Use PreparedStatement.setClob()? . . . . . . . . . . . . . . . . . . 53413-12. How Do You Use PreparedStatement.setDate()? . . . . . . . . . . . . . . . . . . 53813-13. How Do You Use PreparedStatement’s setFloat() and

setDouble()? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54213-14. How Do You Use PreparedStatement.setNull()? . . . . . . . . . . . . . . . . . . . 54513-15. How Do You Use PreparedStatement.setObject()?. . . . . . . . . . . . . . . . . 54813-16. How Do You Use PreparedStatement.setRef()? . . . . . . . . . . . . . . . . . . . 55313-17. How Do You Use PreparedStatement.setString()? . . . . . . . . . . . . . . . . . 55813-18. How Do You Use PreparedStatement’s setTime() and

setTimestamp()? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56113-19. How Do You Use PreparedStatement.setURL()?. . . . . . . . . . . . . . . . . . . 565

■CHAPTER 14 Exploring JDBC Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569

14-1. What Are JDBC Utilities?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56914-2. How Do You Close a java.sql.Connection Object? . . . . . . . . . . . . . . . . . . 56914-3. How Do You Commit and Close a Connection Object?. . . . . . . . . . . . . . . 57114-4. How Do You Roll Back and Close a Connection Object? . . . . . . . . . . . . . 57314-5. How Do You Close a ResultSet Object? . . . . . . . . . . . . . . . . . . . . . . . . . . 57514-6. How Do You Close a Statement Object?. . . . . . . . . . . . . . . . . . . . . . . . . . 57614-7. How Do You Close a PreparedStatement Object? . . . . . . . . . . . . . . . . . . 57814-8. How Do You Close Statement and Connection Objects

Together? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57914-9. How Do You Close ResultSet, Statement, and Connection

Objects Together? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58014-10. How Do You Return a Long Text Column/Field from a

Database? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58214-11. How Do You Store a Long Text Field in a Database? . . . . . . . . . . . . . . . 58314-12. How Do You Get Table Names from a Database? . . . . . . . . . . . . . . . . . 58414-13. How Do You Get View Names from a Database? . . . . . . . . . . . . . . . . . . 585

■CONTENTS xvii

5203ch00_FM.qxd 8/12/05 12:17 PM Page xvii

Page 19: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

14-14. How Do You Get Table and View Names from a Database?. . . . . . . . . . 58614-15 How Do You Convert an InputStream Object to a Byte Array? . . . . . . . . 58614-16. How Do You Get a Current java.sql.Date Object?. . . . . . . . . . . . . . . . . . 58714-17. How Do You Get a Trimmed String from a Database

Column? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58714-18. How Do You Load a JDBC Driver? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58814-19. How Do You Format a String?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58814-20. How Do You Format an Integer (int Data Type)?. . . . . . . . . . . . . . . . . . . 59014-21. How Do You Format an Integer Object? . . . . . . . . . . . . . . . . . . . . . . . . . 59114-22. How Do You Format a Double Data Type? . . . . . . . . . . . . . . . . . . . . . . . 59114-23. How Do You Format a java.math.BigDecimal Object? . . . . . . . . . . . . . . 59314-24. How Do You Format a Double Object? . . . . . . . . . . . . . . . . . . . . . . . . . . 59414-25. How Do You Build a URL in the Format Needed by the

JDBC Drivers? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59414-26. How Do You Get the Version Number of a JDBC Driver? . . . . . . . . . . . . 59714-27. What Is a JDBC Utility Component (DbUtils)?. . . . . . . . . . . . . . . . . . . . . 59814-28. How Do You Debug/Display a SQLException Object? . . . . . . . . . . . . . . 60514-29. How Do You Debug/Display a SQLWarning Object? . . . . . . . . . . . . . . . . 60514-30. How Do You Debug/Display a ResultSet Object? . . . . . . . . . . . . . . . . . . 60614-31. What Is the Best Way to Generate a Random GUID? . . . . . . . . . . . . . . . 608

■INDEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615

■CONTENTSxviii

5203ch00_FM.qxd 8/12/05 12:17 PM Page xviii

Page 20: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

About the Author

■DR. MAHMOUD PARSIAN is a Sun-certified Java programmer and a seniorlead software engineer at Ask Jeeves (http://www.ask.com). He receivedhis doctorate degree in computer science from Iowa State University and hasbeen working in the software industry for more than 20 years. Mahmoud’sexpertise is in Java technology, JDBC, database design/development, andserver-side Java programming. Mahmoud’s current project is MyJeeves(http://myjeeves.ask.com).

Mahmoud’s honors include the following:

• Ask Jeeves Bright Star Award, Ask Jeeves; November 2004

• Octopus Award, Octopus.com; July 2001

• Cisco Systems Leadership Award, Cisco Systems; June 2000

• Individual Achievement Award, Cisco Systems; July 2000

• Winner of the Circle of Excellence Award; Digital Equipment Corporation, 1991

• Winner of the Best Quality (Alex Trotman, CEO) Award; Ford Motor Company, 1990

• Five-time winner of the Specialist of the Quarter Award; Digital Equipment Corporation,1990–94

You can contact Mahmoud at [email protected].

xix

5203ch00_FM.qxd 8/12/05 12:17 PM Page xix

Page 21: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch00_FM.qxd 8/12/05 12:17 PM Page xx

Page 22: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

About the Technical Reviewer

■SUMIT PAL is a Java and J2EE technical architect. He has more than 12 years ofexperience in software development and has worked for Microsoft and Oracleas a full-time employee. He has a master’s degree in computer science.

Sumit loves to swim and play badminton and now loves to crawl with hisbaby daughter.

xxi

5203ch00_FM.qxd 8/12/05 12:17 PM Page xxi

Page 23: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch00_FM.qxd 8/12/05 12:17 PM Page xxii

Page 24: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Acknowledgments

I’d like to thank my wife, Behnaz; my daughter, Maral; and my son, Yaseen. They all have had to putup with many lost family evenings and weekends. Without their love and support I could never havefinished this book.

I’d also like to thank my dear friend and teacher, Dr. Ramachandran Krishnaswamy. He taughtme the fundamentals of computer science and showed me how to be a “good” teacher.

In addition, I’d like to thank a few special individuals at Apress:

• First, I owe a huge debt of gratitude to Steve Anglin, the lead editor of this book. Steve believedin my book project and provided tremendous support for writing this book. Thank you, Steve.

• Jim Sumser, development editor, provided great support and help for writing this book, andwhen things were not rosy, he gave me moral support and encouragement, which I appreci-ate a lot. Jim’s input on structuring this book was great. Thank you, Jim.

• I thank my technical reviewer, Sumit Pal, for his great job in reviewing the whole book. Sumit’scritical questioning kept me on the right path. I value his input and objectivity. Sumit’s atten-tion to detail and JDBC coding skills are the reason this book is here today. I am grateful forthat.

• I deeply thank Beckie Stones, project manager, for this book. Beckie’s outstanding work andcaring attitude were always refreshing and rejuvenating. She was well organized and helpedme tremendously in many ways. I owe you a hearty thanks!

• I thank Kim Wimpsett, copy editor, for her outstanding editing skills and her understanding ofmy JDBC code. Her contributions have greatly improved the accuracy, readability, and valueof this book.

• I’ll also take this opportunity to thank many other fine people at Apress: Dan Appleman,Ewan Buckingham, Gary Cornell, Tony Davis, Jason Gilmore, Jonathan Hassell, Chris Mills,Dominic Shakeshaft, Grace Wong, Nicole LeClerc, Kari Brooks-Copony, Katie Stence, TimTate, Kurt Krames, and Tom Debolski.

I’d also like to thank my “octopus” colleagues: Tuoc Luong, Tony Xue, Chih-Ming Shih, Nick Tran,Peter Zhang, and Annette Truong.

Last, but not least, I thank my sister, Nayer Azam Parsian, and my brother, Dr. Ahmad Parsian,for their support and just being there for me.

xxiii

5203ch00_FM.qxd 8/12/05 12:17 PM Page xxiii

Page 25: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch00_FM.qxd 8/12/05 12:17 PM Page xxiv

Page 26: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Introduction

This book provides complete and working solutions for performing database tasks using JDBC.You can cut and paste solutions from this book to build your own JDBC database applications. Allthe solutions have been compiled and tested against two leading databases: MySQL and Oracle.This book is ideal for anyone who knows some Java (can read/write basic Java programs) and a littleJDBC (can read/write basic queries using JDBC and SQL) and wants to learn more about JDBC. Eachsection of this book is a complete recipe (including the database setup, the solution, and the MySQLand Oracle solutions), so you can use the code directly in your projects (although sometimes you mayneed to cut and paste the sections you need).

What Is in This Book?This book provides solid guidelines for using JDBC to solve tough problems, such as how to load yourpictures (binary data) into an Oracle/MySQL database and how to retrieve your pictures from thedatabase as a URL. Most of the solutions presented in this book have been used and tested in real-world database applications. In fact, I have designed and developed all the JDBC code for MyJeeves(http://myjeeves.ask.com) using the same philosophies as in this book. You can cut and paste theprovided solutions and tailor them to your own JDBC applications.

What Is the Focus of This Book?According to Sun (http://java.sun.com/jdbc), the creator of JDBC, the JDBC API contains twomajor sets of interfaces:

• The first is the JDBC API for application writers.

• The second is the lower-level JDBC driver API for driver writers.

This book focuses on the JDBC API for application writers.

What This Book Is NotThis is not a book to learn Java programming language and the basics of object-oriented program-ming. I am assuming you know the basics of Java, SQL, and object-oriented programming.

What Is the Structure of This Book?This book is filled with recipes: it asks real questions and provides real, compiled working answers.You can use Java/JDBC to access many kinds of relational (and possibly nonrelational) databasemanagement systems (such as Oracle, MySQL, DB2, SQL Server, and Access, to mention a few).

The goal of this book is to provide step-by-step instructions for using JDBC with two popularrelational databases: Oracle and MySQL. I selected these two databases for the following reasons: xxv

5203ch00_FM.qxd 8/12/05 12:17 PM Page xxv

Page 27: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

• Oracle is the de facto standard for commercial database applications of major companies.

• MySQL is a high-speed, open-source relational database (you can even use a debugger todebug your JDBC method calls).

For every problem raised, you’ll see two solutions: one expressed using the Oracle databaseand the other one using MySQL.

What Does JDBC Do?In a nutshell, JDBC is a Java API for executing SQL statements (such as querying a database, insertingnew records, creating a table, and so on). JDBC makes it possible to perform three tasks:

• Establish a connection with a relational database.

• Using the database connection, send SQL statements (such as a select, insert, update, meta-data request, and so on) and result sets.

• Process the result sets (retrieved from database).

JDBC allows Java programs (applets and applications) to communicate with relational databases(so-called SQL databases) easily and efficiently. JDBC consists of classes in the package java.sqland some JDBC extensions in the package javax.sql. Both of these packages are included in theJava 2 Standard Edition (J2SE) version 1.5 (which covers JDBC 3.0).

Who Is This Book For?This book is for software engineers and database application developers who know the basics of Javaand JDBC. I also assume you know the basics of the Java programming language (writing a class,defining a new class from an existing class, using basic control structures such as while-loop,if-then-else, and so on). Also, I assume you have a basic understanding of relational databaseconcepts and SQL. Like in any Apress problem-solution book, you are encouraged to use the solu-tions for your own database applications and as a launching pad for discovering new databasesolutions using Java/JDBC technology. You can also customize these solutions/recipes as you applythem to a particular problem.

What Software Is Used in This Book?When developing solutions and examples for this book, I used the following software and programmingenvironments:

• Relational databases:

• Oracle 8i Enterprise Edition Release 8.1.7.0.0 (from http://www.oracle.com)

• Oracle 9i Enterprise Edition Release 9.2.0.1.0 (from http://www.oracle.com)

• Oracle 10g Release 10.1.0.2.0 (from http://www.oracle.com)

• MySQL 4.0 (from http://www.mysql.com)

• MySQL 4.1.7 (from http://www.mysql.com)

■INTRODUCTIONxxvi

5203ch00_FM.qxd 8/12/05 12:17 PM Page xxvi

Page 28: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

• Programming languages:

• Java programming language, J2SE 1.4.2 (from http://java.sun.com)

• Java programming language, J2SE 5.0 (from http://java.sun.com)

• Operating systems:

• Linux Enterprise Edition (from http://www.redhat.com)

• Windows XP Professional (from http://www.microsoft.com)

• Web servers:

• Tomcat (http://jakarta.apache.org/tomcat/)

All programs in this book were tested with J2SE 1.4.2 and J2SE 5.0 (from http://java.sun.com/).Examples are given in mixed operating system environments (Linux and Windows XP Professional).For all examples and solutions, I developed them using basic text editors (such as Notepad fromMicrosoft, TextPad from http://www.textpad.com, and vi in Linux) and compiled them using theJava command-line compiler (javac).

Comments and Questions for This Book?I am always interested in your feedback and comments regarding the problems and solutions describedin this book. Please e-mail comments and questions for this book to [email protected]. Youcan also find me at http://www.jdbccookbook.com.

Mahmoud Parsian

■INTRODUCTION xxvii

5203ch00_FM.qxd 8/12/05 12:17 PM Page xxvii

Page 29: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch00_FM.qxd 8/12/05 12:17 PM Page xxviii

Page 30: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Introducing JDBC

This chapter defines some key terms for the remaining chapters. JDBC is a platform-independentinterface between relational databases and Java. In today’s Java world, JDBC is a standard applicationprogramming interface (API) for accessing enterprise data in relational databases (such as Oracle,MySQL, Sybase, and DB2) using Structured Query Language (SQL).

In this chapter, I will introduce all aspects of JDBC, and then you will learn about the more specificins and outs of JDBC in the following chapters. Data (or information) is at the heart of most businessapplications, and JDBC deals with data stored and manipulated in relational database systems.

This book takes an examples-based approach to describing the features and functionalitiesavailable in JDBC. Whether you are a new or an experienced database/JDBC developer, you shouldfind the examples and accompanying text a valuable and accessible knowledge base for creatingyour own database solutions.

■Note Java and JDBC are trademarks of Sun Microsystems in the United States and other countries. Accordingto Sun Microsystems, JDBC is not an acronym (but most Java engineers believe JDBC stands for Java DatabaseConnectivity).

In this book, I will use some basic Java/JDBC utility classes (such as the DatabaseUtil class),which are available for download from the book’s Web site. The DatabaseUtil class provides meth-ods for closing JDBC objects (such as Connection, ResultSet, Statement, and PreparedStatement).VeryBasicConnectionManager is a simple class that provides Connection objects for Oracle and MySQLby using getConnection(dbVendor). In real production applications, the VeryBasicConnectionManagerclass is not an acceptable solution and should be replaced by a connection pool manager (such as thecommons-dbcp package from http://jakarta.apache.org/commons/dbcp/ or Excalibur from http://excalibur.apache.org/). I will use these classes to demonstrate JDBC concepts for different vendorssuch as Oracle and MySQL. (Connection pooling is a technique used for reusing and sharing Connectionobjects among requesting clients.)

1-1. What Is JDBC?JDBC is a set of programming APIs that allows easy connection to a wide range of databases (espe-cially relational databases) through Java programs. In Java 2 Platform, Standard Edition (J2SE) 5.0,the JDBC API is defined by two packages:

java.sql provides the API for accessing and processing data stored in a data source (usually a rela-tional database) using the Java programming language. This package provides the foundation andmost commonly used objects (such as Connection, ResultSet, Statement, and PreparedStatement).This package may be used in J2SE and Java 2 Platform, Enterprise Edition (J2EE) environments.

1

C H A P T E R 1

■ ■ ■

5203ch01.qxd 8/11/05 4:05 PM Page 1

Page 31: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC2

Figure 1-1. Java database application using JDBC

javax.sql provides the API for server-side data source access and processing from the Java pro-gramming language. According to the Java Development Kit (JDK) documentation, “this packagesupplements the java.sql package and, as of the version 1.4 release, is included in the JDK. Itremains an essential part of [J2EE].” This package provides services for J2EE (such as DataSourceand RowSet).

JDBC (http://java.sun.com/products/jdbc/) is Sun Microsystems’ attempt to create a platform-neutral interface between relational databases and Java. According to Sun Microsystems, “JDBCtechnology is an API that lets you access virtually any tabular data source from the Java programminglanguage. It provides cross-DBMS connectivity to a wide range of SQL databases, and now, with thenew JDBC API, it also provides access to other tabular data sources, such as spreadsheets or flat files.”

More specifically, JDBC is a low-level, simple (has a well-defined API), and portable (since Javais portable across platforms) SQL call-level interface (CLI) written in Java. JDBC is an industry-standard SQL database access interface, providing consistent and uniform access to a wide range ofrelational databases (such as Oracle, MySQL, DB2, Sybase, Microsoft SQL Server 2000, MicrosoftAccess, and so on). It also provides a common base on which higher-level tools and interfaces canbe built. JDBC has a support mechanism for the ODBC bridge. The ODBC bridge is a library thatimplements JDBC in terms of the ODBC-standard C API.

In today’s programming world, JDBC is the standard for communication between a Java appli-cation and a relational database. The JDBC API is released in two versions, JDBC version 1.2 (releasedwith JDK 1.1.x in the package java.sql) and version 2.0 (released with Java platform 2 in the packagesjava.sql and javax.sql—now both of these packages are included in J2SE 1.4). JDBC is simple andpowerful because it is a database-independent way of manipulating data from any relational data-base. JDBC version 1.3 is the latest from Sun Microsystems and is included in JDK 1.5.

In a nutshell, JDBC is a database-independent API for accessing a relational database. You passSQL to Java methods in the JDBC classes (the packages java.sql and javax.sql) and get back JDBCobjects (such as ResultSet) that represent the results of your query. JDBC is designed in a simpleway, so most database programmers need to learn only a few methods to do most of what databaseprogrammers need to do to accomplish database programming tasks.

Figure 1-1 shows how a database application uses JDBC to interact with one or more databases.

5203ch01.qxd 8/11/05 4:05 PM Page 2

Page 32: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 3

Figure 1-2. JDBC’s high-level architecture

In this section, I presented the basic outline of the JDBC architecture. JDBC’s DriverManager classprovides the basic service for managing a set of JDBC drivers. I will talk about these classes andinterfaces in detail in section 1-10.

1-2. What Is JDBC’s High-Level Architecture?Figure 1-2 illustrates a high-level architecture of JDBC. You can use JDBC from your Java programs(applications, applets, servlets, and JavaServer Pages).

Your Java programs interact only with the JDBC API. The sole purpose of JDBC is to read data(such as employee records) from databases and to write data (such as inserting new employee records)back to the databases. With JDBC, though, you can do more than reading/writing records. You caneven read the metadata about tables, views, and other useful objects (such as stored proceduresand indexes) in databases. For example, using JDBC’s DatabaseMetadata, you can find out the nameand number of columns for a given table and their associated data types. This information is usefulin developing graphical user interface (GUI)–based applications.

According to Sun Microsystems, the JDBC API contains two major sets of interfaces:

• The first is the JDBC API for application writers.

• The second is the lower-level JDBC driver API for driver writers. (For details on JDBC technologydrivers, please visit http://java.sun.com/products/jdbc/overview.html.)

The focus in this book is on the JDBC API for application writers.

1-3. What Is JDBC’s Detailed Architecture?Figure 1-3 illustrates a detailed architecture of JDBC.

5203ch01.qxd 8/11/05 4:05 PM Page 3

Page 33: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC4

The JDBC API does most of the things through the DriverManager class (java.sql.DriverManager).What is DriverManager? It is a connection factory class. In fact, DriverManager is the only class thatcan create database connections. (Each database connection is represented by an instance ofa java.sql.Connection.) The DriverManager uses drivers to create connections. Each vendor (suchas Oracle, MySQL, and Sybase) provides a set of drivers.

If you refer to JDBC’s architecture (Figure 1-3), you can observe the following facts:

• Java code calls a JDBC library (using the java.sql and javx.sql packages).

• JDBC loads a driver; for example, an Oracle driver is loaded using the following code snippet:

Class.forName("oracle.jdbc.driver.OracleDriver")

• Calling Class.forName() automatically creates an instance of the driver and registers thedriver with the DriverManager class.

• The driver talks to a particular database such as Oracle or MySQL.

Note that you can have more than one driver and therefore more than one database.Figure 1-4 illustrates how a Java application uses JDBC to interact with one or more relational

databases (such as Oracle and MySQL) without knowing about the underlying JDBC driver imple-mentations. Figure 1-4 exposes the core JDBC classes/interfaces that interact with Java/JDBCapplications.

Figure 1-3. JDBC’s detailed architecture

5203ch01.qxd 8/11/05 4:05 PM Page 4

Page 34: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 5

Who provides these JDBC drivers? Usually, a database vendor (such as MySQL, Oracle, Sybase,and so on) writes a JDBC driver (a specific software for a specific database), which is a set of classes/interfaces that implements these interfaces (such as java.sql.Driver) for a particular database system.Following the JDBC architecture, a Java database application uses the DriverManager class to get thejava.sql.Connection object, which represents a database connection. Then, using a Connection object,you can create Statement/PreparedStatement/CallableStatement, which can execute SQL queries andstored procedures and return results as ResultSet objects. (ResultSet is a table of data representinga database result set, which is usually generated by executing a statement that queries the database.)

The JDBC API is comprised of two Java packages: java.sql and javax.sql. The following are coreJDBC classes, interfaces, and exceptions in the java.sql package:

• DriverManager: This class loads JDBC drivers in memory. You can also use it to create java.sql.Connection objects to data sources (such as Oracle, MySQL, and so on).

• Connection: This interface represents a connection with a data source. You can use the Connectionobject for creating Statement, PreparedStatement, and CallableStatement objects.

• Statement: This interface represents a static SQL statement. You can use it to retrieve ResultSetobjects.

• PreparedStatement: This interface extends Statement and represents a precompiled SQLstatement. You can use it to retrieve ResultSet objects.

• CallableStatement: This interface represents a database stored procedure. You can use it toexecute stored procedures in a database server.

• ResultSet: This interface represents a database result set generated by using SQL’s SELECTstatement.

• SQLException: This class is an exception class that provides information on a database accesserror or other errors.

Figure 1-4. Java application using JDBC components

5203ch01.qxd 8/11/05 4:05 PM Page 5

Page 35: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC6

1-4. What Is a Relational Database?What is a database? What is a relational database? A database is a system to store, retrieve, andorganize information. A telephone yellow pages is a database, which stores names and businessnames and their associated phone numbers. A database management system (DBMS) is a computerapplication that enables a user to store, manage, and retrieve data. If you store your addresses andphone numbers online, you can consider the file where you store the information a database.

Dr. E. F. Codd invented the relational database at IBM in 1970. (Codd passed away on April 18,2003.) The term relational database has many definitions. I will mention two of them here:

• A relational database is a finite set of data items organized as a set of formally describedtables from which data can be accessed or reassembled in many different ways without hav-ing to reorganize the database tables.

• A relational database management system (RDBMS) is a persistent system that organizesdata into related rows and columns as specified by the relational model. MySQL, Oracle, andMicrosoft SQL Server are examples of RDBMSs.

Examples of relational databases are Oracle, MySQL, Sybase, Microsoft SQL Server 2000, MicrosoftAccess, DB2, and so on. The major purpose of JDBC is to access/update relational databases. Thestandard user and application program interface to a relational database is SQL. SQL statements areused both for retrieving information from a relational database by using interactive queries and forretrieving data for reports.

You can assume the following for a relational database:

• A database is essentially a container/repository for tables, views, and stored procedures.

• A table/view is a container comprised of rows/records.

• A row/record is a container comprised of columns.

• A column is a single data item having a name, type, and value.

• A database stores all its data inside tables, with nothing else. All operations on data takeplace on the tables themselves or produce additional tables as the result.

1-5. What Is ODBC?Open Database Connectivity (ODBC) is a programming interface from Microsoft that provides a com-mon language for Windows applications to access databases on a network. ODBC is a C-based interfaceto SQL-based database systems. It provides a consistent interface for communicating with a databaseand for accessing database metadata (information about the database system vendor and how thetables, views, and data are stored).

A client can connect to a database and manipulate it in a standard way. ODBC began as a PCstandard, and it has nearly become an industry standard. Vendors provide specific drivers or bridges(the so-called ODBC bridge) to their particular database management system. For example, to accessan ODBC-based database from a Java client, you may use a JDBC-ODBC bridge developed by SunMicrosystems and Merant (now Serena Software). (For details, see http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/bridge.doc.html.) Therefore, you can use the JDBC-ODBC bridge toaccess databases that support ODBC; for example, you can use the JDBC-ODBC bridge to accessMicrosoft Access databases.

According to Microsoft’s ODBC Programmer’s Reference (http://msdn.microsoft.com/library/),the ODBC architecture has four components:

5203ch01.qxd 8/11/05 4:05 PM Page 6

Page 36: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 7

Figure 1-5. ODBC architecture

• Application: Performs processing and calls ODBC functions to submit SQL statements andretrieve results.

• Driver manager: Loads and unloads drivers on behalf of an application. Processes ODBCfunction calls or passes them to a driver.

• Driver: Processes ODBC function calls, submits SQL requests to a specific data source, andreturns results to the application. If necessary, the driver modifies an application’s request sothat the request conforms to syntax supported by the associated DBMS.

• Data source: Consists of the data the user wants to access and its associated operating system,DBMS, and network platform (if any) used to access the DBMS.

Figure 1-5 shows an ODBC architecture and the relationship between these four components.

ODBC is a set of function calls based on the SQL Access Group (SAG) function set for utilizinga SQL database system (back-end system). The SAG set implements the basic functionality of DynamicSQL. Embedded SQL commands can be translated to call ODBC. With ODBC, Microsoft extendedthe basic SAG function set to include functions for accessing the database catalog and for controllingand determining the capabilities of ODBC drivers and their data sources (back ends). Microsoft alsohas refined and fleshed out the SAG proposal. Microsoft supplies the ODBC driver manager for itsoperating systems (Windows, Windows 95, and Windows NT). The ODBC driver manager coordinatesaccess to ODBC drivers and their associated data sources.

Although SQL is well suited for manipulating databases, it was not designed to be a generalapplication language; rather, it was intended to be used only as a means of communicating withdatabases. Unfortunately, you cannot easily write a program that will run on multiple platforms,even though the database connectivity standardization issue has been largely resolved. For exam-ple, if you wrote a database client in C++, you might have to totally rewrite the client for anotherplatform; that is, your PC version would not run on a Macintosh. There are two reasons for this. First,C++ as a language is not portable because C++ is not completely specified (for example, how manybits does an “int” data type hold?). Second, and more important, support libraries such as networkaccess and GUI frameworks are different on each platform. Another problem with ODBC is that itsinterface is complicated and takes time to learn to use well. JDBC eliminated these problems andintroduced a platform-independent solution to relational database access.

Because of its poor performance and lack of transaction support, the JDBC-ODBC bridge driveris recommended only for experimental use or when no other alternative is available.

5203ch01.qxd 8/11/05 4:05 PM Page 7

Page 37: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC8

1-6. What Is a JDBC-ODBC Bridge?In a nutshell, the JDBC-ODBC bridge provides JDBC access via most ODBC drivers. The JDBC-ODBCbridge package is the interface between JDBC and ODBC. The JDBC-ODBC bridge—a joint develop-ment of Merant (now Serena Software) and Sun Microsystems—is a JDBC driver that implementsJDBC operations by translating them into ODBC operations. (ODBC operations are implemented inC-based libraries—the ODBC functionality remains in binary code libraries; if your database orhardware platform changes, you will need to replace the ODBC libraries.) The JDBC-ODBC bridgeimplements JDBC for any database for which an ODBC driver is available. The bridge is implementedas the sun.jdbc.odbc Java package and contains a native library used to access ODBC. The sun.jdbc.odbc package is defined in the <jdk1.4-installation-directory>/jre/lib/rt.jar file, which containsthe sun.jdbc.odbc.JdbcOdbcDriver class (which is a required class for loading the JDBC driver).

Not that the JDBC-ODBC bridge is a “one-size-fits-all” approach. Since it is designed to workwith any database that supports ODBC, it may be slower than other JDBC drivers that are designedto take advantage of protocols specific to an individual database. The JDBC-ODBC bridge implementsa “level-one” type of JDBC driver that links to a driver manager (the libodbc.so.1 and libodbcinst.so.1 files) to communicate with a database-specific ODBC driver. The JDBC-ODBC bridge is comprisedof two packages (that list the classes and interfaces):

• sun.jdbc.odbc provides JdbcOdbcDriver for loading the JDBC-ODBC bridge driver.

• sun.jdbc.odbc.ee provides implementations of javax.sql.DataSource andjavax.sql.ConnectionPoolDataSource.

For details on these packages, refer to http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/bridge.html.

How It WorksFigure 1-6 shows a JDBC-ODBC bridge architecture.

Figure 1-6. JDBC-ODBC bridge architecture

5203ch01.qxd 8/11/05 4:05 PM Page 8

Page 38: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 9

The JDBC-ODBC bridge currently targets straightforward, simple applications. It is not meantfor complex applications with many threads and functional components and with complex programlogic during execution.

Is the JDBC-ODBC bridge driver recommended for commercial applications? According to SunMicrosystems, “the JDBC-ODBC bridge driver is recommended only for experimental use orwhen no other alternative is available.” (For details, see http://java.sun.com/j2se/1.3/docs/guide/jdbc/getstart/bridge.doc.html.)

Where can you get the JDBC-ODBC bridge? The JDBC-ODBC bridge is bundled with the Java 2SDK, Standard Edition, so there is no need to download it separately. (The JDBC-ODBC driverclass sun.jdbc.odbc.JdbcOdbcDriver is included in the Java 2 SDK.)

How fast is the JDBC-ODBC bridge? The JDBC-ODBC bridge is not as fast as commercial JDBCdrivers (such as Oracle and MySQL).

Is the JDBC-ODBC bridge multithreaded? No. The JDBC-ODBC bridge does not support con-current access from different threads. The JDBC-ODBC bridge uses synchronized methods toserialize all the calls it makes to the ODBC layer (written in the C programming language). Mul-tithreaded Java programs may use the JDBC-ODBC bridge, but they will not get the advantagesof multithreading.

Does the JDBC-ODBC bridge support multiple concurrent open statements per database connec-tion? No. You can open only one Statement object per Connection object when you are using theJDBC-ODBC bridge.

Does the JDBC-ODBC bridge support all data types? No. It does not support all data types. (Forexample, Blob and Clob data types are not supported.)

Using the JDBC-ODBC BridgeYou can use the JDBC-ODBC bridge by opening a JDBC connection using a database uniformresource locator (URL) with the odbc subprotocol. The subprotocol odbc is a special case that hasbeen reserved for database URLs that specify ODBC-style data source names and has the specialfeature of allowing any number of attribute values to be specified after the subname (the data sourcename). The full syntax for the odbc subprotocol is as follows:

jdbc:odbc:<data-source-name>[;<attribute-name>=<attribute-value>]*

The following are valid jdbc:odbc names:

• Valid URL without username/password: jdbc:odbc:employees

• Valid URL without username/password and additional attributes:jdbc:odbc:caspian;CacheSize=10;ExtensionCase=LOWER

• Valid URL with username/password: jdbc:odbc:payroll;UID=alex;PWD=mypassword

Before you can establish a connection, you must load the bridge driver class, sun.jdbc.odbc.JdbcOdbcDriver. You can load a class in two ways.

First, you can add the class name to the java.lang.System property named jdbc.drivers:

String JDBC_DRIVERS_KEY = "jdbc.drivers";String existingDrivers = System.getProperty(JDBC_DRIVERS_KEY);if (existingDrivers == null) {

// sets the system property indicated by the specified key.System.setProperty(JDBC_DRIVERS_KEY, "sun.jdbc.odbc.JdbcOdbcDriver");

}

5203ch01.qxd 8/11/05 4:05 PM Page 9

Page 39: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC10

else {String newDrivers = existingDrivers + ":" +

"sun.jdbc.odbc.JdbcOdbcDriver";System.setProperty(JDBC_DRIVERS_KEY, newDrivers);

}

Alternatively, the class must be explicitly loaded using the Java class loader. You can performexplicit class loading using the following code:

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

When a class is loaded, the ODBC driver creates an instance of itself and registers this with theJDBC driver manager.

Example of Using the sun.jdbc.odbc PackageTo use a JDBC-ODBC bridge to access a database, you need to follow these steps:

1. Create a database, or use an existing database.

2. Register the database.

3. Create a simple JDBC program.

4. Run the simple JDBC program.

After I cover these steps in the following sections, I will discuss the JDBC program in detail.

Step 1: Creating a Database or Using an Existing Database

For this step, you can use an existing Microsoft Access database called northwind; Figure 1-7 showsnorthwind in a Windows XP environment.

Figure 1-7. Data source definition in a Windows XP environment

5203ch01.qxd 8/11/05 4:05 PM Page 10

Page 40: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 11

Step 2: Registering the Database

Use the Microsoft ODBC Data Source Administrator to register your database, as shown in Figure 1-8.(For details, please see http://www.simongibson.com/intranet/odbc/ or http://www.cicorp.com/data/odbc/.)

Figure 1-8. Using the Microsoft ODBC Data Source Administrator

Step 3: Creating a Simple JDBC Program

The following listing shows a simple JDBC program; I have added line numbers for discussionpurposes.

1.import java.sql.DriverManager;2.import java.sql.Connection;3.import java.sql.Statement;4.import java.sql.ResultSet;5.import jcb.util.DatabaseUtil;6.7. public class Test_JDBC_ODBC {8. public static Connection getConnection() throws Exception {9. String driver = "sun.jdbc.odbc.JdbcOdbcDriver";10. String url = "jdbc:odbc:northwind";11. String username = "";12. String password = "";13. Class.forName(driver); // load JDBC-ODBC driver14. return DriverManager.getConnection(url, username, password);15. }16.17. public static void main(String args[]) {18. Connection conn = null;19. Statement stmt = null;20. ResultSet rs = null;21 try {22. conn = getConnection();23. stmt = conn.createStatement();24 String query =

5203ch01.qxd 8/11/05 4:05 PM Page 11

Page 41: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC12

25 "select EmployeeID, LastName, FirstName from Employees";26. rs = stmt.executeQuery(query);.27. while(rs.next()){28. System.out.println(rs.getString("EmployeeID")+29. "—"+ rs.getString("LastName")+30. "—"+ rs.getString("FirstName"));31. }32. }33. catch (Exception e){34. // handle the exception35. e.printStackTrace();36. System.err.println(e.getMessage());37. }38. finally {39. // release database resources40. DatabaseUtil.close(rs); // close the ResultSet object41. DatabaseUtil.close(stmt); // close the Statement object42. DatabaseUtil.close(conn); // close the Connection object43. }44. }

Step 4: Running the Simple JDBC Program

The next step is to run the simple program:

$ javac Test_JDBC_ODBC.java$ java Test_JDBC_ODBC1.—Davolio—Mary2.—Fuller—Andrew3.—Leverling—Janet4.—Peacock—Margaret5.—Buchanan—Steven6.—Suyama—Michael7.—King—Robert8.—Callahan—Laura9.—Dodsworth—Anne10.—Kournikova—Anna11.—test—test

Breaking the Simple JDBC Program Down

I will now break this program down and discuss the importance of each line:

Lines 1–5: These lines import the required classes and interfaces from the java.sql package.

Lines 8–15: The getConnection() method loads the proper driver for the database I intend to use.In this case, I used Microsoft Access to create the database, which uses ODBC. To handle ODBCdatabases, a JDBC-ODBC bridge acts as the driver. I am using the method Class.forName() toload the driver. This method gets a Connection object from the DriverManager class. All interac-tions with the database happen through this Connection object.

Lines 22–26: These lines generate a SQL query and retrieve data from the database. First, I geta Connection object and then get a Statement object from the Connection object. A Statementobject is basically used to perform a single SQL statement. In this case, I used the executeQuery()method to execute the simple SELECT statement shown.

Lines 27–30: The ResultSet object contains the retrieved data from the database. A ResultSet isa container that holds the results of the query. A ResultSetworks as an iterator, as shown in the code.

5203ch01.qxd 8/11/05 4:05 PM Page 12

Page 42: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 13

Lines 33–37: This is the place to catch the JDBC errors and handle them properly.

Lines 38–43: These lines close all the database resources. In most cases, if you close a databaseresource, all of its dependent resources will close with it. (For example, closing a connectionwill close any statements issued from it, which will close any ResultSet objects obtained fromthem.) From a good software engineering point of view, you should put close() statements ina finally clause, because the Java virtual machine (JVM) guarantees that the statements in thefinally clause will be executed as the last step regardless of whether an exception has happened.

Debugging Supported by the BridgeThe bridge provides extensive tracing when DriverManager tracing is enabled. The following line ofcode enables tracing and sends it to standard out:

java.sql.DriverManager.setLogStream(java.lang.System.out);

When using the DriverManager.setLogStream() method, you may direct the tracing output toany java.io.PrintStream object.

New JDBC 2.0 API Features Supported by the BridgeAccording to Sun Microsystems (http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/bridge.doc.html), the JDBC-ODBC bridge driver supports the following new features in the JDBC2.0 API: batch updates; updatable result sets; scrollable result sets; the new BigDecimal methods;the new Date, Time, and Timestamp methods, and multithreaded ODBC drivers.

Details of JDBC-ODBC BridgeAs noted, the JDBC-ODBC bridge allows Java applications to use the JDBC API with many existingODBC drivers. The bridge is itself a driver based on JDBC technology (a JDBC driver) that is definedin the class sun.jdbc.odbc.JdbcOdbcDriver. The bridge defines the JDBC subprotocol odbc. Fordetails, refer to http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/bridge.html.

1-7. What Is SQL?SQL (pronounced “sea-quill”) is a standardized database language for creating, manipulating, exam-ining, and managing relational databases (such as Oracle, Sybase, DB2, and MySQL). This book willnot extensively explain SQL, although it will provide basic SQL concepts. Specifically, SQL is a keyword-based language. Each statement begins with a unique keyword. SQL statements consist of clauses,which begin with a keyword. SQL syntax is not case-sensitive.

IBM (and Dr. E. F. Codd) originally created SQL in the mid-1970s, but many vendors have sincedeveloped dialects of SQL. In the 1980s, the American National Standards Institute (ANSI) starteddeveloping a relational database language standard. ANSI and the International Organization forStandardization (ISO) published SQL standards in 1986 and 1987, respectively. ANSI and ISO jointlyworked on an extension to the standard called SQL2 (or SQL-92 or SQL/92). A SQL3 effort is underwayto enhance relational capabilities and add support for object-oriented features. The SQL3 standardprovides new features, such as allowing you to define new data types (so a column can referencea record type by itself).

SQL is a Data Manipulation Language (DML—the set of SQL commands affecting the contentof database objects) and a Data Definition Language (DDL—the set of SQL commands affecting thestructure of database objects). SQL also provides commands for controlling transactions (such ascommit and rollback). Table 1-1 shows some DML commands, Table 1-2 shows some DDL commands,and Table 1-3 shows some transaction commands.

5203ch01.qxd 8/11/05 4:05 PM Page 13

Page 43: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC14

Table 1-1. Partial Data Manipulation Language Commands

SQL Command Semantics

SELECT Retrieves data from the database

INSERT Inserts new rows into a table

UPDATE Updates existing rows in a table

DELETE Deletes existing rows from a table

Table 1-2. Partial Data Definition Language Commands

SQL Command Semantics

CREATE Creates a new database object

DROP Drops an existing database object

ALTER Modifies the structure of a database object

GRANT Grants privileges on tables and views to other users

REVOKE Revokes privileges on tables and views from other users

Table 1-3. Partial Transaction Commands

SQL Command Semantics

COMMIT statement Commits the current transaction

ROLLBACK statement Rolls back the current transaction

SQL statements perform tasks such as reading data from a database, updating data on a data-base, or deleting data from a database. A relational database system (such as Oracle or MySQL) containsone or more objects called tables (a table is a two-dimensional table with rows and columns). Thedata for the database is stored in these tables. Tables are uniquely identified by their names and arecomprised of columns and rows. Columns contain the column name, data type, and any otherattributes for the column (such as security and privileges). Rows contain the records or data for thecolumns. Table 1-4 shows a sample table called MyEmployees.

Table 1-4. MyEmployees Table

Id firstName lastName title salary

60 Bill Russel CTO 980000

70 Alex Baldwin Software Engineer 88000

40 Alex Taylor Hardware Engineer 90000

50 Jane Shakian Manager 88000

30 Mary Keys CEO 100000

10 Alex Smith Software Engineer 78000

20 Bob Sundance Manager 85000

5203ch01.qxd 8/11/05 4:05 PM Page 14

Page 44: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 15

As you may observe, Table 1-4 has five columns (Id, firstName, lastName, title, and salary)and seven rows/records.

The following code listings show how to use SQL to do common tasks. Specifically, the followingcode shows how to create a table using SQL:

CREATE TABLE employees (badgeNumber varchar(10),lastName varchar(32),firstName varchar(32),dept varchar(10)

);

The following code shows how to retrieve data:

SELECT badgeNumber, lastName FROM employees;

The following code shows how to insert data:

INSERT INTO employees(badgeNumber, lastName, firstName)VALUES ('12345', 'Smith', 'Alex');

The following code shows how to update data:

UPDATE employees SET firstName='Alex Jr'WHERE badgeNumber='12345';

1-8. What Is JDBC Programming?The following are the steps involved in JDBC programming:

1. Import the required packages.

2. Register the JDBC drivers.

3. Open a connection to a database.

4. Create a Statement object.

5. Execute a query and return a ResultSet object.

6. Process the ResultSet object.

7. Close the ResultSet and Statement objects.

8. Close the connection.

The first hands-on experience with JDBC in this book involves a basic example to illustrate theoverall concepts related to creating and accessing data in a database. The following sections describein detail the steps involved in writing a database application.

Step 1: Creating a DatabaseDatabase creation is DBMS-specific. This means each vendor has a specific set of commands forcreating a database. For this obvious reason, database creation commands are not portable. (This isthe main reason JDBC has stayed away from creating database commands.)

You can create a database using the command line or tools supplied by the database vendor orusing SQL statements fed to the database from a Java program; this task is normally carried out bya database administrator (or someone with database administrator privileges). Typically, you willuse a CREATE DATABASE statement, but each vendor has its own way of creating databases. (Be sure toreview your vendor-specific SQL reference, as it is not part of the SQL standard but is DBMS-dependent.)Not all JDBC drivers support database creation through DDL.

5203ch01.qxd 8/11/05 4:05 PM Page 15

Page 45: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC16

The following listing shows a shell script (create_kitty.sh) in Unix (Sun/Solaris) that createsan Oracle database called kitty. It is assumed that the Oracle database is installed on /home/oracle(the value of the ORACLE_BASE environment variable) directory. In the example, ORACLE_HOME has thevalue /home/oracle/product817.

$ chmod 775 create_kitty.sh$ cat create_kitty.sh#!/bin/shORACLE_SID=kittyexport ORACLE_SID/home/oracle/product817/bin/svrmgrl << EOFspool /home/oracle/admin/kitty/create/crdb1.logconnect internal startup nomount pfile ="/home/oracle/admin/kitty/pfile/initkitty.ora"

CREATE DATABASE "kitty"maxdatafiles 254maxinstances 8maxlogfiles 32character set UTF8national character set UTF8DATAFILE '/u01/oradata/kitty/system01.dbf'SIZE 260M AUTOEXTEND ON NEXT 10240Klogfile

'/u01/oradata/kitty/redo01.log' SIZE 500K,'/u01/oradata/kitty/redo02.log' SIZE 500K,'/u01/oradata/kitty/redo03.log' SIZE 500K;

disconnectspool offexitEOF

$ ./create_db.sh

Creating and dropping databases in MySQL is easy; just create a database called kitty andthen drop the database as shown in the following code listing:

mysql> show databases;+----------+| Database |+----------+| mysql || test || tiger |+----------+3 rows in set (0.00 sec)

mysql> create database kitty;Query OK, 1 row affected (0.01 sec)

5203ch01.qxd 8/11/05 4:05 PM Page 16

Page 46: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 17

mysql> show databases;+----------+| Database |+----------+| kitty || mysql || test || tiger |+----------+4 rows in set (0.00 sec)

mysql> drop database kitty;Query OK, 0 rows affected (0.00 sec)

mysql> show databases;+----------+| Database |+----------+| mysql || test || tiger |+----------+3 rows in set (0.00 sec)

Step 2: Connecting to an Existing DatabaseThis is the first job of the JDBC driver, and specific information (such as the database URL and databaseusername/password) must be passed to it. The most important requirements are a database URL,a database username (a valid username in the database), and a database password (the associatedpassword for the database user). Depending on the type of driver, many other arguments, attributes,or properties may be available.

The java.sql package contains mostly interfaces. So, which classes implement JDBC’s interfaces?The implementation of these interfaces is all part of the JDBC driver. A JDBC driver allows a Javaapplication to communicate with a SQL database. A JDBC driver is a set of database-specific imple-mentations for the interfaces defined by the JDBC. These driver classes come into being througha bootstrap process. This is best shown by stepping through the process of using JDBC to connect toa database, using Oracle’s type 4 JDBC driver as an example.

First, the main driver class must be loaded into the Java VM:

Class.forName("oracle.jdbc.driver.OracleDriver");

The specified driver (that is, the oracle.jdbc.driver.OracleDriver class) must implement thejava.sql.Driver interface. A class initializer (static code block) within the oracle.jdbc.driver.OracleDriver class registers the driver with the java.sql.DriverManager class.

Next, you need to obtain a connection to the database:

String dbURL = "jdbc:oracle:thin:@localhost:1521:kitty";String dbUsername = "system";String dbPassword = "manager";java.sql.Connection connection =

java.sql.DriverManager.getConnection(dbURL, dbUsername, dbPassword);

5203ch01.qxd 8/11/05 4:05 PM Page 17

Page 47: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC18

DriverManager determines which registered driver to use by invoking the acceptsURL(String url)method of each driver, passing each the JDBC URL. The first driver to return “true” in response willbe used for this connection. In this example, OracleDriver will return “true,” so DriverManager theninvokes the connect() method of OracleDriver to obtain an instance of OracleConnection. It is thisdatabase-specific connection instance implementing the java.sql.Connection interface that ispassed back from the java.sql.DriverManager.getConnection() call.

You can also use an alternate method for creating a database connection: you can first geta SQL driver and then use the SQL driver to get a connection, like so:

String dbURL = "jdbc:oracle:thin:@localhost:1521:kitty";String dbUsername = "system";String dbPassword = "manager";java.util.Properties dbProps = new java.util.Properties();java.sql.Driver sqlDriver =

getSqlDriver("oracle.jdbc.driver.OracleDriver");dbProps.put("user", dbUsername);dbProps.put("password", dbPassword);java.sql.Connection connection = sqlDriver.connect(databaseURL, dbProps);

The getSqlDriver method is as follows:

/*** Get a sql driver* @param driver the name of JDBC driver* @return a JDBC driver based on a given driver name*/public static java.sql.Driver getSqlDriver(String driver)

throws InstantiationException,ClassNotFoundException,IllegalAccessException {

java.sql.Driver sqlDriver = null;sqlDriver = (java.sql.Driver) Class.forName(driver).newInstance();System.out.println("getSqlDriver() is OK. sqlDriver=" + sqlDriver);return sqlDriver;

}

The bootstrap process continues when you create a statement:

java.sql.Connection connection = <get-a-valid-Connection-object>;java.sql.Statement statement = connection.createStatement();

The connection reference points to an instance of OracleConnection. This database-specificimplementation of Connection returns a database-specific implementation of Statement, namely,OracleStatement.

Invoking the execute() method of this Statement object will execute the database-specific codenecessary to issue a SQL statement against the Oracle database and retrieve the results (as a table):

String query = "SELECT id, lastName FROM MyEmployees";java.sql.ResultSet result = statement.executeQuery(query);

The result is a table returned by executing the SELECT statement. Again, what is actuallyreturned is an instance of OracleResultSet, which is an Oracle-specific implementation of thejava.sql.ResultSet interface. By iterating the result, you can get all the selected records.

So, the purpose of a JDBC driver is to provide these implementations that hide all the database-specific details behind standard Java interfaces.

5203ch01.qxd 8/11/05 4:05 PM Page 18

Page 48: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 19

Step 3: Creating a TableThe database is a set of tables (plus other data structures to manage these tables, privileges, and roles);the tables are the actual components that contain data, in the form of rows/records and columns. Youcan create tables using the CREATE TABLE (DDL) statement. This statement has many options, withsome differing from vendor to vendor. For details on creating tables, refer to your DBMS SQL refer-ence for specifics. The following code shows the syntax for creating a table in Oracle and MySQL:

create table MyEmployees (id INT PRIMARY KEY,firstName VARCHAR(20),lastName VARCHAR(20),title VARCHAR(20),salary INT

);

To create a table using the mysql prompt, use the following code:

mysql> create table MyEmployees (- id INT PRIMARY KEY,

firstName VARCHAR(20),lastName VARCHAR(20),title VARCHAR(20),salary INT

);Query OK, 0 rows affected (0.04 sec)

mysql> describe MyEmployees;+-----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-----------+-------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || firstName | varchar(20) | YES | | NULL | || lastName | varchar(20) | YES | | NULL | || title | varchar(20) | YES | | NULL | || salary | int(11) | YES | | NULL | |+-----------+-------------+------+-----+---------+-------+5 rows in set (0.02 sec)

With Oracle, use the following code at the sqlplus prompt:

$ sqlplus octopus/octopusSQL*Plus: Release 8.1.7.0.0 - Production on Thu Jun 6 18:35:52 2002SQL> create table MyEmployees (2 id INT PRIMARY KEY,3 firstName VARCHAR(20),4 lastName VARCHAR(20),5 title VARCHAR(20),6 salary INT7 );

Table created.SQL> describe MyEmployees;Name Null? Type---------- -------- -------------ID NOT NULL NUMBER(38)FIRSTNAME VARCHAR2(20)LASTNAME VARCHAR2(20)TITLE VARCHAR2(20)

5203ch01.qxd 8/11/05 4:05 PM Page 19

Page 49: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC20

In general, in real project development, you define a SQL script that defines all the tables, views,and indexes. But it is also possible to create a table with a Java program. I will provide solutions inMySQL and Oracle. The following code shows the solution for MySQL:

$ javac CreateEmployeeTable.java$ java CreateEmployeeTable mysql---CreateEmployeeTable begin------CreateEmployeeTable: table created---

$ mysql --user=root --password=root --default-character-set=utf8mysql> use octopus;Database changedmysql> desc MyEmployees3;+-----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-----------+-------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || firstName | varchar(20) | YES | | NULL | || lastName | varchar(20) | YES | | NULL | || title | varchar(20) | YES | | NULL | || salary | int(11) | YES | | NULL | |+-----------+-------------+------+-----+---------+-------+5 rows in set (0.01 sec)

mysql> select * from MyEmployees3;+-----+-----------+----------+-------+--------+| id | firstName | lastName | title | salary |+-----+-----------+----------+-------+--------+| 100 | Alex | NULL | NULL | NULL || 200 | Mary | NULL | NULL | NULL |+-----+-----------+----------+-------+--------+2 rows in set (0.02 sec)

To run CreateEmployeeTable.java, use the following code:

import java.sql.Connection;import java.sql.Statement;import java.sql.DriverManager;import java.sql.SQLException;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class CreateEmployeeTable {

private static final String EMPLOYEE_TABLE ="create table MyEmployees3 ( " +" id INT PRIMARY KEY, " +" firstName VARCHAR(20), " +" lastName VARCHAR(20), " +" title VARCHAR(20), " +" salary INT " +")";

public static void main(String args[]) {Connection conn = null;Statement stmt = null;System.out.println("---CreateEmployeeTable begin---");

5203ch01.qxd 8/11/05 4:05 PM Page 20

Page 50: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 21

String dbVendor = args[0]; // database vendortry {conn = VeryBasicConnectionManager.getConnection(dbVendor);stmt = conn.createStatement();stmt.executeUpdate(EMPLOYEE_TABLE);stmt.executeUpdate("insert into MyEmployees3(id, firstName) "+

"values(100, 'Alex')");stmt.executeUpdate("insert into MyEmployees3(id, firstName) "+

"values(200, 'Mary')");System.out.println("---CreateEmployeeTable: table created---");

}catch(ClassNotFoundException ce) {System.out.println("error: failed to load JDBC driver.");ce.printStackTrace();

}catch(SQLException se) {System.out.println("JDBC error:" +se.getMessage());se.printStackTrace();

}catch(Exception e) {System.out.println("other error:"+e.getMessage());e.printStackTrace();

}finally {

// close JDBC/database resourcesDatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

The following code shows the solution for Oracle:

$ javac CreateEmployeeTable.java$ java CreateEmployeeTable oracle---CreateEmployeeTable begin------CreateEmployeeTable: table created---

$ sqlplus scott/tigerSQL*Plus: Release 10.1.0.2.0 - Production on Thu Oct 28 10:50:58 2004SQL> desc MyEmployees3;Name Null? Type----------------- -------- -------------ID NOT NULL NUMBER(38)FIRSTNAME VARCHAR2(20)LASTNAME VARCHAR2(20)TITLE VARCHAR2(20)SALARY NUMBER(38)

SQL> select * from MyEmployees3;

ID FIRSTNAME LASTNAME TITLE SALARY----- --------- -------- ----- ------100 Alex200 Mary

5203ch01.qxd 8/11/05 4:05 PM Page 21

Page 51: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC22

Step 4: Populating a Database (Inserting Rows/Records into a Database)Again, you can enter and maintain data using database-specific tools (GUI tools, SQL*Plus, and soon) or using SQL statements sent with JDBC programs.

To insert records using SQL, use this code:

insert into MyEmployees(id, firstName, lastName, title, salary)values(60, 'Bill', 'Russel', 'CTO', 980000);

insert into MyEmployees(id, firstName, lastName, title, salary)values(70, 'Alex', 'Baldwin', 'Software Engineer', 88000);

To insert records using JDBC, I will provide a Java program that accomplishes this. The inter-face is as follows:

java InsertEmployeeRecord <db-vendor> <id> <firstName><lastName> <title> <salary>

The InsertEmployeeRecord.main() method will accept six arguments, and then it will insertthat record into the database.

Now, to insert the following records, use this code:

60 Bill Russel CTO 98000070 Alex Baldwin Software Engineer 88000

and do the following from the command line:

$ java InsertEmployeeRecord oracle 60 Bill Russel CTO 980000$ java InsertEmployeeRecord oracle 70 Alex Baldwin "Software Engineer" 88000$ java InsertEmployeeRecord mysql 60 Bill Russel CTO 980000$ java InsertEmployeeRecord mysql 70 Alex Baldwin "Software Engineer" 88000

Note that "Software Engineer" has double quotes to represent a single string.

Step 5: Retrieving Table RecordsI will now show how to write a program to accept an ID and to selectively retrieve all related infor-mation (such as firstName, lastName, and so on) for that ID. I will show how to do this by using JDBCto get results in variables. The interface is as follows:

java GetEmployee <db-vendor> <id>

For example, the following is the code to retrieve table records from MySQL:

$ java GetEmployeeRecord mysql 60ok: loaded MySql driver.GetEmployeeRecord: main(): record retrieved.GetEmployeeRecord: main(): firstName=BillGetEmployeeRecord: main(): lastName=RusselGetEmployeeRecord: main(): title=CTOGetEmployeeRecord: main(): salary=980000

For example, the following is the code to retrieve table records from Oracle:

$java GetEmployeeRecord oracle 70ok: loaded Oracle driver. GetEmployeeRecord: main(): record retrieved.GetEmployeeRecord: main(): firstName=AlexGetEmployeeRecord: main(): lastName=BaldwinGetEmployeeRecord: main(): title=Software EngineerGetEmployeeRecord: main(): salary=88000

5203ch01.qxd 8/11/05 4:05 PM Page 22

Page 52: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 23

To review the discussion so far, you can use any JDBC program to load a JDBC driver with this:

Class.forName(driver);

Then, you may use the DriverManager class to get a database connection:

Connection conn = null;conn = DriverManager.getConnection(url, username, password);

The connection (java.sql.Connection) is the most important interface in JDBC. Withouta connection, you cannot do much.

Step 6: Processing the Result SetResultSet.next() returns a boolean; it returns “true” if there is a next row and “false” if not (meaningthe end of the data/set has been reached). Conceptually, a pointer/cursor is positioned just before thefirst row when the ResultSet is obtained. Invoking the next() method moves to the first row, thenthe second, and so on.

Once positioned at a row, the application can get the data on a column-by-column basis usingthe appropriate ResultSet.getXXX method. Here are the methods used in the example to collect thedata:

if (rs.next()) {String firstName = rs.getString(1);String lastName = rs.getString(2);String title = rs.getString(3);int salary = rs.getInt(4);

}

Alternatively, you can use the column names (instead of column positions):

if (rs.next()) {String firstName = rs.getString(“firstName”);String lastName = rs.getString(“lastName”);String title = rs.getString(“title”);int salary = rs.getInt(“salary”);

}

Step 7: Closing JDBC ObjectsReleasing/closing JDBC resources (such as the ResultSet, Statement, PreparedStatement, and Connectionobjects) immediately instead of waiting for this to happen can improve the overall performance ofyour application. From a good software engineering point of view, you should put close() statementsin a finally clause; this guarantees the statements in the finally clause will be executed as the laststep regardless of whether an exception has taken place.

Step 8: Closing ResultSetResultSet has a close() method that releases the ResultSet object’s database and JDBC resourcesimmediately instead of waiting for this to happen when it is automatically closed. The followingcode shows some sample code for closing a ResultSet object; it is always a good idea to have utilityclasses close these JDBC resources:

/*** Close the ResultSet object. Releases the* ResultSet object's database and JDBC resources* immediately instead of waiting for them to be* automatically released.* @param rs a ResultSet object.

5203ch01.qxd 8/11/05 4:05 PM Page 23

Page 53: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC24

public static void close(java.sql.ResultSet rs) {if (rs == null) {

return;}

try {rs.close();// result set is closed now

}catch(Exception ignore) {

// ignore the exception// could not close the result set// cannot do much here

}}

Step 9: Closing StatementStatement has a close() method that releases this Statement object’s database and JDBC resourcesimmediately instead of waiting for this to happen when it is automatically closed. The followingcode shows some sample code for closing a Statement object; it is always a good idea to have utilityclasses close these JDBC resources:

/*** Close the Statement object. Releases the Statement* object's database and JDBC resources immediately instead* of waiting for them to be automatically released.* @param stmt a Statement object.*/public static void close(java.sql.Statement stmt) {

if (stmt == null) {return;

}

try {stmt.close();// result set is closed now

}catch(Exception ignore) {

// ignore the exception// could not close the statement// cannot do much here

}}

Step 10: Closing PreparedStatementPreparedStatement does not have a direct close() method, but since PreparedStatement extendsStatement, then you may use Statement.close() for the PreparedStatement objects; it is alwaysa good idea to have utility classes close these JDBC resources. The following code shows you how toclose a PreparedStatement object:

/*** Close the PreparedStatement object. Releases the* PreparedStatement object's database and JDBC* resources immediately instead of waiting for them

5203ch01.qxd 8/11/05 4:05 PM Page 24

Page 54: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 25

* to be automatically released.* @param pstmt a PreparedStatement object.*/public static void close(java.sql.PreparedStatement pstmt) {

if (pstmt == null) {return;

}

try {pstmt.close();// PreparedStatement object is closed now

}catch(Exception ignore) {

// ignore the exception// could not close the PreparedStatement// cannot do much here

}}

Step 11: Closing the ConnectionIf you are using a connection pool manager (to manage a set of database Connection objects), thenyou need to release (this is called a soft close) the Connection object to the connection pool manager;otherwise, you can use the close() method, which releases this Connection object’s database andJDBC resources immediately instead of waiting for them to be automatically released. The followingcode listing shows some sample code for closing a Connection object; it is always a good idea to haveutility classes close these JDBC resources:

/*** Close the Connection object. Releases the Connection* object's database and JDBC resources immediately instead* of waiting for them to be automatically released.* @param conn a Connection object.*/public static void close(java.sql.Connection conn) {

if (conn == null) {return;

}

try {if (!conn.isClosed()) {

// close the Connection objectconn.close();

}// Connection object is closed now

}catch(Exception ignore) {

// ignore the exception// could not close the Connection object// cannot do much here

}}

5203ch01.qxd 8/11/05 4:05 PM Page 25

Page 55: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC26

1-9. What Is the JDBC API (in a Nutshell)?The JDBC API is comprised of two Java packages (both are included in JDK 1.4):

• java.sql: This is the initial package that provides the API for accessing and processing datastored in a data source (usually a relational database) using the Java programming language.For details, please refer to the official documentation at http://java.sun.com/j2se/1.5.0/docs/api/java/sql/package-summary.html.

• javax.sql: This is an extended package that provides the API for server-side data source accessand processing from the Java programming language. For details, please refer to the official doc-umentation at http://java.sun.com/j2se/1.5.0/docs/api/javax/sql/package-summary.html.

As mentioned, the java.sql package provides the API for accessing and processing data storedin a data source (usually a relational database) using the Java programming language. This JDBC APIincludes a framework and architecture whereby different drivers (implemented by different databasevendors) can be installed dynamically to access different data sources (see Figure 1-9). Although theJDBC API is mainly geared to passing SQL statements to a database, it provides for reading and writ-ing data from any data source with a tabular format. The reader/writer facility, available through thejavax.sql.RowSet group of interfaces, can be customized to use and update data from a spreadsheet,flat file, or any other tabular data source.

The javax.sql package supports the following concepts:

• The DataSource interface as an alternative to DriverManager for establishing a connectionwith a data source. Chapter 2 discusses DataSource in detail.

• Connection pooling

• Distributed transactions

• Row sets

1-10. What Are the Core JDBC Classes/Interfaces?Figure 1-10 shows the core JDBC classes/interfaces used to access a database.

Figure 1-9. JDBC architecture in a nutshell

5203ch01.qxd 8/11/05 4:05 PM Page 26

Page 56: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 27

Figure 1-10. Core JDBC classes/interfaces

The core JDBC classes/interfaces are as follows:

• java.sql.DriverManager: The major task of the DriverManager class is to access the JDBCdrivers and create java.sql.Connection (database connection) objects.

• java.sql.Connection: This interface represents a database connection. This is the key foraccessing most of the database objects (such as tables, columns, and so on).

• java.sql.Statement: The Connection object creates Statement objects. You can use theStatement object to execute SQL statements and queries, which produce ResultSet objects(the result of executing SQL statements and queries).

• java.sql.PreparedStatement: The Connection object creates PreparedStatement (parameterizedstatement) objects. You can use the PreparedStatement object to execute SQL statements andqueries, which produce ResultSet objects (the result of executing SQL statements and queries).

• java.sql.CallableStatement: The Connection object creates CallableStatement (statementsused to execute stored procedures) objects. You can use the PreparedStatement object to exe-cute SQL statements and queries, which produce ResultSet objects (the result of executingSQL statements and queries).

• java.sql.ResultSet: The result of a SQL query is returned via a ResultSet object (a table ofdata representing a database result set, which is usually generated by executing a statementthat queries the database).

1-11. What Is a JDBC Driver?A JDBC driver allows a Java application/client to communicate with a SQL database. A JDBC driveris a Java class that implements the JDBC’s java.sql.Driver interface and understands how to convertprogram (and typically SQL) requests for a particular database. The JDK provides a JDBC-ODBCbridge driver. A Java program that uses the JDBC API loads the specified driver for a particular DBMSbefore it actually connects to a database. The JDBC DriverManager class then sends all JDBC API callsto the loaded driver.

5203ch01.qxd 8/11/05 4:05 PM Page 27

Page 57: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC28

Therefore, in order to connect to a relational database, your Java application/client requiresa driver. This is a piece of software (that is, a Java class) that connects programs to databases ina standard way (JDBC). This is the Java version of ODBC.

Installing a JDBC driver is simply a matter of configuring the Java CLASSPATH (an operating systemenvironment variable) to ensure that the required JDBC classes and interfaces are available. Consultthe documentation for the database you are using to find out where to obtain the JDBC driver, orconsult the list of available JDBC drivers. Drivers are typically packaged as either .zip or .jar archives.

To use Oracle’s JDBC driver in an application, you must set your CLASSPATH environment variableto point to the driver files. The CLASSPATH environment variable tells the JVM and other applicationswhere to find the Java class libraries used in a Java program. You have two ways of setting your CLASSPATHenvironment variable.

You can find Oracle’s implementation of the java.sql package and other supporting classesfound in a file called classes12.zip. For convenience, rename this file to classes12.jar. (Plus, ina Unix environment, Apache Tomcat expects .jar files from the lib directories.) Add the full path-name of the classes12.jar file (this file is from Oracle and contains the implementation of all interfacesdefined in the java.sql package) to the CLASSPATH environment variable, as shown in the followingexamples.

The following apply to Oracle 8.1.7:

• Unix, C shell:

setenv CLASSPATH /home/oracle/product817/jdbc/lib/classes12.jar:$CLASSPATH

• Unix, bash:

CLASSPATH=/home/oracle/product817/jdbc/lib/classes12.jar:$CLASSPATHexport CLASSPATH

• Windows 2000:

set CLASSPATH=c:\home\oracle\product817\jdbc\lib\classes12.jar;%CLASSPATH%

The following apply to Oracle 9i:

• Unix, C shell:

setenv CLASSPATH /home/oracle/oracle9/jdbc/lib/ojdbc.jar:$CLASSPATH

• Unix, bash:

CLASSPATH=/home/oracle/oracle9/jdbc/lib/ojdbc.jar:$CLASSPATHexport CLASSPATH

• Windows 2000:

set CLASSPATH=c:\home\oracle\oracle9\jdbc\lib\ojdbc.jar;%CLASSPATH%

The following apply to MySQL:

• Unix, C shell:

setenv CLASSPATH /home/mysql/jars/mysql.jar:$CLASSPATH

• Unix, bash:

CLASSPATH=/home/mysql/jars/mysql.jar:$CLASSPATHexport CLASSPATH

• Windows 2000:

set CLASSPATH=c:\home\mysql\jars\mysql.jar;%CLASSPATH%

5203ch01.qxd 8/11/05 4:05 PM Page 28

Page 58: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 29

1-12. How Do You Load a JDBC Driver?Before you can connect to the database, you have to load/register the JDBC driver. A JDBC driver isa Java class that implements JDBC’s java.sql.Driver interface. JDK provides the JDBC-ODBC bridgedriver. (The driver name is the sun.jdbc.odbc.JdbcOdbcDriver class, which you can connect to datasources registered by the Microsoft ODBC Data Source Administrator.) Note that the driver name willbe different for each database vendor. The JDBC-ODBC bridge driver (provided by Sun Microsystemsas part of JDK) is sun.jdbc.odbc.JdbcOdbcDriver, the JDBC driver for MySQL database is org.gjt.mm.mysql.Driver (or you can use com.mysql.jdbc.Driver), and the JDBC driver for Oracle databaseis oracle.jdbc.driver.OracleDriver. Each database vendor might have more than one driver for thesame database (with different efficiency and price/performance features).

The class you are loading must be locatable in the CLASSPATH environment variable. This meansyou need to properly add your JAR file, which contains the driver class and supporting classes (whenadding a JAR file to your CLASSPATH, use the absolute path name to the JAR file), to the CLASSPATHenvironment variable. (Java will extract class information from the CLASSPATH environment variable.)

You can load a JDBC driver in six ways:

• Method 1: You can use Class.forName().

• Method 2: You can use DriverManager.registerDriver().

• Method 3: You can create an instance of a Driver class.

• Method 4: You can use System.properties() inside a program.

• Method 5: You can use System.properties() from a command line.

• Method 6: You can use a Thread class.

Method 1: Using Class.forName()In Java, every object has a corresponding class object. This class object instance is shared among allinstances of that class type. Class.forName() loads and returns the class/interface object for thegiven class name. So, for example, if you write the following:

String className = "org.gjt.mm.mysql.Driver";Class driverObject = Class.forName(className);

the JVM will load and return the class object for the org.gjt.mm.mysql.Driver class. Using JDBC,Class.forName() loads the JDBC driver into the JVM. Therefore, a call to Class.forName("X") causesthe class named X to be initialized.

Method 2: Using DriverManager.registerDriver()java.sql.DriverManager provides basic services for managing a set of JDBC drivers. The DriverManagerclass is the management layer of JDBC, working between the user and the JDBC drivers. It keeps trackof the JDBC drivers that are available and handles establishing a connection between a database andthe appropriate driver.

DriverManager.registerDriver(driverObject) registers the given driverObject with theDriverManager class. A newly loaded driver class should call the method registerDriver to makeitself known to the DriverManager class, which is shown in the following code using MySQL:

//String className = "org.gjt.mm.mysql.Driver";try {

// Registers the given driver with the DriverManager.// A newly loaded driver class should call the method// registerDriver to make itself known to the DriverManager.DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());// here the class is loaded

5203ch01.qxd 8/11/05 4:05 PM Page 29

Page 59: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC30

}catch (SQLException e) {

// database access error// driver not loaded; handle the exceptione.printStackTrace();

}

Method 3: Creating an Instance of a Driver ClassYou can load the JDBC drivers by creating an object using the vendor’s JDBC Driver class. For exam-ple, the following example loads two JDBC drivers:

public class LoadJDBCDrivers {// load Oracle driver:oracle.jdbc.driver.OracleDriver oracleDriver =

new oracle.jdbc.driver.OracleDriver();

// load MySQL driver:org.gjt.mm.mysql.Driver mysqlDriver =

new org.gjt.mm.mysql.Driver();}}

Method 4: Using System.properties() Inside a ProgramYou can provide a Java System property, jdbc.drivers, whose value is a list of driver class names.(Class names are separated by a colon.) The following code, the TestSystemProperty program,proves the concept:

import java.util.*;import java.io.*;import java.sql.*;

public class TestSystemProperty {

// Create an JDBC/ODBC connection...public static Connection getJdbcOdbcConnection() throws Exception {

//String driver = "sun.jdbc.odbc.JdbcOdbcDriver";// note: northwind data source must be defined// using the ODBC Data Source AdministratorString url = "jdbc:odbc:northwind";String username = "";String password = "";//Class.forName(driver); // load JDBC-ODBC driverreturn DriverManager.getConnection(url, username, password);

}

// Create an Oracle connection...public static Connection getOracleConnection() throws Exception {

//String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:scorpian";String username = "octopus";String password = "octopus";//Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

5203ch01.qxd 8/11/05 4:05 PM Page 30

Page 60: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 31

// Create an MySQL connection...public static Connection getMySqlConnection() throws Exception {

//String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/tiger";String username = "root";String password = "root";//Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

public static void main(String[] args) {System.out.println("-- TestSystemProperty begin --");Connection oracleConn = null;Connection mysqlConn = null;Connection odbcConn = null;

System.out.println("System.getProperty(\"jdbc.drivers\")="+System.getProperty("jdbc.drivers"));

// try getting a MySQL connectiontry {

mysqlConn = getMySqlConnection();System.out.println("mysqlConn="+mysqlConn);

}catch(Exception e) {

System.out.println("error 1111="+e.getMessage());}

// try getting an ODBC connection againtry {

odbcConn = getJdbcOdbcConnection();System.out.println("mysqlConn="+odbcConn);

}catch(Exception e) {

System.out.println("error 2222="+e.getMessage());}System.out.println("-- TestSystemProperty end --");

}}

The following code shows how to run the TestSystemProperty program:

$ javac TestSystemProperty.java

$ java TestSystemProperty-- TestSystemProperty begin --System.getProperty("jdbc.drivers")=nullerror 1111=No suitable drivererror 2222=No suitable driver

$java -Djdbc.drivers=org.gjt.mm.mysql.Driver:sun.jdbc.odbc.JdbcOdbcDriver TestSystemProperty

-- TestSystemProperty begin --System.getProperty("jdbc.drivers")=

org.gjt.mm.mysql.Driver:sun.jdbc.odbc.JdbcOdbcDriver

5203ch01.qxd 8/11/05 4:05 PM Page 31

Page 61: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC32

mysqlConn=com.mysql.jdbc.Connection@efd552mysqlConn=sun.jdbc.odbc.JdbcOdbcConnection@fd54d6-- TestSystemProperty end --

$

You should put the specified classes (following option -D) in the CLASSPATH environment vari-able; then the DriverManager class automatically loads the JDBC drivers into your program. Thesun.jdbc.odbc.JdbcOdbcDriver is part of the JDK distribution, so there is no need to add it directlyto the CLASSPATH environment variable.

Method 5: Using System.properties() from a Command LineYou can also pass the driver names to jdbc.drivers (as a System property), whose value is a list ofdriver class names (class names are separated by a colon), like so:

java -Djdbc.drivers=org.gjt.mm.mysql.Driver:sun.jdbc.odbc.JdbcOdbcDriver<jdbc-application-program>

Method 6: Using a Thread ClassAnother method to load a JDBC driver is to use a Thread class, like so:

String className = "sun.jdbc.odbc.JdbcOdbcDriver";Class theClass = null;try {

Thread currentThread = Thread.currentThread();theClass = currentThread.getContextClassLoader().loadClass(className);// here the class is loaded

}catch (ClassNotFoundException e) {

// your class not found in CLASSPATH// driver not loaded// handle the exceptione.printStackTrace();// ...

}

Sun Microsystems maintains a list of JDBC drivers for all major database systems. You can findthis list at http://java.sun.com/products/jdbc/jdbc.drivers.html.

1-13. How Do You Test a JDBC Driver Installation?How do you test to see if a certain JDBC driver is installed properly? The simplest way is to try toload the desired JDBC driver; if it loads successfully, then it means your JDBC driver is installedproperly. Otherwise, either the JDBC driver is not properly added to your CLASSPATH environmentvariable or there might be some other problem.

MySQLTo test a JDBC driver installation using MySQL, use the following code:

public class TestJDBCDriverInstallation_MySQL {public static void main(String[] args) {

System.out.println("-- TestJDBCDriverInstallation_MySQL begin --");

// Test a JDBC driver installation

5203ch01.qxd 8/11/05 4:05 PM Page 32

Page 62: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 33

try {String className = "org.gjt.mm.mysql.Driver";Class driverObject = Class.forName(className);System.out.println("driverObject="+driverObject);System.out.println("your installation of JDBC Driver OK.");

}catch(Exception e) {

// your installation of JDBC driver failedSystem.out.println("Failed: JDBC Driver Error: "+e.getMessage());

}

System.out.println("-- TestJDBCDriverInstallation_MySQL end --");}

}

Next, set PATH and CLASSPATH, but do not add the Driver JAR:

$ set PATH=.;C:\java\j2sdk15\bin$ set CLASSPATH=.;c:\jdk\lib\dt.jar;c:\jdk\lib\tools.jar$ set CLASSPATHCLASSPATH=.;c:\jdk\lib\dt.jar;c:\jdk\lib\tools.jar

Then, compile and run the test program:

$ javac TestJDBCDriverInstallation_MySQL.java$ java TestJDBCDriverInstallation_MySQL-- TestJDBCDriverInstallation_MySQL begin --JDBC Driver Error: org.gjt.mm.mysql.Driver-- TestJDBCDriverInstallation_MySQL end --

Next, add the MySQL Connector/J driver to your CLASSPATH:

$ set CLASSPATHCLASSPATH=.;c:\jdk\lib\dt.jar;c:\jdk\lib\tools.jar$ set CLASSPATH=%CLASSPATH%;c:\j\mysql-connector-java-3.1.1-alpha-bin.jar$ set CLASSPATHCLASSPATH=.;c:\jdk\lib\dt.jar;c:\jdk\lib\tools.jar;c:\j\

mysql-connector-java-3.1.1-alpha-bin.jar

Finally, run the test program, and the program should succeed:

$ java TestJDBCDriverInstallation_MySQL-- TestJDBCDriverInstallation_MySQL begin --driverObject=class org.gjt.mm.mysql.Driveryour installation of JDBC Driver OK.-- TestJDBCDriverInstallation_MySQL end --

OracleTo test a JDBC driver installation using Oracle, use the following:

public class TestJDBCDriverInstallation_Oracle {public static void main(String[] args) {

System.out.println("-- TestJDBCDriverInstallation_Oracle begin --");

// Test a JDBC Driver Installationtry {

String className = "oracle.jdbc.driver.OracleDriver";Class driverObject = Class.forName(className);System.out.println("driverObject="+driverObject);System.out.println("your installation of JDBC Driver OK.");

5203ch01.qxd 8/11/05 4:05 PM Page 33

Page 63: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC34

catch(Exception e) {// your installation of JDBC Driver FailedSystem.out.println("Failed: JDBC Driver Error: "+e.getMessage());

}

System.out.println("-- TestJDBCDriverInstallation_Oracle end --");}

}

Next, set PATH and CLASSPATH, but do not add the Driver JAR:

$ set PATH=.;C:\java\j2sdk15\bin$ set CLASSPATH=.;c:\jdk\lib\dt.jar;c:\jdk\lib\tools.jar$ set CLASSPATHCLASSPATH=.;c:\jdk\lib\dt.jar;c:\jdk\lib\tools.jar

Then, compile and run the test program:

$ javac TestJDBCDriverInstallation_Oracle.java$ java TestJDBCDriverInstallation_Oracle-- TestJDBCDriverInstallation_Oracle begin --JDBC Driver Error: oracle.jdbc.driver.OracleDriver-- TestJDBCDriverInstallation_Oracle end --

Next, add the Oracle Thin driver to your CLASSPATH:

$ set CLASSPATHCLASSPATH=.;c:\jdk\lib\dt.jar;c:\jdk\lib\tools.jar$ set CLASSPATH=%CLASSPATH%;c:\j\ojdbc14.jar$ set CLASSPATHCLASSPATH=.;c:\jdk\lib\dt.jar;c:\jdk\lib\tools.jar;c:\j\ojdbc14.jar

Finally, run the test program, and the program should succeed:

$ java TestJDBCDriverInstallation_Oracle-- TestJDBCDriverInstallation_Oracle begin --driverObject=class oracle.jdbc.driver.OracleDriveryour installation of JDBC Driver OK.-- TestJDBCDriverInstallation_Oracle end --

1-14. Where Can You Obtain a JDBC Driver for Your DatabaseSystem?You can find a JDBC driver for your database system in several places:

• Check the vendor of the database system; for example, Oracle (http://www.oracle.com) andMySQL (http://www.mysql.com) provide their own JDBC drivers for their database systems.

• Sun Microsystems maintains a list of JDBC drivers for all major database systems. You canfind this list at http://servlet.java.sun.com/products/jdbc/drivers; this database currentlyhas 219 JDBC drivers.

• The last resort is to search for a JDBC driver using an engine such as Ask.com or Google.com.

1-15. What Is a JDBC Driver Type?According to Sun Microsystems (http://java.sun.com/products/jdbc/driverdesc.html), JDBCtechnology drivers fit into one of four categories:

5203ch01.qxd 8/11/05 4:05 PM Page 34

Page 64: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 35

A JDBC-ODBC bridge provides JDBC API access via one or more ODBC drivers. Note that someODBC native code (and, in many cases, native database client code) must be loaded on eachclient machine that uses this type of driver. Hence, this kind of driver is generally most appro-priate when automatically installing and downloading a Java technology application is notimportant.

A native-API and partly Java technology–enabled driver converts JDBC calls into calls on theclient API for Oracle, Sybase, Informix, DB2, or other DBMSs. Note that, like the bridge driver,this style of driver requires that some binary code be loaded on each client machine.

A network-protocol fully Java technology–enabled driver translates JDBC API calls into a DBMS-independent net protocol that is then translated to a DBMS protocol by a server. This net servermiddleware is able to connect all its Java technology-based clients to many different databases.The specific protocol used depends on the vendor. In general, this is the most flexible JDBC APIalternative. It is likely that all vendors of this solution will provide products suitable for intranetuse. For these products to also support Internet access, they must handle the additional require-ments for security, access through firewalls, and so on, that the Web imposes. Several vendors areadding JDBC technology–based drivers to their existing database middleware products.

A native-protocol fully Java technology–enabled driver converts JDBC technology calls into thenetwork protocol used by DBMSs directly. This allows a direct call from the client machine tothe DBMS server and is a practical solution for intranet access. Since many of these protocolsare proprietary, the database vendors will be the primary source for this style of driver. Severaldatabase vendors have these in progress.

1-16. What Types of JDBC Drivers Exist?JDBC can use four types of drivers to connect to databases.

The type 1 driver translates its calls into ODBC calls (see Figure 1-11). ODBC then interacts withthe desired database. It is the most available but slowest type, and it works only on Microsoft Windowsand Sun Solaris. There is only one driver in existence, sun.jdbc.odbc.JdbcOdbcDriver. You may usethis driver to access the Microsoft Access personal database.

The type 2 driver translates its calls to the native (C, C++, Pascal, and so on) API calls of thedesired database, which then call the desired database (see Figure 1-12). For example, Oracle CallInterface (OCI) calls are used in developing Oracle’s type 2 drivers. (The Oracle OCI is a set of low-

Figure 1-11. JDBC driver type 1

5203ch01.qxd 8/11/05 4:05 PM Page 35

Page 65: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC36

Figure 1-12. JDBC driver type 2

Figure 1-13. JDBC driver type 3

level proprietary APIs used to interact with Oracle databases. It allows you to use operations such aslogon, execute, parse, fetch, and so on. OCI programs are normally written in C or C++, althoughthey can be written in almost any programming language; for details, see http://www.orafaq.com/faqoci.htm.) You will need a driver for each database and operating system combination. Performanceis faster than type 4 drivers.

The type 3 driver is a multitier (n-tier) driver (see Figure 1-13). It is database-independent unlikethe type 1, 2, and 4 drivers. Type 3 drivers connect to an application server (such as WebLogic). Theapplication server in turn connects to a database using a type 1, 2, or 4 driver. The user just specifiesthe type 3 driver’s class name and a data source name to connect to; there is no need to change theclient code when the back-end database changes.

The type 4 driver is for databases that have written their APIs in Java, not C or C++ (see Figure 1-14).So, no translation is needed at runtime by the driver; it calls the database using its Java API. MySQL’sConnector/J driver and Oracle’s Thin driver are both type 4. Note that type 1, 2, and 4 are two-tier(client-server) drivers. In other words, there is no application server in the middle. You can use thesedrivers in typical client-server environments.

5203ch01.qxd 8/11/05 4:05 PM Page 36

Page 66: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 37

Figure 1-14. JDBC driver type 4

Figure 1-15. JDBC drivers and databases

Figure 1-15 illustrates relationships of drivers with databases.

1-17. What Are the Selection Criteria for JDBC Drivers?When you are selecting a driver for an application, you need to consider the following factors:

• Features and ease of use: Does it support the features you need?

• Speed/performance: Does it handle the operations as fast as possible?

• Reliability: Does it pass a set of standard tests?

• Security: Does it handle privileges properly?

• Portability: Can you use the driver in another platform?

5203ch01.qxd 8/11/05 4:05 PM Page 37

Page 67: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC38

• Support: Can the selling company help you when you need some help?

• Price: Can you afford the price with respect to your project requirements?

• Open source: Will the license allow you to add/modify features?

• Compatibility: Is the JDBC driver on the client side compatible with the one on the applica-tion server?

1-18. What Is the URL Syntax for Connecting to a Database?The database URL syntax is based on the premise of URLs on the Internet. The format is as follows:

jdbc:<subprotocol>:<subname>

The subprotocol just shown represents where you would put the particular type of databasemechanism, and the subname is used to actually connect to a database. The database URL tells JDBCwhere your database is located. You can even specify a port in the subname where the database shouldconnect. Table 1-5 shows some examples of what a JDBC URL looks like.

Table 1-5. Examples of JDBC URL

Database URL Database Vendor

jdbc:db2:MYDB2 DB2 IBM

jdbc:mysql://localhost/octopus MySQL MySQL

jdbc:oracle:thin:@calistoga:1521:wooster Oracle 8i Oracle

jdbc:oracle:thin:@10.100.212.12:1521:db9 Oracle 9i Oracle

jdbc:odbc:msAccess Access Microsoft

jdbc:inetdae7://mparsian/northwind SWL Server 2000 Microsoft

In general, the structure of the database URL depends on the type and brand of JDBC driverbeing used. In the case of Oracle’s JDBC drivers, the URL structure is as follows:

driver_name:@driver_specific_information

where driver_name specifies the name of the Oracle JDBC driver you want to use. This may be anyone of the names listed in Table 1-6.

Table 1-6. Oracle JDBC Driver Names

Driver Name Description

jdbc:oracle:thin Oracle JDBC Thin driver (for Oracle 7 and above)

jdbc:oracle:oci Oracle JDBC OCI driver (for Oracle 9i and above)

jdbc:oracle:oci8 Oracle JDBC OCI8 driver (for Oracle 8i and Oracle 8)

jdbc:oracle:oci7 Oracle JDBC OCI7 driver (for Oracle 7)

driver_specific_information specifies any driver-specific information required to connect tothe database. This depends on the driver being used. In the case of the Oracle JDBC Thin driver(type 4 driver), you can specify the driver-specific information in the following format:

hostName:portNumber:DATABASE_SID

5203ch01.qxd 8/11/05 4:05 PM Page 38

Page 68: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 39

For all the Oracle JDBC drivers, you can specify the driver-specific information using an OracleNet8 (or above) keyword-value pair in the following format:

(description=(address=(host=HOST_NAME)(protocol=tcp)(port=PORT_NUMBER)

)(connect_data=(sid=DATABASE_SID))

)

The syntax elements are as follows:

• HOST_NAME: Specifies the name of the machine on which the database is running.

• PORT_NUMBER: Specifies the port number on which the Net8 database listener waits forrequests. 1521 is the default port number.

• DATABASE_SID: Specifies the system identifier (SID) of the database instance to which youwant to connect. Your DBA will be able to provide you with the correct database SID to use.

You may also use an Oracle Net8’s TNSNAMES string. (For more information on this, refer to yourOracle installation directory, $ORACLE_HOME/network/ADMIN/tnsnames.ora, and consult the Oracledocumentation. The tnsnames.ora file is a configuration file used to define Oracle database instancesto Oracle tools on other services that use Oracle databases.) The following example shows the connect()method being used to connect to a database using the Oracle Thin driver (note that, in this example,the database SID is wooster, the database user is scott, and the database password is tiger):

Oracle.connect("jdbc:oracle:thin:@(description=(address=(host=localhost)" +"(protocol=tcp)(port=1521))(connect_data=(sid=wooster)))","scott","tiger");

The Oracle JDBC Thin driver is the most widely used driver since it has the least amount of sys-tem resource requirements and is generally used in lightweight, client-based programs such as Javaapplets. You can use the Oracle JDBC Thin driver to access Oracle 7 databases and above.

1-19. What Is the Mapping Between Java to JDBC SQL Types?Table 1-7 shows how the JDBC SQL types translate to their equivalent Java types.

Table 1-7. JDBC Types Mapped to Java Types

JDBC Type Java Type

CHAR java.lang.String

VARCHAR java.lang.String

LONGVARCHAR java.lang.String

NUMERIC java.math.BigDecimal

DECIMAL java.math.BigDecimal

BIT boolean

TINYINT byte

Continued

5203ch01.qxd 8/11/05 4:05 PM Page 39

Page 69: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC40

Table 1-7. Continued

JDBC Type Java Type

SMALLINT short

INTEGER int

BIGINT long

REAL float

FLOAT double

DOUBLE double

BINARY byte[]

VARBINARY byte[]

LONGVARBINARY byte[]

DATE java.sql.Date

TIME java.sql.Time

TIMESTAMP java.sql.Timestamp

CLOB java.sql.Clob

BLOB java.sql.Blob

ARRAY java.sql.Array

DISTINCT Mapping of underlying type

STRUCT java.sql.Struct

REF java.sql.Ref

JAVA_OBJECT Underlying Java class

Table 1-8 has two purposes. First, it illustrates the general correspondence between types inthe Java programming language and the JDBC types. Second, it shows the mapping used byCallableStatement.getXXX methods and SQLInput.readXXX methods.

Table 1-8. Java Types Mapped to JDBC Types

Java Type JDBC Type

java.lang.String CHAR, VARCHAR, or LONGVARCHAR

java.math.BigDecimal NUMERIC

boolean BIT

byte TINYINT

short SMALLINT

int INTEGER

long BIGINT

float REAL

double DOUBLE

byte[] BINARY, VARBINARY or LONGVARBINARY

java.sql.Date DATE

java.sql.Time TIME

java.sql.Timestamp TIMESTAMP

java.sql.Clob CLOB

java.sql.Blob BLOB

5203ch01.qxd 8/11/05 4:05 PM Page 40

Page 70: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 41

Java Type JDBC Type

java.sql.Array ARRAY

java.sql.Struct STRUCT

java.sql.Ref REF

Java class JAVA_OBJECT

Table 1-9 shows the conversions used for IN parameters before they are sent to the DBMS andused by the PreparedStatement.setXXX and RowSet.setXXX methods. These same conversions arealso used by ResultSet.updateXXX methods and SQLOutput.writeXXX methods. The mapping forString will normally be VARCHAR but will turn into LONGVARCHAR if the given value exceeds the driver’slimit on VARCHAR values. The same is true for byte[], which may be mapped to either VARBINARY orLONGVARBINARY values, depending on the driver’s limit on VARBINARY values. In most cases, the choicebetween CHAR and VARCHAR is not significant. In any case, drivers will just make the right choice. Thesame is true for the choice between BINARY and VARBINARY.

Table 1-9. JDBC Types Mapped to Java Object Types

JDBC Type Java Object Type

CHAR java.lang.String

VARCHAR java.lang.String

LONGVARCHAR java.lang.String

NUMERIC java.math.BigDecimal

DECIMAL java.math.BigDecimal

BIT java.lang.Boolean

TINYINT java.lang.Integer

SMALLINT java.lang.Integer

INTEGER java.lang.Integer

BIGINT java.lang.Long

REAL java.lang.Float

FLOAT java.lang.Double

DOUBLE java.lang.Double

BINARY byte[]

VARBINARY byte[]

LONGVARBINARY byte[]

DATE java.sql.Date

TIME java.sql.Time

TIMESTAMP java.sql.Timestamp

DISTINCT Object type of underlying type

CLOB java.lang.Clob

BLOB java.lang.Blob

ARRAY java.lang.Array

STRUCT java.lang.Struct or java.lang.SQLData

REF java.lang.Ref

JAVA_OBJECT Underlying Java class

5203ch01.qxd 8/11/05 4:05 PM Page 41

Page 71: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC42

Table 1-10 shows the mapping from Java object types to JDBC types that is used by theResultSet.getObject and CallableStatement.getObject methods.

Table 1-10. Java Object Types Mapped to JDBC Types

Java Object Type JDBC Type

java.lang.String CHAR, VARCHAR, or LONGVARCHAR

java.math.BigDecimal NUMERIC

java.lang.Boolean BIT

java.lang.Integer INTEGER

java.lang.Long BIGINT

java.lang.Float REAL

java.lang.Double DOUBLE

byte[] BINARY, VARBINARY, or LONGVARBINARY

java.sql.Date DATE

java.sql.Time TIME

java.sql.Timestamp TIMESTAMP

java.sql.Clob CLOB

java.sql.Blob BLOB

java.sql.Array ARRAY

java.sql.Struct STRUCT

java.sql.Ref REF

Java class JAVA_OBJECT

1-20. How Do You Handle JDBC Errors/Exceptions?In JDBC, errors/exceptions are identified by the java.sql.SQLException class (which extends thejava.lang.Exception class). SQLException is a “checked” exception. Java has two types of exceptions:checked and unchecked (called runtime). A checked exception is a subclass of java.lang.Throwable(the Throwable class is the superclass of all errors and exceptions in the Java language) but not ofRunTimeException (RuntimeException is the superclass of those exceptions that can be thrown duringthe normal operation of the JVM). Checked exceptions have to be caught (and handled properly) orappear in a method that specifies in its signature that it throws that kind of exception.

When a JDBC object (such as Connection, Statement, ResultSet, and so on) encounters a seriouserror, it throws a SQLException. For example, invalid database URLs, invalid database usernames/passwords, database connection errors, malformed SQL statements, nonexistent table/views, andinsufficient database privileges all throw SQLException objects.

The client (database application program) accessing a database server needs to be aware of anyerrors returned from the server. JDBC gives access to such information by providing several levels oferror conditions:

SQLException: SQLException is a Java exception that, if not handled, will terminate the clientapplication. SQLException is an exception that provides information on a database access erroror other type of error.

SQLWarning: SQLWarning is a subclass of SQLException, but it represents nonfatal errors or unex-pected conditions and, as such, can be ignored. SQLWarning is an exception that providesinformation on database access warnings. Warnings are silently chained to the object whosemethod caused it to be reported.

5203ch01.qxd 8/11/05 4:05 PM Page 42

Page 72: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC 43

BatchUpdateException: BatchUpdateException is an exception thrown when an error occursduring a batch update operation. In addition to the information provided by SQLException,a BatchUpdateException provides the update counts for all commands that were executed suc-cessfully during the batch update, that is, all commands that were executed before the erroroccurred. The order of elements in an array of update counts corresponds to the order in whichcommands were added to the batch.

DataTruncation: DataTruncation is an exception that reports a DataTruncation warning (onreads) or throws a DataTruncation exception (on writes) when JDBC unexpectedly truncatesa data value.

The SQLException class extends the java.lang.Exception class and defines an additional methodcalled getNextException(). This allows JDBC classes to chain a series of SQLException objects together.In addition, the SQLException class defines the getMessage(), getSQLState(), and getErrorCode()methods to provide additional information about an error/exception.

I will discuss the SQLException class in Chapter 11.In general, a JDBC client application might have a catch block that looks something like the

following:

String dbURL = ...;String dbUser = ...;String dbPassword = ...;Connection conn = null;try {

conn = DriverManager.getConnection(dbURL, dbUser, dbPassword);//// when you are here, it means that exception has not// happened and you can use the Connection object// (i.e., conn) to do something useful with the database...

}catch (SQLException e) {

// something went wrong: maybe dbUser/dbPassword is not defined// maybe the dbURL is malformed, and other possible reasons.// now handle the exception, maybe print the error code// and maybe log the error, ...while(e != null) {

System.out.println("SQL Exception/Error:");System.out.println("error message=" + e.getMessage());System.out.println("SQL State= " + e.getSQLState());System.out.println("Vendor Error Code= " + e.getErrorCode());// it is possible to chain the errors and find the most// detailed errors about the exceptione = e.getNextException( );

}}

To understand transaction management, you need to understand the Connection.setAutoCommit()method. Its signature is as follows:

void setAutoCommit(boolean autoCommit) throws SQLException

According to J2SE 1.5, setAutoCommit() sets this connection’s autocommit mode to the givenstate. If a connection is in autocommit mode, then all its SQL statements will be executed and com-mitted as individual transactions. Otherwise, its SQL statements are grouped into transactions thatare terminated by a call to either the method commit or the method rollback. By default, new connec-tions are in autocommit mode.

5203ch01.qxd 8/11/05 4:05 PM Page 43

Page 73: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 1 ■ INTRODUCING JDBC44

The following code shows how to handle commit() and rollback() when an exception happens:

String dbURL = ...;String dbUser = ...;String dbPassword = ...;Connection conn = null;try {

conn = DriverManager.getConnection(dbURL, dbUser, dbPassword);conn.setAutoCommit(false); // begin transactionstmt.executeUpdate("CREATE TABLE cats_tricks(" +

"name VARCHAR(30), trick VARHAR(30))") ;stmt.executeUpdate("INSERT INTO cats_tricks(name, trick) " +

"VALUES('mono', 'rollover')") ;conn.commit() ; // commit/end transactionconn.setAutoCommit(true) ;

}catch(SQLException e) {

// print some useful error messagesSystem.out.println("SQL Exception/Error:");System.out.println("error message=" + e.getMessage());System.out.println("SQL State= " + e.getSQLState());System.out.println("Vendor Error Code= " + e.getErrorCode());// roll back the transactionconn.rollback();// optionally, you may set the auto commit to "true"conn.setAutoCommit(true) ;

}

The following code forces the exception to happen: instead of VARCHAR (when creating thecats_tricks table), you can type VARZCHAR. (The database server will not understand VARZCHAR, andtherefore it will throw an exception.)

String dbURL = ...;String dbUser = ...;String dbPassword = ...;Connection conn = null;try {

conn = DriverManager.getConnection(dbURL, dbUser, dbPassword);conn.setAutoCommit(false);stmt.executeUpdate("CREATE TABLE cats_tricks("+

"name VARZCHAR(30), trick VARHAR(30))") ;stmt.executeUpdate("INSERT INTO cats_tricks(name, trick) "+

"VALUES('mono', 'rollover')") ;conn.commit() ;conn.setAutoCommit(true) ;

}catch(SQLException e) {

// print some useful error messagesSystem.out.println("SQL Exception/Error:");System.out.println("error message=" + e.getMessage());System.out.println("SQL State= " + e.getSQLState());System.out.println("Vendor Error Code= " + e.getErrorCode());// rollback the transactionconn.rollback();// optionally, you may set AutoCommit to "true"conn.setAutoCommit(true) ;

}

5203ch01.qxd 8/11/05 4:05 PM Page 44

Page 74: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Exploring JDBC’s Novel Features

This chapter further defines some key JDBC terms for the remaining chapters. Topics defined inthis chapter are as follows:

• Database metadata

• Transaction management

• Connection pool management

• Steps for improving JDBC applications

• Two-tier and three-tier models for JDBC

2-1. What Is Database Metadata?What is metadata? Metadata is data about data. What is database metadata? Database metadata isdata, or information, about a database; the database in turn provides structured, descriptive infor-mation about other data. You can use JDBC to obtain information about the structure of a databaseand its tables, views, and stored procedures. For example, you can get a list of tables in a particulardatabase and the column names for any table. This information is useful when programming forany database because the structure of a database may not be known to the programmer but can beobtained by using database metadata and result set metadata.

JDBC provides four interfaces that deal with database metadata:

java.sql.DatabaseMetaData: Provides comprehensive information about the database asa whole: table names, table indexes, database product name and version, and actions thedatabase supports. Most of the solutions in this chapter are extracted from the solution classDatabaseMetaDataTool.

java.sql.ResultSetMetaData: Gets information about the types and properties of the columnsin a ResultSet object.

java.sql.ParameterMetaData: Gets information about the types and properties of the parametersin a PreparedStatement object. ParameterMetaData, introduced in JDBC 3.0, retrieves informationsuch as the number of parameters in PreparedStatement, the type of data that can be assignedto the parameter, and whether the parameter value can be set to null.

javax.sql.RowSetMetaData: This interface extends the ResultSetMetaData object, which containsinformation about the columns in a RowSet object. This interface has methods for setting thevalues in a RowSetMetaData object. When a RowSetReader object reads data into a RowSet object,it creates a RowSetMetaData object and initializes it using the methods in the RowSetMetaDatainterface. Then the reader passes the RowSetMetaData object to the row set. The methods inthis interface are invoked internally when an application calls the method RowSet.execute; anapplication programmer would not use them directly.

45

C H A P T E R 2

■ ■ ■

5203ch02.qxd 8/11/05 4:18 PM Page 45

Page 75: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES46

For example, to get the name of tables, you can write the following code:

String databaseID = "payroll";// get a valid database Connection objectConnection conn = getConnection(databaseID);DatabaseMetaData dbmd = conn.getMetaData();ResultSet rs = null;if (dbmd == null) {

System.out.println("vendor does not support database metadata");}else {

rs = dbmd.getTables( null, null, null, new String[] {"TABLE"});// iterate result set object and get table informationwhile (rs.next()) {

...}

}

In general, using database metadata (DatabaseMetaData) is expensive and not good on perfor-mance; therefore, you should make sure to use it effectively. For example, when you know the catalog/schema name, you should pass it to DatabaseMetaData.getTables() and other methods that requiresuch data. (Explicitly passing values rather than null will improve performance.) Since schema infor-mation does not change frequently, you can cache this database metadata information.

2-2. What Is a Transaction?According to the Merriam-Webster online dictionary (http://m-w.com), a transaction is defined asthe following:

• Something transacted, especially an exchange or transfer of goods, services, or funds

• Plural: the often published record of the meeting of a society or association

• An act, process, or instance of transacting

• A communicative action or activity involving two parties or things that reciprocally affect orinfluence each other

The word transaction has a special meaning in database technology. A transaction defines anatomic scope of work performed by a database server. From the begin/start transaction commanduntil the commit/rollback command, all requests to manipulate (modify/update/insert/delete)database tables will either succeed (commit) or fail (roll back) together.

To simulate a real business transaction, a program may need to perform several steps. A finan-cial program (such as automatic teller machines—ATMs), for example, might give/transfer cash froma checking account to the account holder (user of the ATM) with the steps listed in the followingpseudocode:

begin transactionStep 1: debit checking accountStep 2: deliver cache to ATM placeholder (to the ATM user)Step 3: update history log

commit transaction

Either all three of these steps must complete or all must fail. Otherwise, data integrity is lost.Because the steps within a transaction are a unified whole, a transaction is often defined as an indi-visible unit of work.

5203ch02.qxd 8/11/05 4:18 PM Page 46

Page 76: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 47

If step 1, step 2, and step 3 finish completely and successfully (that is, with no errors/exceptions),then the transaction will be committed; if, for any reason, one of the steps is not completed, thenthe transaction will terminate/end. In general, a transaction can end in two ways: with a commit orwith a rollback. When a transaction commits (all the steps finish successfully), the data modifica-tions made by its statements are saved. If a step within a transaction fails, the transaction rolls back,undoing the effects of all steps in the transaction. In the pseudocode, for example, if sufficient fundsdo not appear in the user’s checking account (or a disk drive crashed during the update history logstep), then the transaction will roll back and undo the data modifications made by the debit statementto the checking account.

According to http://en.wikipedia.org/wiki/ACID, atomicity, consistency, isolation, and dura-bility (ACID) are considered to be the key transaction processing features of a DBMS. Without theACID characteristics, the integrity of the database cannot be guaranteed. The following section dis-cusses ACID in more depth.

Understanding Transactions in Database SystemsFrom what you have seen already, you can conclude that a transaction is a set of logically relatedactions/steps. From a relational database point of view, a transaction is a set of one or more SQLstatements that make up a logical unit of work that you can either commit or roll back and that willbe recovered in the event of a system failure. All the statements in the transaction are atomic. Inaddition, a transaction is associated with a single java.sql.Connection object.

JDBC allows SQL statements to be grouped into a single transaction. Thus, you can ensure theACID properties using JDBC transactional features. Table 2-1 further explains the ACID characteristics.

Table 2-1. ACID Properties*

ACID Property Description

Atomic A transaction’s changes to the database are atomic: either all happen or nonehappen.

Consistent A transaction is a correct transformation of the state. The actions taken asa group do not violate any of the integrity constraints associated with the state.

Isolated Even though transactions can execute concurrently, it appears to eachtransaction that others executed either before or after it.

Durable Once a transaction completes successfully (for example, commit() returnssuccess), then its changes to the state of the database survive any laterfailures.

* This information comes from http://www.hk8.org/old_web/linux/dbi/ch06_03.htm.

According to TheFreeDictionary.com (http://encyclopedia.thefreedictionary.com/database%20transaction), a database transaction is a unit of interaction with a DBMS or similarsystem that is treated in a coherent and reliable way independent of other transactions. Ideally,a database system will guarantee all the ACID characteristics in databases.

Understanding How JDBC Performs TransactionsIn JDBC, the java.sql.Connection object performs the transaction control. When a connection iscreated, by default it is in autocommit mode (which means that each SQL statement is treated asa transaction by itself—which might not be efficient or might not match business requirements)and will be committed as soon as its execution finishes.

5203ch02.qxd 8/11/05 4:18 PM Page 47

Page 77: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES48

2-3. How Do You Turn On Autocommit Mode?You can turn on autocommit mode for an active Connection object with this code:

java.sql.Connection conn = ...;...conn.setAutoCommit(true);

2-4. How Do You Turn Off Autocommit Mode?You can turn off autocommit mode for an active Connection object with this code:

java.sql.Connection conn = ...;...conn.setAutoCommit(false);

2-5. How Do You Roll Back a Transaction?At any point before committing a transaction, you may invoke rollback() (a method of a Connectionobject) to roll back the transaction and restore values to the last commit point (before the attemptedupdates). The following example shows rollback() and commit() together:

java.sql.Connection conn = getConnection();conn.setAutoCommit(false); // start a transactionjava.sql.Statement stmt = conn.createStatement();stmt.executeUpdate("INSERT INTO emp_table(id, name) VALUES('11', 'alex')");stmt.executeUpdate("INSERT INTO emp_table(id, name) VALUES('22', 'mary')");conn.rollback(); // the preceding inserts will not commit;stmt.executeUpdate("INSERT INTO emp_table(id, name) VALUES('33', 'jeff')");conn.commit(); // end the transaction

Let’s walk through the preceding example to understand the effects of the rollback() andcommit() methods:

1. You first set autocommit off, indicating that the following JDBC statements need to be con-sidered as a unit.

2. You attempt to insert two new records into the emp_table table; however, this change hasnot been finalized (committed) yet.

3. When you invoke rollback(), you cancel the two insert statements and in effect remove anyintention of inserting tuples for 'alex' and 'mary'.

4. Note that emp_table now is still as it was before you attempted the two insert statements.

5. You then attempt another insert, and this time, you commit the transaction. It is only nowthat emp_table is permanently affected and has the new tuple (with a value of 'jeff') in it.

2-6. How Do You Start and End a Transaction in JDBC?JDBC’s default behavior is to consider every operation as atomic; this means for every single state-ment, a transaction begins, and if execution of the statement is successful, then it is committed;otherwise, an exception will be thrown (the transaction is rolled back automatically). Therefore, bydefault in the JDBC model, a connection is in autocommit mode, and its autocommit flag is set totrue; this means each statement executed in this connection has its own transaction that is commit-ted and rolled back when the call to executeQuery, for instance, is returning.

5203ch02.qxd 8/11/05 4:18 PM Page 48

Page 78: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 49

Using Explicit Transactions: Model 1Using JDBC, you can use explicit transactions by following this model:

Connection conn = <get-a-valid-connection-object>;// set the connection in explicit transaction mode// begin a new transactionconn.setAutoCommit(false);

conn.executeQuery(sqlQuery1); // step-1conn.executeQuery(sqlQuery2); // step-2conn.executeQuery(sqlQuery3); // step-3

// commits all the transactionsconn.commit();

or by following this:

// cancel (roll back) all the transactionsconn.rollback();

Using Explicit Transactions: Model 2Using JDBC, you can also use explicit transactions by following this model:

Connection conn = <get-a-valid-connection-object>// set the connection in explicit transaction mode// begin a new transactionconn.setAutoCommit(false);try {

conn.executeQuery(sqlQuery1); // step 1conn.executeQuery(sqlQuery2); // step 2conn.executeQuery(sqlQuery3); // step 3// commits all the transactionsconn.commit();

}catch (Exception e){

//cancel (roll back) all the transactionsconn.rollback();// to see what went wronge.printStackTrace();

}

Model 2 is the preferred way of doing transactions in JDBC.Instead, you can set the transaction to the autocommit mode by using this:

conn.setAutoCommit(true);

2-7. What Is Connection Pool Management?Some JDBC objects (such as java.sql.Connection) are expensive. It might take up to two to threeseconds to create these objects. If you have a lot of clients that are all interested in database services,you do not need to create a single connection per client and then close/discard it after usage.Instead, you can define a pool of connections and, when a client needs a connection, lend a connec-tion to a client. When the client is done with that borrowed connection, the client simply returnsthe connection to the pool of connections. (Returning the Connection object to the pool is calleda soft close.) This way you can improve your application’s performance.

To manage a pool of connections, you need a pool manager. A pool manager is a control structure(a class with some useful methods for checking in and checking out Connection objects) that managesConnection objects by requiring them to be checked in and out of a finite pool. The number of

5203ch02.qxd 8/11/05 4:18 PM Page 49

Page 79: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES50

Connection objects available at any given time will vary. A database client can check out a Connectionobject, use it, and then return it to the pool manager so some other client can use it. Sometimes, theconnection pool will run dry (which means all Connection objects are checked out by clients); if noConnection objects are in the pool when a client makes a request, the client will have to wait untilone is checked back in.

This sharing of Connection objects has at least two benefits:

Limited connections: The number of connections allowed to a particular database may be lim-ited because of server capacity or database licensing restrictions. Hence, you need to limit thenumber of connections created. (You can best handle this with a pool of connections and poolmanager infrastructure.)

Performance: Database connections are costly in terms of the amount (two to three seconds)of time needed for connecting and disconnecting to the database. Reusing and managing theconnections (by the pool manager), rather than discarding and re-creating them every time oneis needed, means that each Connection object will be connected to the database for its entirelifetime. (If a connection goes bad—because of a network problem, for example—then the poolmanager will have to replace bad/defected/stalled connections with new good Connectionobjects.) This reuse of Connection objects results in a drastic increase in performance, as theconnection is already live when the client acquires it.

Connection Pool Manager Minimal FunctionalityThe database connection pool manager should provide the following methods to database clients:

• Check out: Get an open and valid connection from the pool.

• Check in: Return a connection to the pool.

• Release: Release/free all resources, and close all connections at shutdown.

Also, for management purposes, a pool manager should provide the following methods:

• Get the pool status.

• Get the connections status (how many have been checked out, and so on).

• Enable/disable the pool manager.

Connection Pool RequirementsTo define a connection pool, you need to provide the following:

• Database URL

• JDBC driver class

• Username (database user)

• Password (database user’s password)

• Pool minimum (minimum number of connection to be created)

• Pool maximum (maximum number of connection to be created)

• Pool grow amount (how much the pool will be increased by)

• Timeout (timeout for Connection objects)

• Vendor parameters (that help you use vendor-specific features)

• Read-only/write (whether connections will update database)

5203ch02.qxd 8/11/05 4:18 PM Page 50

Page 80: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 51

Connection Pool ImplementationsMany commercial/open-source implementations of connection pools are available.

The javax.sql package provides two public interfaces for managing a pool of Connection objects:

• ConnectionPoolDataSource: This is a factory for PooledConnection objects. An object thatimplements this interface will typically be registered with a naming service that is based onthe Java Naming and Directory Interface (JNDI).

• PooledConnection: This is an object that provides hooks for connection pool management.A PooledConnection object represents a physical connection to a data source. The connec-tion can be recycled rather than being closed when an application is finished with it, thusreducing the number of connections that need to be made.

The following are sample implementations of connection pools:

• The open-source Apache Avalon/Excalibur (org.apache.avalon.excalibur.pool)

• Oracle’s package (oracle.jdbc.pool)

• Apache’s open-source Commons Pool project

• Apache’s open-source DBCP component and the package org.apache.commons.dbcp (whichrelies on code in the commons-pool package to provide the underlying object pool mechanismsit utilizes)

• WebLogic’s connection pool (weblogic.jdbc.connectionPool)

2-8. How Do You Improve the Performance of JDBC Applications?

A program that does not perform acceptably is not functional. Every program must satisfya set of users, sometimes a large and diverse set. If the performance of the program is trulyunacceptable to a significant number of those users, it will not be used. A program that is notbeing used is not performing its intended function.

From the Performance Management Guide at http://publib.boulder.ibm.com/infocenter/pseries/index.jsp?topic=/com.ibm.aix.doc/aixbman/prftungd/perfplanning.htm

What is performance? And what is a performance problem? According to Merriam-Webster’sdictionary, performance means the following:

• The execution of an action/something accomplished

• The fulfillment of a claim, promise, or request

When a software problem is reported, it can be either a functional problem or a performanceproblem. When a software application (such as a JDBC application) is not behaving correctly, this isreferred to as a functional problem. For example, if a SQL query does not return the desired numberof records for a table, this is a functional problem.

Sometimes functional problems lead to performance problems; for example, this happens whenfunctions are working correctly but the speed of the functions is slow. In these cases, rather than tunethe system, it is more important to determine the root cause of the problem and fix it.

In addition, if you make lots of unnecessary metadata calls to the database server, then yourwhole application will run slowly. Another example is when you create a database Connection objectper client request and then discard it after the user request finishes. (Creating database Connection

5203ch02.qxd 8/11/05 4:18 PM Page 51

Page 81: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES52

objects is expensive, so you should use a pool mechanism for sharing database Connection objects.)Experience indicates that performance advice usually depends on the specifics and type of yourdatabase application, server, and environment. This means there’s no single silver bullet for perfor-mance problems.

The following sections contain tips to improve JDBC performance. The performance of yourJDBC application depends on many factors, including the network speed, the Web server, the appli-cation server, the database server environment, and the way you have written your application. I’llfocus on the JDBC component only.

To improve the performance of your JDBC applications, you should consider the followingguidelines and use them when necessary.

Improving Performance: Selecting the Right JDBC DriverThe JDBC driver is the key player in any JDBC application; it acts as a mediator between the data-base application (written in Java) and the database server. When you are selecting a driver for anapplication, you need to consider the following factors:

• Driver type: JDBC defines four types of drivers to use.

• Type 1 (JDBC-ODBC driver)

• Type 2 (native-API, partly Java driver)

• Type 3 (JDBC-net, all-Java driver)

• Type 4 (native-protocol, all-Java driver)

■Note Depending on your requirements, you can choose one driver from these four types. Type 3 and type 4drivers are faster than other drivers; type 3 has a facility for optimization techniques (such as connection poolingand caching database objects) provided by an application server, and type 4 drivers do not need to translate data-base calls to ODBC or a native connectivity interface. Type 2 drivers give better performance than type 1 drivers.

• Speed/performance: The driver must meet the project requirements.

• Reliability: The driver should perform for many hours in a production environment.

• Security: The driver should support access control lists for managing different users and resources.

• Portability: The driver should move from one platform to another.

• Support: The driver should be able to get help in case of a problem.

• Price: The driver should be affordable.

• Open-source: The driver should add/modify features in a timely manner.

Improving Performance: Simplifying SQL QueriesEvaluating complex SQL queries on a database server is slow and will reduce concurrency. ComplexSQL queries will take more time to be parsed.

Improving Performance: Minimizing the Use of Database Metadata MethodsIn general, database metadata methods take longer than other JDBC methods to execute. Comparedto other JDBC methods, database metadata methods that generate java.sql.ResultSet objects arerelatively slow. Database applications should cache information returned from result sets that gen-erate database metadata methods so that multiple executions are not needed.

5203ch02.qxd 8/11/05 4:18 PM Page 52

Page 82: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 53

Improving Performance: Managing Database Connection ObjectsThis is one of the most important factors in JDBC application optimizations. Properly using Connectionobjects can improve the response time of your applications drastically. Lack of attention to connec-tion management can bring your system to its knees in a heavy load (a large number of clients at thesame time).

The java.sql.Connection interface encapsulates database connection functionality. Using theConnection interface, you can fine-tune the following operations, which we’ll look at one by one inthe following sections:

• Setting optimal connection properties

• Using a connection pool

• Controlling a transaction

• Choosing the optimal transaction isolation level

• Closing the connection when finished

Setting Optimal Connection Properties

A JDBC driver provides settings for connection properties. Each of these properties affects the per-formance. Some of the connection property names are different per driver. For example, Oracle andMySQL drivers have some common connection properties, but each of them also provides a uniqueset of connection properties for their driver implementations.

You can set connection properties in at least two ways and then create a new Connection object:

• DriverManager.getConnection(String url, java.util.Properties props)

• Driver.connect(String url, java.util.Properties props)

where props (the second argument) represents the connection properties. For example, MySQLConnector/J has a property called autoReconnect, which can have true and false values. WhenautoReconnect=true, the driver tries to reestablish bad connections. (If a connection has becomedefunct/stale, then the driver reestablishes the connection.)

Setting an Optimal Row Prefetch ValueYou can pass connection properties (that is, database-specific information to the database server bypassing properties) using the java.util.Properties object to improve performance. For example,when you use the Oracle database, you can pass the default number of rows that must be prefetchedfrom the database server and the default batch value that triggers an execution request. Oracle hasthe default value of 10 for both properties. By increasing the value of these properties, you canreduce the number of database calls, which in turn improves performance. The following code illus-trates this approach. It sets defaultRowPrefetch to 40 and defaultBatchValue to 15. (Depending onyour database application requirements, you should set these values accordingly.) DriverManagerwill use these properties to create Connection objects that satisfy these new property values. Someof these property names are the same among all vendors, but most of them will be different. Youshould consult the vendor’s specific driver properties.

To set a prefetch value://import packages, and register the driverimport java.sql.Connection;import java.sql.DriverManger;import java.util.Properties;import oracle.jdbc.OracleDriver;...// register the driverDriverManager.registerDriver (new OracleDriver());

5203ch02.qxd 8/11/05 4:18 PM Page 53

Page 83: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES54

// define database user/passwordString dbUser = "mp";String dbPassword = "mp2";String dbURL = "jdbc:oracle:thin:@myserver:1521:scorpian";// specify the Properties objectProperties props = new Properties();props.put("user", dbUser);props.put("password", dbPassword);props.put("defaultRowPrefetch","40");props.put("defaultBatchValue","15");

// create a new Connection object with desired propertiesConnection conn = DriverManager.getConnection(dbURL, props);

Using a Connection Pool

Among JDBC objects, creating a Connection object (java.sql.Connection) to the database server isexpensive. It is even more expensive if the database server is located on a remote machine. A data-base application can easily spend several seconds every time it needs to establish a connection.Connection pooling is a technique used for sharing server resources (such as connections) amongrequesting clients.

Each time a resource (such as a Statement or PreparedStatement object) attempts to accessa database, it must connect to that database. And to connect to a database, you need a databaseConnection object (that is, a java.sql.Connection object). A database connection incurs overhead—it requires resources to create the connection, maintain it, and then release it when it is no longerrequired. The overhead is high for Web-based database applications, because Web users connectand disconnect more frequently. In addition, Web user interactions are typically shorter than otherapplications because of the surfing nature of Internet users.

In a nutshell, a connection pool is a set of available connections that can be reused. It is thecontainer (that can be represented as a Java object) of a finite set of Connection objects in whicha defined amount of active database connections are waiting to be used and reused; when a clientneeds a database connection, it checks out a connection from a pool and then uses it (to do some-thing useful such as querying a table), and then the client checks in the Connection object to thepool (after using the connection). All this takes place without physically opening or closing a con-nection on the database server, which alone will boost your database application’s performance.

A connection pool contains a finite number of open database connections with a minimum andmaximum number of connections (you need a maximum, because some databases limit the numberof active connections), which means the connection pool has open connections between minimumand maximum numbers that you specify. The pool expands and shrinks between the minimum andmaximum sizes depending on the incremental capacity. You need to give minimum, maximum, andincremental sizes (which is called a grow amount) as properties to the pool in order to maintain thisfunctionality. You get the connection from the pool rather than directly from the database. For exam-ple, you can set up your connection pool using the following properties:

• JDBC driver: oracle.jdbc.driver.OracleDriver

• Database URL: jdbc:oracle:thin:@myserver:1521:maui

• Database user: mp

• User’s password: mp2

• Minimum: 6

• Maximum: 30

• Grow amount: 3

5203ch02.qxd 8/11/05 4:18 PM Page 54

Page 84: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 55

Connection management is important to database application performance. Optimize yourdatabase application by connecting once and using multiple statement objects, instead of perform-ing multiple connections. Several connection management tools are available, so you do not needto start from scratch. Refer to the next section, “Evaluating Choices for Connection Pools.”

Evaluating Choices for Connection Pools

You have several choices when selecting a connection pool management tool:

• Use a third-party connection pool management tool: For this you need to license softwarefrom a vendor so you can understand the requirements and restrictions.

• Use Apache Avalon Excalibur’s pool: You can find Avalon Excalibur’s pool implementations inthe org.apache.avalon.excalibur.pool package. Many implementations are thread-safe,and one is not. You have the option of not limiting used resources at all or limiting the usedresources based on specific rules.

• Use Apache Avalon Excalibur’s DataSource: Avalon Excalibur’s DataSource package inorg.apache.avalon.excalibur.datasource allows you to manage pooled connections inone of two ways. You can have the package handle it for you, or you can use a J2EE server’sDataSource management. It provides the same kind of access regardless of which methodyou choose—since you obtain them through Avalon’s Component Manager infrastructure.

• Use Oracle’s package: This is oracle.jdbc.pool.

• Use Apache’s Commons Pool project: You can find this at http://jakarta.apache.org/commons/pool.

• Use Apache’s DBCP component: You can find this at http://jakarta.apache.org/commons/dbcp in the package org.apache.commons.dbcp. This package relies on code in the commons-poolpackage to provide the underlying object pool mechanisms it utilizes.

• Use WebLogic’s connection pool: This is weblogic.jdbc.connectionPool.

• Use connection pools supported by an application server (such as BEA’s WebLogic and IBM’sWebSphere): Most application servers support connection pooling, which you need to con-figure. (You need to give properties such as the minimum, maximum, and growth amount tothe application server.)

• You can use JDBC interfaces: Use javax.sql.ConnectionPoolDataSource and javax.sql.PooledConnection if your driver implements these interfaces.

• Use custom connection pools: You can create your own connection pool if you are not usingany application server or JDBC 2.0–compatible driver. In general, this is error-prone; youneed to test your solution before releasing it to the production environment.

Controlling a Transaction

In database terminology, a transaction represents one atomic unit of work or a bunch of code in theprogram that executes in its entirety or not at all. To be precise, it is all or no work. In JDBC, a trans-action is a set of one or more Statement objects that execute as a single unit. The java.sql.Connectioninterface provides the following methods to control a transaction in JDBC:

boolean getAutoCommit();void setAutoCommit(boolean autoCommit);void commit();void rollback();

5203ch02.qxd 8/11/05 4:18 PM Page 55

Page 85: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES56

In JDBC, by default, the autocommit mode is true. This means a transaction starts and commitsafter each statement’s execution on a connection. If a connection is in autocommit mode, then allits SQL statements will be executed and committed as individual transactions. (In this case, you donot need to write a commit() method explicitly after each statement.) When the autocommit modeis true, your database application gives poor performance (since beginning and ending/committinga transaction will cost some time). Therefore, to improve the performance of your application, it isbetter to group logically related JDBC statements and then execute all of them under the umbrellaof a transaction.

This batch transaction gives good performance by reducing commit calls after each statement’sexecution. The batch transaction approach is as follows:

Connection conn = null;Statement stmt = null;PreparedStatement pstmt = null;PreparedStatement pstmt2 = null;try{

conn = getConnection();// start transaction explicitlyconn.setAutoCommit(false);String updateBookTable ="update books_table set author=? where isbn=?";

pstmt = conn.prepareStatement(updateBookTable);pstmt.setString(1, "Don Knuth");pstmt.setString(2, "1234567890");pstmt.executeUpdate();

String createBook ="insert into books_table(author, isbn) values(?, ?)";

pstmt2 = conn.prepareStatement(createBook);pstmt2.setString(1, "Mahmoud Parsian");pstmt2.setString(2, "1122334455");pstmt2.executeUpdate();

//// end transaction explicitly//conn.commit();

}catch(SQLException e){

// handle the exception// undo all of the operationsconnection.rollback();

}finally{

// close the JDBC resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(pstmt2);DatabaseUtil.close(conn);

}

Choosing the Optimal Transaction Isolation Level

According to http://www.javaperformancetuning.com/tips/jdbctransaction.shtml, you should dothe following:

5203ch02.qxd 8/11/05 4:18 PM Page 56

Page 86: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 57

If you are not using stored procedures or triggers, turn off autocommit. All transaction levelsoperate faster with autocommit turned off, and doing this means you must code commits.Coding commits while leaving auto-commit on will result in extra commits being done forevery db operation. Use the appropriate transaction level. Increasing performance costs fortransaction levels are TRANSACTION_NONE, TRANSACTION_READ_UNCOMMITTED,TRANSACTION_READ_COMMITTED, TRANSACTION_REPEATABLE_READ, TRANSACTION_SERIALIZABLE. Note that TRANSACTION_NONE, with autocommit set to true gives access totriggers, stored procedures, and large object columns.

Data accessibility is controlled through the transaction isolation level mechanism. Transactionisolation level determines the degree to which multiple interleaved transactions are prevented frominterfering with each other in a multiuser database system. How do you achieve transaction isolation?You achieve it by locking mechanisms that guide the reading and writing of transaction data.

The java.sql.Connection interface provides methods and constants to set and get transactionisolation levels. For example:

public interface Connection {public static final int TRANSACTION_NONE = 0public static final int TRANSACTION_READ_COMMITTED = 2public static final int TRANSACTION_READ_UNCOMMITTED = 1public static final int TRANSACTION_REPEATABLE_READ = 4public static final int TRANSACTION_SERIALIZABLE = 8int getTransactionIsolation();void setTransactionIsolation(int transactionIsolationLevel);

}

You can set the transaction isolation level with the setTransactionIsolation() method bypassing the previous constants (TRANSACTION_XXX) to this method. You can also get the existingtransaction isolation level with the getTransactionIsolation() method.

Improving Performance: Avoiding Using Generic Search PatternsDatabaseMetaData and ResultSetMetaData have methods that you can use to issue generic searchpatterns to the database server by passing the null values. Using null arguments or search patternsin database metadata methods results in generating time-consuming queries. (In a productionenvironment, you might cache these metadata values in order to improve the performance.) In addition,network traffic potentially increases because of unwanted results. Always supply as many non-nullarguments to result sets that generate database metadata methods as possible.

Because DatabaseMetaData methods are slow, database applications should invoke them as effi-ciently as possible. Many applications pass the fewest non-null arguments necessary for the functionto return success.

The non-efficient method call is as follows:

Connection conn = ...;DatabaseMetaData meta = conn.getMetaData();ResultSet rs = meta.getTables (null, null, "EmpTable", null);

The efficient method call is:Connection conn = ...;DatabaseMetaData meta = conn.getMetaData();String[] tableTypes = { "TABLE" };ResultSet rs =

meta.getTables ("empCatalog", "empSchema", "EmpTable", tableTypes);

5203ch02.qxd 8/11/05 4:18 PM Page 57

Page 87: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES58

Improving Performance: Retrieving Only Required DataRetrieving only the required data can reduce the size of data retrieved. For example, do not issue thefollowing:

select * from emp_table where ...

If you just need the id and name, then issue the following:

select id, name from emp_table where ...

You should return only the rows you need. If you return ten columns when you need only twocolumns, you will get decreased performance, especially if the unnecessary rows include long data.

Another way to reduce network traffic and improve your application performance is to reducethe size of any data being retrieved to some manageable limit by calling driver-specific methods:

• Statement.setMaxRows(int max): Sets the limit for the maximum number of rows that anyResultSet object can contain to the given number. If the limit is exceeded, the excess rowsare silently dropped.

• Statement.setMaxFieldSize(): Sets the limit for the maximum number of bytes in a ResultSetcolumn storing character or binary values to the given number of bytes. This limit appliesonly to BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR, and LONGVARCHAR fields. If the limit isexceeded, the excess data is silently discarded. For maximum portability, use values greaterthan 256.

• ResultSet.setFetchSize(int max): Gives the JDBC driver a hint as to the number of rowsthat should be fetched from the database when more rows are needed for this ResultSetobject.

• Statement.setFetchSize(int max): Gives the JDBC driver a hint as to the number of rowsthat should be fetched from the database.

Another method of reducing the size of the data being retrieved is to decrease the column size.You can accomplish this by defining an optimized schema. For example, if the driver allows you todefine the packet size, use the smallest packet size that will meet your needs.

The Statement Object vs. the PreparedStatement Object

Generally, use the Statement object instead of the PreparedStatement object. If you plan to execute a SQLquery many times with different input parameter values, then you should use the PreparedStatementobject. JDBC drivers are optimized based on the perceived use of the functions being executed.Choose between the PreparedStatement object and the Statement object depending on the planneduse. The Statement object is optimized for a single execution of a SQL statement. In contrast, thePreparedStatement object is optimized for SQL statements that will be executed two or more times.You should note that the overhead for the initial execution of a PreparedStatement object is high.The advantage comes with subsequent executions of the SQL statement.

How to Choose the Right Cursor

Choosing the appropriate type of cursor (that is, the ResultSet object) allows maximum applicationflexibility. Three types of cursors exist and can impact the performance of your JDBC application:

ResultSet.TYPE_FORWARD_ONLY: The constant indicating the type for a ResultSet object whosecursor can move only forward. A forward-only cursor provides excellent performance forsequential reads of all the rows in a table.

5203ch02.qxd 8/11/05 4:18 PM Page 58

Page 88: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 59

ResultSet.TYPE_SCROLL_INSENSITIVE: The constant indicating the type for a ResultSet objectthat is scrollable but generally not sensitive to changes made by others. Insensitive cursors areideal for database applications that require high levels of concurrency on the database serverand require the ability to scroll forward and backward through result sets. The first request toan insensitive cursor fetches all the rows and stores them on the client. Therefore, the first requestis very slow when long data is retrieved. Note that subsequent requests do not require any net-work traffic and are processed quickly.

ResultSet.TYPE_SCROLL_SENSITIVE: The constant indicating the type for a ResultSet object thatis scrollable and generally sensitive to changes made by others. Using sensitive cursors, eachrequest generates network traffic; therefore, performance can be very slow.

Batch Multiple Update Statements

A batch update is a set of multiple update statements (such as SQL’s UPDATE and INSERT statements)submitted to the database for processing as one. Sending multiple update statements to the databasetogether as a unit can, in some situations, be much more efficient than sending each update state-ment separately. You can use the java.sql.Statement, java.sql.PreparedStatement, and java.sql.CallableStatement objects to submit batch updates.

The Oracle and MySQL JDBC drivers allow you to accumulate SQL INSERT, SQL DELETE, andSQL UPDATE operations of Statement and PreparedStatement objects at the client and send them tothe database server in batches. This feature reduces round-trips to the database server; therefore,this can improve the performance of database applications.

Batch Multiple Update Statements Using java.sql.Statement

The following code illustrates batch updates using the java.sql.Statement object:

Connection conn = null;Statement stmt = null;try {

// get a valid Connection objectconn = ...// turn off autocommit// and start a new transactionconn.setAutoCommit(false);

stmt = conn.createStatement();stmt.addBatch(

"INSERT INTO books_table(isbn, author) VALUES ('11223344', 'Donald Knuth')");stmt.addBatch(

"INSERT INTO books_table(isbn, author) VALUES ('11223355', 'Donald Knuth')");stmt.addBatch(

"INSERT INTO dept_table(name, city) VALUES ('software', 'New York')");stmt.addBatch(

"INSERT INTO dept_table(name, city) VALUES ('marketing', 'Los Gatos')");stmt.addBatch("delete from dept_table where name = 'hardware'");

// submit a batch of update commands for executionint[] updateCounts = stmt.executeBatch();

// commit the transactionconn.commit();

}catch(Exception e) {

// handle the exception

5203ch02.qxd 8/11/05 4:18 PM Page 59

Page 89: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES60

e.printStackTrace();conn.rollback();

}

Batch Multiple Update Statements Using java.sql.PreparedStatement

The following code illustrates batch updates using the java.sql.PreparedStatement object:

Connection conn = null;PreparedStatement pstmt = null;try {

// get a valid Connection objectconn = ...// turn off autocommit// and start a new transactionconn.setAutoCommit(false);

pstmt = conn.prepareStatement("insert into dept_table values (?, ?)");

pstmt.setString (1, "sales");pstmt.setString (2, "Troy");pstmt.addBatch(); //JDBC queues this for later execution

pstmt.setString (1, "business");pstmt.setString (2, "Los Angeles");pstmt.addBatch(); //JDBC queues this for later execution

pstmt.setString (1, "services");pstmt.setString (2, "Sunnyvale");pstmt.addBatch(); //JDBC queues this for later execution

// now, the queue size equals the batch value of 3// submit a batch of update commands for execution// submit the updates to the DBMS; calling the// PreparedStatement.executeBatch() clears the statement's// associated list of batch elements.int[] updateCounts = pstmt.executeBatch();

// commit the transactionconn.commit();

}catch(Exception e) {

// handle the exceptione.printStackTrace();conn.rollback();

}

Batch Multiple Update Statements Using java.sql.CallableStatement

The batch update facility works the same with CallableStatement objects as it does with PreparedStatement objects. You can use the CallableStatement (which extends PreparedStatement)to execute SQL stored procedures (methods/procedures defined inside the database server).

Multiple sets of input parameter values may be associated with a callable statement and sent tothe DBMS together. Stored procedures invoked using the batch update facility with a callable statementmust return an update count and may not have IN or INOUT parameters. The CallableStatement.executeBatch() method should throw an exception if this restriction is violated.

5203ch02.qxd 8/11/05 4:18 PM Page 60

Page 90: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 61

To call a stored procedure, you invoke methods in the CallableStatement interface. The basicsteps are as follows:

1. Invoke the Connection.prepareCall method to create a CallableStatement object.

2. Invoke the CallableStatement.setXXX methods to pass values to the input (IN) parameters.

3. Invoke the CallableStatement.registerOutParameter method to indicate which parametersare output-only (OUT) parameters or input and output (INOUT) parameters.

4. Invoke the CallableStatement.executeUpdate method to call the stored procedure.

5. If the stored procedure returns result sets, retrieve the result sets.

6. Invoke the CallableStatement.getXXX methods to retrieve values from the OUT parametersor INOUT parameters.

Please note that for batch updates steps 3, 5, and 6 are not required.The following code snippet illustrates calling a stored procedure that has two input parameters,

no output parameters, and no returned ResultSet objects:

Connection conn = null;CallableStatement cstmt = null;...// Create a CallableStatement objectcstmt = con.prepareCall("CALL insert_book_stored_procedure(?, ?)");

// prepare the first batchcstmt.setString (1, "11223344");cstmt.setString (2, "Donald E. Knuth");cstmt.addBatch(); // queue the stored procedure call

// prepare the second batchcstmt.setString (1, "11223377");cstmt.setString (2, "Donald E. Knuth");cstmt.addBatch(); // queue the stored procedure call

// call the stored procedurescstmt.executeBatch();

Improving Performance: Caching PreparedStatement ObjectsCaching prepared statements is another mechanism (introduced in JDBC 3.0) through which youcan improve a JDBC application’s response time. Use PreparedStatement object pooling only withSQL queries that rarely change. You should not use this in queries that have dynamically generatedSQL or queries with frequently changing variables.

Improving Performance: Avoiding Memory LeaksIf you receive messages that you are “running out of cursors” or that you are “running out of memory,”make sure all your Statement, PreparedStatement, CallableStatement, Connection, and ResultSetobjects are explicitly closed. To close these objects, use the close() method. From a software engi-neering point of view, you should put close() statements in a finally clause because this guaranteesthat the statements in the finally clause will be executed as the last step regardless of whether anexception has taken place.

The following code provides a sample code template to do this:

5203ch02.qxd 8/11/05 4:18 PM Page 61

Page 91: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES62

Connection conn = null;Statement stmt = null;PreparedStatement pstmt = null;ResultSet rs = null;try {

// get a valid Connection objectconn = ...// turn off autocommit, and start a new transactionconn.setAutoCommit(false);

stmt = conn.createStatement();stmt.executeUpdate("create table dept_table(...)");

String insert = "insert into dept_table values (?, ?)";pstmt = conn.prepareStatement(insert);

pstmt.setString (1, "sales");pstmt.setString (2, "Troy");pstmt.addBatch(); //JDBC queues this for later execution

pstmt.setString (1, "business");pstmt.setString (2, "Los Angeles");pstmt.addBatch(); //JDBC queues this for later execution

// now, the queue size equals the batch value of 3// submit a batch of update commands for execution// submit the updates to the DBMS; calling the// PreparedStatement.executeBatch() clears the statement's// associated list of batch elements.int[] updateCounts = pstmt.executeBatch();

rs = stmt.executeQuery("select * from dept_table");while (rs.next()) {

...}

// commit the transactionconn.commit();

}catch(Exception e) {

// handle the exceptione.printStackTrace();conn.rollback();

}finally {

// close JDBC resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}

The following is according to Oracle documentation:

5203ch02.qxd 8/11/05 4:18 PM Page 62

Page 92: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 63

The Oracle JDBC drivers do not have finalizer methods. They perform cleanup routines byusing the close() method of the ResultSet and Statement classes. If you do not explicitly closeyour result set and statement objects, significant memory leaks can occur. You could also runout of cursors in the database. Closing a result set or statement releases the correspondingcursor in the database. Similarly, you must explicitly close Connection objects to avoid leak-ing and running out of cursors on the server side. When you close the connection, the JDBCdriver closes any open statement objects associated with it, thus releasing the cursor objectson the server side.

For properly closing JDBC resources such as Connection, ResultSet, and Statement, refer toChapter 14.

2-9. What Are Oracle’s JDBC Drivers?Oracle’s JDBC drivers, releases 7.3.4, 8.0, 9.0, and 10g (and their higher versions), implement thestandard JDBC interface as defined at http://java.sun.com/jdbc. These drivers comply with JDBCversions 1, 2, and 3. In addition to the standard JDBC API, Oracle drivers provide properties, type,and performance extensions. In most situations, as much as possible, you should stay away fromusing the extensions (otherwise your programs will not be portable to other JDBC drivers).

Introducing the Oracle JDBC Thin DriverThe Thin driver does not require Oracle software on the client side. It connects to any Oracle databaseof version 7.2 and higher. The driver requires a TCP/IP listener on the server side.

Oracle’s Thin driver is oracle.jdbc.driver.OracleDriver.

Registering the Oracle JDBC Thin DriverThere are several ways to register the driver:

//// this approach uses the Class.forName() method//try {

Class.forName("oracle.jdbc.driver.OracleDriver");}catch(ClassNotFoundException e) {

// handle the exception properlySystem.out.println("cannot find oracle.jdbc.driver.OracleDriver");System.exit(1);

}

//// this approach uses the DriverManager.registerDriver() method//try {

DriverManager.registerDrive( new oracle.jdbc.driver.OracleDriver());}catch(SQLException e) {

// handle the exception properlySystem.out.println("cannot register oracle.jdbc.driver.OracleDriver");System.exit(1);

}

5203ch02.qxd 8/11/05 4:18 PM Page 63

Page 93: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES64

Introducing the Oracle JDBC OCI DriverWhat is the OCI? The OCI is a set of low-level APIs to perform Oracle database operations (for example,logon, execute, parse, fetch records). OCI programs are normally written in C/C++, although theycan be written in almost any programming language. OCI programs are not precompiled. To use theOCI driver, you must also download the appropriate shared library or DLL files.

Oracle’s JDBC OCI drivers are type 2 JDBC drivers. They provide an implementation of the JDBCinterfaces, and the implementation uses the OCI to interact with an Oracle database. This driver canaccess Oracle 7.x and higher servers. Because they use native methods, they are platform-specific.The JDBC OCI driver requires an Oracle client installation including SQL*Net and all other dependentfiles. The supported platforms are as follows:

• Solaris: Version 2.5 and above

• Windows: Windows 95 and NT 3.51 and above

• Linux: Red Hat 9

2-10. How Do You Connect with Oracle’s JDBC Thin Driver?The JDBC Thin driver provides the only way to access Oracle from the Web (applets). It is smallerand faster than the OCI drivers, and it does not require a preinstalled version of the JDBC drivers.The following code shows how to connect with the Thin driver:

import java.sql.*;import jcb.util.DatabaseUtil;

class TestJDBCDriver_Thin {public static Connection getConnection() throws Exception {

String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:scorpian";String username = "octopus";String password = "octopus";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

public static void main (String args []) throws Exception {Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

conn = getConnection();stmt = conn.createStatement();rs = stmt.executeQuery ("select object_name from user_objects");

while (rs.next()) {// print object_nameSystem.out.println (rs.getString(1));

}}catch(Exception e) {

// handle exceptionSystem.out.println(e.getMessage());

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);

5203ch02.qxd 8/11/05 4:18 PM Page 64

Page 94: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 65

DatabaseUtil.close(conn);}

}}

2-11. How Do You Connect with Oracle’s JDBC OCI Driver?To use OCI drivers, you must have Net8 (SQL*Net) installed and working before attempting to useone of the OCI drivers. The solution is almost identical to the Thin driver with the exception of thegetConnection() method:

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:oci9:@scorpian";String username = "octopus";String password = "octopus";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

2-12. How Do You Connect with Oracle’s KPRB Driver?You can obtain a handle to the default or current connection (KPRB driver) by calling the OracleDriver.defaultConnection() method. You should note that you do not need to specify a database URL,username, or password, as you are already connected to a database session.

Remember to not close the default connection. Closing the default connection might throw anexception in future releases of Oracle. The solution is almost identical to the Thin driver with theexception of the getConnection() method:

public static Connection getConnection() throws Exception {oracle.jdbc.driver.OracleDriver oracleDriver =

new oracle.jdbc.driver.OracleDriver();return oracleDriver.defaultConnection();

}

Please note that if you use Oracle’s KPRB driver, do not close the default connection (otherwisethe database will shut down).

2-13. What Are MySQL’s JDBC Drivers?MySQL Connector/J is the official JDBC driver for MySQL. The following is according to the MySQLdocumentation (http://www.mysql.com/products/connector-j/index.html):

MySQL Connector/J is a native Java driver that converts JDBC calls into the network protocolused by the MySQL database. It lets developers working with the Java programming languageeasily build programs and applets that interact with MySQL and connect all corporate data,even in a heterogeneous environment. MySQL Connector/J is a type 4 JDBC driver and hasa complete JDBC feature set that supports the capabilities of MySQL.

MySQL Connector/J is an implementation of Sun’s JDBC 3.0 API for the MySQL relational data-base server. It strives to conform as much as possible to the JDBC API as specified at http://java.sun.com/jdbc. It is known to work with many third-party products, including Apache Tomcat, JBoss,BEA WebLogic, IBM WebSphere, Eclipse, and Borland JBuilder.

5203ch02.qxd 8/11/05 4:18 PM Page 65

Page 95: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES66

What Is MySQL’s Driver Class Name?The name of the class that implements java.sql.Driver in MySQL Connector/J is com.mysql.jdbc.Driver. The org.gjt.mm.mysql.Driver class name is also usable to remain backward-compatiblewith MM.MySQL. You should use this class name when registering the driver or when otherwiseconfiguring software to use MySQL Connector/J.

What Is the JDBC URL Format for MySQL Connector/J?The JDBC URL format for MySQL Connector/J is as follows, with the items in square brackets ([ ])being optional:

jdbc:mysql://[host][,failoverhost...][:port]/[database][?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...

If the hostname is not specified, it defaults to 127.0.0.1. If the port is not specified, it defaultsto 3306, the default port number for MySQL servers. For connection properties, please refer tohttp://www.mysql.com/documentation/connector-j/index.html.

2-14. How Do You Register the MySQL Driver with the DriverManager?The following code shows how to register the MySQL driver with the driver manager:

public class LoadMySQLDriver {public static void main(String[] args) {

try {Class.forName("com.mysql.jdbc.Driver");

}catch (ClassNotFoundException ce) {

// handle the exception}catch (Exception e) {

// handle the exception}

}}

2-15. How Do You Get a MySQL Connection from the DriverManager?The following example shows how you can get a Connection object from the driver manager. Thereare a few different signatures for the getConnection() method. You should consult the JDK APIdocumentation for the proper methods to use.

The following are the methods you can use to create a MySQL Connection object:

• Method 1: Use only the database URL.

• Method 2: Use the database URL and pass "user" and "password" as parameters.

• Method 3: Pass the "user" and "password" parameters as a java.util.Properties object.

Method 1: Using Only the Database URLThe following code shows the first method:

5203ch02.qxd 8/11/05 4:18 PM Page 66

Page 96: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 67

import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;

class TestGetMySQLConnection_1public static Connection

getConnection(String dbURL, String user, String password)throws SQLException, ClassNotFoundException {//load a driverClass.forName("com.mysql.jdbc.Driver");// form a connection string acceptable to MySQL driverString connString =

dbURL + "?user=" + user + "&password=" + password;return DriverManager.getConnection(connString);

}

public static void main(String[] args) {Connection conn = null;try {

conn = getConnection("jdbc:mysql://localhost/empDB","root", "root2");

// do something useful with the Connection object}catch (ClassNotFoundException ce) {

// there was problem in loading a driver// handle the exception

}catch (SQLException e) {

// handle any database exceptionsSystem.out.println("SQLException: " + ex.getMessage());System.out.println("SQLState: " + ex.getSQLState());System.out.println("VendorError: " + ex.getErrorCode());

}}

}

Method 2: Using the Database URL and Passing "user" and "password" As ParametersThe following code shows the second method:

import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;

class TestGetMySQLConnection_2public static Connection

getConnection(String dbURL, String user, String password)throws SQLException, ClassNotFoundException {//load a driverClass.forName("com.mysql.jdbc.Driver");return DriverManager.getConnection(dbURL, user, password);

}

public static void main(String[] args) {Connection conn = null;

5203ch02.qxd 8/11/05 4:18 PM Page 67

Page 97: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES68

try {conn = getConnection("jdbc:mysql://localhost/empDB",

"root", "root2");// do something useful with the Connection object

}catch (ClassNotFoundException ce) {

// there was problem in loading a driver// handle the exception

}catch (SQLException e) {

// handle any exceptionsSystem.out.println("SQLException: " + ex.getMessage());System.out.println("SQLState: " + ex.getSQLState());System.out.println("VendorError: " + ex.getErrorCode());

}}

}

Method 3: Passing "user" and "password" Parameters As a java.util.Properties ObjectThe following code shows the third method:

import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.util.Properties;

class TestGetMySQLConnection_3public static Connection getConnection(String dbURL,

String user,String password)

throws SQLException, ClassNotFoundException {//load a driverClass.forName("com.mysql.jdbc.Driver");// create a Properties object and put the "user" and "password"// information; you may add additional properties to the driver// by invoking props.put(key, value) method.Properties props = new Properties();props.put("user", user);props.put("password", password);// add additional connection properties// Should the driver try to reestablish bad connections?// the default value is false; here we set it to true.// not that these are driver-specific keys.props.put("autoReconnect", "true");

return DriverManager.getConnection(dbURL, props);}

public static void main(String[] args) {Connection conn = null;try {

conn = getConnection("jdbc:mysql://localhost/empDB","root", "root2");

// do something useful with the Connection object}

5203ch02.qxd 8/11/05 4:18 PM Page 68

Page 98: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 69

catch (ClassNotFoundException ce) {// there was problem in loading a driver// handle the exception

}catch (SQLException e) {

// handle any exceptionsSystem.out.println("SQLException: " + ex.getMessage());System.out.println("SQLState: " + ex.getSQLState());System.out.println("VendorError: " + ex.getErrorCode());

}}

}

2-16. What Are the Key JDBC Concepts for an Oracle Database?The following code illustrates JDBC concepts for an Oracle database. I will show you some sampleoutput and discuss it line by line after presenting the full code here:

1 import java.sql.*;2 import jcb.util.DatabaseUtil;34 public class SimpleProgramToAccessOracleDatabase {5 public static Connection getConnection()6 throws SQLException {7 String driver = "oracle.jdbc.driver.OracleDriver";8 // load the Oracle JDBC Driver9 Class.forName(driver);10 // define database connection parameters11 String dbURL = "jdbc:oracle:thin:@localhost:1521:scorpian";12 String dbUser = "octopus";13 String dbPassword = "octopus";14 // get a database Connection object: A connection (session)15 // with a specific database. SQL statements are executed, and16 // results are returned within the context of a connection.17 return DriverManager.getConnection(dbURL, dbUser, dbPassword);18 }1920 public static void main(String[] args)21 throws SQLException {22 Connection conn = null ; // Connection object23 Statement stmt = null; // statement object24 ResultSet rs = null; // result set object25 try {26 conn = getConnection(); // without Connection, can not do much27 // create a statement: This object will be used for executing28 // a static SQL statement and returning the results it produces.29 stmt = conn.createStatement();3031 // start a transaction32 conn.setAutoCommit(false);3334 // create a table called cats_tricks35 stmt.executeUpdate("CREATE TABLE cats_tricks " +36 "(name VARCHAR2(30), trick VARCHAR2(30))");37 // insert two new records to the cats_tricks table38 stmt.executeUpdate(

5203ch02.qxd 8/11/05 4:18 PM Page 69

Page 99: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES70

39 "INSERT INTO cats_tricks VALUES('mono', 'rollover')" );40 stmt.executeUpdate(41 "INSERT INTO cats_tricks VALUES('mono', 'jump')" );4243 // commit the transaction44 conn.commit();4546 // set autocommit to true (from now on every single47 // statement will be treated as a single transaction48 conn.setAutoCommit(true) ;4950 // get all the records from the cats_tricks table51 rs = stmt.executeQuery(52 "SELECT name, trick FROM cats_tricks");5354 // iterate the result set, and get one row at a time55 while( rs.next() ) {56 String name = rs.getString(1); // 1st column in query57 String trick = rs.getString(2); // 2nd column in query58 System.out.println("name="+name);59 System.out.println("trick="+trick);60 System.out.println("==========");61 }62 }63 catch(ClassNotFoundException ce){64 // if the driver class not found, then we will be here65 System.out.println(ce.getmessage());66 }67 catch(SQLException e){68 // something went wrong, we are handling the exception here69 if ( conn != null ){70 conn.rollback();71 conn.setAutoCommit(true);72 }7374 System.out.println("--- SQLException caught ---");75 // iterate and get all of the errors as much as possible.76 while ( e != null ){77 System.out.println("Message : " + e.getMessage());78 System.out.println("SQLState : " + e.getSQLState());79 System.out.println("ErrorCode : " + e.getErrorCode());80 System.out.println("---");81 e = e.getNextException();82 }83 }84 finally { // close db resources85 DatabaseUtil.close(rs);86 DatabaseUtil.close(stmt);87 DatabaseUtil.close(conn);88 }89 }90 }

Running the Simple ProgramThis code shows how to run the program:

5203ch02.qxd 8/11/05 4:18 PM Page 70

Page 100: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 71

$ javac SimpleProgramToAccessOracleDatabase.java$ java SimpleProgramToAccessOracleDatabasename=monotrick=rollover==========name=monotrick=jump==========

$ sqlplus octopus/octopusSQL*Plus: Release 9.2.0.1.0 - Production on Sun Feb 22 23:23:10 2004SQL> desc cats_tricks;Name Null? Type------------------ -------- --------------NAME VARCHAR2(30)TRICK VARCHAR2(30)

SQL> select * from cats_tricks;

NAME TRICK---------- ----------mono rollovermono jump

SQL>

Discussing the Simple Program to Illustrate JDBC Concepts for an Oracle DatabaseThe following list breaks down and explains the program line by line:

• Lines 1–2: Import the required classes and interfaces from the java.sql package.

• Lines 5–18: Define the Oracle Connection object (use the driver manager to create a Connectionobject).

• Lines 29–29: Using the Connection object (called conn), create a generic Statement object, whichcan execute SQL statements and queries.

• Lines 32–32: Start a transaction by setting the autocommit mode to false.

• Lines 35–40: Issue a set of SQL statements (none of these will be committed until you executeconn.commit(), which will commit all the SQL statements to your desired database).

• Lines 44–44: Commit the transaction by exceuting conn.commit(). Either all the SQL statementswill be executed successfully or none of them will execute (if there is a problem).

• Lines 48–48: Set the autocommit mode to true (from now on every single SQL statement willbe treated as a single transaction).

• Lines 51–51: Get all the records from the cats_tricks table (by using the Statement.executeQuery() method, which returns a ResultSet object and holds the result of the SQL query).

• Lines 55–61: Iterate the result set (the ResultSet object called rs), and get one row at a time.

• Lines 67–83: Deal with exception handling. If the driver class not found, then you will be insidelines 69–70; if there is a database exception (identified by SQLException), then the programlogic will take you to lines 73–87.

• Lines 84–88: Close the database resources.

5203ch02.qxd 8/11/05 4:18 PM Page 71

Page 101: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES72

2-17. What Are the Key JDBC Concepts for a MySQL Database?The solution to this problem is the same as the previous solution—you will merely replace thegetConnection() method with the following (which is specific to the MySQL database):

public static Connection getConnection()throws SQLException {String driver = " com.mysql.jdbc.Driver ";// load the MySQL JDBC driverClass.forName(driver);// define database connection parametersString dbURL = " jdbc:mysql://localhost/tiger ";String dbUser = "root";String dbPassword = "root";// get a database Connection object: A connection (session)// with a specific database. SQL statements are executed, and// results are returned within the context of a connection.return DriverManager.getConnection(dbURL, dbUser, dbPassword);

}

To run this simple program, use this code:

$ javac SimpleProgramToAccessMySQLDatabase.java$ java SimpleProgramToAccessMySQLDatabasename=monotrick=rollover==========name=monotrick=jump==========

$ mysqlWelcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 4 to server version: 4.0.15-nt

mysql> show databases;+----------+| Database |+----------+| empdb || javatest || mysql || test || tiger |+----------+5 rows in set (0.02 sec)

mysql> use tiger;Database changedmysql> desc cats_tricks;+-------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+-------+| name | varchar(30) | YES | | NULL | || trick | varchar(30) | YES | | NULL | |+-------+-------------+------+-----+---------+-------+2 rows in set (0.00 sec)

5203ch02.qxd 8/11/05 4:18 PM Page 72

Page 102: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 73

mysql> select * from cats_tricks;+------+----------+| name | trick |+------+----------+| mono | rollover || mono | jump |+------+----------+2 rows in set (0.00 sec)

2-18. How Do You Set Your Environment for JDBC?To enable the JDBC APIs, you must set your CLASSPATH environmental variable to indicate to theJava virtual machine and Java-based applications where to find the class libraries, including user-defined class libraries. To view the content of a CLASSPATH on Windows, use the following code:

$ set CLASSPATHCLASSPATH=.;c:\jdk142\lib\dt.jar;c:\jdk142\lib\tools.jar;c:\mp\book\src;c:\j\httpclient.jar

To view the content of a CLASSPATH on Unix/Linux, use the following code:

$ echo $CLASSPATH.:/usr/java/j2sdk1.4.2_03/lib/dt.jar:/usr/java/j2sdk1.4.2_03/lib/tools.jar:/usr/java/j2sdk1.4.2_03/lib/htmlconverter.jar

For example, if you want to add the Oracle driver to your CLASSPATH, then you must do the fol-lowing, before running your JDBC applications. You can find Oracle’s implementation of the java.sqlpackage and other supporting classes in the ojdbc14.jar file.

■Note C:\oracle\product\10.1.0\Db_1\jdbc\lib\ojdbc14.jar is an Oracle JDBC driver for the Oracle 10gdatabase in a Windows environment, and /export/oracle/product/10.1.0/Db_1/jdbc/lib/ojdbc14.jar isan Oracle JDBC driver for the Oracle 10g database in a Linux environment.

Do the following for Windows:

$ set CLASSPATH=%CLASSPATH%;c:\oracle\product\10.1.0\Db_1\jdbc\lib\ojdbc14.jar$ set CLASSPATHCLASSPATH=.;c:\jdk142\lib\dt.jar;c:\jdk142\lib\tools.jar;c:\mp\book\src;c:\j\httpclient.jar;c:\oracle\product\10.1.0\Db_1\jdbc\lib\ojdbc14.jar

Do the following for Unix/Linux:

$ export CLASSPATH=$CLASSPATH:\/export/oracle/product/10.1.0/Db_1/jdbc/lib/ojdbc14.jar$ echo $CLASSPATH.:/usr/java/j2sdk1.4.2_03/lib/dt.jar:/usr/java/j2sdk1.4.2_03/lib/tools.jar:/usr/java/j2sdk1.4.2_03/lib/htmlconverter.jar:/export/oracle/product/10.1.0/Db_1/jdbc/lib/ojdbc14.jar

Each time you want to use JDBC, you will have to set your CLASSPATH properly. You can placethese settings in script/batch files (.bat in a Windows environment and .sh files a Unix/Linux envi-ronment). To avoid typing these settings (when you need the CLASSPATH), you may invoke the scripts.

5203ch02.qxd 8/11/05 4:18 PM Page 73

Page 103: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES74

2-19. What Are Some JDBC Resources?Plenty of JDBC resources are available; I’ll mention some of the books, tools, and Web sites devotedto JDBC.

JDBC BooksThese are useful and practical books about JDBC topics:

• JDBC API Tutorial and Reference, Third Edition by Maydene Fisher, Jon Ellis, and JonathanBruce (Addison Wesley, 2003)

• Database Programming with JDBC and Java, Second Edition by George Reese (O’Reilly, 2000)

• Java Persistence for Relational Databases by Richard Sperko (Apress, 2003)

• Java Programming with Oracle JDBC by Donald Bales (O’Reilly, 2001)

• Oracle 9i Java Programming: Solutions for Developers Using PL/SQL and Java by Bjarki Holmet al. (Wrox, 2001)

• MySQL Cookbook by Paul DuBois (O’Reilly, 2002)

JDBC Web SitesFor links to information about JDBC technology, see the JDBC technology home page at http://java.sun.com/products/jdbc/index.jsp. The following are additional helpful links:

• The features and benefits of JDBC: http://java.sun.com/products/jdbc/

• Oracle database: http://www.oracle.com

• MySQL database: http://www.mysql.com

• JDBC documentation: http://dev.mysql.com/doc/connector/j/en/index.html

• Aids for learning to use the JDBC API: http://java.sun.com/products/jdbc/learning.html

• JDBC tutorial: http://java.sun.com/docs/books/tutorial/jdbc/

• JDBC API interface in a nutshell: http://www.cs.unc.edu/Courses/wwwp-s98/members/thornett/jdbc/183.html

• Tutorial on using JDBC under Windows: http://www.npac.syr.edu/users/gcf/uccjdbcaccess97/

• JDBC, explained: http://www-106.ibm.com/developerworks/db2/library/techarticle/norton/0102_norton.html

• Online courses about database access:http://java.sun.com/developer/onlineTraining/Database

• Free JDBC books: http://www.javaolympus.com/freebooks/FreeJDBCBooks.jsp

• JDBC Frequently Asked Questions (FAQs):

• http://java.sun.com/products/jdbc/faq.html#1

• http://www.jguru.com/faq/JDBC

• http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.htm

• http://www.fankhausers.com/postgresql/jdbc/

• http://www.white-mountain.org/jdbc/FAQ.html

• http://e-docs.bea.com/wls/docs81/faq/jdbc.html

5203ch02.qxd 8/11/05 4:18 PM Page 74

Page 104: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 75

2-20. How Do You Debug Problems Related to the JDBC API?You have several ways to debug your JDBC programs. One simple way is to use lots of System.out.println() statements so you can see what is retrieved and printed. Another good way is to trace yourJDBC calls (that is, enable JDBC tracing). The JDBC trace contains a detailed listing of the activityoccurring in the system that is related to JDBC method calls.

You can enable tracing of JDBC operations by using DriverManager. You can use the DriverManager.setLogWriter() method to enable tracing of JDBC operations. The DriverManager.setLogWriter()method sets the logging/tracing PrintWriter object that is used by DriverManager and all drivers. Ifyou use a DataSource object to get a connection, you use the DataSource.setLogWriter() method toenable tracing. And for connections that can participate in distributed transactions, you can use theXADataSource.setLogWriter() method.

■Note The key to tracing JDBC operations is to pass a java.io.PrintWriter object to the DriverManager.setLogWriter() method.

Creating a Simple Program to Illustrate Tracing of JDBC OperationsThe following code shows a simple program to illustrate the tracing of JDBC operations:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.*;import jcb.meta.*;

// simple program to illustrate the tracing of JDBC operationspublic class TestDebug_MySQL {

public static Connection getConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

public static int countRows(Connection conn, String tableName)throws SQLException {// select the number of rows in the tableStatement stmt = null;ResultSet rs = null;int rowCount = -1;try {

stmt = conn.createStatement();rs = stmt.executeQuery("SELECT COUNT(*) FROM "+ tableName);// get the number of rows from the result setrs.next();rowCount = rs.getInt(1);

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);

}

5203ch02.qxd 8/11/05 4:18 PM Page 75

Page 105: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES76

return rowCount;}

public static void main(String[] args) {Connection conn = null;try {

System.out.println("------TestDebug_MySQL begin---------");PrintWriter pw =

new PrintWriter(new FileOutputStream("mysql_debug.txt"));DriverManager.setLogWriter(pw);conn = getConnection();String tableName = args[0];System.out.println("tableName="+tableName);System.out.println("conn="+conn);System.out.println("rowCount="+countRows(conn, tableName));System.out.println("------TestDebug_MySQL end---------");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(conn);

}}

}

Running the Simple Tracing ProgramThis code shows how to run the simple program:

$ javac TestDebug_MySQL.java$ java TestDebug_MySQL dsns------TestDebug_MySQL begin---------tableName=dsnsconn=com.mysql.jdbc.Connection@1546e25rowCount=30------TestDebug_MySQL end---------

$ cat mysql_debug.txtDriverManager.initialize: jdbc.drivers = nullJDBC DriverManager initializedregisterDriver: driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@b8df17]DriverManager.getConnection("jdbc:mysql://localhost/octopus")

trying driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@b8df17]

getConnection returning driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@b8df17]

2-21. Does MySQL Support Transactions?The short answer is “yes” and “no” (depending on the table type at the time of table creation). TheMySQL database system supports several storage engines that act as handlers for different tabletypes. MySQL storage engines include both those that handle transaction-safe tables and those thathandle non-transaction-safe tables.

5203ch02.qxd 8/11/05 4:18 PM Page 76

Page 106: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 77

MySQL has the following table types (note that, among these table types, only InnoDB andBDB support transactions):

MyISAM: This is the default table type and does not support transactions.

ISAM: According to the MySQL manual, “the original storage engine was ISAM, which managednontransactional tables. This engine has been replaced by MyISAM and should no longer beused. It is deprecated in MySQL 4.1 and will be removed in MySQL 5.0.”

InnoDB: The InnoDB storage engine that handles transaction-safe tables was introduced inlater versions of MySQL 3.23. It is available in source distributions as of MySQL 3.23.34a. InnoDBalso is included in MySQL-Max binary distributions for MySQL 3.23. Beginning with MySQL 4.0,InnoDB is included by default in all MySQL binary distributions. In source distributions, youcan enable or disable either engine by configuring MySQL as you like. See http://www.innodb.comfor more information.

BDB: The BDB storage engine that handles transaction-safe tables was introduced in later versionsof MySQL 3.23. It is available in source distributions as of MySQL 3.23.34a. BDB is included inMySQL-Max binary distributions on those operating systems that support it. See http://www.sleepycat.com for more information.

Heap: According to the MySQL manual, “the MEMORY (heap) storage engine creates tables withcontents that are stored in memory. Before MySQL 4.1, MEMORY tables were called HEAP tables.As of 4.1, HEAP is a synonym for MEMORY, and MEMORY is the preferred term. Each MEMORY table isassociated with one disk file. The filename begins with the table name and has an extension of.frm to indicate that it stores the table definition.” To specify explicitly that you want a MEMORYtable, indicate that with an ENGINE or TYPE table option:

CREATE TABLE t (i INT) ENGINE = MEMORY;CREATE TABLE t (i INT) TYPE = HEAP;

MEMORY tables are stored in memory and use hash indexes. This makes them fast and useful forcreating temporary tables! However, when the server shuts down, all data stored in MEMORY tables islost.

According to the MySQL online manual, “when you create a new table, you can tell MySQL whattype of table to create by adding an ENGINE or TYPE table option to the CREATE TABLE statement.” Forexample:

CREATE TABLE t (i INT) ENGINE = INNODB;CREATE TABLE t (i INT) TYPE = MEMORY;

ENGINE is the preferred term but cannot be used before MySQL 4.0.18. TYPE is available beginningwith MySQL 3.23.0, the first version of MySQL for which multiple storage engines were available. Ifyou omit the ENGINE or TYPE option, the default table type is usually MyISAM. You can change this bysetting the table_type system variable. To convert a table from one type to another, use an ALTER TABLEstatement that indicates the new type:

ALTER TABLE t ENGINE = MYISAM;ALTER TABLE t TYPE = BDB;

MySQL does not support distributed transactions. The latest version (mysql-connector-java-3.1.4-beta) of MySQL Connector/J (the JDBC driver) does not implement the javax.sql.XAConnectioninterface. (It is an object that provides support for distributed transactions.)

2-22. Does Oracle Support Transactions?The answer is “yes.” Oracle supports transactions and distributed transactions (a single transactionthat can apply to multiple heterogeneous databases that may reside on separate servers).

5203ch02.qxd 8/11/05 4:18 PM Page 77

Page 107: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES78

Using Oracle, by default, DML operations (INSERT, UPDATE, DELETE) are committed automaticallyas soon as they are executed. This is known as autocommit mode. You can, however, disable auto-commit mode with the following method call on the Connection object:

java.sql.Connection conn = ...;conn.setAutoCommit(false);

If you disable autocommit mode, then you must manually commit changes with the appropriatemethod call on the Connection object, like so:

conn.commit();

or roll them back, like so:

conn.rollback();

Oracle’s Support for a Distributed TransactionAccording to Oracle, a distributed transaction, sometimes referred to as a global transaction, is a setof two or more related transactions that must be managed in a coordinated way.

The transactions that constitute a distributed transaction might be in the same database butmore typically are in different databases and often in different locations. Each transaction of a dis-tributed transaction is referred to as a transaction branch.

Oracle provides a JDBC implementation of distributed transactions. In Oracle, distributedtransactions are supported through the standard javax.sql package.

Distributed transactions are multiphased transactions, often using multiple databases, that mustbe committed in a coordinated way. Oracle provides distributed transactions by implementing javax.sql.XADataSource and javax.sql.XAConnection and its own proprietary implementation. XAConnectionis a database connection that can be used in a distributed transaction, and XADataSource is a datasource that can be used in a distributed transaction.

Oracle supplies the following three packages that have classes to implement distributed trans-action functionality according to the XA standard:

• oracle.jdbc.xa (the OracleXid and OracleXAException classes)

• oracle.jdbc.xa.client

• oracle.jdbc.xa.server

For more details on these APIs, please see Oracle’s JDBC documentation. Distributed transactionsrequire the JDBC 2.0 Optional Package (JDBC 2.0 Standard Extension API) in the javax.sql package.This is available under either JDK 1.2.x or 1.1.x.

Oracle Distributed Transactions Code SampleCode sample is provided in Oracle 9i JDBC Developer’s Guide and Reference (http://ocpdba.net/9idoc/java.920/a96654.pdf). The example uses a two-phase distributed transaction with two trans-action branches, each to a separate database (both databases are Oracle 9i).

2-23. What Do the Different Versions of JDBC Offer?Sun Microsystems’ Java group (http://java.sun.com) prepares and maintains the JDBC specification(http://java.sun.com/products/jdbc). Since JDBC is just a specification (suggestions for writing andusing JDBC drivers), third-party vendors (such as Oracle, MySQL, IBM, and so on) develop JDBCdrivers adhering to this specification. JDBC developers then use these drivers to access data sources.To be JDBC-compliant, the driver has to pass a suite of tests developed by Sun Microsystems.

5203ch02.qxd 8/11/05 4:18 PM Page 78

Page 108: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 79

JDBC has gone through several major releases; for more details, please refer to http://java.sun.com/products/jdbc/index.jsp:

JDBC 1.0: The first version provides basic functionality; it focuses on ease of use.

JDBC 2.0: The second version offers more advanced features and server-side capabilities. Fordetails on JDBC 2.0, refer to http://java.sun.com/developer/onlineTraining/Database/JDBC20Intro/.

JDBC 3.0: This version provides performance optimizations. It adds features in the areas ofconnection pooling and statement pooling, and it provides a migration path to Sun Microsystems’connector architecture. The JDBC 3.0 API is the latest update of the JDBC API. It contains manyfeatures, including scrollable result sets and the SQL:1999 data types. The JDBC 3.0 API is com-prised of two packages: the java.sql package (standard package, foundation API) and thejavax.sql package (which adds server-side capabilities). You automatically get both packageswhen you download J2SE 1.4.2 or J2SE 5.0. You can get the JDBC 3.0 specification from the fol-lowing site: http://java.sun.com/products/jdbc/download.html.

JDBC 4.0: Refer to http://www.jcp.org/en/jsr/detail?id=221.

2-24. What Is the Core Functionality of a JDBC Driver?As mentioned in Chapter 1, a JDBC driver is a Java class that implements the JDBC driver interface(java.sql.Driver) and is loaded into the JDBC driver manager. The java.sql.Driver interface is onethat every driver class must implement. The Oracle and MySQL products come with JDBC drivers,and the JDBC driver enables a Java application to communicate with a relational/SQL database.

In summary, a JDBC driver makes it possible to perform the following steps:

1. Create a database Connection object (java.sql.Connection object).

2. Send SQL queries to the database using the Connection object.

3. Process the results (as java.sql.ResultSet) returned from the database.

4. Close the ResultSet and other objects.

5. Close the Connection object.

The following test program demonstrates the steps outlined previously. I offer a MySQL and anOracle version to suit your environment.

Creating a Core JDBC Functionality Test Program (MySQL Version): SmallTestMySQL.javaThe following program creates a table (called employees_table). If the table creation is successful,then it inserts two new records into that table; finally, it reads all the existing records (the two insertedrecords) using a ResultSet object via the Statement.executeQuery() method.

import java.sql.*;import jcb.util.DatabaseUtil;

public class SmallTestMySQL {

private static final String EMPLOYEE_TABLE ="create table employees_table ( " +" id INT PRIMARY KEY, " +" name VARCHAR(20))";

5203ch02.qxd 8/11/05 4:18 PM Page 79

Page 109: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES80

public static Connection getConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

public static void main(String args[]) throws Exception {Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a database connectionconn = getConnection();// create tablestmt = conn.createStatement();stmt.executeUpdate(EMPLOYEE_TABLE);System.out.println("SampleTestMySQL: main(): table created.");// insert couple of recordsstmt.executeUpdate("insert into employees_table(id, name)"+

values('100', 'alex')");stmt.executeUpdate("insert into employees_table(id, name)"+

values('200', 'mary')");

// get all employee records from the databasers = stmt.executeQuery("select id, name from employees_table");while (rs.next()) {

int id = rs.getInt(1);String name = rs.getString(2);System.out.println("id = "+id+"\t name = "+ name);

}}catch( Exception e ) {

// handle the exceptione.printStackTrace();

}finally {

// close database resource not neededDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Testing the SampleTestMySQL ProgramUse this code to test the program:

$ javac SmallTestMySQL.java$ java SmallTestMySQLSampleTestMySQL: main(): table created.id = 100 name = alexid = 200 name = mary

5203ch02.qxd 8/11/05 4:18 PM Page 80

Page 110: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 81

Viewing the Output of MySQL DatabaseThis is the output from SmallTestMySQL.java:

C:\mysql\bin>mysql --user=root --password=rootWelcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 2 to server version: 4.0.18-nt

mysql> use octopus;Database changedmysql> desc employees_table;+-------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || name | varchar(20) | YES | | NULL | |+-------+-------------+------+-----+---------+-------+2 rows in set (0.00 sec)

mysql> select * from employees_table;+-----+------+| id | name |+-----+------+| 100 | alex || 200 | mary |+-----+------+2 rows in set (0.00 sec)

Creating a Core JDBC Functionality Test Program (Oracle Version): SmallTestOracle.javaThe SmallTestOracle.java program is identical to the previous SmallTestMySQL.java program,except for the getConnection() method:

import java.sql.*;import jcb.util.DatabaseUtil;

public class SmallTestOracle {

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "scott";String password = "tiger";Class.forName(driver); // load Oracle driverReturn DriverManager.getConnection(url, username, password);

}

public static void main(String args[]) throws Exception {// same as the main() method of SmallTestMySQL class.

}}

Testing the SampleTestOracle ProgramUse this code to test the program:

$ javac SmallTestOracle.java

5203ch02.qxd 8/11/05 4:18 PM Page 81

Page 111: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES82

SmallTestOracle: main(): table created.id = 100 name = alexid = 200 name = mary

Viewing the Output of Oracle DatabaseThis is the output from SmallTestOracle.java:

$ sqlplus scott/tigerSQL> desc employees_table;Name Null? Type------------------------------------ -------- -------------ID NOT NULL NUMBER(38)NAME VARCHAR2(20)

SQL> select * from employees_table;

ID NAME---------- --------------------

100 alex200 mary

2-25. Where Can You Find Information and Pointers for Writinga JDBC Driver?Before writing any JDBC driver, you should review driver writers documentation from SunMicrosystems (http://java.sun.com/products/jdbc/driverdevs.html). This document specifiesthe minimum that a JDBC driver must implement to be compliant with the JDBC API. Several driv-ers are available:

• SimpleText database: http://www.thoughtinc.com/simpletext.html

• MySQL Connector/J: http://dev.mysql.com/downloads/connector/j/3.0.html

• FreeTDS: http://www.freetds.org

• RmiJdbc: http://rmijdbc.objectweb.org/index.html

• jxDBCon open-source JDBC driver framework: http://jxdbcon.sourceforge.net

2-26. What Is the JDBC Driver Certification Program?Sun Microsystems offers a JDBC Driver Certification Program, which helps end users find out ifa specific JDBC driver has passed a suite of well-defined tests (to determine whether it satisfiesJDBC driver requirements). For more details on this program, refer to http://java.sun.com/products/jdbc/certification.html.

The intent of JDBC driver certification program is to give a tool to organizations to make theright selection of JDBC drivers:

Today driver vendors offer a number of drivers based on JDBC technology (“JDBC drivers”)that support various databases. However, not all JDBC drivers implement all the functional-ity required for working with J2EE-compatible products. The new JDBC certificationprogram will help J2EE-compatible product vendors and applications developers pick theappropriate JDBC drivers for their needs with the confidence that the driver has been certi-fied to work with J2EE compatible products.

5203ch02.qxd 8/11/05 4:18 PM Page 82

Page 112: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 83

Any database vendor (that has JDBC drivers) can add entries to this database. This databasecontains all JDBC driver entries sent to Sun Microsystems by JDBC driver vendors requesting to belisted in this database. Using the Web interface to this database, you can select a list of JDBC driversbased on your particular needs (for example, database support and features required). Also, you cancheck the Certified for J2EE check box to list only those drivers that have been certified for use withJ2EE-compatible products.

2-27. What Is a Two-Tier Model for JDBC?Typically, a two-tier model refers to a client-server model. A two-tier model for JDBC refers to client-server architectures in which the user interface (Java/JDBC application) runs on the client machineand the database management system (that is, a database server) is stored on the server machine.Figure 2-2 represents a two-tier model for JDBC.

Figure 2-1. JDBC driver database

Sun Microsystems maintains a searchable database of JDBC drivers at http://industry.java.sun.com/products/jdbc/drivers. Figure 2-1 shows this database’s Web interface.

5203ch02.qxd 8/11/05 4:18 PM Page 83

Page 113: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES84

In a two-tier model for JDBC, the actual application logic can run on either the client or the server.In two-tier architectures, the application code (Java/JDBC application) resides on the fat client, whichis used to process data.

The major problem with two-tier database applications is that they become complex and hardto support as the user base increases in size. For example, your database application might run finefor 20 concurrent users, but it might be problematic for 100 concurrent users. By using proper design/programming and configuration techniques, you can avoid these problems.

With a two-tier model, the database connections are expensive (it takes a long time to establisha Connection object); therefore, it is recommended you use a connection pool manager for handlingJDBC connections.

2-28. What Is a Three-Tier Model for JDBC?Typically, a three-tier model is an extension of a two-tier (so-called client-server) model. Instead ofhaving a fat client, you move most of the business logic to the middle tier (application server level).J2EE is an example of a three-tier model. A three-tier model for JDBC has three tiers:

Client tier: The client tier is responsible for presenting data, receiving user inputs and events,and controlling and managing the user interface.

Middle tier (so-called application server tier): This tier is responsible for implementing the busi-ness rules (which create business objects) that are available to the client tier. IBM WebSphere(http://www.ibm.com/), BEA WebLogic Server (http://www.bea.com/), and Oracle ApplicationServer (http://www.oracle.com/) are examples of middle tier and Web servers.

Data-server tier: This tier is responsible for data storage and manages the persistence of appli-cation data. It is usually composed of one/more relational database servers (such as Oracle orMySQL) and may contain the following:

• Database tables/views/triggers (used primarily for storing data)

• Stored procedures (used to execute database on the server-side)

• File servers (for storing huge files such as images, PDF files, and text)

Figure 2-3 presents a three-tier model for JDBC.

Figure 2-2. Two-tier model for JDBC

5203ch02.qxd 8/11/05 4:18 PM Page 84

Page 114: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES 85

In a three-tier model for JDBC, the actual application logic runs on the middle tier. In general,tree-tier models are more scalable than two-tier models, because it is easy to add middle-tier anddata-server tiers easily. For some JDBC applications, three-tier architecture can be slow because ofan additional level of communication. But you can use connection pool management and cachingservices to improve the overall performance. Also, you can optimize your SQL queries for betterperformance.

Typical Internet Application ArchitectureFigure 2-4 represents a typical three-tier model. In this model, a client application (for example,a browser) communicates with a Java servlet/JSP in a Web server (such as Tomcat from the ApacheSoftware Foundation) using Hypertext Transfer Protocol (HTTP) or HTTP Over SSL (HTTPS). (Tomcatis the servlet container that is used in the official reference implementation for the Java servletand JSP technologies. For details, see the official Web site at http://jakarta.apache.org/tomcat/index.html.)

The Java code (servlet/JSP) in the Web server uses the JDBC API to access the relational data-base server.

Figure 2-3. Three-tier model for JDBC

5203ch02.qxd 8/11/05 4:18 PM Page 85

Page 115: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 2 ■ EXPLORING JDBC’S NOVEL FEATURES86

The Advantages of a Three-Tier Architecture for JDBCYou get a clear separation of the user interface control and data presentation from the applicationlogic in a three-tier model. This means these components can be developed independently (as longas the interfaces among tiers are well defined).

Changes in the data-server tier (such as adding new tables and adding/modifying columns toexisting tables) will not impact the clients because clients do not access the data-server tier directly.

Dynamic load balancing is another advantage of a three-tier architecture for JDBC. You cantackle performance problems easier than two-tier models: if a performance problem happens, youcan add additional middle-tier servers or move the server process to other servers at runtime.

Figure 2-4. Typical JDBC Internet architecture

5203ch02.qxd 8/11/05 4:18 PM Page 86

Page 116: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Making Database Connections

Relativity teaches us the connection between the different descriptions of one and thesame reality.

—Albert Einstein

In this chapter, you will examine all aspects of database connections using JDBC. You will then learnabout the ins and outs of the java.sql.Connection object (which denotes a connection or a sessionwith a specific database); this is one of the most important interfaces defined in JDBC. You can createa java.sql.Connection object in a couple of ways, and you will look at all the specific solutions in thischapter.

The purpose of this chapter is to provide snippets and reusable code samples and methodsthat deal with the Connection interface defined in the java.sql package. For writing this chapter,I relied on JDK 1.4 and the final release of the JDBC 3.0 specification.

This chapter’s focus will be on answering the following question: how do you obtain a java.sql.Connection object from the java.sql and javax.sql packages? I can present this question in anotherway: what are the connection options? This chapter will answer these questions in detail and provideyou with working code.

To select data from tables/views, update columns in tables, create a new table in the database,or do anything useful with any database, you need a database connection. (In JDBC, this is calledjava.sql.Connection.) Typically, database access in any environment starts with the connection.

To illustrate the importance of the java.sql.Connection object, I will list the basic steps forusing JDBC:

1. Load the JDBC driver.

2. Define the connection URL.

3. Establish the JDBC connection (that is, create a java.sql.Connection object).

4. Create the JDBC Statement object.

5. Execute a query or update.

6. Process the results.

7. Close the connection.

8. Commit (or roll back) as appropriate.

Without the Connection object, you cannot accomplish many database operations. Steps 4–8depend on the Connection object created in step 3. Most of the important JDBC API depends onthe Connection object. Java.sql.Connection is a factory for the Statement, PreparedStatement, andCallableStatement objects.

87

C H A P T E R 3

■ ■ ■

5203ch03.qxd 8/11/05 4:22 PM Page 87

Page 117: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS88

Figure 3-1. Relationship of Connection to other objects

3-1. What Is a Connection Object?The java.sql.Connection object represents a single logical database connection. You use the Connectionobject for sending a set of SQL statements to the database server and managing the committing oraborting (rollback) of those SQL statements. Without the Connection object, you cannot do much.

The Connection object has the following capabilities:

• Creates SQL statements

• Executes SQL queries, inserts, updates, and deletes

• Handles commits and rollbacks

• Provides metadata regarding the database connection

It is important to note that a Connection object is thread-safe and can be shared between threadswithout the need for additional synchronization. On the other hand, a Statement object (created froma Connection object) is not thread-safe, and unexpected results can occur if multiple threads accessthe same Statement object. Therefore, multiple threads can share the same Connection object but shouldeach create their own Statement objects.

According to JDK 1.4, a java.sql.Connection object is a connection (session) with a specificdatabase. SQL statements are executed and results are returned within the context of a connection.A Connection object’s database is able to provide information describing its tables, its supported SQLgrammar, its stored procedures, the capabilities of this connection, and so on. This information isobtained with the getMetaData() method.

■Note By default, a Connection object is in autocommit mode, which means it automatically commits changesafter executing each statement. If autocommit mode has been disabled, the method commit must be called explic-itly in order to commit changes; otherwise, database changes will not be saved.

3-2. What Is the Relationship of Connection to Other Objects?To illustrate the importance of the Connection object, Figure 3-1 shows how Connection objects arecreated.

5203ch03.qxd 8/11/05 4:22 PM Page 88

Page 118: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 89

Also, to illustrate the importance of the Connection object in the java.sql package, Figure 3-2shows the interactions and relationships between the key classes and interfaces in the java.sqlpackage. It also shows the methods involved in creating statements, setting parameters, and retriev-ing results; for details, please see the final release of the JDBC 3.0 specification, published by SunMicrosystems.

Figure 3-2. Interactions and relationships between the key JDBC classes and interfaces

3-3. What Are the Connection Creation Options?From the JDBC API (the java.sql and javax.sql packages), you can conclude that there are onlyfive ways to create JDBC Connection objects:

• java.sql.DriverManager: This class is the basic service for managing a set of JDBC drivers.

• java.sql.Driver: This is the interface that every driver class must implement. Each drivershould supply a class that implements the Driver interface.

• javax.sql.DataSource: This interface is new in the JDBC 2.0 API, and according to JDK 1.4,using a DataSource object is the preferred means of connecting to a data source; you can cre-ate more portable code by using DataSource and JNDI.

5203ch03.qxd 8/11/05 4:22 PM Page 89

vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
Page 119: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS90

• javax.sql.PooledConnection: This object provides hooks for connection pool management.A PooledConnection object represents a physical connection to a data source.

• Connection cache: This is an optional extension but not standard.

3-4. What Is the Function of the DriverManager Class?In a nutshell, the DriverManager class manages connections to databases. According to JDK 1.4, theDriverManager provides a basic service for managing a set of JDBC drivers. As part of its initialization,the DriverManager class will attempt to load the driver classes referenced in the jdbc.drivers systemproperty. This allows a user to customize the JDBC drivers used by their applications.

In your ~/.hotjava/properties file, you might, for example, specify the following:

jdbc.drivers=org.gjt.mm.mysql.Driver:oracle.jdbc.driver.OracleDriver

Note that drivers are separated by a colon here. A program can also explicitly load JDBC driversat any time. For example, the following example loads MySQL and Oracle drivers:

Class.forName("org.gjt.mm.mysql.Driver");Class.forName("jdbc.driver.OracleDriver");

When getConnection() is called, the DriverManager will attempt to locate a suitable driver fromamong those loaded at initialization and those loaded explicitly using the same class loader as thecurrent applet or application. Figure 3-3 illustrates the function of the DriverManager class.

Figure 3-3. Function of the DriverManager class

The DriverManager class manages the JDBC drivers in the system. It maintains a registry of driversand locates the appropriate driver to handle a JDBC database URL.

On startup, DriverManager loads all the managers specified by the system propertyjdbc.drivers. The value of this property should be a colon-separated list of fully qualified driverclass names. You can load additional drivers at any time by simply loading the driver class withClass.forName(String driverClassname). The driver should automatically register itself in a staticinitializer.

5203ch03.qxd 8/11/05 4:22 PM Page 90

vikrant
Highlight
vikrant
Highlight
Page 120: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 91

3-5. How Do You Create Connection(s) Using the DriverManagerClass?The DriverManager class (defined in the java.sql package) provides services for managing a set ofJDBC drivers. In a nutshell, because JDBC can work with many drivers (the Oracle driver, MySQLdriver, Sybase driver, and so on), the DriverManager class loads and configures your database driver onyour client. As part of its initialization, the DriverManager class will attempt to load the driver classesreferenced in the jdbc.drivers system property.

How It WorksThe DriverManager class provides three static (class-level) methods for getting connections, asexplained in Table 3-1.

Table 3-1. Getting the Connection Object

Method Summary

static Connection getConnection Attempts to establish a connection to the given (String url) database URL.

static Connection getConnection Attempts to establish a connection to the given (String url, Properties info) database URL; the info parameter is a list of

arbitrary string key-value pairs as connectionarguments. Normally here at least a user andpassword property should be included. Eachvendor has a different set of keys.

static Connection getConnection Attempts to establish a connection to the given (String url, String user, String password) database URL.

You must first establish a connection with the relational DBMS you want to use. This involvestwo steps:

1. Loading the driver

2. Making the connection

SolutionLoading the driver or drivers you want to use is simple. For example, if you want to use the JDBC-ODBCbridge driver, use the following code, which will load the driver:

java.lang.String driverName = "sun.jdbc.odbc.JdbcOdbcDriver";java.lang.Class driverClass = java.lang.Class.forName(driverName);

Alternatively, you can write the following (ignoring the return of the forName() method):

java.lang.String driverName = "sun.jdbc.odbc.JdbcOdbcDriver";java.lang.Class.forName(driverName);

A call to forName("X") causes the class named X to be initialized. Your driver documentationwill give you the class name to use. For instance, if the class name is org.gjt.mm.mysql.Driver (thisdriver is for the MySQL database), you would load the driver with the following line of code:

java.lang.String driverName = "org.gjt.mm.mysql.Driver";java.lang.Class.forName(driverName);

5203ch03.qxd 8/11/05 4:22 PM Page 91

vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
Page 121: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS92

Please note that you do not need to create an instance of a driver and register it with theDriverManager class, because calling Class.forName() will do that for you automatically. (This isa singleton class, and therefore it doesn’t have any public constructors.)

If you were to create your own instance, you would be creating an unnecessary duplicate, but itwould do no harm. When you have loaded a driver, it is available for making a connection with a rela-tional DBMS. For the driver to be successfully loaded, the driver class must be in the CLASSPATHenvironment variable; otherwise, these code fragments will throw an exception.

For example, if you want to use the Oracle 9i (Oracle 8i) database, then you need to include theojdbc.jar file in classes12.zip (provided by Oracle) in the CLASSPATH environment variable. If youwant to access more than one database, then you need to include all the required JAR files in yourCLASSPATH. These JAR files include classes and interfaces that support the implementation of inter-faces and classes in the java.sql and javax.sql packages defined by the JDK.

You can load the driver in another way: just use the following code. When you have loaded a driver,it is available for making a connection with the relational database.

String driverName = "org.gjt.mm.mysql.Driver";Class driverClass = Class.forName(driverName);

DriverManager.registerDriver((Driver) driverClass.newInstance());

How do you make sure your desired drivers are loaded? Use the DriverManager.getDrivers()method, and then enumerate all the loaded JDBC drivers:

// Print out all loaded JDBC drivers.java.util.Enumeration e = java.sql.DriverManager.getDrivers();while (e.hasMoreElements()) {

Object driverAsObject = e.nextElement();System.out.println("JDBC Driver="+driverAsObject);

}

As I mentioned, the second step in establishing your connection is to have the appropriate driverconnect to the DBMS. You have several ways to specify connection information in the DriverManager.getConnection() method. The following sections describe three methods.

Method 1: Making the Connection

The simplest method is to use a database URL that includes the database name, hostname, andport number of the database server, as well as two additional parameters to specify the databaseusername and password. The following is the general idea:

/*** Attempts to establish a connection to the given database* URL. The DriverManager attempts to select an appropriate* driver from the set of registered JDBC drivers.*/String dbURL = ...; // a database URL of the form

// jdbc:subprotocol:subnameString dbUsername = ...; // the database user on whose behalf

// the connection is being madeString dbPassword = ...; // the user's passwordConnection conn = null;try {

conn = DriverManager.getConnection(dbURL,dbUsername,dbPassword);

}catch(SQLException e) {// if a database access error occurs// handle the exception here.

5203ch03.qxd 8/11/05 4:22 PM Page 92

vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
Page 122: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 93

e.printStackTrace();...

}finally {// close the db resources such as Connection objectDatabaseUtil.close(conn);

}

DatabaseUtil is a utility class, which has a set of close() methods for closing database/JDBCresources (such as Connection, Statement, and ResultSet objects). Therefore, to connect to a rela-tional database, you need the parameters described in Table 3-2.

Table 3-2. Connection Object Requirements

Parameter Description

driverName JDBC driver

dbURL Database URL (uniquely identifies a database)

dbUsername Database username

dbPassword Database user password

If you are using the JDBC-ODBC bridge driver, the JDBC URL will start with jdbc:odbc:. Therest of the URL is generally your data source name or database system.

So, if you are using ODBC to access an ODBC data source called Payroll, for example, yourJDBC URL could be jdbc:odbc:Payroll. In place of dbUsername, you put the name you use to log intothe relational DBMS; in place of dbPassword, you put the dbUsername’s password for the DBMS. So, ifyou log into your DBMS with a login name of scott and a password of tiger, just the lines of codeshown will establish a connection:

/** * Attempts to establish a connection to the given database* URL. The DriverManager attempts to select an appropriate* driver from the set of registered JDBC drivers.*/String dbURL = "jdbc:odbc:Payroll";String dbUsername = "scott";String dbPassword = "tiger";Connection conn = null;try {

//// when the method getConnection is called, the// DriverManager will attempt to locate a suitable// driver from amongst those loaded at initialization// and those loaded explicitly using the same class loader// as the current applet or application.//conn = DriverManager.getConnection(dbURL,

dbUsername,dbPassword);

// use the Connection object}catch(SQLException e) {// if a database access error occurs// handle the exception here.e.printStackTrace();...

}

5203ch03.qxd 8/11/05 4:22 PM Page 93

Page 123: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS94

finally {// close the db resources such as Connection objectDatabaseUtil.close(conn);

}

Each vendor has a special format for the database URL. For example, Table 3-3 shows sampledatabase driver names and sample database URLs. (In Table 3-3, the server name is localhost, andthe database name is scorpian.)

Table 3-3. Vendor URL Formats

Vendor Driver Name URL Sample

Oracle 8i oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@localhost:1521:scorpian

Oracle 9i oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@localhost:1521:scorpian

MySQL org.gjt.mm.mysql.Driver jdbc:mysql://localhost/scorpian

Microsoft sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:scorpianAccess

Sybase com.sybase.jdbc.SybDriver jdbc:sybase:Tds:scorpian:2638(jConnect 4.2)

Sybase com.sybase.jdbc2.jdbc.SybDriver jdbc:sybase:Tds:scorpian:2638(jConnect 5.2)

MS SQL com.microsoft.jdbc. jdbc:microsoft:sqlserver://Server 2000 sqlserver.SQLServerDriver localhost:1433

MS SQL weblogic.jdbc.mssqlserver4.Driver jdbc:weblogic:mssqlserver4:database@Server 2000 localhost:port

IBM DB2 COM.ibm.db2.jdbc.net.DB2Connection jdbc:db2://localhost:6789/scorpian

If you are using a JDBC driver developed by a third party, the documentation will tell you whatto use for the driver name and the database URL format.

Method 2: Making the Connection

In this method, you add connection options to the end of the database URL. In using getConnection(String url), the dbUsername and dbPassword values are appended to the databaseURL. For example, one way of connecting to a database is through the JDBC driver manager usingthe method DriverManager.getConnection.

This method uses a string containing a URL. The following is an example of using the JDBCdriver manager to connect to Microsoft SQL Server 2000 while passing the username and password(as appended to the end of the URL):

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");String dbURL =

"jdbc:microsoft:sqlserver://localhost:1433;User=sa;Password=admin";Connection conn = DriverManager.getConnection(dbURL);

For Microsoft SQL Server, the complete connection URL format used with the driver manageris as follows:

jdbc:microsoft:sqlserver://hostname:port[;property=value...]

5203ch03.qxd 8/11/05 4:22 PM Page 94

vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
Page 124: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 95

where the following is true:

• hostname: Specifies the TCP/IP address or TCP/IP hostname (the assumption is that yournetwork resolves hostnames to IP addresses) of the server to which you are connecting.

• port: Specifies the database server port

• property=value: Specifies connection properties

Appending dbUsername and dbPassword values to the end of the database URL might causeunnecessary errors because of the creation of long strings. (For example, you might forget to sepa-rate the fields with a semicolon.)

Method 3: Making the Connection

In this third and final method that I will explain, you can set connection information in a java.util.Properties object and pass this information to the DriverManager.getConnection() method.

With this approach, you do not need to append dbUsername and dbPassword values to the endof the dbURL. Instead of appending values to the end of the dbURL, you put these values as a set of(key,value) into an instance of the java.util.Properties object. The following example specifiesthe server, user, and password in a Properties object:

public static final String DATABASE_USER = "user";public static final String DATABASE_PASSWORD = "password";

String oracleDriver = "oracle.jdbc.driver.OracleDriver";Class.forName(oracleDriver);String dbURL = "jdbc:oracle:thin:@localhost:1521:scorpian";String dbUsername = "scott";String dbPassword = "tiger";// these are properties that get passed// to DriverManager.getConnection(...)java.util.Properties dbProperties = new java.util.Properties();jdbcProperties.put(DATABASE_USER, dbUsername);jdbcProperties.put(DATABASE_PASSWORD, dbPassword);Connection conn = DriverManager.getConnection(dbURL, dbProperties);

Using the getConnection(String url, Properties info), you can pass additional parameters(such as Character Encoding Scheme) to the database as a set of (key,value) pairs.

3-6. How Do You Get a List of Loaded Drivers?To make sure your desired drivers are loaded, use the DriverManager.getDrivers() method andthen enumerate all loaded JDBC drivers:

import java.sql.Driver;import java.sql.DriverManager;import java.util.Enumeration;...// Retrieves an Enumeration with all of the currently// loaded JDBC drivers to which the current caller has access.Enumeration<Driver> e = DriverManager.getDrivers();while (e.hasMoreElements()) {

Driver driver = e.nextElement();String className = driver.getClass().getName();System.out.println("Driver class name="+className);

}

5203ch03.qxd 8/11/05 4:22 PM Page 95

vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
Page 125: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS96

SolutionTo get a list of currently loaded drivers, use this solution:

package jcb.util;

import java.util.*;import java.io.*;import java.sql.*;

/*** This class provides methods to support* JDBC driver-related functions.*/public class DriverManagerTool {

/*** Retrieves an XML with all of the currently loaded* JDBC drivers to which the current caller has access.* @return all loaded JDBC drivers as an XML (serialized* as a String object).*/public static String getLoadedDrivers() {

java.util.Enumeration e = java.sql.DriverManager.getDrivers();StringBuffer sb = new StringBuffer("<?xml version='1.0'>");sb.append("<loaded_drivers>");while (e.hasMoreElements()) {

Object driver = e.nextElement();appendXMLTag(sb, "loadedDriver", driver.toString());

}sb.append("</loaded_drivers>");return sb.toString();

}

private static void appendXMLTag(StringBuffer buffer,String tagName,String value) {

buffer.append("<");buffer.append(tagName);buffer.append(">");buffer.append(value);buffer.append("</");buffer.append(tagName);buffer.append(">");

}}

And here is how you can get a list of loaded client drivers:

// Print out all loaded JDBC drivers.public static void main(String[] args) throws Exception {

try {// register two driversClass.forName("org.gjt.mm.mysql.Driver");Class.forName("oracle.jdbc.driver.OracleDriver");// get loaded driversSystem.out.println(getLoadedDrivers());

}catch(Exception e) {

5203ch03.qxd 8/11/05 4:22 PM Page 96

Page 126: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 97

e.printStackTrace();}

}

Here is some output from the solution:

<?xml version='1.0'><loaded_drivers>

<loadedDriver>com.mysql.jdbc.Driver@affc70</loadedDriver><loadedDriver>oracle.jdbc.driver.OracleDriver@1fc4bec</loadedDriver>

</loaded_drivers>

3-7. How Do You Connect to an Oracle Database?To connect to an Oracle database, you need to create an instance of the java.sql.Connection object.

■Note The java.sql.Connection object is an interface, and you need an implementing class for it. Pleaseremember that you do not need to implement this interface, because Oracle’s JDBC driver provides such an imple-mentation.

The following example uses an Oracle JDBC driver to connect to an Oracle database instancelocated at server TIGER and port number 1521 with an SID (Oracle database ID) called scorpian. Tomake sure you have the right values for the SID and port, you may view the tnsnames.ora file(<oracle-installation-directory>/OraHome9i/network/admin/tnsnames.ora).

The following shows a portion of the tnsnames.ora file for an Oracle 9i installation. As you canobserve, tiger is the server name, tiger.usa.com is the full server name, and scorpian is an SID.

...SCORPIAN.USA.COM =(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = TIGER)(PORT = 1521))

)(CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = scorpian.tiger.usa.com)

))

...

Solution (Fragment)In this solution, I will present the most important code, and then I will expand this fragment to a classand some client code so you can see it really working.

Connection conn = null;try {

// Load the JDBC driverString driverName = "oracle.jdbc.driver.OracleDriver";Class.forName(driverName);

//// Create a connection to the database//

5203ch03.qxd 8/11/05 4:22 PM Page 97

Page 127: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS98

String serverName = "TIGER";String portNumber = "1521";String sid = "scorpian";String url = "jdbc:oracle:thin:@" + serverName + ":" +

portNumber + ":" + sid;String username = "octopus";String password = "octopus";conn = DriverManager.getConnection(url, username, password);// here the Connection object is ready to be used.

}catch (ClassNotFoundException e) {

// Could not find the database driver// When you are here, it means that your .jar file (which contains// the Driver class) has not been added to the CLASSPATH properly// You have to fix this problem before you// can establish a connection to an Oracle database.// deal with the exception...

}catch (SQLException e) {

// Could not connect to the database.// Either database server is down, or one/more of// your parameters is/are not specified correctly.// deal with the exception...

}finally {

// close the Connection objectDatabaseUtil.close(conn);

}

Solution: BasicConnectionManager ClassYou can rewrite the previous code as a class with a couple of methods, as shown here:

import java.sql.*;import java.util.*;import jcb.util.DatabaseUtil;/*** This class provides three basic methods:* 1) how to create a connection for a given (url, username, password)* 2) how to create a connection for a given (url, databaseProperties)* 3) how to load a driver*/public class BasicConnectionManager {

public static final String DATABASE_USER = "user";public static final String DATABASE_PASSWORD = "password";

/*** Load the JDBC driver.* @param driverName the driver name.* @throws ClassNotFoundException Failed to load the driver.*/public static void loadDriver(String driverName)

throws ClassNotFoundException {java.lang.Class.forName(driverName);

}}

5203ch03.qxd 8/11/05 4:22 PM Page 98

Page 128: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 99

/*** Get the connection from a given (url, user, password).* @param url database URL.* @param username database user.* @param user's password.* @return Connection object.* @throws SQLException Failed to create a Connection object.*/public static Connection getConnection(String url,

String username,String password)

throws SQLException {return DriverManager.getConnection(url, username, password);

}

/*** Get the connection from a given (url, user, password).* @param url database URL.* @param dbProperties database properties (includes database* username, password, and other database attributes).* @return Connection object.* @throws SQLException Failed to create a Connection object.*/public static Connection getConnection(String url,

Properties dbProperties)throws SQLException {return DriverManager.getConnection(url, dbProperties);

}

Client Code: Using the BasicConnectionManager ClassThe following code shows some client code to work the BasicConnectionManager class just created.I will provide an output sample later.

import java.sql.*;

public class TestOracleConnectionManager {

/*** Client program to create two connections.*/public static void main(String[] args) {

try {// Step 1: Load the JDBC driverString driverName = "oracle.jdbc.driver.OracleDriver";BasicConnectionManager.loadDriver(driverName);

}catch (ClassNotFoundException e) {// Could not find the database driver; when you are// here, it means that your .jar file (which contains// the Driver class) has not been added to the CLASSPATH// properly; you have to fix this problem before you// can establish a connection to an Oracle database.// deal with the exception...e.printStackTrace();

}

5203ch03.qxd 8/11/05 4:22 PM Page 99

Page 129: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS100

Connection conn1 = null;Connection conn2 = null;String serverName = "TIGER";String portNumber = "1521";String sid = "scorpian";String url = "jdbc:oracle:thin:@" + serverName + ":" + portNumber + ":" + sid;String username = "octopus";String password = "octopus";try {

// Step 2: Create a connection to the databaseconn1 = BasicConnectionManager.getConnection(url, username, password);System.out.println("conn1="+conn1);System.out.println("-----------------------");

// Step 3: Create another connection to the databaseconn2 = BasicConnectionManager.getConnection(url, username, password);System.out.println("conn2="+conn2);System.out.println("-----------------------");

}catch (SQLException e) {

// Could not connect to the database.// Either database server is down or one/more of// your parameters is/are not specified correctly.// deal with the exception...e.printStackTrace();

}finally {

BasicConnectionManager.close(conn1);BasicConnectionManager.close(conn2);

}}

}

The client output is as follows:

conn1=oracle.jdbc.driver.OracleConnection@66e815-----------------------conn2=oracle.jdbc.driver.OracleConnection@a37368-----------------------

3-8. How Do You Connect to a MySQL Database?To connect to a MySQL database, you need to create an instance of the java.sql.Connection object.(Note that java.sql.Connection is an interface, and you need an implementing class for it.)

The following example uses a MySQL JDBC driver to connect to a MySQL database instancelocated at server localhost and port number 3306 with a database called empDB. To make sure youhave the right values for the database name and port, you may view the database names by usinginteractive mysql, as shown here:

C:\mysql\bin>mysqlWelcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 4 to server version: 4.0.2-alpha-nt

mysql> show databases;

5203ch03.qxd 8/11/05 4:22 PM Page 100

Page 130: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 101

+----------+| Database |+----------+| empdb || mysql || test || tiger |+----------+4 rows in set (0.00 sec)

Solution As a Code FragmentIn this solution, I will present the most important code, and then I will expand this fragment toa class and some client code so you can see it really working. As you can observe, empDB is one of thedatabases in MySQL.

Connection conn = null;try {

// Load the JDBC driverString driverName = "org.gjt.mm.mysql.Driver";Class.forName(driverName);

// Create a connection to the databaseString url = "jdbc:mysql://localhost/empDB";String username = "root";String password = "root";

conn = DriverManager.getConnection(url, username, password);// use the Connection object

}catch (ClassNotFoundException e) {

// Could not find the database driver// When you are here, it means that your// .jar file (which contains the Driver class)// has not been added to the CLASSPATH properly// You have to fix this problem before you// can establish a connection to an Oracle database.// deal with the exception...

}catch (SQLException e) {

// Could not connect to the database.// Either database server is down or one/more of// your parameters is/are not specified correctly// deal with the exception...

}finally {

DatabaseUtil.close(conn);}

Solution As a Class and Methods: BasicConnectionManager ClassI will use the same BasicConnectionManager class as I presented in section 3-7 previously, since it isgeneric enough to apply to the current recipe context.

Client Using the BasicConnectionManager Class: MySQL DatabaseThe following code shows the client code to work with the BasicConnectionManager class in the contextof a MySQL database:

5203ch03.qxd 8/11/05 4:22 PM Page 101

Page 131: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS102

import java.sql.*;import jcb.util.DatabaseUtil;

public class TestMySqlConnectionManager {

/*** Client program to create two connections.*/public static void main(String[] args) {

try {// Step 1: Load the JDBC driverString driverName = "org.gjt.mm.mysql.Driver";BasicConnectionManager.loadDriver(driverName);

}catch (ClassNotFoundException e) {

// Could not find the database driver;// deal with the exception...e.printStackTrace();

}

Connection conn1 = null;Connection conn2 = null;String url = "jdbc:mysql://localhost/empDB";String username = "root";String password = "root";try {

// Step 2: Create a connection to the databaseconn1 = BasicConnectionManager.getConnection(url, username, password);System.out.println("conn1="+conn1);System.out.println("-----------------------");

// Step 3: Create another connectionconn2 = BasicConnectionManager.getConnection(url, username, password);System.out.println("conn2="+conn2);System.out.println("-----------------------");

}catch (SQLException e) {

// Could not connect to the database.// Either database server is down or one/more of// your parameters is/are not specified correctly.// deal with the exception...e.printStackTrace();

}finally {

DatabaseUtil.close(conn1);DatabaseUtil.close(conn2);

}}

}

The client output is as follows:

conn1=com.mysql.jdbc.Connection@19616c7-----------------------conn2=com.mysql.jdbc.Connection@b166b5-----------------------

5203ch03.qxd 8/11/05 4:22 PM Page 102

Page 132: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 103

3-9. How Do You Get a List of All Available Parameters for Creatinga JDBC Connection?Interactive development environments (such as NetBeans, VisualCafe, and Borland JBuilder) mightneed to discover all the available parameters for creating a JDBC connection. JDBC provides methodsfor discovering such information.

To get a list of all the available parameters for creating a JDBC connection, you need to providetwo pieces of information: a driver class name and a database URL. This code fragment shows howto get such information:

String driverName = ...String dbURL = ...Class.forName(driverName);Driver driver = DriverManager.getDriver(url);// Get available propertiesDriverPropertyInfo[] info = driver.getPropertyInfo(url, null);

The result is returned as an array of DriverPropertyInfo objects, which provides driver proper-ties for making a connection. According to the JDK 1.4 documentation, the DriverPropertyInfoclass is of interest only to advanced programmers who need to interact with a driver via themethod getDriverProperties to discover and supply properties for connections. You can use theDriverPropertyInfo objects for building GUI tools (to discover the required properties and let the userfill/select values for those properties).

Most of the JDBC drivers need to know the database username and password of the user con-necting to the database. The properties the driver needs in order to connect to the database will bereturned as an array of DriverPropertyInfo objects from the java.sql.Driver’s getPropertyInfo()method.

SolutionThis solution, which I’ll present in the following sections in richer context than just the code frag-ment, returns the result as XML (serialized as a String object), which can be useful for all types ofclients. Let’s look at that XML output format, and then I will present the solution.

XML Output FormatThe XML returned has the following format:

<?xml version='1.0'><DriverPropertyInformation

driver="driver-class-name"url="database-url"><DriverProperty>

<name>driver-property-name</name><required>true/false</required><value>driver-property-value</value><description>driver-property-description</description><choices>driver-property-choices</choices>

</DriverProperty><DriverProperty>

...</DriverProperty>...

</DriverPropertyInformation>

5203ch03.qxd 8/11/05 4:22 PM Page 103

Page 133: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS104

Solution: Java ClassThe following code shows how to take the code fragment and implement it within a Java class:

import java.util.*;import java.sql.*;import javax.sql.*;

public class DriverTool {/*** Listing All Available Parameters for Creating a JDBC* Connection. Driver.getPropertyInfo() returns a list* of all available properties that can be supplied when* using the driver to create a JDBC connection. This list* can be displayed to the user.** @param driverName the driver name.* @param url the database url.* @return returns the result as an XML (serialized as a String)* @throws ClassNotFoundException Failed to find the database driver.* @throws SQLException Failed to get available parameters for* Creating a JDBC Connection.*/public static String getDriverPropertyInformaton(String driverName,

String url)throws ClassNotFoundException, SQLException {// Load the driverClass.forName(driverName);// Get the Driver instanceDriver driver = DriverManager.getDriver(url);

// Get available propertiesDriverPropertyInfo[] info = driver.getPropertyInfo(url, null);StringBuffer sb = new StringBuffer("<?xml version='1.0'>");sb.append("<DriverPropertyInformation driver=\"");sb.append(driverName);sb.append("\" url=\"");sb.append(url);sb.append("\">");for (int i=0; i<info.length; i++) {

appendProperty(sb, info[i]);}sb.append("</DriverPropertyInformation>");return sb.toString();

}

private static void appendProperty(StringBuffer sb,DriverPropertyInfo info) {

sb.append("<DriverProperty>");// Get name of propertyappendXMLTag(sb, "name", info.name);// Is property value required?appendXMLTag(sb, "required", info.required);// Get current valueappendXMLTag(sb, "value", info.value);// Get description of propertyappendXMLTag(sb, "description", info.description);

5203ch03.qxd 8/11/05 4:22 PM Page 104

Page 134: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 105

// Get possible choices for property;// if null, value can be any stringString[] choices = info.choices;sb.append("<choices>");if (choices != null) {

for (int c=0; c < choices.length; c++) {appendXMLTag(sb, "choice", choices[c]);

}}sb.append("</choices>");sb.append("</DriverProperty>");

}

private static void appendXMLTag(StringBuffer buffer,String tagName,String value) {

buffer.append("<");buffer.append(tagName);buffer.append(">");buffer.append(value);buffer.append("</");buffer.append(tagName);buffer.append(">");

}

Client Program for the MySQL DatabaseThe following code shows some client code to run with the new class:

public static void main(String[] args) {try {

String driverName = "org.gjt.mm.mysql.Driver";String dbURL = "jdbc:mysql://localhost/empDB";String driverPropertyInformaton =getDriverPropertyInformaton(driverName, dbURL);System.out.println("--- driverPropertyInformaton ---");System.out.println(driverPropertyInformaton);System.out.println("--------------------------------");

}catch (ClassNotFoundException e) {

// Could not find the database driver; when you are// here, it means that your .jar file (which contains// the Driver class) has not been added to the CLASSPATH// properly; you have to fix this problem before you// can establish a connection to an Oracle database.// deal with the exception...e.printStackTrace();

}catch (SQLException e) {

// Could not connect to the database.// Either database server is down or one/more of// your parameters is/are not specified correctly.// deal with the exception...e.printStackTrace();

}}

5203ch03.qxd 8/11/05 4:22 PM Page 105

Page 135: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS106

Output for the MySQL DatabaseThe following is the partial XML output. For the complete output (about three pages), either run theprogram or visit this book’s Web site at http://www.apress.com (the filename is DriverTool.out).

<?xml version='1.0'><DriverPropertyInformation driver="org.gjt.mm.mysql.Driver"

url="jdbc:mysql://localhost/empDB"><DriverProperty>

<name>HOST</name><required>true</required><value>localhost</value><description>Hostname of MySQL Server</description><choices></choices>

</DriverProperty>

<DriverProperty><name>PORT</name><required>false</required><value>3306</value><description>Port number of MySQL Server</description><choices></choices>

</DriverProperty>

<DriverProperty><name>DBNAME</name><required>false</required><value>empDB</value><description>Database name</description><choices></choices>

</DriverProperty>...

</DriverPropertyInformation>

3-10. How Do You Create Connection(s) Using the DriverInterface?The Driver interface supports the creation of a database connection. This is the interface that everydriver class must implement. Therefore, each driver should supply a class that implements the Driverinterface. You can use the Driver interface’s connect() method to create new database connections.

JDK 1.4 defines the connect() method:

public Connection connect(String url, Properties info)throws SQLException

This attempts to make a database connection to the given URL. The driver should return null ifit realizes it is the wrong kind of driver to connect to the given URL. This will be common, becausewhen the JDBC driver manager is asked to connect to a given URL, it passes the URL to each loadeddriver in turn. The driver should throw a SQLException if it is the right driver to connect to the givenURL but has trouble connecting to the database.

You can use the java.util.Properties argument to pass arbitrary string tag-value pairs as connec-tion arguments. Normally, at least user and password properties should be included in the Propertiesobject.

5203ch03.qxd 8/11/05 4:22 PM Page 106

Page 136: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 107

These are the parameters:

• url: The URL of the database to which to connect.

• info: A list of arbitrary string key/value pairs as connection arguments. Normally at leasta user or password property should be included.

This returns a Connection object that represents a connection to the URL. And it throwsa SQLException if a database access error occurs.

Creating an Instance of java.sql.DriverTo create an instance of a Driver, you can use the following method:

public static java.sql.Driver getDriver(String driverClassName)throws InstantiationException,

ClassNotFoundException,IllegalAccessException {

Class driverClass = Class.forName(driverClassName);java.sql.Driver driver = (java.sql.Driver) driverClass.newInstance();System.out.println("getDriver: driver is OK. driver=" + driver);return driver;

}

Once you have an instance of java.sql.Driver, then you can use the connect() method to createa new database connection. Here is the code for creating a new database connection:

/*** Get a new database connection from a given driver, url,* and properties (includes "user" and "password" properties).* @param driver the JDBC driver.* @param url a database URL.* @param props the database properties.* @return a new database Connection object.* @throws SQLException Failed to create a new database connection.*/public static java.sql.Connection getConnection(java.sql.Driver driver,

String url,java.util.Properties props)

throws SQLException {if (driver == null) {

return null;}else {

return driver.connect(url, props);}

}

Creating a New Connection Object from a DriverHere is the code to create a new Connection object from a driver (I will present the output of thissection after the code):

public static void main(String[] args) {try {

java.sql.Driver mysqlDriver = getDriver("org.gjt.mm.mysql.Driver");String url = "jdbc:mysql://localhost/empDB";String username = "root";String password = "root";

5203ch03.qxd 8/11/05 4:22 PM Page 107

Page 137: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS108

java.util.Properties props = new java.util.Properties();props.put("user", username);props.put("password", password);java.sql.Connection conn = getConnection(mysqlDriver, url, props);System.out.println("conn="+conn);

}catch(Exception e) {

e.printStackTrace();}

}

The output is as follows:

getDriver: driver is OK. driver=org.gjt.mm.mysql.Driver@1e63e3dconn=com.mysql.jdbc.Connection@19616c7

3-11. What Is Basic Connection Management?Sometimes you may want to connect to multiple data sources and view the results of different queriesin a single page (such as a Web-based application); for example, you might be getting historic stockmarket data for comparing two companies, and each set of data might reside in a different database.In some database applications, it is not uncommon to access more than one data source.

I have developed a “basic” Java package called Basic Connection Management (BCM) thatenables you to get connections from multiple data sources. For details, please see the jcb.bcm pack-age, as shown in Figure 3-4. Please note that, for saving space, I will show only portions of the BCMpackage here; you can download the entire code from the book’s Web site.

Figure 3-4. BCM architecture

Client programs use only the java.sql.Connection interface and a manager class calledConnectionManager. The ConnectionManager class uses ConnectionFactory (an abstract class), whichis extended by three specific classes:

5203ch03.qxd 8/11/05 4:22 PM Page 108

Page 138: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 109

• OracleConnectionFactory (creates Oracle Connection objects)

• MysqlConnectionFactory (creates Oracle Connection objects)

• OdbcConnectionFactory (creates JDBC-ODBC Connection objects)

ConnectionManager is a singleton class, which makes sure that only one instance of an objectexists at any one time. (Singleton is a design pattern; for details, please refer to the Design Patternsbook by the Gang of Four.) Therefore, clients will not instantiate objects of a singleton class butretrieve/get a reference to the single object created inside singleton class.

Using BCM with Your Values: User, Password, Database NameTo use the BCM package with your own values, you need to modify the following values in the specificconnection factory class. For example, to supply your own values for an Oracle database connection,update the following in the OracleConnectionFactory class:

public static final String ORACLE_DRIVER ="oracle.jdbc.driver.OracleDriver";

public static final String ORACLE_URL_PREFIX ="jdbc:oracle:thin:@localhost:1521:";

public static final String DEFAULT_USER = "scott";public static final String DEFAULT_PASSWORD = "tiger";public static final String DEFAULT_DATABASE = "scorpian";

Clients Accessing BCMYou can get connections using either of two methods that you’ll look at now in turn.

Method 1: BCM Access Using the Data Source Name

For this method, just use the data source name (the Connection object will be obtained using thedefault user, password, and database name). The sample client code is as follows:

package jcb.bcm;

import java.sql.*;import jcb.util.DatabaseUtil;

/*** This client demonstrates the Basic Connection Management* (BCM package) using the data source name.*/public class TestConnectionFactory2 {

// the only reference to the ConnectionManagerstatic ConnectionManager cm = null;

// driver methodpublic static void main(String[] args) {

Connection conn1 = null;Connection conn2 = null;Connection conn3 = null;try {

// get the only instance of ConnectionManagercm = cm.getInstance();

5203ch03.qxd 8/11/05 4:22 PM Page 109

Page 139: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS110

// use default values for user, password, databaseconn1 = cm.getConnection(ConnectionManager.DATA_SOURCE_ORACLE);System.out.println("oracle connection="+conn1);

// get a connection to MySQL databaseconn2 = cm.getConnection(ConnectionManager.DATA_SOURCE_MYSQL);System.out.println("mysql connection="+conn2);

// get a connection to a JDBC-ODBC registered database// NOTE: the odbc data source is registered by// Microsoft's ODBC Data Source Administratorconn3 = cm.getConnection(ConnectionManager.DATA_SOURCE_ODBC);System.out.println("odbc connection="+conn3);

}catch(Exception e){

//handle the exceptione.printStackTrace();

}finally {

// close all connectionsDatabaseUtil.close(cm);DatabaseUtil.close(conn1);DatabaseUtil.close(conn2);DatabaseUtil.close(conn3);

}}

}

Output from the Client Program

The following is the output:

$ javac TestConnectionFactory2.java$ java jcb.bcm.TestConnectionFactory2Connection not created. Opening connection phase...Connecting to 1 database...Connection successful..oracle connection=oracle.jdbc.driver.OracleConnection@106082Connection not created. Opening connection phase...Connecting to 2 database...Connection successful..mysql connection=com.mysql.jdbc.Connection@11121f6Connection not created. Opening connection phase...Connecting to 3 database...Connection successful..odbc connection=sun.jdbc.odbc.JdbcOdbcConnection@95c083Closing connection

Method 2: BCM Access Using the Data Source Name

For the second method, use the data source name, user, password, and database name. This codeshows a sample client using this method:

5203ch03.qxd 8/11/05 4:22 PM Page 110

Page 140: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 111

package jcb.bcm;

import java.sql.*;

/*** This client demonstrates the Basic Connection Management* (BCM package) using the data source name, user, password,* and database name. These values override the default values.*/public class TestConnectionFactory3 {

// the only reference to the ConnectionManagerstatic ConnectionManager cm = null;

public static void main(String[] args){Connection conn1 = null;Connection conn2 = null;Connection conn3 = null;try{

// get the only instance of ConnectionManagercm = cm.getInstance();

// get Oracle connection and override the default valuesconn1 = cm.getConnection(ConnectionManager.DATA_SOURCE_ORACLE,

"scott", "tiger", "scorpian");System.out.println("oracle connection="+conn1);

// get a connection to an Access database using JDBC-ODBC// NOTE: the odbc data source is registered by// Microsoft's ODBC Data Source Administratorconn2 = cm.getConnection(ConnectionManager.DATA_SOURCE_ODBC,

null, null, "northwind");System.out.println("odbc connection="+conn2);

// get a connection to MySQL databaseconn3 = cm.getConnection(ConnectionManager.DATA_SOURCE_MYSQL,

"root", "root", "snipit");System.out.println("odbc connection="+conn3);

}catch(Exception e){

// handle the exceptionse.printStackTrace();

}finally{

// close all connectionsDatabaseUtil.close(cm);DatabaseUtil.close(conn1);DatabaseUtil.close(conn2);DatabaseUtil.close(conn3);

}}

}

5203ch03.qxd 8/11/05 4:22 PM Page 111

Page 141: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS112

Output from the Client Program

This is the output:

$ javac TestConnectionFactory3.java$ java jcb.bcm.TestConnectionFactory3Connection not created. Opening connection phase...Connecting to 1 database...Connection successful..oracle connection=oracle.jdbc.driver.OracleConnection@106082Connection not created. Opening connection phase...Connecting to 3 database...Connection successful..odbc connection=sun.jdbc.odbc.JdbcOdbcConnection@1006d75Connection not created. Opening connection phase...Connecting to 2 database...Connection successful..odbc connection=com.mysql.jdbc.Connection@95c083Closing connection

BCM Package FeaturesThe BCM package has the following features:

• The ConnectionManager is implemented as a singleton pattern.

• The ConnectionFactory is an abstract class, and it is implemented as a singleton pattern.

• Connection objects are instantiated by the following arguments:

• Database user

• Database user’s password

• Database name

• The BCM package has three specific factory classes that provide connections (note that eachspecific connection factory will create one connection, at most, and it will reuse connectionsas needed):

• OracleConnectionFactory (creates Oracle Connection objects)

• MysqlConnectionFactory (creates Oracle Connection objects)

• OdbcConnectionFactory (creates JDBC-ODBC Connection objects)

BCM’s ConnectionManagerThe purpose of ConnectionManager is to manage connections supplied by the ConnectionFactoryclass. The ConnectionManager is extensible, and you may add data sources. The ConnectionFactory isresponsible for delivering connections to the ConnectionManager class (the ConnectionManager classmanages only those connection created by the ConnectionFactory class).

package jcb.bcm;

import java.sql.*;

/*** This class manages database connections.* This is a "singleton" class. A singleton* class is a class with only one object for

5203ch03.qxd 8/11/05 4:22 PM Page 112

Page 142: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 113

* a given class and this object should be* easily accessible to clients.*/class ConnectionManager {

...}

BCM’s Connection FactoriesThe BCM has three factories: OracleConnectionFactory, MySqlConnectionFactory, and OdbcConnectionFactory. The main job of these factories is to produce Connection objects for clients.

OracleConnectionFactory

The purpose of OracleConnectionFactory is to pass the right parameters to ConnectionFactory forcreating Oracle connections. This class provides a default driver class and also provides a databaseURL prefix. (You need just to append the name of Oracle SID to it.)

package jcb.bcm;

import java.sql.*;

/*** This class extends the abstract ConnectionFactory* for connecting to an Oracle database.**/class OracleConnectionFactory extends ConnectionFactory {

...}

MySQLConnectionFactory

The purpose of MysqlConnectionFactory is to pass the right parameters to ConnectionFactory forcreating connections. This class provides a default driver class and also provides a database URLprefix. (You need just to append the name of the MySQL database to it.)

package jcb.bcm;

import java.sql.*;

/*** This class extends the abstract ConnectionFactory* for connecting to a MySQL database.**/class MysqlConnectionFactory extends ConnectionFactory {

...}

3-12. How Do You Determine If a Database Supports Transactions?The Connection interface has a method that provides information about supporting the transac-tions for the database engine. The Oracle and MySQL databases (and some other databases such asSybase) support transactions; meanwhile, the Microsoft Access database does not support transactions.

5203ch03.qxd 8/11/05 4:22 PM Page 113

Page 143: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS114

General SolutionThe following code presents the general solution, and then I will show how to test the solutionagainst Oracle and MySQL databases.

import java.sql.Connection;import java.sql.DatabaseMetaData;import jcb.util.DatabaseUtil;...Connection conn = null;try {

conn = <get-a-database-connection>;DatabaseMetaData dbMetaData = conn.getMetaData();if (dbMetaData.supportsTransactions()) {

// Transactions are supported}else {

// Transactions are not supported}

}catch (SQLException e) {

// handle the exception:// could not determine if database// server supports transactions

}finally {

DatabaseUtil.close(conn);}

Practical SolutionThis code shows a practical implementation of the previous general solution, which you will thenbe able to test against Oracle and MySQL databases:

import java.sql.Connection;import java.sql.SQLException;import java.sql.DatabaseMetaData;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class TestSupportsTransactions {

public static boolean supportsTransactions(Connection conn)throws SQLException {

if (conn == null) {return false;

}

DatabaseMetaData dbMetaData = conn.getMetaData();if (dbMetaData == null) {

// metadata is not supportedreturn false;

}

return dbMetaData.supportsTransactions();}

5203ch03.qxd 8/11/05 4:22 PM Page 114

Page 144: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 115

public static void main(String[] args) {Connection conn = null;try {

String dbVendor = args[0];conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("--- begin Test ---");System.out.println("dbVendor="+dbVendor);System.out.println("conn="+conn);System.out.println("Transaction Support:"+

supportsTransactions(conn));System.out.println("--- end of Test ---");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

DatabaseUtil.close(conn);}

}}

Running Solution for an Oracle DatabaseThis shows how to test the solution for Oracle:

$ javac TestSupportsTransactions.java$ java TestSupportsTransactions oracle--- begin Test ---dbVendor=oracleconn=oracle.jdbc.driver.T4CConnection@341960Transaction Support:true--- end of Test ---

Running Solution for a MySQL DatabaseThis shows how to test the solution for MySQL:

$ javac TestSupportsTransactions.java$ java TestSupportsTransactions mysql--- begin Test ---dbVendor=mysqlconn=com.mysql.jdbc.Connection@8fce95Transaction Support:true--- end of Test ---

3-13. How Do You Limit the Number of Rows Returned froma SQL Query?A JDBC client program to a database can limit the number of rows returned. When a SQL query isexecuted, the number of rows of data that a driver physically copies from the database server to theclient is called the fetch size. You can control the fetch size (by “setting” and “getting” the fetch size).

To improve the performance of a specific query, you can adjust the fetch size to better matchthe use of the query. You can set the fetch size on an instance of java.sql.Statement, in which case,all rows (result sets) created from that statement will use that fetch size.

5203ch03.qxd 8/11/05 4:22 PM Page 115

Page 145: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS116

You can also set the fetch size on a result set at any time. In this case, the next time data needsto be fetched from the database server, the driver will copy over as many rows as specified by thecurrent fetch size.

Solution: Getting the Fetch SizeThis code shows how to get the fetch size:

import java.sql.ResultSet;import java.sql.Statement;import java.sql.Connection;import java.sql.SQLException;import jcb.util.DatabaseUtil;...ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

conn = <get-a-database-connection>;

// Get the fetch size of a statementstmt = conn.createStatement ();int fetchSize = stmt.getFetchSize();

// Get the fetch size of a result setrs = stmt.executeQuery("select author from books_table");int fetchSize2 = rs.getFetchSize();

}catch (SQLException e) {

// handle the exception}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

Solution: Setting the Fetch SizeThis code shows how to set the fetch size:

import java.sql.ResultSet;import java.sql.Statement;import java.sql.Connection;import java.sql.SQLException;import jcb.util.DatabaseUtil;...ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

conn = <get-a-database-connection>;

// Set the fetch size of a statementstmt = conn.createStatement();stmt.setFetchSize(20);

5203ch03.qxd 8/11/05 4:22 PM Page 116

Page 146: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 117

// Create a result set, which will return only 20 rowsrs = stmt.executeQuery("SELECT author FROM books_table");// Change the fetch size on the result set//rs.setFetchSize(40);

}catch (SQLException e) {

// handle the exception}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

3-14. How Do You Get the Driver of a Connection?Given a connection, it is not possible to determine the driver that created the connection. Althoughthe connection can return a driver name, the returned name cannot be used to find the driver.

import java.sql.DatabaseMetaData;import java.sql.Connection;import java.sql.SQLException;import jcb.util.DatabaseUtil;...

Connection conn = null;try {

conn = <get-a-valid-database-connection>;DatabaseMetaData dbmd = conn.getMetaData();if (dbmd == null) {

System.out.println("vendor does not support metadata");}else {

String driverName = dbmd.getDriverName();}

}catch (SQLException e) {

// handle the exception}finally {

DatabaseUtil.close(conn);}

The best you can do is to use the same database URL that was used to create the Connectionobject:

import java.sql.Driver;import java.sql.DriverManager;import java.sql.Connection;import java.sql.SQLException;import jcb.util.DatabaseUtil;...Connection conn = null;try {

// create connection from database URLconn = DriverManager.getConnection(url, username, password);// Get driver from database URLDriver driver = DriverManager.getDriver(url);

5203ch03.qxd 8/11/05 4:22 PM Page 117

Page 147: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS118

catch (SQLException e) {// handle the exception

}finally {

DatabaseUtil.close(conn);}

3-15. How Do You Commit and Roll Back Updates to a Database?The Connection interface has several methods that control the commit and rollback updates toa database. These methods are as follows:

• void commit(): Makes all changes permanent that were made since the previouscommit/rollback and releases any database locks currently held by this Connection object.

• boolean getAutoCommit(): Retrieves the current autocommit mode for this Connectionobject.

• boolean isReadOnly(): Retrieves whether this Connection object is in read-only mode.

• void setReadOnly(boolean readOnly): Puts this connection in read-only mode as a hint tothe driver to enable database optimizations.

• void rollback(): Undoes all changes made in the current transaction and releases any data-base locks currently held by this Connection object.

• void rollback(Savepoint savepoint): Undoes all changes made after the given Savepointobject was set.

• void setAutoCommit(boolean autoCommit): Sets this connection’s autocommit mode to thegiven state.

• Savepoint setSavepoint(): Creates an unnamed savepoint in the current transaction andreturns the new Savepoint object that represents it.

• Savepoint setSavepoint(String name): Creates a savepoint with the given name in the cur-rent transaction and returns the new Savepoint object that represents it.

• void setTransactionIsolation(int level): Attempts to change the transaction isolationlevel for this Connection object to the one given.

By default, a database connection commits all updates to the database immediately and auto-matically. For example, executing an UPDATE SQL query immediately commits the change. Thefollowing code shows how to disable autocommits and explicitly commit:

import java.sql.Connection;import java.sql.SQLException;import jcb.util.DatabaseUtil;...Connection conn = null;try {

conn = <get-a-database-connection>;//// disable autocommit//conn.setAutoCommit(false);

// execute any number of SQL updates...

//// commit updates

5203ch03.qxd 8/11/05 4:22 PM Page 118

Page 148: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 119

//conn.commit();

}catch (SQLException e) {

//// Roll back update//conn.rollback();

}finally {

DatabaseUtil.close(conn);}

3-16. How Do You Determine If a SQL Warning Occurs?Some database operations (such as truncation errors) can cause a warning (such as a SQLWarning)that is not handled by an exception (such as a SQLException). You must explicitly check for thesedatabase warnings. An example of a warning is a data truncation error during a read operation (seethe DataTruncation class).

You have three places to check for a warning; the following code recipes check for each of thesein turn:

• Connection object

• Statement object

• ResultSet object

Check for Warnings Using the Connection ObjectThe following code shows how to check for warnings using the Connection object:

import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLWarning;import jcb.util.DatabaseUtil;...Connection conn = null;try {

conn = <get-a-database-connection>;//// do some operations with the Connection object//

//// get warnings on Connection object//SQLWarning warning = conn.getWarnings();while (warning != null) {

//// handle connection warning//String message = warning.getMessage();String sqlState = warning.getSQLState();int errorCode = warning.getErrorCode();warning = warning.getNextWarning();

}}

5203ch03.qxd 8/11/05 4:22 PM Page 119

Page 149: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS120

catch (SQLException e) {// exception happened while getting the warnings;// you may handle it or ignore it

}finally {

DatabaseUtil.close(conn);}

Check for Warnings Using the Statement ObjectThis code shows how to check for warnings using the Statement object:

import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLWarning;import java.sql.Statement;import jcb.util.DatabaseUtil;...Statement stmt = null;Connection conn = null;try {

conn = <get-a-database-connection>;// do some operations with the Connection object//// Get warnings on Statement object//

// Create a statementstmt = conn.createStatement();

// use the statement...

// get warnings on Statement objectSQLWarning warning = stmt.getWarnings();while (warning != null) {

//// handle connection warning//String message = warning.getMessage();String sqlState = warning.getSQLState();int errorCode = warning.getErrorCode();warning = warning.getNextWarning();

}catch (SQLException e) {

// exception happened while getting the warnings;// you may handle it or ignore it

}finally {

DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

Check for Warnings Using the ResultSet ObjectThis code shows how to check for warnings using the ResultSet object:

import java.sql.Connection;import java.sql.SQLException;

5203ch03.qxd 8/11/05 4:22 PM Page 120

Page 150: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 121

import java.sql.SQLWarning;import java.sql.Statement;import jcb.util.DatabaseUtil;...ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

conn = <get-a-database-connection>;// do some operations with the Connection object// create a statementstmt = conn.createStatement();

// use the statement...

// get a result setrs = stmt.executeQuery("SELECT author FROM books_table");while (resultSet.next()) {

// Use result set// and process columns retrieved

// get warnings on the current row of the ResultSet objectSQLWarning warning = resultSet.getWarnings();while (warning != null) {

// Process result set warnings...//// handle connection warning//String message = warning.getMessage();String sqlState = warning.getSQLState();int errorCode = warning.getErrorCode();warning = warning.getNextWarning();

}}

}catch (SQLException e) {

// exception happened while getting the warnings;// you may handle it or ignore it}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

3-17. What Are the MySQL Connection Properties?You can specify additional properties to the JDBC driver, either by placing them in a java.util.Properties instance and passing that instance to the DriverManager when you connect or by addingthem to the end of your JDBC URL as name-value pairs. The first connection property needs to bepreceded with ?; separate additional key-value pair properties with &.

In the following examples, I provide three recipes for passing additional properties to the JDBC driver.

Example 1: Passing Additional Properties Using java.util.PropertiesThe following code shows how to pass additional properties using java.util.Properties:

5203ch03.qxd 8/11/05 4:22 PM Page 121

Page 151: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS122

import java.sql.DriverManager;import java.sql.Connection;import java.util.Properties;...//// define and set connection properties//Properties properties = new java.util.Properties();properties.put("user", "root");properties.put("password","rootp");properties.put("useUnicode","true");properties.put("characterEncoding","utf8");String databaseURL = "jdbc:mysql://localhost/snipit";

//// create Connection object//Connection conn =

DriverManager.getConnection(databaseURL, properties);

Example 2: Passing Additional Properties Using a Database URLThis code shows how to pass additional properties using a database URL:

import java.sql.DriverManager;import java.sql.Connection;...//// define and set connection properties using a database URL//String databaseURL = "jdbc:mysql://localhost/snipit?user=root"+

"&password=rootp&useUnicode=true&characterEncoding=utf8";

//// create Connection object//Connection conn = DriverManager.getConnection(databaseURL);

Example 3: Passing Additional Properties Using java.util.Properties and a Database URLYou can also pass connection properties by using java.util.Properties and a database URL, asshown here:

import java.util.Properties;import java.sql.DriverManager;import java.sql.Connection;...//// define and set connection properties//Properties props = new Properties();props.put("user", "root");props.put("password","rootp");

// add additional connection properties to the database URLString databaseURL = "jdbc:mysql://localhost/snipit?" +

"useUnicode=true&characterEncoding=utf8";

5203ch03.qxd 8/11/05 4:22 PM Page 122

Page 152: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 123

//// create Connection object//Connection conn = DriverManager.getConnection(databaseURL, props);

For more information about the java.sql.Connection properties, their definitions, andtheir default values, refer to http://dev.mysql.com/doc/connector/j/en/cj-driver-classname.html#id2624462.

3-18. What Are the Oracle Connection Properties?In general, to get a database Connection object (that is, a java.sql.Connection) object, you need to pro-vide three values: a database URL, the database user, and the database user’s password. Some vendors(such as Oracle) provide additional properties (such as defaultRowPrefetch and defaultBatchValue)for their drivers. Also, I will show how to use Oracle roles for user logon (such as SYS).

You can use the DriverManager’s getConnection() method to pass additional properties; itssignature is as follows:

import java.sql.Connection;import java.util.Properties;...public static Connection getConnection(String url,

Properties info)

Specifying a Database URL and Properties ObjectThe following signature takes a URL, together with a Properties object that specifies a usernameand password (perhaps among other things):

getConnection(String URL, Properties info);

where the URL is of this form:

jdbc:oracle:<drivertype>:@<database>

In addition to the URL, use an object of the standard Java Properties class as input. For example:

java.util.Properties info = new java.util.Properties();info.put ("user", "scott");info.put ("password","tiger");info.put ("defaultRowPrefetch","15");Connection conn = DriverManager.getConnection("jdbc:oracle:oci:@", info);

Oracle 10’s JDBC drivers recognize an additional driver properties; see http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.htm for more information.

How to Use Roles for Oracle’s SYS LogonOracle has two prebuilt database users (SYS and SYSTEM) that have a lot of privileges for managingdatabase resources. To specify the role (mode) for the SYS logon, use the internal_logon connectionproperty.

The following example illustrates how to use the internal_logon and sysdba arguments tospecify the SYS logon:

import java.sql.*;import java.math.*;...// register driverDriverManager.registerDriver (new oracle.jdbc.OracleDriver());

5203ch03.qxd 8/11/05 4:22 PM Page 123

Page 153: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS124

//specify the Properties objectjava.util.Properties info = new java.util.Properties();info.put ("user", "sys");info.put ("password", "change_on_install");info.put ("internal_logon","sysdba");

//specify the Connection objectString dbURL = "jdbc:oracle:thin:@mydatabase"Connection conn = DriverManager.getConnection(dbURL,info);

3-19. Can a JDBC Application Connect to More Than One Database?The answer is “yes.” A JDBC application can connect to any number of databases. For each database,you need to create a proper connection before accessing the database.

Solution: Connecting to More Than One DatabaseThe following code shows a code snippet that connects to two databases:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.*;import jcb.meta.*;

public class TestConnectToMoreThanOneDatabase {

/*** Create an Oracle connection...*/public static Connection getOracleConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:scorpian";String username = "octopus";String password = "octopus";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

/*** Create a MySQL connection...*/public static Connection getMySqlConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/tiger";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

public static void main(String[] args) {Connection oracleConn = null;Connection mysqlConn = null;

5203ch03.qxd 8/11/05 4:22 PM Page 124

Page 154: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 125

try {System.out.println("-- TestConnectToMoreThanOneDatabase begin --");// get connection to an Oracle databaseoracleConn = getOracleConnection();

// get connection to a MySQL databasemysqlConn = getMySqlConnection();

System.out.println("oracleConn="+oracleConn);System.out.println("mysqlConn="+mysqlConn);

// now, you may use oracleConn to access an Oracle database// and use mysqlConn to access a MySQL database

//// use oracleConn and mysqlConn//System.out.println("-- TestConnectToMoreThanOneDatabase end --");

}catch(Exception e){

// handle the exceptione.printStackTrace();System.exit(1);

}finally {// release database resourcesDatabaseUtil.close(oracleConn);DatabaseUtil.close(mysqlConn);

}}

}

Testing Connection to More Than One DatabaseTo test more than one database, use this code:

$ javac TestConnectToMoreThanOneDatabase.java$ java TestConnectToMoreThanOneDatabase-- TestConnectToMoreThanOneDatabase begin --oracleConn=oracle.jdbc.driver.OracleConnection@16f0472mysqlConn=com.mysql.jdbc.Connection@18d107f-- TestConnectToMoreThanOneDatabase end --

3-20. How Do You Test to See If Your Connection Is Alive?Among java.sql objects, the JDBC Connection object (java.sql.Connection) is the most importantobject in the java.sql package. When you get a Connection object, with some simple tests you canfigure it out to see if the connection is a valid connection. The following scenarios show how a con-nection can go bad:

• If you hold a Connection object for a while and then the database server goes down (for anyreason), then that connection is no longer a valid connection.

• If you hold a Connection object for a while and then the connection times out (closes itself),then that connection is no longer a valid connection.

5203ch03.qxd 8/11/05 4:22 PM Page 125

Page 155: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS126

• If you hold a Connection object for a while and then the network connection dies, then thatconnection is no longer a valid connection.

• There might be other situations where you might think you are holding a valid connectionbut the reality is that the connection cannot do much (which is an obsolete connection).

Now, the real question is this: once you have a connection, how do you determine if that con-nection is a valid/useful connection and you can get/set data from/to a database? In the followingrecipes, I have developed a method that accepts a connection and its vendor and then returns trueif the connection is a valid connection; otherwise, it returns false. If a connection’s validation fails, itmust be destroyed, and a new Connection object must be created and returned.

Solution: Testing Validity of ConnectionThe following code is quite lengthy, but it shows a full creation-and-test sequence so that you cansee the validation technique at work. I’ve put plenty of comments in the code so you can easily seewhat it’s doing. I provide some sample output immediately after this listing.

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.*;import jcb.meta.*;

public class TestValidityOfConnection {

/*** Create an Oracle connection...*/public static Connection getOracleConnection() throws Exception {

String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:scorpian";String username = "octopus";String password = "octopus";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

/*** Create a MySQL connection...*/public static Connection getMySqlConnection() throws Exception {

String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/tiger";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

/*** Test validity of a connection* @param conn a JDBC Connection object* @param vendor a database vendor: { "oracle", "mysql", ...}* @return true if a given Connection object is a valid one;* otherwise return false.

5203ch03.qxd 8/11/05 4:22 PM Page 126

Page 156: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 127

* @throws Exception Failed to determine if a given connection is valid.*/public static boolean isValidConnection(Connection conn,

String vendor)throws Exception {if (conn == null) {

// null Connection object is not validreturn false;

}

if (conn.isClosed()) {// closed Connection object is not validreturn false;

}

// here you have a Connection object that is not null and// that is not closed, but it might be a defunct object// in order to determine whether it is a valid connection,// depends on the vendor of the database://// for Oracle database:// you may use the Connection object// with query of "select 1 from dual";// if the query returns the result, then// it is a valid Connection object.//// for MySQL database:// you may use the Connection object// with query of "select 1"; if the// query returns the result, then it// is a valid Connection object.

if (vendor.equalsIgnoreCase("mysql")) {// if you need to determine if the connection// is still valid, you should issue a simple// query, such as "SELECT 1". The driver will// throw an exception if the connection is// no longer valid.return testConnection(conn, "select 1");

}else if (vendor.equalsIgnoreCase("oracle")) {

// if you need to determine if the connection// is still valid, you should issue a simple// query, such as "SELECT 1 from dual". The driver// will throw an exception if the connection is// no longer valid.return testConnection(conn, "select 1 from dual");

}else {

// you may add additional vendors here.return false;

}}

/*** Test validity of a connection* @param conn a JDBC Connection object

5203ch03.qxd 8/11/05 4:22 PM Page 127

Page 157: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS128

* @param query a sql query to test against database connection* @return true if a given Connection object is a valid one;* otherwise return false.*/public static boolean testConnection(Connection conn,

String query) {ResultSet rs = null;Statement stmt = null;try {

stmt = conn.createStatement();if (stmt == null) {

return false;}

rs = stmt.executeQuery(query);if (rs == null) {

return false;}

if (rs.next()) {// Connection object is valid: you were able to// connect to the database and return something useful.return true;

}

// there is no hope any more for the validity// of the Connection objectreturn false;

}catch(Exception e) {

// something went wrong: connection is badreturn false;

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);

}}

public static void main(String[] args) {Connection oracleConn = null;Connection mysqlConn = null;try {

System.out.println("-- TestValidityOfConnection begin --");

// get connection to an Oracle databaseoracleConn = getOracleConnection();System.out.println("oracleConn="+oracleConn);System.out.println(isValidConnection(oracleConn, "oracle"));

// get connection to a MySQL databasemysqlConn = getMySqlConnection();System.out.println("mysqlConn="+mysqlConn);System.out.println(isValidConnection(mysqlConn, "mysql"));

5203ch03.qxd 8/11/05 4:22 PM Page 128

Page 158: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 129

System.out.println("databases are shutting down...");

// sleep for 30 seconds (enough time to shut down// both Oracle and MySQL databases) and during this// time shut down both Oracle and MySQL databases:Thread.sleep(30000);

// wake up after 30 seconds// test to see if the Oracle connection is valid?System.out.println("oracleConn="+oracleConn);System.out.println(isValidConnection(oracleConn, "oracle"));

// test to see if the MySQL connection is valid?System.out.println("mysqlConn="+mysqlConn);System.out.println(isValidConnection(mysqlConn, "mysql"));

System.out.println("-- TestValidityOfConnection end --");}catch(Exception e){

// handle the exceptione.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(oracleConn);DatabaseUtil.close(mysqlConn);

}}

}

Running Program: Testing Validity of ConnectionUse this code to test the validity of the connection:

$ javac TestValidityOfConnection.java

$ java TestValidityOfConnection-- TestValidityOfConnection begin --oracleConn=oracle.jdbc.driver.OracleConnection@1a125f0truemysqlConn=com.mysql.jdbc.Connection@1372a1atruedatabases are shutting down...oracleConn=oracle.jdbc.driver.OracleConnection@1a125f0falsemysqlConn=com.mysql.jdbc.Connection@1372a1afalse-- TestValidityOfConnection end --java.sql.SQLException: Io exception: Connection reset by peer: socket write errorat oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:134)at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:179)at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:333)at oracle.jdbc.driver.OracleConnection.close(OracleConnection.java:1442)at jcb.db.DatabaseUtil.close(DatabaseUtil.java:67)at TestValidityOfConnection.main(TestValidityOfConnection.java:176)

5203ch03.qxd 8/11/05 4:22 PM Page 129

Page 159: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS130

3-21. How Do You Keep the Connection Alive in a ProductionEnvironment?Maybe you have heard the following from a software/database engineer: “I have a database applica-tion that works fine for a day and then stops working overnight....”

This can happen to many database applications. In general, when you get a Connection objectfrom database server, there is a timeout property associated with the Connection object; when this timeexpires, then the Connection object becomes stale/defunct (becoming a dead, useless Connectionobject). For example, the MySQL database closes connections after eight hours of inactivity. So,you either need to use a connection pool that handles stale connections or use the autoReconnectparameter.

Using the MySQL database, automatic reconnection is available. Because the Connection objecthas to “ping” the database before each query, this is turned off by default. To use it, you need to passautoReconnect=true in the connection URL. You may also change the number of reconnect tries andthe initial timeout value via the parameters maxReconnects=n (the default is 3) and initialTimeout=n(the default is two seconds). The timeout is an exponential back-off timeout; in other words, if youhave initial timeout of two seconds and a maxReconnects of three seconds, then the driver will time-out for two seconds, four seconds, and then sixteen seconds between each reconnection attempt.

Solution: Creating Connection with Properties (MySQL)The following code shows how to keep connections alive in a production environment:

import java.sql.*;import java.util.*;import jcb.util.DatabaseUtil;

public class TestCreateConnectionWithProperties_MySQL {

public static final String DATABASE_USER = "user";public static final String DATABASE_PASSWORD = "password";public static final String MYSQL_AUTO_RECONNECT = "autoReconnect";public static final String MYSQL_MAX_RECONNECTS = "maxReconnects";

/*** Create MySQL connection...which will live for a long time*/public static Connection getConnection() throws Exception {

String driver = "org.gjt.mm.mysql.Driver";// load the driverClass.forName(driver);String dbURL = "jdbc:mysql://localhost/tiger";String dbUsername = "root";String dbPassword = "root";

// these are properties that get passed// to DriverManager.getConnection(...)java.util.Properties connProperties = new java.util.Properties();connProperties.put(DATABASE_USER, dbUsername);connProperties.put(DATABASE_PASSWORD, dbPassword);

// set additional connection properties:// if connection stales, then make automatically// reconnect; make it alive again;// if connection stales, then try for reconnection;connProperties.put(MYSQL_AUTO_RECONNECT, "true");

5203ch03.qxd 8/11/05 4:22 PM Page 130

Page 160: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 131

connProperties.put(MYSQL_MAX_RECONNECTS, "4");return DriverManager.getConnection(dbURL, connProperties);

}

public static void main(String[] args) {Connection conn = null;try {

System.out.println("-- TestCreateConnection_MySQL begin --");// get connection to an Oracle databaseconn = getConnection();System.out.println("conn="+conn);// use connection ...System.out.println("-- TestCreateConnection_MySQL end --");

}catch(Exception e){

// handle the exceptione.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(conn);

}}

}

Test: Creating Connection with Properties (MySQL)To test, use this code:

$ javac TestCreateConnectionWithProperties_MySQL.java$ java TestCreateConnectionWithProperties_MySQL-- TestCreateConnection_MySQL begin --conn=com.mysql.jdbc.Connection@14ed9ff-- TestCreateConnection_MySQL end --

3-22. How Do You Disconnect from a Database?To do something useful with a database, you need a Connection (java.sql.Connection interface)object. When you are done with your database task, you need to close the Connection object.(Closing a Connection object is referred to as disconnecting from a database.) If you have borrowedyour Connection object from a connection pool manager, then you have to return it to the connec-tion pool manager; otherwise, you need to close it properly.

Case 1: Borrowing a Connection Object from a Connection Pool ManagerAssume that there is a connection pool manager identified by a singleton class ConnectionPoolManager,which manages a pool of connections for several data sources. Further assume that this class hastwo static methods:

getConnection(String dataSourceName)releaseConnection(Connection conn)

Then you can write the following snippet for getting a connection and then a disconnectionfrom the database. Note that the connection pool manager does a soft close (rather than a hardclose) on the connections, which means it just returns the connection to the pool to be used byother clients.

5203ch03.qxd 8/11/05 4:22 PM Page 131

Page 161: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS132

Connection conn = null;try {

// get Connection object from the poolString dataSourceName = "myDataSource";conn = ConnectionPoolManager.getConnection(dataSourceName);System.out.println("conn="+conn);

// use connection do something useful with it.}catch(Exception e){

// handle the exceptione.printStackTrace();// do more handling

}finally {

// disconnect from the database// release Connection object: this is a soft closeif (conn != null) {

ConnectionPoolManager.releaseConnection(conn);}

}

Case 2: Not Borrowing a Connection Object from a Connection Pool ManagerAssume that there is static method (getConnection()) that returns a new Connection object. Thenyou can write the following:

import jcb.util.DatabaseUtil;...Connection conn = null;try {

// get Connection object from the poolString dataSourceName = "myDataSource";conn = getConnection(dataSourceName);System.out.println("conn="+conn);

// use connection do something useful with it.}catch(Exception e){

// handle the exceptione.printStackTrace();// do more handling

}finally {

// disconnect from the database// release Connection object: this is a soft close

Databaseutil.close(conn);}

3-23. What Are the Rules for Connection’s Autocommit?The java.sql.Connection interface has several methods that control the commit and rollbackupdates to a database. The void setAutoCommit(boolean autoCommit) method directly deals withthe Connection object’s autocommit mode; it sets this connection’s autocommit mode to the givenstate.

5203ch03.qxd 8/11/05 4:22 PM Page 132

Page 162: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 133

According to the JDBC specification, by default a database connection commits all updates tothe database immediately and automatically. By default, new connections are in autocommit mode.For example, executing an UPDATE SQL query immediately commits the change.

Solution: Disabling AutocommitsThe following example shows how to disable autocommits and explicitly commit:

import java.sql.*;import jcb.util.DatabaseUtil;...Connection conn = null;try {

conn = <get-a-database-connection>;

// disable autocommitconn.setAutoCommit(false);

// execute any number of SQL updates...

// commit updatesconn.commit();

}catch (SQLException e) {

// Roll back updateconn.rollback();

}finally {

// disconnect from the database: release Connection objectDatabaseUtil.close(conn);

}

Enabling AutocommitsThe following example shows how to enable autocommits:

import java.sql.*;import jcb.util.DatabaseUtil;...Connection conn = null;try {

conn = <get-a-database-connection>;

// enable autocommitconn.setAutoCommit(true);

// execute any number of SQL updates...// NOTE: from now on, every statement will commit

}catch (SQLException e) {

// handle the exception}finally {

// disconnect from the database: release Connection objectDatabaseUtil.close(conn);

}

5203ch03.qxd 8/11/05 4:22 PM Page 133

Page 163: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS134

What Are the Rules for Autocommit?The rules for autocommit are as follows: if autocommit is true, then commit happens automaticallyafter every SQL query/statement; if autocommit is false, then it does not. You can get the autocom-mit value by invoking the Connection.getAutoCommit() method.

You can turn off autocommit mode for an active database connection with the following:

java.sql.Connection conn = valid-database-connection-object;conn.setAutoCommit(false) ;

and turn it back on again with this:

conn.setAutoCommit(true);

Once autocommit is off (that is, false), no SQL statements will be committed (that is, the data-base will not be permanently updated) until you have explicitly told it to commit by invoking theConnection.commit() method:

conn.commit();

Detailed Autocommit RulesIn general, autocommit rules are complex:

• If autocommit is true and the method is executeUpdate(), then the commit happens whenthe method completes.

• If autocommit is true and the method is executeQuery(), then the commit happens when theResultSet object (the result set obtained from the executeQuery() method) is closed.

• If autocommit is true and the method is execute(), then the commit depends on the type ofstatement/query passed to the execute() method.

• If autocommit is false and the method is execute() or executeUpdate() and the SQL statementis CREATE/ALTER/DROP, then the commit happens upon statement completion.

• If autocommit is false and the SQL statement is not CREATE/ALTER/DROP, then nothing happens.(The commit will take place by the explicit commit() method.)

3-24. How Do You Create a New Type Map?In JDBC, type mapping enables user-defined types (UDTs). What is a UDT? It is a data type com-prised of other types (such as VARCHAR, INTEGER, and so on).

Using Oracle database, a data type is a UDT that encapsulates a data structure along with themethods (functions and procedures) needed to manipulate the data. The data is referred to as attributes,and the set of operations specified on the data are called the methods of the object type.

In relational databases, UDTs enable users to create object-relational databases. To create (writea UDT to a database) and retrieve (read a UDT from a database) UDTs, you can manipulate them asSQL structs and SQL arrays, or you can define Java classes that are mapped to the SQL UDTs, whichyou then use to materialize the UDTs in your Java program. JDBC provides interfaces (such as java.sql.SQLData, java.sql.SQLInput, and java.sql.SQLOutput) to enable read/write operations of UDTs.In this chapter, we will look at SQLInput and SQLOutput in detail with the sample running programs(using Oracle 10g).

Oracle enables users to define UDTs. The MySQL database does not offer to define a UDT andtherefore does not support SQLData, SQLInput, and SQLOutput.

5203ch03.qxd 8/11/05 4:22 PM Page 134

Page 164: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 135

What Is Type Mapping?Before you look at UDTs, you need to understand type mapping. According to the J2SE documentation,a user may create a new type map, which is a java.util.Map object, make an entry in it, and pass itto the java.sql methods that can perform custom mapping. In this case, the method will use thegiven type map instead of the one associated with the connection.

For example, the following code fragment specifies that the SQL type BOOK will be mapped tothe class Book in the Java programming language. The code fragment retrieves the type map for theConnection object conn, inserts the entry into it, and then sets the type map with the new entry asthe connection’s type map.

java.sql.Connection conn = a-valid-connection-object;java.util.Map map = conn.getTypeMap();map.put("mySchemaName.BOOK", Class.forName("Book"));conn.setTypeMap(map);

3-25. How Do You Create a SQL to Java Type Map Entry?You can create a SQL to Java type map entry as follows:

map.put (<SQL Type Name>,<Java class which implements java.sql.SQLData interface>);

java.sql.SQLData is the interface used for the custom mapping of a SQL UDT to a class in theJava programming language. The Class object for a class implementing the SQLData interface will beentered in the appropriate Connection object’s type map along with the SQL name of the UDT forwhich it is a custom mapping.

java.sql.Connection support for Java Type MapThe Connection interface has two methods for supporting Java type maps, as shown here:

getTypeMapSignature:

Map getTypeMap() throws SQLExceptionDescription:

Retrieves the Map object associated with thisConnection object. Unless the application has addedan entry, the type map returned will be empty.

Returns:the java.util.Map object associated with this Connection object

Throws:SQLException - if a database access error occurs

setTypeMapSignature:

void setTypeMap(Map map) throws SQLExceptionDescription:

Installs the given TypeMap object as the type map forthis Connection object. The type map will be used for thecustom mapping of SQL structured types and distinct types.

Parameters:map - the java.util.Map object to install as the replacementfor this Connection object's default type map

Throws:SQLException - if a database access error occurs or the givenparameter is not a java.util.Map object

5203ch03.qxd 8/11/05 4:22 PM Page 135

Page 165: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS136

Example: Using Custom MappingI will provide a complete example on using custom mapping. To fully understand the concept, youneed to understand the SQLData interface. This is according to the JDK documentation:

The interface, SQLData, used for the custom mapping of an SQL user-defined type (UDT) toa class in the Java programming language. The class object for a class implementing theSQLData interface will be entered in the appropriate Connection object’s type map alongwith the SQL name of the UDT for which it is a custom mapping.

For details of SQLData interface, please refer to the JDK documentation.

Example: Using Custom Mapping: Oracle Database Preparation

This code shows how to prepare the Oracle database:

$ sqlplus scott/tigerSQL*Plus: Release 10.1.0.2.0 - Production on Tue Oct 12 09:48:48 2004SQL> create or replace type BOOK as object(2 isbn varchar2(10),3 title varchar2(20),4 author varchar2(20),5 edition integer6 );7 /

Type created.

SQL> describe scott.BOOK;Name Null? Type----------------------------------------- -------- ------------ISBN VARCHAR2(10)TITLE VARCHAR2(20)AUTHOR VARCHAR2(20)EDITION NUMBER(38)

SQL> create table book_table(2 id varchar2(5),3 book_object BOOK4 );

Table created.

SQL> desc book_table;Name Null? Type----------------------------------------- -------- -----------ID VARCHAR2(5)BOOK_OBJECT BOOK

SQL> insert into book_tablevalues('11111', BOOK('1111111111', 'MyTitle', 'Me', 12));1 row created.

SQL> insert into book_tablevalues('22222', BOOK('2222222222', 'YourTitle', 'You', 10));1 row created.

5203ch03.qxd 8/11/05 4:22 PM Page 136

Page 166: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 137

SQL> select * from book_table;

ID BOOK_OBJECT(ISBN, TITLE, AUTHOR, EDITION)--------- ----------------------------------------------11111 BOOK('1111111111', 'MyTitle', 'Me', 12)22222 BOOK('2222222222', 'YourTitle', 'You', 10)

Example: Using Custom Mapping: Implementing SQLData

Suppose you have decided to use a custom mapping for the structured type BOOK so you can simplymake changes to the Java class that maps BOOK. The Java class will have a field for each attribute ofBOOK, and you can name the class and the fields whatever you want. The first thing required fora custom mapping is to create a Java class that implements the interface SQLData. (Note that youcan use tools to create your required Java class.)

To use the SQLData interface, the implementing Java class must have a member for each elementin the named row but can have additional members other than these. The members need not be pub-lic and can be in any order. The class must implement the writeSQL(), readSQL(), and getSQLTypeName()methods as defined in the SQLData interface but can provide additional methods.

The Book class implements the java.sql.SQLData interface for the custom mapping of the BOOKtype, as shown here:

1 import java.sql.SQLData;2 import java.sql.SQLInput;3 import java.sql.SQLOutput;4 import java.sql.SQLException;5 import java.io.Serializable;67 /**8 * A class to hold a copy of "SCOTT.BOOK" data type9 */10 public class Book implements SQLData, Serializable {1112 public static final String SQL_TYPE_NAME = "SCOTT.BOOK";13 public String isbn;14 public String title;15 public String author;16 public int edition;1718 // this constructor is required by Oracle's JDBC driver.19 // if you exclude this constructor, then you will get a20 // SQLException: "Inconsistent java and sql object types:21 // InstantiationException: Book"22 public Book() {23 }2425 public Book (String isbn,26 String title,27 String author,28 int edition) {29 this.isbn = isbn;30 this.title = title;31 this.author = author;32 this.edition = edition;33 }3435 // retrieves the fully qualified name of the SQL

5203ch03.qxd 8/11/05 4:22 PM Page 137

Page 167: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS138

36 // user-defined type that this object represents.37 public String getSQLTypeName() {38 return SQL_TYPE_NAME;39 }4041 // populates this object with data it reads from stream42 public void readSQL(SQLInput stream, String sqlType)43 throws SQLException {44 this.isbn = stream.readString();45 this.title = stream.readString();46 this.author = stream.readString();47 this.edition = stream.readInt();48 }4950 // writes this object to stream51 public void writeSQL(SQLOutput stream)52 throws SQLException {53 stream.writeString(this.isbn);54 stream.writeString(this.title);55 stream.writeString(this.author);56 stream.writeInt(this.edition);57 }5859 /**60 * For debugging: prints the raw data obtained from db.61 */62 public void print() {63 System.out.println("--- Book print() raw data begin ---");64 System.out.println("isbn="+isbn);65 System.out.println("title="+title);66 System.out.println("author="+author);67 System.out.println("edition="+edition);68 System.out.println("--- Book print() raw data end ---");69 }70 }

Example: Using Custom Mapping: Using a Connection’s Type Map

After writing a Java class that implements the interface java.sql.SQLData, the only other thing youhave to do to set up a custom mapping is to make an entry in a type map. For this example, thatmeans entering the fully qualified SQL name for BOOK and the Class object for the class Book.

A type map, an instance of java.util.Map, is associated with every new connection when it iscreated, so you can just use that one. Assuming that conn is the active connection, the followingcode fragment adds an entry for the UDT BOOK to the type map associated with conn (the Connectionobject):

java.util.Map map = conn.getTypeMap();map.put("SCOTT.BOOK", Class.forName("Book");

Note that whenever you call the ResultSet.getObject() method to retrieve an instance of BOOK,the JDBC driver will check the type map associated with the connection and see that it has an entryfor the custom type BOOK. The JDBC driver will note the Class object for Book, create an instance ofit, and then map the custom type BOOK to the Java object Book.

Example: Using Custom Mapping: Writing a New Record

You can insert UDTs into a database in two ways:

5203ch03.qxd 8/11/05 4:22 PM Page 138

Page 168: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 139

• Method 1: You can use a typical insert statement. For example, you can use the following SQLinsert statement to create a new UDT in the database:

insert into book_table values(?, BOOK(?, ?, ?, ?))

• Method 2: You can use a typical insert statement with setObject(). You can use the followingSQL insert statement to create a new UDT in the database where the second argument is a UDT:

insert into book_table values(?, ?)

Method 1 Solution: Using Custom Mapping: Writing a New Record

This code shows how to write a new record:

1 import java.util.*;2 import java.io.*;3 import java.sql.*;45 import jcb.db.*;6 import jcb.meta.*;78 public class InsertCustomType_Oracle {910 public static Connection getConnection() throws Exception {11 String driver = "oracle.jdbc.driver.OracleDriver";12 String url = "jdbc:oracle:thin:@localhost:1521:caspian";13 String username = "scott";14 String password = "tiger";15 Class.forName(driver); // load Oracle driver16 return DriverManager.getConnection(url, username, password);17 }1819 public static void main(String[] args) {20 System.out.println("--- InsertCustomType_Oracle begin ---");21 if (args.length != 5) {22 System.out.println("usage: java InsertCustomType_Oracle "+23 "<id> <isbn> <title> <author> <edition>");24 System.exit(1);25 }2627 String id = args[0];28 String isbn = args[1];29 String title = args[2];30 String author = args[3];31 int edition = Integer.parseInt(args[4]);3233 Connection conn = null;34 PreparedStatement pstmt = null;35 try {36 conn = getConnection();37 String insert =38 "insert into book_table values(?, BOOK(?, ?, ?, ?))";39 pstmt = conn.prepareStatement(insert);40 pstmt.setString(1, id);41 pstmt.setString(2, isbn);42 pstmt.setString(3, title);43 pstmt.setString(4, author);

5203ch03.qxd 8/11/05 4:22 PM Page 139

Page 169: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS140

44 pstmt.setInt(5, edition);45 pstmt.executeUpdate();46 System.out.println("--- InsertCustomType_Oracle end ---");47 }48 catch(Exception e){49 e.printStackTrace();50 System.exit(1);51 }52 finally {53 DatabaseUtil.close(pstmt);54 DatabaseUtil.close(conn);55 }56 }}

Running Example: Using Custom Mapping: Writing a New Record

To run the example, use this code:

$ javac InsertCustomType_Oracle.java$ java InsertCustomType_Oracle 77777 1122334455 "Strong Tigers" "Bob Smith" 3--- InsertCustomType_Oracle begin ------ InsertCustomType_Oracle end ---

SQL> select id, book_object from book_table;

ID BOOK_OBJECT(ISBN, TITLE, AUTHOR, EDITION)----- -----------------------------------------11111 BOOK('1111111111', 'MyTitle', 'Me', 12)22222 BOOK('2222222222', 'YourTitle', 'You', 10)77777 BOOK('1122334455', 'Strong Tigers', 'Bob Smith', 3)

Method 2 Solution: Using Custom Mapping: Writing a New Record

This example presents a solution that writes a UDT into a database by using the setObject()method.

This is the code before running the program:

SQL> select * from book_table;

ID BOOK_OBJECT(ISBN, TITLE, AUTHOR, EDITION)--- ------------------------------------------------------22 BOOK('2222222222', 'YourTitle', 'You', 10)11 BOOK('1111111111', 'MyTitle', 'Me', 12)

Compare the database before and after this solution to see how the custom mapping code isworking.

1 import java.util.*;2 import java.io.*;3 import java.sql.*;45 import jcb.db.*;6 import jcb.meta.*;78 public class InsertCustomType2_Oracle {9 public static Connection getConnection() throws Exception {10 String driver = "oracle.jdbc.driver.OracleDriver";

5203ch03.qxd 8/11/05 4:22 PM Page 140

Page 170: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 141

11 String url = "jdbc:oracle:thin:@localhost:1521:caspian";12 String username = "scott";13 String password = "tiger";14 Class.forName(driver); // load Oracle driver15 return DriverManager.getConnection(url, username, password);16 }1718 public static void main(String[] args) {19 System.out.println("--- InsertCustomType2_Oracle begin ---");20 if (args.length != 5) {21 System.out.println("usage: java InsertCustomType2_Oracle "+22 "<id> <isbn> <title> <author> <edition>");23 System.exit(1);24 }2526 String id = args[0];27 String isbn = args[1];28 String title = args[2];29 String author = args[3];30 int edition = Integer.parseInt(args[4]);3132 // create the Book object33 Book book = new Book(isbn, title, author, edition);34 book.printNoConversion();3536 Connection conn = null;37 PreparedStatement pstmt = null;38 try {39 conn = getConnection();40 // create type map41 java.util.Map map = conn.getTypeMap();42 System.out.println("map="+map);43 map.put("SCOTT.BOOK", Class.forName("Book"));44 System.out.println("map="+map);4546 String insert =47 "insert into book_table(ID, BOOK_OBJECT) values(?, ?)";48 pstmt = conn.prepareStatement(insert);49 pstmt.setString(1, id);50 pstmt.setObject(2, book);51 pstmt.executeUpdate();52 System.out.println("--- InsertCustomType2_Oracle end ---");53 }54 catch(Exception e){55 e.printStackTrace();56 System.exit(1);57 }58 finally {59 DatabaseUtil.close(pstmt);60 DatabaseUtil.close(conn);61 }62 }63 }

To run the sample program, use this code:

$ javac InsertCustomType2_Oracle.java$ java InsertCustomType2_Oracle 44 5556668888 "How to play tennis" "Borg" 3

5203ch03.qxd 8/11/05 4:22 PM Page 141

Page 171: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS142

--- InsertCustomType2_Oracle begin ------ Book print() raw data begin ---isbn=5556668888title=How to play tennisauthor=Borgedition=3--- Book print() raw data end ---map={} map={SCOTT.BOOK=class Book}--- InsertCustomType2_Oracle end ---

Here’s the database after running the program:

SQL> select * from book_table;

ID BOOK_OBJECT(ISBN, TITLE, AUTHOR, EDITION)--- ------------------------------------------------------22 BOOK('2222222222', 'YourTitle', 'You', 10)11 BOOK('1111111111', 'MyTitle', 'Me', 12)44 BOOK('5556668888', 'How to play tennis', 'Borg', 3)

This solution shows how to read an existing record using custom mapping.Here’s how to prepare the Oracle database:

$ sqlplus scott/tigerSQL*Plus: Release 10.1.0.2.0 - Production on Thu Oct 14 10:52:20 2004

SQL> desc scott.book;Name Null? Type----------------------------------------- -------- ------------------ISBN VARCHAR2(10)TITLE VARCHAR2(20)AUTHOR VARCHAR2(20)EDITION NUMBER(38)

SQL> desc book_table;Name Null? Type----------------------------------------- -------- ------------------ID VARCHAR2(5)BOOK_OBJECT BOOK

SQL> select * from book_table;

ID BOOK_OBJECT(ISBN, TITLE, AUTHOR, EDITION)----- ----------------------------------------------------------------22 BOOK('2222222222', 'YourTitle', 'You', 10)11 BOOK('1111111111', 'MyTitle', 'Me', 12)

For the following solution, I will present a line-by-line discussion of the sample program codeafter the code and explain how it uses custom mapping and how it changes the database:

1 import java.util.*;2 import java.io.*;3 import java.sql.*;45 import jcb.db.*;6 import jcb.meta.*;78 public class ReadCustomType_Oracle {910 public static Connection getConnection() throws Exception {

5203ch03.qxd 8/11/05 4:22 PM Page 142

Page 172: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 143

11 String driver = "oracle.jdbc.driver.OracleDriver";12 String url = "jdbc:oracle:thin:@localhost:1521:caspian";13 String username = "scott";14 String password = "tiger";15 Class.forName(driver); // load Oracle driver16 return DriverManager.getConnection(url, username, password);17 }1819 public static void main(String[] args) {20 System.out.println("--- ReadCustomType_Oracle begin ---");2122 if (args.length != 1) {23 System.out.println("usage: java ReadCustomType_Oracle <id>");24 System.exit(1);25 }2627 String id = args[0];28 System.out.println("input id="+id);2930 Connection conn = null;31 ResultSet rs = null;32 PreparedStatement pstmt = null;33 try {34 conn = getConnection();35 System.out.println("conn="+conn);36 java.util.Map map = conn.getTypeMap();37 System.out.println("map="+map);38 map.put("SCOTT.BOOK", Class.forName("Book"));39 System.out.println("map="+map);4041 String query =42 "SELECT id, book_object FROM book_table where id=?";43 pstmt = conn.prepareStatement(query);44 pstmt.setString(1, id);45 rs = pstmt.executeQuery();46 while(rs.next()) {47 String ID = rs.getString(1);48 System.out.println("ID="+ID);49 Object bookObject = rs.getObject(2);50 System.out.println("bookObject="+bookObject);51 Book book = (Book) bookObject;52 book.print();53 System.out.println("=======================");54 }55 System.out.println("--- ReadCustomType_Oracle end ---");56 }57 catch(Exception e){58 e.printStackTrace();59 System.exit(1);60 }61 finally {62 DatabaseUtil.close(rs);63 DatabaseUtil.close(pstmt);64 DatabaseUtil.close(conn);65 }66 }67 }

5203ch03.qxd 8/11/05 4:22 PM Page 143

Page 173: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS144

Here’s how to run the sample program using ReadCustomType_Oracle:

$ java ReadCustomType_Oracle 11111--- ReadCustomType_Oracle begin ---input id=11111conn=oracle.jdbc.driver.T4CConnection@ce5b1cmap={}map={SCOTT.BOOK=class Book}ID=11111bookObject=Book@1d64c37--- Book print() raw data begin ---isbn=1111111111title=MyTitleauthor=Meedition=12--- Book print() raw data end ---=======================--- ReadCustomType_Oracle end ---

$ java ReadCustomType_Oracle 22222--- ReadCustomType_Oracle begin ---input id=22222conn=oracle.jdbc.driver.T4CConnection@ce5b1cmap={}map={SCOTT.BOOK=class Book}ID=22222bookObject=Book@1d64c37--- Book print() raw data begin ---isbn=2222222222title=YourTitleauthor=Youedition=10--- Book print() raw data end ---=======================--- ReadCustomType_Oracle end ---

This breaks down the program:

Lines 1–6: Import required Java packages.

Lines 10–18: The getConnection() method returns a new database Connection object.

Lines 37–40: Here, you provide mapping information to the JDBC driver to map SCOTT.BOOK (theSQL data type) to the Java data type as the Book class. Note that when a java.sql.Connectionobject is first established, the default type map is empty; you must populate it to use any SQLto Java mapping functionality. When using Oracle database, SQL type names in the type mapmust be all uppercase, because that is how the Oracle database stores SQL names.

Lines 46–54: The variable bookObject is now an instance of the class Book, with each attributevalue being the current value of one of the fields of Book. Note that you have to cast the objectretrieved by getObject() to a Book object before assigning it to bookObject. When you retrievedata from Oracle 10g, it is in hexadecimal form. The method Book.print() converts data fromhexadecimal to regular string and Book.printNoConversion() prints the raw data (in hexadeci-mal format).

Lines 58–61: Handle database exceptions (such as SQLException) and other possible exceptions(such as Exception).

Lines 62–66: Release database resources such as Connection and Statement objects.

5203ch03.qxd 8/11/05 4:22 PM Page 144

Page 174: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 145

3-26. Is There Any Limit on the Number of Connections forJDBC?JDBC API has not set any restrictions on the maximum number of database connections; also, youcould say that JDBC drivers don’t have any scalability restrictions by themselves. JDBC providesa method (DatabaseMetaData.getMaxConnections()) that retrieves the maximum possible numberof concurrent connections to this database. The following code shows a snippet for using theDatabaseMetaData.getMaxConnections() method:

import jcb.util.DatabaseUtil;...Connection conn = null;try {

// get connection to an Oracle databaseconn = getConnection();System.out.println("conn="+conn);

// use connection ...DatabaseMetaData metadata = conn.getMetaData();if (metadata == null) {

System.out.println("vendor does not support metadata");}else {

int maxConnections = metadata.getMaxConnections();System.out.println("maxConnections="+maxConnections);

}}catch(Exception e){

// handle the exceptione.printStackTrace();

}finally {

// release database resourcesDatabaseUtil.close(conn);

}

DatabaseMetaData.getMaxConnections() returns the maximum number of active connectionspossible at one time; a result of zero means that there is no limit or the limit is not known.

You can limit the maximum number of connections created by a database server by using thefollowing methods:

• You can set control parameters, which will be read by database servers.

• You can limit the number of connections opened by the connection pool manager.

The following sections discuss how to set control parameters, which will be read by databaseservers such as Oracle and MySQL.

Limiting Max Connections: OracleUsing Oracle database, the maximum number of connections may be restricted by the number ofprocesses (in the init.ora file) on the server. For example, the init.ora file has a section for defin-ing processes and sessions:

########################## Processes and Sessions#########################processes=150

5203ch03.qxd 8/11/05 4:22 PM Page 145

Page 175: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS146

In general, a JDBC connection can consume anywhere from one to four file descriptors. Thesolution is to increase the per-process file descriptor limit.

To increase/decrease the maximum connections for Oracle, you need to do the following steps:

1. Stop the Oracle database server.

2. Edit the init.ora configuration file with processes=200.

3. Start the Oracle database server.

In Linux/Unix environments, if the Oracle database fails to restart properly, then it could bethat the processes number exceeds the Linux/Unix system parameter. You may need to update thesemaphores of the database server machine. Please refer to Oracle database installation and admin-istration references for the system parameter requirements.

Limiting Max Connections: MySQLThe MySQL database controls the number of simultaneous client connections allowed by themax_connections variable. To see all system variables, issue the following command:

mysql> SHOW VARIABLES;+-------------------------+--------------------+| Variable_name | Value |+-------------------------+--------------------+| back_log | 50 || basedir | C:\mysql\ || ... + ... || max_connections | 100 || ... | ... || version | 4.0.18-nt || wait_timeout | 28800 |+-------------------------+--------------------+126 rows in set (0.01 sec)

You can view system variables and their values by using the SHOW VARIABLES statement:

mysql> SHOW VARIABLES LIKE 'max_conn%';+--------------------+-------+| Variable_name | Value |+--------------------+-------+| max_connections | 100 || max_connect_errors | 10 |+--------------------+-------+2 rows in set (0.00 sec)

As you can see, the default value for the max_connections system variable is 100. Increasing thisvalue increases the number of file descriptors that mysqld requires.

To increase/decrease the max_connections variable for mysql, you need to follow these steps:

1. Stop the MySQL database server.

2. Edit the MySQL database server configuration file (my.ini file in Windows and my.cnf in Linux),and set the following entries:

[mysqld]set-variable = max_connections=300

3. Start the MySQL database server.

4. Check the max_connections variable by executing the following:

5203ch03.qxd 8/11/05 4:22 PM Page 146

Page 176: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 147

mysql> SHOW VARIABLES LIKE 'max_connections%';+--------------------+-------+| Variable_name | Value |+--------------------+-------+| max_connections | 300 |+--------------------+-------+1 rows in set (0.00 sec)

Also, you can set these variables (such as max_connections) at runtime by using the set command.MySQL’s set sets different types of variables that affect the operation of the server or your client. Youcan use it to assign values to user variables or system variables.

mysql> set global max_connections=200;Query OK, 0 rows affected (0.00 sec)

mysql> SHOW VARIABLES LIKE 'max_connections%';+-----------------+-------+| Variable_name | Value |+-----------------+-------+| max_connections | 200 |+-----------------+-------+1 row in set (0.00 sec)

3-27. How Do You Connect As SYSDBA or SYSOPER to an OracleDatabase?In the Oracle database, SYSDBA and SYSOPER are special system privileges that allow an administratorto perform basic database administration tasks, such as creating the database and performing instantstartup and shutdown.

According to Oracle’s JDBC documentation, the only way to do this is to use the java.util.Properties object when connecting, rather than specifying the username and password as strings.Put the username into the user property and the password into the password property. Then, putthe mode into the internal_logon property. For example:

String databaseURL = "jdbc:oracle:thin:@localhost:1521:scorpian";java.util.Properties props = new java.util.Properties();props.put("user", "scott");props.put("password", "tiger");props.put("internal_logon", "sysoper");java.sql.Connection conn = null;try {

conn = java.sql.DriverManager.getConnection(databaseURL, props);// success: you got the Connection object.

// use the Connection object for performing some useful tasks.}catch(SQLException e) {

// failure: handle the exception// could not get a Connection object.e.printStackTrace();

}finally {

// close Connection object...}

Please note that when connecting as SYSDBA or SYSOPER using the Thin driver, the Oracle RDBMSmust be configured to use a password file. See “Creating and Maintaining a Password File” in theOracle Database Administrator’s Guide.

5203ch03.qxd 8/11/05 4:22 PM Page 147

Page 177: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS148

3-28. How Do You Check MySQL’s/Oracle’s JDBC Installation?After you install the database and JDBC, it is time to check the JDBC installation. This means youshould be able to connect to the database and then select/retrieve some information.

SolutionYou can use the following sample JDBC program to check the MySQL or Oracle JDBC installation.You can also modify the code to support other databases.

I present this code with line numbers and offer a discussion of its lines immediately afterward.

1 import java.sql.*;23 import jcb.util.DatabaseUtil;4 import jcb.db.VeryBasicConnectionManager;56 public class CheckJDBCInstallation {7 /**8 * Test Validity of JDBC Installation9 * @param conn a JDBC Connection object10 * @param dbVendor db vendor {"oracle", "mysql" }11 * @return true if a given Connection object is12 * a valid one; otherwise return false.13 * @throws Exception Failed to determine if a given14 * connection is valid.15 */16 public static boolean isValidConnection(Connection conn,17 String dbVendor)18 throws Exception {1920 if (conn == null) {21 // null Connection object is not valid22 return false;23 }2425 if (conn.isClosed()) {26 // closed Connection object is not valid27 return false;28 }2930 // here you have a Connection object which is not null and31 // which is not closed, but it might be a defunct object32 // in order to determine whether it is a valid connection,33 // depends on the vendor of the database:34 //35 // for MySQL database:36 // you may use the Connection object37 // with query of "select 1"; if the38 // query returns the result, then it39 // is a valid Connection object.40 //41 // for Oracle database:42 // you may use the Connection object43 // with query of "select 1 from dual"; if44 // the query returns the result, then it45 // is a valid Connection object.

5203ch03.qxd 8/11/05 4:22 PM Page 148

Page 178: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 149

46 if (dbVendor.equalsIgnoreCase("mysql")) {47 return testConnection(conn, "select 1");48 }49 else if (dbVendor.equalsIgnoreCase("oracle")) {50 return testConnection(conn, "select 1 from dual");51 }52 else {53 return false;54 }5556 }5758 /**59 * Test Validity of a Connection60 * @param conn a JDBC Connection object61 * @param query a sql query to test against db connection62 * @return true if a given Connection object is a valid one;63 * otherwise return false.64 */65 public static boolean testConnection(Connection conn,66 String query) {67 ResultSet rs = null;68 Statement stmt = null;69 try {70 stmt = conn.createStatement();71 if (stmt == null) {72 return false;73 }7475 rs = stmt.executeQuery(query);76 if (rs == null) {77 return false;78 }7980 // Connection object is valid: you were able to81 // connect to the database and return something useful.82 if (rs.next()) {83 return true;84 }8586 // there is no hope any more for the validity87 // of the Connection object88 return false;89 }90 catch(Exception e) {91 // something went wrong: connection is bad92 return false;93 }94 finally {95 // close database resources96 DatabaseUtil.close(rs);97 DatabaseUtil.close(stmt);98 }99 }100101 public static void main(String[] args) {102 Connection conn = null;

5203ch03.qxd 8/11/05 4:22 PM Page 149

Page 179: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS150

103 try {104 System.out.println("-- CheckJDBCInstallation begin --");105 String dbVendor = args[0];106 // get connection to a database107 System.out.println("dbVendor="+dbVendor);108 conn = VeryBasicConnectionManager.getConnection(dbVendor);109 System.out.println("conn="+conn);110 System.out.println("valid connection = "+111 isValidConnection(conn, dbVendor));112 System.out.println("-- CheckJDBCInstallation end --");113 }114 catch(Exception e){115 // handle the exception116 e.printStackTrace();117 System.exit(1);118 }119 finally {120 // release database resources121 DatabaseUtil.close(conn);122 }123 }124 }

Discussing CheckJDBCInstallationThe following discussion breaks down the program:

Lines 1–4: Import the required classes and interfaces from the java.sql package.

Lines 16–56 : isValidConnection() accepts a Connection object and determines whether it isa valid Connection object. After making sure that the connection is not closed, it invokes thetestConnection() method, which retrieves the minimum information after connecting to thedatabase.

Lines 65–99 : The testConnection() method tests the validity of a Connection object by executinga minimal SELECT statement. The validity check statement for MySQL is select 1 (no need tomention a table name). The validity check statement may differ from vendor to vendor. Thevalidity check statement for Oracle is select 1 from dual. (In Oracle databases, dual is a tablethat is created by Oracle along with the data dictionary. It consists of exactly one column whosename is dummy and one record. The value of that record is X. The owner of dual is SYS, but the dualtable can be accessed by every user.)

Running CheckJDBCInstallation for the MySQL DatabaseTo run the program, use this code for MySQL:

$ mysql --user=root --password=root --default-character-set=utf8Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 3 to server version: 4.1.7-nt

mysql> exitBye

$ javac CheckJDBCInstallation.java$ java CheckJDBCInstallation mysql-- CheckJDBCInstallation begin --dbVendor=mysql

5203ch03.qxd 8/11/05 4:22 PM Page 150

Page 180: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 3 ■ MAKING DATABASE CONNECTIONS 151

conn=com.mysql.jdbc.Connection@8fce95valid connection = true-- CheckJDBCInstallation end --

Running CheckJDBCInstallation for the Oracle DatabaseTo run the program, use this code for Oracle:

$ sqlplus scott/tigerSQL*Plus: Release 10.1.0.2.0 - Production on Wed Oct 27 17:11:41 2004

SQL> exitDisconnected from Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 -

$ javac CheckJDBCInstallation.java$ java CheckJDBCInstallation oracle-- CheckJDBCInstallation begin --dbVendor=oracleconn=oracle.jdbc.driver.T4CConnection@341960valid connection = true-- CheckJDBCInstallation end --

5203ch03.qxd 8/11/05 4:22 PM Page 151

Page 181: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch03.qxd 8/11/05 4:22 PM Page 152

Page 182: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Making Database Connections UsingDataSource

Relativity teaches us the connection between the different descriptions of one and the samereality.

—Albert Einstein

In this chapter, you will learn how to make database connections using JDBC’s DataSource object.The purpose of this chapter is to provide snippets, reusable code samples, and methods that dealwith the Connection objects using the javax.sql.DataSource interface. When writing this chapter,I relied on JDK 1.4 and the final release of the JDBC 3.0 specification.

This chapter’s focus will be on answering the following question: how do you obtaina java.sql.Connection object from javax.sql.DataSource objects? I can present this question inanother way: what are the connection options? This chapter will answer these questions, and foreach case I will provide working code.

To select data from tables/views, update columns in tables, create a new table in a database,or do anything useful with any database, you need a database connection (in JDBC, this is calledjava.sql.Connection). Typically, database access in any environment starts with the connection.

4-1. How Do You Create Connection Using a DataSource Object?DataSource (defined in the javax.sql package) is an abstraction layer for Java database applications.Database applications may use a JNDI context to find DataSource attributes that are configured onthe deployment server. You can obtain a DataSource object in two ways:

• Using JNDI

• Without using JNDI

JNDI is an API for accessing different kinds of naming and directory services. JNDI is not specificto a particular naming or directory service; it can be used to access many different kinds of systemsincluding file systems, Common Object Request Broker Architecture (CORBA), Java Remote MethodInvocation (RMI), and Enterprise JavaBeans (EJB), as well as directory services such as LightweightDirectory Access Protocol (LDAP) and Network Information Service (NIS). Although you can useJDBC to access a set of relational databases, you can use JNDI to access a set of naming and directoryservices.

You will look at both of these options in this chapter. From a portability point of view, obtain-ing a DataSource interface using JNDI is more portable than not using JNDI, according to http://java.sun.com/products/jdbc/articles/package2.html:

153

C H A P T E R 4

■ ■ ■

5203ch04.qxd 8/11/05 4:23 PM Page 153

vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
Page 183: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE154

The DataSource interface provides an alternative to the DriverManager class for makinga connection to a data source. Using a DataSource implementation is better for two impor-tant reasons: it makes code more portable, and it makes code easier to maintain.A DataSource object represents a real-world data source. Depending on how it is imple-mented, the data source can be anything from a relational database to a spreadsheet or a filein tabular format.When a DataSource object has been registered with a JNDI naming service,an application can retrieve it from the naming service and use it to make a connection to thedata source it represents.

The following snippet shows how to retrieve the DataSource object associated with the logicalname jdbc/InventoryDB and then use it to get a connection. The first two lines use the JNDI API toget the DataSource object; the third line uses JDBC API to get the connection.

Context ctx = new InitialContext();DataSource ds = (DataSource) ctx.lookup("jdbc/InventoryDB");Connection con = ds.getConnection("myUserName", "myPassword");

The JDK 1.4 documentation defines javax.sql.DataSource as follows:

A factory for connections to the physical data source that this DataSource object represents.An alternative to the DriverManager facility, a DataSource object is the preferred means ofgetting a connection. An object that implements the DataSource interface will typically beregistered with a naming service based on the JNDI API.

The DataSource interface is implemented by a driver vendor. Three types of implementations exist:

• Basic implementation: Produces a standard Connection object.

• Connection pooling implementation: Produces a Connection object that will automaticallyparticipate in connection pooling. This implementation works with a middle-tier connec-tion pooling manager.

• Distributed transaction implementation: Produces a Connection object that may be used fordistributed transactions and almost always participates in connection pooling. This imple-mentation works with a middle-tier transaction manager and almost always with a connectionpooling manager.

A DataSource object has properties that you can modify when necessary. For example, if thedata source moves to a different server, you can change the property for the server. The benefit isthat because the data source’s properties can be changed, any code accessing that data source doesnot need to be changed.

A driver that is accessed via a DataSource object does not register itself with the DriverManagerfacility. Rather, a DataSource object is retrieved through a lookup operation and then used to createa Connection object. With a basic implementation, the connection obtained through a DataSourceobject is identical to a connection obtained through the DriverManager facility.

This is according to Struts (http://struts.apache.org/faqs/database.html):

As a rule, you should always use a connection pool to access a database. The DataSourceinterface is the preferred way to implement a connection pool today. Many containers anddatabase systems now bundle a DataSource implementation that you can use. Most often,the DataSource is made available through JNDI. The JNDI approach makes it easy for yourbusiness classes to access the DataSource without worrying about who set it up.

5203ch04.qxd 8/11/05 4:23 PM Page 154

vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
Page 184: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE 155

Figure 4-1. Life cycle of a DataSource object

When connecting to a data source (a relational database such as MySQL or Oracle) usinga DataSource object registered with a JNDI naming service rather than using the DriverManager facil-ity, you get three benefits:

• It makes code more portable.

• It makes code much easier to maintain.

• You get the benefit of connection pooling.

For details on these benefits, refer to JDBC API Tutorial and Reference, Third Edition (Addison-Wesley, 2003) by Maydene Fisher, Jon Ellis, and Jonathan Bruce.

JDBC 2.0 introduced a DataSource interface that eliminates connection URLs and driver namesin your Java applications. DataSource enables you to register (using JNDI API) instances of DataSourcewith a unique name; then, other applications can retrieve the registered DataSource using the uniquename. A DataSource object provides a new method for JDBC clients to obtain a DBMS connection(represented as java.sql.Connection). A DataSource object is usually created, deployed (that is, reg-istered), located (lookup operation), and managed separately from the Java applications that use it.

Figure 4-1 shows the life cycle of a DataSource object.

In this figure, note that the Directory and Naming Service item is JNDI-enabled and can bind/register and hold any number of DataSource objects. Most databases can use CORBA-based namingand directory services, but in order not to tie yourself into a specific implementation of JNDI, in thischapter you will see how to use a file system–based reference implementation of a JNDI SPI driverfrom JavaSoft. In real production applications, you should select a commercially available namingand directory service product (such as Sun’s Directory Server, Novell’s Directory Server, and so on).

One Java application (Java Application 1) can create a data source (using some data sourceconfiguration from an XML file, a relational database, or a file-based system). Also, Java Application 1can register (or bind) and manage the created data source. In registering a data source, you have toassociate the data source with a unique key. Once the data source is registered, it is accessible toother applications (such as Java Application 2). Java Application 2 can get registered data sources

5203ch04.qxd 8/11/05 4:23 PM Page 155

vikrant
Highlight
vikrant
Highlight
Page 185: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE156

using a lookup method by providing the unique key. Some vendors (such as BEA’s WebLogic andIBM’s WebSphere) provide tools for deploying DataSource objects and then provide browsing/lookup operations to get the DataSource objects.

4-2. How Do You Create a DataSource Object?To create a DataSource object, you define it with a vendor-specific Java class, which implements theDataSource interface. For the sake of this discussion, assume that DBVendorDataSource (which isa vendor-specific Java class) implements the DataSource interface. Then you can create a DataSourceobject by writing the following code:

//// generic solution//DBVendorDataSource vendorDataSource = new DBVendorDataSource();vendorDataSource.setServerName("saratoga");vendorDataSource.setDatabaseName("payrollDatabase");vendorDataSource.setDescription("the data source for payroll");//// you can set other attributes by using vendorDataSource.setXXX(...)//

// now cast it to DataSourceDataSource payrollDS = (DataSource) vendorDataSource;

4-3. How Do You Create a DataSource Object Using Oracle?To create a DataSource object using the Oracle database, you should use the (vendor-specific)OracleDataSource class (defined in the oracle.jdbc.pool package and available from Oracle). Youcan create a DataSource object by writing the following code:

import oracle.jdbc.pool.OracleDataSource;import javax.sql.DataSource;...OracleDataSource oracleDataSource = new OracleDataSource();oracleDataSource.setServerName("saratoga");oracleDataSource.setDatabaseName("payrollDatabase");oracleDataSource.setDescription("the data source for payroll");//// you can set other attributes by// invoking oracleDataSource.setXXX(...)//

// now cast it to DataSourceDataSource payrollDS = (DataSource) oracleDataSource;

4-4. How Do You Create a DataSource Object Using MySQL?To create a DataSource object using the MySQL database, you should use the (vendor-specific)MySQLDataSource class. You can create a DataSource object by writing the following code:

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import javax.sql.DataSource;...MysqlDataSource mysqlDataSource = new MysqlDataSource();mysqlDataSource.setServerName("saratoga");

5203ch04.qxd 8/11/05 4:23 PM Page 156

vikrant
Highlight
Page 186: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE 157

mysqlDataSource.setDatabaseName("payrollDatabase");mysqlDataSource.setDescription("the data source for payroll");//// NOTE: you can set other attributes by// invoking mysqlDataSource.setXXX( )//

// now cast it to DataSourceDataSource payrollDS = (DataSource) mysqlDataSource;

4-5. How Do You Create a DataSource Object Using a RelationalDatabase (Oracle/MySQL)?To create a DataSource object using an Oracle, MySQL, Sybase, or DB2 database, you introducea vendor parameter. (The vendor parameter uniquely identifies a specific database such as Oracle,MySQL, or Sybase.) Depending on the vendor parameter, you apply a different implementationclass for creating a DataSource object. For example, if vendor equals oracle, then you select theOracleDataSource class, and if vendor equals mysql, then you select the MysqlDataSource class; oth-erwise, you return null. (In addition, you can modify this to support more than two vendors.)

Invoking getDataSource() with a Specific User/PasswordThis shows how to invoke getDataSource() with a specific user/password:

/*** This method creates a DataSource object with a* specific username/password. If the vendor parameter* is not specified, then it returns null.** @param vendor the vendor parameter: "oracle", "mysql",**/public static javax.sql.DataSource getDataSource(String vendor,String user,String password,String databaseName,String driverType,String networkProtocol,int portNumber,String serverName) throws SQLException {

if (vendor.equals("oracle")) {// create Oracle's DataSourceOracleDataSource ods = new OracleDataSource(); ods.setUser(user);ods.setPassword(password);ods.setDatabaseName(databaseName);ods.setDriverType(driverType);ods.setNetworkProtocol(networkProtocol);ods.setPortNumber(portNumber);ods.setServerName(serverName);return ods;

}else if (vendor.equals("mysql")) {

// create MySQL's DataSource

5203ch04.qxd 8/11/05 4:23 PM Page 157

vikrant
Highlight
vikrant
Highlight
Page 187: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE158

MysqlDataSource mds = new MysqlDataSource();mds.setUser(user);mds.setPassword(password);mds.setDatabaseName(databaseName);//mds.setDriverType(driverType);//mds.setNetworkProtocol(networkProtocol);mds.setPortNumber(portNumber);mds.setServerName(serverName);return mds;

}else {

return null;}

}

Viewing the getDataSource() Results with a Specific User/PasswordThis code shows the result:

public static void main(String[] args)throws SQLException, javax.naming.NamingException {// create an Oracle DataSource with// specific username/passwordDataSource ods = getDataSource("oracle",

"system", "gozal", "scorpian", "thin","tcp", 1521, "localhost");

Connection oraConn = ods.getConnection();System.out.println("oraConn="+oraConn);

// create a MySQL DataSource with// specific username/passwordDataSource mds = getDataSource("mysql",

"root", "root", "tiger","", "", 3306, "localhost");

Connection myConn = mds.getConnection();System.out.println("myConn="+myConn);

}

Invoking getDataSource() Without a Specific User/PasswordThis shows how to invoke getDataSource() without a specific user/password:

/*** This method creates a DataSource object without a* specific username/password. If the vendor parameter* is not specified, then it returns null. When client* uses this DataSource, it has to pass username/password.** @param vendor the vendor parameter: "oracle", "mysql",**/public static DataSource getDataSource(String vendor,String databaseName,

5203ch04.qxd 8/11/05 4:23 PM Page 158

vikrant
Highlight
vikrant
Highlight
Page 188: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE 159

String driverType,String networkProtocol,int portNumber,String serverName) throws SQLException {

if (vendor.equals("oracle")) {// create Oracle's DataSourceOracleDataSource ods = new OracleDataSource();ods.setDatabaseName(databaseName);ods.setDriverType(driverType);ods.setNetworkProtocol(networkProtocol);ods.setPortNumber(portNumber);ods.setServerName(serverName);return ods;

}else if (vendor.equals("mysql")) {

// create MySQL's DataSourceMysqlDataSource mds = new MysqlDataSource();mds.setDatabaseName(databaseName);//mds.setDriverType(driverType);//mds.setNetworkProtocol(networkProtocol);mds.setPortNumber(portNumber);mds.setServerName(serverName);return mds;

}else {return null;

}}

Viewing the getDataSource() Results Without a Specific User/PasswordThis code shows the result:

public static void main(String[] args)throws SQLException, javax.naming.NamingException {// create an Oracle DataSource with// specific username/passwordDataSource ods = getDataSource("oracle","scorpian", "thin", "tcp", 1521, "localhost");

String user = "system";String password = "gozal";Connection oraConn = ods.getConnection(user, password);System.out.println("oraConn="+oraConn);

// create a MySQL DataSource with// specific username/passwordDataSource mds = getDataSource("mysql","root", "root", "tiger", "", "", 3306, "localhost");

String user2 = "root";String password2 = "root";Connection myConn = mds.getConnection(user2, password2);System.out.println("myConn="+myConn);

}

5203ch04.qxd 8/11/05 4:23 PM Page 159

vikrant
Highlight
vikrant
Highlight
Page 189: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE160

4-6. How Do You Create a DataSource Object Using a DataSourceFactory?You can create DataSource objects by using a data source factory (DSF) object.

First, you create a DSF object; second, you use the DSF object to create DataSource objects. Tocreate a DSF object, you have at least two options: you can write your own custom code or usea third-party package. In this section, I will show how to use the package org.apache.torque.dsfactoryfrom Apache. (For details about this package, please see http://db.apache.org/torque-32/.) Thispackage has a DataSourceFactory interface implemented by several classes (JndiDataSourceFactory,PerUserPoolDataSourceFactory, and SharedPoolDataSourceFactory). You may provide your ownimplementation class as well for DataSourceFactory. DataSourceFactory is defined as follows:

package org.apache.torque.dsfactory;import org.apache.commons.configuration.Configuration;

//A factory that returns a DataSource.public interface DataSourceFactory {

// returns the DataSource configured by the factory.public javax.sql.DataSource getDataSource()

throws TorqueException;// initialize the factory.public void initialize(Configuration configuration)

throws TorqueException;}

You can use one of the implementation classes to create a DataSourceFactory and then use thecreated DSF to create a DataSource object:

JndiDataSourceFactory jndiDSF = new JndiDataSourceFactory();// create a DataSource configuration objectConfiguration config = getConfiguration(<your-datasource-properties>);// first you need to initializejndiDSF.initialize(config);...// get a DataSource object:DataSource ds = jndiDSF.getDataSource();...// now use the DataSource object to create Connection objects:Connection conn = ds.getConnection();

4-7. What Are the DataSource Properties?A DataSource object has several properties that identify and describe the real-world data source(such as an Oracle database, a MySQL database, and so on) that the object represents. These prop-erties include information such as the driver type, the URL of the database server, the name of thedatabase, the network protocol to use to communicate with the database server, and so on.

DataSource properties follow the JavaBeans design pattern and are usually set when a DataSourceobject is created and deployed. The JDBC API specifies a standard set of properties and a standardname for each property. Table 4-1 describes the standard name, the data type, and a description foreach of the standard properties. According to the JDBC specification, a DataSource implementationdoes not have to support all of these properties.

5203ch04.qxd 8/11/05 4:23 PM Page 160

vikrant
Highlight
Page 190: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE 161

Table 4-1. Standard DataSource Properties

Property Name Type Description

databaseName String The name of a particular database on a server

dataSourceName String The logical name for the underlying XADataSource orConnectionPoolDataSource object; used only when pooling ofconnections or distributed transactions are implemented

description String A description of this data source

networkProtocol String The network protocol used to communicate with the server

password String The user’s database password

portNumber Int The port number where a server is listening for requests

roleName String The initial SQL role name

serverName String The database server name

user String The user’s account name

If a DataSource object supports a property, it must supply getter (get<PropertyName>) andsetter (set<PropertyName>) methods for it. The following code fragment illustrates the methods thata DataSource object, ds, would need to include if it supports, for example, the property description:

DataSource ds = <get-a-DataSource object>;ds.setDescription("This db server is for payroll processing.");String description = ds.getDescription();

4-8. How Do You Deploy/Register a DataSource?Assume that you have created a DataSource object and you want to deploy/register it asjdbc/PayrollDataSource. Using JNDI naming services, you can bind/register a data source witha JNDI naming service.

The naming service provides distributed naming support and can be implemented in manydifferent ways:

• File-based systems

• java.naming.factory.initial (for example, com.sun.jndi.fscontext.RefFSContextFactory)

• java.naming.provider.url (for example, file:c:\\jdbcDataSource)

• Directory-based systems (such as LDAP)

• java.naming.factory.initial (for example, com.ibm.websphere.naming.WsnInitialContextFactory)

• java.naming.provider.url (for example, java:comp/env/jdbc/SampleDB)

• CORBA-based systems

• NIS-based systems

• DNS-based systems

• RMI-based systems

• java.naming.factory.initial (for example, com.sun.jndi.rmi.registry.RegistryContextFactory)

• java.naming.provider.url (for example, rmi://localhost:1099)

5203ch04.qxd 8/11/05 4:23 PM Page 161

vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
vikrant
Highlight
Page 191: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE162

4-9. How Do You Use a File-Based System for Registeringa DataSource Object?To use the file system–based JNDI, you need to do the following:

1. Download fscontext1_*.zip from http://java.sun.com/products/jndi. (The asterisk refersto a version number of the software bundle.)

2. Extract providerutil.jar and fscontext.jar.

3. Include in your CLASSPATH environment variable the providerutil.jar and fscontext.jarfiles extracted from the fscontext1_*.zip file. (Also, use a full pathname for your JAR files.)

In this way, you can deploy a DataSource object by using the following code listings. Specifically,this is the getContext() method:

private static Context getContext(String classFactory,String providerURL)

throws javax.naming.NamingException {//// Set up environment for creating initial context//Hashtable env = new Hashtable();env.put(Context.INITIAL_CONTEXT_FACTORY, classFactory);env.put(Context.PROVIDER_URL, providerURL);Context context = new InitialContext(env);return context;

}

private static Context getFileSystemContext(String directoryName)throws javax.naming.NamingException {//// Set up environment for creating initial context//Hashtable env = new Hashtable();env.put(Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");env.put(Context.PROVIDER_URL, "file:" + directoryName);Context context = new InitialContext(env);return context;

}

This is the deployDataSource() method:

private static void deployDataSource(Context context,String jndiName,DataSource ds)

throws javax.naming.NamingException{//// register the data source under the jndiName using// the provided context//context.bind(jndiName, ds);

}

And this code shows how to deploy/register the DataSource object:

// dataSourceName is a unique name to identify the data source.String jndiDataSourceName = "jdbc/PayrollDataSource";

5203ch04.qxd 8/11/05 4:23 PM Page 162

Page 192: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE 163

// create an Oracle DataSource with// specific username/passwordDataSource ods = createDataSource("oracle",

"system", "gozal", "scorpian", "thin","tcp", 1521, "localhost");

// now bind it using JNDIString directoryName = "c:\\jdbcDataSource";Context context = getFileSystemContext(directoryName);

// register/bind DataSourcedeployDataSource(context, jndiDataSourceName, ods);

What happens when you deploy a data source using the file system? Under the directory name(c:\jdbcDataSource), it creates a file called .bindings, which has the following content (the contentof the .bindings file has been formatted to fit the page):

#This file is used by the JNDI FSContext.#Sun Feb 02 00:20:35 PST 2003jdbc/MyDataSource/RefAddr/3/Encoding=Stringjdbc/MyDataSource/RefAddr/5/Type=databaseNamejdbc/MyDataSource/RefAddr/6/Type=networkProtocoljdbc/MyDataSource/FactoryName=oracle.jdbc.pool.OracleDataSourceFactoryjdbc/MyDataSource/RefAddr/1/Encoding=Stringjdbc/MyDataSource/RefAddr/6/Encoding=Stringjdbc/MyDataSource/RefAddr/7/Type=portNumberjdbc/MyDataSource/RefAddr/6/Content=tcpjdbc/MyDataSource/RefAddr/0/Type=urljdbc/MyDataSource/RefAddr/5/Content=scorpianjdbc/MyDataSource/RefAddr/4/Encoding=Stringjdbc/MyDataSource/RefAddr/3/Content=thinjdbc/MyDataSource/RefAddr/1/Content=systemjdbc/MyDataSource/ClassName=oracle.jdbc.pool.OracleDataSourcejdbc/MyDataSource/RefAddr/1/Type=userNamejdbc/MyDataSource/RefAddr/2/Encoding=Stringjdbc/MyDataSource/RefAddr/7/Encoding=Stringjdbc/MyDataSource/RefAddr/2/Type=passWordjdbc/MyDataSource/RefAddr/3/Type=driverTypejdbc/MyDataSource/RefAddr/0/Encoding=Stringjdbc/MyDataSource/RefAddr/5/Encoding=Stringjdbc/MyDataSource/RefAddr/7/Content=1521jdbc/MyDataSource/RefAddr/4/Type=serverNamejdbc/MyDataSource/RefAddr/4/Content=localhostjdbc/MyDataSource/RefAddr/2/Content=gozaljdbc/MyDataSource/RefAddr/0/Content=jdbc\:oracle\:thin\:

@(DESCRIPTION\=(ADDRESS\=(PROTOCOL\=tcp)(PORT\=1521)(HOST\=localhost))(CONNECT_DATA\=(SID\=scorpian)))

4-10. What Is the Problem with File-Based DataSource Objects?The problem with file-based DataSource objects is the compromise on security. Since the passwordis not encrypted (in any way), this is a security hole; therefore, it is not a viable choice in productionsystems. In production systems, you should use production-ready “directory-based” products. (Inthese cases, the password can be protected by groups/roles privileges.) For example, the passwordin file-based systems can be viewed as clear text if you have access to the file system; see the follow-ing password line—no encryption is used at all:

5203ch04.qxd 8/11/05 4:23 PM Page 163

Page 193: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE164

#This file is used by the JNDI FSContext.#Sun Feb 02 00:20:35 PST 2003jdbc/MyDataSource/RefAddr/3/Encoding=Stringjdbc/MyDataSource/RefAddr/5/Type=databaseName...jdbc/MyDataSource/RefAddr/2/Type=passWord...jdbc/MyDataSource/RefAddr/2/Content=gozal

4-11. How Do You Retrieve a Deployed/Registered DataSource?Assume the registered name of a data source is jdbc/PayrollDataSource. Using JNDI , you can lookup (search) a data source with a JNDI naming service. In this way, you can access a DataSourceobject by using code that looks like this:

String dataSourceName = "jdbc/PayrollDataSource";String user = "dbUser":String password = "dbPassword";Context context = new InitialContext();DataSource ds = (DataSource) context.lookup(dataSourceName);Connection conn = ds.getConnection(user, password);//// or//// if a DataSource has been created (before registration)// with user and password attributes, then you can get the// connection without passing the user/password.Connection con = ds.getConnection();

You can use a Java method to accomplish the task:

public static DataSource getDataSource(String dataSourceName)throws Exception {if (dataSourceName == null) {

return null;}Context context = new InitialContext();DataSource ds = (DataSource) context.lookup(dataSourceName);return ds;

}

Then you can use the getDataSource() method for getting Connection objects:

String dataSourceName = "jdbc/PayrollDataSource";String user = "dbUser":String password = "dbPassword";DataSource ds = null;try {

ds = getDataSource(dataSourceName);}catch(Exception e) {

// handle the exception// DataSource is not available/registered

}

if (ds != null) {Connection conn = ds.getConnection(user, password);//

5203ch04.qxd 8/11/05 4:23 PM Page 164

Page 194: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE 165

// or//// if a DataSource has been created (before registration)// with user and password attributes, then you can get the// connection without passing the user/password.// Connection conn = ds.getConnection();

}

4-12. How Do You Obtain a Connection with the DataSourceWithout Using JNDI?A DataSource object provides a portable way for JDBC clients to obtain a DBMS connection. To cre-ate a DataSource object, you can use a vendor-specific class (such as OracleDataSource from Oracleor MysqlDataSource from MySQL), and then you can cast it to a DataSource object.

Oracle ExampleUsing the OracleDataSource class, you can create an instance of a DataSource object like so:

String databaseName = "scorpian";String driverType = "thin";String networkProtocol = "tcp";int portNumber = 1521;String serverName = "localhost";...OracleDataSource ods = new OracleDataSource();ods.setDatabaseName(databaseName);

ods.setDriverType(driverType);ods.setNetworkProtocol(networkProtocol);ods.setPortNumber(portNumber);ods.setServerName(serverName);DataSource oracleDS = (DataSource) ods;

Once you have created an instance of a DataSource object, you can use the getConnection()method:

String username = "system";String password = "gozal";java.sql.Connection connection = null;try {

connection = oracleDS.getConnection(username, password);}catch(SQLException e) {

// database access error occurred// handle the exceptione.printStackTrace();...

}

MySQL ExampleUsing the MysqlDataSource class, you can create an instance of a DataSource object:

String databaseName = "tiger";String networkProtocol = "tcp";int portNumber = 3306;String serverName = "localhost";

5203ch04.qxd 8/11/05 4:23 PM Page 165

Page 195: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE166

...MysqlDataSource mds = new MysqlDataSource();mds.setDatabaseName(databaseName);mds.setNetworkProtocol(networkProtocol);mds.setPortNumber(portNumber);mds.setServerName(serverName);DataSource mysqlDS = (DataSource) mds;

Once you have created an instance of a DataSource object, you can use the getConnection()method:

String username = "root";String password = "root";java.sql.Connection connection = null;try {

connection = mysqlDS.getConnection(username, password);}catch(SQLException e) {

// database access error occurred// handle the exceptione.printStackTrace();...

}

4-13. How Do You Obtain a Connection with the DataSourceUsing JNDI?The steps for obtaining and using a connection with the javax.sql package API differ slightly fromthose using the java.sql package core API. Using the javax.sql package, you access a relationaldatabase as follows:

1. Retrieve a javax.sql.DataSource object from the JNDI naming service.

2. Obtain a Connection object from the data source. (If a data source is created with a user-name and password, then you can get a connection without passing a username andpassword; otherwise, you must pass the username and password.)

3. Using the Connection object (java.sql.Connection), send SQL queries or updates to thedatabase management system.

4. Process the results (returned as ResultSet objects).

Figure 4-2 shows DataSource and JNDI configuration.

5203ch04.qxd 8/11/05 4:23 PM Page 166

vikrant
Highlight
vikrant
Highlight
Page 196: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE 167

Figure 4-2. DataSource and JNDI configuration

DataSource provides two methods for obtaining a java.sql.Connection object:

• Connection getConnection(): Attempts to establish a connection with the data source thatthis DataSource object represents

• Connection getConnection(String username, String password): Attempts to establish a con-nection with the data source that this DataSource object represents

The following example shows these steps (assuming that the name of the DataSource object isjava:comp/env/jdbc/employeeDB):

import jcb.util.DatabaseUtil;...java.sql.Connection conn = null;java.sql.Statement stmt = null;java.sql.ResultSet rs = null;try {

// Retrieve a DataSource through the JNDI naming servicejava.util.Properties parms = new java.util.Properties();parms.setProperty(Context.INITIAL_CONTEXT_FACTORY,

"com.ibm.websphere.naming.WsnInitialContextFactory");

// Create the Initial Naming Contextjavax.naming.Context context = new javax.naming.InitialContext(parms);

// Look up through the naming service to retrieve a DataSource objectjavax.sql.DataSource ds = (javax.sql.DataSource)

context.lookup("java:comp/env/jdbc/employeeDB");

//Obtain a connection from the DataSource; here you// are assuming that the DataSource is created with// the required username and password.conn = ds.getConnection();

// query the databasestmt = conn.createStatement();rs = stmt.executeQuery("SELECT id, lastname FROM employees");

5203ch04.qxd 8/11/05 4:23 PM Page 167

vikrant
Highlight
vikrant
Highlight
Page 197: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

// process the resultswhile (rs.next()) {

String id = rs.getString("id");String lastname = rs.getString("lastname");// process and work with results (id, lastname)

}}catch (SQLException e) {

// handle SQLExceptione.printStackTrace();

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

CHAPTER 4 ■ MAKING DATABASE CONNECTIONS USING DATASOURCE168

5203ch04.qxd 8/11/05 4:23 PM Page 168

vikrant
Highlight
Page 198: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Exploring the ResultSet Interface

The purpose of this chapter is to provide Java snippets, reusable code samples, classes, and meth-ods that deal with the ResultSet interface (defined in java.sql.package). When writing this chapter,I relied on JDK 1.4 and the final release of the JDBC 3.0 specification. This chapter will focus onexplaining the ResultSet interface and retrieving data.

5-1. What Is a ResultSet?The ResultSet interface (not a class) is defined in the java.sql package as java.sql.ResultSet. (Inthis chapter, I will use ResultSet instead of the full name java.sql.ResultSet.) The ResultSet inter-face encapsulates the results of a SQL query (such as a select id, name from employees). You mightsay that the ResultSet and Connection interfaces are the most important objects for getting the resultof a SQL query and connecting to relational (SQL-based) databases, respectively.

The java.sql package contains the core JDBC API, and the ResultSet interface is defined inthis package. According to the JDBC 3.0 specification, “the ResultSet interface provides methodsfor retrieving and manipulating the results of executed queries.” A SQL query returns a ResultSetcontaining the requested data (as a set of rows of data, depending on the submitted SQL query),which is retrieved by type. The ResultSetMetaData interface provides information about a ResultSet.

But what, really, is a ResultSet? What happens when you execute the following query?

Connection conn = <get-a-connection-object>;Statement stmt = conn.createStatement();String query = "SELECT column_1, column_2 FROM mytable";ResultSet rs = stmt.executeQuery(query);

Does it load the table in random access memory (RAM)? For example, when I do this:

String column1 = rs.getString(1)

does it give access to the RAM or to the DBMS? All of these answers depend on the type of your JDBCdriver (that is, on the JDBC driver’s implementation) that you are using. In general, JDBC drivers donot actually execute the statement until next() is called. With each iteration that you call with next(),the next record from the result set is pulled over the network. Some JDBC drivers fetch more thanone record at a time to limit network traffic (which can improve your database application’s perfor-mance). If your JDBC driver supports setting the fetch size, you can modify the number of recordsfetched with the setFetchSize() method. (Note that all drivers do not support this feature.)

So you can understand ResultSet, I will show how to define a simple table (in MySQL andOracle), then populate it, and finally retrieve some records from it.

169

C H A P T E R 5

■ ■ ■

5203ch05.qxd 8/11/05 4:24 PM Page 169

Page 199: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE170

Setting Up the Oracle DatabaseThe following code shows how to set up the Oracle database:

SQL> create table employees (2 id varchar(10) not null primary key,3 name varchar(20) not null,4 age int5 );

Table created.

SQL> desc employees;Name Null? Type---------------- -------- ------------ID NOT NULL VARCHAR2(10)NAME NOT NULL VARCHAR2(20)AGE NUMBER(38)

SQL> insert into employees(id, name, age) values('11', 'Alex Smith', 25);SQL> insert into employees(id, name, age) values('22', 'Don Knuth', 65);SQL> insert into employees(id, name, age) values('33', 'Mary Kent', 35);SQL> insert into employees(id, name, age) values('44', 'Monica Seles', 30);SQL> insert into employees(id, name) values('99', 'Alex Edison');SQL> commit;Commit complete.

SQL> select id, name, age from employees;

ID NAME AGE---------- -------------------- ----------11 Alex Smith 2522 Don Knuth 6533 Mary Kent 3544 Monica Seles 3099 Alex Edison

Setting Up the MySQL DatabaseThe following code shows how to set up the MySQL database:

create table employees (id varchar(10) not null primary key,name varchar(20) not null,age int

);

insert into employees(id, name, age) values('11', 'Alex Smith', 25);insert into employees(id, name, age) values('22', 'Don Knuth', 65);insert into employees(id, name, age) values('33', 'Mary Kent', 35);insert into employees(id, name, age) values('44', 'Monica Seles', 30);insert into employees(id, name) values('99', 'Alex Edison');mysql> select * from employees;

5203ch05.qxd 8/11/05 4:24 PM Page 170

Page 200: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

+----+--------------+------+| id | name | age |+----+--------------+------+| 11 | Alex Smith | 25 || 22 | Don Knuth | 65 || 33 | Mary Kent | 35 || 44 | Monica Seles | 30 || 99 | Alex Edison | NULL |+----+--------------+------+Five Rows in a Set (0.00 Sec)

SolutionI will now provide a simple Java class to demonstrate how to use ResultSet by querying the employeestable and retrieving employee information from a database. Note that the age column can acceptnull values as well; because of this, after getting the value of age from a ResultSet, you check (byinvoking ResultSet.wasNull()) to see if it is a null value.

import java.sql.*;

import jcb.db.VeryBasicConnectionManager;import jcb.util.DatabaseUtil;

public class DemoResultSet {

public static void main(String[] args) {Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

System.out.println("--DemoResultSet begin--");String dbVendor = args[0]; // { "mysql", "oracle" }conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "select id, name, age from employees";

// create a statementstmt = conn.createStatement();

// execute query and return result as a ResultSetrs = stmt.executeQuery(query);

// extract data from the ResultSetwhile (rs.next()) {

String id = rs.getString(1);System.out.println("id="+id);String name = rs.getString(2);System.out.println("name="+name);// age might be null (according to schema)int age = rs.getInt(3);if (rs.wasNull()) {

System.out.println("age=null");}

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 171

5203ch05.qxd 8/11/05 4:24 PM Page 171

Page 201: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE172

else {System.out.println("age="+age);

}System.out.println("---------------");

}System.out.println("--DemoResultSet end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

The important methods and concepts of this solution are as follows:

• The getConnection() method gets a database connection for the sample Oracle database.

• createStatement() creates a Statement object for sending SQL statements to the database.

• executeQuery() is used for Statement objects that return a ResultSet, which is basicallya SELECT statement.

• next() moves the cursor down one row from its current position. The first next() sets thecursor on the first row; the next() method enables you to iterate through all the recordsretrieved.

• getString(int columnIndex) retrieves the value of the designated column in the current rowof this ResultSet object as a String in the Java programming language. You use getString(1)and getString(2) to get the id and name, respectively. (In the query, id is defined in the firstcolumn, and name is defined in the second column.) Note that when using column index, thefirst column is 1, the second is 2, and so on.

• getInt(int columnIndex) retrieves the value of the designated column in the current row ofthis ResultSet object as an “int” in the Java programming language. You use getInt(3) to getthe age of an employee (note that age is defined in the third column of the query).

• wasNull() reports whether the last column read had a value of SQL NULL.

• The ResultSet object’s getXXX() methods (such as getString() and getInt()) retrieve columndata. JDBC defines types to match the SQL data types, and there is a getXXX() method foreach. You can use the getXXX() method in two ways with the same semantics. (You can retrievethe value of the designated column in the current row of this ResultSet object as an XXX type.)

• getXXX(int columnIndex) is the preferred way of getting data since there is no need to get thecolumn’s metadata information.

• getXXX(String columnName) might be a little bit slow because of getting the column’s meta-data information.

Running the Solution for the Oracle DatabaseThe output of the demo program for Oracle is as follows:

5203ch05.qxd 8/11/05 4:24 PM Page 172

Page 202: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 173

$ javac DemoResultSet.java$ java DemoResultSet oracle--DemoResultSet begin--conn=oracle.jdbc.driver.T4CConnection@2ce908---------------id=11name=Alex Smithage=25---------------id=22name=Don Knuthage=65---------------id=33name=Mary Kentage=35---------------id=44name=Monica Selesage=30---------------id=99name=Alex Edisonage=null-----------------DemoResultSet end--

Running the Solution for the MySQL DatabaseThe output of the demo program for MySQL is as follows:

$ javac DemoResultSet.java$ java DemoResultSet mysql--DemoResultSet begin--conn=com.mysql.jdbc.Connection@1c6f579---------------id=11name=Alex Smithage=25---------------id=22name=Don Knuthage=65---------------id=33name=Mary Kentage=35---------------id=44name=Monica Selesage=30---------------id=99name=Alex Edisonage=null-----------------DemoResultSet end--

5203ch05.qxd 8/11/05 4:24 PM Page 173

Page 203: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE174

Figure 5-1. Relationship of ResultSet to other classes/interfaces

5-2. What Is the Relationship of ResultSet to OtherClasses/Interfaces?To show the importance of the ResultSet object, Figure 5-1 shows the interactions and relationshipsbetween the key classes and interfaces in the java.sql package. The figure also shows the methodsinvolved in creating statements, setting parameters, and retrieving results. (For details, please seethe final release of the JDBC 3.0 specification, published by Sun Microsystems.)

As you can observe from the low-level class/interface relationships, to connect to a relationaldatabase you use an instance of the Connection object. (To get the Connection object, you need toprovide a database URL, username, and password.) Then, to find out the names of the database views,tables, and columns (so-called metadata), you need to get an instance of the DatabaseMetaDataobject from the Connection object. Next, to issue a SQL query, you compose the SQL query stringand use the Connection object to create a Statement object. By executing the statement, you obtaina ResultSet object, and to find out the names of the column rows in that ResultSet, you need toobtain an instance of the ResultSetMetaData object.

5203ch05.qxd 8/11/05 4:24 PM Page 174

Page 204: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 175

5-3. How Does the JDK Define a ResultSet?The JDK documentation (http://java.sun.com/j2se/1.5.0/docs/api/java/sql/ResultSet.html)defines the ResultSet interface as follows:

A table of data representing a database result set, which is usually generated by executinga statement that queries the database. A ResultSet object maintains a cursor pointing to itscurrent row of data. Initially the cursor is positioned before the first row. The next methodmoves the cursor to the next row, and because it returns false when there are no more rows inthe ResultSet object, it can be used in a while loop to iterate through the result set.

A default ResultSet object is not updatable and has a cursor that moves forward only. Thus,you can iterate through it only once and only from the first row to the last row. It is possible toproduce ResultSet objects that are scrollable and/or updatable. The following code fragment,in which con is a valid Connection object, illustrates how to make a result set that is scrolla-ble and insensitive to updates by others, and that is updatable. See ResultSet fields for otheroptions.

Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);

ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");// rs will be scrollable, will not show changes made by others,// and will be updatable

The ResultSet interface provides getter methods (getBoolean, getLong, and so on) for retrievingcolumn values from the current row. You can retrieve values using either the index number of the col-umn or the name of the column. In general, using the column index will be more efficient. Columnsare numbered from 1. For maximum portability, result set columns within each row should be readin left-to-right order, and each column should be read only once. (You may read each column morethan once, but this will add more time to your application.)

The numbers, types, and properties of a ResultSet object’s columns are provided by theResulSetMetaData object returned by the ResultSet.getMetaData method.

5-4. What Kinds of ResultSet Objects Exist?ResultSet objects of a database can have different functionality and characteristics. (Note that inrelational database terminology, result sets are called cursors.) These characteristics are as follows:

• Result set type.

• Result set concurrency.

• Result set (cursor) holdability, which indicates whether the cursor is closed at the Connection.commit() time. Holdability can control the closing of cursors at the transaction commit time.

What is a result set type? According to the JDBC 3.0 specification, the type of a ResultSet objectdetermines the level of its functionality in two main areas: the ways in which the cursor can bemanipulated and how concurrent changes made to the underlying data source are reflected by theResultSet object. (A complete set of rows returned by a SQL statement is known as a result set, andthe cursor is a pointer, which points to one of the records in the result set.)

JDBC defines the following result set types:

5203ch05.qxd 8/11/05 4:24 PM Page 175

Page 205: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE176

• Forward-only: Identified by java.sql.ResultSet.TYPE_FORWARD_ONLY, forward-only resultsets allow you to move forward, but not backward, through the data. The application canmove forward using the next() method. In general, most of the applications work withforward-only result sets.

• Scroll-insensitive: Identified by java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, a scroll-insensitive result set ignores changes that are made while it is open. It provides a static viewof the underlying data it contains. The membership, order, and column values of rows arefixed when the result set is created.

• Scroll sensitive: Identified by java.sql.ResultSet.TYPE_SCROLL_SENSITIVE, a scroll-sensitiveresult set provides a dynamic view of the underlying data, reflecting changes that are made whileit is open. The membership and ordering of rows in the result set may be fixed, depending onhow it is implemented.

You should note that not all JDBC drivers support each of these result set types, and DatabaseMetaData provides methods to determine which ones are supported through its supportsResultSetType() method. The result set’s type affects sensitivity only to those changesmade by others, and you cannot specify whether a modifiable result set is sensitive to its ownchanges. This behavior varies from one JDBC driver to the next, but the ownUpdatesAreVisible(),ownInsertsAreVisible(), and ownDeletesAreVisible() methods in DatabaseMetaData are providedso that you can determine what behavior to expect from the driver.

5-5. How Do You Set a ResultSet Type?The java.sql.Connection interface contains nine methods that set the result set type (see Table 5-1).(Some of the methods do not set the result set type explicitly; they take the default behavior.)

Table 5-1. Connection Methods That Set the ResultSet Type

Method Description

Statement createStatement() Creates a Statement objectfor sending SQL statementsto the database. Result setscreated using the returnedStatement object will bydefault be typeTYPE_FORWARD_ONLY and havea concurrency level ofCONCUR_READ_ONLY.

Statement createStatement(int resultSetType,int resultSetConcurrency) Creates a Statement object

that will generate ResultSetobjects with the given typeand concurrency.

Statement createStatement(int resultSetType,int resultSetConcurrency,int resultSetHoldability) Creates a Statement object

that will generate ResultSetobjects with the given type,concurrency, and holdability.

5203ch05.qxd 8/11/05 4:24 PM Page 176

Page 206: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 177

Method Description

CallableStatement prepareCall(String sql) Creates a CallableStatementobject for calling databasestored procedures. Resultsets created using thereturned CallableStatementobject will by default be typeTYPE_FORWARD_ONLY and havea concurrency level ofCONCUR_READ_ONLY.

CallableStatement prepareCall(String sql,int resultSetType,int resultSetConcurrency) Creates a CallableStatement

object that will generateResultSet objects with thegiven type and concurrency.

CallableStatement prepareCall(String sql,int resultSetType,int resultSetConcurrency,int resultSetHoldability) Creates a CallableStatement

object that will generateResultSet objects with thegiven type and concurrency.

PreparedStatement prepareStatement(String sql) Creates a PreparedStatementobject for sendingparameterized SQLstatements to the database.Result sets created using thereturned PreparedStatementobject will by default be typeTYPE_FORWARD_ONLY and havea concurrency level ofCONCUR_READ_ONLY.

PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency) Creates a PreparedStatement

object that will generateResultSet objects with thegiven type and concurrency.

PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency,int resultSetHoldability) Creates a PreparedStatement

object that will generateResultSet objects with thegiven type, concurrency, andholdability.

In all of these methods, the resultSetType parameter can take only one of the following values:

5203ch05.qxd 8/11/05 4:24 PM Page 177

Page 207: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE178

• java.sql.ResultSet.TYPE_FORWARD_ONLY: The constant indicating the type for a ResultSetobject whose cursor may move only forward

• java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE: The constant indicating the type fora ResultSet object that is scrollable but generally not sensitive to changes made by others

• java.sql.ResultSet.TYPE_SCROLL_SENSITIVE: The constant indicating the type for a ResultSet

object that is scrollable and generally sensitive to changes made by others

5-6. How Do You Get a ResultSet Type?ResultSet.getType returns the type of this result set. Here are the details from JDK 1.4 defined forthe ResultSet interface:

public int getType()throws SQLException

This retrieves the type of this ResultSet object. The type is determined by the Statement objectthat created the result set. It returns one of the following:

java.sql.ResultSet.TYPE_FORWARD_ONLYjava.sql.ResultSet.TYPE_SCROLL_INSENSITIVEjava.sql.ResultSet.TYPE_SCROLL_SENSITIVE

This throws SQLException if a database access error occurs.Now you can test the program to get the result set type:

ResultSet rs = <get-a-result-set>;int rsType = rs.getType();if (rsType == java.sql.ResultSet.TYPE_FORWARD_ONLY) {

...}else if (rsType == java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE) {

...}else if (rsType == java.sql.ResultSet.TYPE_SCROLL_SENSITIVE) {

...}else {

// it is an error...

}

5-7. Which ResultSet Types Are Supported by Databases?In JDBC, three result set types are defined in the ResultSet interface:

• static int TYPE_FORWARD_ONLY: The constant indicating the type for a ResultSet objectwhose cursor may move only forward

• static int TYPE_SCROLL_INSENSITIVE: The constant indicating the type for a ResultSetobject that is scrollable but generally not sensitive to changes made by others

• static int TYPE_SCROLL_SENSITIVE: The constant indicating the type for a ResultSet objectthat is scrollable and generally sensitive to changes made by others

By using DatabaseMetaData, you can find out if a database supports the defined result set types:

public boolean supportsResultSetType(int type)

5203ch05.qxd 8/11/05 4:24 PM Page 178

Page 208: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 179

This retrieves whether this database supports the given result set type. The parameter is type,which is defined in java.sql.ResultSet. It returns true if the database supports the given result settype; otherwise, it returns false. It throws SQLException if a database access error occurs.

You can use the method getAvailableResultSetTypes() (defined in the class DatabaseMetaDataTool)to find out the result set types supported by your desired vendor. DatabaseMetaDataTool is a user-defined class that provides services for handling database metadata. (You can download this classfrom the book’s Web site, and you can use this class for Oracle, MySQL, and other databases.)

SolutionThis solution uses the DatabaseMetaDataTool class, which provides services for handling data-base metadata. (You can download this class from the book’s Web site.) DatabaseMetaDataTool.getAvailableResultSetTypes() returns all available result set types for a given database.

import java.sql.*;

import jcb.db.VeryBasicConnectionManager;import jcb.meta.DatabaseMetaDataTool;import jcb.util.DatabaseUtil;

public class TestGetResultSetTypes {

public static void main(String[] args) {Connection conn = null;try {

System.out.println("--TestGetResultSetTypes begin--");String dbVendor = args[0]; // { "mysql", "oracle" }conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

String resultSetTypes =DatabaseMetaDataTool.getAvailableResultSetTypes(conn);

System.out.println(resultSetTypes);System.out.println("--TestGetResultSetTypes end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(conn);

}}

}

Running the Solution for the Oracle DatabaseThis code shows how to run the solution for the Oracle database:

$ javac TestGetResultSetTypes.java$ java TestGetResultSetTypes oracle--TestGetResultSetTypes_Oracle begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------

5203ch05.qxd 8/11/05 4:24 PM Page 179

Page 209: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE180

<ResultSetTypes><type name="TYPE_FORWARD_ONLY"/><type name="TYPE_SCROLL_INSENSITIVE"/><type name="TYPE_SCROLL_SENSITIVE"/>

</ResultSetTypes>--TestGetResultSetTypes end--

Running the Solution for the MySQL DatabaseThis code shows how to run the solution for the MySQL database:

$ javac TestGetResultSetTypes.java$ java TestGetResultSetTypes mysql--TestGetResultSetTypes begin--conn=com.mysql.jdbc.Connection@124bbbf---------------<ResultSetTypes>

<type name="TYPE_SCROLL_INSENSITIVE"/></ResultSetTypes>--TestGetResultSetTypes end--

5-8. What Is a ResultSet Concurrency?Result sets have one of two concurrency types. A read-only result set is of type CONCUR_READ_ONLY,and an updatable result set is of type CONCUR_UPDATABLE. Therefore, the concurrency mode of a resultset refers to the ability to modify the data returned by a result set. JDBC defines the following con-currency types for a result set:

• Read-only: If your application does not need to modify this data, specifying java.sql.ResultSet.CONCUR_READ_ONLY for the concurrency mode parameter will cause the statementto create result sets that are read-only. (The result cannot be modified, updated, or deleted.)

java.sql.ResultSet.CONCUR_READ_ONLY// The constant indicating the concurrency mode// for a ResultSet object that may NOT be updated.

• Updatable: Specifying java.sql.ResultSet.CONCUR_UPDATABLE for the concurrency modeparameter allows your application to make changes to the data in the result set and havethose changes stored in the underlying table(s) in the database.

java.sql.ResultSet.CONCUR_UPDATABLE// The constant indicating the concurrency mode// for a ResultSet object that may be updated.

To further explain, a read-only result set does not allow its contents to be updated. In Java, youcan relate this to constants, which are prefixed with public static final or private static final.The read-only result sets can increase the overall level of concurrency between transactions, becausemultiple read-only locks can be held on a data item simultaneously.

An updatable result set allows its contents to be updated and acts the opposite of read-onlyresult sets. An updatable result set may use database write locks to mediate access to the same dataitem using different transactions. Because only a single write lock may be held at one time on a dataitem, updatable result sets can reduce concurrency.

5203ch05.qxd 8/11/05 4:24 PM Page 180

Page 210: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 181

5-9. How Do You Set ResultSet Concurrency?The java.sql.Connection interface has nine methods that set the result set concurrency; Table 5-1listed the methods. In all of the methods, the Concurrency parameter can take only one of the fol-lowing values:

• java.sql.ResultSet.CONCUR_READ_ONLY: The constant indicating the concurrency mode fora ResultSet object that may not be updated

• java.sql.ResultSet.CONCUR_UPDATABLE: The constant indicating the concurrency mode fora ResultSet object that may be updated

If the ResultSet Concurrency parameter is not passed explicitly, then it takes the default value(which is CONCUR_READ_ONLY).

5-10. How Do You Get ResultSet Concurrency?ResultSet.getConcurrency() returns the concurrency of this result set. Here are the details fromJDK 1.4 defined for the ResultSet interface:

public int getConcurrency()

This retrieves the concurrency mode of this ResultSet object. The concurrency used is deter-mined by the Statement object that created the result set. This returns the concurrency type,either ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE. It throws SQLException ifa database access error occurs.

Now you can test the program to get the result set type using this code:

ResultSet rs = ...int rsConcurrency = rs.getConcurrency();if (rsConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) {

...}else if (rsConcurrency == java.sql.ResultSet.CONCUR_UPDATABLE) {

...}else {

// it is an error...

}

5-11. What Is ResultSet Holdability?JDBC 3.0 adds support for specifying result set (or cursor) holdability. Result set holdability is theability to specify whether cursors (or a result set such as java.sql.ResultSet) should be held openor closed at the end of a transaction. A holdable cursor, or result set, is one that does not automati-cally close when the transaction that contains the cursor is committed.

You may improve database performance by including the ResultSet holdability. If ResultSetobjects are closed when a commit operation is implicitly or explicitly called, this can also improveperformance.

5203ch05.qxd 8/11/05 4:24 PM Page 181

Page 211: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE182

5-12. How Do You Set ResultSet Holdability?You can supply the following constants to the Connection methods createStatement, prepareStatement,and prepareCall:

• java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT: The constant indicating that ResultSetobjects should not be closed when the method Connection.commit is called. ResultSetobjects (cursors) are not closed; they are held open when the method commit is called.

• java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT: The constant indicating that ResultSetobjects should be closed when the method Connection.commit is called. ResultSet objects(cursors) are closed when the method commit is called. Closing cursors at commit can resultin better performance for some applications.

5-13. How Do You Set ResultSet Holdability Using the Connection Object?Also, you can set the result set holdability by using the Connection object. This shows how to setholdability to hold the cursor over commit:

// changes the holdability of ResultSet objects created// using this Connection object to the given holdability.int holdability = java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;Connection conn = ... get a connection object ...conn.setHoldability(holdability);

This shows how to set holdability to close the cursor at commit:

// changes the holdability of ResultSet objects created// using this Connection object to the given holdability.int holdability = java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT;Connection conn = ... get a connection object ...conn.setHoldability(holdability);

5-14. How Do You Check ResultSet Holdability?The default holdability of a ResultSet object depends on how the DBMS and driver are implemented.You can call the DatabaseMetaData method supportsResultSetHoldability to see if your desired DBMSsupports result set holdability (it retrieves whether this database supports the given result set hold-ability).

Connection conn = ... get a database connection object;DatabaseMetaData dbMetaData = conn.getDatabaseMetaData();if (dbMetaData == null) {

// database metadata is not supported by driver...

}else {

if dbMeta.supportsResultSetHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT) {//// this database hold cursors over commit//...

}if dbMeta.supportsResultSetHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT) {//// this database close cursors at commit

5203ch05.qxd 8/11/05 4:24 PM Page 182

Page 212: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 183

//...

}}

5-15. How Do You Get ResultSet Holdability?The default holdability of a ResultSet object depends on how the DBMS and driver are implemented.You can call the DatabaseMetaData method getResultSetHoldability to get the default holdabilityfor result sets returned by your DBMS and driver.

You can get the result set holdability in three ways.First, you can use the Statement object:

int Statement.getResultSetHoldability()// Retrieves the result set holdability for ResultSet// objects generated by this Statement object.

Second, you can use the DatabaseMetaData object:

int DatabaseMetaData.getResultSetHoldability()// Retrieves the default holdability of this ResultSet object.

Finally, you can use the Connection object:

int Connection.getHoldability()// Retrieves the current holdability of ResultSet// objects created using this Connection object.

5-16. How Do You Create and Manipulate ResultSet Objects?According to JDK 1.4, the ResultSet object is a table of data representing a database result set, whichis usually generated by executing a statement that queries the database. Further, “a ResultSet objectmaintains a cursor pointing to its current row of data. Initially the cursor is positioned before the firstrow. The next method moves the cursor to the next row, and because it returns false when there areno more rows in the ResultSet object, it can be used in a while loop to iterate through the result set.”

A default ResultSet object (when the result set type is not set explicitly in creating Statement,PreparedStatement, and CallableStatement) is not updatable and has a cursor that moves forwardonly. Thus, you can iterate through it only once and only from the first row to the last row. It is possi-ble to produce ResultSet objects that are scrollable and/or updatable. The code in the next sectionillustrates how to make a result set that is scrollable, insensitive to updates by others, and updata-ble. See the ResultSet fields for other options.

5-17. How Do You Get Rows from a Database Table?The SQL SELECT statement selects data from a table. The tabular result is stored in a result table(called the result set). The following example executes a SQL SELECT query and creates a result set:

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// first get a valid java.sql.Connection objectconn = getConnection();// Create a result set containing all data from employees table

5203ch05.qxd 8/11/05 4:24 PM Page 183

Page 213: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE184

stmt = conn.createStatement();rs = stmt.executeQuery("SELECT * FROM employees");// now iterate result set object (rs) to get rows

}catch (SQLException e) {

// handle exception heree.printStackTrace();// more statements here for handling the exception

}finally {

// close resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

To understand this fully, refer to the following examples.The SQL SELECT statement selects data from a table. The tabular result is stored in a result table

(called the result set). The general simplified syntax for SQL SELECT is as follows:

SELECT column_name(s)FROM table_name

For example, to select the columns named id and firstname from the employees table, usea SELECT statement like this:

SELECT id, firstnameFROM employees

Please note that the first row is metadata for an employees table (see Table 5-2).

Table 5-2. employees Table

id firstname lastname dept

100 Alex Smith Sales

200 Mary Taylor Software

300 Bob Stewart Racing

Table 5-3 shows the result.

Table 5-3. Result from Database

id firstName

100 Alex

200 Mary

300 Bob

To select all columns from the employees table, use * instead of column names, like this:

SELECT * FROM employees

Note that in real-world applications, if you do not need all the columns, just list the onesrequired; this can boost the performance of your application. Table 5-4 shows the results.

5203ch05.qxd 8/11/05 4:24 PM Page 184

Page 214: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 185

Table 5-4. Result from Database

id firstName lastName dept

100 Alex Smith Sales

200 Mary Taylor Software

300 Bob Stewart Racing

5-18. How Do You Get Data from a ResultSet?A result set (represented as a ResultSet object) contains the results of a SQL query. (This query canbe selected rows from a table, or it can be some data returned by a DatabaseMetaData object.) Ingeneral, the results are kept in a set of rows (the number of rows can be zero, one, two, and so on),one of which is designated the current row. A row must be made current before data can be retrievedfrom it. The result set maintains a reference to the current row called the cursor. The cursor is posi-tioned before the first row when a result set is created. When a result set’s next() method is called,the cursor moves to the first row of the result set, and that row becomes the current row.

For each column, you have two ways to retrieve the data from the current row. The first methoduses a column index starting from 1. The second uses a column name. For example, with the follow-ing query:

SELECT id, name, address FROM employees

you can retrieve the value for the address using a column index of 3 or using the column nameaddress.

Method 1: Retrieving the Value of the Address Column Using the Index NumberThe following code shows how to retrieve the value of the address column using the index number:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

conn = getConnection(); // get a Connection object// create a result set containing all data// from your desired tablestmt = conn.createStatement();String query = "SELECT id, name, address FROM employees";rs = stmt.executeQuery(query);// Fetch each row from the result setwhile (rs.next()) {

// Get the data from the row using the column name// note that using a column index is better than// using the column name: using column name might// add overhead: there is a need to// get column metadata info.String employeeAddress = rs.getString(3);...

}}catch (SQLException e) {// handle the exception...

}

5203ch05.qxd 8/11/05 4:24 PM Page 185

Page 215: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE186

finally {// close ResultSet, Statement, Connection

}

Method 2: Retrieving the Value of the Address Column Using the Column NameThe following code shows how to retrieve the value of the address column using the column name:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

conn = getConnection(); // get a Connection object// create a result set containing all data// from your desired tablestmt = conn.createStatement();String query = "SELECT id, name, address FROM employees";rs = stmt.executeQuery(query);// Fetch each row from the result setwhile (rs.next()) {

// Get the data from the row using the column name// note that using a column name might add// overhead: there is a need to get column metadata info.String employeeAddress = rs.getString("address");...

}}catch (SQLException e) {// handle the exception...

}finally {

// close ResultSet, Statement, Connection}

For both methods (using the index of the column and using the column name), you invoke anappropriate method to extract the data value of the given column.

5-19. How Do You Get Rows from a Database Table?You can use a SQL query (by using the SELECT statement or executing a stored procedure, whichreturns set of rows) to get data from tables/views. The result of the SQL query is called a result set.(JDBC represents this as a java.sql.ResultSet object.)

The next example executes a SQL SELECT query (selecting all rows from the table books_table)and creates a result set:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

conn = < get-a-database-connection>;// Create a result set containing all data from books_tablestmt = connection.createStatement();String query = "SELECT * FROM books_table";rs = stmt.executeQuery(query);// to get the data from result set (rs),// you may iterate by the next() method

5203ch05.qxd 8/11/05 4:24 PM Page 186

Page 216: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 187

while (rs.next()) {// use getXXX() to extract the data from each row}

}catch (SQLException e) {// handle the exception

}finally {// close the ResultSet (rs), connection (conn)// and statement (stmt) objects here

}

5-20. How Do You Get Data from a ResultSet?A result set (expressed as a java.sql.ResultSet object) contains the results of a SQL query. Theresults are kept in a set of rows, one of which is designated the current row.

According to the Java 2 documentation, “a ResultSet object maintains a cursor pointing to itscurrent row of data. Initially the cursor is positioned before the first row. The next method moves thecursor to the next row, and because it returns false when there are no more rows in the ResultSetobject, it can be used in a while loop to iterate through the result set.” When a ResultSet object’snext() method is called, the cursor moves to the first row of the result set, and that row becomes thecurrent row.

Note that a default ResultSet object is not updatable and has a cursor that moves forward only.Thus, you can iterate through it only once and only from the first row to the last row. However, it ispossible to produce ResultSet objects that are scrollable and/or updatable (by passing some parameterswhen creating result sets).

Two scenarios exist when getting data from a ResultSet object:

• Case 1: You know the name (and position) and type of each column. (In this case, you knowthe SQL query, so you know the name and types of each column. There is no need to discoverthe names/types of columns at runtime.)

• Case 2: You do not know the name/type of columns. (In this case, you need to discover thename and types of columns at runtime.)

You have two ways to retrieve the data from the current row. The first uses a column indexstarting from 1. The second uses a column name. For example, with the query SELECT employee_id,employee_name FROM employee_table, you can retrieve the value for employee_id using a columnindex of 1 or using the column name employee_id, and you can retrieve the value for employee_nameusing a column index of 2 or using the column name employee_name. Note that the order of ResultSet.getXXX(index=1, 2, 3, ...) will not cause any error at all. Therefore, in this example, ResultSet.getString(1) can follow ResultSet.getString(2), or ResultSet.getString(2) can follow ResultSet.getString(1).

The next two sections address these questions in detail.

Case 1: You Know the Name, Position, and Type of Each ColumnWhen you know the name (and position) and type of each column of the result set, you have twooptions for getting the data:

• Using the column index (starting from 1)

• Using the column names

5203ch05.qxd 8/11/05 4:24 PM Page 187

Page 217: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE188

Using a column index (starting from 1), you can extract the value of columns. In the followingclass, you select all rows from the employees table and then extract the values using the columnindex. Before running this solution, you will see the employees table and its contents (using theOracle 9i database).

Solution

The following sample solution extracts data from the ResultSet object by using the column index(1, 2, and so on):

import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class ExtractResultSetByIndex {public static void main(String[] args) {

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

System.out.println("--ExtractResultSetByIndex begin--");String dbVendor = args[0];conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "select id, name, age from employees";

// create a statementstmt = conn.createStatement();

// execute query and return result as a ResultSetrs = stmt.executeQuery(query);

// extract data from the ResultSet (using the column indexes)while (rs.next()) {

String id = rs.getString(1); // index 1 = "id" columnString name = rs.getString(2); // index 2 = "name" columnSystem.out.println("id="+id);System.out.println("name="+name);// according to table def., age can be nullint age = rs.getInt(3); // index 3 = "age" columnif (rs.wasNull()){

System.out.println("age=null");}else {

System.out.println("age="+age);}System.out.println("---------------");

}System.out.println("--ExtractResultSetByIndex end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

5203ch05.qxd 8/11/05 4:24 PM Page 188

Page 218: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 189

finally {// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle Database

This is the code to set up the Oracle database:

SQL> desc employees;Name Null? Type------------------------------------ -------- -------------ID NOT NULL VARCHAR2(10)NAME VARCHAR2(20)AGE NUMBERSQL> select * from employees;

ID NAME AGE---------- -------------------- ----------11 Alex Smith 2522 Don Knuth 6533 Mary Kent 3544 Monica Seles 3099 Alex Edison

SQL> desc employees;Name Null? Type----------------------------------------- -------- ------------ID NOT NULL VARCHAR2(10)NAME NOT NULL VARCHAR2(20)AGE NUMBER(38)

Running the Solution for the Oracle Database

This is how you run the solution for the Oracle database:

$ java ExtractResultSetByIndex oracle--ExtractResultSetByIndex begin--conn=oracle.jdbc.driver.T4CConnection@341960---------------id=11name=Alex Smithage=25---------------id=22name=Don Knuthage=65---------------id=33name=Mary Kentage=35

5203ch05.qxd 8/11/05 4:24 PM Page 189

Page 219: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE190

---------------id=44name=Monica Selesage=30---------------id=99name=Alex Edisonage=null-----------------ExtractResultSetByIndex end--

Setting Up the MySQL Database

This is the code to set up the MySQL database:

mysql> select * from employees;+----+--------------+------+| id | name | age |+----+--------------+------+| 88 | Peter Pan | NULL || 77 | Donald Duck | NULL || 33 | Mary Kent | 35 || 44 | Monica Seles | 30 |+----+--------------+------+4 rows in set (0.05 sec)

Running the Solution for the MySQL Database

This is how you run the solution for the MySQL database:

$ java ExtractResultSetByIndex mysql--ExtractResultSetByIndex begin--conn=com.mysql.jdbc.Connection@1dd46f7---------------id=88name=Peter Panage=null---------------id=77name=Donald Duckage=null---------------id=33name=Mary Kentage=35---------------id=44name=Monica Selesage=30-----------------ExtractResultSetByIndex end--

Case 2: You Do Not Know the Name, Position, and Type of Each ColumnThis solution is identical to ExtractResultSetByIndex with the exception of three lines. Specifically,you need to replace the following lines:

5203ch05.qxd 8/11/05 4:24 PM Page 190

Page 220: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 191

String id = rs.getString(1); // index 1 is the "id" columnString name = rs.getString(2); // index 2 is the "name" columnint age = rs.getInt(3); // index 3 is the "age" column

with these:

String id = rs.getString("id"); // index 1 is the "id" columnString name = rs.getString("name"); // index 2 is the "name" columnint age = rs.getInt("age"); // index 3 is the "age" column

5-21. How Do You Determine If a Fetched Value Is NULL?When a ResultSet.getXXX() method encounters a NULL in the database, it will convert it to a defaultvalue (the default value depends on the type of column). For example, if NULL was encountered ina VARCHAR field, ResultSet.getString() will return "". If NULL was encountered in a NUMBER (or INT)field, ResultSet.getInt() will return 0. To determine whether the actual value is a NULL, wasNull()must be called. This method must be called immediately after the value is fetched from the result set.

The wasNull() method has the following signature (according to JDK 1.4):

public boolean wasNull() throws SQLException

This reports whether the last column read had a value of SQL NULL. Note that you must first callone of the getter methods on a column to try to read its value and then call the method wasNull tosee if the value read was SQL NULL.

This returns true if the last column value read was SQL NULL. It returns false otherwise.The following snippet tests a retrieved column value for NULL:

ResultSet rs = <a-valid_result-set>;while (rs.next()) {

String id = rs.getString(1);if (rs.wasNull()) {

//// then the first column was null//

}else {

//// the first column was not null//

}//...

}

5-22. How Do You Get the Column Names in a Result Set?Given a ResultSet object, it is possible to get the column name, column type, table name (from whichtable this column came from), and position; all of this is possible by using ResultSetMetaData.(ResultSetMetaData behaves differently for Oracle and MySQL; for the Oracle database, ResultSetMetaData does not provide the correct table name.)

SolutionThis is the solution:

import java.sql.*;

import jcb.util.DatabaseUtil;

5203ch05.qxd 8/11/05 4:24 PM Page 191

Page 221: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE192

import jcb.db.VeryBasicConnectionManager;

public class GetColumnNamesFromResultSet {

public static String getColumnNames(ResultSet rs)throws SQLException {if (rs == null) {

return null;}

// get result set metadataResultSetMetaData rsMetaData = rs.getMetaData();int numberOfColumns = rsMetaData.getColumnCount();StringBuffer columnNames = new StringBuffer("<columnNames>");

// get the column names; column indexes start from 1for (int i=1; i<numberOfColumns+1; i++) {

String columnName = rsMetaData.getColumnName(i);// Get the name of the column's table nameString tableName = rsMetaData.getTableName(i);columnNames.append("<column name=\""+columnName+

"\" table=\""+tableName+"\"/>");}columnNames.append("</columnNames>");return columnNames.toString();

}

public static void main(String[] args) {Connection conn = null;Statement stmt = null;ResultSet rs = null;String dbVendor = args[0]; // database vendortry {

System.out.println("--GetColumnNamesFromResultSet begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "select id, name, age from employees";

// create a statementstmt = conn.createStatement();

// execute query and return result as a ResultSetrs = stmt.executeQuery(query);

// get the column names from the ResultSetString columnNames = getColumnNames(rs);System.out.println(columnNames);System.out.println("--GetColumnNamesFromResultSet end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}

5203ch05.qxd 8/11/05 4:24 PM Page 192

Page 222: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 193

finally {// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac GetColumnNamesFromResultSet.java$ java GetColumnNamesFromResultSet oracle--GetColumnNamesFromResultSet begin--conn=oracle.jdbc.driver.OracleConnection@11ddcde---------------<columnNames>

<column name="ID" table=""><column name="NAME" table=""><column name="AGE" table="">

</columnNames>--GetColumnNamesFromResultSet end--

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac GetColumnNamesFromResultSet.java$ java GetColumnNamesFromResultSet mysql--GetColumnNamesFromResultSet begin--conn=com.mysql.jdbc.Connection@15c7850---------------<columnNames>

<column name="id" table="employees"/><column name="name" table="employees"/><column name="age" table="employees"/>

</columnNames>--GetColumnNamesFromResultSet end--

5-23. How Do You Get the Number of Rows in a Database Table?To get the number of rows (sometimes database experts use the term cardinality) from a table, youneed to issue the following SQL query:

select count(*) form <table-name>

SolutionI provide a complete solution in the CountRows class that you can download from this book’s Website. The following shows only the key method:

public static int countRows(Connection conn, String tableName)throws SQLException {

// select the number of rows in the tableStatement stmt = null;

5203ch05.qxd 8/11/05 4:24 PM Page 193

Page 223: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE194

ResultSet rs = null;int rowCount = -1;try {

stmt = conn.createStatement();rs = stmt.executeQuery("SELECT COUNT(*) FROM "+ tableName);// get the number of rows from the result setrs.next();rowCount = rs.getInt(1);

}finally {DatabaseUtil.close(rs);DatabaseUtil.close(stmt);

}return rowCount;

}

Running the Solution for the Oracle DatabaseTo execute the Oracle solution, use this code:

$ javac CountRows.java$ java CountRows oracle employees------CountRows begin---------tableName=employeesconn=oracle.jdbc.driver.OracleConnection@11ddcderowCount=4------CountRows_Oracle end---------

Running the Solution for the MySQL DatabaseTo execute the MySQL solution, use this code:

$ javac CountRows.java$ java CountRows mysql employees------CountRows_MySQL begin---------tableName=employeesconn=com.mysql.jdbc.Connection@15c7850rowCount=4------CountRows end---------

5-24. How Do You Get BLOB Data from a Database Table?A BLOB is a Binary Large OBject (such as an image, video, document, and so on) in a database. BLOBscan be very large, 2GB or more, depending on the database. A BLOB is a reference to data in a database.There are some restrictions for BLOBs:

• BLOB columns cannot be keys.

• SQL queries cannot group or sort on BLOB.

To get BLOB data from a database table, you create a table with a BLOB column, populate it, andthen get the data.

Setting Up the Oracle DatabaseThe Oracle table specification is as follows:

5203ch05.qxd 8/11/05 4:24 PM Page 194

Page 224: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 195

Figure 5-2. Binary files to be inserted into a database

$ sqlplus mp/mp2SQL*Plus: Release 9.2.0.1.0 - Production on Thu Sep 4 13:55:27 2003

SQL> create table my_pictures(2 id varchar(10) not null primary key,3 photo BLOB4 );

Table created.

SQL> desc my_pictures;Name Null? Type-------------- -------- --------------ID NOT NULL VARCHAR2(10)PHOTO BLOB

Next, populate an Oracle table with binary data and verify the population. To accomplish this task,you will learn how to develop a simple Java program (the InsertBLOB_Oracle class) that inserts thedesired binary files into the my_pictures table. Figure 5-2 shows the picture (binary files) to be inserted.

Viewing the Oracle Database Before Running the SolutionThis shows the Oracle database before running the solution:

SQL> describe my_pictures;Name Null? Type----------------------------- -------- ------------ID NOT NULL VARCHAR2(10)PHOTO BLOB

SQL> select id from my_pictures;no rows selected

5203ch05.qxd 8/11/05 4:24 PM Page 195

Page 225: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE196

Solution: Using the Oracle DatabaseUsing Oracle, when you first insert a new record containing a BLOB data type, you must insert therecord with an “empty” BLOB before the BLOB column can be updated with real data. You can insertan empty BLOB with the Oracle EMPTY_BLOB() function. The EMPTY_BLOB() function returns an emptylocator of type BLOB (note that EMPTY_BLOB is a proprietary feature of Oracle). The following codeshows how to do this:

import java.io.*;import java.sql.*;import java.text.*;

import jcb.db.*;import jcb.meta.*;

import oracle.jdbc.driver.*;import oracle.sql.BLOB;

public class InsertBLOB_Oracle {Connection conn;

public InsertBLOB_Oracle() throws Exception {DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:caspian", "mp", "mp2");

}

public static void main(String[] args)throws Exception {if (args.length != 2) {

System.out.println("usage: java InsertBLOB_Oracle <id> <binary-file>");System.exit(0);

}

String id = args[0].trim();String binaryFileName = args[1].trim();new InsertBLOB_Oracle().process(id, binaryFileName);

}

public void process(String id, String binaryFileName)throws Exception {int rows = 0;FileInputStream fin = null;OutputStream out = null;ResultSet rs = null;Statement stmt = null;oracle.sql.BLOB photo = null;

try {conn.setAutoCommit(false);stmt = conn.createStatement();

System.out.println("This creates the LOB locators");rows = stmt.executeUpdate("insert into my_pictures"+"(id, photo ) values ('"+id+"', empty_blob() )");

System.out.println(rows + " rows inserted");

5203ch05.qxd 8/11/05 4:24 PM Page 196

Page 226: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 197

// now retrieve the BLOB locatorrs = stmt.executeQuery("select photo from "+"my_pictures where id = '"+id+ "' for update nowait");

rs.next();photo = ((OracleResultSet)rs).getBLOB(1);

// Now, we have the BLOB locator, store the photoFile binaryFile = new File(binaryFileName);fin = new FileInputStream(binaryFile);out = photo.getBinaryOutputStream();// Get the optimal buffer size from the BLOBbyte[] buffer = new byte[photo.getBufferSize()];int length = 0;while ((length = fin.read(buffer)) != -1) {out.write(buffer, 0, length);

}

// you've got to close the output stream before// you commit, or the changes are lost!out.close();fin.close();conn.commit();

}finally {DatabaseUtil.close(rs);DatabaseUtil.close(stmt);

}}

protected void finalize() throws Throwable {DatabaseUtil.close(conn);super.finalize();

}}

Running the SolutionTo execute the Oracle solution, use this code:

$ javac InsertBLOB_Oracle.java$ dir d:\mp\book\images2Directory of d:\mp\book\images205/08/2003 10:13a 36,932 duck1.jpg05/09/2003 08:50a 20,754 duck2.jpg05/07/2003 01:45p 18,843 tiger1.jpg05/09/2003 08:30a 33,097 tiger2.jpg08/25/2003 03:18p 63,241 tiger3.jpg08/25/2003 03:21p 79,158 tiger4.jpg

6 File(s) 252,025 bytes

$ java InsertBLOB_Oracle duck1 d:\mp\book\images2\duck1.jpg$ java InsertBLOB_Oracle duck2 d:\mp\book\images2\duck2.jpg$ java InsertBLOB_Oracle tiger1 d:\mp\book\images2\tiger1.jpg$ java InsertBLOB_Oracle tiger2 d:\mp\book\images2\tiger2.jpg$ java InsertBLOB_Oracle tiger3 d:\mp\book\images2\tiger3.jpg$ java InsertBLOB_Oracle tiger4 d:\mp\book\images2\tiger4.jpg

5203ch05.qxd 8/11/05 4:24 PM Page 197

Page 227: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE198

Viewing the Oracle Database After Running the SolutionThis is the Oracle database after running the solution:

SQL> select id from my_pictures;

ID----------tiger1tiger2tiger3tiger4duck1duck2

6 rows selected.

The following example demonstrates how to retrieve bytes from a BLOB:

ResultSet rs = null;Statement stmt = null;Connection conn = null;Blob blob = nullInputStream is = null;try {

conn = getConnection();stmt = conn.createStatement();String query = "SELECT col_blob FROM mysql_all_table";rs = stmt.executeQuery(query);if (rs.next()) {

// Get the BLOB from the result setblob = rs.getBlob("col_blob");

// Get the number bytes in the BLOBlong blobLength = blob.length();

// Get bytes from the BLOB in a byte arrayint pos = 1; // position is 1-basedint len = 10;byte[] bytes = blob.getBytes(pos, len);

// Get bytes from the BLOB using a streamis = blob.getBinaryStream();int b = is.read();

}}catch (IOException io) {

// handle the IOException}catch (SQLException se) {

// handle the SQLException}catch (Exception e) {

// handle the Exception}finally {// close ResultSet, Statement, Connection// close InputStream

}

5203ch05.qxd 8/11/05 4:24 PM Page 198

Page 228: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 199

5-25. How Do You Get CLOB Data from a Database Table?A CLOB is a Character Large OBject. You can use it to store large text data (such as ASCII/text files,PostScript files, and serialized objects). The java.sql.Clob object corresponds to a CLOB LOCATOR inthe SQL-99 standard.

To show how to retrieve data from CLOB columns, I will first provide a general solution and thenprovide the vendor-specific (MySQL and Oracle) solutions.

General Solution: Retrieving Data from CLOB ColumnsAssume that the employees table has two columns: the first column is VARCHAR(10), and the secondcolumn is a CLOB data type. The following code snippet shows how to retrieve CLOB values from theResultSet object using the getClob() method:

ResultSet rs = null;Statement stmt = null;Connection conn = null;BufferedReader reader = null;try {

// get a Connection objectconn = getConnection();// create a scrollable ResultSet objectString query = "select emp_id, emp_resume from employees";stmt = connection.createStatement(

ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

rs = stmt.executeQuery(query);while (rs.next()) {

String id = rs.getString(1);Clob resume = rs.getClob(2);int clobLength = (int) resume.length();// create a buffer to read the stream into a character arrayreader = new BufferedReader(resume.getCharacterStream());char[] buffer = new char[ clobLength ];reader.read( buffer, 0, clobLength );

}}catch (SQLException e) {

// handle the exception}finally {

// close database and other resourcesreader.close();

}

MySQL/Oracle Solutions: Retrieving Data from CLOB ColumnsUsing MySQL’s driver or Oracle 10 drivers, you can use one of the following methods (which returnsthe entire CLOB data):

ResultSet.getString(clob_column_index)ResultSet.getString(clob_column_name)

5203ch05.qxd 8/11/05 4:24 PM Page 199

Page 229: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE200

5-26. How Do You Match Using Wildcards in a SQL Statement?SQL provides wildcard matching of text using the LIKE clause. The following is a SQL statement thatuses a LIKE clause and a wildcard character. This SQL statement will find all rows with names thatstart with Pat.

SELECT * FROM my_table WHERE name LIKE 'Pat%'

Two wildcard characters are available. The underscore (_) matches any character. The percentsign (%) matches zero or more characters.

try {// Create a statementStatement stmt = connection.createStatement();

// Select the row if col_string contains the word patString sql = "SELECT * FROM my_table WHERE col_string LIKE '%pat%'";

// Select the row if col_string ends with the word patsql = "SELECT * FROM my_table WHERE col_string LIKE 'pat%'";

// Select the row if col_string starts with abc and ends with xyzsql = "SELECT * FROM my_table WHERE col_string LIKE 'abc%xyz'";

// Select the row if col_string equals the word pat%sql = "SELECT * FROM my_table WHERE col_string LIKE 'pat\\%'";

// Select the row if col_string has 3 characters and// starts with p and ends with tsql = "SELECT * FROM my_table WHERE col_string LIKE 'p_t'";

// Select the row if col_string equals p_tsql = "SELECT * FROM my_table WHERE col_string LIKE 'p\\_t'";

// Execute the queryResultSet resultSet = stmt.executeQuery(sql);

}catch (SQLException e) {

// handle the exception}

5-27. How Do You Read/Extract Data from a Microsoft ExcelSpreadsheet File?Microsoft Excel is a spreadsheet program that you can use to perform numerical calculations andbookkeeping tasks. Valuable corporate data is often stored in Microsoft Excel spreadsheets. Microsoftrefers to Microsoft Excel sheets (so-called tables) as [Sheet1$], [Sheet2$], and so on. (This kind ofnaming tables is not standard in the relational database industry.) For details, see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q295646.

How It WorksAssume you have created the Microsoft Excel spreadsheet shown in Figure 5-3 in a worksheet calledSheet1 (the default sheet name) and you have saved the file in C:\mp\msAccess\emps.xls.

5203ch05.qxd 8/11/05 4:24 PM Page 200

Page 230: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 201

Figure 5-3. Microsoft Excel spreadsheet

Since Microsoft Excel comes with an ODBC driver, I will show how to use the JDBC-ODBCbridge driver that comes with Sun Microsystems’ JDK to connect to the spreadsheet. In MicrosoftExcel, the name of the worksheet is equivalent to the database table name, and the header namesfound on the first row of the worksheet are equivalent to the table column/field names. Therefore,when accessing Microsoft Excel via JDBC, it is important to place your data with the headers start-ing at row 1.

To access a Microsoft spreadsheet, you need to create a new ODBC data source using theMicrosoft Excel driver.

Configuring ODBCThe first step is to set up an ODBC connection to your data source, which is the Microsoft ExcelC:\mp\msAccess\emps.xls spreadsheet file. Follow these steps:

1. Open the Control Panel, select Administrative Tools, and then select ODBC Data Sources.

2. Select System DSN, and then the click the Add button.

3. Select the Microsoft Excel Driver (*.xls) item, as shown in Figure 5-4.

4. Click the Finish button.

Figure 5-4. Creating a new data source

5203ch05.qxd 8/11/05 4:24 PM Page 201

Page 231: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE202

Giving Your Data Source a NameGive your data source a name; as shown in Figure 5-5, I have used the name excelDB. Next, click theSelect Workbook button. Select the name of the Microsoft Excel spreadsheet you want to use (thatis, C:\mp\msAccess\emps.xls), and then click OK. Your ODBC Excel data source is now complete.

Solution: A JDBC Program to Access/Read Microsoft ExcelThe following sample program reads data from Microsoft Excel and prints retrieved data to thestandard output:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.*;import jcb.meta.*;

public class TestAccessExcel {

public static Connection getConnection() throws Exception {String driver = "sun.jdbc.odbc.JdbcOdbcDriver";String url = "jdbc:odbc:excelDB";String username = ""; // username does not need to be empty.String password = ""; // password does not need to be empty.Class.forName(driver); // load JDBC-ODBC driverreturn DriverManager.getConnection(url, username, password);

}

public static void main(String args[]) {Connection conn=null;Statement stmt=null;ResultSet rs=null;try {

conn = getConnection();stmt = conn.createStatement();String excelQuery = "select * from [Sheet1$]";rs=stmt.executeQuery(excelQuery);

while(rs.next()){System.out.println(rs.getString("BadgeNumber")+" "+ rs.getString("FirstName")+" "+

Figure 5-5. Setting up an ODBC Microsoft Excel data source

5203ch05.qxd 8/11/05 4:24 PM Page 202

Page 232: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 203

rs.getString("LastName"));}

}catch (Exception e){

// handle the exceptione.printStackTrace();System.err.println(e.getMessage());

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Discussing the JDBC Program to Access Microsoft ExcelTo recap, you have done the following:

• You have connected to the Microsoft Excel ODBC data source the same way you would connectto any database server.

• The only significant difference is in the SELECT statement. Although your data is residing inthe worksheet called Sheet1, you need to refer to the sheet as Sheet1$ in your SQL statements.And because the dollar sign symbol is a reserved character in SQL, you have to encapsulatethe word Sheet1$ in brackets, as shown in the previous code.

To run the JDBC program to access Microsoft Excel, use this code:

$ javac TestAccessExcel.java$ java TestAccessExcel1100.0 Alex Smith1200.0 Mary Dell1300.0 Jeff Borg1400.0 Joe Goldman

5-28. How Do You Write Data to a Microsoft Excel Spreadsheet File?The JDBC API allows you to write to Microsoft Excel. Here, I will show how to create new recordsusing Microsoft Excel spreadsheets.

To access a Microsoft Excel spreadsheet, you need to create a new ODBC data source using theMicrosoft Excel driver. You will then give it the data source name (DSN) of excelDB and have it pointto file C:\mp\msAccess\emps.xls. The next sections show these steps in detail.

Configuring ODBCThe first step is to set up an ODBC connection to your data source, which is the Microsoft ExcelC:\mp\msAccess\emps.xls spreadsheet file. To set up Excel as a data source, follow these steps:

1. Open the Control Panel, select Administrative Tools, and then select ODBC Data Sources.

2. Select System DSN, then click the Add button.

3. Select the Microsoft Excel Driver (*.xls) item.

4. Click the Finish button.

5203ch05.qxd 8/11/05 4:24 PM Page 203

Page 233: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE204

Giving Your Data Source a NameAs you did when accessing/reading data from Microsoft Excel, you need to give your data sourcea name. (This example is using excelDB.) Next, click the Select Workbook button. Select the name ofthe Excel spreadsheet you want to use (that is, C:\mp\msAccess\emps.xls), and then click OK. YourODBC Excel data source is now complete.

To be able to write new data (using your JDBC program) to your spreadsheet file, make surethat the Read Only check box is unchecked (not set); if the Read Only check box is checked, thenyou cannot write new data to the file.

Solution: A JDBC Program to Write into Microsoft ExcelThis code shows how to write data into Microsoft Excel:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.*;import jcb.meta.*;

public class TestWriteExcel {public static Connection getConnection() throws Exception {

String driver = "sun.jdbc.odbc.JdbcOdbcDriver";String url = "jdbc:odbc:excelDB";String username = "";String password = "";Class.forName(driver); // load JDBC-ODBC driverreturn DriverManager.getConnection(url, username, password);

}public static void main(String args[]) {

Connection conn=null;Statement stmt=null;ResultSet rs=null;try {

conn = getConnection();stmt = conn.createStatement();String excelQuery = "insert into [Sheet1$](BadgeNumber, "+"FirstName, LastName) values('9999', 'Al', 'Kent')";

stmt.executeUpdate(excelQuery);}catch (Exception e){

// handle the exceptione.printStackTrace();System.err.println(e.getMessage());

}finally {

// release database resourcesDatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Discussing the JDBC Program to Access Microsoft ExcelTo recap, you have done the following:

5203ch05.qxd 8/11/05 4:24 PM Page 204

Page 234: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 205

• You have connected to the Microsoft Excel ODBC data source the same way you would con-nect to any database server.

• The only significant difference is in the INSERT statement. Although your data is residing inthe worksheet called Sheet1, you have to refer to the sheet as Sheet1$ in your SQL state-ments. And because the dollar sign symbol is a reserved character in SQL, you have toencapsulate the word Sheet1$ in brackets, as shown in the previous code.

Running the JDBC Program to Write Data into Microsoft ExcelTo run the program, use this code:

$ javac TestWriteExcel.java$ java TestWriteExcel

Viewing the Content of Microsoft Excel After Inserting New DataFigure 5-6 shows the Microsoft Excel spreadsheet after inserting the new data.

Figure 5-6. After inserting new data

5-29. Which Is the Preferred Collection Class to Use for StoringDatabase Result Sets?When retrieving database results, you can use java.util.ArrayList or java.util.LinkedList.(Both of these classes implement the java.util.List interface.) The following is according to SunMicrosystems (http://java.sun.com/developer/JDCTechTips/2002/tt0910.html):

• Appending elements to the end of a list has a fixed, averaged cost for both ArrayList andLinkedList. For ArrayList, appending typically involves setting an internal array location tothe element reference but occasionally results in the array being reallocated. For LinkedList,the cost is uniform and involves allocating an internal Entry object.

• Inserting or deleting elements in the middle of an ArrayList implies that the rest of thelist must be moved. Inserting or deleting elements in the middle of a LinkedList has a fixedcost.

• A LinkedList does not support efficient random access.

• An ArrayList has space overhead in the form of reserve capacity at the end of the list.A LinkedList has significant space overhead per element.

• Sometimes a Map structure is a better choice than a List.

5203ch05.qxd 8/11/05 4:24 PM Page 205

Page 235: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE206

Adding Database Results to the Rear of the ListTherefore, in retrieving database results, if you are adding objects to the rear (tail) of the list, thebest collection implementation to use is the ArrayList. The benefits include the following:

• Retaining the original retrieval order of records

• Quick insertion at the tail

The program structure for retrieving database results is as follows:

ResultSet rs = stmt.executeQuery("...");List list = new ArrayList();while(rs.next()) {

list.add(result.getString("column-name"));}

Note that if the result set (the ResultSet object) has multiple columns, you have to combinethem into your own data structure for each row.

The following code is proof that ArrayList is faster than LinkedList:

import java.util.List;import java.util.ArrayList;import java.util.LinkedList;

public class ListDemo {// number of objects to add to liststatic final int SIZE = 1000000;

static long timeList(List list) {long start = System.currentTimeMillis();Object obj = new Object();

for (int i = 0; i < SIZE; i++) {// add object to the rear of the listlist.add(obj);

}

return System.currentTimeMillis() - start;}

public static void main(String args[]) {// do timing for LinkedListSystem.out.println("time for LinkedList = " +

timeList(new LinkedList()));

// do timing for ArrayListSystem.out.println("time for ArrayList = " +

timeList(new ArrayList()));}

}

The following runs the demo:

$ javac ListDemo.java$ java ListDemotime for LinkedList = 620time for ArrayList = 210

As you can see, adding objects to the end of ArrayList is faster.

5203ch05.qxd 8/11/05 4:24 PM Page 206

Page 236: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE 207

Adding Database Results to the Head of the ListIn retrieving database results, if you are adding objects to the head (front) of the list, the best collec-tion implementation to use is the LinkedList. The benefits include the following:

• Retaining the reverse of original retrieval order of records

• Quick insertion at the head

The program structure for retrieving database results is as follows:

ResultSet rs = stmt.executeQuery("...");List list = new LinkedList();while(rs.next()) {list.add(0, result.getString("column-name"));

}

Note that if the result set (the ResultSet object) has multiple columns, you have to combinethem into your own data structure for each row.

The following is proof that LinkedList is faster than ArrayList:

import java.util.List;import java.util.ArrayList;import java.util.LinkedList;

public class ListDemoHead {static final int SIZE = 100000;

static long timeList(List list) {long start = System.currentTimeMillis();Object obj = new Object();for (int i = 0; i < SIZE; i++) {

// add object to the head of the listlist.add(0, obj);

}

return System.currentTimeMillis() - start;}

public static void main(String args[]) {// do timing for LinkedListSystem.out.println("time for LinkedList = " +

timeList(new LinkedList()));

// do timing for ArrayListSystem.out.println("time for ArrayList = " +

timeList(new ArrayList()));}

}

The following runs the demo:

$ javac ListDemoHead.java$ java ListDemoHeadtime for LinkedList = 80time for ArrayList = 7011

As you can see, adding objects to the head of LinkedList is faster.

5203ch05.qxd 8/11/05 4:24 PM Page 207

Page 237: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 5 ■ EXPLORING THE RESULTSET INTERFACE208

5-30. How Do You Retrieve a Whole Row/Record of Data at OnceInstead of Calling an Individual ResultSet.getXXX() Method forEach Column?The ResultSet object is a two-dimensional table of data representing a database result set, which isusually generated by executing a statement that queries the database. A ResultSet object representsdata retrieved in table form. The ResultSet interface does not have any method to retrieve a wholerow/record of data at once. The ResultSet.getXXX() methods are the only way to retrieve data froma ResultSet object, which means you have to make a method call for each column of a row.

If you need to pass the entire row/record as a single object, then you can do the following: getall the column values and their associated data types, and then create a Row (user-defined object;you have to define this object) using these values. According to Sun Microsystems (http://java.sun.com/products/jdbc/index.jsp), it is unlikely that using ResultSet.getXXX() methods is the cause ofany performance problem. Also note that using ResultSet.getXXX(index-number) is usually fasterthan using ResultSet.getXXX(column-name); using column-name will require additional method callsto the database server for getting metadata information for column names. In general, usingResultSet.getXXX(index-number) is better than using ResultSet.getXXX(column-name) because ifthe database schema changes (such as renaming column names), then you do not need to modifyyour JDBC code at all.

5203ch05.qxd 8/11/05 4:24 PM Page 208

Page 238: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Working with Scrollable andUpdatable ResultSet Objects

The purpose of this chapter is to provide Java snippets, reusable code samples, classes, and meth-ods that deal with the “scrollable” and “updatable” ResultSet objects. When writing this chapter,I relied on JDK 1.4 and the final release of the JDBC 3.0 specification.

6-1. What Is a Scrollable ResultSet?A ResultSet is scrollable if you have the ability to move its cursor backward as well as forward. TheJDBC 2.0 API introduced the scrollable ResultSet concept. The ResultSet object has methods thatlet you move the cursor to a particular row and check the position of the cursor. Scrollable ResultSetobjects make it possible to create a GUI tool for browsing ResultSet objects; this is probably one ofthe main uses of this feature. Another use is moving to a particular row in order to update it.

Before you can use a scrollable ResultSet, you have to create one. The following snippet showsone way to create a scrollable ResultSet object:

// assume getConnection() returns a Connection objectConnection conn = getConnection();...Statement stmt = conn.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);

String query = "SELECT id, name FROM employees";ResultSet rs = stmt.executeQuery(query);

When creating a Statement object, you need to specify two arguments to the method createStatement(). The first argument indicates the type of a ResultSet object and can be one ofthree constants:

• ResultSet.TYPE_FORWARD_ONLY: A constant indicating the type for a ResultSet object whosecursor may move only forward (creates a nonscrollable ResultSet object)

• ResultSet.TYPE_SCROLL_INSENSITIVE: A constant indicating the type for a ResultSet objectthat is scrollable but generally not sensitive to changes made by others

• ResultSet.TYPE_SCROLL_SENSITIVE: A constant indicating the type for a ResultSet object thatis scrollable and generally sensitive to changes made by others

209

C H A P T E R 6

■ ■ ■

5203ch06.qxd 8/11/05 4:25 PM Page 209

Page 239: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS210

The second argument is one of two ResultSet constants for specifying whether a result set isread-only or writable/updatable:

• ResultSet.CONCUR_READ_ONLY: A constant indicating the concurrency mode for a ResultSetobject that may not be updated

• ResultSet.CONCUR_UPDATABLE: A constant indicating the concurrency mode for a ResultSet

object that may be updated

The important point to remember is that if you specify a ResultSet type, you must also specifywhether it is read-only or writable/updatable. The following example shows a scrollable ResultSetusing a sample program, DemoScrollableResultSet.

SolutionThis solution uses the following ResultSet methods:

• next(): Moves the cursor down one row from its current position

• afterLast(): Moves the cursor to the end of this ResultSet object, just after the last row

• previous(): Moves the cursor to the previous row in this ResultSet object

Here’s the solution:

1 import java.sql.ResultSet;2 import java.sql.Statement;3 import java.sql.Connection;4 import java.sql.DriverManager;5 import java.sql.SQLException;67 import jcb.util.DatabaseUtil;8 import jcb.db.VeryBasicConnectionManager;910 public class DemoScrollableResultSet {1112 public static void main(String[] args) {13 Connection conn = null;14 Statement stmt = null;15 ResultSet rs = null;16 String dbVendor = args[0]; // vendor = {"mysql", "oracle" }17 try {18 conn = VeryBasicConnectionManager.getConnection(dbVendor);19 System.out.println("--DemoScrollableResultSet begin--");20 System.out.println("conn="+conn);21 System.out.println("-------");2223 // prepare query24 String query = "select id, name from employees";2526 // create a statement27 stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,28 ResultSet.CONCUR_READ_ONLY);2930 // execute query and return result as a ResultSet31 rs = stmt.executeQuery(query);32

5203ch06.qxd 8/11/05 4:25 PM Page 210

Page 240: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

33 // extract data from the ResultSet34 // scroll from top35 while (rs.next()) {36 String id = rs.getString(1);37 String name = rs.getString(2);38 System.out.println("id=" + id + " name=" + name);39 }40 System.out.println("---------");4142 // scroll from the bottom43 rs.afterLast();44 while (rs.previous()) {45 String id = rs.getString(1);46 String name = rs.getString(2);47 System.out.println("id=" + id + " name=" + name);48 }49 System.out.println("---------");5051 System.out.println("--DemoScrollableResultSet end--");52 }53 catch(Exception e){54 e.printStackTrace();55 System.exit(1);56 }57 finally {58 // release database resources59 DatabaseUtil.close(rs);60 DatabaseUtil.close(stmt);61 DatabaseUtil.close(conn);62 }63 }64 }

Running the Solution for the MySQL DatabaseHere’s how to run the solution for the MySQL database:

$ javac DemoScrollableResultSet.java$ java DemoScrollableResultSet mysql--DemoScrollableResultSet begin--conn=com.mysql.jdbc.Connection@19616c7---------------id=11 name=Alex Smithid=22 name=Don Knuthid=33 name=Mary Kentid=44 name=Monica Seles---------------id=44 name=Monica Selesid=33 name=Mary Kentid=22 name=Don Knuthid=11 name=Alex Smith-----------------DemoScrollableResultSet end--

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 211

5203ch06.qxd 8/11/05 4:25 PM Page 211

Page 241: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

6-2. How Do You Determine If a Database Supports ScrollableResultSets?A scrollable ResultSet object allows the cursor to be moved to any row in the ResultSet. You can useDatabaseMetaData to determine if a database supports scrollable ResultSet objects.

The following snippet shows how to check whether scrollable ResultSet objects are supported:

Connection conn = null;DatabaseMetaData dbmd = null;try {

// assume getConnection() returns a Connection objectconn = getConnection();dbmd = conn.getMetaData();if (dbmd == null) {

// database metadata not supported// cannot determine scrollability using DatabaseMetaData

}else {

if (dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)) {// insensitive scrollable result sets are supported

}if (dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)) {

// sensitive scrollable result sets are supported}if (!dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE) &&

!dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)) {// updatable result sets are not supported

}}

}catch (SQLException e) {

// handle the exception}finally {

// close database resources such as Connection object ...}

6-3. How Do You Create a Scrollable ResultSet?A scrollable ResultSet object allows the cursor to be moved to any row in the ResultSet.

If a JDBC driver supports ResultSet scrollability, then you can use the JDBC API to createa scrollable ResultSet object. The java.sql.Connection interface has the following methods forcreating scrollable ResultSet objects (for details, refer to the JDK documentation):

Statement createStatement(int resultSetType,int resultSetConcurrency)

// Creates a Statement object that will generate ResultSet// objects with the given type and concurrency.

Statement createStatement(int resultSetType,int resultSetConcurrency,int resultSetHoldability)

// Creates a Statement object that will generate ResultSet// objects with the given type, concurrency, and holdability.

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS212

5203ch06.qxd 8/11/05 4:25 PM Page 212

Page 242: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency)

// Creates a PreparedStatement object that will generate// ResultSet objects with the given type and concurrency.

PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency,int resultSetHoldability)

// Creates a PreparedStatement object that will generate// ResultSet objects with the given type, concurrency, and holdability.

CallableStatement prepareCall(String sql,int resultSetType,int resultSetConcurrency)

// Creates a CallableStatement object that will generate// ResultSet objects with the given type and concurrency.

CallableStatement prepareCall(String sql,int resultSetType,int resultSetConcurrency,int resultSetHoldability)

// Creates a CallableStatement object that will generate// ResultSet objects with the given type and concurrency.

Usage: Creating an Insensitive Scrollable ResultSetThe following shows how to create an insensitive scrollable ResultSet:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

// Create an insensitive scrollable result setstmt = connection.createStatement(

ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

// Create the desired scrollable ResultSet objectString query = "select id, name from employees";rs = stmt.executeQuery(query);

}catch (SQLException e) {

// handle the exception}finally {

// close resources: ResultSet, Statement, and Connection objects}

Usage: Creating a Sensitive Scrollable ResultSetThe following shows how to create a sensitive scrollable ResultSet:

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 213

5203ch06.qxd 8/11/05 4:25 PM Page 213

Page 243: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

conn = getConnection(); // get a Connection object

// Create a sensitive scrollable result setstmt = connection.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);

// Create the desired scrollable ResultSet objectString query = "select id, name from employees";rs = stmt.executeQuery(query);

}catch (SQLException e) {

// handle the exception}finally {

// close resources: ResultSet, Statement, and Connection objects}

6-4. How Do You Determine If a ResultSet Is Scrollable?Given a ResultSet object, how do you determine whether that ResultSet is scrollable? By using theResultSet object’s getType() method, you can answer this question:

public static boolean isScrollable(ResultSet rs) {if (rs == null) {

return false;}

try {// get type of the result setint type = rs.getType();

if ((type == ResultSet.TYPE_SCROLL_INSENSITIVE) ||(type == ResultSet.TYPE_SCROLL_SENSITIVE)) {// Result set is scrollablereturn true;

}else {

// Result set is not scrollablereturn false;

}}catch (SQLException e) {

return false;}

}

6-5. How Do You Move the Cursor in a Scrollable ResultSet?A scrollable ResultSet object has a set of specific methods for moving cursors. Table 6-1 lists therecord scrolling methods (for details, see the JDK documentation).

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS214

5203ch06.qxd 8/11/05 4:25 PM Page 214

Page 244: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Table 6-1. ResultSet Object’s Scrolling Methods

Method Semantics

first() Moves to the first record

last() Moves to the last record

next() Moves to the next record

previous() Moves to the previous record

beforeFirst() Moves to immediately before the first record

afterLast() Moves to immediately after the last record

absolute(int) Moves to an absolute row number, and takes a positive or negativeargument

relative(int) Moves backward or forward a specified number of rows, and takesa positive or negative argument

The following example demonstrates various methods for moving the cursor in a scrollableResultSet object:

import java.sql.*;import jcb.util.DatabaseUtil;

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

// Create a scrollable result setstmt = connection.createStatement(

ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

// create your desired SQL queryString query = ""SELECT id, name FROM employees";

// create scrollable ResultSet objectrs = stmt.executeQuery(query);

// Move cursor forwardwhile (rs.next()) {

// Get data at cursorString id = rs.getString(1);String name = rs.getString(2);

}

// Move cursor backwardwhile (rs.previous()) {

// Get data at cursorString id = rs.getString(1);String name = rs.getString(2);

}

// Move cursor to the first rowrs.first();

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 215

5203ch06.qxd 8/11/05 4:25 PM Page 215

Page 245: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

// Move cursor to the last rowrs.last();

// Move cursor to the end, after the last rowrs.afterLast();

// Move cursor to the beginning, before the first row.// cursor position is 0.rs.beforeFirst();

// Move cursor to the second rowrs.absolute(2);

// Move cursor to the last rowrs.absolute(-1);

// Move cursor to the second-to-last rowrs.absolute(-2);

// Move cursor down 5 rows from the current row. If this moves// cursor beyond the last row, cursor is put after the last rowrs.relative(5);

// Move cursor up 3 rows from the current row. If this moves// cursor beyond the first row, cursor is put before the first rowrs.relative(-3);

}catch (SQLException e) {

// handle the exception}finally {

// close database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

6-6. How Do You Get the Cursor Position in a Scrollable Result Set?The following code shows how to get the cursor position in a given scrollable ResultSet object:

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

get a Connection objectconn = getConnection();// Create a scrollable result setstmt = connection.createStatement(

ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

rs = stmt.executeQuery("SELECT * FROM my_table");

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS216

5203ch06.qxd 8/11/05 4:25 PM Page 216

Page 246: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

// Get cursor positionint pos = rs.getRow(); // 0boolean b = rs.isBeforeFirst(); // true

// Move cursor to the first rowrs.next();

// Get cursor positionpos = rs.getRow(); // 1b = rs.isFirst(); // true

// Move cursor to the last rowrs.last();// Get cursor positionpos = rs.getRow(); // If table has 10 rows, value would be 10b = rs.isLast(); // true

// Move cursor past last rowrs.afterLast();

// Get cursor positionpos = rs.getRow(); // If table has 10 rows, value would be 11b = rs.isAfterLast(); // true

}catch (SQLException e) {

// handle exception}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

6-7. How Do You Get the Number of Rows in a Table Usinga Scrollable ResultSet?The following example gets the number of rows in a scrollable ResultSet object by moving the cursorto the last row of the ResultSet object and then calling the ResultSet.getRow() method. In general,this method is not a proper way of finding the number of rows; you should use SQL query selectcount(*) from <table-name> for finding the number of rows in a given table. This example demon-strates the flexibility of scrollable ResultSet objects.

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

// create a scrollable ResultSet objectString query = "select id from employees";

stmt = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

rs = stmt.executeQuery(query);

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 217

5203ch06.qxd 8/11/05 4:25 PM Page 217

Page 247: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

// move to the end of the result setrs.last();

// get the row number of the last row, which is also the row countint rowCount = rs.getRow();

// now you may move the cursor to the front of this ResultSet object,// just before the first rowrs.beforeFirst();

}catch (SQLException e) {

// handle the exception}finally {

// close database resources}

I have provided a complete solution, the GetNumberOfRowsScrollableResultSet class, for getting the number of rows from a scrollable ResultSet object; you can downloadGetNumberOfRowsScrollableResultSet from this book’s Web site. To save space, here I will show only how the solution works:

$ javac GetNumberOfRowsScrollableResultSet_MySQL.java$ java GetNumberOfRowsScrollableResultSet_MySQL------GetNumberOfRowsScrollableResultSet_MySQL begin---------conn=com.mysql.jdbc.Connection@19616c7---------------id=33id=44id=77id=88---------------rowCount=4------GetNumberOfRowsScrollableResultSet_MySQL end---------

6-8. How Do You Determine If a Database Supports UpdatableResultSets?An updatable ResultSet object allows you to modify data in a table by using the ResultSet methodsrather than by sending SQL queries to the database server.

You can use the following snippet to determine if your database supports an updatable ResultSet:

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;try {

get a Connection objectconn = getConnection();

DatabaseMetaData dbmd = conn.getMetaData();if (dbmd == null) {

// impossible to make a decision// because metadata is not supportedthrow new Exception("DatabaseMetaData not supported");

}

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS218

5203ch06.qxd 8/11/05 4:25 PM Page 218

Page 248: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

if (dbmd.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE)) {

// Updatable ResultSets are supported}else {

// Updatable ResultSets are not supported}

}catch (SQLException e) {

// handle the exception}finally {

DatabaseUtil.close(conn);}

6-9. How Do You Create an Updatable ResultSet?An updatable ResultSet object allows you to modify data in a table through the ResultSet.

If the database does not support updatable ResultSet objects, the result sets returned fromexecuteQuery() will be read-only. To get updatable results, the Statement object used to create theresult sets must have the concurrency type ResultSet.CONCUR_UPDATABLE. The query of an updatableresult set must specify the primary key as one of the selected columns and select from only one table.For some JDBC drivers, the SQL query SELECT * FROM my_table will return a read-only result set, somake sure you specify the column names.

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

conn = getConnection();// create a statement that will return// updatable result setsstmt = conn.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

// Primary key pk_column must be specified// so that the result set is updatablers = stmt.executeQuery("SELECT pk_column FROM my_table");

}catch (SQLException e) {

// handle exception}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

6-10. How Do You Determine If a ResultSet Is Updatable?Given a ResultSet object, how do you determine if it is updatable? Using the ResultSet.getConcurrency()method, you can determine the updatability of a given ResultSet object:

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 219

5203ch06.qxd 8/11/05 4:25 PM Page 219

Page 249: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

String query = "select id from employees";

// ...parameters here enable/disable ResultSet updatability...stmt = conn.createStatement(...parameters);

// create a ResultSet objectrs = stmt.executeQuery(query);

// get concurrency of the ResultSet objectint concurrency = resultSet.getConcurrency();

if (concurrency == ResultSet.CONCUR_UPDATABLE) {// ResultSet is updatable

}else {

// ResultSet is not updatable}

}catch (SQLException e) {

// handle the exception}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

6-11. How Do You Update a Row in a Database Table Using anUpdatable Result Set?Updating the current row of an updatable result set involves calling the ResultSet.updateXXX()methods followed by a call to updateRow():

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// Create an updatable result setstmt = conn.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS220

5203ch06.qxd 8/11/05 4:25 PM Page 220

Page 250: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

rs = stmt.executeQuery("SELECT * FROM my_table");

// Move cursor to the row to updaters.first();

// Update the value of column column_1 on that rowrs.updateString("column_1", "new data");

// Update the row; if autocommit is enabled,// update is committedrs.updateRow();

}catch (SQLException e) {

// handle exception}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

6-12. How Do You Cancel Updates to an Updatable ResultSet?You can cancel the effects of calling the Result.updateXXX() methods by calling cancelRowUpdates().Please note that you cannot cancel updates after you have called updateRow().

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// Create an updatable result setstmt = conn.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

rs = stmt.executeQuery("SELECT * FROM my_table");

// Move cursor to the row to updaters.first();

// Update the value of column column_1 on that rowrs.updateString("column_1", "new data");

// Discard the update to the rowrs.cancelRowUpdates();

}catch (SQLException e) {

// handle exception}

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 221

5203ch06.qxd 8/11/05 4:25 PM Page 221

Page 251: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

finally {DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

6-13. How Do You Insert a Row into a Database Table Using anUpdatable ResultSet?An updatable ResultSet object supports a specific row called the insert row. It is a buffer for holdingthe values of a new row. After you have filled the fields in the insert row, you can insert the new rowinto the database using the Result.insertRow() method.

How It WorksThe following snippet shows how to insert a new row into a table using an updatable ResultSet object:

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// Create an updatable result setstmt = conn.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

String query = "select id, name from employees";

// Create the desired ResultSet objectrs = stmt.executeQuery(query);

// Move cursor to the "insert row"rs.moveToInsertRow();

// Set values for the new row.rs.updateString("id", "66");rs.updateString("name", "Harrison Ford");

// Insert the rowrs.insertRow();

}catch (SQLException e) {

// handle exception}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS222

5203ch06.qxd 8/11/05 4:25 PM Page 222

Page 252: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

SolutionHere is the solution:

1 import java.sql.*;23 import jcb.util.DatabaseUtil;4 import jcb.db.VeryBasicConnectionManager;56 public class InsertRowUpdatableResultSet {78 public static void main(String[] args) {9 Connection conn = null;10 Statement stmt = null;11 ResultSet rs = null;12 try {13 String dbVendor = args[0]; // vendor = {"mysql", "oracle" }14 System.out.println("--InsertRowUpdatableResultSet begin--");15 conn = VeryBasicConnectionManager.getConnection(dbVendor);16 System.out.println("conn="+conn);1718 // prepare query19 String query = "select id, name from employees";2021 // create a statement22 stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,23 ResultSet.CONCUR_UPDATABLE);24 // execute query and return result as a ResultSet25 rs = stmt.executeQuery(query);2627 // extract data from the ResultSet28 // scroll from top29 while (rs.next()) {30 String id = rs.getString(1);31 String name = rs.getString(2);32 System.out.println("id=" + id + " name=" + name);33 }34 System.out.println("=======");3536 // Move cursor to the "insert row"37 rs.moveToInsertRow();3839 // Set values for the new row.40 rs.updateString("id", args[1]);41 rs.updateString("name", args[2]);4243 // Insert the new row44 rs.insertRow();4546 // scroll from the top again47 rs.beforeFirst();48 while (rs.next()) {49 String id = rs.getString(1);50 String name = rs.getString(2);51 System.out.println("id=" + id + " name=" + name);52 }53 System.out.println("--InsertRowUpdatableResultSet end--");54 }

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 223

5203ch06.qxd 8/11/05 4:25 PM Page 223

Page 253: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

55 catch(Exception e){56 e.printStackTrace();57 System.exit(1);58 }59 finally {60 // release database resources61 DatabaseUtil.close(rs);62 DatabaseUtil.close(stmt);63 DatabaseUtil.close(conn);64 }65 }66 }

Discussing the SolutionThe following explains the solution:

• Lines 1–4: Import the required classes and interfaces.

• Line 15: The VeryBasicConnectionManager.getConnection() method returns an instance ofa Connection object. You may alter this method to pool your connections for real-worldapplications.

• Lines 22–23: Create a Statement object, which will enable you to create an updatable andscrollable ResultSet object.

• Lines 25–33: Create an updatable and scrollable ResultSet object, and then iterate all the rows.

• Line 37: Move the cursor to the insert row. Note that this statement is outside a loop, andtherefore only one row will be inserted.

• Lines 40–41: Set values for the new row.

• Line 44: Insert the new row.

• Lines 47–52: Move the cursor to the first row, and then iterate all the rows again.

• Lines 59–64: Release database resources.

MySQL Database Before Running the SolutionThis is the MySQL database before running the solution:

mysql> select * from employees;+----+--------------+------+| id | name | age |+----+--------------+------+| 11 | Alex Smith | 25 || 22 | Don Knuth | 65 || 33 | Mary Kent | 35 || 44 | Monica Seles | 30 || 99 | Alex Edison | NULL |+----+--------------+------+5 rows in set (0.01 sec)

Running the Solution for the MySQL DatabaseThis is the solution for the MySQL database:

$ javac InsertRowUpdatableResultSet.java$ java InsertRowUpdatableResultSet mysql 777 "Donald Duck"

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS224

5203ch06.qxd 8/11/05 4:25 PM Page 224

Page 254: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

--InsertRowUpdatableResultSet begin--conn=com.mysql.jdbc.Connection@1c6f579id=11 name=Alex Smithid=22 name=Don Knuthid=33 name=Mary Kentid=44 name=Monica Selesid=99 name=Alex Edison=======id=11 name=Alex Smithid=22 name=Don Knuthid=33 name=Mary Kentid=44 name=Monica Selesid=99 name=Alex Edisonid=777 name=Donald Duck--InsertRowUpdatableResultSet end--

MySQL Database After Running the SolutionThis is the MySQL database after running the solution:

mysql> select * from employees;+-----+--------------+------+| id | name | age |+-----+--------------+------+| 11 | Alex Smith | 25 || 22 | Don Knuth | 65 || 33 | Mary Kent | 35 || 44 | Monica Seles | 30 || 777 | Donald Duck | NULL || 99 | Alex Edison | NULL |+-----+--------------+------+6 rows in set (0.00 sec)

Oracle Database Before Running the SolutionThis is the Oracle database before running the solution:

SQL> select * from employees;ID NAME AGE---------- -------------------- ----------11 Alex Smith 2522 Don Knuth 6533 Mary Kent 3544 Monica Seles 3099 Alex Edison

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ java InsertRowUpdatableResultSet oracle 777 "Donald Duck"--InsertRowUpdatableResultSet begin--conn=oracle.jdbc.driver.T4CConnection@2ce908id=11 name=Alex Smithid=22 name=Don Knuthid=33 name=Mary Kentid=44 name=Monica Selesid=99 name=Alex Edison

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 225

5203ch06.qxd 8/11/05 4:25 PM Page 225

Page 255: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

=======id=11 name=Alex Smithid=22 name=Don Knuthid=33 name=Mary Kentid=44 name=Monica Selesid=99 name=Alex Edison--InsertRowUpdatableResultSet end--

Oracle Database After Running the SolutionThis is the Oracle database after running the solution:

SQL> select * from employees;ID NAME AGE---------- -------------------- ----------777 Donald Duck11 Alex Smith 2522 Don Knuth 6533 Mary Kent 3544 Monica Seles 3099 Alex Edison

6 rows selected.

6-14. How Do You Delete a Row from a Database Table Using anUpdatable ResultSet?To delete your desired row from a ResultSet object, create a scrollable and updatable ResultSetobject, then point to your desired row, and finally invoke the deleteRow() method.

How It WorksThe following snippet demonstrates the required steps for deleting a row:

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// Create an updatable result setString query = "select id, name from employees";stmt = conn.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

rs = stmt.executeQuery(query);

// Delete the first rowrs.first();rs.deleteRow();

}

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS226

5203ch06.qxd 8/11/05 4:25 PM Page 226

Page 256: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

catch (SQLException e) {// handle exception

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

Usage: The Database Before Running the Test ProgramThis is the database before running the test program:

mysql> use octopus;Database changedmysql> select * from employees;+----+--------------+------+| id | name | age |+----+--------------+------+| 11 | Alex Smith | 45 || 22 | Don Knuth | 65 || 33 | Mary Kent | 35 || 44 | Monica Seles | 30 |+----+--------------+------+4 rows in set (0.00 sec)

The Script: Test Program, First RunThe following is the script for running the test program (first run):

$ javac DeleteRowUpdatableResultSet_MySQL.java$ java DeleteRowUpdatableResultSet_MySQL------DeleteRowUpdatableResultSet_MySQL begin---------conn=com.mysql.jdbc.Connection@19616c7---------------id=11 name=Alex Smithid=22 name=Don Knuthid=33 name=Mary Kentid=44 name=Monica Seles---------------id=22 name=Don Knuthid=33 name=Mary Kentid=44 name=Monica Seles---------------------DeleteRowUpdatableResultSet_MySQL end---------

Usage: The Database After the First RunThis is the database after the first run:

mysql> use octopus;Database changedmysql> select * from employees;

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 227

5203ch06.qxd 8/11/05 4:25 PM Page 227

Page 257: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

+----+--------------+------+| id | name | age |+----+--------------+------+| 22 | Don Knuth | 65 || 33 | Mary Kent | 35 || 44 | Monica Seles | 30 |+----+--------------+------+3 rows in set (0.00 sec)

mysql>

The Script: Test Program, Second RunThis is how to run the text program for a second time:

$ java DeleteRowUpdatableResultSet_MySQL------DeleteRowUpdatableResultSet_MySQL begin---------conn=com.mysql.jdbc.Connection@19616c7---------------id=22 name=Don Knuthid=33 name=Mary Kentid=44 name=Monica Seles---------------id=33 name=Mary Kentid=44 name=Monica Seles---------------------DeleteRowUpdatableResultSet_MySQL end---------

Usage: The Database After the Second RunThis is the database after the second run:

mysql> select * from employees;+----+--------------+------+| id | name | age |+----+--------------+------+| 33 | Mary Kent | 35 || 44 | Monica Seles | 30 |+----+--------------+------+2 rows in set (0.00 sec)

mysql>

Discussing the SolutionThe following explains the solution:

• Lines 1–6: Import the required classes and interfaces.

• Lines 10–18: The getConnection() method returns an instance of a Connection object. Youcan alter this method to pool your connections for real-world applications.

• Lines 21–29: Create a Connection object.

• Lines 34–40: Create a Statement object, which will enable you to create an updatable andscrollable ResultSet object.

• Lines 45–56: Create an updatable and scrollable ResultSet object, and then iterate all the rows.

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS228

5203ch06.qxd 8/11/05 4:25 PM Page 228

Page 258: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

• Lines 61–62: Point at the first row of the ResultSet object, and then delete it using thedeleteRow() method.

• Lines 67–72: Move the cursor to the first row, and then iterate all the rows again.

• Lines 81–86: Release the database resources.

6-15. How Do You Refresh a Row in an Updatable ResultSet?The following code shows how to refresh a row in an updatable ResultSet object. ResultSet.refreshRow()does the job. This method refreshes the current row with its most recent value in the database:

import java.sql.*;import jcb.util.Databaseutil;...Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// Create an updatable result setstmt = conn.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

rs = stmt.executeQuery("SELECT * FROM my_table");

// Use the result set...

// Retrieve the current values of the row from the databasers.refreshRow();

}catch (SQLException e) {

// handle exception}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}

CHAPTER 6 ■ WORKING WITH SCROLLABLE AND UPDATABLE RESULTSET OBJECTS 229

5203ch06.qxd 8/11/05 4:25 PM Page 229

Page 259: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch06.qxd 8/11/05 4:25 PM Page 230

Page 260: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Reading and Writing BLOBs

This chapter shows how to use JDBC’s rich data type BLOB. The BLOB type stores/retrieves largebinary objects such as PDF files, video clips, JPEG/GIF pictures, and Microsoft Word documents.Today’s databases (such as Oracle and MySQL) easily handle BLOBs.

This chapter’s focus will be on the following topics:

• How to insert/write a BLOB into a database

• How to retrieve/read a BLOB from a database

• How to update an existing BLOB in a database

• How to delete an existing BLOB in a database

7-1. What Is a BLOB?A BLOB is a Binary Large OBject (such as an image, PDF document, or audio data) in a database (rep-resented as a column object in a database record/row). Database vendors (such as Oracle, MySQL,DB2, and so on) implement BLOBs in different ways, but as long as you use the common interfacejava.sql.Blob, you should not care about their internal implementations. BLOBs can be large, up to2GB or more, depending on the database. In general, BLOBs are typically larger than a single block ofstorage in your database (which might cause performance problems). You should use BLOBs withcare and, whenever possible, try to use caching algorithms to improve the overall performance ofdatabase applications.

To summarize, a BLOB is any arbitrarily large piece of data that can be treated as autonomous.The JDBC type BLOB represents a SQL3 BLOB. Typically, a JDBC BLOB value is mapped to an instanceof the java.sql.Blob interface in the Java programming language. If a driver follows the standardimplementation, a Blob object logically points to the BLOB value on the server rather than contain-ing its binary data, greatly improving efficiency. The java.sql.Blob interface provides methods formaterializing the BLOB data on the client when that is desired.

Describing the java.sql.Blob InterfaceJDBC provides a single interface (java.sql.Blob) for handling large binary objects. According toJDK 1.4.2, java.sql.Blob is defined as follows:

231

C H A P T E R 7

■ ■ ■

5203ch07a.qxd 8/11/05 4:25 PM Page 231

Page 261: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS232

The representation (mapping) in the Java programming language of an SQL BLOB value. AnSQL BLOB is a built-in type that stores a Binary Large Object as a column value in a row ofa database table. By default drivers implement Blob using an SQL locator (BLOB), whichmeans that a Blob object contains a logical pointer to the SQL BLOB data rather than thedata itself. A Blob object is valid for the duration of the transaction in which it was created.

Methods in the interfaces ResultSet, CallableStatement, and PreparedStatement, such asgetBlob and setBlob allow a programmer to access an SQL BLOB value. The Blob interface pro-vides methods for getting the length of an SQL BLOB (Binary Large Object) value, formaterializing a BLOB value on the client, and for determining the position of a pattern ofbytes within a BLOB value. In addition, this interface has methods for updating a BLOB value.

Describing the java.sql.Blob Interface MethodsTable 7-1 lists the java.sql.Blob interface methods (according to JDK 1.4.2).

Table 7-1. The java.sql.Blob Interface Methods

Return Type Method Description

InputStream getBinaryStream() Retrieves the BLOB value designatedby this Blob instance as a stream

byte[] getBytes(long pos, int length) Retrieves all or part of the BLOB valuethat this Blob object represents as anarray of bytes

long length() Returns the number of bytes in theBLOB value designated by this Blobobject

long position(Blob pattern, Retrieves the byte position in the long stat) BLOB value designated by this Blob

object at which pattern begins

long position(byte[] pattern, Retrieves the byte position at long start) which the specified byte array

pattern begins within the BLOB valuethat this Blob object represents

OutputStream setBinaryStream(long pos) Retrieves a stream that can be usedto write to the BLOB value that thisBlob object represents

int setBytes(long pos, byte[] bytes) Writes the given array of bytes to theBLOB value that this Blob objectrepresents, starting at position pos,and returns the number of byteswritten

int setBytes(long pos, byte[] bytes, Writes all or part of the given byteint offset, int len) array to the BLOB value that this Blob

object represents and returns thenumber of bytes written

void truncate(long len) Truncates the BLOB value that thisBlob object represents to be lenbytes in length

5203ch07a.qxd 8/11/05 4:25 PM Page 232

Page 262: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 233

Oracle BLOBsOracle has only one type (called BLOB) to support the SQL BLOB data type. The oracle.sql.BLOB classimplements the java.sql.Blob interface. Oracle also supports a proprietary BFILE (binary file) datatype, implemented by the oracle.sql.BFILE class. (Using BFILE, the binary file is stored as an oper-ating system file, and the reference to that file is kept at the database level.) BFILE is not defined inJDBC but can be accessed by Oracle and JDBC APIs.

MySQL BLOBsMySQL has four kinds of BLOBs (the JDBC calls are the same for these four types):

• TINYBLOB: A binary object that is stored with its length. This cannot be a key. The maximumlength is 255 characters (8 bits).

• BLOB: A binary object that is stored with its length. This cannot be a key. The maximum lengthis 16,535 characters (16 bits).

• MEDIUMBLOB: A binary object that is stored with its length. This cannot be a key. The maximumlength is 16,777,216 characters (24 bits).

• LONGBLOB: A binary object that is stored with its length. This cannot be a key. The range is4,294,967,295 characters (32 bits).

7-2. How Do You Define a BLOB Data Type in a Table?Suppose that in a MyPictures table you want to store pictures (each picture has an ID and a name).Then you might define your table as in the following code listings.

Defining the Table: Oracle and MySqlThis code defines the table:

create table MyPictures (id INT PRIMARY KEY,name VARCHAR(20),photo BLOB

);

Creating the Table: OracleThis code creates a table based on Oracle:

$ sqlplus octopus/octopusSQL*Plus: Release 8.1.7.0.0 - Production on Fri Jun 7 13:37:25 2002

SQL> create table MyPictures (2 id INT PRIMARY KEY,3 name VARCHAR(20),4 photo BLOB5 );

Table created.

SQL> describe MyPictures;

5203ch07a.qxd 8/11/05 4:25 PM Page 233

Page 263: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS234

Name Null? Type------------------------------- -------- ------------ID NOT NULL NUMBER(38)NAME VARCHAR2(20)PHOTO BLOB

Creating the Table: MySQLThis code creates a table based on MySQL:

mysql> use octopus;Database changedmysql> create table MyPictures (

-> id INT PRIMARY KEY,-> name VARCHAR(20),-> photo BLOB-> );

Query OK, 0 rows affected (0.04 sec)

mysql> describe MyPictures;+-----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-----------+-------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || name | varchar(20) | YES | | NULL | || photo | blob | YES | | NULL | |+-----------+-------------+------+-----+---------+-------+3 rows in set (0.01 sec)

7-3. What Are the Restrictions for Using BLOBs?Every vendor has some restrictions on using the BLOB data type. Typical restrictions for BLOBs are asfollows:

• BLOB columns cannot be keys (primary or foreign).

• You cannot group or sort on a BLOB.

7-4. How Do You Create a java.sql.Blob Object?You can create a java.sql.Blob object in only one way, and that is by using a ResultSet object’smethods. Consider the MyPictures table defined earlier (using a MySql or Oracle database):

create table MyPictures (id INT PRIMARY KEY,name VARCHAR(20),photo BLOB

);

The ResultSet interface has two methods for creating a java.sql.Blob object:

• getBlob(int columnPosition)

• getBlob(String columnName)

In the following code listings, I will show how to create a java.sql.Blob object by using theoverloaded getBlob() methods.

5203ch07a.qxd 8/11/05 4:25 PM Page 234

Page 264: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 235

ResultSet.getBlob(int position)The following shows ResultSet.getBlob(int position):

Connection conn = <get-a-valid-connection-object>Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery("select photo from MyPictures");while (rs.next()) {

Blob blob = rs.getBlob(1);// now the Blob object is created and you can apply// methods defined in the java.sql.Blob interface...

}

ResultSet.getBlob(String columnName)The following shows ResultSet.getBlob(String columnName):

Connection conn = <get-a-valid-connection-object>Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery("select photo from MyPictures");while (rs.next()) {

Blob blob = rs.getBlob("photo");// now the Blob object is created and you can apply// methods defined in the java.sql.Blob interface...

}

7-5. How Do You Materialize BLOB Data?A SQL BLOB maps into a java.sql.Blob object. If you want to operate on the BLOB data, you must firstmaterialize it on the client. The java.sql.Blob interface has two methods for materializing the BLOBdata:

• getBinaryStream(): Materializes the BLOB value as an input stream (java.io.InputStream)

• getBytes(): Materializes part or all of the BLOB as an array of bytes

Materializing a BLOB Value As an Input Stream (java.io.InputStream)This shows how to materialize the BLOB value as an input stream:

//// prints out all bytes in the BLOB//byte b;java.sql.Blob blob = <a-valid-blob>java.io.InputStream input = blob.getBinaryStream();while ((b = input.read()) > -1) {

// process the byteSystem.out.println(b);

}

Materializing a BLOB As an Array of BytesThis shows how to materialize the BLOB as an array of bytes:

5203ch07a.qxd 8/11/05 4:25 PM Page 235

Page 265: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS236

//// prints out all bytes in the BLOB//long length;java.sql.Blob blob = <a-valid-blob>// note that the first byte is at position 1byte[] blobData = blob.getBytes(1, length);for (int i=0; i < length; i++) {

// process the byteSystem.out.println(blobData[i]);

}

7-6. How Do You Insert a New Record with a BLOB?Now that the MyPictures table (which includes a BLOB column) is defined, you should be able to useJDBC to insert new records (which will contain a photo as a BLOB data type). Suppose you want toinsert the following data:

id name photo filename1 n1 c:/temp/kournikova/zanna1.jpg2 n2 c:/temp/kournikova/zanna2.jpg3 n3 c:/temp/kournikova/zanna3.jpg4 n4 c:/temp/kournikova/zanna4.jpg5 n5 c:/temp/kournikova/zanna5.jpg

Your goal is to write a program that will accept an ID, name, and photo (as a filename) andinsert them into the MyPictures table. The client interface is as follows:

java InsertPictureToMySql <id> <name> <photo>java InsertPictureToOracle <id> <name> <photo>

Therefore, you need to develop two classes (InsertPictureToMySql.java and InsertPictureToOracle.java). To insert the first three records into a MySQL database, execute the following:

java InsertPictureToMySql 1 n1 c:/temp/kournikova/zanna1.jpgjava InsertPictureToMySql 2 n2 c:/temp/kournikova/zanna2.jpgjava InsertPictureToMySql 3 n3 c:/temp/kournikova/zanna3.jpg

To insert the first three records into an Oracle 9i database, execute the following:

java InsertPictureToOracle 1 n1 c:/temp/kournikova/zanna1.jpgjava InsertPictureToOracle 2 n2 c:/temp/kournikova/zanna2.jpgjava InsertPictureToOracle 3 n3 c:/temp/kournikova/zanna3.jpg

Solution Using MySQL: InsertPictureToMySql.javaHere’s the InsertPictureToMySql.java solution:

import java.io.*;import java.sql.*;import jcb.util.DatabaseUtil;public class InsertPictureToMySql {

String INSERT_PICTURE ="insert into MyPictures(id, name, photo) values (?, ?, ?)";

Connection conn = null;

/*** constructor

5203ch07a.qxd 8/11/05 4:25 PM Page 236

Page 266: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 237

*/public InsertPictureToMySql() throws SQLException {

DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());conn = DriverManager.getConnection(

"jdbc:mysql://localhost/octopus", "root", "root");}public static void main(String[] args)

throws Exception, IOException, SQLException {if ((args == null) || (args.length != 3)) {

System.err.println("Usage: java InsertPictureToMySql <id> <name> <photo>");System.exit(0);

}String id = DatabaseUtil.trimArgument(args[0]);String name = DatabaseUtil.trimArgument(args[1]);String photo = DatabaseUtil.trimArgument(args[2]);new InsertPictureToMySql().insert(id, name, photo);

}public void insert(String id, String name, String photo)

throws IOException, SQLException {FileInputStream fis = null;PreparedStatement ps = null;try {

// begin transactionconn.setAutoCommit(false);

File file = new File(photo);fis = new FileInputStream(file);ps = conn.prepareStatement(INSERT_PICTURE);ps.setString(1, id);ps.setString(2, name);ps.setBinaryStream(3, fis,(int)file.length());ps.executeUpdate();

// end transactionconn.commit();

}finally {

DatabaseUtil.close(ps);DatabaseUtil.close(fis);

}}protected void finalize() throws Throwable {

DatabaseUtil.close(conn);super.finalize();

}}

As you can see, the code for the MySQL database is straightforward, but that is not the casewith the Oracle database.

Solution Using Oracle: InsertPictureToOracle.javaIn Oracle, before you can insert a real BLOB, you need to insert an empty BLOB (called empty_blob()in Oracle). empty_blob() is an Oracle function call that creates an empty Blob object. Therefore, inOracle, you cannot just insert a Blob object into a column. First, create a column with empty_blob().Second, update that column with the real Blob object.

5203ch07a.qxd 8/11/05 4:25 PM Page 237

Page 267: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS238

Here’s the InsertPictureToOracle.java solution:

import java.io.*;import java.sql.*;import java.text.*;import jcb.util.DatabaseUtil;

// add these imports for access to the required Oracle classesimport oracle.jdbc.driver.*;import oracle.sql.BLOB;

public class InsertPictureToOracle {Connection conn;

public InsertPictureToOracle() throws SQLException {DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());conn = DriverManager.getConnection(

"jdbc:oracle:thin:@mparsian:1521:scorpian", "octopus", "octopus");}

public static void main(String[] args)throws Exception, IOException, SQLException {if ((args == null) || (args.length != 3)) {

System.err.println("Usage: java InsertPictureToOracle <id> <name> <photo>");System.exit(0);

}String id = DatabaseUtil.trimArgument(args[0]);String name = DatabaseUtil.trimArgument(args[1]);String photo = DatabaseUtil.trimArgument(args[2]);new InsertPictureToOracle().insert(id, name, photo);

}public void insert(String id, String name, String photo)

throws Exception, IOException, SQLException {int rows = 0;FileInputStream fin = null;OutputStream out = null;ResultSet result = null;Statement stmt = null;oracle.sql.BLOB oracleBlob = null;

try {conn.setAutoCommit(false);stmt = conn.createStatement();result = stmt.executeQuery("select id from MyPictures where id = "+ id);while (result.next()) {

rows++;}

if (rows > 1) {System.err.println("Too many rows!");System.exit(1);

}

result.close();result = null;

5203ch07a.qxd 8/11/05 4:25 PM Page 238

Page 268: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 239

if (rows == 0) {System.out.println("This creates the LOB locators");rows = stmt.executeUpdate("insert into MyPictures "+"(id, name, photo ) values ("+id+", ""+ name +"", empty_blob() )");

System.out.println(rows + " rows inserted");// Now retrieve the locatorrows = 0;result = stmt.executeQuery("select photo from MyPictures "+

"where id = "+id+ " for update nowait");result.next();oracleBlob = ((OracleResultSet)result).getBLOB(1);result.close();result = null;

}stmt.close();stmt = null;// Now that you have the locator, store the photoFile binaryFile = new File(photo);fin = new FileInputStream(binaryFile);out = oracleBlob.getBinaryOutputStream();// Get the optimal buffer size from the BLOBbyte[] buffer = new byte[oracleBlob.getBufferSize()];int length = 0;while ((length = fin.read(buffer)) != -1) {

out.write(buffer, 0, length);}// You have to close the output stream before// you commit, or the changes are lost!out.close();out = null;fin.close();fin = null;conn.commit();

}finally {

DatabaseUti.close(result);DatabaseUti.close(stmt);DatabaseUti.close(out);DatabaseUti.close(fin);

}}protected void finalize() throws Throwable {

DatabaseUti.close(conn);super.finalize();

}}

Discussing the InsertPictureToOracle Class: Populating a BLOB ColumnNote that handling BLOBs in Oracle is different from handling them in MySQL. The preceding exampledemonstrated how to populate a BLOB column by reading data from a stream. The following stepsassume that you have already created your Connection object (called conn) and Statement object(called stmt). MyPictures is the table that was created in the previous sections.

To write a given JPEG file to a BLOB, follow these steps:

5203ch07a.qxd 8/11/05 4:25 PM Page 239

Page 269: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS240

1. Begin by using SQL statements to create the BLOB entry in the table. Use Oracle’sempty_blob() function to create the BLOB locator.

rows = stmt.executeUpdate("insert into MyPictures (id, name, photo ) "+"values ("+id+", ""+ name +"", empty_blob() )");

2. Get the BLOB locator from the table.

result = stmt.executeQuery("select photo from MyPictures where id = "+id+ " for update nowait");

result.next();oracleBlob = ((OracleResultSet)result).getBLOB(1);

3. Declare a file handler for the picture file. This value will be used later to ensure that the entirefile is read into the BLOB. Next, create a FileInputStream object to read the contents of theJPEG file and an OutputStream object to retrieve the BLOB as a stream.

// Now that you have the locator,// store the photoFile binaryFile = new File(photo);fin = new FileInputStream(binaryFile);out = oracleBlob.getBinaryOutputStream();

4. Call getBufferSize() to retrieve the ideal buffer size to use in writing to the BLOB, and thencreate the buffer byte array.

byte[] buffer = new byte[oracleBlob.getBufferSize()];

5. Use the read() method to read the JPEG file to the byte array buffer and then use the write()method to write it to the BLOB. When you finish, close the input and output streams.

while ((length = fin.read(buffer)) != -1) {out.write(buffer, 0, length);

}

7-7. How Do You Select and Display a BLOB in a JFrame?The following example demonstrates how to retrieve a BLOB data type from the database. In this case,you will retrieve the photo identified by ID (id is the primary key for the MyPictures table) and dis-play it in its own JFrame. Given that the code is lengthy to accomplish this job, the example is splitover several pages. First, you perform a query to select the Blob object of interest (by providing theID) and pull it back to the client (also known as materializing the Blob object). The rest of the codesimply creates a JFrame to hold the retrieved image.

Develop the following class:

java BlobSelect <db-vendor-mysql> <id>java BlobSelect <db-vendor-oracle> <id>

The BlobSelect class accepts a database vendor (mysql or oracle) and an ID (the primary key tothe MyPictures table) and extracts and displays the desired BLOB in a JFrame.

Extracting BLOBs from MySQLFigure 7-1 shows the BLOB from MySQL by invoking this:

$ BlobSelect mysql 3

5203ch07a.qxd 8/11/05 4:25 PM Page 240

Page 270: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 241

Extracting BLOBs from OracleFigure 7-2 shows the BLOB from Oracle by invoking this:

$ java BlobSelect oracle 3

Solution: BlobSelect.java

The following shows the BlobSelect.java solution:

import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.sql.*;

Figure 7-1. Displaying the BLOB from MySQL

Figure 7-2. Displaying the BLOB from Oracle

5203ch07a.qxd 8/11/05 4:25 PM Page 241

Page 271: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS242

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

/*** This class displays a Blob object in a JFrame*/public class BlobSelect extends JPanel {

// look and feel constantspublic static final String METAL_LOOK_AND_FEEL =

"javax.swing.plaf.metal.MetalLookAndFeel";

/*** Constructor to display Blob object.* @param id the primary key to the MyPictures table* @param conn Connection object.*/public BlobSelect(int id, Connection conn) {...}

/*** Extract and return the Blob object.* @param id the primary key to the Blob object.* @param conn Connection object.*/public static byte[] getBLOB(int id, Connection conn) {...}

public static void main(String args[]) throws Exception {UIManager.setLookAndFeel(METAL_LOOK_AND_FEEL) ;JFrame frame = new JFrame("Blob Demo for MySQL Database");frame.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {System.exit(0);

}});

Connection conn = null;String dbVendor = args[0]; // { "mysql", "oracle" }int id = Integer.parseInt(args[1]);conn = VeryBasicConnectionManager.getConnection(dbVendor);frame.setContentPane(new BlobSelect(id, conn)) ;frame.pack();frame.setVisible(true);

}}

Solution: getBLOB()

The following shows the getBLOB() solution:

/*** Extract and return the Blob object.* @param id the primary key to the Blob object.* @param conn Connection object.*/public static byte[] getBLOB(int id, Connection conn)

throws Exception {

5203ch07a.qxd 8/11/05 4:25 PM Page 242

Page 272: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 243

ResultSet rs = null;PreparedStatement pstmt = null;String query = "SELECT photo FROM MyPictures WHERE id = ?" ;try {

pstmt = conn.prepareStatement(query) ;pstmt.setInt(1, id);rs = pstmt.executeQuery();rs.next();Blob blob = rs.getBlob("photo");// materialize BLOB onto clientreturn blob.getBytes(1, (int)blob.length());

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

Solution: Constructor BlobSelect()

The following shows the BlobSelect() solution:

/*** Constructor to display Blob object.* @param id the primary key to the MyPictures table* @param conn Connection object.*/public BlobSelect(int id, Connection conn)

throws Exception {// materialize BLOB onto clientImageIcon icon = new ImageIcon(getBLOB(id, conn)) ;JLabel photoLabel = new JLabel(icon) ;setLayout(new GridLayout(1, 1));add(photoLabel);

}

7-8. How Do You Delete an Existing BLOB from the OracleDatabase?The SQL DELETE statement deletes rows in a table. Its simple syntax is as follows:

DELETE FROM table_nameWHERE column_name = some_value

Say your goal is to delete an existing database record, which has a BLOB column. To delete therecord, you need the primary key for that record (to locate the record). For solving this problem, youwill use the MyPictures table. (The ID column is the primary key, and PHOTO is the BLOB column todelete.)

Viewing the Oracle Database Before DeletionHere is the MyPictures table before the deletion:

5203ch07a.qxd 8/11/05 4:25 PM Page 243

Page 273: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS244

SQL> desc MyPictures;Name Null? Type-------------------------------- -------- ------------ID NOT NULL NUMBER(38)NAME VARCHAR2(20)PHOTO BLOB

SQL> select id, name from MyPictures;

ID NAME---------- --------------------

10 goofy-1020 goofy-20

SQL>

Displaying BLOB DataFigure 7-3 shows the BLOB from Oracle by invoking this:

$ java BlobSelectOracle 10

Solution: DeleteOracleBlobRecordThe following is the DeleteOracleBlobRecord solution:

import java.sql.*;import java.io.*;import jcb.db.*;

/*** This class deletes an Oracle's BLOB record for a given PK.*/public class DeleteOracleBlobRecord {

private static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "scott";String password = "tiger";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

Figure 7-3. Displaying the BLOB from Oracle

5203ch07a.qxd 8/11/05 4:25 PM Page 244

Page 274: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 245

/*** deletes an Oracle's BLOB record for a given PK.* @param id the primary key to the BLOB record.*/private static void deleteBlobRecord(String id) throws Exception {

Connection conn = null ;PreparedStatement pstmt = null;String query = "delete from MyPictures where id = ?";try {

conn = getConnection();pstmt = conn.prepareStatement(query) ;pstmt.setString(1, id);pstmt.executeUpdate();

}finally {

DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

public static void main(String args[]) throws Exception {if (args.length != 1) {

System.out.println("usage: java DeleteOracleBlobRecord <id>");System.exit(1);

}deleteBlobRecord(args[0]) ;

}}

Deleting an Oracle BLOB RecordTo delete a BLOB record with a primary key of 10, you issue the following command:

$ java DeleteOracleBlobRecord 10

Viewing the Oracle Database After DeletionThis is the database after the deletion:

SQL> select id, name from MyPictures;

ID NAME---------- --------------------

20 goofy-20

7-9. How Do You Delete an Existing BLOB from the MySQLDatabase?The solution for MySQL is identical to Oracle’s solution with the exception of the getConnection()method. The getConnection() for MySQL is as follows. You can download the complete solution (theDeleteMySqlBlobRecord class) for MySQL from this book’s Web site.

/*** Get a MySQL connection object.*/public static Connection getConnection() throws Exception {

5203ch07a.qxd 8/11/05 4:25 PM Page 245

Page 275: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS246

String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

7-10. How Do You Serialize a Java Object to the Oracle Database?You could ask this question in another way: how can you save a Java object (that is, an instance ofany Java class) to a database for later use? Before serializing Java objects to the Oracle database, youwill now look at what Java object serialization is.

Object serialization provides a program with the ability to read or write a whole object to andfrom a raw byte stream. It allows Java objects and primitives to be encoded into a byte stream suit-able for streaming to some type of network or to a file system (or, more generally, to a transmissionmedium or storage facility); this is according to “The Wonders of Java Object Serialization” by BrianT. Kurotsuchi (http://www.acm.org/crossroads/xrds4-2/serial.html).

Serializing a Java object requires that it meets only one of two criteria. The class either mustimplement the java.io.Serializable interface, which has no methods (this is a Java marker inter-face) that you need to write, or must implement the Externalizable interface. (The Externalizableinterface gives a client more choices for serialization, which provides a possibility for customizingthe serialization process.) The Externalizable interface defines two methods:

• void readExternal(ObjectInput in): The object implements the readExternal method torestore its contents by calling the methods of DataInput for primitive types and readObjectfor objects, strings, and arrays.

• void writeExternal(ObjectOutput out): The object implements the writeExternal methodto save its contents by calling the methods of DataOutput for its primitive values or calling thewriteObject method of ObjectOutput for objects, strings, and arrays.

In other words, serialization is the storing of a Java object’s current state on any permanent/persistent storage media (such as a file system or a database) for later reuse. In the JDK, serializationtakes place using three things:

• Implementing the java.io.Serializable interface.

• Using ObjectOutputStream, you write an object to a stream.

• Using ObjectInputStream, you read an object from a stream.

Example: Serializing Java Objects to a FileThe following snippet shows how to serialize two Java objects to a file called myFile.ser:

FileOutputStream out = new FileOutputStream("myFile.ser");ObjectOutputStream stream = new ObjectOutputStream(out);stream.writeObject("my string");stream.writeObject(new Date());stream.flush();

Example: Unserializing (Deserializing) Java Objects from a FileThe following snippet shows how to deserialize Java objects from a file called myFile.ser:

5203ch07a.qxd 8/11/05 4:25 PM Page 246

Page 276: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 247

FileInputStream in = new FileInputStream("myFile.ser");ObjectInputStream stream = new ObjectInputStream(in);String myString = (String) stream.readObject();Date date = (Date) stream.readObject();

Example: Serializing Java Objects to the Oracle DatabaseAs mentioned, serialization is the process of converting an entire Java object and all its data andattributes into a serial form: a form that can be transmitted over a stream. Typically, Java objects areserialized and passed around using ObjectInputStream/ObjectOutputStream. The serialization takesplace automatically as part of the writeObject() method. Serialized Java objects can be deserializedas well. That is, they can be read from an ObjectInputStream and reconstructed into an exact copy ofthe object that was serialized. Like serialization, deserialization is transparent, taking place as partof the readObject() method.

Serialized objects can be written to (and read from) files/databases, which is a great way of per-manently storing the state of an object for future use or reference.

Preparing the Oracle Database for Serializing Java Objects

The following code shows how to create the table java_objects (to store Java objects) and thesequence java_object_sequence (to be able to create Java object identifiers or primary key values):

$ sqlplus octopus/octopusSQL*Plus: Release 9.2.0.1.0 - Production on Sat Oct 11 23:14:59 2003SQL> CREATE SEQUENCE java_object_sequence

2 INCREMENT BY 13 START WITH 14 NOMAXVALUE5 NOCYCLE6 ;

Sequence created.

SQL> CREATE TABLE java_objects (2 object_id NUMBER,3 object_name varchar(128),4 object_value BLOB DEFAULT empty_blob(),5 primary key (object_id));

Table created.

SQL> desc java_objects;Name Null? Type---------------- -------- -------------OBJECT_ID NOT NULL NUMBEROBJECT_NAME VARCHAR2(128)OBJECT_VALUE BLOB

SQL> select SEQUENCE_NAME, MIN_VALUE, MAX_VALUE, INCREMENT_BY, LAST_NUMBER2 from user_sequences;

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY LAST_NUMBER-------------------- ---------- ---------- ------------ -----------ID_SEQ 1 1.0000E+27 1 21JAVA_OBJECT_SEQUENCE 1 1.0000E+27 1 1

SQL> commit;

5203ch07a.qxd 8/11/05 4:25 PM Page 247

Page 277: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS248

Serializing/DeSerializing Java Objects

The following class, SerializeJavaObjects_Oracle, performs these tasks:

• Serializes a Java object (as a binary object) to the Oracle database

• Deserializes a Java object from the Oracle database

Here is SerializeJavaObjects_Oracle:

import java.io.*;import java.sql.*;import java.util.*;import oracle.jdbc.driver.*;import oracle.sql.*;import jcb.util.DatabaseUtil;

/*** The following class provides:* 1) how to serialize a Java object to the Oracle database.* 2) how to deserialize a Java object from the Oracle database.*/class SerializeJavaObjects_Oracle {

static final String GET_JAVA_OBJECT_SEQUENCE ="SELECT java_object_sequence.nextval FROM dual";

static final String WRITE_OBJECT_SQL ="BEGIN INSERT INTO java_objects(object_id, object_name, object_value) " +"VALUES (?, ?, empty_blob()) RETURN object_value INTO ?; END;";

static final String READ_OBJECT_SQL ="SELECT object_value FROM java_objects WHERE object_id = ?";

/*** Create a Connection object.*/public static Connection getConnection() ...

/*** Serialize a Java object: this method writes a Java object* to an Oracle database (serialization).*/public static long writeJavaObject(Connection conn, Object object) ...

/*** Deserialize a Java object: this method reads a Java object* from an Oracle database (de-serialization).*/public static Object readJavaObject(Connection conn, long id) ...

/*** Create a primary key id for Java objects*/private static long getNextSequenceValue (Connection conn) ...

private static List buildList() ...

/*** This is the driver method (for testing purposes).*/

5203ch07a.qxd 8/11/05 4:25 PM Page 248

Page 278: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 249

public static void main (String args[]) {Connection conn = null;try {

// connect to the databaseconn = getConnection();System.out.println("conn="+conn);

// turn off AutoCommitconn.setAutoCommit(false);

List list = buildList();System.out.println("[Before Serialization] list="+list);

// serialize list (as a Java object)long objectID = writeJavaObject(conn, list);

// commit the transactionconn.commit();

System.out.println("Serialized objectID => " + objectID);// deserialize a Java object from a given objectIDList listFromDatabase = (List) readJavaObject(conn, objectID);System.out.println("[After De-Serialization] list=" + listFromDatabase);

}catch (Exception e) {

e.printStackTrace();}finally {

DatabaseUtil.close(conn);}

}}

SerializeJavaObjects_Oracle: getConnection()

The following shows getConnection():

/*** Create a Connection object.*/public static Connection getConnection() throws Exception {

String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "mp";String password = "mp2";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

SerializeJavaObjects_Oracle: writeJavaObject()

The following shows writeJavaObject():

/*** Serialize a Java object: this method writes a Java object* to an Oracle database (serialization).*/

5203ch07a.qxd 8/11/05 4:25 PM Page 249

Page 279: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS250

public static long writeJavaObject(Connection conn, Object object)throws Exception {long id = getNextSequenceValue(conn);String className = object.getClass().getName();CallableStatement cstmt = conn.prepareCall(WRITE_OBJECT_SQL);// set and register input parameterscstmt.setLong(1, id);cstmt.setString(2, className);

// register output parameterscstmt.registerOutParameter(3, java.sql.Types.BLOB);

cstmt.executeUpdate(); // exec. stored procedureBLOB blob = (BLOB) cstmt.getBlob(3);OutputStream os = blob.getBinaryOutputStream();ObjectOutputStream stream = new ObjectOutputStream(os);stream.writeObject(object);stream.flush();stream.close();os.close();DatabaseUtil.close(cstmt);System.out.println("writeJavaObject: done serializing: " + className);return id;

}

SerializeJavaObjects_Oracle: readJavaObject()

The following shows readJavaObject():

/*** Deserialize a Java object: this method reads a Java object* from an Oracle database (deserialization).*/public static Object readJavaObject(Connection conn, long id)

throws Exception {Object object = null;ResultSet rs = null;PreparedStatement pstmt =null;try {

pstmt = conn.prepareStatement(READ_OBJECT_SQL);pstmt.setLong(1, id);rs = pstmt.executeQuery();rs.next();InputStream is = rs.getBlob(1).getBinaryStream();ObjectInputStream oip = new ObjectInputStream(is);object = oip.readObject();String className = object.getClass().getName();oip.close();is.close();System.out.println("readJavaObject: done de-serializing: " + className);

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(pstmt);

}return object;

}

5203ch07a.qxd 8/11/05 4:25 PM Page 250

Page 280: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 251

SerializeJavaObjects_Oracle: getNextSequenceValue()

The following shows getNextSequenceValue():

/*** Create a primary key id for Java objects*/private static long getNextSequenceValue (Connection conn)

throws SQLException {Statement stmt = null;ResultSet rs = null;long id = -1; // undefinedtry {

stmt = conn.createStatement();stmt.executeQuery(GET_JAVA_OBJECT_SEQUENCE);rs = stmt.executeQuery(GET_JAVA_OBJECT_SEQUENCE);rs.next();id = rs.getLong(1);

finally {DatabaseUtil.close(rs);DatabaseUtil.close(stmt);

}return id;

}

SerializeJavaObjects_Oracle: buildList()

The following shows buildList():

private static List buildList() {List list = new ArrayList();

list.add("This is a short string.");list.add(new Integer(1234));list.add(new java.util.Date());return list;

}

Running SerializeJavaObjects_Oracle

The following shows how to run the program:

$ javac SerializeJavaObjects_Oracle.java$ java SerializeJavaObjects_Oracleconn=oracle.jdbc.driver.OracleConnection@6e70c7[Before Serialization]list=[This is a short string., 1234, Sun Oct 12 20:35:54 PDT 2003]writeJavaObject: done serializing: java.util.ArrayListSerialized objectID => 1readJavaObject: done de-serializing: java.util.ArrayList[After De-Serialization]list=[This is a short string., 1234, Sun Oct 12 20:35:54 PDT 2003]

$ java SerializeJavaObjects_Oracleconn=oracle.jdbc.driver.OracleConnection@6e70c7[Before Serialization]list=[This is a short string., 1234, Sun Oct 12 20:35:59 PDT 2003]writeJavaObject: done serializing: java.util.ArrayListSerialized objectID => 2

5203ch07a.qxd 8/11/05 4:25 PM Page 251

Page 281: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS252

readJavaObject: done de-serializing: java.util.ArrayList[After De-Serialization]list=[This is a short string., 1234, Sun Oct 12 20:35:59 PDT 2003]

Viewing the Database After Running SerializeJavaObjects_Oracle

This is the database after running the program:

SQL> select object_id, object_name from java_objects;

OBJECT_ID OBJECT_NAME--------- --------------------

1 java.util.ArrayList2 java.util.ArrayList

7-11. How Do You Serialize a Java Object to the MySQL Database?Serializing Java objects to MySQL is straightforward and much simpler than Oracle: you just usesetObject() and getObject() of a PreparedStatement object.

Preparing the MySQL Database for Serializing Java ObjectsCreate the table java_objects as follows to store Java objects. The primary key is automaticallygenerated by using the AUTO_INCREMENT feature of the MySQL database (which is semantically equiva-lent to using the SEQUENCE concept of Oracle).

mysql> CREATE TABLE java_objects (object_id INT AUTO_INCREMENT,object_name varchar(128),object_value BLOB,primary key (object_id)

);

Query OK, 0 rows affected (0.09 sec)

mysql> desc java_objects;+--------------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+--------------+--------------+------+-----+---------+----------------+| object_id | int(11) | | PRI | NULL | auto_increment || object_name | varchar(128) | YES | | NULL | || object_value | blob | YES | | NULL | |+--------------+--------------+------+-----+---------+----------------+3 rows in set (0.04 sec)

Serializing/DeSerializing Java ObjectsThe following class, SerializeJavaObjects_MySQL, performs these tasks:

• Serializes a Java object to the MySQL database

• Deserializes a Java object from the MySQL database

Here is SerializeJavaObjects_MySQL:

5203ch07a.qxd 8/11/05 4:26 PM Page 252

Page 282: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 253

import java.io.*;import java.sql.*;import java.util.*;import jcb.util.DatabaseUtil;/*** This class provides the following features:* 1) how to serialize a Java object to the MySQL database.* 2) how to deserialize a Java object from the MySQL database.**/class SerializeJavaObjects_MySQL {

static final String WRITE_OBJECT_SQL ="INSERT INTO java_objects(object_name, object_value) VALUES (?, ?)";

static final String READ_OBJECT_SQL ="SELECT object_value FROM java_objects WHERE object_id = ?";

public static Connection getConnection() ...

/*** Serialize a Java object: this method writes a Java object* to a MySQL database (serialization).*/public static long writeJavaObject(Connection conn, Object object) ...

/*** Deserialize a Java object: this method reads a Java object* from MySQL database (deserialization).*/public static Object readJavaObject(Connection conn, long id) ...

private static List buildList() ...

/*** This is the driver method (for testing purposes).*/public static void main (String args[]) {

Connection conn = null;try {

// connect to the databaseconn = getConnection();System.out.println("conn="+conn);

// turn off AutoCommitconn.setAutoCommit(false);

List list = buildList();System.out.println("[Before Serialization] list="+list);

// serialize list (as a Java object)long objectID = writeJavaObject(conn, list);

// commit the transactionconn.commit();

5203ch07a.qxd 8/11/05 4:26 PM Page 253

Page 283: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS254

System.out.println("Serialized objectID => " + objectID);// deserialize list a Java object from a given objectIDList listFromDatabase = (List) readJavaObject(conn, objectID);System.out.println("[After De-Serialization] list=" + listFromDatabase);

}catch (Exception e) {

e.printStackTrace();}finally {

DatabaseUtil.close(conn);}

}}

SerializeJavaObjects_MySQL: getConnection()

This shows getConnection():

/*** Create a MySQL Connection object.*/public static Connection getConnection() throws Exception {

String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

SerializeJavaObjects_MySQL: writeJavaObject()

This shows writeJavaObject():

/*** Serialize a Java object: this method writes a Java object* to an Oracle database (serialization).*/public static long writeJavaObject(Connection conn, Object object)

throws Exception {ResultSet rs = null;PreparedStatement pstmt = null;int id = -1; // undefined valuetry {

String className = object.getClass().getName();pstmt = conn.prepareStatement(WRITE_OBJECT_SQL);// set input parameterspstmt.setString(1, className);pstmt.setObject(2, object);pstmt.executeUpdate();// get the generated key for the object_idrs = pstmt.getGeneratedKeys();if (rs.next()) {

id = rs.getInt(1);}System.out.println("writeJavaObject: done serializing: " + className);

}

5203ch07a.qxd 8/11/05 4:26 PM Page 254

Page 284: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS 255

finally {DatabaseUtil.close(rs);DatabaseUtil.close(pstmt);

}

return id;}

SerializeJavaObjects_MySQL: readJavaObject()

This shows readJavaObject():

/*** Deserialize a Java object: this method reads a Java object* from a MySQL database (deserialization).*/public static Object readJavaObject(Connection conn, long id)

throws Exception {Object object = null;ResultSet rs = null;PreparedStatement pstmt = null;try {

pstmt = conn.prepareStatement(READ_OBJECT_SQL);pstmt.setLong(1, id);rs = pstmt.executeQuery();rs.next();object = rs.getObject(1);String className = object.getClass().getName();System.out.println("readJavaObject: done de-serializing: " + className);

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(pstmt);

}return object;

}

SerializeJavaObjects_MySQL: buildList()

This shows buildList():

private static List buildList() {List list = new ArrayList();list.add("This is a short string.");list.add(new Integer(1234));list.add(new java.util.Date());return list;

}

Running SerializeJavaObjects_MySQL

This code shows how to run the program:

$ javac SerializeJavaObjects_MySQL.java$ java SerializeJavaObjects_MySQLconn=com.mysql.jdbc.Connection@cd2c3c[Before Serialization]list=[This is a short string., 1234, Sun Oct 12 21:16:20 PDT 2003]

5203ch07a.qxd 8/11/05 4:26 PM Page 255

Page 285: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 7 ■ READING AND WRITING BLOBS256

writeJavaObject: done serializing: java.util.ArrayListSerialized objectID => 1readJavaObject: done de-serializing: java.util.ArrayList[After De-Serialization]list=[This is a short string., 1234, Sun Oct 12 21:16:20 PDT 2003]

$ java SerializeJavaObjects_MySQLconn=com.mysql.jdbc.Connection@cd2c3c[Before Serialization]list=[This is a short string., 1234, Sun Oct 12 21:16:30 PDT 2003]writeJavaObject: done serializing: java.util.ArrayListSerialized objectID => 2readJavaObject: done de-serializing: java.util.ArrayList[After De-Serialization]list=[This is a short string., 1234, Sun Oct 12 21:16:30 PDT 2003]

Viewing the Database After Running SerializeJavaObjects_MySQL

This code shows the database after running the program:

SQL> select object_id, object_name from java_objects;mysql> select object_id, object_name from java_objects;+-----------+---------------------+| object_id | object_name |+-----------+---------------------+| 1 | java.util.ArrayList || 2 | java.util.ArrayList |+-----------+---------------------+2 rows in set (0.02 sec)

7-12. Should You Use byte[] or java.sql.Blob? Which Has the BestPerformance?If you have the choice of manipulating binary data (a binary column of a record such as a BLOB datatype), should you use byte[] or java.sql.Blob? Which has the best performance?

For better performance, you should use java.sql.Blob, since it does not extract any data fromthe database until you explicitly ask it to do so (by invoking the getBinaryStream() or getBytes()method). The JDBC data type java.sql.Blob wraps a database locator (which is essentially a pointerto a byte array). That pointer is a rather large number (between 32 and 256 bits in size), but the effortto extract it from the database is insignificant next to extracting the full BLOB content. For insertioninto the database, you should use a byte[] since data has not been uploaded to the database yet.Therefore, use the java.sql.Blob object only for data extraction (whenever possible).

When using binary data, you should pay attention to data encoding as well: if your database sup-ports only the Latin character set, and if your client wants to get that data in the ja_JP locale, this willnot work—the client will get mostly garbage characters. To handle more than one locale (such asen_US and ja_JP), use UTF-8 encoding for your database. Oracle and MySQL support UTF-8 databases,and MySQL’s JDBC driver supports several Connection properties (useUnicode, characterEncoding)for character encoding. For more details on MySQL, refer to http://dev.mysql.com/doc/connector/j/en/index.html.

5203ch07a.qxd 8/11/05 4:26 PM Page 256

Page 286: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Reading and Writing CLOBs

In this chapter, you will examine all aspects of JDBC’s CLOB data type. Specifically, this chaptershows how to use JDBC’s rich data type CLOB. This data type stores/retrieves large character/textobjects such as large text files or Java/HTML/XML/PostScript source files.

The word clob has different meanings; I will use CLOB for the SQL data type and Clob for thejava.sql.Clob interface (which represents a SQL CLOB data type).

This chapter’s focus is on the following topics:

• How to define a CLOB data type in a database

• How to insert/write a CLOB into a database

• How to retrieve/read a CLOB from a database

• How to update an existing CLOB in a database

• How to delete an existing CLOB in a database

You can define, retrieve, store, and update the CLOB data type the same way you do other JDBCdata types. You use either the ResultSet.getClob() method or the CallableStatement.getClob()method to retrieve CLOBs, the PreparedStatement.setClob() method to store them, and theResultSet.updateClob() method to update them.

8-1. What Is a CLOB?A CLOB is a Character Large OBject in a database. Database vendors (such as Oracle, MySQL, IBMDB2, and so on) implement CLOBs in different ways, but as long as you use the common interfacejava.sql.Clob, you should not care about the internal implementations. CLOBs can be very large,up to 2GB or more, depending on the database. Use CLOBs with care; whenever possible, try to usecaching algorithms to improve the overall performance of database applications. Large characterdata files can be stored as CLOB types.

JDBC’s Support for CLOBJDBC provides a single interface (java.sql.Clob) for handling large character/text objects. Accordingto J2SE 5.0, java.sql.Clob is defined as follows:

257

C H A P T E R 8

■ ■ ■

5203ch08.qxd 8/11/05 4:27 PM Page 257

Page 287: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS258

[java.sql.Clob is] the mapping in the Java programming language for the SQL CLOB type.An SQL CLOB is a built-in type that stores a Character Large OBject as a column value ina row of a database table. By default drivers implement a Clob object using an SQL locator(CLOB), which means that a Clob object contains a logical pointer to the SQL CLOB datarather than the data itself. A Clob object is valid for the duration of the transaction in whichit was created. The Clob interface provides methods for getting the length of an SQL CLOB(Character Large Object) value, for materializing a CLOB value on the client, and for search-ing for a substring or CLOB object within a CLOB value. Methods in the interfaces ResultSet,CallableStatement, and PreparedStatement, such as getClob and setClob, allow a program-mer to access an SQL CLOB value. In addition, this interface has methods for updatinga CLOB value.

Table 8-1 describes the java.sql.Clob interface.

Table 8-1. java.sql.Clob Interface Description (According to J2SE 5.0)

Return Type Method Description

InputStream getAsciiStream() Retrieves the CLOB value designatedby this Clob object as an ASCIIstream.

Reader getCharacterStream() Retrieves the CLOB value designatedby this Clob object asa java.io.Reader object (or asa stream of characters).

String getSubString(long pos, Retrieves a copy of the specified int length) substring in the CLOB value

designated by this Clob object. Notethat Oracle CLOBs can be up to 4GB,which exceeds the maximum “int”limit.

long length() Retrieves the number of charactersin the CLOB value designated by thisClob object.

long position(Clob searchstr, l Retrieves the character position at ong start) which the specified Clob object

searchstr appears in this Clobobject.

long position(String searchstr, Retrieves the character position at long start) which the specified substring

searchstr appears in the SQL CLOBvalue represented by this Clob object.

OutputStream setAsciiStream(long pos) Retrieves a stream to be used towrite ASCII characters to the CLOBvalue that this Clob object represents,starting at position pos.

Writer setCharacterStream(long pos) Retrieves a stream to be used towrite a stream of Unicodecharacters to the CLOB value thatthis Clob object represents, atposition pos.

int setString(long pos, String str) Writes the given Java String to theCLOB value that this Clob objectdesignates at the position pos.

5203ch08.qxd 8/11/05 4:27 PM Page 258

Page 288: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 259

Return Type Method Description

int setString(long pos, String str, Writes len characters of str, int offset, int len) starting at the character offset, to

the CLOB value that this Clobrepresents.

void truncate(long len) Truncates the CLOB value that thisClob designates to have a length oflen characters.

Oracle CLOBsOracle has only one type (called CLOB) to support large character/text objects. The oracle.sql.CLOBclass is the Oracle JDBC driver’s implementation of the standard JDBC java.sql.Clob interface.

MySQL CLOBsThis is according to the MySQL reference manual:

The four TEXT types TINYTEXT, TEXT, MEDIUMTEXT, and LONGTEXT correspond to thefour CLOB types and have the same maximum lengths and storage requirements. The onlydifference between BLOB and TEXT types is that sorting and comparison is performed incase-sensitive fashion for BLOB values and case-insensitive fashion for TEXT values. In otherwords, a TEXT is a case-insensitive BLOB.

MySQL has four kinds of CLOBs (please note that the JDBC calls are the same for these fourtypes). Portions of MySQL’s CLOB can be indexed.

• TINYTEXT: A character object that is stored with its length. Cannot be a key. The maximumlength is 255 characters (8 bits). Takes the (varying per row) length plus 1 byte in the table.

• TEXT: A character object that is stored with its length. Cannot be a key. The maximum lengthis 16,535 characters (16 bits). Takes the (varying per row) length plus 2 bytes in the table.

• MEDIUMTEXT: A character object that is stored with its length. Cannot be a key. The maximumlength is 16,777,216 characters (24 bits). Takes the (varying per row) length plus 3 bytes in thetable.

• LONGTEXT: A character object that is stored with its length. Cannot be a key. The range is4,294,967,295 characters (32 bits). Takes the (varying per row) length plus 4 bytes in the table.

According to MySQL (http://dev.mysql.com/doc/connector/j/en/index.html), “the Clob imple-mentation does not allow in-place modification (they are copies, as reported by the DatabaseMetaData.locatorsUpdateCopies() method). Because of this, you should use the PreparedStatement.setClob()method to save changes back to the database.”

8-2. How Do You Define a CLOB Data Type in a Table?Suppose that in your DataFiles table you want to store large text files. Then you might define yourtable as in the following sections.

Defining the Table: Oracle 9iThe following defines a table based on Oracle 9i:

5203ch08.qxd 8/11/05 4:27 PM Page 259

Page 289: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS260

create table DataFiles (id INT PRIMARY KEY,fileName VARCHAR(20),fileBody CLOB

);

Defining the Table: MySQLThe following defines a table based on MySQL:

create table DataFiles (id INT PRIMARY KEY,fileName VARCHAR(20),fileBody TEXT

);

Creating the Table: Oracle 9iThe following creates the table for Oracle 9i:

C:\> sqlplus system/passwordSQL*Plus: Release 9.2.0.1.0 - Production on Thu Apr 17 08:40:37 2003Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production

SQL> create table DataFiles2 (id INT PRIMARY KEY,3 fileName varchar(20),4 fileBody CLOB5 );

Table created.SQL> describe DataFiles;

Name Null? Type--------- --------- -------------ID NOT NULL NUMBER(38)FILENAME VARCHAR2(20)FILEBODY CLOB

Creating the Table: MySQLThe following defines the table for MySQL:

mysql> create table DataFiles (-> id INT PRIMARY KEY,-> fileName VARCHAR(20),-> fileBody TEXT-> );

Query OK, 0 rows affected (0.09 sec)

mysql> describe DataFiles;+----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------+-------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || fileName | varchar(20) | YES | | NULL | || fileBody | text | YES | | NULL | |+----------+-------------+------+-----+---------+-------+3 rows in set (0.02 sec)

5203ch08.qxd 8/11/05 4:27 PM Page 260

Page 290: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 261

8-3. What Are the Restrictions for Using CLOBs?For CLOB restrictions (such as indexing and the number of CLOBs that can be used per row), youshould consult the database vendor’s documentation. In general, restrictions for CLOBs are as follows:

• CLOB columns cannot be keys (primary or foreign).

• One cannot group or sort on CLOB.

The MySQL database allows you to index portions of the CLOB data type. For details, refer to theMySQL reference manual. In the MySQL database, you may also use SQL’s LIKE statement for searchingkeywords and sentences. (You need to understand the performance of SQL’s LIKE before using it.)

8-4. How Do You Create a java.sql.Clob Object?You can create a java.sql.Clob object in only one way, and that is by using a ResultSet object’smethods. Consider the DataFiles table defined earlier (using the Oracle database):

create table DataFiles(id INT PRIMARY KEY,fileName varchar(20),fileBody CLOB

);

The ResultSet interface has two methods for creating a java.sql.Clob object:

• getClob(int columnPosition)

• getClob(String columnName)

I will show how to create a java.sql.Clob object by using the overloaded getClob() methods.

ResultSet.getClob(int columnPosition)This shows ResultSet.getClob(int columnPosition):

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

conn = <get-a-valid-connection-object>stmt = conn.createStatement();rs = stmt.executeQuery("select fileBody from DataFiles");while (rs.next()) {

java.sql.Clob clob = rs.getClob(1); // first column// now the Clob object is created and you can apply// methods defined in the java.sql.Clob interface...

}}catch(SQLException se) {

// handle database exception...

}catch(Exception e) {

// handle other exceptions...

}finally {

5203ch08.qxd 8/11/05 4:27 PM Page 261

Page 291: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS262

// close resources: rs, stmt, conn...

}

ResultSet.getClob(String columnName)This shows ResultSet.getClob(int columnPosition):

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

conn = <get-a-valid-connection-object>stmt = conn.createStatement();rs = stmt.executeQuery("select fileBody from DataFiles");while (rs.next()) {

java.sql.Clob clob = rs.getClob("fileBody");// now the Clob object is created and you can apply// methods defined in the java.sql.Clob interface...

}}catch(SQLException se) {

// handle database exception...

}catch(Exception e) {

// handle other exceptions...

}finally {

// close resources: rs, stmt, conn...

}

8-5. How Do You Materialize CLOB Data?A SQL CLOB maps to a java.sql.Clob object. If you want to operate on CLOB data, you must first mate-rialize it on the client (that is, retrieve the CLOB value’s data and put it in memory on the client in theform of a Java object). The java.sql.Clob interface has four methods for materializing CLOB data:

• getAsciiStream(): Materializes the CLOB value as an input stream (java.io.InputStream)

• getCharacterStream(): Materializes the CLOB value as a stream of Unicode characters(java.io.Reader)

• getSubString(): Materializes all of the CLOB as a String object

• getSubString(): Materializes part of the CLOB as a String object

To materialize the CLOB value, assume that there is a Java method that returns a validjava.sql.Clob object:

public java.sql.Clob getClob(...)throws SQLException {...

}

I will use the getClob(...) method in the following snippets.

5203ch08.qxd 8/11/05 4:27 PM Page 262

Page 292: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 263

Materializing the CLOB Value As an Input Stream (java.io.InputStream)This shows how to materialize the CLOB value as an input stream:

import jcb.util.DatabaseUtil;...//// prints out all of ASCII bytes in the CLOB//byte b; // as an ASCII bytejava.sql.Clob clob = null;java.io.InputStream input = null;try {

clob = getClob(...);input = clob.getAsciiStream();while ((b = input.read()) > -1) {

// process the ASCII byteSystem.out.println(b);

}}catch(SQLException se) {

// handle database exception...

}catch(Exception e) {

// handle other exceptions...

}finally {

// close resourcesDatabaseUtil close(input);

}

Materializing the CLOB Value As a Stream of Unicode Characters (java.io.Reader)This shows how to materialize the CLOB value as a stream of Unicode characters:

import jcb.util.DatabaseUtil;...

//// prints out all of Unicode characters in the CLOB//

// The character read, as an integer// in the range 0 to 65535 (0x00-0xffff)int aCharacter;java.sql.Clob clob = null;java.io.Reader input = null;try {

clob = getClob(...);input = clob.getCharacterStream();while ((aCharacter = input.read()) > -1) {

// process the unicode characterSystem.out.println(aCharacter);

}}catch(SQLException se) {

// handle database exception...

}

5203ch08.qxd 8/11/05 4:27 PM Page 263

Page 293: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS264

catch(Exception e) {// handle other exceptions...

}finally {

// close resourcesDatabaseUtil close(input);

}

Materializing the CLOB As a String Object (Get the Whole CLOB)This shows how to materialize the CLOB value as a String object:

//// get the whole CLOB as a String object//long length;java.sql.Clob clob = null;try {

clob = getClob(...);length = clob.length();// note that the first character is at position 1String wholeClob = clob.getSubString(1, (int) length);

}catch(SQLException se) {

// handle database exception...

}catch(Exception e) {

// handle other exceptions...

}

Materializing the CLOB As a String Object (Get Part of the CLOB)This shows how to materialize the CLOB value as a String object:

//// get a part of the clob as a String object// get 25 characters starting from position 10//long length = 25;long startingPosition = 10;java.sql.Clob clob = null;String partialClobAsString = null;try {

clob = getClob(...);partialClobAsString = clob.getSubString(startingPosition, length);

}catch(SQLException se) {

// handle database exception...

}catch(Exception e) {

// handle other exceptions...

}

5203ch08.qxd 8/11/05 4:27 PM Page 264

Page 294: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 265

You can express this as a Java method:

/** Get a part of the clob as a String object get "length"* characters starting from position "startingPosition"** @param clob a CLOB object* @param startingPosition the first character of* the substring to be extracted.* @param length the number of consecutive characters* to be copied* @throws SQLException if there is an error accessing* the CLOB value**/public static String getPartialClob(java.sql.Clob clob,

long length,long startingPosition)

throws SQLException {if (clob == null) {

return null;}

return clob.getSubString(startingPosition, length);}

8-6. How Do You Insert a New Record with a CLOB?Consider the DataFiles table (which has a CLOB column):

create table DataFiles(id INT PRIMARY KEY,fileName varchar(20),fileBody CLOB

);

You should be able to use JDBC to insert new records (which will contain fileBody as a CLOBdata type). Suppose you want to insert the following data:

id fileName fileBody (content of text file)-- -------- -------------------------------1 file1.txt c:/temp/data/file1.txt2 file2.txt c:/temp/data/file2.txt3 file3.txt c:/temp/data/file3.txt4 file4.txt c:/temp/data/file4.txt

Your goal is to write a program that will accept id, fileName, and fileBody (the content of thetext file, as a full filename) and insert them into the DataFiles table. The client interface is as follows:

java InsertTextFileToMySQL <id> <fileName> <fileBody>java InsertTextFileToOracle <id> <fileName> <fileBody>

Therefore, you need to develop two classes (InsertTextFileToMySQL.java and InsertTextFileToOracle.java). To insert the four records into a MySQL database, execute the following:

java InsertTextFileToMySQL 1 file1.txt c:/temp/data/file1.txtjava InsertTextFileToMySQL 2 file2.txt c:/temp/data/file2.txtjava InsertTextFileToMySQL 3 file3.txt c:/temp/data/file3.txtjava InsertTextFileToMySQL 4 file4.txt c:/temp/data/file4.txt

5203ch08.qxd 8/11/05 4:27 PM Page 265

Page 295: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS266

To insert the first three records into an Oracle 9i/10g database, execute the following:

java InsertTextFileToOracle 1 file1.txt c:/temp/data/file1.txtjava InsertTextFileToOracle 2 file2.txt c:/temp/data/file2.txtjava InsertTextFileToOracle 3 file3.txt c:/temp/data/file3.txtjava InsertTextFileToOracle 4 file4.txt c:/temp/data/file4.txt

MySQL Solution: InsertTextFileToMySQL.javaThe following shows the InsertTextFileToMySQL.java solution:

import java.io.*;import java.sql.*;import jcb.db.DatabaseUtil;

public class InsertTextFileToMySQL {

private static final String INSERT_TEXT_FILE ="insert into DataFiles(id, fileName, fileBody) values (?, ?, ?)";

private static String trimArgument(String s) {if ((s == null) || (s.length() == 0)) {

return s;}else {

return s.trim();}

}

public static Connection getConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

public static void main(String[] args) {

if ((args == null) || (args.length != 3)) {System.err.println("Usage: java InsertTextFileToMySQL ");System.exit(0);

}

String id = trimArgument(args[0]);String name = trimArgument(args[1]);String textFile = trimArgument(args[2]);

FileInputStream fis = null;PreparedStatement pstmt = null;Connection conn = null;try {

conn = getConnection();conn.setAutoCommit(false);

File file = new File(textFile);fis = new FileInputStream(file);

5203ch08.qxd 8/11/05 4:27 PM Page 266

Page 296: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 267

pstmt = conn.prepareStatement(INSERT_TEXT_FILE);pstmt.setString(1, id);pstmt.setString(2, name);pstmt.setAsciiStream(3, fis,(int)file.length());pstmt.executeUpdate();conn.commit();

}catch (Exception e) {System.err.println("Error: " + e.getMessage());e.printStackTrace();

}finally {

DatabaseUtil.close(pstmt);DatabaseUtil.close(fis);DatabaseUtil.close(conn);

}}

}

Testing InsertTextFileToMySQL.javaTesting the solution involves three steps.

Step 1: Prepare Input Text Files

Step 1 is to prepare the input text files:

$ cat c:/temp/data/file1.txtthis is file1.hello world.This is the last line.

$ cat c:/temp/data/file2.txtimport java.util.*;import java.io.*;import java.sql.*;

public class TestMySQL {public static Connection getConnection() throws Exception {

String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}}$

Step 2: Compile and Run the Program

Step 2 is to compile and run the program:

$ javac InsertTextFileToMySQL.java$ java InsertTextFileToMySQL 10 file1 c:/temp/data/file1.txt$ java InsertTextFileToMySQL 20 file2 c:/temp/data/file2.txt

5203ch08.qxd 8/11/05 4:27 PM Page 267

Page 297: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS268

Step 3: Check the Database Content

Step 3 is to check the database content. Using MySQL’s command line, you can view the insertedCLOB data.

mysql> use octopus;Database changed

mysql> desc DataFiles;+----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------+-------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || fileName | varchar(20) | YES | | NULL | || fileBody | text | YES | | NULL | |+----------+-------------+------+-----+---------+-------+3 rows in set (0.00 sec)

mysql> select * from datafiles;+----+----------+----------------------------------------------| id | fileName | fileBody+----+----------+----------------------------------------------| 10 | file1 | this is file1.| | | hello world.| | | This is the last line.+----+----------+----------------------------------------------| 20 | file2 | import java.util.*;| | | import java.io.*;| | | import java.sql.*;| | || | | public class TestMySQL {| | | public static Connection getConnection() throws Exception {| | | String driver = "org.gjt.mm.mysql.Driver";| | | String url = "jdbc:mysql://localhost/octopus";| | | String username = "root";| | | String password = "root";| | | Class.forName(driver); // load MySQL driver| | | return DriverManager.getConnection(url, username, password);| | | }| | | }+----+----------+---------------------------------------------2 rows in set (0.00 sec)

mysql>

Oracle Solution: InsertTextFileToOracle.javaThe solution using the Oracle database, the InsertTextFileToOracle class, is identical to the MySQLdatabase solution with the exception of the getConnection() method, shown next. You can down-load the complete solution from the book’s Web site.

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "scott";String password = "tiger";

5203ch08.qxd 8/11/05 4:27 PM Page 268

Page 298: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 269

Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

Testing InsertTextFileToOracle.javaTesting the solution involves two steps.

Step 1: Compile and Run the Program

Step 1 is to prepare the input text files:

$ cat c:/temp/data/file1.txtthis is file1.hello world.This is the last line.

$ cat c:/temp/data/file2.txtimport java.util.*;import java.io.*;import java.sql.*;

public class TestMySQL {public static Connection getConnection() throws Exception {

String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}}$

$ javac InsertTextFileToOracle.java$ java InsertTextFileToOracle 100 file1 c:/temp/data/file1.txt$ java InsertTextFileToOracle 200 file2 c:/temp/data/file2.txt

Step 2: Check the Database Content, and View CLOB Data

Step 2 is to check the database content. I have formatted the output so it is easier to read.

$ sqlplus scott/tigerSQL*Plus: Release 10.1.0.2.0 - Production on Thu Jul 22 14:31:43 2004SQL> desc datafiles;Name Null? Type---------------------------------- -------- --------------ID NOT NULL NUMBER(38)FILENAME VARCHAR2(20)FILEBODY CLOB

SQL> select * from datafiles;

ID FILENAME FILEBODY----- -------- ---------------------------------------100 file1 this is file1.

hello world.This is the last line.

5203ch08.qxd 8/11/05 4:27 PM Page 269

Page 299: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS270

200 file2 import java.util.*;import java.io.*;import java.sql.*;

public class TestMySQL...

As you can see, using the CLOB data type with the MySQL and Oracle 10g databases is straight-forward, but that is not the case with the Oracle 8 and Oracle 9 databases. In Oracle 8 and Oracle 9,before you can insert a real CLOB, you need to insert an empty CLOB (called empty_clob() in Oracle).empty_clob() is an Oracle function call that creates an empty Clob object. Therefore, in Oracle, youcannot just insert a Clob object into a column. First, you create a column with empty_clob(). Second,you update that column with the real Clob object.

8-7. How Do You Select and Display a CLOB in a JFrame?The following example demonstrates how to retrieve a CLOB data type from the database. In this case,you retrieve the fileBody (content of the text file) identified by an ID (id is the primary key for theDataFiles table), displaying it in its own JFrame. Given that the code is lengthy to accomplish thisjob, the example is split over several pages. First, you perform a query to select the CLOB of interest(by providing the ID) and pull it back to the client (also known as materializing the CLOB). The rest ofthe code simply creates a JFrame to hold the retrieved text.

I have developed two classes: ClobSelectMySQL and ClobSelectOracle. You can invoke theseclasses by passing the ID of the file:

java ClobSelectMySQL <id>java ClobSelectOracle <id>

The ClobSelectMySQL and ClobSelectOracle classes accept an ID (the primary key to theDataFiles table) and extract and display the desired CLOB in a JFrame.

Extracting a CLOB from MySQLFigure 8-1 shows the CLOB from MySQL by invoking this:

java ClobSelectMySQL 10

Figure 8-2 shows the CLOB from MySQL by invoking this:

java ClobSelectMySQL 20

Figure 8-1. Viewing MySQL CLOB data using JFrame

5203ch08.qxd 8/11/05 4:27 PM Page 270

Page 300: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 271

Extracting a CLOB from OracleFigure 8-3 shows the CLOB from MySQL by invoking this:

java ClobSelectMySQL 100

Figure 8-4 shows the CLOB from MySQL by invoking this:

java ClobSelectMySQL 200

Figure 8-2. Viewing MySQL CLOB data using JFrame

Figure 8-3. Viewing Oracle CLOB data using JFrame

Figure 8-4. Viewing Oracle CLOB data using JFrame

5203ch08.qxd 8/11/05 4:27 PM Page 271

Page 301: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS272

Solution: ClobSelectMySQL.javaThe following shows the ClobSelectMySQL.java solution:

import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.sql.*;

import jcb.db.*;

/*** This class displays a Clob object in a JFrame*/public class ClobSelectMySQL extends JPanel {

// look and feel constantspublic static final String MOTIF_LOOK_AND_FEEL =

"com.sun.java.swing.plaf.motif.MotifLookAndFeel";

public static final String WINDOWS_LOOK_AND_FEEL ="com.sun.java.swing.plaf.windows.WindowsLookAndFeel";

public static final String METAL_LOOK_AND_FEEL ="javax.swing.plaf.metal.MetalLookAndFeel";

/*** Get a connection object.*/public static Connection getConnection() ...

/*** Extract and return the CLOB object.* @param id the primary key to the CLOB object.*/public static String getCLOB(int id) ...

/*** Constructor to display CLOB object.* @param id the primary key to the DataFiles table*/public ClobSelectMySQL(int id) ...

public static void main(String args[]) ...}

getConnection()

The following shows getConnection():

/*** Get a connection object.*/public static Connection getConnection() throws Exception {

String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";

5203ch08.qxd 8/11/05 4:27 PM Page 272

Page 302: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 273

Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

getCLOB()

The following shows getCLOB():

/*** Extract and return the CLOB object as String.* @param id the primary key to the CLOB object.*/public static String getCLOB(int id) throws Exception {

Connection conn = null ;ResultSet rs = null;PreparedStatement pstmt = null;String query = "SELECT fileBody FROM DataFiles WHERE id = ?" ;try {

conn = getConnection();pstmt = conn.prepareStatement(query) ;pstmt.setInt(1, id);rs = pstmt.executeQuery();rs.next();Clob clob = rs.getClob(1);// materialize CLOB onto clientString wholeClob = clob.getSubString(1, (int) clob.length());return wholeClob;

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

constructor ClobSelectMySQL()

The following shows ClobSelectMySQL():

/*** Constructor to display CLOB object.* @param id the primary key to the DataFiles table*/public ClobSelectMySql(int id) throws Exception {

setLayout(new GridLayout(1, 1));add(new TextArea(getCLOB(id), 3, 10));

}

main()

The following shows main():

public static void main(String args[]) throws Exception {int id = Integer.parseInt(args[0]);UIManager.setLookAndFeel(METAL_LOOK_AND_FEEL) ;JFrame frame = new JFrame("CLOB Demo for MySQL Database. id="+id);frame.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {

5203ch08.qxd 8/11/05 4:27 PM Page 273

Page 303: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS274

System.exit(0);}

});frame.setContentPane(new ClobSelectMySql(id)) ;frame.pack();frame.setVisible(true);

}

Solution: ClobSelectOracle.javaThe Oracle solution is identical to the MySQL solution with the exception of the getConnection()method, which returns an Oracle Connection object. You can download the complete Oracle solutionfrom the book’s Web site.

This is the Java method getConnection() for the Oracle database:

/*** Get an Oracle connection object.*/public static Connection getConnection() throws Exception {

String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@scorpian:1521:caspian";String username = "scott";String password = "tiger";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

8-8. How Do You Select and Display an Oracle CLOB Usinga Servlet?I will use the data set up in the previous recipe to show how to select and display an Oracle CLOBusing a servlet.

Viewing Oracle CLOB DataI have developed a servlet, DisplayOracleClobServlet, that accepts the ID of a file and displays theassociated file. (As you can see from the previous pages, the output has not been formatted, and theCLOB data has not been altered.) Run the servlet with an ID of 100, as shown in Figure 8-5, and thenrun the servlet with an ID of 200, as shown in Figure 8-6. Next, run the servlet with an ID of 300, whichis not in the database. If the file’s ID does not exist in the database, then you will get an error, as shownin Figure 8-7.

Figure 8-5. Viewing Oracle CLOB data using a servlet (id=100)

5203ch08.qxd 8/11/05 4:27 PM Page 274

Page 304: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 275

If the database connection information is not valid (a wrong username/password or wrongdatabase URL) or if the database is not available, then you will get the error shown in Figure 8-8.

Displaying a CLOB Using a Servlet: DisplayOracleClobServletThe following shows the DisplayOracleClobServlet solution:

import java.io.*;import java.sql.*;

import javax.servlet.*;import javax.servlet.http.*;

import jcb.db.DatabaseUtil;

public class DisplayOracleClobServlet extends HttpServlet {

Figure 8-6. Viewing Oracle CLOB data using a servlet (id=200)

Figure 8-7. Viewing nonexistent Oracle CLOB data using a servlet

Figure 8-8. Displaying error message using a servlet

5203ch08.qxd 8/11/05 4:27 PM Page 275

Page 305: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS276

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "scott";String password = "tiger";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

public void doGet(HttpServletRequest request,HttpServletResponse response)

throws IOException, ServletException {

System.out.println("-- DisplayOracleClobServlet begin --");

Clob fileAsCLOB = null;Connection conn = null;Statement stmt = null;ResultSet rs = null;

String id = request.getParameter("id").trim();String query = "select fileBody from DataFiles where id = "+id;ServletOutputStream out = response.getOutputStream();

// all responses will be in text/html formatresponse.setContentType("text/html");

try {conn = getConnection();

}catch(Exception e) {

out.println("<html><head><title>CLOB Example</title></head>");out.println("<body><h4>Database Connection Problem.</h4>");out.println("<h5>"+e.getMessage()+"</h5></body></html>");return;

}

try {stmt = conn.createStatement();rs = stmt.executeQuery(query);if (rs.next()) {

fileAsCLOB = rs.getClob(1);}else {

out.println("<html><head><title>CLOB Example</title></head>");out.println("<body><h3>No file found for id="+id+"</h3></body></html>");

return;}

// Materialize the CLOB as a String object (get the whole clob).long length = fileAsCLOB.length();// note that the first character is at position 1String fileAsString = fileAsCLOB.getSubString(1, (int) length);

5203ch08.qxd 8/11/05 4:27 PM Page 276

Page 306: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 277

// write it for displayout.println(fileAsString);System.out.println("CLOB writing done.");

}catch (SQLException e) {

out.println("<html><head><title>Error: CLOB Example</title></head>");out.println("<body><h3>Error="+e.getMessage()+"</h3></body></html>");return;

}finally {DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}System.out.println("-- DisplayOracleClobServlet end --");

}

public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {doGet(request, response);

}}

8-9. How Do You Select and Display a MySQL CLOB Usinga Servlet?MySQL supports CLOB and offers four data types for using it:

Type Format---------- ------------------------------------------------------TINYTEXT A string with a maximum length of 255 charactersTEXT A string with a maximum length of 65,535 charactersMEDIUMTEXT A string with a maximum length of 16,777,215 charactersLONGTEXT A string with a maximum length of 4,294,967,295 characters

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database for this example:

mysql> use octopus;Database changed

mysql> desc DataFiles;+----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------+-------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || fileName | varchar(20) | YES | | NULL | || fileBody | text | YES | | NULL | |+----------+-------------+------+-----+---------+-------+3 rows in set (0.00 sec)

mysql> select * from datafiles;

5203ch08.qxd 8/11/05 4:27 PM Page 277

Page 307: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS278

Figure 8-9. Viewing MySQL CLOB data using a servlet (id=10)

+----+----------+----------------------------------------------| id | fileName | fileBody+----+----------+----------------------------------------------| 10 | file1 | this is file1.| | | hello world.| | | This is the last line.+----+----------+----------------------------------------------| 20 | file2 | import java.util.*;| | | import java.io.*;| | | import java.sql.*;| | || | | public class TestMySQL {| | | public static Connection getConnection() throws Exception {| | | String driver = "org.gjt.mm.mysql.Driver";| | | String url = "jdbc:mysql://localhost/octopus";| | | String username = "root";| | | String password = "root";| | | Class.forName(driver); // load MySQL driver| | | return DriverManager.getConnection(url, username, password);| | | }| | | }+----+----------+---------------------------------------------2 rows in set (0.00 sec)

mysql>

Viewing MySQL CLOB DataI have developed a servlet, DisplayMySqlClobServlet, that accepts the ID of a file and displays theassociated file. (As you can see, the output has not been formatted, and the CLOB data has not beenaltered.) Run the servlet with an ID of 10, as shown in Figure 8-9, and then run the servlet with an IDof 20, as shown in Figure 8-10. Next, run the servlet with an ID of 30 (which is not in the database); ifthe data is not in the database, you will see the error shown in Figure 8-11.

5203ch08.qxd 8/11/05 4:27 PM Page 278

Page 308: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 279

Figure 8-10. Viewing MySQL CLOB data using a servlet (id=20)

Figure 8-11. Viewing nonexistent Oracle CLOB data using a servlet

Figure 8-12. Displaying error message using a servlet

If the database connection information is not valid (a wrong username/password or wrongdatabase URL) or if database is not available, then you will get the error shown in Figure 8-12.

Displaying a CLOB Using a Servlet: DisplayMySqlClobServletThe MySQL solution is identical to the Oracle solution with the exception of the getConnection()method, which returns a MySQL Connection object. You can download the complete MySQL solu-tion from the book’s Web site.

This is getConnection() for the MySQL database:

public static Connection getConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";

5203ch08.qxd 8/11/05 4:27 PM Page 279

Page 309: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS280

Figure 8-13. Displaying a generated GUID for a CLOB using a servlet

String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

8-10. How Do You Select and Display an Oracle CLOB (As a URL)Using a Servlet?If CLOB data (such as an RSS feed, a text résumé, or an HTML blog) is too big (more than a couple ofmegabytes) and is shared among many users, it is a good idea to retrieve the CLOB from the database,then create a copy of it on the server side, and finally make it URL addressable (to avoid the per-formance cost).

Here I will provide a solution that displays a CLOB as a URL on the browser; when you click theURL (or open the URL in the browser), then you will view the CLOB. Therefore, a servlet will acceptan ID of a CLOB, and then it will store the CLOB on the server as a text file and send an associated URL(of the CLOB) to the browser. To solve this problem effectively, you need to create a directory (on theWeb server side) and make it URL addressable; therefore, you need to define the following twoparameters (defined inside the servlet):

• CLOB_DIRECTORY, the directory where CLOB data will be placed as files

• CLOB_URL, the CLOB_DIRECTORY as a URL

Setting Up the DatabaseFor this solution, I will use the CLOB data prepared in earlier sections.

SolutionI have developed a servlet, DisplayOracleClobAsURLServlet, that accepts the ID of a file and displaysthe associated file. For example, if id=200, then you will get the screen shown in Figure 8-13.

By opening the CLOB as a URL, you will get the screen shown in Figure 8-14.

5203ch08.qxd 8/11/05 4:27 PM Page 280

Page 310: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 281

Figure 8-14. Displaying a CLOB using a generated GUID

Figure 8-15. Displaying an error for a nonexistent CLOB using a servlet

If the file’s ID does not exist in the database, then you will get the screen shown in Figure 8-15.

If the database connection information is not valid (wrong username/password or wrongdatabase URL), then the servlet will display an error message.

Displaying a CLOB Using a Servlet: DisplayOracleClobAsURLServletThe following shows the DisplayOracleClobAsURLServlet solution:

import java.io.*;import java.sql.*;

import javax.servlet.*;import javax.servlet.http.*;

import jcb.db.DatabaseUtil;import jcb.util.IOUtil;import jcb.util.RandomGUID;

public class DisplayOracleClobAsURLServlet extends HttpServlet {

// directory where clob data will be placed as files.private static final String CLOB_DIRECTORY =

"c:/tomcat/webapps/octopus/clobDir";

5203ch08.qxd 8/11/05 4:27 PM Page 281

Page 311: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS282

// CLOB_DIRECTORY as a URLprivate static final String CLOB_URL =

"http://localhost:8000/octopus/clobDir";

private static final String CLOB_FILE_PREFIX = "/clob-";

public static Connection getConnection() ...private static String getClobAsURL(Clob Clob) ...public void doGet(...) ...

public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {doGet(request, response);

}}

getConnection()

The following shows getConnection():

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "scott";String password = "tiger222";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

getClobAsURL()

The following shows getClobAsURL():

private static String getClobAsURL(Clob Clob) throws Exception {InputStream in = null;FileOutputStream out = null;try {

if (Clob == null) {return null;

}

// get a random GUID for Clob filenameString guid = RandomGUID.getGUID();String ClobFile = CLOB_DIRECTORY + CLOB_FILE_PREFIX + guid;in = Clob.getAsciiStream();if (in == null) {

return null;}

out = new FileOutputStream(ClobFile);int length = (int) Clob.length();int bufferSize = 1024;byte[] buffer = new byte[bufferSize];while ((length = in.read(buffer)) != -1) {

out.write(buffer, 0, length);}

5203ch08.qxd 8/11/05 4:27 PM Page 282

Page 312: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 283

out.flush();return CLOB_URL + CLOB_FILE_PREFIX + guid;

}finally {IOUtil.close(in);IOUtil.close(out);

}}

doGet()

The following shows doGet():

public void doGet(HttpServletRequest request,HttpServletResponse response)

throws IOException, ServletException {Clob clob = null;Connection conn = null;Statement stmt = null;ResultSet rs = null;String id = request.getParameter("id").trim();String query = "select fileBody from DataFiles where id = "+id;ServletOutputStream out = response.getOutputStream();response.setContentType("text/html");out.println("<html><head><title>DisplayOracleClobAsURLServlet"+"</title></head>");

try {conn = getConnection();

}catch(Exception e) {

out.println("<body><h4>Database Connection Problem.</h4>");out.println("<h5>"+e.getMessage()+"</h5></body></html>");return;

}

try {stmt = conn.createStatement();rs = stmt.executeQuery(query);if (rs.next()) {clob = rs.getClob(1);out.println("<body><h3>file id="+id+"</h3>"+

getClobAsURL(clob)+"</body></html>");}else {out.println("<body><h1>No File found for id="+id+"</h1></body></html>");return;

}}catch (Exception e) {

out.println("<body><h1>Error="+e.getMessage()+"</h1></body></html>");return;

}finally {DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

5203ch08.qxd 8/11/05 4:27 PM Page 283

Page 313: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS284

Figure 8-16. Displaying a generated GUID for a CLOB using a servlet

Figure 8-17. Displaying a CLOB using a generated GUID

8-11. How Do You Select and Display a MySQL CLOB (As a URL)Using a Servlet?If CLOB data (such as an RSS feed, a text résumé, or an HTML blog) is too big (more than a couple ofmegabytes) and is shared among many users, it is a good idea to retrieve the CLOB from the database,then create a copy of it on the server side, and finally make it URL addressable (to avoid the per-formance cost).

Here I provide a solution that displays a CLOB as a URL on the browser; when you click the URL(or open the URL in the browser), then you will view the CLOB. Therefore, a servlet will accept an IDof a CLOB, and then it will store the CLOB on the server as a text file and send an associated URL (ofthe CLOB) to the browser. To solve this problem effectively, you need to create a directory (on the Webserver side) and make it URL addressable; therefore, you need to define the following two parame-ters (defined inside the servlet):

• CLOB_DIRECTORY, the directory where CLOB data will be placed as files

• CLOB_URL, the CLOB_DIRECTORY as a URL

SolutionI have developed a servlet, DisplayMySqlClobAsURLServlet, that accepts the ID of a file and displaysthe associated file. For example, if id=10, then you will get the screen shown in Figure 8-16.

By opening the CLOB as a URL, you will get the screen shown in Figure 8-17.

If the file’s ID does not exist in the database, then you will get the screen shown in Figure 8-18.

5203ch08.qxd 8/11/05 4:27 PM Page 284

Page 314: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 285

Figure 8-18. Displaying an error for a nonexistent CLOB using a servlet

If the database connection information is not valid (a wrong username/password or wrongdatabase URL), then you will get an error message.

Setting Up the MySQL DatabaseTo show this example in action, I will use the DataFiles table defined and populated in earlier sections.

Displaying a CLOB Using a Servlet: DisplayMySqlClobAsURLServletThe MySQL solution is identical to the Oracle solution with the exception of the getConnection()method, which returns a MySQL Connection object. You can download the complete MySQL solutionfrom the book’s Web site.

This is getConnection() for the MySQL database:

public static Connection getConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

8-12. How Do You Insert a CLOB into an Oracle Database Usinga Servlet?The SQL INSERT statement inserts new rows/records into a table. The general syntax is as follows:

INSERT INTO table_name (column_name_1, column_name_2, ...)VALUES (value_for_column_1, value_for_column_2, ...)

To insert a CLOB into an Oracle database using a servlet, you will represent the intended CLOB asa URL (the URL will be pointing to CLOB data, such as a text file). The reason for this is that servletscannot access the local file system (a client’s/browser’s local machine). You can also pass the CLOB tothe database as a String object. Therefore, you will represent the CLOB as a URL. The following sectionswill use the DataFiles table defined in the earlier sections.

Setting Up the Oracle DatabaseOracle 10 has simplified CLOBs in JDBC. There is no need to use Oracle’s proprietary SQL functions,such as empty_clob. Inserting CLOBs is simple; in fact, CLOBs are just long String objects. This showshow to set up the database:

5203ch08.qxd 8/11/05 4:27 PM Page 285

Page 315: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS286

$ sqlplus scott/tigerSQL*Plus: Release 10.1.0.2.0 - Production on Fri Feb 18 16:42:51 2005Connected to: Oracle Database 10g Enterprise Edition Release 10.1.0.2.0

SQL> desc datafiles;Name Null? Type----------------------------- -------- ------------ID NOT NULL NUMBER(38)FILENAME VARCHAR2(20)FILEBODY CLOB

SQL> select id, filename from datafiles;

ID FILENAME---------- --------------------

1000 file12000 file2

SQL> insert into datafiles(id, filename, filebody)2 values(4000, 'file4000', 'This is the content of file4000.');

1 row created.

SQL> select id, filename from datafiles;

ID FILENAME---- --------4000 file40001000 file12000 file2

Creating the Oracle Servlet InterfaceThe servlet interface is as follows:

http://localhost:8000/octopus/servlet/InsertClobToOracleServlet?id=<id>&name=<name>&file=<file-as-URL>

Therefore, InsertClobToMySqlServlet has three parameters:

• id (the ID of file)

• name (the name of file)

• file (the URL of file, representing the CLOB; the servlet will open the URL, construct a CLOB,and insert it into the CLOB column of the DataFiles table)

Now insert a new record with the following data in the DataFiles table:

• id (500)

• name (file500)

• file (the URL of file: http://www.geocities.com/mparsian/data/file500.txt)

Figure 8-19 shows the content of the URL (http://www.geocities.com/mparsian/data/file500.txt).

5203ch08.qxd 8/11/05 4:27 PM Page 286

Page 316: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 287

Figure 8-19. Displaying a file using a servlet

Figure 8-20. Inserting a new CLOB record using a servlet

Figure 8-21. Reinserting the same CLOB record using a servlet

Therefore, the servlet call is as follows:

http://localhost:8000/octopus/servlet/InsertClobToOracleServlet?id=500&name=file500&file=http://www.geocities.com/mparsian/data/file500.txt

Inserting a New CLOB RecordFigure 8-20 shows the result of inserting a new CLOB record using a Java servlet.

Reinserting the Same CLOB RecordFigure 8-21 shows the result of reinserting a new CLOB record using a servlet.

Viewing the Database Content After InsertionUsing DisplayOracleClobServlet, you can view the CLOB in a Web browser, as shown in Figure 8-22.

5203ch08.qxd 8/11/05 4:27 PM Page 287

Page 317: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS288

Solution: InsertClobToOracleServletThe following shows the InsertClobToOracleServlet solution:

import java.io.*;import java.sql.*;

import javax.servlet.*;import javax.servlet.http.*;

import jcb.db.DatabaseUtil;import jcb.util.IOUtil;

public class InsertClobToOracleServlet extends HttpServlet {static final String INSERT_CLOB =

"insert into datafiles(id, filename, filebody) values (?, ?, ?)";

public static Connection getConnection() throws Exception {... }public void doGet(...) {... }public void doPost(...) {...}public void insertCLOB(...) {....}public static String getClobsContentAsString(...) {...}private static String trimParameter(...) {...}

}

getConnection()

The following shows getConnection():

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "scott";String password = "tiger";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

doGet()

The following shows doGet():

public void doGet(HttpServletRequest request,HttpServletResponse response)

throws IOException, ServletException {

Figure 8-22. Viewing CLOB using a servlet

5203ch08.qxd 8/11/05 4:27 PM Page 288

Page 318: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 289

String fileContent = null;Connection conn = null;

String id = trimParameter(request.getParameter("id"));String name = trimParameter(request.getParameter("name"));String fileAsURL = trimParameter(request.getParameter("file"));ServletOutputStream out = response.getOutputStream();

response.setContentType("text/html");out.println("<html><head><title>InsertClobToOracleServlet</title></head>");

try {conn = getConnection();fileContent = getClobsContentAsString(fileAsURL);insertCLOB(conn, id, name, fileContent);out.println("<body><h4>OK: inserted a new record with id="+id+"</h4></body></html>");

}catch(Exception e) {e.printStackTrace();out.println("<body><h4>Error: "+e.getMessage()+"</h4></body></html>");

}}

insertCLOB()

The following shows insertCLOB():

public void insertCLOB(Connection conn,String id,String name,String fileContent)

throws Exception {PreparedStatement pstmt = null;try {

pstmt = conn.prepareStatement(INSERT_CLOB);pstmt.setString(1, id);pstmt.setString(2, name);pstmt.setString(3, fileContent);pstmt.executeUpdate();

}finally {

DatabaseUtil.close(pstmt);}

}

getClobsContentAsString()

The following shows getClobsContentAsString():

public static String getClobsContentAsString(String urlAsString)throws Exception {InputStream content = null;try {

java.net.URL url = new java.net.URL(urlAsString);java.net.URLConnection urlConn = url.openConnection();urlConn.connect();content = urlConn.getInputStream();

5203ch08.qxd 8/11/05 4:27 PM Page 289

Page 319: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS290

return IOUtil.inputStreamToString(content);}finally {

IOUtil.close(content);}

}

trimParameter()

The following shows trimParameter():

private static String trimParameter(String s) {if ((s == null) || (s.length() == 0)) {

return s;}else {

return s.trim();}

}}

8-13. How Do You Insert a CLOB into a MySQL Database Usinga Servlet?To insert a CLOB into a MySQL database using a servlet, you will represent the intended CLOB as a URL(the URL will be pointing to a CLOB data, such as a text file). The reason for this is that servlets cannotaccess the local file system (a client’s/browser’s local machine). You can also pass the CLOB to thedatabase as a String object. Therefore, you will represent the CLOB as a URL. The following sectionsuse the DataFiles table defined in the earlier questions.

Setting Up the MySQL DatabaseInserting CLOBs in MySQL is simple; in fact, CLOBs are just long String objects. This shows how to setup the database:

mysql> desc datafiles;+----------+------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------+------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || fileName | varchar(6) | YES | | NULL | || fileBody | text | YES | | NULL | |+----------+------------+------+-----+---------+-------+3 rows in set (0.00 sec)

mysql> select id, filename from datafiles;+----+----------+| id | filename |+----+----------+| 20 | file2 |+----+----------+1 row in set (0.00 sec)

mysql> insert into datafiles(id, filename, filebody)2 values(4000, 'file4000', 'This is the content of file4000.');

Query OK, 1 row affected, 1 warning (0.00 sec)

5203ch08.qxd 8/11/05 4:27 PM Page 290

Page 320: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 291

Figure 8-23. Viewing CLOB using a servlet

mysql> select id, filename from datafiles;+------+----------+| id | filename |+------+----------+| 20 | file2 |+------+----------+| 4000 | file4000 |+------+----------+2 rows in set (0.00 sec)

Creating the Oracle Servlet InterfaceThe servlet interface is as follows:

http://localhost:8000/octopus/servlet/InsertClobToMySqlServlet?id=<id>&name=<name>&file=<file-as-URL>

Therefore, InsertClobToMySqlServlet has three parameters:

• id (the ID of file)

• name (the name of file)

• file (the URL of file representing the CLOB; the servlet will open the URL, construct a CLOB,and insert it into the CLOB column of the DataFiles table)

Let’s insert a new record with the following data in the DataFiles table:

• id (500)

• name (file500)

• file (the URL of file: http://www.geocities.com/mparsian/data/file500.txt)

Figure 8-23 shows the content of the URL (http://www.geocities.com/mparsian/data/file500.txt).

Therefore, the servlet call is as follows:

http://localhost:8000/octopus/servlet/InsertClobToMySqlServlet?id=500&name=file500&file=http://www.geocities.com/mparsian/data/file500.txt

Inserting a New CLOB RecordFigure 8-24 shows the result of inserting a new CLOB record.

5203ch08.qxd 8/11/05 4:27 PM Page 291

Page 321: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS292

Figure 8-25. Viewing an inserted CLOB record using a servlet

Viewing the Database Content After InsertionUsing MySQL’s command prompt, you can view the inserted record (with id=500):

mysql> select id, filename from datafiles;+------+----------+| id | filename |+------+----------+| 20 | file2 |+------+----------+| 500 | file500 |+------+----------+| 4000 | file4000 |+------+----------+3 rows in set (0.00 sec)

mysql> select id, filename, filebody from datafiles where id=500;+-----+----------+--------------------------------------+| id | filename | filebody |+-----+----------+--------------------------------------+| 500 | file50 | This is the first line of file500. || | | This is the 2nd of file500. || | | This is the end of file500. |+-----+----------+--------------------------------------+

1 row in set (0.00 sec)

Using DisplayOracleClobServlet, you can view the CLOB in a Web browser, as shown inFigure 8-25.

Solution: InsertClobToMySqlServletThe MySQL solution is identical to the Oracle solution with the exception of the getConnection()method, which returns a MySQL Connection object. You can download the complete MySQL solutionfrom the book’s Web site.

Figure 8-24. Inserting a new CLOB record using a servlet

5203ch08.qxd 8/11/05 4:27 PM Page 292

Page 322: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 293

The following is getConnection() for the MySQL database:

public static Connection getConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

8-14. How Do You Update an Existing CLOB of an Oracle DatabaseUsing a Servlet?The SQL UPDATE statement modifies the existing column’s data in a table. The simplified syntax is asfollows (which updates a single column):

UPDATE table_nameSET column_name_1 = new_value_1,

cloumn_name_2 = new_value_2, ...WHERE column_name_x = some_value_1 and

column_name_y = some_value_2 and ...

You can update any number of columns using the SQL UPDATE statement. For details, pleaserefer to the following Web site: http://www.w3schools.com/sql/sql_update.asp.

To update a CLOB in an Oracle database using a servlet, you will represent the new value ofa CLOB as a URL (the URL will be pointing to a CLOB data, such as a text file). The reason for this isthat servlets cannot access the local file system (a client’s/browser’s local machine). You can alsopass the new CLOB value to the database as a String object. Therefore, you will represent the newvalue of a CLOB as a URL. The following sections use the DataFiles table defined in the earlier sec-tions.

Setting Up the Oracle DatabaseOracle 10 has simplified CLOBs in JDBC. There is no need to use Oracle’s proprietary SQL functions,such as empty_clob. Inserting/updating CLOBs is simple; in fact, CLOBs are just long String objects.This shows how to set up the database:

$ sqlplus scott/tigerSQL*Plus: Release 10.1.0.2.0 - Production on Sat Feb 19 22:38:38 2005Copyright (c) 1982, 2004, Oracle. All rights reserved.

SQL> desc datafiles;Name Null? Type------------------------------- -------- ----------ID NOT NULL NUMBER(38)FILENAME VARCHAR2(20)FILEBODY CLOB

SQL> select id, filename, filebody from datafiles where id=1000;

ID FILENAME FILEBODY----- -------- ----------------------------1000 file1 this is file1. hello world.

5203ch08.qxd 8/11/05 4:27 PM Page 293

Page 323: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS294

Figure 8-26. Displaying a file using a servlet

SQL> update datafiles2 set filebody='this is a long ... string. aha.'3 where id=1000;

1 row updated.

SQL> commit;Commit complete.

SQL> select id, filename, filebody from datafiles where id=1000;ID FILENAME FILEBODY----- -------- -------------------------------1000 file1 this is a long ... string. aha.

Creating the Oracle Servlet InterfaceThe servlet interface is as follows:

http://localhost:8000/octopus/servlet/UpdateOracleClobServlet?id=<id>&file=<file-as-URL>

Therefore, UpdateOracleClobServlet has two parameters:

• id (the ID of file, which uniquely identifies record)

• file (the URL of file representing the CLOB; the servlet will open the URL, construct a CLOB,and update it into the CLOB column of the DataFiles table)

Let’s update an existing record with the following data in the DataFiles table:

• id (1000)

• file (the URL of file: http://www.geocities.com/mparsian/data/file500.txt)

Figure 8-26 shows the content of the URL (http://www.geocities.com/mparsian/data/file500.txt).

Therefore, the servlet call is as follows (but all in one line):

http://localhost:8000/octopus/servlet/UpdateOracleClobServlet?id=1000&file=http://www.geocities.com/mparsian/data/file500.txt

Updating an Existing CLOB RecordFigure 8-27 shows the result of updating an existing CLOB record.

5203ch08.qxd 8/11/05 4:27 PM Page 294

Page 324: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 295

Figure 8-27. Updating a CLOB using a servlet

Figure 8-28. Viewing an updated CLOB using a servlet

Database Content After InsertionThe following shows the database after the insertion:

SQL> select id, filename, filebody from datafiles where id=1000;

ID FILENAME FILEBODY---- -------- ----------------------------------1000 file1 This is the first line of file500.

This is the 2nd of file500.This is the end

Using DisplayOracleClobServlet, you can view the CLOB in a Web browser, as shown inFigure 8-28.

The Solution: UpdateOracleClobServletThe following shows the UpdateOracleClobServlet solution:

import java.io.*;import java.sql.*;

import javax.servlet.*;import javax.servlet.http.*;

import jcb.db.DatabaseUtil;import jcb.util.IOUtil;

public class UpdateOracleClobServlet extends HttpServlet {static final String UPDATE_CLOB =

"update datafiles set filebody=? where id=?";

public static Connection getConnection() throws Exception {... }public void doGet(...) {... }public void doPost(...) {...}

5203ch08.qxd 8/11/05 4:27 PM Page 295

Page 325: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS296

public void updateCLOB(...) {....}public static String getClobsContentAsString(...) {...}private static String trimParameter(...) {...}

}

getConnection()

The following shows getConnection():

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "scott";String password = "tiger";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

doGet()

The following shows doGet():

public void doGet(HttpServletRequest request,HttpServletResponse response)

throws IOException, ServletException {String fileContent = null;Connection conn = null;String id = trimParameter(request.getParameter("id"));String fileAsURL = trimParameter(request.getParameter("file"));ServletOutputStream out = response.getOutputStream();

response.setContentType("text/html");out.println("<html><head><title>UpdateOracleClobServlet</title></head>");

try {conn = getConnection();fileContent = getClobsContentAsString(fileAsURL);updateCLOB(conn, id, fileContent);out.println("<body><h4>OK: updated an existing "+

record with id="+id+"</h4></body></html>");}catch(Exception e) {e.printStackTrace();out.println("<body><h4>Error: "+e.getMessage()+"</h4></body></html>");

}}

updateCLOB()

The following shows updateCLOB():

public void updateCLOB(Connection conn, String id, String fileContent)throws Exception {PreparedStatement pstmt = null;try {

pstmt = conn.prepareStatement(UPDATE_CLOB);pstmt.setString(1, fileContent);pstmt.setString(2, id);

5203ch08.qxd 8/11/05 4:27 PM Page 296

Page 326: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 297

pstmt.executeUpdate();}finally {

DatabaseUtil.close(pstmt);}

}

getClobsContentAsString()

The following shows getClobsContentAsString():

public static String getClobsContentAsString(String urlAsString)throws Exception {InputStream content = null;try {

java.net.URL url = new java.net.URL(urlAsString);java.net.URLConnection urlConn = url.openConnection();urlConn.connect();content = urlConn.getInputStream();return IOUtil.inputStreamToString(content);

}finally {

IOUtil.close(content);}

}

trimParameter()

The following shows trimParameter():

private static String trimParameter(String s) {if ((s == null) || (s.length() == 0)) {

return s;}else {

return s.trim();}

}

8-15. How Do You Update an Existing CLOB of a MySQL DatabaseUsing a Servlet?The SQL UPDATE statement modifies the existing column’s data in a table. The simplified syntax is asfollows (which updates a single column):

UPDATE table_nameSET column_name_1 = new_value_1,

column_name_2 = new_value_2, ...WHERE column_name_x = some_value_1 and

column_name_y = some_value_2 and ...

You can update any number of columns using the SQL UPDATE statement. For details, pleaserefer to the following Web site: http://www.w3schools.com/sql/sql_update.asp.

To update a CLOB in a MySQL database using a servlet, you will represent the new value of a CLOBas a URL (the URL will be pointing to a CLOB data, such as a text file). The reason for this is that servletscannot access the local file system (a client’s/browser’s local machine). You can also pass the new CLOBvalue to the database as a String object. Therefore, you will represent the new value of a CLOB as a URL.The following sections use the DataFiles table defined in the earlier questions.

5203ch08.qxd 8/11/05 4:27 PM Page 297

Page 327: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS298

Each database vendor handles BLOB/CLOB updates differently. According to the MySQLdocumentation (http://dev.mysql.com/doc/connector/j/en/cj-implementation-notes.html),“the Clob implementation does not allow in-place modification (they are copies, as reported bythe DatabaseMetaData.locatorsUpdateCopies() method). Because of this, you should use thePreparedStatement.setClob() method to save changes back to the database. The JDBC API doesnot have a ResultSet.updateClob() method.” On the other hand, in Oracle, “to write LOB (largeobjects such as CLOB and BLOB) data, the application must acquire a write lock on the LOB object.One way to accomplish this is through a SELECT FOR UPDATE. Also, disable autocommit mode.”

Setting Up the MySQL DatabaseIn MySQL, updating existing CLOBs is simple; in fact, CLOBs are just long String objects. This showshow to set up the database:

mysql> use octopus;Database changedmysql> desc datafiles;+----------+------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------+------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || fileName | varchar(6) | YES | | NULL | || fileBody | text | YES | | NULL | |+----------+------------+------+-----+---------+-------+3 rows in set (0.00 sec)

mysql> select id, filename, filebody-> from datafiles where id=4000;

+------+----------+----------------------------------+| id | filename | filebody |+------+----------+----------------------------------+| 4000 | file40 | This is the content of file4000. |+------+----------+----------------------------------+1 row in set (0.00 sec)

mysql> update datafiles-> set filebody='My New CLOB Value...!!!'-> where id=4000;

Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0

mysql> select id, filename, filebody-> from datafiles where id=4000;

+------+----------+-------------------------+| id | filename | filebody |+------+----------+-------------------------+| 4000 | file40 | My New CLOB Value...!!! |+------+----------+-------------------------+1 row in set (0.00 sec)

5203ch08.qxd 8/11/05 4:27 PM Page 298

Page 328: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 299

Figure 8-29. Displaying a file using a servlet

Figure 8-30. Updating a CLOB using a servlet

Creating the MySQL Servlet InterfaceThe servlet interface is as follows:

http://localhost:8000/octopus/servlet/UpdateMySqlClobServlet?id=<id>&file=<file-as-URL>

Therefore, InsertClobToMySqlServlet has two parameters:

• id (the ID of file, which uniquely identifies record)

• file (the URL of file representing the CLOB; the servlet will open the URL, construct a CLOB,and update it into the CLOB column of the DataFiles table)

Let’s update an existing record with the following data in the DataFiles table:

• id (4000)

• file (the URL of file: http://www.geocities.com/mparsian/data/file500.txt)

Figure 8-29 shows the content of the URL (http://www.geocities.com/mparsian/data/file500.txt).

Therefore, the servlet call is as follows (but all in one line):

http://localhost:8000/octopus/servlet/UpdateMySqlClobServlet?id=4000&file=http://www.geocities.com/mparsian/data/file500.txt

Updating an Existing CLOB RecordFigure 8-30 shows the results of updating an existing CLOB record.

5203ch08.qxd 8/11/05 4:27 PM Page 299

Page 329: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS300

Figure 8-31. Viewing an updated CLOB using a servlet

Viewing the Database Content After InsertionThis shows the database after the insertion:

mysql> select id, filename, filebody from datafiles where id=4000;+------+----------+---------------------------------------------- +| id | filename | filebody |+------+----------+-----------------------------------------------+| 4000 | file40 | This is the first line of file500. || | | This is the 2nd of file500. || | | This is the end of file500. |+------+----------+-----------------------------------------------+1 row in set (0.00 sec)

Using DisplayMySqlClobServlet, you can view the CLOB in a Web browser, as shown inFigure 8-31.

Solution: UpdateMySqlClobServletThe MySQL solution is identical to the Oracle solution with the exception of the getConnection()method, which returns a MySQL Connection object. You can download the complete MySQL solu-tion from the book’s Web site.

The following is getConnection() for the MySQL database:

public static Connection getConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

8-16. How Do You Delete an Existing CLOB of an OracleDatabase Using a Servlet?The SQL DELETE statement deletes rows in a table. The simple syntax is as follows:

DELETE FROM table_nameWHERE column_name_1 = some_value_1 and

Column_name_2 = some_value_2 and ...

The goal is to delete an existing database record that has a CLOB column. You can do this byproviding the primary key for the desired record (to be deleted). You may also delete the CLOB recordusing SQL’s LIKE statement against the content of the CLOB (the body of the file), but this is not

5203ch08.qxd 8/11/05 4:27 PM Page 300

Page 330: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 301

Figure 8-32. Deleting a CLOB using a servlet

recommended. (Most databases will not allow you to index the CLOBs; for example, MySQL allowsyou to index portions of CLOBs.) For solving this problem, you will use the DataFiles table (the idcolumn is the primary key, and fileBody is the CLOB column). The servlet interface is as follows:

http://localhost:8000/octopus/servlet/DeleteClobFromOracleServlet?id=<id>

Therefore, DeleteClobFromMySqlServlet has only one parameter:

• id (the ID of CLOB, which is the primary key that identifies the record)

To delete an existing record with the ID of 500, issue this:

http://localhost:8000/octopus/servlet/DeleteClobFromOracleServlet?id=500

Viewing the Database Content Before DeletionThis is the database before the deletion:

SQL> desc DataFiles;Name Null? Type--------------------- -------- ------------ID NOT NULL NUMBER(38)FILENAME VARCHAR2(20)FILEBODY CLOB

SQL> select id, filename from dataFiles;

ID FILENAME---------- --------------------

4000 file40001000 file12000 file2500 file500

Using a Servlet to Delete a Record (with CLOB)Therefore, the servlet call is as follows:

http://localhost:8000/octopus/servlet/DeleteClobFromOracleServlet?id=500

Viewing the Actual Servlet Call for Deleting a Record (with CLOB)Figure 8-32 shows the result of deleting a record.

Viewing the Database Content After DeletionThis shows the database after the deletion:

5203ch08.qxd 8/11/05 4:27 PM Page 301

Page 331: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS302

SQL> select id, filename from dataFiles;

ID FILENAME---------- --------------------

4000 file40001000 file12000 file2

Solution: DeleteClobFromOracleServletThis shows the DeleteClobFromOracleServlet solution:

import java.io.*;import java.sql.*;

import javax.servlet.*;import javax.servlet.http.*;

import jcb.db.DatabaseUtil;import jcb.util.IOUtil;

public class DeleteClobFromOracleServlet extends HttpServlet {

private static final String DELETE_CLOB_RECORD ="delete from DataFiles where id = ?";

public static Connection getConnection() throws Exception {...}public void doGet(...) {...}public void doPost(...) {...}

}

getConnection()

This shows getConnection():

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@matrix:1521:caspian";String username = "mp";String password = "mp2";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

doGet()

This shows doGet():

public void doGet(HttpServletRequest request,HttpServletResponse response)

throws IOException, ServletException {

Connection conn = null;PreparedStatement pstmt = null;String id = request.getParameter("id").trim();ServletOutputStream out = response.getOutputStream();response.setContentType("text/html");out.println("<html><head><title>Delete CLOB Record</title></head>");

5203ch08.qxd 8/11/05 4:27 PM Page 302

Page 332: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 303

try {conn = getConnection();pstmt = conn.prepareStatement(DELETE_CLOB_RECORD);pstmt.setString(1, id);pstmt.executeUpdate();pstmt.executeUpdate();out.println("<body><h4>deleted CLOB record with id="+id+"</h4></body></html>");

}catch (Exception e) {

out.println("<body><h4>Error="+e.getMessage()+"</h4></body></html>");}finally {DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

doPost()

This shows doPost():

public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {doGet(request, response);

}

8-17. How Do You Delete an Existing CLOB of an MySQL DatabaseUsing a Servlet?The goal is to delete an existing database record, which has a CLOB column. Basically, you can do thisby providing the primary key for the desired record (to be deleted). For solving this problem, youwill use the DataFiles table (the id column is the primary key, and fileBody is the CLOB column).The servlet interface is as follows:

http://localhost:8000/octopus/servlet/DeleteClobFromMySqlServlet?id=<id>

Therefore, DeleteClobFromMySqlServlet has only one parameter:

• id (the ID of CLOB, which is the primary key that identifies the record)

To delete an existing record with the ID of 500, you will issue the following servlet call:

http://localhost:8000/octopus/servlet/DeleteClobFromMySqlServlet?id=500

Viewing the Database Content Before DeletionThis is the database before the deletion:

mysql> use octopus;Database changedmysql> desc datafiles;

5203ch08.qxd 8/11/05 4:27 PM Page 303

Page 333: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS304

Figure 8-33. Deleting a CLOB using a servlet

+----------+------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------+------------+------+-----+---------+-------+| id | int(11) | | PRI | 0 | || fileName | varchar(6) | YES | | NULL | || fileBody | text | YES | | NULL | |+----------+------------+------+-----+---------+-------+3 rows in set (0.00 sec)

mysql> select id, filename from datafiles;+----+----------+| id | filename |+----+----------+| 10 | file1 || 20 | file2 |+----+----------+2 rows in set (0.05 sec)

Using a Servlet to Delete a Record (with CLOB)Therefore, the servlet call is as follows (deleting an existing record with the ID of 10):

http://localhost:8000/octopus/servlet/DeleteClobFromMySqlServlet?id=10

Viewing the Actual Servlet Call for Deleting a Record (with CLOB)Figure 8-33 shows the result of deleting a record.

Viewing the Database Content After DeletionThis is the database after the deletion:

mysql> select id, filename from datafiles;+----+----------+| id | filename |+----+----------+| 20 | file2 |+----+----------+1 row in set (0.00 sec)

Solution: DeleteClobFromMySqlServletThe MySQL solution is identical to the Oracle solution with the exception of getConnection(),which returns a MySQL Connection object. You can download the complete MySQL solution fromthe book’s Web site.

5203ch08.qxd 8/11/05 4:27 PM Page 304

Page 334: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 8 ■ READING AND WRITING CLOBS 305

The following is getConnection() for the MySQL database:

public static Connection getConnection() throws Exception {String driver = "org.gjt.mm.mysql.Driver";String url = "jdbc:mysql://localhost/octopus";String username = "root";String password = "root";Class.forName(driver); // load MySQL driverreturn DriverManager.getConnection(url, username, password);

}

8-18. Should You Use java.lang.String or java.sql.Clob? WhichHas the Best Performance?If you have the choice of manipulating large text data (a large text column of a record such as theCLOB data type), should you use java.lang.String or java.sql.Clob? Which has the best performance?

For better performance, you should use java.sql.Clob, since it does not extract any data fromthe database until you explicitly ask it to (by invoking the getAsciiStream() or getCharacterStream()method). The JDBC data type java.sql.Clob wraps a database locator (which is essentially a pointerto char). That pointer is a rather large number (between 32 and 256 bits in size), but the effort to extractit from the database is insignificant next to extracting the full CLOB content. For insertion into thedatabase, you should use java.lang.String since data has not been uploaded to the database yet.Therefore, use the java.sql.Clob object only for data extraction (whenever possible).

5203ch08.qxd 8/11/05 4:27 PM Page 305

Page 335: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch08.qxd 8/11/05 4:27 PM Page 306

Page 336: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Working with Date, Time, andTimestamp in JDBC

Date, Time, and Timestamp are important data types in commercial and banking applications. Forexample, the following are some of their uses:

• Time of airline arrival

• Date of purchase

• Hire date of an employee

• Update date of employee’s salary

• Purge date of database

• Purchase date of items

• Account creation date

• Account expiration date

The purpose of this chapter is to provide snippets and code samples that deal with Date, Time,and Timestamp. Please note that most of the solutions are provided as independent static methods(In other words, these methods do not depend on external static data structures and rely only on thepassed input arguments; therefore, you can use these methods just as they appear in this chapter.)

The java.sql package provides the following classes that deal with date-related data types(portions of these descriptions were taken from the Java 2 Platform, Standard Edition 1.4.1):

java.sql.Date: Mapping for SQL DATE. The java.sql.Date class extends the java.util.Dateclass. This is a thin wrapper around a millisecond value that allows JDBC to identify this asa SQL DATE value. A millisecond value represents the number of milliseconds that have passedsince January 1, 1970, 00:00:00.000 GMT. To conform with the definition of SQL DATE, the milli-second values wrapped by a java.sql.Date instance must be “normalized” by setting the hours,minutes, seconds, and milliseconds to zero in the particular time zone with which the instanceis associated.

java.sql.Time: Mapping for SQL TIME. The java.sql.Time class extends the java.util.Dateclass. This is a thin wrapper around the java.util.Date class that allows the JDBC API to iden-tify this as a SQL TIME value. The Time class adds formatting and parsing operations to supportthe JDBC escape syntax for time values. The date components should be set to the “zero epoch”value of January 1, 1970, and should not be accessed.

307

C H A P T E R 9

■ ■ ■

5203ch09.qxd 8/11/05 4:28 PM Page 307

Page 337: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC308

java.sql.Timestamp: Mapping for SQL TIMESTAMP. The java.sql.Timestamp class extends thejava.util.Date class. This is a thin wrapper around java.util.Date that allows the JDBC API toidentify this as a SQL TIMESTAMP value. It adds the ability to hold the SQL TIMESTAMP “nanos value”and provides formatting and parsing operations to support the JDBC escape syntax for timestampvalues. This type is a composite of a java.util.Date and a separate nanosecond value. Onlyintegral seconds are stored in the java.util.Date component. The fractional seconds—thenanos—are separate. The getTime method will return only integral seconds. If a time value thatincludes the fractional seconds is desired, you must convert nanos to milliseconds (nanos/1,000,000) and add this to the getTime value. The Timestamp.equals(Object) method never returnstrue when passed a value of type java.util.Date because the nanos component of a date isunknown. As a result, the Timestamp.equals(Object) method is not symmetric with respect tothe java.util.Date.equals(Object) method. Also, the hash code method uses the underlyingjava.util.Data implementation and therefore does not include nanos in its computation. Becauseof the differences between the Timestamp class and the java.util.Date class mentioned previ-ously, it is recommended that you check the vendor’s implementation of SQL’s DATE, TIME, andTIMESTAMP data types. The inheritance relationship between Timestamp and java.util.Date reallydenotes implementation inheritance, not type inheritance.

Each database vendor provides specific data types for supporting Date, Time, and Timestamp. Tofully understand these data types and provide JDBC access to these types, you will see how to use theMySQL and Oracle databases in the examples.

9-1. What Is the Mapping of Date-Related SQL and Java Types?Three JDBC types are related to time:

• The JDBC DATE type (java.sql.Date, which extends java.util.Date) represents a date con-sisting of the day, month, and year. The corresponding SQL DATE type is defined in SQL-92.

• The JDBC TIME type (java.sql.Time, which extends java.util.Date) represents a time consist-ing of hours, minutes, and seconds. The corresponding SQL TIME type is defined in SQL-92.

• The JDBC TIMESTAMP type (java.sql.Timestamp, which extends java.util.Date) represents DATEplus TIME plus a nanosecond field. The corresponding SQL TIMESTAMP type is defined in SQL-92.

The DATE type is where most mismatches occur. This is because the java.util.Date class repre-sents both Date and Time, but SQL has the following three types to represent date and time information:

• A SQL DATE type that represents the date only (01/26/88)

• A SQL TIME type that specifies the time only (09:06:56)

• A SQL TIMESTAMP that represents the time value in nanoseconds

Table 9-1 shows the mapping of JDBC types to Java types, which can be mapped back to JDBCtypes, because this is a one-to-one relationship mapping.

Table 9-1. JDBC Types Mapped to Java Types

JDBC Type Java Type

DATE java.sql.Date

TIME java.sql.Time

TIMESTAMP java.sql.Timestamp

5203ch09.qxd 8/11/05 4:28 PM Page 308

Page 338: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 309

9-2. How Do You Retrieve Date, Time, and Timestamp froma Database?The ResultSet interface provides the necessary methods for extracting Date, Time, and Timestampfrom a database. Table 9-2 lists the ResultSet.getXXX() methods for retrieving data from the Date,Time, and Timestamp types.

Table 9-2. ResultSet Methods for Retrieving Date, Time, and Timestamp (According to the JDK)

Return Type Method Description

java.sql.Date getDate(int columnIndex) Retrieves the value of thedesignated column in the currentrow of this ResultSet object asa java.sql.Date object in the Javaprogramming language.

java.sql.Date getDate(int columnIndex, Retrieves the value of the designated Calendar cal) column in the current row of this

ResultSet object as a java.sql.Dateobject in the Java programminglanguage.

java.sql.Date getDate(String columnName) Retrieves the value of the designatedcolumn in the current row of thisResultSet object as a java.sql.Dateobject in the Java programminglanguage.

java.sql.Date getDate(String columnName, Retrieves the value of the designated Calendar cal) column in the current row of this

ResultSet object as a java.sql.Dateobject in the Java programminglanguage.

java.sql.Time getTime(int columnIndex) Retrieves the value of the designatedcolumn in the current row of thisResultSet object as a java.sql.Timeobject in the Java programminglanguage.

java.sql.Time getTime(int columnIndex, Retrieves the value of the designated Calendar cal) column in the current row of this

ResultSet object as a java.sql.Timeobject in the Java programminglanguage.

java.sql.Time getTime(String columnName) Retrieves the value of the designatedcolumn in the current row of thisResultSet object as a java.sql.Timeobject in the Java programminglanguage.

java.sql.Time getTime(String columnName, Retrieves the value of the designated Calendar cal) column in the current row of this

ResultSet object as a java.sql.Timeobject in the Java programminglanguage. This method uses the givencalendar to construct an appropriatemillisecond value for the time if theunderlying database does not storetime zone information.

Continued

5203ch09.qxd 8/11/05 4:28 PM Page 309

Page 339: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC310

Table 9-2. (Continued)

Return Type Method Description

java.sql.Timestamp getTimestamp(int columnIndex) Retrieves the value of thedesignated column in the currentrow of this ResultSet object asa java.sql.Timestamp object in theJava programming language.

java.sql.Timestamp getTimestamp(int columnIndex, Retrieves the value of the designated Calendar cal) column in the current row of this

ResultSet object asa java.sql.Timestamp object in theJava programming language.

java.sql.Timestamp getTimestamp(String columnName) Retrieves the value of thedesignated column in the currentrow of this ResultSet object asa java.sql.Timestamp object.

java.sql.Timestamp getTimestamp(String columnName, Retrieves the value of the designated Calendar cal) column in the current row of this

ResultSet object asa java.sql.Timestamp object in theJava programming language.

9-3. How Does MySQL Handle Date, Time, and Timestamp?In MySQL, the date and time types are DATETIME, DATE, TIMESTAMP, TIME, and YEAR. According to theMySQL manual, the DATETIME, DATE, and TIMESTAMP types are related:

DATETIME: Use the DATETIME type when you need values that contain both date and time infor-mation. MySQL retrieves and displays DATETIME values in YYYY-MM-DD HH:MM:SS format.The supported range is from 1000-01-01 00:00:00 to 9999-12-31 23:59:59. (Supported meansthat although other values might work, there is no guarantee they will.)

DATE: Use the DATE type when you need only a date value without a time part. MySQL retrievesand displays DATE values in YYYY-MM-DD format. The supported range is from 1000-01-01 to9999-12-31.

TIMESTAMP: The TIMESTAMP column type provides a type that you can use to automatically markINSERT or UPDATE operations with the current date and time. If you have multiple TIMESTAMPcolumns, only the first one is updated automatically. For automatic updating of the first TIMESTAMPcolumn, please refer to the MySQL manual.

9-4. How Does Oracle Handle Date, Time, and Timestamp?The Oracle DATE data type contains both date and time information. The Oracle DATE data type isa complex data type that encapsulates date, time, and timestamp concepts.

Table 9-3 shows the mapping between JDBC data types, Java native data types, and Oracledata types:

5203ch09.qxd 8/11/05 4:28 PM Page 310

Page 340: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 311

Table 9-3.Oracle’s Date Types

Standard JDBC Data Types Java Native Data Types Oracle Data Types

java.sql.Types.DATE java.sql.Date DATE

java.sql.Types.TIME java.sql.Time DATE

java.sql.Types.TIMESTAMP javal.sql.Timestamp DATE

To understand these three classes, I will show how to define a table that has columns thatdirectly deal with these three classes. Here is the definition of the table:

create table TestDates(id VARCHAR2(10) NOT NULL Primary Key,date_column DATE,time_column DATE,timestamp_column DATE

);

Here is how you create the TestDates table (the output has been formatted):

$ sqlplus scott/tigerSQL*Plus: Release 9.2.0.1.0 - Production on Sun Nov 24 21:47:08 2002

SQL> create table TestDates(2 id VARCHAR2(10) NOT NULL Primary Key,3 date_column DATE,4 time_column DATE,5 timestamp_column DATE6 );

Table created.SQL> commit;Commit complete.

SQL> describe TestDatesName Null? Type--------------------- -------- -------------ID NOT NULL VARCHAR2(10)DATE_COLUMN DATETIME_COLUMN DATETIMESTAMP_COLUMN DATE

I will cover the following topics for the TestDates table:

• Inserting a new record

• Retrieving an existing record

• Updating an existing record

• Deleting an existing record

To simplify your work in these examples, you will get a JDBC Connection object from a staticmethod. (In real database applications, you would get the Connection object, java.sql.Connection,from a connection pool manager, which manages a set of JDBC Connection objects.)

5203ch09.qxd 8/11/05 4:28 PM Page 311

Page 341: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC312

Oracle Date and Time Types: Inserting a New RecordThe following program creates a new record in the TestDates table (with an ID of "id100"). Here isthe program listing:

import java.sql.*;import jcb.util.DatabaseUtil;

public class InsertDate {public static Connection getConnection() throws Exception {

String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:scorpian";Class.forName(driver);System.out.println("ok: loaded oracle driver.");return DriverManager.getConnection(url, "scott", "tiger");

}

public static void main(String args[]) {String INSERT_RECORD = "insert into TestDates(id, date_column, "+

"time_column, timestamp_column) values(?, ?, ?, ?)";Connection conn = null;PreparedStatement pstmt = null;try {

conn = getConnection();pstmt = conn.prepareStatement(INSERT_RECORD);pstmt.setString(1, "id100");

java.util.Date date = new java.util.Date();long t = date.getTime();java.sql.Date sqlDate = new java.sql.Date(t);java.sql.Time sqlTime = new java.sql.Time(t);java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(t);System.out.println("sqlDate="+sqlDate);System.out.println("sqlTime="+sqlTime);System.out.println("sqlTimestamp="+sqlTimestamp);pstmt.setDate(2, sqlDate);pstmt.setTime(3, sqlTime);pstmt.setTimestamp(4, sqlTimestamp);pstmt.executeUpdate();

}catch( Exception e ) {

e.printStackTrace();System.out.println("Failed to insert the record.");System.exit(1);

}finally {

DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Testing InsertDateRunning the InsertDate.java program will generate the following output (the values are beforeinsertion to TestDates):

5203ch09.qxd 8/11/05 4:28 PM Page 312

Page 342: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 313

$ javac InsertDate.java$ java InsertDateok: loaded oracle driver.sqlDate=2002-11-24sqlTime=23:05:52sqlTimestamp=2002-11-24 23:05:52.717

Oracle Date and Time Types: Retrieving an Existing RecordThe following program retrieves an existing record from the TestDates table (with an ID of "id100").Here is the program listing:

import java.sql.*;import jcb.util.DatabaseUtil;

public class GetDate {

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:scorpian";Class.forName(driver);System.out.println("ok: loaded oracle driver.");return DriverManager.getConnection(url, "scott", "tiger");

}

public static void main(String args[]) {String GET_RECORD = "select date_column, time_column, "+"timestamp_column from TestDates where id = ?";

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

conn = getConnection();pstmt = conn.prepareStatement(GET_RECORD);pstmt.setString(1, "id100");rs = pstmt.executeQuery();if (rs.next()) {

java.sql.Date dbSqlDate = rs.getDate(1);java.sql.Time dbSqlTime = rs.getTime(2);java.sql.Timestamp dbSqlTimestamp = rs.getTimestamp(3);System.out.println("dbSqlDate="+dbSqlDate);System.out.println("dbSqlTime="+dbSqlTime);System.out.println("dbSqlTimestamp="+dbSqlTimestamp);

}}catch( Exception e ) {

e.printStackTrace();System.out.println("Failed to insert the record.");System.exit(1);

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

5203ch09.qxd 8/11/05 4:28 PM Page 313

Page 343: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC314

Testing GetDateRunning the GetDate.java program will generate the following output (the values are after retrievalfrom TestDates):

$ javac GetDate.java$ java GetDateok: loaded oracle driver.dbSqlDate=2002-11-24dbSqlTime=23:05:52dbSqlTimestamp=2002-11-24 23:05:52.0

As you can observe, whatever values you deposited into the database, you were able to get backcorrectly (with the exception of Timestamp, which differs in value, so this is negligible).

9-5. How Do You Get the Current Date As a java.util.Date Object?The class java.util.Date represents a specific instant in time, with millisecond precision. The fol-lowing method returns the current date as a java.util.Date object:

/*** Get current date as a java.util.Date object* @return current date as a java.util.Date object*/public static java.util.Date getJavaUtilDate() {

java.util.Date date = new java.util.Date();return date;

}

9-6. How Do You Create a java.sql.Date Object?The class java.sql.Date descends from the java.util.Date class but uses only the year, month, andday values. The JDK defines java.sql.Date as follows:

public class java.sql.Dateextends java.util.Date

Also, the JDK says this about java.sql.Date:

A thin wrapper around a millisecond value that allows JDBC to identify this as an SQL DATEvalue. A milliseconds value represents the number of milliseconds that have passed sinceJanuary 1, 1970 00:00:00.000 GMT. To conform with the definition of SQL DATE, the milli-second values wrapped by a java.sql.Date instance must be “normalized” by setting thehours, minutes, seconds, and milliseconds to zero in the particular time zone with which theinstance is associated.

You have several ways to create a java.sql.Date object.

Creating java.sql.Date Using java.util.DateThis is how you create a java.sql.Date object using java.util.Date:

public static java.sql.Date getJavaSqlDate() {java.util.Date today = new java.util.Date();return new java.sql.Date(today.getTime());

}

5203ch09.qxd 8/11/05 4:28 PM Page 314

Page 344: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 315

Creating java.sql.Date Using java.util.CalendarThe following is according to the JDK:

The Calendar class is an abstract class that provides methods for converting between a spe-cific instant in time and a set of calendar fields such as YEAR, MONTH, DAY_OF_MONTH,HOUR, and so on, and for manipulating the calendar fields, such as getting the date of thenext week. An instant in time can be represented by a millisecond value that is an offset fromthe epoch, January 1, 1970, 00:00:00.000 GMT (Gregorian).

Using the Calendar object, you can set the year, month, and day portions to the desired values.But you must set the hour, minute, second, and millisecond values to zero. At that point, Calendar.getTime().getTime() is invoked to get the java.util.Date in milliseconds. That value is then passedto a java.sql.Date constructor.

// get a calendar using the default time zone and locale.Calendar calendar = Calendar.getInstance();

// set Date portion to January 1, 1970calendar.set(Calendar.YEAR, 1970 );calendar.set(Calendar.MONTH, Calendar.JANUARY );calendar.set(Calendar.DATE, 1 );

// normalize the objectcalendar.set(Calendar.HOUR_OF_DAY, 0 );calendar.set(Calendar.MINUTE, 0 );calendar.set(Calendar.SECOND, 0 );calendar.set(Calendar.MILLISECOND, 0 );

java.sql.Date javaSqlDate =new java.sql.Date( calendar.getTime().getTime() );

Creating java.sql.Date Using the java.sql.Date.valueOf() MethodYou can use a static method (valueOf()) of the java.sql.Date class. The java.sql.Date object’s valueOf()method accepts a String, which must be the date in JDBC time escape format: YYYY-MM-DD. Forexample, to create a Date object representing November 1, 2000, you would use this:

String date = "2000-11-01";java.sql.Date javaSqlDate = java.sql.Date.valueOf(date);

The java.sql.Date.valueOf() method throws an IllegalArgumentException (a runtime excep-tion) if the given date is not in the JDBC date escape format (YYYY-MM-DD).

Creating java.sql.Date Using GregorianCalendarGregorianCalendar is a concrete subclass of Calendar and provides the standard calendar sys-tem used by most of the world. You may use the year, month, and day of month to constructa GregorianCalendar with the given date set in the default time zone with the default locale. Forexample, to create a Date object representing November 1, 2000, you would use this:

// month value is 0-based. e.g., 0 for January.Calendar calendar = new GregorianCalendar

(2000, // year10, // month1); // day of month

5203ch09.qxd 8/11/05 4:28 PM Page 315

Page 345: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC316

9-7. How Do You Get the Current Timestamp As ajava.sql.Timestamp Object?The following methods return a current java.sql.Timestamp object.

Getting a Timestamp Object Using java.util.DateThis shows how to get a Timestamp object using java.util.Date:

/*** Return a Timestamp for right now* @return Timestamp for right now*/public static java.sql.Timestamp getJavaSqlTimestamp() {

java.util.Date today = new java.util.Date();return new java.sql.Timestamp(today.getTime());

}

Getting a Timestamp Object Using System.currentTimeMillis()This shows how to get a Timestamp object using System.currentTimeMillis():

/*** Return a Timestamp for right now* @return Timestamp for right now*/public static java.sql.Timestamp nowTimestamp() {

return new java.sql.Timestamp(System.currentTimeMillis());}

9-8. How Do You Get the Current Timestamp As a java.sql.TimeObject?The following method returns a current java.sql.Time object:

public static java.sql.Time getJavaSqlTime() {java.util.Date today = new java.util.Date();return new java.sql.Time(today.getTime());

}

9-9. How Do You Convert from a java.util.Date Object to ajava.sql.Date Object?The following code snippet shows how to convert from a java.util.Date object to a java.sql.Dateobject:

java.util.Date utilDate = new java.util.Date();java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());System.out.println("utilDate:" + utilDate));System.out.println("sqlDate:" + sqlDate));

This shows how to do the preceding as a method:

/*** Creates a java.sql.Date from a java.util.Date* return null if the parameter was null* @param utilDate the date to turn into the java.sql.Date object

5203ch09.qxd 8/11/05 4:28 PM Page 316

Page 346: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 317

* @return the date or null if the utilDate was null*/public static java.sql.Date sqlDate(java.util.Date utilDate) {if (utilDate == null) {

return null;}else {

return new java.sql.Date(utilDate.getTime());}

}

9-10. How Do You Convert a String Date Such As 2003/01/10into a java.util.Date Object?When you want to convert a date expressed as a String object, you can use java.text.SimpleDateFormatfor formatting purposes, like so:

import java.text.SimpleDateFormat;...try {

SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");String date = "2003/01/10";java.util.Date utilDate = formatter.parse(date);System.out.println("date:" + date));System.out.println("utilDate:" + utilDate));

}catch (ParseException e) {

System.out.println(e.toString());e.printStackTrace();

}

This shows how to do the preceding as a method:

import java.text.SimpleDateFormat;.../*** Make a java.util.Date from a date as a string format of MM/DD/YYYY.* @param dateString date as a string of MM/DD/YYYY format.* @return a java.util.Date; if input is null return null;* @exception throws ParseException if the input is not valid format.*/public static java.util.Date makeDate(String dateString)

throws ParseException {if ((dateString == null) || (dateString.length() == 0)) {

return null;}SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");return formatter.parse(dateString);

}

9-11. How Do You Create Yesterday’s Date from a Date in theString Format of MM/DD/YYYY?The following code listing creates yesterday’s date from a Date in the String format of MM/DD/YYYY:

/*** Create yesterday's date (as java.util.Date) from a date as a string* format of MM/DD/YYYY. If input is 03/01/2000, then it will

5203ch09.qxd 8/11/05 4:28 PM Page 317

Page 347: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC318

* return a date with string value of 02/29/2000; and if your input* is 03/01/1999, then it will return a date with string value of* 02/28/1999.* @param dateString date as a string of MM/DD/YYYY format.* @return a yesterday's date (as java.util.Date); if input is null* return null;* @exception throws ParseException if the input is not valid format.*/public static java.util.Date makeYesterdayDate(String dateString)

throws ParseException {

if ((dateString == null) || (dateString.length() == 0)) {return null;

}

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");GregorianCalendar gc = new GregorianCalendar();// the following stmt. will throw a ParseException// if dateString does not have a valid format.java.util.Date d = sdf.parse(dateString);gc.setTime(d);System.out.println("Input Date = " + sdf.format(d));int dayBefore = gc.get(Calendar.DAY_OF_YEAR);gc.roll(Calendar.DAY_OF_YEAR, -1);int dayAfter = gc.get(Calendar.DAY_OF_YEAR);if(dayAfter > dayBefore) {

gc.roll(Calendar.YEAR, -1);}gc.get(Calendar.DATE);java.util.Date yesterday = gc.getTime();System.out.println("Yesterdays Date = " + sdf.format(yesterday));return yesterday;

}

9-12. How Do You Create a java.util.Date Object from a Year,Month, Day Format?The following code listing creates a java.util.Date object from a Year, Month, and Day format:

import java.text.SimpleDateFormat;import java.util.Date;.../*** Make a java.util.Date from Year, Month, Day.* @param year the year* @param month the month* @param day the day* @throws ParseException failed to make a date.*/public static Date getJavaUtilDate(int year, int month, int day)

throws ParseException {

String date = new String(year) +"/"+ new String(month) +"/"+ new String(day);java.util.Date utilDate = null;

5203ch09.qxd 8/11/05 4:28 PM Page 318

Page 348: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 319

try {SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");utilDate = formatter.parse(date);System.out.println("utilDate:" + utilDate));return utilDate;

}catch (ParseException e) {

System.out.println(e.toString());e.printStackTrace();

}}

9-13. How Do You Convert a String Date Such As 2003/01/10 toa java.sql.Date Object?The following shows how to convert a String date such as 2003/01/10 into a java.sql.Date object:

import java.text.SimpleDateFormat;...try {

SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");String date = "2003/01/10";java.util.Date utilDate = formatter.parse(date);java.sql.Date sqlDate = new java.sql.Date(util.getTime());System.out.println("date:" + date));System.out.println("sqlDate:" + sqlDate));

}catch (ParseException e) {

System.out.println(e.toString());e.printStackTrace();

}

This shows how to do the preceding as a method using getJavaSqlDate():

import java.text.SimpleDateFormat;.../*** Make a java.sql.Date from (year, month, day).* @param year the year* @param month the month* @param day the day* @throws ParseException failed to make a date.*/public static java.sql.Date getJavaSqlDate(int year, int month, int day)

throws ParseException {String date = new String(year) +"/"+ new String(month) +"/"+ new String(day);java.sql.Date sqlDate = null;try {

SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");java.util.Date utilDate = formatter.parse(date);sqlDate = new java.sql.Date(util.getTime());System.out.println("sqlDate:" + sqlDate));return sqlDate;

}

5203ch09.qxd 8/11/05 4:28 PM Page 319

Page 349: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC320

catch (ParseException e) {System.out.println(e.toString());e.printStackTrace();

}}

9-14. How Do You Get a Timestamp Object?This shows how to get a Timestamp object:

java.sql.Date date = new java.util.Date();java.sql.Timestamp timestamp = new java.sql.Timestamp(date.getTime());System.out.println("date: " + date);System.out.println("Timestamp: " + timestamp);

This shows how to get a Timestamp object as a method:

import java.sql.Timestamp;import java.util.Date;.../*** Creates a java.sql.Timestamp from a java.util.Date or* return null if the parameter was null* @param utilDate the date to turn into the SQL Timestamp object* @return the timestamp or null if the utilDate was null*/public static Timestamp getJavaSqlTimestamp(Date utilDate) {

if (utilDate == null) {return null;

}else {

return new Timestamp(utilDate.getTime());}

}

/*** Creates a current java.sql.Timestamp* @return the timestamp*/public static Timestamp getJavaSqlTimestamp() {Date date = new Date();return new Timestamp(date.getTime());

}

9-15. How Do You Create a java.sql.Time Object?java.sql.Time descends from java.util.Date but uses only the hour, minute, and second values.The JDK defines java.sql.Time as follows:

public class java.sql.Time extends java.util.Date

In addition, the JDK says this about java.sql.Time:

A thin wrapper around the java.util.Date class that allows the JDBC API to identify this as anSQL TIME value. The Time class adds formatting and parsing operations to support the JDBCescape syntax for time values. The date components should be set to the “zero epoch” value ofJanuary 1, 1970, and should not be accessed.

5203ch09.qxd 8/11/05 4:28 PM Page 320

Page 350: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 321

You have different ways to create a java.sql.Time object, which are presented next.

Creating a java.sql.Time Object from a java.util.Date ObjectThe following shows how to create a java.sql.Time object from a java.util.Date object:

/*** Creates a java.sql.Time from a java.util.Date or return null* if the parameter was null.* @param utilDate the date to turn into the SQL Time object* @return the time or null if the utilDate was null*/public static java.sql.Time getJavaSqlTime(java.util.Date utilDate) {if (utilDate == null) {

return null;}else {

return new java.sql.Time(utilDate.getTime());}

}

Creating a java.sql.Time Object from a java.util.Calendar ObjectYou can use a Calendar object by setting the year, month, and day portions to January 1, 1970, whichis Java’s zero epoch. The millisecond value must also be set to zero. At that point, Calendar.getTime().getTime() is invoked to get the time in milliseconds. That value is then passed to a java.sql.Timeconstructor.

Calendar cal = Calendar.getInstance();

// set Date portion to January 1, 1970cal.set( Calendar.YEAR, 1970 );cal.set( Calendar.MONTH, Calendar.JANUARY );cal.set( Calendar.DATE, 1 );

// set milliseconds portion to 0cal.set( Calendar.MILLISECOND, 0 );

java.sql.Time javaSqlTime = new java.sql.Time( cal.getTime().getTime() );

Creating a java.sql.Time Object Using java.sql.Time.valueOf()The java.sql.Time object’s valueOf() method accepts a String, which must be the time in JDBCtime escape format—HH:MM:SS. For example, to create a java.sql.Time object for 9:23 p.m., youcan write this:

java.sql.Time javaSqlTime = java.sql.Time.valueOf( "21:23:00" );

Creating a java.sql.Time Object Using the java.lang.System ClassThe following shows how to create a java.sql.Time object using the java.lang.System class:

java.sql.Time tm = new java.sql.Time(System.currentTimeMillis());

5203ch09.qxd 8/11/05 4:28 PM Page 321

Page 351: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC322

9-16. How Do You Convert the Current Time to a java.sql.DateObject?The following shows how to convert the current time to a java.sql.Date object:

import java.util.Calendar;import java.sql.Date;...Calendar currenttime = Calendar.getInstance();Date sqldate = new Date((currenttime.getTime()).getTime());or as a method:

import java.util.Calendar;import java.sql.Date;...public static Date getCurrentJavaSqlDate () {

Calendar currenttime = Calendar.getInstance();return new Date((currenttime.getTime()).getTime());

}

9-17. How Do You Determine the Day of the Week from Today’sDate?The answer to this question is to use java.util.Calendar.SUNDAY, java.util.Calendar.MONDAY, andso on:

public static int getDayOfWeek() {java.util.Date today = new java.util.Date();java.sql.Date date = new java.sql.Date(today.getTime());java.util.GregorianCalendar cal = new java.util.GregorianCalendar();cal.setTime(date);return cal.get(java.util.Calendar.DAY_OF_WEEK);

}

9-18. How Do You Determine the Day of the Week from a Givenjava.util.Date Object?The answer to this question is to use java.util.Calendar.SUNDAY, java.util.Calendar.MONDAY, andso on:

public static int getDayOfWeek(java.util.Date utilDate)throws Exception {

if (utilDate == null) {throw new Exception("date can not be null.");

}java.sql.Date d = new java.sql.Date(utilDate.getTime());java.util.GregorianCalendar cal = new java.util.GregorianCalendar();cal.setTime(d);return cal.get(java.util.Calendar.DAY_OF_WEEK);

}

9-19. How Do You Convert java.sql.Date to java.util.Date?The following shows how to convert java.sql.Date to java.util.Date:

5203ch09.qxd 8/11/05 4:28 PM Page 322

Page 352: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 323

public java.util.Date convert(java.sql.Date source) {if (source == null) {

return null;}return new java.util.Date(source.getTime());

}

9-20. What Is java.text.SimpleDateFormat?According to J2SE 5.0, SimpleDateFormat extends the DateFormat class and is defined as follows:

SimpleDateFormat is a concrete class for formatting and parsing dates in a locale-sensitivemanner. It allows for formatting (date -> text), parsing (text -> date), and normalization.SimpleDateFormat allows you to start by choosing any user-defined patterns for date-timeformatting. However, you are encouraged to create a date-time formatter with either get-TimeInstance, getDateInstance, or getDateTimeInstance in DateFormat. Each of these classmethods can return a date/time formatter initialized with a default format pattern.You maymodify the format pattern using the applyPattern methods as desired. For more informationon using these methods, see DateFormat.

Table 9-4 lists some useful format strings for java.text.SimpleDateFormat.

Table 9-4. Sample Format Strings for SimpleDateFormat

Standard String Format Purpose

ISO 8160 yyyy-MM-dd'T'H:mm:ss'Z' Used in EDI/OBI data, and so on

ISO 8610 yyyyMMdd'T'HH:mm:ss Used in XML-RPC

SQL Date yyyy.MM.dd JDBC date format

9-21. How Do You Convert java.util.Date to a Date String in theFormat MM/DD/YYYY?The following shows how to convert java.util.Date to a Date string in the format MM/DD/YYYY:

import java.util.Calendar;import java.util.Date;.../*** Convert java.util.Date to a date String in the format MM/DD/YYYY* @param date the java.util.Date* @return a date String in the format MM/DD/YYYY* (if input is null, then return null).*/public static String toDateString(Date date) {

if (date == null) {return null;

}

Calendar calendar = Calendar.getInstance();calendar.setTime(date);int month = calendar.get(Calendar.MONTH) + 1;int day = calendar.get(Calendar.DAY_OF_MONTH);int year = calendar.get(Calendar.YEAR);

5203ch09.qxd 8/11/05 4:28 PM Page 323

Page 353: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC324

String monthString;if (month < 10) {

monthString = "0" + month;}else {

monthString = "" + month;}

String dayString;if (day < 10) {

dayString = "0" + day;}else {

dayString = "" + day;}

String yearString = "" + year;return monthString + "/" + dayString + "/" + yearString;

}

9-22. How Do You Create a Time String in the FormatHH:MM:SS from an Hour, Minute, Second Format?The following shows how to make a Time string in the format HH:MM:SS from an Hour, Minute,Second format:

/*** Makes a time String in the format HH:MM:SS from separate ints* for hour, minute, and second. If the seconds are 0, then the output* is in HH:MM. It is assumed that all of the input arguments will have* proper values.* @param hour the hours as integer* @param minute the minutes as integer* @param second the seconds integer* @return a time String in the format HH:MM:SS or HH:MM*/public static String toTimeString(int hour, int minute, int second) {

String hourString;if (hour < 10) {

hourString = "0" + hour;}else {

hourString = "" + hour;}

String minuteString;if (minute < 10) {

minuteString = "0" + minute;}else {

minuteString = "" + minute;}String secondString;if (second < 10) {

secondString = "0" + second;

5203ch09.qxd 8/11/05 4:28 PM Page 324

Page 354: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 325

else {secondString = "" + second;

}

if (second == 0) {return hourString + ":" + minuteString;

}else {

return hourString + ":" + minuteString + ":" + secondString;}

}

9-23. How Do You Convert a java.util.Date Object to a TimeString in the Format HH:MM:SS?This method uses the toTimeString(int hour, int minute, int second) method defined previously:

import java.util.Date;import java.util.Calendar;.../*** Convert java.util.Date to a time String in the format HH:MM:SS.* If the seconds are 0, then the output is in HH:MM format.* @param date The java.util.Date* @return a time String in the format HH:MM:SS or HH:MM*/public static String toTimeString(Date date) {

if (date == null)return null;

}

Calendar calendar = Calendar.getInstance();calendar.setTime(date);return (toTimeString(calendar.get(Calendar.HOUR_OF_DAY),

calendar.get(Calendar.MINUTE),calendar.get(Calendar.SECOND)));

}

9-24. How Do You Check for a Leap Year?The following methods check whether a given year is a leap year. A year is a leap year if it is an evenmultiple of 4; however, years divisible by 100 but not by 400 aren’t leap years. For example, 1900 isn’ta leap year, but 1600 and 2000 both are. (An alternate definition of a leap year is that a specific year isa leap year if it is either evenly divisible by 400 or evenly divisible by 4 and not evenly divisible by 100.)

You can check whether a given year is a leap year in one of two ways.

Solution 1: Checking for a Leap YearThis checks for a leap year:

/*** Checks if a year is a leap year. If input is* a negative integer, then it returns false.

5203ch09.qxd 8/11/05 4:28 PM Page 325

Page 355: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC326

* @param year The year to check.* @return true: the year is a leap year;* false: the year is a normal year.*/public static boolean isLeapYear(int year) {

if (year < 0) {return false;

}

if (year % 400 == 0) {return true;

}else if (year % 100 == 0) {

return false;}else if (year % 4 == 0) {

return true;}else {

return false;}

}

Solution 2: Checking for a Leap YearThis also checks for a leap year:

import java.util.GregorianCalendar;.../*** Determining If a Year Is a Leap Year. If input is* a negative integer, then it returns false.* @param year The year to check.* @return true: the year is a leap year;* false: the year is a normal year.*/public static boolean isLeapYear(int year) {

if (year < 0) {return false;

}

GregorianCalendar gcal = new GregorianCalendar();return gcal.isLeapYear(year);

}

9-25. How Do You Convert Between Different Java Date Classes?Table 9-5 shows the conversion of Java Date classes, and Table 9-6 shows the conversion of the JavaDate class to Calendar.

5203ch09.qxd 8/11/05 4:28 PM Page 326

Page 356: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 327

Table 9-5. Conversion of Java Date Classes

From/To... java.util.Date java.sql.Date

java.util.Date to.setTime(from.getTime())

java.sql.Date to.setTime(from.getTime())

java.util.Calendar to = from.getTime() to.setTime(from.getTime().getTime())

long (milliseconds) to.setTime(from) to.setTime(from)

Table 9-6. Conversion of Java Date Classes to Calendar

From/To java.util.Calendar long (Milliseconds)

java.util.Date to.setTime(from) to = from.getTime()

java.sql.Date to.setTimeInMillis(from.getTime()) to = from.getTime()

java.util.Calendar to = from.getTime().getTime()

long (milliseconds) to.setTimeInMillis(from)

9-26. How Do You Add/Subtract Days for a Given Date (Given Asa String)?The following shows how to add/subtract days for a given date (given as a String):

import java.util.Date;import java.util.Calendar;import java.text.SimpleDateFormat;import java.text.ParseException;.../*** Add/Subtract days for a given date given as a string* format of MM/DD/YYYY. If input is 03/01/2000, and delta is -1,* then it will return a date with string value of 02/29/2000;* and if your input is 03/01/1999, and delta is -1, then it will* return a date with string value of 02/28/1999.* @param dateString date as a string of MM/DD/YYYY format.* @param delta the number of days to add/subtract* @return a new date (as java.util.Date) based on delta days; if* input is null return null;* @exception throws ParseException is the input is not valid format.*/public static Date makeDate(String dateString, int delta)

throws ParseException {

if ((dateString == null) || (dateString.length() == 0)) {return null;

}

SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");

// the following stmt. will throw a ParseException// if dateString does not have a valid format.Date d = formatter.parse(dateString);System.out.println("originalDate="+formatter.format(d));

5203ch09.qxd 8/11/05 4:28 PM Page 327

Page 357: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC328

if (delta == 0) {return d;

}

Calendar cal = Calendar.getInstance();cal.setTime(d);cal.add(Calendar.DATE ,delta);Date revisedDate = cal.getTime();System.out.println("revisedDate="+formatter.format(revisedDate));return revisedDate;

}

Testing makeDate()The following shows how to test makeDate():

java.util.Date revisedDate2 = makeDate("03/01/2000", -1);System.out.println("revisedDate2="+revisedDate2);System.out.println("---");java.util.Date revisedDate3 = makeDate("03/01/2000", 10);System.out.println("revisedDate3="+revisedDate3);

Viewing the Output of the TestThe following is the output of the test:

originalDate=03/01/2000revisedDate=02/29/2000revisedDate2=Tue Feb 29 00:00:00 PST 2000---originalDate=03/01/2000revisedDate=03/11/2000revisedDate3=Sat Mar 11 00:00:00 PST 2000

9-27. How Do You Find the Difference Between Two GivenDates?Let’s say that d1 represents 1/10/2000 (as a java.util.Date object) and d2 represents 2/31/2000 (asa java.util.Date object). How do you find the difference of d1 and d2? In other words, how manydays apart are these dates from each other? The answer is 52 days. I will provide the solution forfinding the difference between two java.util.Date objects as well as for between two java.sql.Dateobjects. The following solution is a simple class called DateDiff.

Finding the Difference Between Two java.util.Date ObjectsThe following shows how to find the difference between two java.util.Date objects:

java.util.Date d1 = DateDiff.makeDate("1/10/2000");java.util.Date d2 = DateDiff.makeDate("2/31/2000");int diff = DateDiff.diff( d1, d2 );System.out.println("d1="+d1);System.out.println("d2="+d2);System.out.println("diff="+diff);output will be:

5203ch09.qxd 8/11/05 4:28 PM Page 328

Page 358: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 329

d1=Mon Jan 10 00:00:00 PST 2000d2=Thu Mar 02 00:00:00 PST 2000diff=52

Finding the Difference Between Two java.sql.Date ObjectsThe following shows how to find the difference between two java.sql.Date objects:

java.util.Date utilDate1 = DateDiff.makeDate("12/01/1990");java.sql.Date sqlDate1 = new java.sql.Date(utilDate1.getTime());System.out.println("utilDate1:" + utilDate1);System.out.println("sqlDate1:" + sqlDate1);

java.util.Date utilDate2 = DateDiff.makeDate("1/24/1991");java.sql.Date sqlDate2 = new java.sql.Date(utilDate2.getTime());System.out.println("utilDate2:" + utilDate2);System.out.println("sqlDate2:" + sqlDate2);

int diffSqlDates = diff( sqlDate1, sqlDate2 );System.out.println("diffSqlDates="+diffSqlDates);output will be:

utilDate1:Sat Dec 01 00:00:00 PST 1990sqlDate1:1990-12-01utilDate2:Thu Jan 24 00:00:00 PST 1991sqlDate2:1991-01-24Diffirent Day : 54diffSqlDates=54

Viewing the DateDiff ClassThis is the DateDiff class:

import java.util.*;import java.text.*;

public class DateDiff {

/*** Calculate the difference of two dates* (in terms of number of days).* @param date1 the java.util.Date object* @param date2 the java.util.Date object*/public static int diff( Date date1, Date date2 ) {

Calendar c1 = Calendar.getInstance();Calendar c2 = Calendar.getInstance();

c1.setTime( date1 );c2.setTime( date2 );int diffDay = 0 ;

if ( c1.before( c2 ) ) {diffDay = countDiffDay ( c1, c2 );

}else {

diffDay = countDiffDay ( c2, c1 );}

5203ch09.qxd 8/11/05 4:28 PM Page 329

Page 359: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC330

return diffDay;}

public DateDiff( Date date1, Date date2 ) {int diffDay = diff(date1, date2);System.out.println("Different Day : " + diffDay );

}

public static int countDiffDay( Calendar c1, Calendar c2 ) {int returnInt = 0;while ( !c1.after(c2) ) {

c1.add( Calendar.DAY_OF_MONTH, 1 );returnInt ++;

}

if ( returnInt > 0 ) {returnInt = returnInt - 1;

}

return ( returnInt );}

public static Date makeDate(String dateString)throws Exception {SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");return formatter.parse(dateString);

}

public static void main ( String argv[] ) throws Exception {Calendar cc1 = Calendar.getInstance();Calendar cc2 = Calendar.getInstance();cc1.add( Calendar.DAY_OF_MONTH, 10 );

DateDiff myDate = new DateDiff( cc1.getTime(), cc2.getTime() );

java.util.Date d1 = makeDate("10/10/2000");java.util.Date d2 = makeDate("10/18/2000");DateDiff diff12 = new DateDiff( d1, d2 );

java.util.Date d3 = makeDate("1/1/2000");java.util.Date d4 = makeDate("12/31/2000");int diff34 = diff( d3, d4 );System.out.println("diff34="+diff34);

java.util.Date d5 = makeDate("1/10/2000");java.util.Date d6 = makeDate("2/31/2000");int diff56 = diff( d5, d6 );System.out.println("d5="+d5);System.out.println("d6="+d6);System.out.println("diff56="+diff56);

java.util.Date utilDate1 = DateDiff.makeDate("12/01/1990");java.sql.Date sqlDate1 = new java.sql.Date(utilDate1.getTime());System.out.println("utilDate1:" + utilDate1);System.out.println("sqlDate1:" + sqlDate1);

5203ch09.qxd 8/11/05 4:28 PM Page 330

Page 360: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 331

java.util.Date utilDate2 = DateDiff.makeDate("1/24/1991");java.sql.Date sqlDate2 = new java.sql.Date(utilDate2.getTime());System.out.println("utilDate2:" + utilDate2);System.out.println("sqlDate2:" + sqlDate2);

int diffSqlDates = diff( sqlDate1, sqlDate2 );System.out.println("diffSqlDates="+diffSqlDates);

}}

9-28. How Do You Convert a Timestamp to Month-Day-Year?In Web/GUI applications, you do not need to display a complete Timestamp object (it is too long andmay be unreadable). Some applications may want to display a Timestamp object as a Month-Day-Year string. Here is a class with three methods to accomplish this task:

import java.sql.Timestamp;import java.text.SimpleDateFormat;

/*** DateUtil provides some basic methods* for formatting Timestamp objects.*/public class DateUtil {

/*** SimpleDateFormat object to format Timestamp into Month-Day-Year String.*/private static final SimpleDateFormat monthDayYearformatter =

new SimpleDateFormat("MMMMM dd, yyyy");

/*** SimpleDateFormat object to format Timestamp into "Month-Day" String.*/private static final SimpleDateFormat monthDayformatter =

new SimpleDateFormat("MMMMM dd");

/*** Return Timestamp object as MMMMM DD, YYYY.* @param timestamp a Timestamp object* @return Timestamp object as MMMMM DD, YYYY.*/public static String timestampToMonthDayYear(Timestamp timestamp){

if (timestamp == null) {return null;

}else {

return monthDayYearformatter.format((java.util.Date) timestamp);}

}

/*** Return Timestamp object as MMMMM DD.* @param timestamp a Timestamp object* @return Timestamp object as MMMMM DD.*/

5203ch09.qxd 8/11/05 4:28 PM Page 331

Page 361: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC332

public static String timestampToMonthDay(Timestamp timestamp){if (timestamp == null) {

return null;}else {

return monthDayformatter.format((java.util.Date) timestamp);}

}

/*** Get the current timestamp.* @return the current timestamp.*/public static java.sql.Timestamp getTimestamp() {

java.util.Date today = new java.util.Date();return new java.sql.Timestamp(today.getTime());

}}

9-29. How Do You Determine the Validity of a Format Pattern forSimpleDateFormat?A format pattern such as MMMMM DD, YYYY is a valid one, but MMMMM DD, YYYYZZ is not valid.The following method accepts a format pattern and then returns true if it is a valid format pattern;otherwise, it returns false:

/*** Tests if the date format pattern specified is valid.** @param format The format string to test.** @return true if the format parameter contains* a valid formatting string; false otherwise.*/public static boolean isValidDateFormat(String format) {

if ((format == null) || ((format.length() == 0)) {// not a valid format patternreturn false;

}

java.text.SimpleDateFormat formatter = null;try {

formatter = new java.text.SimpleDateFormat( format );formatter.format( new java.util.Date() );return true;

}catch(Exception e) {

// not a valid format patternreturn false;

}}

5203ch09.qxd 8/11/05 4:28 PM Page 332

Page 362: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 333

9-30. How Do You Get a Date Label from a java.sql.TimestampObject?In GUI database applications, you may need to convert a Timestamp object into a date label suchas Today, Yesterday, This Month, and Older Than a Month. By getting a date label, the user cancategorize data.

The following method gets a date label for a given Timestamp object:

import java.sql.Timestamp;

/*** DateLabel provides some basic methods* for formatting Date and Timestamp objects.*/public class DateLabel {

private static final long One_Day_In_Milliseconds = 86400000;

/*** This date label represents "Today".*/public static final String DATE_LABEL_TODAY = "Today";

/*** This date label represents "Yesterday".*/public static final String DATE_LABEL_YESTERDAY = "Yesterday";

/*** This date label represents "This Month".*/public static final String DATE_LABEL_THIS_MONTH = "This Month";

/*** This date label represents "Older" (older than a month).*/public static final String DATE_LABEL_OLDER = "Older";

/*** This date label represents "none" (when* timestamp is null/undefined).*/public static final String DATE_LABEL_NONE = "";

/*** Get the current timestamp.* @return the current timestamp.*/public static java.sql.Timestamp getTimestamp() {

java.util.Date today = new java.util.Date();return new java.sql.Timestamp(today.getTime());

}

/*** Get the Date Label.* @param ts the timestamp you want to get a data label

5203ch09.qxd 8/11/05 4:28 PM Page 333

Page 363: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC334

* @param now the timestamp you want to compare to* @return the date label for a given timestamp.*/public static String getDateLabel(java.sql.Timestamp ts,

java.sql.Timestamp now) {if (ts == null) {

return DATE_LABEL_NONE;}

if (now == null) {return DATE_LABEL_NONE;

}

long tsTime = ts.getTime();long nowTime = now.getTime();long quotient = (nowTime - tsTime)/One_Day_In_Milliseconds;

if (quotient < 1) {return DATE_LABEL_TODAY;

}else if (quotient < 2) {

return DATE_LABEL_YESTERDAY;}else if (quotient < 30) {

return DATE_LABEL_THIS_MONTH;}else {

return DATE_LABEL_OLDER;}

}

public static void main(String[] args) {java.sql.Timestamp now = getTimestamp();

java.sql.Timestamp ts1 = getTimestamp();System.out.println(getDateLabel(ts1, now));System.out.println(ts1.toString());System.out.println("-------------");

// timestamp in format yyyy-mm-dd hh:mm:ss.fffffffffjava.sql.Timestamp ts22 =

java.sql.Timestamp.valueOf("2005-04-06 09:01:10");System.out.println(getDateLabel(ts22, now));System.out.println(ts22.toString());System.out.println("-------------");

java.sql.Timestamp ts2 =java.sql.Timestamp.valueOf("2005-03-26 10:10:10");

System.out.println(getDateLabel(ts2, now));System.out.println(ts2.toString());System.out.println("-------------");

java.sql.Timestamp ts3 =java.sql.Timestamp.valueOf("2004-07-18 10:10:10");

System.out.println(getDateLabel(ts3, now));System.out.println(ts3.toString());System.out.println("-------------");

5203ch09.qxd 8/11/05 4:28 PM Page 334

Page 364: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 335

java.sql.Timestamp ts4 =java.sql.Timestamp.valueOf("2004-06-20 10:10:10");

System.out.println(getDateLabel(ts4, now));System.out.println(ts4.toString());System.out.println("-------------");

}}

To run the test program, use this:

$ javac DateLabel.java$ java DateLabelToday2005-04-07 09:09:47.605-------------Yesterday2005-04-06 09:01:10.0-------------This Month2005-03-26 10:10:10.0-------------Older2004-07-18 10:10:10.0-------------Older2004-06-20 10:10:10.0-------------

9-31. How Do You Convert a java.sql.Timestamp Object toa java.util.Date Object?java.sql.Timestamp is a wrapper around java.util.Date that allows the JDBC API to identify it asa SQL TIMESTAMP value. This adds the ability to hold the SQL TIMESTAMP nanos value and providesformatting and parsing operations to support the JDBC escape syntax for timestamp values. Thejava.sql.Timestamp object stores the fractional part of the time within itself instead of within theDate superclass.

You can use the following to convert a java.sql.Timestamp object to a java.util.Date object:

public static java.util.Date toDate(java.sql.Timestamp timestamp) {if (timestamp == null) {

return null;}

long milliseconds = timestamp.getTime() + (timestamp.getNanos() / 1000000 );return new java.util.Date(milliseconds);

}

9-32. What Does Normalization Mean for java.sql.Date andjava.sql.Time?To understand normalization, you need to look at the java.sql.Date and java.sql.Time classes. Theclass java.sql.Date descends from the java.util.Date class but uses only the year, month, and dayvalues. JDK defines java.sql.Date and java.sql.Time as follows:

public class java.sql.Date extends java.util.Date

5203ch09.qxd 8/11/05 4:28 PM Page 335

Page 365: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC336

The JDK also says this:

A thin wrapper around a millisecond value that allows JDBC to identify this as an SQL DATEvalue. A milliseconds value represents the number of milliseconds that have passed since Jan-uary 1, 1970 00:00:00.000 GMT. To conform with the definition of SQL DATE, the millisecondvalues wrapped by a java.sql.Date instance must be “normalized” by setting the hours, min-utes, seconds, and milliseconds to zero in the particular time zone with which the instance isassociated.

public class java.sql.Time extends java.util.Date

A thin wrapper around the java.util.Date class that allows the JDBC API to identify this as anSQL TIME value. The Time class adds formatting and parsing operations to support the JDBCescape syntax for time values. The date components should be set to the “zero epoch” value ofJanuary 1, 1970, and should not be accessed.

These classes (java.sql.Date and java.sql.Time) are thin wrappers that extend the java.util.Dateclass, which has both date and time components. java.sql.Date should carry only date information,and a normalized instance has the time information set to zeros. java.sql.Time should carry onlytime information; a normalized instance has the date set to the Java zero epoch ( January 1, 1970)and the milliseconds portion set to zero.

The following sections give a complete example for determining the normalization of java.sql.Time and java.sql.Date objects.

9-33. Does MySQL/Oracle JDBC Driver Normalize java.sql.Dateand java.sql.Time Objects?Normalization depends on the JDBC driver’s implementation of the ResultSet.getDate() andResultSet.getTime() methods. To determine normalization, you must convert the java.sql.Dateand java.sql.Time objects to an associated java.util.Date object.

How It WorksFor example, if a java.sql.Date object displays 2005-07-01, it’s normalized only if its associatedjava.util.Date value is as follows:

Fri Jul 01 00:00:00 EDT 2005

Further, if a java.sql.Time object displays 16:10:12, it’s normalized only if its associatedjava.util.Date value is as follows:

Thu Jan 01 16:10:12 EST 1970

The following solution (the TestNormalization class) checks whether the MySQL/Oracle JDBCdriver supports normalization for the java.sql.Date and java.sql.Time objects. Before reviewingthis solution, you will see how to set up some database objects.

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> create table date_time_table (-> time_col time,-> date_col date,

5203ch09.qxd 8/11/05 4:28 PM Page 336

Page 366: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 337

-> date_time_col datetime-> );

Query OK, 0 rows affected (0.07 sec)

mysql> desc date_time_table;+---------------+----------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+---------------+----------+------+-----+---------+-------+| time_col | time | YES | | NULL | || date_col | date | YES | | NULL | || date_time_col | datetime | YES | | NULL | |+---------------+----------+------+-----+---------+-------+3 rows in set (0.00 sec)

mysql> insert into date_time_table(time_col, date_col, date_time_col)values ('10:34:55', '2004-10-23', '2004-10-23 10:34:55');Query OK, 1 row affected (0.00 sec)

mysql> insert into date_time_table(time_col, date_col, date_time_col)values ('16:12:50', '2005-07-01', '2005-07-01 16:12:50');Query OK, 1 row affected (0.00 sec)

mysql> select * from date_time_table;+----------+------------+---------------------+| time_col | date_col | date_time_col |+----------+------------+---------------------+| 10:34:55 | 2004-10-23 | 2004-10-23 10:34:55 || 16:12:50 | 2005-07-01 | 2005-07-01 16:12:50 |+----------+------------+---------------------+2 rows in set (0.00 sec)

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> create table date_time_table (2 time_col date,3 date_col date,4 date_time_col date5 );

Table created.

SQL> desc date_time_table;Name Null? Type--------------------- -------- -------TIME_COL DATEDATE_COL DATEDATE_TIME_COL DATE

SQL> insert into date_time_table(time_col, date_col, date_time_col)2 values (sysdate, sysdate, sysdate);

1 row created.

SQL> select * from date_time_table;

5203ch09.qxd 8/11/05 4:28 PM Page 337

Page 367: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC338

TIME_COL DATE_COL DATE_TIME--------- --------- ---------01-JUL-05 01-JUL-05 01-JUL-05

SQL> commit;Commit complete.

SolutionThis shows the solution:

import java.sql.*;import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class TestNormalization {

public static void main(String args[]) {String GET_RECORDS ="select time_col, date_col, date_time_col from date_time_table";

ResultSet rs = null;Connection conn = null;Statement stmt = null;try {

String dbVendor = args[0]; // {"mysql", "oracle" }conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);stmt = conn.createStatement();rs = stmt.executeQuery(GET_RECORDS);while (rs.next()) {

java.sql.Time dbSqlTime = rs.getTime(1);java.sql.Date dbSqlDate = rs.getDate(2);java.sql.Timestamp dbSqlTimestamp = rs.getTimestamp(3);System.out.println("dbSqlTime="+dbSqlTime);System.out.println("dbSqlDate="+dbSqlDate);System.out.println("dbSqlTimestamp="+dbSqlTimestamp);System.out.println("-- check for Normalization --");java.util.Date dbSqlTimeConverted =

new java.util.Date(dbSqlTime.getTime());java.util.Date dbSqlDateConverted =

new java.util.Date(dbSqlDate.getTime());System.out.println("dbSqlTimeConverted="+dbSqlTimeConverted);System.out.println("dbSqlDateConverted="+dbSqlDateConverted);

}}catch( Exception e ) {

e.printStackTrace();System.out.println("Failed to get the records.");System.exit(1);

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

5203ch09.qxd 8/11/05 4:28 PM Page 338

Page 368: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC 339

Running the Solution for the MySQL DatabaseAs you can see from the following results, the MySQL driver does support normalization for thejava.sql.Date and java.sql.Time objects:

$ javac TestNormalization.java$ java TestNormalization mysqlok: loaded mysql driver.conn=com.mysql.jdbc.Connection@1e4cbc4dbSqlTime=10:34:55dbSqlDate=2004-10-23dbSqlTimestamp=2004-10-23 10:34:55.0-- check for Normalization --dbSqlTimeConverted=Thu Jan 01 10:34:55 PST 1970dbSqlDateConverted=Sat Oct 23 00:00:00 PDT 2004dbSqlTime=16:12:50dbSqlDate=2005-07-01dbSqlTimestamp=2005-07-01 16:12:50.0-- check for Normalization --dbSqlTimeConverted=Thu Jan 01 16:12:50 PST 1970dbSqlDateConverted=Fri Jul 01 00:00:00 PDT 2005

Running Solution for the Oracle DatabaseAs you can see from the following results, the Oracle 10g driver does support normalization for thejava.sql.Date and java.sql.Time objects:

$ java TestNormalization oracleok: loaded oracle driver.conn=oracle.jdbc.driver.OracleConnection@6e70c7dbSqlTime=16:15:22dbSqlDate=2005-07-01dbSqlTimestamp=2005-07-01 16:15:22.0-- check for Normalization --dbSqlTimeConverted=Thu Jan 01 16:15:22 PST 1970dbSqlDateConverted=Fri Jul 01 00:00:00 PDT 2005

9-34. How Do You Make a java.sql.Timestamp Object for a GivenYear, Month, Day, Hour, and So On?Given the year, month, day, hour, minutes, seconds, and milliseconds, the objective of the followingcode is to create a java.sql.Timestamp object:

import java.sql.Timestamp;import java.util.Calendar;import java.util.GregorianCalendar;.../*** Given year, month, day, hour, minutes, seconds, and* milliseconds, the objective is to create a Timestamp object.* @param year the year* @param month the month* @param day the day* @param hour the hour

5203ch09.qxd 8/11/05 4:28 PM Page 339

Page 369: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 9 ■ WORKING WITH DATE, T IME, AND TIMESTAMP IN JDBC340

* @param minute the minute* @param second the second* @param millisecond the millisecond* @return a java.sql.Timestamp object*/public static Timestamp makeTimestamp(int year,

int month,int day,int hour,int minute,int second,int millisecond) {

Calendar cal = new GregorianCalendar();cal.set(Calendar.YEAR, year);cal.set(Calendar.MONTH, month - 1);cal.set(Calendar.DATE, day);cal.set(Calendar.HOUR_OF_DAY, hour);cal.set(Calendar.MINUTE, minute);cal.set(Calendar.SECOND, second);cal.set(Calendar.MILLISECOND, millisecond);

// now convert GregorianCalendar object to Timestamp objectreturn new Timestamp(cal.getTimeInMillis());

}

9-35. How Do You Get a Date for a Specific Time Zone?The following Java code fragment illustrates how to use a Calendar object to retrieve a date forLos Angeles, California:

import java.sql.Date;import java.sql.ResultSet;import java.util.Calendar;import java.util.TimeZone;...ResultSet rs = stmt.executeQuery(

"SELECT date_created FROM products WHERE product_id = 'PRD-123456'");

//creating an instance of CalendarCalendar cal = Calendar.getInstance();

// get the TimeZone for "America/Los_Angeles"TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");cal.setTimeZone(tz);if (rs.next()) {

// the JDBC driver will use the time zone information in// Calendar to calculate the date, with the result that// the variable dateCreated contains a java.sql.Date object// that is accurate for "America/Los_Angeles".Date dateCreated = rs.getDate(1, cal);

}

5203ch09.qxd 8/11/05 4:28 PM Page 340

Page 370: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Handling Exceptions in JDBC

Example isn’t another way to teach, it is the only way to teach.

—Albert Einstein

The purpose of this chapter is to provide solutions (expressed as snippets and reusable code samples)that deal with the java.sql.SQLException class. You will also examine other JDBC-related exceptionclasses such as SQLWarning.

SQLException, which extends the java.lang.Exception class, is a core JDBC exception class thatprovides information about database access errors and other errors. Most of the JDBC API methodsthrow SQLException, so client programs must handle it properly. For example, using the DriverManager.getConnection() method, if the database URL is invalid, then that method will throw an exceptionof type SQLException.

This chapter covers the following exception classes used in the JDBC API:

java.sql.SQLException: This class extends the java.lang.Exception class. This class is anexception that provides information about database access errors and other errors. Also, theSQLException class provides information in terms of nested/chained exceptions. Using thisclass, you can find vendor error codes and messages.

java.sql.BatchUpdateException: This exception is thrown when an error occurs duringa batch update operation. In addition to the information provided by SQLException, a BatchUpdateException provides the update counts for all commands that were executed suc-cessfully during the batch update (that is, all commands that were executed before the erroroccurred). The order of elements in an array of update counts corresponds to the order inwhich commands were added to the batch.

java.sql.DataTruncation: This exception reports a DataTruncation warning (on reads) or throwsa DataTruncation exception (on writes) when JDBC unexpectedly truncates a data value.

java.sql.SQLWarning: This exception provides information about database access warnings.A warning is silently chained to the object whose method caused the warning to be reported.

In most of the examples in this chapter, I will use the getConnection(String dbVendor) methodto get a JDBC Connection object; you are strongly encouraged to replace it with your desired connectionmethod (such as using a connection pool manager).

341

C H A P T E R 1 0

■ ■ ■

5203ch10.qxd 8/11/05 4:31 PM Page 341

Page 371: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC342

10-1. What Is an Exception?In Java/JDBC programming, a programming exception occurs when an error is discovered while run-ning the program. The most commonly known exceptions are divide-by-zero errors and null pointerexceptions (NullPointerException).

According to the second edition of the Java Language Specification (JLS), available at http://java.sun.com/docs/books/jls/, “when a program violates the semantic constraints of the Java program-ming language, the Java virtual machine signals this error to the program as an exception. An exampleof such a violation is an attempt to index outside the bounds of an array.” Furthermore, the JLS addsthat “an exception is said to be thrown from the point where it occurred and is said to be caught atthe point to which control is transferred. Programs can also throw exceptions explicitly, using throwstatements.”

The JLS states that “every exception is represented by an instance of the class Throwable or one ofits subclasses; such an object can be used to carry information from the point at which an exceptionoccurs to the handler that catches it. Handlers are established by catch clauses of try statements.”

So, to reiterate, an exception occurs when a program is running and something goes wrong.Examples are as follows:

• The program tried to divide by zero.

• The program tried to invoke a method with a nonexistent object.

• The program tried to execute a SQL query, but the database is not available.

• The program tried to insert a new record into a table, but the table does not exist.

• The program tried to open a data file, but the file doesn’t exist.

• The program tried to load a JDBC driver, but the associated class does not exist.

10-2. What Is java.lang.Exception?The java.lang.Exception class and its subclasses are a form of java.lang.Throwable that indicatesconditions that a reasonable application might want to catch.

Consider the following simple Java class Test. (The filename is Test.java; I have added linenumbers for discussion purposes.)

1 public class Test {2 public static void main(String[] args) {3 int x = 1;4 int y = 0;5 System.out.println("before division");6 int z = x/y; // a division by 07 System.out.println("after division");8 }9 }

Let’s compile and run the program:

$ javac Test.java$ java Testbefore divisionException in thread "main" java.lang.ArithmeticException:/ by zero at Test.main(Test.java:6)

The execution of Test stops at line 6; this is caused by the division-by-zero error at x/y; anexception (java.lang.ArithmeticException) has been thrown but has not been handled properly.The exception is thrown to the caller (in this case the operating system is the client/caller) of the

5203ch10.qxd 8/11/05 4:31 PM Page 342

Page 372: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 343

main() method. When an exception is thrown but not handled, then the program terminates. As youcan see, because of the exception, the program terminated abnormally.

So, how do you handle an exception? To handle an exception, enclose the code that is likely tothrow an exception in a try block, and follow it immediately by a catch clause, as follows:

public class Test {public static void main(String[] args) {

int x = 1;int y = 0;

System.out.println("try block starts");try {

System.out.println("before division");int z = x/y; // division by 0System.out.println("after division");

}catch (ArithmeticException ae) {

// catch clause below handles the ArithmeticException// generated by the division by zero. This exception must be// listed before Exception (because it is a subclass of Exception).System.out.println("--- attempt to divide by 0: handle exception");ae.printStackTrace(); // print the details of exception// do whatever here to handle the exceptionSystem.out.println("--- end to handle the exception");

}catch(Exception e) {

// handle the other exceptionse.printStackTrace();System.out.println(e.getMessage());

}System.out.println("catch block ends");

}}

The output of the previous program is as follows:

try block startsbefore division--- attempt to divide by 0: handle exceptionjava.lang.ArithmeticException: / by zero at Test.main(Test.java:9)--- end to handle the exceptioncatch block ends

As you can see (from the output of the modified program), the statement System.out.println("after division") doesn’t execute; once an exception is thrown, the program controlmoves out of the try block and into the catch block (also called the exception handling block). Pleasenote that you don’t need to handle all exceptions. You can ignore system errors and can passresponsibility for handling exceptions upward (this must be explicit).

public static void main (String[] args)throws java.lang.ArithmeticException {...

}

When catching exceptions, the try statement defines the scope of its associated exception han-dlers; you associate exception handlers with a try statement by providing one or more catch blocksdirectly after the try block.

5203ch10.qxd 8/11/05 4:31 PM Page 343

Page 373: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC344

try {<code might throw an exception>

}catch (SomeException1 e1) {

<code to handle exception e1>}catch (SomeException2 e2) {

<code to handle exception e2>}...finally {

<code that is always executed>}

The code within the try block is executed. If an exception is thrown, then execute the codewithin the matching catch; if no match is found, pass the exception upward and always execute thecode on the finally clause (regardless of whether an exception has taken place).

In the preceding example, if SomeException1 is a subclass of SomeException2, then the order ofSomeException1 and SomeException2 in the catch block is important. (The order I have listed them iscorrect—if you list SomeException2 first, then SomeException1 exceptions will never be caught, becauseall objects of SomeException1 are castable to SomeException2. If the try block throws SomeException1or SomeException2, then the handler will go to <code to handle exception e1>.) If SomeException1is not a subclass of SomeException2, then the order of listing exceptions in the catch block is notimportant. You should note that the order of exception handlers matters! You should list the morespecific exceptions first. For example, if you list java.lang.Exception first, then all other exceptionswill be ignored (because most exceptions can be cast to java.lang.Exception). Therefore, the orderof catch blocks is important.

10-3. What Are the Components of an Exception?An exception object carries three important pieces of information:

• The type of exception: The exception class. If you design your exception classes as fine-grainedclasses, then the exception classes will give you enough information to react to the type ofexception; for example, the java.lang.ArrayIndexOutOfBoundsException exception is a well-defined exception, and it indicates that a client program has accessed an index that doesnot exist.

• Where the exception occurred: The stack trace. The JVM generates a stack trace foryou. (A Java stack trace is a snapshot of the threads and monitors in a JVM.)

• Context and explanatory information: The error message and other state information.

Each item listed previously is relevant to a different party. Software entities care about the excep-tion class; the JVM and the code that calls the throwing method use it to determine how to handle theexception. The other two items are relevant to engineers; an engineer analyzes the stack trace to debugthe problem, and a developer examines the error message. Each party must receive the information itneeds to effectively deal with the error.

10-4. What Is the Definition of SQLException?SQLException is an exception that provides information about database access errors or other errors.

SQLException is an exception class and is used in most of the JDBC API. When JDBC cannothandle the situation or there is an error in a request or response, then an instance of SQLException is

5203ch10.qxd 8/11/05 4:31 PM Page 344

Page 374: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 345

thrown. The SQLException class extends the java.lang.Exception class, and it adds a few extra methods.Since JDK 1.4, SQLExceptions can be chained (by using the setNextException() and getNextException()methods), so a getNextException() has been added that will return another SQLException, if there areanymore. It also has the ability to give you the error message, SQLState, and vendor-specific error codes.

According to the JDK, the SQLException class is defined in the java.sql package as follows (fordetails, you can look at the SQLException.java source code provided by the JDK src.zip file):

package java.sql;

public class SQLExceptionextends java.lang.Exception

According to the JDK, each SQLException provides several kinds of information:

• A string describing the error. This is used as the Java Exception message, available via themethod getMessage().

• A SQLState string, which follows either the XOPEN SQLState conventions or the SQL-99 con-ventions. The values of the SQLState string are described in the appropriate specification.You can use the DatabaseMetaData method getSQLStateType to discover whether the driverreturns the XOPEN type or the SQL-99 type.

• An integer error code that is specific to each vendor. Normally this will be the actual error codereturned by the underlying database.

• A chain to the next exception. You can use this to provide additional error information.

The SQLException class has four methods and four constructors, as shown in Table 10-1 andTable 10-2.

Table 10-1. java.sql.SQLException Constructors

Constructor Description

SQLException() Constructs a SQLException object. The reason field defaults tonull, the SQLState field defaults to null, and the vendorCodefield defaults to 0.

SQLException(String reason, Constructs a SQLException object with the given reason fieldString SQLState) and SQLState; the vendorCode field defaults to 0.

SQLException(String reason, Constructs a fully specified SQLException object.String SQLState, int vendorCode)

Table 10-2. java.sql.SQLException Methods

Return Type Method Description

int getErrorCode() Retrieves the vendor-specific exception code for thisSQLException object

SQLException getNextException() Retrieves the exception chained to this SQLExceptionobject

String getSQLState() Retrieves the SQLState for this SQLException object

void setNextException Adds a SQLException object to the end of the chain(SQLException ex)

5203ch10.qxd 8/11/05 4:31 PM Page 345

Page 375: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC346

10-5. Is SQLException a “Checked” Exception?In Java, two kinds of exceptions exist: checked and unchecked. The semantics of these exceptionscome from Java compiling. In general, a Java compiler looks for code that does not properly handlechecked (explicitly defined) exceptions; any such code causes a compiler error. Any exception forwhich a lack of proper handling will cause a Java compiler error is called a checked exception. Anyother kind is an unchecked exception.

For example, the following Java code will not compile because java.sql.SQLException is a checkedexception (not a runtime exception—all SQLExceptions(s) must be catched explicitly):

import java.sql.Connection;import java.io.DatabaseMetaData;import java.sql.SQLException;...public static DatabaseMetaData getDbMetaData (Connection conn) {

if (conn == null) {return null;

}// the Connection.getMetaData() can throw SQLExceptionDatabaseMetaData dbmd = conn getMetaData();return dbmd;

}

On the other hand, the following Java code will compile:

import java.sql.Connection;import java.io.DatabaseMetaData;import java.sql.SQLException;...public static DatabaseMetaData getDbMetaData (Connection conn)

throws SQLException {if (conn == null) {

return null;}// the Connection.getMetaData() can throw SQLExceptionDatabaseMetaData dbmd = conn getMetaData();return dbmd;

}

Some of Java exceptions are unchecked (runtime exceptions), which means the compiler willnot complain if your code does not properly handle them. In general, unchecked exceptions indicateconditions that often can’t be known during the compilation process.

For example, the following is a valid Java method (it compiles with no problem), even though itwill throw a java.lang.ArithmeticException (at runtime) when y=0:

public static int divide(int x, int y) {return x / y;

}

Therefore, in Java, unchecked exceptions are the ones that inherit from java.lang.RuntimeExceptionand java.lang.Error. By following all these Java definitions, java.sql.SQLException is a checkedexception, and you must catch it explicitly.

10-6. What Is the Relationship of SQLException to Other Classes?The Java language supports inheritance among classes and interfaces. The exception classes (those classesinvolved with exceptions) hold information about the nature of the exception; the three most commonexception classes are as follows:

5203ch10.qxd 8/11/05 4:31 PM Page 346

Page 376: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 347

java.lang.Throwable: This is the parent class of all exception-related classes. (The Throwableclass is the superclass of all errors and exceptions in the Java language.) Only objects that areinstances of this class (or one of its subclasses) are thrown by the JVM or can be thrown by theJava throw statement. Similarly, only this class or one of its subclasses can be the argument typein a catch clause.

java.lang.Error: This is the other subclass of Throwable, but this is reserved for catastrophicerrors that can’t be normally be handled in a user’s program. (An Error is a subclass of Throwablethat indicates serious problems that a reasonable application should not try to catch. Most sucherrors are abnormal conditions. The ThreadDeath error, though a “normal” condition, is alsoa subclass of Error because most applications should not try to catch it.)

java.lang.Exception: This class is for errors that can be generally handled (most Java applicationswill subclass the Exception class). The class Exception and its subclasses are a form of Throwablethat indicates conditions that a reasonable application might want to catch.

Figure 10-1 shows the relationship of the java.sql.SQLException class to other classes.

Figure 10-1. Relationship of SQLException to other Java classes

5203ch10.qxd 8/11/05 4:31 PM Page 347

Page 377: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC348

10-7. What Is an Example of Using SQLException?In the following sections, I will provide a simple example that shows how to use the SQLException class.

How It WorksIn most of the JDBC API, the SQLException class must be caught. For example, if you want to retrieverecords from a nonexistent table, then a SQLException will be thrown.

For example, suppose you want to create a new table (called MY_DVDS) in the MySQL database.I will show how to run the solution twice; in the second example, I will provide the “wrong” SQL syntaxto force the SQLException.

SolutionHere’s the solution:

import java.sql.*;import jcb.db.VeryBasicConnectionManager;import jcb.util.DatabaseUtil;

public class CreateTable {

public static void main(String args[]) {Statement stmt = null;Connection conn = null;String dbVendor = args[0];String createTable = args[1];try {

conn = VeryBasicConnectionManager.getConnection(dbVendor); // 1stmt = conn.createStatement(); // 2stmt.executeUpdate(createTable); // 3// creation of table was successful.

}catch(SQLException e) {

// creation of table failed: handle the exception// either one of the above statements has been failedSystem.err.println("SQLException: " + e.getMessage());

}catch(Exception e) {

// other errors: handle the exceptionSystem.err.println("SQLException: " + e.getMessage());

}finally {

DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Running the Solution for the MySQL DatabaseHere’s how to run the solution for the MySQL database:

$ javac CreateTable.java$ java CreateTable mysql "create table MY_DVDS (id INTEGER, title VARCHAR(32))"$ java CreateTable mysql "create tableZZ MY_DVDS2 (id INTEGER, title VARCHAR(32))"

5203ch10.qxd 8/11/05 4:31 PM Page 348

Page 378: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 349

SQLException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tableZZ MY_DVDS2 (id INTEGER, title VARCHAR(32))' at line 1

Viewing the MySQL Database After Running the SolutionHere’s the MySQL database after running the solution:

mysql> desc my_dvds;+-------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+-------+| id | int(11) | YES | | NULL | || title | varchar(32) | YES | | NULL | |+-------+-------------+------+-----+---------+-------+2 rows in set (0.00 sec)

Running the Solution for the Oracle DatabaseHere’s how to run the solution for the Oracle database:

$ javac CreateTable.java$ java CreateTable oracle "create table MY_DVDS (id INTEGER, title VARCHAR(32))"$ java CreateTable oracle "create tableZZ MY_DVDS2 (id INTEGER, title VARCHAR(32))"SQLException: ORA-00901: invalid CREATE command

Viewing the Oracle Database After Running the SolutionHere’s the Oracle database after running the solution:

SQL> desc my_dvds;Name Null? Type------------------------------- -------- -------------ID NUMBER(38)TITLE VARCHAR2(32)

10-8. How Do You Get the Details of a SQLException?The SQLException object is comprised of several components. When debugging database-related pro-grams, sometimes you may want to get the details of a SQLException. The following code segmentdemonstrates how to retrieve the information in a SQLException:

java.sql.Connection conn = null;try {

// get the Connection objectconn = getConnection();

// Execute SQL statements using Connection object ...JDBC API...}catch (SQLException e) {

while (e != null) {// Retrieve a readable message identifying// the reason for the exceptionString errorMessage = e.getMessage();System.err.println("sql error message:"+errorMessage);

5203ch10.qxd 8/11/05 4:31 PM Page 349

Page 379: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC350

// This vendor-independent string contains a code// that identifies the reason for the exception.// The code follows the Open Group SQL conventions.String sqlState = e.getSQLState();System.err.println("sql state:"+sqlState);

// Retrieve a vendor-specific error code// identifying the reason for the exception.int errorCode = e.getErrorCode();System.err.println("error code:"+errorCode);

// Get driver name: if it is necessary to execute code// based on this error code, you should ensure that the// expected driver is being used before using the error code.String driverName = conn.getMetaData().getDriverName();System.err.println("driver name:"+driverName);processDetailError(drivername, errorCode);

// the exception may have been chained;// process the next chained exceptione = e.getNextException();

}finally {

// close the Connection object}

}

processDetailError():private static void processDetailError(String driverName,

int errorCode) {if ((driverName.equals("MySql Driver")) &&

(errorCode == 121)) {// process MySQL error...

}else if ((driverName.equals("Oracle JDBC Driver")) &&

(errorCode == 123)) {// process Oracle error...

}...

}

10-9. What Is SQLException Chaining?Each SQLException provides chain information, and you can use chains to provide additional errorinformation.

A JDBC problem can stem from various problems (such as a driver is not loadable, a database is notavailable, a table does not exist, and so on). The SQLException class has a method, getNextException(),that returns either the next exception or null when all exceptions have been retrieved. Obtainingmultiple exceptions this way is termed chaining.

The SQLException class has two methods that provide further information: a method to get (orchain) additional exceptions and a method to set an additional exception.

SQLException.getSQLState() returns a SQLState identifier based on the XOPEN SQL specifica-tion. A SQLState string follows either the XOPEN SQLState conventions or the SQL-99 conventions.The values of the SQLState string are described in the appropriate specifications. You can use the

5203ch10.qxd 8/11/05 4:31 PM Page 350

Page 380: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 351

DatabaseMetaData.getSQLStateType() method to discover whether the driver returns the XOPENtype or the SQL-99 type. Your database-specific reference manuals should list some of these.

SQLException.getErrorCode() retrieves the vendor-specific error code. (It retrieves the vendor-specific exception code for this SQLException object.)

SQLException.getNextException() retrieves the next SQLException or null if there are no moreexceptions. Many things can go wrong between your client program and the database. This methodallows you to track all problems that occur. (Also, the setNextException()method allows the programmerto add a SQLException to the chain.)

Typical catch code would look similar to the following:

try {// some database/JDBC work

}catch (SQLException se) {

//// se is the first exception, you may handle this and get// the other chained exceptions;//// in most of the situations, this exception is sufficient and// there might not be any need for chained exceptions (this will// depend on the requirements of the project)//while (se != null) {

// get the next exception from the chain and handle the exceptionSystem.out.println("SQL Exception:" + se.getMessage());System.out.println("SQL State: " + se.getSQLState());System.out.println("Vendor specific Error Code: " + se.getErrorCode());

se = se.getNextException(); // this is chaining//// handle the se (if necessary)//

}}

10-10. How Do You Get All SQLException Instances?If you are debugging your database application and you want to be aware of every little thing thatgoes wrong within the database, you might use a printSQLExceptions() method such as the follow-ing one (please note that this method is for debugging purposes only; in a production environment,you would not call such a method):

void printSQLExceptions(SQLException e) {while (e != null) {System.out.println("SQLException: " + e.getMessage());System.out.println("SQL State: " + e.getSQLState());System.out.println("Vendor specific Error Code: " + e.getErrorCode());e = e.getNextException();

}}

You can then use the printSQLExceptions() method as follows:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

5203ch10.qxd 8/11/05 4:31 PM Page 351

Page 381: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC352

conn = getConnection();stmt = conn.createStatement();rs = stmt.executeQuery("SELECT id, name FROM EMPLOYEES");...

}catch(SQLException e) {

printSQLExceptions(e);// handle the exception

}finally {

// clean up and close the database/JDBC resources}

10-11. What Is a SQLWarning?The SQLWarning class is an exception that provides information about database access warnings.

SQLWarning is a class that extends the SQLException class but is not thrown like other exceptions.Warnings are silently chained to the object whose method caused the warning to be reported. To getthe SQLWarning exception, the programmer must specifically ask for warnings. SQLWarning indicatesthat something not exactly right occurred but its effect was not severe enough to end processing.

In general, if SQLException is not caught, it will halt the program, but SQLWarning does notwarrant halting the entire program. Warnings may be retrieved from the java.sql.Connection,java.sql.Statement, and java.sql.ResultSet objects. Trying to retrieve a warning on a connectionafter it has been closed will cause an exception to be thrown. Similarly, trying to retrieve a warning ona statement after it has been closed or on a result set after it has been closed will cause an exception tobe thrown. Note that closing a statement also closes the result set that it might have produced.

The SQLWarning class has two methods:

• SQLWarning getNextWarning(): Retrieves the warning chained to this SQLWarning object (returnsthe next SQLException in the chain and returns null if none)

• void setNextWarning(SQLWarning w): Adds a SQLWarning object (denoted by w) to the end ofthe chain

Table 10-3 lists the java.sql.SQLWarning constructors, and Table 10-4 lists the java.sql.SQLWarningmethods.

Table 10-3. java.sql.SQLWarning Constructors

Constructor Description

SQLWarning() Constructs a default SQLWarning object.

SQLWarning(String_reason) Constructs a SQLWarning object with the givenvalue for a reason. SQLState defaults to null, andvendorCode defaults to 0.

SQLWarning(String reason, String SQLState) Constructs a SQLWarning object with the givenreason field and SQLState. vendorCode defaults to 0.

SQLWarning(String reason, Constructs a fully specified SQLWarning objectString SQLState, int vendorCode) initialized with the given values.

5203ch10.qxd 8/11/05 4:31 PM Page 352

Page 382: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 353

Table 10-4. java.sql.SQLWarning Methods

Method Description

SQLWarning getNextWarning() Retrieves the warning chained to thisSQLWarning object

void setNextWarning(SQLWarning w) Adds a SQLWarning object to the end of the chain

10-12. How Do You Get All SQLWarning Instances?If you are debugging a database application and you want to be aware of every little thing that goeswrong within the database, you might use a printSQLWarnings() method such as the following one(please note that this method is for debugging purposes only; in a production environment, you wouldnot call such a method):

void printSQLWarnings(SQLWarning w) {while (w != null) {

System.out.println("SQLWarning: " + w.getMessage());System.out.println("SQL State: " + w.getSQLState());System.out.println("Vendor specific Error Code: " + w.getErrorCode());w = w.getNextWarning();

}}

You can then use the printSQLWarnings() method as follows:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

stmt = conn.createStatement();printSQLWarnings(stmt.getWarnings());

rs = stmt.executeQuery("SELECT id, name FROM EMPLOYEES");printSQLWarnings(rs.getWarnings());...

}catch(SQLException e) {

printSQLExceptions(e);// handle the exception

}finally {

// clean up and close the JDBC data structures}

10-13. How Do You Determine Whether a SQL Warning HasOccurred?This section shows how to determine whether a SQLWarning has occurred. Some database operations(such as getting a result set from a database query) can cause a warning that is not handled by a SQLexception (that is, SQLException). You must explicitly check for these warnings. An example of a warn-ing is a data truncation error during a read operation. For details, see the java.sql.DataTruncationclass; this is an exception that reports a DataTruncation warning (on reads) or throws a DataTruncationexception (on writes) when JDBC unexpectedly truncates a data value.

5203ch10.qxd 8/11/05 4:31 PM Page 353

Page 383: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC354

You can check for a warning in three places:

• The Connection object (java.sql.Connection)

• The Statement object (java.sql.Statement)

• The ResultSet object (java.sql.ResultSet)

For each case, I will provide an example on how to check for a warning.

Checking for a Warning Using a Connection ObjectThis shows how to check for a warning using a Connection object:

// Get warnings on Connection objectConnection conn = null;try {

conn = getConnection(); // get a java.sql.Connection objectSQLWarning warning = conn.getWarnings();while (warning != null) {

// process connection warningString message = warning.getMessage();String sqlState = warning.getSQLState();int errorCode = warning.getErrorCode();warning = warning.getNextWarning();

}}catch (SQLException e) {

// ignore the exception}finally {// close JDBC resources: ResultSet, Statement, Connection

}

Checking for a Warning Using a Statement ObjectThis shows how to check for a warning using a Statement object:

// Get warnings on Statement objectConnection conn = null;Statement stmt = null;try {

conn = getConnection(); // get a java.sql.Connection objectstmt = conn.createStatement(); // create a statement

// use the statement...

// get warnings on Statement objectwarning = stmt.getWarnings();if (warning != null) {

// Process statement warnings...}

}catch (SQLException e) {

// ignore the exception}finally {// close JDBC resources: ResultSet, Statement, Connection

}

5203ch10.qxd 8/11/05 4:31 PM Page 354

Page 384: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 355

Checking for a Warning Using a ResultSet ObjectThis shows how to check for a warning using a ResultSet object:

// Get warnings on ResultSet objectResultSet rs = null;Connection conn = null;Statement stmt = null;try {

conn = getConnection(); // get a java.sql.Connection objectstmt = conn.createStatement(); // create a statement// get a result setString sqlQuery = "select id, name from employees"rs = stmt.executeQuery(sqlQuery);while (rs.next()) {

// use result set// ...// get warnings on the current row of the ResultSet objectwarning = rs.getWarnings();if (warning != null) {

// process result set warnings...}

}}catch (SQLException e) {

// ignore the exception}

finally {// close JDBC resources: ResultSet, Statement, Connection

}

10-14. How Do You Create and Traverse a Custom SQLWarningStructure?JDBC allows you to create and traverse a custom SQLWarning.

How It WorksAssume that you are interested in getting the salary of an employee by a badge number (assume thateach employee has a unique badge number). Furthermore, assume that the company has a businesspolicy (a so-called business rule) that no employee can make more than $200,000. (For simplicity, youcan further assume that all salaries are integers.) Based on this, you want to pass a badge number andget the salary of the employee.

SolutionHere’s the solution:

import java.sql.*;

public class RasingCustomSqlWarning {

public static void main(String[] args) {if (args.length != 1) {System.out.printout("usage: RasingCustomSqlWarning ");System.exit(1);

}

5203ch10.qxd 8/11/05 4:31 PM Page 355

Page 385: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC356

// args[0] denotes the badge numberString badgeNumber = args[0];try {

// get the salaryint salary = getEmployeeSalary(badgeNumber);

// all is OK. we got the salarySystem.out.println("Got all data from database.");System.out.println("emp. badge number="+badgeNumber);System.out.println("emp. salary="+salary);

}catch(SQLException e) {

// print out root SQLExceptionSystem.err.println("An SQL exception occurred: " + e);e.printStackTrace();

// get all chained SQLExceptionswhile((e = e.getNextException()) != null) {

System.err.println("Contained reason: " + e);}

}}

private static int getEmployeeSalary(String badgeNumber)throws SQLException {// Status flag resulting from database data should be// created from normal business rules in a live situation.boolean somethingWrongHappened = false;int salary = getEmployeeSalaryFromDatabase(badgeNumber);if (salary > 200000) {

somethingWrongHappened = true;}

if(somethingWrongHappened) {// Create two custom SQL WarningsSQLWarning rootWarning =new SQLWarning("Employee Salary Business rules not properly regarded");

SQLWarning containedWarning =new SQLWarning("Salary over $200,000.");

// Chain the warningsrootWarning.setNextWarning(containedWarning);

// Notify the caller of the warningsthrow rootWarning;

}}

private static int getEmployeeSalaryFromDatabase(String badgeNumber)throws SQLException {// return the salary of employee with// the badge number = badgeNumber

}}

5203ch10.qxd 8/11/05 4:31 PM Page 356

Page 386: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 357

10-15. How Do You Wrap Database Exceptions?Sometimes you do not want to use JDBC exceptions directly. Instead, you will want to know how tocreate and use wrapper database exceptions.

How It WorksIn real-world database applications, programmers use several types of database exceptions to wrapany SQLException that occurs at runtime. In the database applications that use this framework, youwill find it most convenient to make those exceptions runtime exceptions so they bubble up to anexception handler. Some might argue that these exceptions should be declarative so they can be han-dled as close to the point of error as possible. However, that would make the SQL execution processalmost as cumbersome as the original SQLException, something you should try explicitly to avoid.

The following exception class wraps SQLException, which can provide custom error messages.Wrapping database exceptions enables programmers to catch exceptions at a high level (such as witha user-defined DatabaseException) rather than at a low level (such as with a JDBC-defined SQLException).

SolutionHere’s the solution:

public class DatabaseException extends java.lang.Object {

SQLException exception = null; // wrapped SQLExceptionint errorCode; // error codeString errorMessage = null; // error message

// constructors ...public DatabaseException(SQLException e) {

this.exception = exception;...

}

// constructors ...public DatabaseException(SQLException e, String extraMessage) {

this(exception);...

}

public String getErrorMessage() {return errorMessage;

}

public int getErrorCode() {return errorCode;

}

// and other supporting methods...}

Now you can use DatabaseException rather than use SQLException directly; here is an example:

public double getEmployeeSalary(int badgeNumber)throws DatabaseException {try {

...// get employee's salary for a given badge number

5203ch10.qxd 8/11/05 4:31 PM Page 357

Page 387: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC358

...}catch (SQLException e) {

throw new DatabaseException(e, "could not get the employee's salary");}

}

10-16. What Are the SQLState Codes Returned by SQLException.getSQLState()?What is a list of the possible SQLState codes returned by SQLException.getSQLState()? To answerthis question, you need to look at the details of the SQLException object. Each SQLException objectprovides several kinds of information:

• A string describing the error. This is used as the Java Exception message, available via themethod getMessage().

• A SQLState string of length 5, which follows either the XOPEN SQLState conventions or theSQL-99 conventions. The values of the SQLState string are described in the documents speci-fied by ANSI and XOPEN. You can use the DatabaseMetaData method getSQLStateType() todiscover whether the driver returns the XOPEN type or the SQL-99 type.

• An integer error code that is specific to each vendor. Normally this will be the actual errorcode returned by the underlying database.

• A chain to the next Exception. You can use this to provide additional error information.

For details on SQLState, refer to the SQL-92 specification (http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt), which defines the basic SQLState codes. But note that each data-base vendor implements its own SQLState codes. (Therefore, a vendor’s SQLState codes might differfrom the SQL-92 specification.)

What is a SQLState code? In JDBC, the java.sql.SQLException class provides information aboutdatabase access errors or other errors. For handling the exceptions thrown by JDBC, it is desirableto analyze the error codes, understand them, and take the necessary actions (handle the excep-tions). You can use SQLException.getSQLState() to get the SQLState code as follows:

try {...

}catch(SQLException e) {

String sqlStateCode = e.getSQLState();...

}

SQLException.getSQLState() returns a SQLState identifier based on the XOPEN SQL or SQL-99specification. Your database-specific reference manuals should list some or all of these; alterna-tively, you can refer to your database tool vendor’s documentation for information to find SQLStatecodes.

How Are SQLState Codes Formatted?Each SQLState code conforms to the following rules (note that the SQLException class does notenforce these rules; it is up to database vendors to follow these standards):

5203ch10.qxd 8/11/05 4:31 PM Page 358

Page 388: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 359

• It is a character string constant of length 5.

• Each character must be a digit from 0 to 9 or a nonaccented uppercase letter (A through Z).

• It is a two-character class code immediately followed by a three-character subclass code.

• The SQLState class (the first two characters) cannot be 00, since this represents successfulcompletion.

What Is a SQLState Code in Oracle 9i?According to the Oracle documentation (specifically, the Oracle 9i JDBC Developer’s Guide and Ref-erence), for errors originating in the JDBC driver the getSQLState() method returns no usefulinformation. For errors originating in the Oracle RDBMS, this method returns a five-digit code indi-cating the SQLState.

In Oracle 9i, the SQLException.getErrorCode() method returns better semantics than theSQLException.getSQLState() method. For errors originating in either the JDBC driver or theRDBMS, this method returns the five-digit SQLState code.

Since error codes play an important role in handling database exceptions, the following tableslist the error codes for Oracle and MySQL. Specifically, Table 10-5 lists Oracle’s predefined classcodes, and Table 10-6 lists Oracle’s SQLState status codes. This information comes fromhttp://www.lc.leidenuniv.nl/awcourse/oracle/appdev.920/a97269/toc.htm. Further, Table 10-7lists MySQL’s SQLState status codes and messages; this information comes from http://com.mysql.jdbc.SQLError.java.

Table 10-5. Oracle Predefined Class Codes

Class Condition

00 Success completion

01 Warning

02 No data

07 Dynamic SQL error

08 Connection exception

0A Feature not supported

21 Coordinately violation

22 Data exception

23 Integrity constraint violation

24 Invalid cursor state

25 Invalid transaction state

26 Invalid SQL statement name

27 Triggered data change violation

28 Invalid authorization specification

2A Direct SQL syntax error or access rule violation

2B Dependent privilege descriptors still exist

2C Invalid character set name

2D Invalid transaction termination

2E Invalid connection name

33 Invalid SQL descriptor name

(Continued)

5203ch10.qxd 8/11/05 4:31 PM Page 359

Page 389: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC360

Table 10-5. Continued

Class Condition

34 Invalid cursor name

35 Invalid condition number

37 Dynamic SQL syntax error or access rule violation

3C Ambiguous cursor name

3D Invalid catalog name

3F Invalid schema name

40 Transaction rollback

42 Syntax error or access rule violation

44 With check option violation

HZ Remote database access (reserved for conditions defined in ISO/IECDIS 9579-2, Remote Database Access)

Table 10-6. Oracle SQLState Status Codes

Code Condition Oracle Error(s)

00000 Successful completion ORA-00000

01000 Warning

01001 Cursor operation conflict

01002 Disconnect error

01003 NULL value eliminated in set function

01004 String data-right truncation

01005 Insufficient item descriptor areas

01006 Privilege not revoked

01007 Privilege not granted

01008 Implicit zero-bit padding

01009 Search condition too long for info schema

0100A Query expression too long for info schema

02000 No data ORA-01095 and ORA-01403

07000 Dynamic SQL error

07001 Using clause not matching parameter specs

07002 Using clause not matching target specs

07003 Cursor specification cannot be executed

07004 Using clause required for dynamic parameters

07005 Prepared statement not a cursor specification

07006 Restricted data type attribute violation

07007 Using clause required for result components invalid descriptor count

07008 Invalid descriptor count SQL-02126

07009 Invalid descriptor index

08000 Connection exception

08001 SQL client unable to establish SQL connection

5203ch10.qxd 8/11/05 4:31 PM Page 360

Page 390: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 361

Code Condition Oracle Error(s)

08002 Connection name in use

08003 Connection does not exist SQL-02121

08004 SQL server rejected SQL connection

08006 Connection failure

08007 Transaction resolution unknown

0A000 Feature not supported ORA-03000..03099

0A001 Multiple server transactions

21000 Cardinality violation ORA-01427 and SQL-02112

22000 Data exception

22001 String data, right truncation ORA-01406

22002 NULL value, no indicator parameter SQL-02124

22003 Numeric value out of range ORA-01426

22005 Error in assignment

22007 Invalid date/time format

22008 Date/time field overflow ORA-01800..01899

22009 Invalid time zone displacement value

22011 Substring error

22012 Division by zero ORA-01476

22015 Interval field overflow

22018 Invalid character value for cast

22019 Invalid escape character ORA-00911

22021 Character not in repertoire

22022 Indicator overflow ORA-01411

22023 Invalid parameter value ORA-01025 andORA-04000..04019

22024 Unterminated C string ORA-01479 and ORA-01480

22025 Invalid escape sequence ORA-01424 and ORA-01425

22026 String data-length mismatch ORA-01401

22027 Trim error

23000 Integrity constraint violation ORA-02290..02299

24000 Invalid cursor state ORA-001002, ORA-001003,SQL-02114, and SQL-02117

25000 Invalid transaction state SQL-02118

26000 Invalid SQL statement name

27000 Triggered data change violation

28000 Invalid authorization specification

2A000 Direct SQL syntax error or access rule violation

2B000 Dependent privilege descriptors still exist

2C000 Invalid character set name

2D000 Invalid transaction termination

2E000 Invalid connection name

(Continued)

5203ch10.qxd 8/11/05 4:31 PM Page 361

Page 391: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC362

Table 10-6. Continued

Code Condition Oracle Error(s)

33000 Invalid SQL descriptor name

34000 Invalid cursor name

35000 Invalid condition number

37000 Dynamic SQL syntax error or access rule violation

3C000 Ambiguous cursor name

3D000 Invalid catalog name

3F000 Invalid schema name

40000 Transaction rollback ORA-02091 and ORA-02092

40001 Serialization failure

40002 Integrity constraint violation

40003 Statement completion unknown

42000 Syntax error or access rule violation ORA-00022, ORA-00251,ORA-00900..00999,ORA-01031,ORA-01490..01493,ORA-01700..01799,ORA-01900..02099,ORA-02140..02289,ORA-02420..02424,ORA-02450..02499,ORA-03276..03299,ORA-04040..04059, andORA-04070..04099

44000 With check option violation ORA-01402

60000 System error ORA-00370..00429,ORA-00600..00899,ORA-06430..06449,ORA-07200..07999, andORA-09700..09999

61000 Shared server and detached process errors ORA-00018..00035,ORA-00050..00068,ORA-02376..02399, andORA-04020..04039

62000 Shared server and detached process errors ORA-00100..00120 andORA-00440..00569

63000 Oracle*XA and two-task interface errors ORA-00150..00159,ORA-02700..02899,ORA-03100..03199,ORA-06200..06249, andSQL-02128

64000 Control file, database file, and redo file errors; ORA-00200..00369 and archival and media recovery errors ORA-01100..01250

65000 PL/SQL errors ORA-06500..06599

66000 Oracle Net driver errors ORA-06000..06149,ORA-06250..06429,ORA-06600..06999,ORA-12100..12299, andORA-12500..12599

5203ch10.qxd 8/11/05 4:31 PM Page 362

Page 392: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 363

Code Condition Oracle Error(s)

67000 Licensing errors ORA-00430..00439

69000 SQL*Connect errors ORA-00570..00599 andORA-07000..07199

72000 SQL execute phase errors ORA-00001,ORA-01000..01099,ORA-01400..01489,ORA-01495..01499,ORA-01500..01699,ORA-02400..02419,ORA-02425..02449,ORA-04060..04069,ORA-08000..08190,ORA-12000..12019,ORA-12300..12499, andORA-12700..21999

82100 Out of memory (could not allocate) SQL-02100

82101 Inconsistent cursor cache (UCE/CUC mismatch) SQL-02101

82102 Inconsistent cursor cache (no CUC entry for UCE) SQL-02102

82103 Inconsistent cursor cache (out-or-range CUC ref) SQL-02103

82104 Inconsistent cursor cache (no CUC available) SQL-02104

82105 Inconsistent cursor cache (no CUC entry in cache) SQL-02105

82106 Inconsistent cursor cache (invalid cursor number) SQL-02106

82107 Program too old for runtime library; re-precompile SQL-02107

82108 Invalid descriptor passed to runtime library SQL-02108

82109 Inconsistent host cache (out-or-range SIT ref) SQL-02109

82110 Inconsistent host cache (invalid SQL type) SQL-02110

82111 Heap consistency error SQL-02111

82113 Code generation internal consistency failed SQL-02115

82114 Reentrant code generator gave invalid context SQL-02116

82117 Invalid OPEN or PREPARE for this connection SQL-02122

82118 Application context not found SQL-02123

82119 Unable to obtain error message text SQL-02125

82120 Precompiler/SQLLIB version mismatch SQL-02127

82121 NCHAR error; fetched number of bytes is odd SQL-02129

82122 EXEC TOOLS interface not available SQL-02130

82123 Runtime context in use SQL-02131

82124 Unable to allocate runtime context SQL-02132

82125 Unable to initialize process for use with threads SQL-02133

82126 Invalid runtime context SQL-02134

HZ000 Remote database access

5203ch10.qxd 8/11/05 4:31 PM Page 363

Page 393: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC364

Table 10-7. MySQL SQLState Codes and Messages

Code Description

01002 Disconnect error

01004 Data truncated

01006 Privilege not revoked

01S00 Invalid connection string attribute

01S01 Error in row

01S03 No rows updated or deleted

01S04 More than one row updated or deleted

07001 Wrong number of parameters

08001 Unable to connect to data source

08002 Connection in use

08003 Connection not open

08004 Data source rejected establishment of connection

08007 Connection failure during transaction

08S01 Communication link failure

21S01 Insert value list does not match column list

22003 Numeric value out of range

22005 Numeric value out of range

22008 Date/time field overflow

22012 Division by zero

28000 Invalid authorization specification

42000 Syntax error or access violation

S0001 Base table or view already exists

S0002 Base table not found

S0011 Index already exists

S0012 Index not found

S0021 Column already exists

S0022 Column not found

S0023 No default for column

S1000 General error

S1001 Memory allocation failure

S1002 Invalid column number

S1009 Invalid argument value

S1C00 Driver not capable

S1T00 Timeout expired

10-17. What Is a BatchUpdateException?BatchUpdateException is a class that extends the SQLException class and provides information abouterrors during a batch update operation. This exception is thrown by the Statement.executeBatch()method if one of the SQL commands in the batch fails.

5203ch10.qxd 8/11/05 4:31 PM Page 364

Page 394: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 365

According to the Java documentation, the BatchUpdateException class is defined as follows:

package java.sql;

public class BatchUpdateExceptionextends SQLException

Further, the documentation says this:

An exception thrown when an error occurs during a batch update operation. In addition tothe information provided by SQLException, a BatchUpdateException provides the updatecounts for all commands that were executed successfully during the batch update, that is, allcommands that were executed before the error occurred. The order of elements in an array ofupdate counts corresponds to the order in which commands were added to the batch.

After a command in a batch update fails to execute properly and a BatchUpdateException isthrown, the driver may or may not continue to process the remaining commands in thebatch. If the driver continues processing after a failure, the array returned by the methodBatchUpdateException.getUpdateCounts will have an element for every command in thebatch rather than only elements for the commands that executed successfully before the error.In the case where the driver continues processing commands, the array element for any com-mand that failed is Statement.EXECUTE_FAILED.

Table 10-8 lists the BatchUpdateException constructors, and Table 10-9 lists the method.

Table 10-8. Constructors for BatchUpdateException

Constructor Description

BatchUpdateException() Constructs a BatchUpdateException object withthe reason field, SQLState, and update countinitialized to null and the vendor codeinitialized to 0

BatchUpdateException(int[] updateCounts) Constructs a BatchUpdateException initializedto null for the reason field and SQLState and 0for the vendor code

BatchUpdateException(String reason, Constructs a BatchUpdateException initialized int[] updateCounts) with reason, updateCounts, and null for the

SQLState and 0 for the vendor code

BatchUpdateException(String reason, Constructs a BatchUpdateException initializedString SQLState, int[] updateCounts) with the given arguments (reason, SQLState,

and updateCounts) and 0 for the vendor code

BatchUpdateException(String reason, Constructs a fully specifiedString SQLState, int vendorCode, BatchUpdateException object, initializing itint[] updateCounts) with the given values

Table 10-9. Method for BatchUpdateException

Return Type Method Description

int[] getUpdateCounts() Retrieves the update count for each update statementin the batch update that executed successfully beforethis exception occurred

5203ch10.qxd 8/11/05 4:31 PM Page 365

Page 395: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC366

In the following sections, I will provide two complete examples using BatchUpdateException. Thefirst example (the Demo_BatchUpdateException_1 class) uses a batch update and passes all valid/correctSQL statements; the second example (the Demo_BatchUpdateException_2 class) uses a batch update andpasses a mix of correct and incorrect SQL statements, which will cause BatchUpdateException to be thrown.

Example 1: Demo_BatchUpdateException_1The following is Demo_BatchUpdateException_1:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_BatchUpdateException_1 {

public static void checkUpdateCounts(int[] updateCounts) {for (int i=0; i<updateCounts.length; i++) {

if (updateCounts[i] >= 0) {// Successfully executed;// the number represents number of affected rowsSystem.out.println("OK: updateCount="+updateCounts[i]);

}else if (updateCounts[i] == Statement.SUCCESS_NO_INFO) {

// Successfully executed;// number of affected rows not availableSystem.out.println("OK: updateCount=Statement.SUCCESS_NO_INFO");

}else if (updateCounts[i] == Statement.EXECUTE_FAILED) {

// Failed to executeSystem.out.println("updateCount=Statement.EXECUTE_FAILED");

}}

}

public static void main(String[] args) {Connection conn = null;Statement stmt = null;try {

System.out.println("-- begin-- dbVendor="+args[0]);String dbVendor = args[0];conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);

// Disable autocommitconn.setAutoCommit(false);

// create Statement objectstmt = conn.createStatement();stmt.addBatch("DELETE FROM animals_table");

5203ch10.qxd 8/11/05 4:31 PM Page 366

Page 396: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 367

stmt.addBatch("INSERT INTO animals_table(id, name) "+"VALUES(111, 'ginger')");

stmt.addBatch("INSERT INTO animals_table(id, name) "+"VALUES(222, 'lola')");

stmt.addBatch("INSERT INTO animals_table(id, name) "+"VALUES(333, 'freddy')");

// Execute the batchint[] updateCounts = stmt.executeBatch();

// all statements were successfully executed.// updateCounts contains one element for each// batched statement. The updateCounts[i] contains// the number of rows affected by that statement.checkUpdateCounts(updateCounts);

// since there were no errors, commitconn.commit();System.out.println("-- end --");

}catch (BatchUpdateException e) {

// Not all of the statements were successfully executedint[] updateCounts = e.getUpdateCounts();

// Some databases will continue to execute after one fails.// If so, updateCounts.length will equal the number of batched// statements. If not, updateCounts.length will equal the number// of successfully executed statementscheckUpdateCounts(updateCounts);

// Either commit the successfully executed statements// or roll back the entire batchtry {

conn.rollback();}catch (Exception e2) {

e.printStackTrace();System.exit(1);

}}catch (Exception e) {

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

5203ch10.qxd 8/11/05 4:31 PM Page 367

Page 397: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC368

Viewing the MySQL Database Before Running the Solution

This shows the MySQL database before running the solution:

mysql> desc animals_table;+-------+-------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+----------------+| id | int(11) | | PRI | NULL | auto_increment || name | varchar(10) | | | | |+-------+-------------+------+-----+---------+----------------+2 rows in set (0.00 sec)

mysql> select * from animals_table;+-----+--------+| id | name |+-----+--------+| 22 | puffy || 77 | muffy |+-----+--------+2 rows in set (0.00 sec)

Running the Solution for the MySQL Database

This shows how to run the solution for the MySQL database:

$ javac Demo_BatchUpdateException_1.java$ java Demo_BatchUpdateException_1 mysql-- begin-- dbVendor=mysqlconn=com.mysql.jdbc.Connection@750159---------------OK; updateCount=2OK; updateCount=1OK; updateCount=1OK; updateCount=1-- end --

Discussing the Output for the Solution

This is what happened:

• updateCount=2 refers to the fact that the SQL statement DELETE FROM animals_table impactedtwo rows (that is, two rows were deleted).

• updateCount=1 refers to the fact that the SQL INSERT INTO animals_table(id, name) ...statement impacted one row (that is, one row was inserted).

Viewing the MySQL Database After Running the Solution

This is the MySQL database after running the solution:

mysql> select * from animals_table;+-----+--------+| id | name |+-----+--------+| 111 | ginger || 222 | lola || 333 | freddy |+-----+--------+

5203ch10.qxd 8/11/05 4:31 PM Page 368

Page 398: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 369

Viewing the Oracle Database Before Running the Solution

This is the Oracle database before running the solution:

SQL> create table animals_table(2 id int, name varchar2(10));

Table created.

SQL> desc animals_table;Name Null? Type----------------------------------------- -------- --------------ID NUMBER(38)NAME VARCHAR2(10)

SQL> insert into animals_table(id, name) values(22, 'puffy');SQL> insert into animals_table(id, name) values(77, 'muffy');SQL> commit;Commit complete.

SQL> select * from animals_table;

ID NAME---------- ----------

22 puffy77 muffy

Running the Solution for the Oracle Database

This shows how to run the solution for Oracle:

$ javac Demo_BatchUpdateException_1.java$ java Demo_BatchUpdateException_1 oracle-- begin-- dbVendor=oracleconn=oracle.jdbc.driver.T4CConnection@6e293aOK: updateCount=2OK: updateCount=1OK: updateCount=1OK: updateCount=1-- end --

Discussing the Output for the Solution

This is what happened:

• updateCount=2 refers to the fact that the SQL statement DELETE FROM animals_tableimpacted two rows (that is, two rows were deleted).

• updateCount=1 refers to the fact that the SQL INSERT INTO animals_table(id, name) ...statement impacted one row (that is, one row was inserted).

Viewing the Oracle Database After Running the Solution

This is the Oracle database after running the solution:

5203ch10.qxd 8/11/05 4:31 PM Page 369

Page 399: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC370

SQL> select * from animals_table;

ID NAME---------- ----------

111 ginger222 lola333 freddy

Example 2: Demo_BatchUpdateException_2This shows Demo_BatchUpdateException_2:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_BatchUpdateException_2 {

public static void checkUpdateCounts(int[] updateCounts) {for (int i=0; i<updateCounts.length; i++) {

if (updateCounts[i] >= 0) {// Successfully executed;// the number represents number of affected rowsSystem.out.println("OK; updateCount="+updateCounts[i]);

}else if (updateCounts[i] == Statement.SUCCESS_NO_INFO) {

// Successfully executed; number of affected rows not availableSystem.out.println("OK; updateCount=Statement.SUCCESS_NO_INFO");

}else if (updateCounts[i] == Statement.EXECUTE_FAILED) {

// Failed to executeSystem.out.println("Failure; updateCount=Statement.EXECUTE_FAILED");

}}

}

public static void main(String[] args) {Connection conn = null;Statement stmt = null;try {

System.out.println("-- begin-- dbVendor="+args[0]);String dbVendor = args[0];conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);

// Disable autocommitconn.setAutoCommit(false);

// create Statement objectstmt = conn.createStatement();stmt.addBatch("DELETE FROM animals_table");

5203ch10.qxd 8/11/05 4:31 PM Page 370

Page 400: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 371

stmt.addBatch("INSERT INTO animals_table(id, name) "+"VALUES(444, 'ginger')");

// we intentionally pass a table name (animals_tableZZ)// that does not existstmt.addBatch("INSERT INTO animals_tableZZ(id, name) "+

"VALUES(555, 'lola')");stmt.addBatch("INSERT INTO animals_table(id, name) "+

"VALUES(666, 'freddy')");

// Execute the batchint[] updateCounts = stmt.executeBatch();

// all statements were successfully executed.// updateCounts contains one element for each// batched statement. The updateCounts[i] contains// the number of rows affected by that statement.checkUpdateCounts(updateCounts);

// since there were no errors, commitconn.commit();System.out.println("-- end --");

}catch (BatchUpdateException e) {

// Not all of the statements were successfully executedint[] updateCounts = e.getUpdateCounts();

// Some databases will continue to execute after one// fails. If so, updateCounts.length will equal the// number of batched statements. If not, updateCounts.length// will equal the number of successfully executed statementscheckUpdateCounts(updateCounts);

// Either commit the successfully executed// statements or roll back the entire batchtry {

conn.rollback();}catch (Exception e2) {

e.printStackTrace();System.exit(1);

}}catch (Exception e) {

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

5203ch10.qxd 8/11/05 4:31 PM Page 371

Page 401: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC372

Viewing the MySQL Database Before Running the Solution

This is the MySQL database before running the solution:

mysql> select * from animals_table;+-----+--------+| id | name |+-----+--------+| 111 | ginger || 222 | lola || 333 | freddy |+-----+--------+3 rows in set (0.00 sec)

Running the Solution for the MySQL Database

For discussion purposes, I have added line numbers to the output:

1 $ javac Demo_BatchUpdateException_2.java2 $ java Demo_BatchUpdateException_2 mysql3 -- begin-- dbVendor=mysql4 conn=com.mysql.jdbc.Connection@1301ed85 OK; updateCount=36 OK; updateCount=17 Failure; updateCount=Statement.EXECUTE_FAILED8 OK; updateCount=1

Viewing the MySQL Database After Running the Solution

This is the MySQL database after running the solution:

mysql> select * from animals_table;+-----+--------+| id | name |+-----+--------+| 444 | ginger || 666 | freddy |+-----+--------+2 rows in set (0.01 sec)

Discussing the Output of Demo_BatchUpdateException_2

This is what happened:

• Line 5: updateCount=3 refers to the fact that the SQL statement DELETE FROM animals_tableimpacted three rows (that is, three rows were deleted).

• Line 6: updateCount=1 refers to the fact that the SQL INSERT INTO animals_table(id, name)... statement impacted one row (that is, one row was inserted).

• Line 7: updateCount=Statement.EXECUTE_FAILED refers to the fact that the SQL INSERT INTOanimals_tableZZ(id, name) ... statement failed (because animals_tableZZ is a nonexistenttable).

• Line 8: updateCount=1 refers to the fact that the SQL INSERT INTO animals_table(id, name)... statement impacted one row (that is, one row was inserted).

5203ch10.qxd 8/11/05 4:31 PM Page 372

Page 402: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 373

Viewing the Oracle Database Before Running the Solution

This is the Oracle database before running the solution:

SQL> select * from animals_table;

ID NAME---------- ----------

111 ginger222 lola333 freddy

Running the Solution for the Oracle Database

For discussion purposes, I have added line numbers to the output:

1 $ javac Demo_BatchUpdateException_2.java2 $ java Demo_BatchUpdateException_2 oracle3 -- begin-- dbVendor=oracle4 conn=oracle.jdbc.driver.T4CConnection@6e293a5 OK; updateCount=36 OK; updateCount=1

Viewing the Oracle Database After Running the Solution

This is the Oracle database after running the solution:

SQL> select * from animals_table;

ID NAME---------- ----------

111 ginger222 lola333 freddy

Discussing the Output for the Solution

Because Oracle supports true transactions, none of the inserts was committed (because of thenonexistent table name animals_tableZZ).

10-18. What Is a DataTruncation Class?DataTruncation is a class that extends the SQLWarning class and provides information about datatruncation errors when JDBC unexpectedly truncates a data value (during read/write operations,such as when a column is defined as VARCHAR(10) and you pass more than ten characters during anINSERT/UPDATE operation). Note that no methods in the JDBC API throw the DataTruncation exceptionexplicitly/directly.

You can catch a DataTruncation exception as a SQLWarning or SQLException exception becauseit is derived from SQLWarning (which is derived from SQLException). When it is caught as a SQLWarningor SQLException, it must be explicitly cast to DataTruncation before you can access the methods ofthat interface.

For handling and customizing DataTruncation exceptions, MySQL’s JDBC driver has a connectionproperty named jdbcCompliantTruncation, which has the following definition (http://72.14.207.104/search?q=cache:9L3ToT2Upx8J:engr.smu.edu/~coyle/cse7346/S12.MySql.Jdbc.pdf+definition+of+jdbcCompliantTruncation&hl=en):

5203ch10.qxd 8/11/05 4:31 PM Page 373

Page 403: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC374

Should the driver throw java.sql.DataTruncation exceptions when data is truncated as isrequired by the JDBC specification when connected to a server that supports warnings(MySQL 4.1.0 and newer)?

The value of this property can be true/false. You can set this property by either adding itto the database URL or adding it to the java.util.Properties object and then passing it to theDriverManager class. You can use the following three approaches to find out if DataTruncation hasoccurred:

First approach: To find out if a DataTruncation exception has occurred, you need to catch thatexception. MySQL’s JDBC driver (Connector/J) throws a DataTruncation exception if you try towrite more data than expected (for example, if a column is defined as VARCHAR(10) and youpass more than ten characters). You can use the following snippet:

try {...

}catch(DataTruncation dt) {

// data truncation has happened.//// use the DataTruncation methods to// handle the exception or ignore it

}

Second approach: To find out if a DataTruncation exception has occurred, you need to invokeStatement.getWarnings(), PreparedStatement.getWarnings(), CallableStatement.getWarnings(),or ResultSet.getWarnings() and then check to see if the warning (the SQLWarning object) is aninstance of DataTruncation; if it is an instance of DataTruncation, then you must cast thewarning object to the DataTruncation object and use its methods accordingly.

Third approach: Finally, you can get the SQLWarning object from Statement, PreparedStatement,CallableStatement, or ResultSet and then check to see if SQLWarning.getSQLState() == "01004".If that is the case, then it means that the SQLWarning object is an instance of DataTruncation. AllJDBC-compliant drivers must use 01004 for SQLWarning.getSQLState().

The following examples will demonstrate these concepts.According to the Java documentation, the DataTruncation class is defined as follows:

package java.sql;

public class DataTruncationextends SQLWarning

The documentation also says this:

An exception that reports a DataTruncation warning (on reads) or throws a DataTruncationexception (on writes) when JDBC unexpectedly truncates a data value. The SQLState fora DataTruncation is 01004.

Table 10-10 shows the DataTruncation constructor.

5203ch10.qxd 8/11/05 4:31 PM Page 374

Page 404: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 375

Table 10-10. DataTruncation Constructor

Constructor Description

DataTruncation(int index, boolean parameter, Creates a DataTruncation object with theboolean read, int dataSize, int transferSize) SQLState initialized to 01004, the reason field

set to data truncation, the vendorCode set tothe SQLException default, and the other fieldsset to the given values

The constructor’s parameters are as follows:

• index: The index of the parameter or column value

• parameter: True if a parameter value was truncated

• read: True if a read was truncated

• dataSize: The original size of the data

• transferSize: The size after truncation

Table 10-11 shows the DataTruncation methods.

Table 10-11. DataTruncation Methods

Return Type Method Name Description

int getDataSize() Gets the number of bytes of data that should have beentransferred

int getIndex() Retrieves the index of the column or parameter that wastruncated

boolean getParameter() Indicates whether the value truncated was a parametervalue or a column value

boolean getRead() Indicates whether the value was truncated on a read

int getTransferSize() Gets the number of bytes of data actually transferred

10-19. How Do You Use DataTruncation?The following sections explain how to create and use a DataTruncation object.

How It WorksThe DataTruncation class has only one constructor, which has five parameters. The following codecreates a DataTruncation object with the SQLState initialized to 01004, the reason field set to data trun-cation, the vendorCode set to the SQLException default, and the other fields set to the given values:

// Example of a DataTruncation:// the index of the parameter or column valueint index = 4;

// true if a parameter value was truncatedboolean parameter = true;

// true if a read was truncatedboolean read = false;

// the original size of the dataint dataSize = 210;

5203ch10.qxd 8/11/05 4:31 PM Page 375

Page 405: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC376

// the size after truncationint transferSize = 40;

DataTruncation dt = new DataTruncation(index,parameter,read,dataSize,transferSize);

I will now provide two examples using DataTruncation. The first example uses the instanceofoperator to determine if a given SQLWarning object is an instance of the DataTruncation object. Thesecond example uses SQLWarning.getSQLState() and then checks its value against 01004 (the SQLStatecode for a DataTruncation object is 01004).

SolutionThis example (the Demo_DataTruncation_1 class) tries to insert a row with two columns so that oneof the columns will be oversized, which will cause DataTruncation to be thrown:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.*;import jcb.meta.*;

public class Demo_DataTruncation_1 {

public static void displayError(DataTruncation dataTruncation) {if ( dataTruncation != null ) {

System.out.println("Data truncation error: ");System.out.println(dataTruncation.getDataSize() +" bytes should have been ");

if (dataTruncation.getRead()) {System.out.println("Read (Error:) ");

}else {

System.out.println("Written (Error:) ");}System.out.println(dataTruncation.getTransferSize() +" number of bytes of data actually transferred.");

}}

public static void displayError(SQLWarning warning) {while ( warning != null ) {

if ( warning instanceof DataTruncation ) {displayError((DataTruncation) warning);

}else {

System.out.println(" Warning: " + warning.getMessage());}warning = warning.getNextWarning();

}}

5203ch10.qxd 8/11/05 4:31 PM Page 376

Page 406: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 377

public static void main(String[] args) {Connection conn = null;Statement stmt = null;try {

System.out.println("-- Demo_DataTruncation_1 begin --");// args[0] = dbVendor = {"mysql", "oracle" }conn = VeryBasicConnectionManager.getConnection(args[0]); //System.out.println("conn="+conn);System.out.println("---------------");

// create Statement objectstmt = conn.createStatement();stmt.executeUpdate("DELETE FROM animals_table");displayError(stmt.getWarnings());// try to write more data for the name column.stmt.executeUpdate("INSERT INTO animals_table(id, name)"+

"VALUES(111, 'ginger123456789')");displayError(stmt.getWarnings());

System.out.println("-- Demo_DataTruncation_1 end --");}catch (DataTruncation dt) {

System.out.println("-- got DataTruncation exception --");displayError(dt);System.out.println("-- printStackTrace --");dt.printStackTrace();System.exit(1);

}catch (SQLException se) {

System.out.println("-- got SQLException exception --");System.out.println("Database error message: "+se.getMessage());System.exit(1);

}catch (Exception e) {

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Viewing the MySQL Database Before Running the Solution

This is the MySQL database before running the solution:

mysql> desc animals_table;+-------+-------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+----------------+| id | int(11) | | PRI | NULL | auto_increment || name | varchar(10) | | | | |+-------+-------------+------+-----+---------+----------------+2 rows in set (0.00 sec)

5203ch10.qxd 8/11/05 4:31 PM Page 377

Page 407: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC378

mysql> select * from animals_table;+-----+--------+| id | name |+-----+--------+| 444 | ginger || 666 | freddy |+-----+--------+2 rows in set (0.00 sec)

Running the Solution for the MySQL Database

This shows how to run the solution for the MySQL database:

$ javac Demo_DataTruncation_1_MySQL.java$ java Demo_DataTruncation_1 mysql-- Demo_DataTruncation_1 begin --conn=com.mysql.jdbc.Connection@143c8b3----------------- got DataTruncation exception --Data truncation error: 0 bytes should have been Written (Error:)0 number of bytes of data actually transferred.-- printStackTrace --com.mysql.jdbc.MysqlDataTruncation: Data truncation:Data truncated for column 'name' at row 1at com.mysql.jdbc.SQLError.convertShowWarningsToSQLWarnings(SQLError.java:695)at com.mysql.jdbc.MysqlIO.scanForAndThrowDataTruncation(MysqlIO.java:3317)

at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1741)at com.mysql.jdbc.Connection.execSQL(Connection.java:2370)at com.mysql.jdbc.Connection.execSQL(Connection.java:2297)at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1289)at Demo_DataTruncation_1.main(Demo_DataTruncation_1.java:54)

Viewing the MySQL Database After Running the Solution

As you can see, this example tried to insert ginger123456789 for the name column; because the sizeof this data (15) is larger than 10 (the maximum size of a name column), the exception takes place.MySQL’s JDBC driver chops the data and inserts only the maximum allowed, as follows:

mysql> select * from animals_table;+-----+------------+| id | name |+-----+------------+| 111 | ginger1234 |+-----+------------+1 row in set (0.00 sec)

Viewing the Oracle Database Before Running the Solution

This is the Oracle database before running the solution:

SQL> select id, name from animals_table;no rows selectedSQL>

5203ch10.qxd 8/11/05 4:31 PM Page 378

Page 408: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC 379

Running the Solution for the Oracle Database

This shows how to run the solution for the Oracle database:

$ javac Demo_DataTruncation_1.javajava Demo_DataTruncation_1 oracle-- Demo_DataTruncation_1 begin --conn=oracle.jdbc.driver.T4CConnection@6e293a----------------- got SQLException exception --Database error message: ORA-12899: value too large for column "SCOTT"."ANIMALS_TABLE"."NAME" (actual: 15, maximum: 10)

Viewing the Oracle Database After Running the Solution

As you can see, this example tried to insert ginger123456789 for the name column; because the sizeof this data (15) is larger than 10 (the maximum size of a name column), the exception happens.Oracle’s JDBC driver (unlike MySQL) does not insert the record, as follows:

SQL> select id, name from animals_table;no rows selectedSQL>

10-20. How Do You Use DataTruncation for ResultSet?Generating a ResultSet object may throw a DataTruncation exception.

As you can see from the definition of the DataTruncation class, it is a special case of SQLWarning(DataTruncation is a subclass of SQLWarning) that applies to column and parameter values. To geta DataTruncation, first execute a SQL query, then get a ResultSet object, and finally get a DataTruncationfrom your ResultSet. The DataTruncation class provides several methods to determine the column orparameter index to which it applies, the actual and expected lengths, and so on. When DataTruncation ischained to a ResultSet object, it can be distinguished from other JDBC warnings based on its SQLStatecode of 01004.

If data truncation occurs during a read operation (using SQL’s SELECT statement) from a ResultSetobject, then a DataTruncation object will be added to the ResultSet object’s warning list, and themethod will return as much data as it was able to read.

The following code snippet shows how to use DataTruncation with the ResultSet object:

import java.sql.*;...public static final String DATA_TRUNCATION_SQL_STATE = “01004”;...ResultSet rs = null;Statement stmt = null;Connection conn = null;SQLWarning warning = null;

String sqlQuery = "select ... from ..."; // form your sql querytry {

conn = getConnection();stmt = conn.createStatement();rs = stmt.executeQuery(sqlQuery);warning = rs.getWarnings();while (warning != null) {

if (warn.getSQLState().equals(DATA_TRUNCATION_SQL_STATE)) {

5203ch10.qxd 8/11/05 4:31 PM Page 379

Page 409: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 10 ■ HANDLING EXCEPTIONS IN JDBC380

//// this means it is a DataTruncation exception// and its SQLState is initialized to 01004//DataTruncation dt = (DataTruncation) warning;int columnNumber = dt.getIndex();int transferSize = dt.getTransferSize();int dataSize = dt.getDataSize();// do something useful with this information// ...

}else {

// it is some other SQLWarning// handle the exception

}

warning = warning.getNextWarning();

}}catch(SQLException se) {

// handle SQLException}catch(Exception e) {

// handle Exception}finally {

// close ResultSet, Statement, Connection objects}

5203ch10.qxd 8/11/05 4:31 PM Page 380

Page 410: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Exploring the Statement

This chapter describes an important JDBC interface, java.sql.Statement. This interface formulatesthe SQL statements executed by the database engine. You can use Statement and PreparedStatementto execute SQL queries and statements and return the results (as a ResultSet object or as other datatypes) to the client. You can use CallableStatement to execute a stored procedure/function andreturn the result (as a ResultSet object or as other data types) to the client. You can create all threeinterfaces using a Connection (java.sql.Connection) object. Connection is a factory object for creatingother objects (such as Statement, PreparedStatement, and CallableStatement).

By using a Statement object, you can execute static SQL statements. (SQL queries cannot be para-meterized with the Statement object; in order to parameterize SQL queries, you must use thePreparedStatement object.) The Statement interface enables you to execute both DDL (such as creatinga table or index) and DML (such as retrieving an existing record, inserting a new record, or updating anexisting row). The Statement interface provides the following methods for executing SQL requests:

• execute(String sql): Executes the given SQL statement, which may return multiple results

• executeBatch(): Submits a batch of commands to the database for execution and, if allcommands execute successfully, returns an array of update counts

• executeQuery(String sql): Executes the given SQL statement, which returns a singleResultSet object

• executeUpdate(String sql): Executes the given SQL statement, which may be an INSERT,UPDATE, or DELETE statement or a SQL statement that returns nothing, such as a SQL DDLstatement

11-1. How Do You Represent a SQL Statement Object?Say you want to send your SQL statements to query the database or create database objects (such asnew records/row, tables, and views). How do you do that?

A Statement object is what sends your SQL statement (whether you’re creating a table or select-ing rows from a table) to the database server. According to the JDK, “the Statement object is used forexecuting a static SQL statement and returning the results it produces.” The Statement interface hasabout 40 methods.

You simply create a Statement object and then execute it, supplying the appropriate “execute”method with the SQL statement you want to send. For a SELECT statement, the method is executeQuery().For statements that create or modify tables, the method to use is executeUpdate().

JDK defines the java.sql.Statement as follows:

public interface Statement

381

C H A P T E R 1 1

■ ■ ■

5203ch11.qxd 8/11/05 4:33 PM Page 381

Page 411: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT382

The JDK also says this:

The object used for executing a static SQL statement and returning the results it produces. Bydefault, only one ResultSet object per Statement object can be open at the same time. There-fore, if the reading of one ResultSet object is interleaved with the reading of another, eachmust have been generated by different Statement objects. All execution methods in the State-ment interface implicitly close a statement’s current ResultSet object if an open one exists.

If you want to execute a Statement object many times, you can use a PreparedStatement objectinstead of Statement to reduce execution time. (For details, see Chapter 12.) The main feature ofa PreparedStatement object is that, unlike a Statement object, it is given a SQL statement when it iscreated. The advantage to this is that in most cases this SQL statement will be sent to the databaseserver right away, where it will be compiled. As a result, the PreparedStatement object contains notjust a SQL statement but also a SQL statement that has been precompiled. This means that whenthe PreparedStatement is executed, the database server can just run the PreparedStatement object’sSQL statement without having to compile/parse/check semantics first.

When the database “prepares” a Statement object, it creates a query plan. A query plan indicatesto the database how (using which indexes and other criteria) the SQL query will be executed. Notethat Statement prepares and executes the query plan each time, while PreparedStatement preparesthe query plan just once and then reuses the query plan. Preparing a statement is also referred to asprecompiling a statement.

A Statement object executes simple SQL statements with no parameters, and a PreparedStatementobject can execute SQL statements with any number of “input” parameters. From the performancepoint of view, if the same SQL statement is executed many times, it may be more efficient to usea PreparedStatement object.

11-2. How Do You Create Statement Objects?The java.sql.Connection interface has factory methods for creating Statement and PreparedStatementobjects. A Statement object is created with the Connection method createStatement(): once a Statementobject is created, you may execute any number of SQL queries with that Statement object.

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// create Statement object: once Statement object is// created, you can execute queries, send SQL statements// to the database, and then get the results backstmt = conn.createStatement();

// create a result setString sqlQuery = "SELECT badge_number FROM employee_table";rs = stmt.executeQuery(sqlQuery);

// iterate the result set object, and get all the datawhile (rs.next()) {

int badgeNumber = rs.getInt(1);}

5203ch11.qxd 8/11/05 4:33 PM Page 382

Page 412: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 383

// create another result setString sqlQuery2 = "SELECT dept_id, dept_name FROM dept_table";rs = stmt.executeQuery(sqlQuery2);

// iterate the result set object, and get all the datawhile (rs.next()) {

int deptID = rs.getInt(1);String deptName = rs.getString(2);

}}catch (SQLException e) {

// could not create a Statement object, or other// problems happened; handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

The JDK defines the Connection.createStatement() method as follows:

public Statement createStatement() throws SQLException

The JDK also says this:

Creates a Statement object for sending SQL statements to the database. SQL statements with-out parameters are normally executed using Statement objects. If the same SQL statement isexecuted many times, it may be more efficient to use a PreparedStatement object. Result setscreated using the returned Statement object will by default be type TYPE_FORWARD_ONLYand have a concurrency level of CONCUR_READ_ONLY. It returns a new default Statementobject. It throws a SQLException if a database access error occurs.

11-3. How Do You Create a Scrollable ResultSet?You will need to know how to create a scrollable ResultSet so you can iterate your retrieved recordsforward and backward (that is, move a ResultSet object’s cursor backward as well as forward). Thedefault ResultSet object is not scrollable.

When you use Connection.createStatement(), it creates a Statement object that can createresult set objects that are scrollable. Therefore, by passing some parameters, you can create scrolla-ble result sets. A scrollable result set allows the cursor to be moved to any row in the result set. Thiscapability is useful for GUI tools that browse result sets.

Creating an Insensitive Scrollable ResultSet ObjectThe following example shows how to create an insensitive scrollable ResultSet object:

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// create a statement that creates insensitive scrollable result setStatement stmt = connection.createStatement(

ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

5203ch11.qxd 8/11/05 4:33 PM Page 383

Page 413: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT384

// create a result setrs = stmt.executeQuery("SELECT badge_number FROM employee_table");

// iterate the result set object, and get all the datawhile (rs.next()) {

int badgeNumber = rs.getInt(1);}

}catch (SQLException e) {

// could not create a Statement object, or other problems happened.// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

Creating a Sensitive Scrollable ResultSet ObjectThe following example shows how to create a sensitive scrollable ResultSet object:

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();// create a statement that creates a sensitive scrollable result setstmt = connection.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);

// create a result setrs = stmt.executeQuery("SELECT badge_number FROM employee_table");

// iterate the result set object, and get all the datawhile (rs.next()) {

int badgeNumber = rs.getInt(1);}

}catch (SQLException e) {

// could not create a Statement object, or other problems happened.// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

Before creating scrollable ResultSet(s), you need to determine whether your database supportsscrollable ResultSet objects. A scrollable ResultSet object allows the cursor to be moved to any rowin the result set. Two types of scrollable result sets exist. An insensitive scrollable result set is onewhere the values captured in the result set never change (similar to Java’s final semantics), even ifchanges are made to the table from which the data was retrieved. A sensitive scrollable result set isone where the current values in the table are reflected in the result set. So, if a change is made to a rowin the table, the result set will show the new data when the cursor moves to that row.

The following code can check whether your database supports scrollable ResultSet objects:

5203ch11.qxd 8/11/05 4:33 PM Page 384

Page 414: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 385

Connection conn = null;try {

// get a Connection objectconn = getConnection();

DatabaseMetaData dbmd = conn.getMetaData();if (dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)) {

// insensitive scrollable result sets are supported}

if (dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)) {// sensitive scrollable result sets are supported

}

if (!dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)&& !dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)) {// updatable result sets are not supported

}}catch (SQLException e) {

// handle the exception}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-4. How Do You Create an Updatable ResultSet?At some point, you will probably want to create an updatable ResultSet object, which can be savedback in the database, but the default ResultSet object is not updatable.

An updatable result set allows you to modify data in a table through the ResultSet object. If thedatabase does not support updatable result sets, the result sets returned from executeQuery() willbe read-only. To get updatable results, the Statement object used to create the result sets must havethe concurrency type ResultSet.CONCUR_UPDATABLE. The query of an updatable ResultSet must spec-ify the primary key as one of the selected columns and select from only one table. For some drivers,the "SELECT * FROM <table-name>" SQL query will return a read-only ResultSet, so make sure youspecify the column names.

The following code creates a statement that will return an updatable ResultSet object:

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// create a statement that will return updatable result setsstmt = connection.createStatement(

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

// the primary key badge_number must be specified// so that the result set is updatablers = stmt.executeQuery("SELECT badge_number FROM employee_table");

}

5203ch11.qxd 8/11/05 4:33 PM Page 385

Page 415: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT386

catch (SQLException e) {// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

Before creating an updatable ResultSet object, you need to determine whether your databasesupports updatable ResultSet objects. An updatable ResultSet objects allows modification to datain a table through the result set. You may use the following code to determine whether your databasesupports updatable ResultSet objects:

Connection conn = null;try {

// get a Connection objectconn = getConnection();

DatabaseMetaData dbmd = conn.getMetaData();if (dbmd.supportsResultSetConcurrency(

ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE)) {

// Updatable result sets are supported}else {

// Updatable result sets are not supported}

}catch (SQLException e) {

// handle the exception}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-5. How Do You Execute SQL Statements Using StatementObjects?The Statement interface provides sufficient methods for executing SQL statements. For executingSQL statements, you should follow four steps:

1. Get a java.sql.Connection object.

2. Using a Connection object, create a Statement object.

3. Execute your SQL statement using a Statement object and generate ResultSet objects.

■Note If a method does not return a ResultSet object explicitly, then you may call the Statement.getResultSet()method to get the desired ResultSet object.

4. Extract the values from the generated ResultSet objects.

The Statement interface provides the methods listed in Table 11-1 for executing SQL statements.

5203ch11.qxd 8/11/05 4:33 PM Page 386

Page 416: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 387

Table 11-1. The Statement Object’s execute() Methods

Return Type Method

boolean execute(String sql) Executes the given SQL statement, which mayreturn multiple results

boolean execute(String sql, Executes the given SQL statement, which may int autoGeneratedKeys) return multiple results, and signals the driver

that any autogenerated keys should be madeavailable for retrieval

boolean execute(String sql, Executes the given SQL statement, which may int[] columnIndexes) return multiple results, and signals the driver

that the autogenerated keys indicated in thegiven array should be made available for retrieval

boolean execute(String sql, Executes the given SQL statement, which may String[] columnNames) return multiple results, and signals the driver

that the autogenerated keys indicated in thegiven array should be made available for retrieval

int[] executeBatch() Submits a batch of commands to the databasefor execution and, if all commands executesuccessfully, returns an array of update counts

ResultSet executeQuery(String sql) Executes the given SQL statement, whichreturns a single ResultSet object

int executeUpdate(String sql) Executes the given SQL statement, which maybe an INSERT, UPDATE, or DELETE statement ora SQL statement that returns nothing, such asa SQL DDL statement

int executeUpdate(String sql, Executes the given SQL statement and signals int autoGeneratedKeys) the driver with the given flag about whether the

autogenerated keys produced by this Statementobject should be made available for retrieval

int executeUpdate(String sql, Executes the given SQL statement and signals int[] columnIndexes) the driver that the autogenerated keys indicated

in the given array should be made available forretrieval

int executeUpdate(String sql, Executes the given SQL statement and signals String[] columnNames) the driver that the autogenerated keys indicated

in the given array should be made available forretrieval

11-6. How Do You Create a Database Table Using a Statement?To create a database table, you need to pass the table definition to the executeUpdate() method ofa Statement object. The following example creates a table called employee_table with two columns:

Statement stmt = null;Connection conn = null;try {

// get a Connection object and create Statement objectconn = getConnection();stmt = conn.createStatement();

// create table definition called employee_tableString tableDefinition = "CREATE TABLE employee_table("+

"badge_number VARCHAR(10), last_name VARCHAR(64))";

5203ch11.qxd 8/11/05 4:33 PM Page 387

Page 417: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT388

stmt.executeUpdate(tableDefinition);// table creation was successful

}catch (SQLException e) {

// table creation failed.// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-7. How Do You Drop a Database Table Using a Statement?To drop a database table (delete the structure and content of a table), you need to pass drop table<table-name> to the executeUpdate() method of a Statement object. The following example deletesa table called employee_table:

Statement stmt = null;Connection conn = null;String tableName = "employee_table";try {

// get a Connection object and create a Statement objectconn = getConnection();stmt = conn.createStatement();stmt.executeUpdate("DROP TABLE " + tableName);// table deletion was successful

}catch (SQLException e) {

// table deletion failed.// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-8. How Do You Retrieve Automatically Generated Keys Usinga Statement (MySQL)?Since JDBC 3.0, you can retrieve automatically generated keys using a Statement object. In general,you want to retrieve automatically generated keys when you use the AUTO_INCREMENT attribute inMySQL.

How It WorksThe MySQL database allows for certain columns to be given automatically generated key values.When using automatically generated key values, an INSERT statement is not responsible for supply-ing a value for the column. The database generates a unique value for the column and inserts thevalue. You can use this technique for generating unique primary keys.

The MySQL database uses the AUTO_INCREMENT attribute for generating key values. The fol-lowing example shows how to automatically generate key values before retrieving automaticallygenerated keys.

5203ch11.qxd 8/11/05 4:33 PM Page 388

Page 418: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 389

Setting Up the MySQL DatabaseYou can use the AUTO_INCREMENT attribute to generate a unique identity for new rows, as shown here:

mysql> use octopus;Database changedmysql> CREATE TABLE animals_table (

-> id INT NOT NULL AUTO_INCREMENT,-> name VARCHAR(32) NOT NULL,-> PRIMARY KEY (id)-> );

Query OK, 0 rows affected (0.03 sec)

mysql> desc animals_table;+-------+-------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+----------------+| id | int(11) | | PRI | NULL | auto_increment || name | varchar(32) | | | | |+-------+-------------+------+-----+---------+----------------+2 rows in set (0.01 sec)

mysql> insert into animals_table(name) values('dog');mysql> insert into animals_table(name) values('cat');mysql> insert into animals_table(name) values('rabbit');mysql> select id, name from animals_table;+----+--------+| id | name |+----+--------+| 1 | dog || 2 | cat || 3 | rabbit |+----+--------+3 rows in set (0.00 sec)

Solution

The JDBC 3.0 specification proposes a functional Statement interface that provides access to auto-matically generated key values after an insert. The following code snippet demonstrates how toretrieve AUTO_INCREMENT values using the new JDBC 3.0 method getGeneratedKeys(), which is nowthe preferred method to use if you need to retrieve AUTO_INCREMENT keys.

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

conn = getConnection();stmt = conn.createStatement();// insert a new record into the database// notice that the ID column is not accounted for herestmt.executeUpdate("insert into animals_table (name) values('tiger')");

// Retrieve a result set containing all of the autogenerated keys from the// last update issued on this statement the specific details of the format// of this ResultSet are not clearly specified yetrs = stmt.getGeneratedKeys();

}catch (SQLException e) {

5203ch11.qxd 8/11/05 4:33 PM Page 389

Page 419: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT390

// handle the exception}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

The complete solution is as follows. You should note that, for the MySQL database, if a columnis an AUTO_INCREMENT, you do not need to pass any value at all (this is different for Oracle).

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.VeryBasicConnectionManager;import jcb.util.DatabaseUtil;

public class DemoGetGeneratedKeys {

public static void main(String[] args) {ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

System.out.println("--DemoGetGeneratedKeys begin--");String dbVendor = args[0]; // database vendorconn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);

// create a statementstmt = conn.createStatement();

// insert a record into the animals_table// note that the SQL INSERT is different for each vendorString insert = null;if (dbVendor.equalsIgnoreCase("mysql")) {

insert = "insert into animals_table(name) "+"values('tiger11')";

}else if (dbVendor.equalsIgnoreCase("oracle")) {

insert = "insert into animals_table(id, name) "+"values(ANIMAL_ID_SEQ.nextval, 'tiger11')";

}

stmt.executeUpdate(insert); // insert the record

if (dbVendor.equalsIgnoreCase("mysql")) {rs = stmt.getGeneratedKeys();

}else if (dbVendor.equalsIgnoreCase("oracle")) {

rs = stmt.executeQuery("select ANIMAL_ID_SEQ.currval from dual");}

while (rs.next()) {ResultSetMetaData rsMetaData = rs.getMetaData();int columnCount = rsMetaData.getColumnCount();for (int i = 1; i <= columnCount; i++) {

String key = rs.getString(i);

5203ch11.qxd 8/11/05 4:33 PM Page 390

Page 420: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 391

System.out.println("key " + i + " is " + key);}

}System.out.println("--DemoGetGeneratedKeys end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Viewing the MySQL Database Before Running the Solution

This shows the MySQL database before running the solution:

mysql> select * from animals_table;+----+--------+| id | name |+----+--------+| 1 | dog || 2 | cat || 3 | rabbit |+----+--------+3 rows in set (0.00 sec)

Running the Solution for the MySQL Database

This shows how to run the solution:

$ javac DemoGetGeneratedKeys.java$ java DemoGetGeneratedKeys mysql-- DemoGetGeneratedKeys_MySQL begin --conn=com.mysql.jdbc.Connection@15c7850---------------key 1 is 4-- DemoGetGeneratedKeys_MySQL end --

Viewing the MySQL Database After Running the Solution

This shows the MySQL database after running the solution:

mysql> select * from animals_table;+----+---------+| id | name |+----+---------+| 1 | dog || 2 | cat || 3 | rabbit || 4 | tiger11 |+----+---------+4 rows in set (0.01 sec)

5203ch11.qxd 8/11/05 4:33 PM Page 391

Page 421: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT392

11-9. How Do You Retrieve Automatically Generated Keys Usinga Statement (Oracle)?The Oracle database allows for certain columns to be given automatically generated key values.When using automatically generated key values, an insert statement is not responsible for supply-ing a value for the column. The database generates a unique value for the column and inserts thevalue. You can use this technique for generating unique primary keys. The Oracle database uses theSEQUENCE objects for generating key values. What is a SEQUENCE object? SEQUENCE is an Oracle databaseobject that does the following:

• Automatically generates unique numbers

• Is a sharable object

• Is typically used to create a primary key value

• Replaces application code

• Speeds up the efficiency of accessing sequence values when cached in memory

Setting Up the Oracle DatabaseHow do you create an Oracle SEQUENCE object? You use the CREATE SEQUENCE statement. The followingis a general format for defining a SEQUENCE object to generate sequential numbers automatically:

CREATE SEQUENCE sequence-name[INCREMENT BY n][START WITH n][{MAXVALULE n | NOMAXVALUE}][{MINVALUE n | NOMINVALUE}][{CYCLE | NOCYCLE}][{CACHE n | NOCACHE}];

The following example shows how to create a SEQUENCE object:

CREATE SEQUENCE ANIMAL_ID_SEQINCREMENT BY 1START WITH 1MAXVALUE 10000NOCACHENOCYCLE;

Note that Oracle sequences cannot be accessed directly and can be retrieved only by using theCURRVAL (current value) and NEXTVAL (next value) pseudocolumns. Also, a sequence must be first accessedwith NEXTVAL before CURRVAL can be used. For example, if a sequence name is ANIMAL_ID_SEQ (definedin the next section), then ANIMAL_ID_SEQ.NEXTVAL will generate the next sequence number, and ANIMAL_ID_SEQ.CURRVAL will provide the last generated sequence number.

$ sqlplus mp/mp2SQL*Plus: Release 9.2.0.1.0 - Production on Mon Oct 6 16:34:50 2003Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.

SQL> CREATE SEQUENCE ANIMAL_ID_SEQ2 INCREMENT BY 13 START WITH 14 MAXVALUE 100005 NOCACHE6 NOCYCLE;

Sequence created.

5203ch11.qxd 8/11/05 4:33 PM Page 392

Page 422: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 393

SQL> desc user_sequences;Name Null? Type----------------------------------------- -------- -------------SEQUENCE_NAME NOT NULL VARCHAR2(30)MIN_VALUE NUMBERMAX_VALUE NUMBERINCREMENT_BY NOT NULL NUMBERCYCLE_FLAG VARCHAR2(1)ORDER_FLAG VARCHAR2(1)CACHE_SIZE NOT NULL NUMBERLAST_NUMBER NOT NULL NUMBER

SQL> select SEQUENCE_NAME, MIN_VALUE, MAX_VALUE,INCREMENT_BY, LAST_NUMBERfrom user_sequences;

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY LAST_NUMBER--------------- ---------- ---------- ------------ -----------ANIMAL_ID_SEQ 1 10000 1 1

Oracle Table Creation and Population with ANIMAL_ID_SEQThis shows how to create an Oracle table and populate it with ANIMAL_ID_SEQ:

$ sqlplus mp/mp2SQL*Plus: Release 9.2.0.1.0 - Production on Tue Oct 7 11:43:49 2003Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.

SQL> CREATE TABLE animals_table (2 id INT NOT NULL,3 name VARCHAR(32) NOT NULL,4 PRIMARY KEY (id)5 );

Table created.

SQL> desc animals_table;Name Null? Type---------------- -------- ------------ID NOT NULL NUMBER(38)NAME NOT NULL VARCHAR2(32)

SQL> insert into animals_table(id, name)values(ANIMAL_ID_SEQ.nextval, 'dog');

SQL> insert into animals_table(id, name)values(ANIMAL_ID_SEQ.nextval, 'cat');

SQL> insert into animals_table(id, name)values(ANIMAL_ID_SEQ.nextval, 'rabbit');

SQL> commit;Commit complete.

SQL> select * from animals_table;

5203ch11.qxd 8/11/05 4:33 PM Page 393

Page 423: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT394

ID NAME---------- ---------

1 dog2 cat3 rabbit

SolutionThe JDBC 3.0 specification proposes a functional Statement interface that provides access to getautomatically generated key values after an insert. The following code examples demonstrate howto retrieve Oracle’s sequence values using the new JDBC 3.0 method getGeneratedKeys(), which isnow the preferred method to use if you need to retrieve automatically generated keys.

■Caution The Oracle JDBC driver does not support access to automatically generated key values after an insert.In other words, the Oracle driver does not implement Statement.getGeneratedKeys());. Instead, you have touse the Statement.executeQuery(); method, as follows:

rs = stmt.executeQuery("select ANIMAL_ID_SEQ.currval from dual");

Viewing the Oracle Database Before the Running Solution

This is the Oracle database before running the solution:

SQL> select * from animals_table;

ID NAME---------- ----------

1 dog2 cat3 rabbit

Running the Solution for the Oracle Database

This shows how to run the solution:

$ javac DemoGetGeneratedKeys.java$ java DemoGetGeneratedKeys oracle--DemoGetGeneratedKeys begin--conn=oracle.jdbc.driver.OracleConnection@18fb1f7---------------key 1 is 4--DemoGetGeneratedKeysUsingCurrval_Oracle end--

Viewing the Oracle Database After Running the Solution

This is the Oracle database after running the solution:

SQL> select * from animals_table;

ID NAME---------- -----------

1 dog2 cat3 rabbit4 tiger11

5203ch11.qxd 8/11/05 4:33 PM Page 394

Page 424: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 395

11-10. How Do You Determine Whether a SQL Warning Occurred(Using Statement Objects)?Some database operations can cause a warning that is not handled by an exception. You must checkfor these warnings explicitly. An example of such a warning is a data truncation error during a readoperation. You should check for warnings in three places: on a Connection object, a Statement object,and a ResultSet object. A SQL Warning (in JDBC defined by the java.sql.SQLWarning class) is anexception that provides information about database access warnings. Warnings are silently chainedto the object whose method caused it to be reported.

The following example demonstrates how to check for a warning on a Statement object:

Connection conn = null;SQLWarning warning = null;try {

// get a Connection objectconn = getConnection();

// Create a statementStatement stmt = conn.createStatement();

// Use the statement...such as selecting some rows from a table

// Get warnings on Statement objectwarning = stmt.getWarnings();while (warning != null) {

// Process statement warnings...String message = warning.getMessage();String sqlState = warning.getSQLState();int errorCode = warning.getErrorCode();warning = warning.getNextWarning();

}}catch (SQLException e) {

// handle exception...}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-11. How Do You Set the Number of Rows to Prefetch (UsingStatement Objects)?JDBC enables you to set the number of rows to prefetch when executing a SQL query. Row prefetchallows the user to specify the number of rows to fetch from the result set (the ResultSet object) ineach round-trip to the database. This is one possible way that you can control how many rows arereturned. When a SQL query is executed, the number of rows of data that a driver physically copiesfrom the database to the client is called the fetch size. If you are performance-tuning a particularquery, you might be able to improve the performance by adjusting the fetch size to better match theuse of the query.

You can set the fetch size on a Statement object; in this case, all result sets created from thatstatement will use that fetch size. You can also set the fetch size on a result set at any time. In thiscase, the next time data needs to be fetched from the database, the driver will copy over as manyrows as is specified by the current fetch size.

5203ch11.qxd 8/11/05 4:33 PM Page 395

Page 425: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT396

The following example demonstrates how to set a fetch size using a Statement object:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

// Get the fetch size of a statementstmt = conn.createStatement();int fetchSize = stmt.getFetchSize();

// Set the fetch size on the statement// each driver might have a different default value for fetch sizestmt.setFetchSize(200);

// Create a result setrs = stmt.executeQuery("SELECT badge_number FROM employee_table");

// Change the fetch size on the result setrs.setFetchSize(400);

}catch (SQLException e) {

// handle the exception}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-12. How Do You Create a MySQL Table to Store Java Types(Using Statement Objects)?The following example creates a MySQL table, called mysql_all_types_table, to store Java data types:

Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

stmt = conn.createStatement();

StringBuffer allTypesTable = new StringBuffer("CREATE TABLE mysql_all_types(");// Column Name MySQL Type Java TypeallTypesTable.append("column_boolean BOOL, " // booleanallTypesTable.append("column_byte TINYINT, " // byteallTypesTable.append("column_short SMALLINT, " // shortallTypesTable.append("column_int INTEGER, " // intallTypesTable.append("column_long BIGINT, " // longallTypesTable.append("column_float FLOAT, " // floatallTypesTable.append("column_double DOUBLE PRECISION, " // doubleallTypesTable.append("column_bigdecimal DECIMAL(13,0), " // BigDecimalallTypesTable.append("column_string VARCHAR(254), " // StringallTypesTable.append("column_date DATE, " // DateallTypesTable.append("column_time TIME, " // Time

5203ch11.qxd 8/11/05 4:33 PM Page 396

Page 426: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 397

allTypesTable.append("column_timestamp TIMESTAMP, " // TimestampallTypesTable.append("column_asciistream1 TINYTEXT, " // Clob (< 2^8 bytes)allTypesTable.append("column_asciistream2 TEXT, " // Clob (< 2^16 bytes)allTypesTable.append("column_asciistream3 MEDIUMTEXT, " // Clob (< 2^24 bytes)allTypesTable.append("column_asciistream4 LONGTEXT, " // Clob (< 2^32 bytes)allTypesTable.append("column_blob1 TINYBLOB, " // Blob (< 2^8 bytes)allTypesTable.append("column_blob2 BLOB, " // Blob (< 2^16 bytes)allTypesTable.append("column_blob3 MEDIUMBLOB, " // Blob (< 2^24 bytes)allTypesTable.append("column_blob4 LONGBLOB)"; // Blob (< 2^32 bytes)

stmt.executeUpdate(allTypesTable.toString());// creation of table ok.

}catch (SQLException e) {

// creation of table failed.// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-13. How Do You Create an Oracle Table to Store Java Types(Using Statement Objects)?The Oracle database does not support the primitive boolean Java type directly. A typical workaroundis to use the Oracle CHAR(1) type and convert the boolean value to and from a string. Typically, falseis converted to F, and true is converted to T. The following example creates an Oracle table calledoracle_all_types_table to store Java types. The Oracle database has another restriction for tables:it allows at most one column of the LONG type in a table.

Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

stmt = conn.createStatement();

// Create a VARRAY typestmt.execute("CREATE TYPE char_varray AS VARRAY(10) OF VARCHAR(20)");

// Create an OBJECT typestmt.execute ("CREATE TYPE oracle_object AS OBJECT" +

"(column_string VARCHAR(128), column_integer INTEGER)");

StringBuffer allTypesTable = new StringBuffer("CREATE TABLE oracle_all_types(");// Column Name Oracle Type Java TypeallTypesTable.append("column_short SMALLINT, " // shortallTypesTable.append("column_int INTEGER, " // intallTypesTable.append("column_float REAL, " // floatallTypesTable.append("column_double DOUBLE PRECISION," // doubleallTypesTable.append("column_bigdecimal DECIMAL(13,0), " // BigDecimalallTypesTable.append("column_string VARCHAR2(254), " // StringallTypesTable.append("column_charStream LONG, " // CharacterStreamallTypesTable.append("column_bytes RAW(2000), " // byte[]

5203ch11.qxd 8/11/05 4:33 PM Page 397

Page 427: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT398

allTypesTable.append("column_binarystream RAW(2000), " // BinaryStreamallTypesTable.append("column_timestamp DATE, " // TimestampallTypesTable.append("column_clob CLOB, " // CloballTypesTable.append("column_blob BLOB, " // BloballTypesTable.append("column_bfile BFILE, " // oracle.sql.BFILEallTypesTable.append("column_array char_varray, " // oracle.sql.ARRAYallTypesTable.append("column_object oracle_object)"; // oracle.sql.OBJECT

stmt.executeUpdate(allTypesTable.toString());// creation of table ok.

}catch (SQLException e) {

// creation of table failed.// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-14. How Do You Get Rows from a Database Table Usinga Statement?A SQL SELECT query gets data from a table. The result of the SELECT query is called a result set(expressed as a java.sql.ResultSet object). The following example executes a simple SQL SELECTquery and creates a result set.

Setting Up the DatabaseHere’s how to set up the database:

$ mysql --user=root --password=rootmysql> use test;Database changedmysql> desc employee_table;+--------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+--------------+-------------+------+-----+---------+-------+| badge_number | int(11) | | | 0 | || last_name | varchar(32) | | | | |+--------------+-------------+------+-----+---------+-------+2 rows in set (0.00 sec)

SolutionHere’s the solution:

Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

// get a Connection objectconn = getConnection();

// create a Statement objectstmt = conn.createStatement();

5203ch11.qxd 8/11/05 4:33 PM Page 398

Page 428: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 399

// create a result set objectString query = "SELECT badge_number, last_name FROM employee_table";rs = stmt.executeQuery(query);

// now we may iterate result set object, and get all the rowswhile (rs.next()) {

int badgeNumber = rs.getInt(1); // get the badge_numberString lastName = rs.getString(2); // get the last_name

}}catch (SQLException e) {

// getting rows failed// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-15. How Do You Insert a Row into a Database Table Usinga Statement?The following code shows how to insert a row into a database table using a Statement object:

Connection conn = null;Statement stmt = null;try {

conn = getConnection(); // get a Connection objectstmt = conn.createStatement(); // create a Statement object

// Prepare a statement to insert a recordString sql = "INSERT INTO employee_table (badge_number, last_name) " +

"VALUES('1122', 'alex smith')";

// Execute the insert statementstmt.executeUpdate(sql);

}catch (SQLException e) {

// insert failed// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-16. How Do You Update a Row in a Database Table Usinga Statement?The SQL UPDATE statement modifies the data in a table. The basic syntax is as follows:

UPDATE table_nameSET column_name = new_valueWHERE column_name = some_value

The following example updates a row in a table:

5203ch11.qxd 8/11/05 4:33 PM Page 399

Page 429: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT400

Connection conn = null;Statement stmt = null;try {

// get a Connection objectconn = getConnection();

stmt = conn.createStatement();

// Prepare a statement to update a recordString sql = "UPDATE employee_table "+"SET last_name='mary taylor' WHERE badge_number = '5555'";

// Execute the insert statementint updateCount = stmt.executeUpdate(sql);// updateCount contains the number of updated rows

}catch (SQLException e) {

// update failed// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-17. How Do You Delete All Rows from a Database Table Usinga Statement?You can delete all the rows in a table by using SQL’s DELETE statement. This example deletes all therows from a database table called employee_table:

Connection conn = null;Statement stmt = null;try {

// get a Connection objectconn = getConnection();

stmt = conn.createStatement();

// use SQL DELETEString tableName = "employee_table";String sql = "DELETE FROM " + tableName;

// Execute deletionstmt.executeUpdate(sql);

}catch (SQLException e) {

// deletion failed// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

5203ch11.qxd 8/11/05 4:33 PM Page 400

Page 430: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 401

11-18. How Do You Get the Number of Rows for a DatabaseTable Using a Statement?To count all the rows for a table, you can use the following SQL command:

SELECT COUNT(*) from <table-name>

This example gets the number of rows in a table using this SQL statement:

ResultSet rs = null;Statement stmt = null;Connection conn = null;int rowCount = -1; // nonexistent valuetry {

// get a Connection objectconn = getConnection();

// Select the number of rows in the tablestmt = conn.createStatement();rs = stmt.executeQuery("SELECT COUNT(*) FROM employee_table");

// Get the number of rows from the result setif (rs.next()) {

rowcount = rs.getInt(1);}else {

// error: could not get the number of rows}

if (rsCount == -1) {// error: could not get the number of rows

}}catch (SQLException e) {

// counting rows failed// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-19. How Do You Insert Binary Data into a Database TableUsing a Statement?It is possible to insert binary data with a Statement object (you can convert your binary data intoa stream of String objects and then insert it using a Statement object), but it is much easier (and rec-ommended) to insert binary data using PreparedStatement, which extends the Statement interface.

The following example inserts binary data into a table:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

5203ch11.qxd 8/11/05 4:33 PM Page 401

Page 431: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT402

// prepare a statement to insert binary dataString sql = "INSERT INTO mysql_all_table (col_binarystream) VALUES(?)";PreparedStatement pstmt = conn.prepareStatement(sql);

// create some binary dataString myData = "some string data ...";byte[] binaryData = myData.getBytes();

// set value for the prepared statementpstmt.setBytes(1, binaryData);

// insert the datapstmt.executeUpdate();

// insert was successful}catch (SQLException e) {

// insert failed// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

11-20. How Do You Get Binary Data from a Database Table?This example retrieves binary data from a table:

ResultSet rs = null;Statement stmt = null;Connection conn = null;try {

// get a Connection objectconn = getConnection();

// select records from the tablestmt = conn.createStatement();String query = "SELECT col_binarystream FROM mysql_all_table";rs = stmt.executeQuery(query);while (rs.next()) {

// get data from the binary columnbyte[] bytes = rs.getBytes(1);// process bytes

}}catch (SQLException e) {

// retrieving binary data failed// handle the exception

}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

5203ch11.qxd 8/11/05 4:33 PM Page 402

Page 432: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 403

11-21. How Do You Execute a Batch of SQL Statements ina Database Using a Statement?The java.sql package provides the ability to send multiple updates (such as updating records orcreating new records) to the database server for execution as a batch. The Statement.addBatch()method enables you to accomplish this task, which might improve the performance of the entiretransaction. You can reduce the amount of time it takes to perform repetitive inserts and updates ifyou batch them using the Statement object’s addBatch() method.

The signature of addBatch() is as follows:

public void addBatch(String sql)throws SQLException

This adds the given SQL command to the current list of commands for this Statement object.The commands in this list can be executed as a batch by calling the method executeBatch. Itsparameter is sql; typically this is a static SQL INSERT or UPDATE statement. This throws SQLExceptionif a database access error occurs.

Both the Oracle and MySQL drivers support the Statement.addBatch() and PreparedStatement.addBatch() methods, which send multiple updates to the database server. With batch updating, a setof SQL statements is assembled and then sent altogether to the database server for execution. Batchupdating can improve performance. Using the batch functionality involves two methods (the descrip-tions of the methods are from the JDK documentation):

addBatch(String sql): Adds the given SQL command to the current list of commands for thisStatement object. The commands in this list can be executed as a batch by calling the methodexecuteBatch().

executeBatch(): Submits a batch of commands to the database for execution and, if all commandsexecute successfully, returns an array of update counts. The int elements of the returned arrayare ordered to correspond to the commands in the batch, which are ordered according to howthey were added to the batch. The elements in the array returned by the method executeBatchmay be one of the following:

• A number greater than or equal to zero: Indicates that the command was processedsuccessfully and is an update count giving the number of rows in the database thatwere affected by the command’s execution.

• A value of SUCCESS_NO_INFO: Indicates that the command was processed successfully butthat the number of rows affected is unknown. If one of the commands in a batch updatefails to execute properly, this method throws a BatchUpdateException, and a JDBC drivermay or may not continue to process the remaining commands in the batch. However, thedriver’s behavior must be consistent with a particular DBMS, either always continuingto process commands or never continuing to process commands. If the driver continuesprocessing after a failure, the array returned by the method BatchUpdateException.getUpdateCounts will contain as many elements as there are commands in the batch,and at least one of the elements will be a value of EXECUTE_FAILED, which indicates thatthe command failed to execute successfully and occurs only if a driver continues toprocess commands after a command fails.

This example creates a batch of insert statements. Note that autocommit is disabled, so youhave the choice of committing or not committing in the event of an exception.

Statement stmt = null;Connection conn = null;

5203ch11.qxd 8/11/05 4:33 PM Page 403

Page 433: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT404

try {// get a Connection objectconn = getConnection();

// disable autocommitconn.setAutoCommit(false);

// the number of statements that can be batched depends on a specific drivestmt = conn.createStatement();stmt.addBatch("DELETE FROM animals_table");stmt.addBatch("INSERT INTO animals_table(id, name) VALUES(111, 'ginger')");stmt.addBatch("INSERT INTO animals_table(id, name) VALUES(222, 'lola')");stmt.addBatch("INSERT INTO animals_table(id, name) VALUES(333, 'freddy')");

// Execute the batchint[] updateCounts = stmt.executeBatch();

// All statements were successfully executed.// updateCounts array contains one element for// each batched statement; updateCounts[i] contains// the number of rows affected by that statement.checkUpdateCounts(updateCounts);

// Since there were no errors, commitconn.commit();

}catch (BatchUpdateException e) {

// Not all of the statements were successfully executedint[] updateCounts = e.getUpdateCounts();

// Some databases will continue to execute after one fails.// If so, updateCounts.length will equal the number of// batched statements. If not, updateCounts.length will// equal the number of successfully executed statementscheckUpdateCounts(updateCounts);

// Either commit the successfully executed statements// or roll back the entire batchconn.rollback();

}catch (SQLException e) {

// handle the exception}finally {

// close database/JDBC resources such as// ResultSet(s), Statement(s), and Connection(s)

}

...

public static void checkUpdateCounts(int[] updateCounts) {for (int i=0; i < updateCounts.length; i++) {

if (updateCounts[i] >= 0) {// Successfully executed; the number represents number of affected rowsSystem.out.println("Succ. executed; updateCount="+updateCounts[i]);

}else if (updateCounts[i] == Statement.SUCCESS_NO_INFO) {

5203ch11.qxd 8/11/05 4:33 PM Page 404

Page 434: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 405

// Successfully executed; number of affected rows not availableSystem.out.println("Succ. executed; "+

"updateCount=Statement.SUCCESS_NO_INFO");}else if (updateCounts[i] == Statement.EXECUTE_FAILED) {

// Failed to executeSystem.out.println("Failed to execute; "+

updateCount=Statement.EXECUTE_FAILED");}

}}

Setting Up the Oracle DatabaseThis example will use the animals_table table defined earlier in this chapter.

SQL> CREATE TABLE animals_table (2 id INT NOT NULL,3 name VARCHAR(32) NOT NULL,4 PRIMARY KEY (id)5 );

Table created.SQL> desc animals_table;Name Null? Type---------------- -------- ------------ID NOT NULL NUMBER(38)NAME NOT NULL VARCHAR2(32)

SolutionThis class will delete all the existing records from the animals_table table and then insert the records(111, 'ginger'), (222, 'lola'), and (333, 'freddy') into the animals_table table using theStatement.addBatch() method:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.VeryBasicConnectionManager;import jcb.util.DatabaseUtil;

public class Demo_Statement_AddBatch {

public static void checkUpdateCounts(int[] updateCounts) {for (int i=0; i<updateCounts.length; i++) {

if (updateCounts[i] >= 0) {// Successfully executed; the number// represents number of affected rowsSystem.out.println("OK; updateCount="+updateCounts[i]);

}else if (updateCounts[i] == Statement.SUCCESS_NO_INFO) {

// Successfully executed; number of// affected rows not availableSystem.out.println("OK; updateCount=Statement.SUCCESS_NO_INFO");

}else if (updateCounts[i] == Statement.EXECUTE_FAILED) {

// Failed to execute

5203ch11.qxd 8/11/05 4:33 PM Page 405

Page 435: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT406

}}

}

public static void main(String[] args) {Connection conn = null;Statement stmt = null;String dbVendor = args[0];try {

System.out.println("--Demo_Statement_AddBatch begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);

// Disable autocommitconn.setAutoCommit(false);

// create Statement objectstmt = conn.createStatement();stmt.addBatch("DELETE FROM animals_table");stmt.addBatch("INSERT INTO animals_table(id, name) "+

"VALUES(111, 'ginger')");stmt.addBatch("INSERT INTO animals_table(id, name) "+

"VALUES(222, 'lola')");stmt.addBatch("INSERT INTO animals_table(id, name) "+

"VALUES(333, 'freddy')");

// Execute the batchint[] updateCounts = stmt.executeBatch();

// all statements were successfully executed.// updateCounts contains one element for each// batched statement. The updateCounts[i] contains// the number of rows affected by that statement.checkUpdateCounts(updateCounts);

// since there were no errors, commitconn.commit();

}catch (BatchUpdateException e) {

// Not all of the statements were successfully executedint[] updateCounts = e.getUpdateCounts();

// Some databases will continue to execute after one// fails. If so, updateCounts.length will equal the// number of batched statements. If not, updateCounts.length// will equal the number of successfully executed statementscheckUpdateCounts(updateCounts);

// Either commit the successfully executed// statements or roll back the entire batchtry {

conn.rollback();}catch (Exception e2) {

e.printStackTrace();System.exit(1);

5203ch11.qxd 8/11/05 4:33 PM Page 406

Page 436: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 407

}}catch (Exception e) {

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

Running the Solution for the Oracle DatabaseThis shows how to run the solution (Oracle):

$ javac Demo_Statement_AddBatch.java$ java Demo_Statement_AddBatch oracle--Demo_Statement_AddBatch begin--conn=oracle.jdbc.driver.T4CConnection@6e293a---------------Successfully executed; updateCount=5Successfully executed; updateCount=1Successfully executed; updateCount=1Successfully executed; updateCount=1

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from animals_table;

ID NAME--- -------111 ginger222 lola333 freddy

Setting Up the MySQL DatabaseThis example will use the animals_table table defined earlier in this chapter:

mysql> CREATE TABLE animals_table (-> id INT NOT NULL AUTO_INCREMENT,-> name VARCHAR(32) NOT NULL,-> PRIMARY KEY (id)-> );

Query OK, 0 rows affected (0.03 sec)

mysql> desc animals_table;+-------+-------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+----------------+| id | int(11) | | PRI | NULL | auto_increment || name | varchar(32) | | | | |+-------+-------------+------+-----+---------+----------------+2 rows in set (0.01 sec)

5203ch11.qxd 8/11/05 4:33 PM Page 407

Page 437: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT408

Viewing the Program to Use Statement.addBatch()This class will delete all the existing records from the animals_table table and then insert the records(111, 'ginger'), (222, 'lola'), and (333, 'freddy') into the animals_table table using theaddBatch() method.

Running the Solution for the MySQL DatabaseThis shows how to run the solution (MySQL):

$ javac Demo_Statement_AddBatch.java$ java Demo_Statement_AddBatch mysql--Demo_Statement_AddBatch begin--conn=com.mysql.jdbc.Connection@1546e25---------------Successfully executed; updateCount=2Successfully executed; updateCount=1Successfully executed; updateCount=1Successfully executed; updateCount=1

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from animals_table;+-----+--------+| id | name |+-----+--------+| 111 | ginger || 222 | lola || 333 | freddy |+-----+--------+3 rows in set (0.03 sec)

11-22. How Do You Create an OBJECT Type in an Oracle Database?In the Oracle database, you can create a user-defined composite data structure called an OBJECT. (Thisis a proprietary feature of Oracle database and should not be confused with the java.lang.Objectclass.) The OBJECT structure consists of one or more basic types. For example, you could define anobject called EMPLOYEE_TYPE with a name (VARCHAR2) and a badge_number (NUMBER). An OBJECT can alsocontain other OBJECT structures. The following example creates an EMPLOYEE_TYPE as an Oracle OBJECTand then creates an employee table using the EMPLOYEE_TYPE (as a data type for the first column).

Creating an Oracle OBJECT Using Oracle’s SQL*PlusThis shows how to create the OBJECT structure using SQL*Plus:

$ sqlplus mp/mp2SQL*Plus: Release 9.2.0.1.0 - Production on Mon Nov 17 13:17:01 2003

SQL> CREATE OR REPLACE TYPE employee_type IS OBJECT2 (name VARCHAR2(32),3 badge_number NUMBER);4 /

Type created.

5203ch11.qxd 8/11/05 4:33 PM Page 408

Page 438: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 409

SQL> desc employee_type;Name Null? Type--------------------- -------- ------------NAME VARCHAR2(32)BADGE_NUMBER NUMBER

SQL> create table employee (2 emp employee_type,3 age NUMBER );

Table created.

SQL> desc employee;Name Null? Type---------------- -------- ---------------EMP EMPLOYEE_TYPEAGE NUMBER

SQL> insert into employee(emp, age)2 values ( EMPLOYEE_TYPE('alex', 1234), 24);

SQL> insert into employee(emp, age)2 values ( EMPLOYEE_TYPE('david', 7777), 35);

SQL> commit;Commit complete.

SQL> select * from employee;

EMP(NAME, BADGE_NUMBER) AGE--------------------------- ---EMPLOYEE_TYPE('alex', 1234) 24EMPLOYEE_TYPE('david', 7777) 35

Creating an Oracle OBJECT Using JDBCThis shows how to create the OBJECT structure using JDBC:

Connection conn = null;Statement stmt = null;try {

// create Connection objectconn = getConnection();System.out.println("conn="+conn);

// create Statement objectstmt = conn.createStatement();

// Create the EMPLOYEE_TYPE OBJECTstmt.execute("CREATE TYPE employee_type IS "+

"OBJECT (name VARCHAR2(32), badge_number NUMBER)");

// Create a table with a column to hold a new employee_type OBJECTstmt.execute("create table employee(emp employee_type, age NUMBER)");

}catch (SQLException e) {

5203ch11.qxd 8/11/05 4:33 PM Page 409

Page 439: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT410

// handle the exception}finally {

// close and clean up database/JDBC resources}

11-23. How Do You Insert an OBJECT Value into an Oracle Table?You can insert Oracle’s OBJECT (which is a proprietary feature of the Oracle database and should notbe confused with the java.lang.Object class) into an Oracle table using Oracle’s SQL*Plus and JDBC.The following sections show examples.

Inserting an OBJECT Value into an Oracle Table Using SQL*PlusThis shows how to insert the OBJECT value using SQL*Plus:

SQL> select * from employee;

no rows selected

SQL> insert into employee(emp, age)2 values ( EMPLOYEE_TYPE('alex smith', 1122), 45);

SQL> insert into employee(emp, age)2 values ( EMPLOYEE_TYPE('bob taylor', 1155), 26);

SQL> commit;Commit complete.

SQL> select * from employee;

EMP(NAME, BADGE_NUMBER) AGE--------------------------------- ---EMPLOYEE_TYPE('alex smith', 1122) 45EMPLOYEE_TYPE('bob taylor', 1155) 26

Inserting an OBJECT Value into an Oracle Table Using JDBCThis example inserts two rows into a table with a column that contains an OBJECT type (that is,employee_type OBJECT). The example uses the employee table defined earlier.

Connection conn = null;Statement stmt = null;try {

// create Connection objectconn = getConnection();System.out.println("conn="+conn);

// create Statement objectstmt = conn.createStatement();

// Create the EMPLOYEE_TYPE OBJECT// stmt.execute("CREATE TYPE employee_type IS OBJECT " +// "(name VARCHAR2(32), badge_number NUMBER)");

// Create a table with a column to hold a new employee_type OBJECT// stmt.execute("create table employee(emp employee_type, age NUMBER)");

5203ch11.qxd 8/11/05 4:33 PM Page 410

Page 440: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 411

// insert the two new records:stmt.execute("insert into employee(emp, age) values "+

"( EMPLOYEE_TYPE('alex smith', 1122), 45)");stmt.execute("insert into employee(emp, age) values "+

"( EMPLOYEE_TYPE('bob taylor', 1155), 26)");}catch (SQLException e) {

// handle the exception}finally {

// clean up}

11-24. How Do You Get an Oracle’s OBJECT Value from an OracleTable?Suppose you want to retrieve values contained in an Oracle OBJECT type. The following examplesshow how to retrieve (using SQL*Plus and JDBC) all the records from employee_table.

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> desc employee_type;Name Null? Type---------------- -------- -------------NAME VARCHAR2(32)BADGE_NUMBER NUMBER

SQL>SQL> create table employee (2 emp employee_type,3 age NUMBER );

Table created.

SQL> desc employee;Name Null? Type---------------- -------- --------------EMP EMPLOYEE_TYPEAGE NUMBER

SQL> insert into employee(emp, age)2 values ( EMPLOYEE_TYPE('alex smith', 1122), 45);

SQL> insert into employee(emp, age)2 values ( EMPLOYEE_TYPE('bob taylor', 1155), 26);

SQL> commit;Commit complete.

SQL> select * from employee;

EMP(NAME, BADGE_NUMBER) AGE--------------------------------- ---EMPLOYEE_TYPE('alex smith', 1122) 45EMPLOYEE_TYPE('bob taylor', 1155) 26

5203ch11.qxd 8/11/05 4:33 PM Page 411

Page 441: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT412

SolutionThe Demo_Statement_GetObjectValues_Oracle class retrieves OBJECT values. Note that Oracle’s JDBCimplementation maps NUMBER types to the java.math.BigDecimal objects.

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.*;import jcb.meta.*;

public class Demo_Statement_GetObjectValues_Oracle {

public static Connection getConnection() throws Exception {String driver = "oracle.jdbc.driver.OracleDriver";String url = "jdbc:oracle:thin:@localhost:1521:caspian";String username = "mp";String password = "mp2";Class.forName(driver); // load Oracle driverreturn DriverManager.getConnection(url, username, password);

}

public static void main(String[] args) {Connection conn = null;Statement stmt = null;ResultSet rs = null;try {

System.out.println("--Demo_Statement_GetObjectValues_Oracle begin--");conn = getConnection();System.out.println("conn="+conn);System.out.println("---------------");

// create Statement objectstmt = conn.createStatement();

// Select rows from the employee table// note that emp is an EMPLOYEE_TYPE objectrs = stmt.executeQuery("SELECT emp, age FROM employee");

// Get the OBJECT values from each rowwhile (rs.next()) {

// Handle the first column:// Get the EMPLOYEE_TYPE value from the first column emporacle.sql.STRUCT emp = (oracle.sql.STRUCT) rs.getObject(1);

// Get the emp values from each rowObject[] empValues = emp.getAttributes();

// Get the values of empString name = (String) empValues[0];java.math.BigDecimal badgeNumber =

(java.math.BigDecimal) empValues[1];

// Handle the second column:// Get the age from the second column employee of the rowint age = rs.getInt(2);

5203ch11.qxd 8/11/05 4:33 PM Page 412

Page 442: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 413

System.out.println("name="+name);System.out.println("badgeNumber="+badgeNumber);System.out.println("age="+age);System.out.println("----------------------");

}}catch (Exception e) {

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(stmt);DatabaseUtil.close(conn);

}}

}

To run the solution for an Oracle database, use this code:

$ javac Demo_Statement_GetObjectValues_Oracle.java$ java Demo_Statement_GetObjectValues_Oracle--Demo_Statement_GetObjectValues_Oracle begin--conn=oracle.jdbc.driver.OracleConnection@860d49---------------name=alex smithbadgeNumber=1122age=45----------------------name=bob taylorbadgeNumber=1155age=26----------------------

11-25. How Do You Delete an OBJECT Type from an Oracle Table?The following example deletes the Oracle OBJECT structures and tables created in the earlier sections.

Connection conn = null;Statement stmt = null;try {

// create Connection objectconn = getConnection();System.out.println("conn="+conn);

// create Statement objectstmt = conn.createStatement();

// drop table employee and type employee_type// Oracle's FORCE means ignore referential integritystmt.execute("DROP TABLE employee");stmt.execute("DROP TYPE employee_type FORCE");

}catch (SQLException e) {

// handle the exception}

5203ch11.qxd 8/11/05 4:33 PM Page 413

Page 443: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT414

finally {// clean up

}

11-26. How Does JDBC Support Batch Updates?In relational databases, some applications require multiple updates/inserts for certain operations(batch updates). For example, mapping a tree data structure to a relational model is one of them.For details on mapping trees to SQL, see Joe Celko’s Trees and Hierarchies in SQL for Smarties(Morgan Kaufmann, 2004). A tree data structure has lots of uses, such as representing a hierarchyof employees and folders (such as file and directory systems). Trees in SQL require multiple SQLoperations (such as adding a new node, deleting an existing node, or deleting a subtree) that aregood candidates for the Statement.batchUpdate() operation. With several examples (working witha tree structure in SQL), I will demonstrate how to use the Statement.batchUpdate() properly.

Both the Oracle and MySQL databases support the Statement.addBatch() and Statement.batchUpdate() methods, which send multiple updates to the database server. With batch updating,a set of SQL statements is assembled and then sent altogether to the database server for execution.Batch updating can improve performance.

By using the Statement.batchUpdate() method, you can reduce the number of round-trips tothe database, thereby improving database application performance. This allows you to group multi-ple SQL UPDATE, SQL DELETE, or SQL INSERT statements into a single batch and have the whole batchsent to the database and processed in one trip. In high-volume database applications, wheneverpossible, you should batch update to avoid multiple trips (over the network) to database servers.

How does JDBC support batch operations? The java.sql package provides the ability to sendmultiple updates (such as updating existing records or creating new records) to the database serverfor execution as a batch. The Statement.addbatch() method enables you to accomplish this task,which might improve the performance of the entire transaction. You can reduce the amount of timeit takes to perform repetitive inserts and updates if you batch them using the Statement object’saddBatch() method.

Using the batch functionality involves two methods (the description of the methods are fromthe JDK 1.4 documentation): Statement.addBatch() and Statement.executeBatch() are used togetherto support SQL batch operations. The signature of Statement.addBatch() is as follows:

public void addBatch(String sql)throws SQLException

This adds the given SQL command to the current list of commands for this Statement object.The commands in this list can be executed as a batch by calling the method executeBatch.

The parameter is sql, which is typically a static SQL INSERT or UPDATE statement.This throws SQLException if a database access error occurs.The signature of Statement.executeBatch() is as follows:

public int[] executeBatch()throws SQLException

This submits a batch of commands to the database for execution and, if all commands executesuccessfully, returns an array of update counts. The int elements of the returned array are ordered tocorrespond to the commands in the batch, which are ordered according to how they were added to thebatch. The elements in the array returned by the method executeBatch can be one of the following:

A number greater than or equal to zero: Indicates that the command was processed successfullyand is an update count giving the number of rows in the database that were affected by thecommand’s execution.

5203ch11.qxd 8/11/05 4:33 PM Page 414

Page 444: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 415

A value of SUCCESS_NO_INFO: Indicates that the command was processed successfully but thatthe number of rows affected is unknown. If one of the commands in a batch update fails to exe-cute properly, this method throws a BatchUpdateException, and a JDBC driver may or may notcontinue to process the remaining commands in the batch. However, the driver’s behavior mustbe consistent with a particular DBMS, either always continuing to process commands or nevercontinuing to process commands. If the driver continues processing after a failure, the arrayreturned by the method BatchUpdateException.getUpdateCounts will contain as many elementsas there are commands in the batch, and at least one of the elements will be a value of EXECUTE_FAILED, which indicates that the command failed to execute successfully and occurs only if a drivercontinues to process commands after a command fails.

A driver is not required to implement this method. The possible implementations and returnvalues have been modified in version 1.3 of the Java 2 SDK, Standard Edition, to accommodate theoption of continuing to process commands in a batch update after a BatchUpdateException objecthas been thrown.

Statement.executeBatch() returns an array of update counts containing one element for eachcommand in the batch. The elements of the array are ordered according to the order in which com-mands were added to the batch.

This throws SQLException if a database access error occurs or the driver does not support batchstatements. This throws BatchUpdateException (a subclass of SQLException) if one of the commandssent to the database fails to execute properly or attempts to return a result set.

11-27. How Do You Map Trees into SQL Using Batch Updates?Mapping trees to a relational model is a common problem, with many solutions. Joe Celko has discussedtrees in SQL extensively (for details, refer to http://www.intelligententerprise.com/001020/celko.jhtml). Two major approaches exist: the adjacency list model and the modified preorder tree traversalalgorithm. Just like Joe Celko, I will show a preorder tree traversal algorithm (also known as a nested set).

Here I will focus on three specific tree operations:

• Inserting a new node (InsertNode.java)

• Deleting an existing node (DeleteNode.java)

• Deleting an existing subtree (DeleteSubtree.java)

Suppose you want to map the tree shown in Figure 11-1 to a relational model. (For a moment,ignore the numbers next to the names; you will use these numbers to define and implement the treeusing nested sets later in this chapter.)

5203ch11.qxd 8/11/05 4:33 PM Page 415

Page 445: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT416

Setting Up the DatabaseYou can prepare the database in such a way to map trees into SQL using nested sets. Using nestedsets, you need the following database schema and elements. Note that the root node’s parent (thatis, Albert’s parent) is null.

$ mysql --user=root --password=root --default-character-set=utf8mysql> use snipit;Database changedmysql> drop table folders;

mysql> create table folders (-> id varchar(10),-> parent varchar(10),-> lft int, -- left_pointer-> rgt int -- right_pointer-> );

mysql> insert into folders(id, parent, lft, rgt) values('Albert', null, 1, 12);mysql> insert into folders(id, parent, lft, rgt) values('Bert', 'Albert', 2, 3);mysql> insert into folders(id, parent, lft, rgt) values('Chuck', 'Albert', 4, 11);mysql> insert into folders(id, parent, lft, rgt) values('Donna', 'Chuck', 5, 6);mysql> insert into folders(id, parent, lft, rgt) values('Eddie', 'Chuck', 7, 8);mysql> insert into folders(id, parent, lft, rgt) values('Fred', 'Chuck', 9, 10);

mysql> desc folders;+--------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+--------+-------------+------+-----+---------+-------+| id | varchar(10) | YES | | NULL | || parent | varchar(10) | YES | | NULL | || lft | int(11) | YES | | NULL | || rgt | int(11) | YES | | NULL | |+--------+-------------+------+-----+---------+-------+4 rows in set (0.00 sec)

mysql> select * from folders;+--------+--------+------+------+| id | parent | lft | rgt |+--------+--------+------+------+| Albert | NULL | 1 | 12 || Bert | Albert | 2 | 3 || Chuck | Albert | 4 | 11 || Donna | Chuck | 5 | 6 || Eddie | Chuck | 7 | 8 || Fred | Chuck | 9 | 10 |+--------+--------+------+------+6 rows in set (0.00 sec)

Inserting a New Node (InsertNode.java)The objective of the InsertNode class is to insert a new node for an existing tree (identified by thefolders table). To insert a new node, you need to supply the parent. Therefore, you need two piecesof information for inserting a new node: the node to be inserted and the parent (where it’s to beinserted). So, the InsertNode class’s main() method will accept two arguments: the parentID and ID,where ID is the new node to be inserted and parentID is the ID of the parent for the new node.

5203ch11.qxd 8/11/05 4:33 PM Page 416

Page 446: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 417

To insert Mike under Bert, use this code:

C:\mp\sql-trees>java InsertNode Bert Mike------InsertNode begin---------conn=com.mysql.jdbc.Connection@750159parentID=BertID=Mike------InsertNode end---------

mysql> select * from folders;+---------+--------+------+------+| id | parent | lft | rgt |+---------+--------+------+------+| Albert | NULL | 1 | 14 || Bert | Albert | 2 | 5 || Chuck | Albert | 6 | 13 || Donna | Chuck | 7 | 8 || Eddie | Chuck | 9 | 10 || Fred | Chuck | 11 | 12 || Mike | Bert | 3 | 4 |+---------+--------+------+------+7 rows in set (0.00 sec)

To insert Mary under Bert, use this code:

$ java InsertNode Bert Mary------InsertNode begin---------conn=com.mysql.jdbc.Connection@750159parentID=BertID=Mary------InsertNode end---------

mysql> select * from folders;+---------+--------+------+------+| id | parent | lft | rgt |+---------+--------+------+------+| Albert | NULL | 1 | 16 || Bert | Albert | 2 | 7 || Chuck | Albert | 8 | 15 || Donna | Chuck | 9 | 10 || Eddie | Chuck | 11 | 12 || Fred | Chuck | 13 | 14 || Mike | Bert | 5 | 6 || Mary | Bert | 3 | 4 |+---------+--------+------+------+8 rows in set (0.00 sec)

To insert Jeff under Albert, use this code:

$ java InsertNode Albert Jeff------InsertNode begin---------conn=com.mysql.jdbc.Connection@750159parentID=AlbertID=Jeff------InsertNode end---------

mysql> select * from folders;

5203ch11.qxd 8/11/05 4:33 PM Page 417

Page 447: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT418

+---------+--------+------+------+| id | parent | lft | rgt |+---------+--------+------+------+| Albert | NULL | 1 | 18 || Bert | Albert | 4 | 9 || Chuck | Albert | 10 | 17 || Donna | Chuck | 11 | 12 || Eddie | Chuck | 13 | 14 || Fred | Chuck | 15 | 16 || Mike | Bert | 7 | 8 || Mary | Bert | 5 | 6 || Jeff | Albert | 2 | 3 |+---------+--------+------+------+9 rows in set (0.00 sec)

InsertNode.javaThis shows InsertNode.java:

1 import java.sql.Connection;2 import java.sql.Statement;3 import java.sql.PreparedStatement;4 import java.sql.ResultSet;5 import java.sql.DriverManager;6 import java.sql.BatchUpdateException;7 import java.sql.SQLException;8 import jcb.util.DatabaseUtil;9 public class InsertNode {1011 public static Connection getConnection() throws Exception {12 String driver = "org.gjt.mm.mysql.Driver";13 String url = "jdbc:mysql://localhost/snipit";14 String username = "root";15 String password = "root";16 Class.forName(driver); // load MySQL driver17 return DriverManager.getConnection(url, username, password);19 }2021 public static void main(String[] args) {23 Connection conn = null;24 ResultSet rs = null;25 Statement stmt = null;26 PreparedStatement pstmt = null;27 try {28 System.out.println("------InsertNode begin---------");2930 if (args.length != 2) {31 System.out.println("usage: InsertNode ");32 System.exit(1);33 }3435 conn = getConnection();36 System.out.println("conn="+conn);3738 String parentID = args[0];39 String ID = args[1]; // node with value of ID will be inserted40

5203ch11.qxd 8/11/05 4:33 PM Page 418

Page 448: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 419

41 System.out.println("parentID="+parentID);42 System.out.println("ID="+ID);4344 String getParentLeftRight =45 "select lft, rgt from folders where id = ?";

pstmt = conn.prepareStatement(getParentLeftRight);46 pstmt.setString(1, parentID);47 rs = pstmt.executeQuery();48 rs.next();49 int pLeft = rs.getInt(1);50 int pRight = rs.getInt(2);51 int pLeftPlus1 = pLeft + 1;52 int pLeftPlus2 = pLeft + 2;53 String update1 =54 "update folders set rgt = rgt + 2 where rgt > "+ pLeft;55 String update2 =56 "update folders set lft = lft + 2 where lft > "+ pLeft;57 String insert = "insert into folders (id, parent, lft, rgt)"+58 "values ('"+ID +"', '"+ parentID +"', 0, 0)";59 String update3 = "update folders set lft = "+pLeftPlus1+

", rgt = "+pLeftPlus2+ " where id = '"+ID+"'";// start transaction for batch updates

60 conn.setAutoCommit(false);61 stmt = conn.createStatement();6263 // create a set of batch operations64 stmt.addBatch(update1);65 stmt.addBatch(update2);66 stmt.addBatch(insert);67 stmt.addBatch(update3);6869 // send batch operations to the database server70 int[] batchUpdateCounts = stmt.executeBatch();7172 // commit transaction for batch updates73 conn.commit();74 conn.setAutoCommit(true);75 System.out.println("------InsertNode end---------");76 }77 catch(BatchUpdateException be) {78 System.err.println("--- caught BatchUpdateException ---");79 System.err.println("SQLState: " + be.getSQLState());80 System.err.println("Message: " + be.getMessage());81 System.err.println("Vendor: " + be.getErrorCode());82 System.err.print("Update counts are: ");83 int[] batchUpdateCounts = be.getUpdateCounts();84 for (int i = 0; i < batchUpdateCounts.length; i++) {85 System.err.print(batchUpdateCounts[i] + " ");86 }87 System.err.println("");88 }89 catch(SQLException se) {90 System.err.println("--- caught SQLException ---");91 System.err.println("SQLState: " + se.getSQLState());92 System.err.println("Message: " + se.getMessage());93 System.err.println("Vendor: " + se.getErrorCode());94 }

5203ch11.qxd 8/11/05 4:33 PM Page 419

Page 449: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT420

95 catch (Exception e) {96 // other exceptions97 e.printStackTrace();98 System.exit(1);99 }100 finally {101 // release database resources102 DatabaseUtil.close(rs);103 DatabaseUtil.close(pstmt);104 DatabaseUtil.close(stmt);105 DatabaseUtil.close(conn);106 }107 }108 }

Discussing InsertNode.javaThe following explains how the program works:

Lines 1–9: You import the required classes and interfaces.

Lines 11–19: The getConnection() method returns a java.sql.Connection object.

Lines 38–42: These lines get the command-line arguments parentID and ID (which is the nodeID to be inserted).

Lines 44–50: Here, you retrieve the left and right of the parentID. These values will be used forfurther processing.

Lines 54–57: These lines prepare SQL queries for batch updates.

Line 60: This lines starts the transaction for batch updates. No SQL operation will be commit-ted until you invoke the conn.commit() statement.

Line 61: Creates a generic Statement object to be used for batch updates.

Lines 64–67: Create a set of batch operations. The order of these batch operations is important,and the batch works as a queue (first-in, first-out).

Line 69: Sends batch operations to the database server (operations are not committed yet!).

Line 73: Commits transaction for batch updates (either all succeed or all fail—no partial success).

Lines 100–106: Release database resources (to free up memory and data structures used by theJDBC driver and database server).

Deleting an Existing Node (DeleteNode.java)The objective of the DeleteNode class is to delete an existing node from a tree (identified by thefolders table). To delete an existing node, you need to supply the ID of the node being deleted. Also,you need to indicate whether to promote the subtree of the deleted node; you pass this argument asa boolean (true/false). So, the DeleteNode class’s main() method will accept two arguments: the IDof the node to be deleted and a boolean (true/false) to indicate the promotion of the subtree of thedeleted node.

If a node is a leaf node, it is easy to delete it, because it has no impact on other nodes. But ifa node to be deleted is an inner node (a nonleaf node, which has at least one child), then the dele-tion process is complex. When a node is deleted from the tree, you have to preserve the integrity ofthe tree if the node has any subnodes. You have two approaches:

5203ch11.qxd 8/11/05 4:33 PM Page 420

Page 450: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 421

The first approach is to promote one of the deleted node’s children (a subnode) to take over theposition of the deleted node. The remaining subnodes of the deleted node become subnodesto the promoted node. To understand this, take a look at the following figures: assume that theoriginal tree is Figure 11-2, and you want to delete node B. Using the promotion approach, theresultant tree will become Figure 11-3.

The second approach is to promote the entire subtree (all the children) of the deleted node sothat all the subnodes of the deleted node become the subnodes of the deleted node’s parentnode. Assume that the original tree is Tree-1, and you want to delete node B. Using the promo-tion of the entire subtree approach, the resultant tree will become Figure 11-4.

Figure 11-2. Mapping tree to relational model: original tree

Figure 11-3. Deleting node B by promoting one of the deleted node’s children

5203ch11.qxd 8/11/05 4:33 PM Page 421

Page 451: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT422

Viewing the Tree Structure Before the DeletionThis is the tree structure before the deletion:

mysql> select * from folders;+--------+--------+------+------+| id | parent | lft | rgt |+--------+--------+------+------+| Albert | NULL | 1 | 18 || Bert | Albert | 4 | 9 || Chuck | Albert | 10 | 17 || Donna | Chuck | 11 | 12 || Eddie | Chuck | 13 | 14 || Fred | Chuck | 15 | 16 || Mike | Bert | 7 | 8 || Mary | Bert | 5 | 6 || Jeff | Albert | 2 | 3 |+--------+--------+------+------+

To delete Jeff (under Albert), use this code:

$ java DeleteNode Jeff false------DeleteNode begin---------conn=com.mysql.jdbc.Connection@750159ID=JeffpromoteSubtreeAsString=false------DeleteNode end---------

mysql> select * from folders;+--------+--------+------+------+| id | parent | lft | rgt |+--------+--------+------+------+| Albert | NULL | 1 | 16 || Bert | Albert | 2 | 7 || Chuck | Albert | 8 | 15 || Donna | Chuck | 9 | 10 || Eddie | Chuck | 11 | 12 || Fred | Chuck | 13 | 14 || Mike | Bert | 5 | 6 || Mary | Bert | 3 | 4 |+--------+--------+------+------+8 rows in set (0.01 sec)

To delete Chuck (under Albert), use this code:

$ java DeleteNode Chuck true

Figure 11-4. Deleting node B by promoting the entire subtree

5203ch11.qxd 8/11/05 4:33 PM Page 422

Page 452: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 423

------DeleteNode begin---------conn=com.mysql.jdbc.Connection@750159ID=ChuckpromoteSubtreeAsString=true------DeleteNode end---------

mysql> select * from folders;+--------+--------+------+------+| id | parent | lft | rgt |+--------+--------+------+------+| Albert | NULL | 1 | 14 || Bert | Albert | 2 | 7 || Donna | Albert | 8 | 9 || Eddie | Albert | 10 | 11 || Fred | Albert | 12 | 13 || Mike | Bert | 5 | 6 || Mary | Bert | 3 | 4 |+--------+--------+------+------+7 rows in set (0.00 sec)

To delete Bert (under Albert), use this code:

$ java DeleteNode Bert false------DeleteNode begin---------conn=com.mysql.jdbc.Connection@750159ID=BertpromoteSubtreeAsString=falseID=BertparentID=AlbertdelLeft=2delRight=7newParentID=Mary------DeleteNode end---------

C:\mp\sql-trees>

mysql> select * from folders;+--------+--------+------+------+| id | parent | lft | rgt |+--------+--------+------+------+| Albert | NULL | 1 | 12 || Donna | Albert | 6 | 7 || Eddie | Albert | 8 | 9 || Fred | Albert | 10 | 11 || Mike | Mary | 3 | 4 || Mary | Albert | 2 | 5 |+--------+--------+------+------+6 rows in set (0.00 sec)

DeleteNode.javaThis shows DeleteNode.java:

1 import java.util.*;2 import java.io.*;3 import java.sql.*;45 public class DeleteNode {6

5203ch11.qxd 8/11/05 4:33 PM Page 423

Page 453: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT424

7 public static Connection getConnection() throws Exception {8 String driver = "org.gjt.mm.mysql.Driver";9 String url = "jdbc:mysql://localhost/snipit";10 String username = "root";11 String password = "root";12 Class.forName(driver); // load MySQL driver13 return DriverManager.getConnection(url, username, password);15 }1617 public static void usage() {18 System.out.println("usage: DeleteNode ");19 System.out.println("Example-1: DeleteNode Bob true");20 System.out.println("Example-2: DeleteNode Bob false");21 System.exit(1);22 }2324 public static void main(String[] args) {25 Connection conn = null;26 PreparedStatement pstmt = null;27 try {28 System.out.println("------DeleteNode begin---------");2930 if (args.length != 2) {31 usage();32 }3334 conn = getConnection();35 System.out.println("conn="+conn);3637 String ID = args[0]; // node with value of ID will be deleted38 String promoteSubtreeAsString = args[1];3940 System.out.println("ID="+ID);41 System.out.println("promoteSubtree="+promoteSubtreeAsString);4243 if ((promoteSubtreeAsString == null) ||44 (promoteSubtreeAsString.length() == 0)) {45 usage();46 }4748 boolean promoteSubtree = false;49 if (promoteSubtreeAsString.equals("true")) {50 promoteSubtree = true;51 }52 else if (promoteSubtreeAsString.equals("false")) {53 promoteSubtree = false;54 }55 else {56 usage();57 }58 String getLeftRight =59 "select parent, lft, rgt from folders where id = ?";60 pstmt = conn.prepareStatement(getLeftRight);61 pstmt.setString(1, ID);62 ResultSet rs = pstmt.executeQuery();63 rs.next();64 String parentID = rs.getString(1);

5203ch11.qxd 8/11/05 4:33 PM Page 424

Page 454: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 425

66 int delRight = rs.getInt(3);67 System.out.println("ID="+ID);68 System.out.println("parentID="+parentID);69 System.out.println("delLeft="+delLeft);70 System.out.println("delRight="+delRight);7172 if(promoteSubtree) {73 // promote the subtree74 promoteTheSubTree(conn, ID, parentID, delLeft, delRight);75 }76 else {77 // promote the leftmost sibling to the new parent78 promoteSibling(conn, ID, parentID, delLeft, delRight);79 }808182 System.out.println("------DeleteNode end---------");83 }84 catch (Exception e) {85 e.printStackTrace();86 System.exit(1);87 }88 finally {89 // release database resources90 }91 }929394 public static void promoteTheSubTree(Connection conn,95 String ID,96 String parentID,97 int delLeft,98 int delRight)99 throws SQLException, BatchUpdateException {100101 // start transaction for batch updates102 conn.setAutoCommit(false);103 Statement stmt = conn.createStatement();104105 String deleteID = "delete from folders where ID = '"+ID+"'";106 String update1 = "update folders set lft = lft - 1, "+

"rgt = rgt - 1 where lft between "+delLeft+" and "+delRight;107 String update2 = "update folders set rgt = rgt - 2 "+

"where rgt > "+delRight;108 String update3 = "update folders set lft = lft - 2 "+

"where lft > "+delRight;109 String update4 = "update folders set parent = '"+parentID+

"' where parent='"+ID+"'";110 stmt.addBatch(deleteID);111 stmt.addBatch(update1);112 stmt.addBatch(update2);113 stmt.addBatch(update3);114 stmt.addBatch(update4);115116 // send batch operations to the database server117 int[] batchUpdateCounts = stmt.executeBatch();118

5203ch11.qxd 8/11/05 4:33 PM Page 425

Page 455: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT426

120 conn.commit();121 conn.setAutoCommit(true);122 }123124 public static void promoteSibling(Connection conn,125 String ID,126 String parentID,127 int delLeft,128 int delRight)129 throws SQLException, BatchUpdateException {130131 // promote the leftmost sibling to the new parent132 // find the new parent's ID (which is the new promoted node ID)133 // then set the parent of siblings to the found id134 int delLeftPlus1 = delLeft + 1;135 String findID = "select id from folders where lft = "+delLeftPlus1;136 Statement findStmt = conn.createStatement();137 ResultSet rs = findStmt.executeQuery(findID);138 rs.next();139 String newParentID = rs.getString(1);140 rs.close();141 System.out.println("newParentID="+newParentID);142143 // start transaction for batch updates144 conn.setAutoCommit(false);145 Statement stmt = conn.createStatement();146147 // set the new parents for promoted node's sibling148 String updateParents = "update folders set parent = '"+

newParentID+"' where lft > "+delLeft+" and rgt < "+delRight;149 String update1 = "update folders set lft = lft - 1, "+

"rgt = "+delRight+", parent= '"+parentID+"' where lft = "+delLeftPlus1;

150 String update2 = "update folders set rgt = rgt - 2 "+" where rgt > "+delLeft;

151 String update3 = "update folders set lft = lft - 2 "+"where lft > "+delLeft;

152 String deleteID = "delete from folders where ID = '"+ID+"'";153 stmt.addBatch(updateParents);154 stmt.addBatch(update1);155 stmt.addBatch(update2);156 stmt.addBatch(update3);157 stmt.addBatch(deleteID);158159 // send batch operations to the database server160 int[] batchUpdateCounts = stmt.executeBatch();161162 // commit transaction for batch updates163 conn.commit();164 conn.setAutoCommit(true);165 }166 }

5203ch11.qxd 8/11/05 4:33 PM Page 426

Page 456: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 427

Discussing DeleteNode.javaThe following explains the program:

Lines 1–3: Import the required classes and interfaces.

Lines 7–15: The getConnection() method returns a java.sql.Connection object.

Lines 30–57: Get the command-line argument ID (which is the node ID to be deleted) anda boolean to indicate how to promote subnodes of the deleted node (identified by ID).

Lines 59–70: Here, you retrieve the parent, left, and right of the ID. These values will be used forfurther processing.

Lines 72–79: If the second argument is true, then you must promote the entire subtree.

Line 74: Promotes the subtree. If the second argument is false, then you must promote onlythe leftmost sibling, and the other nodes will not be affected at all.

Line 78: Promotes the leftmost sibling to the new parent.

Lines 94–122: Start the transaction for batch updates. No SQL operation will be committeduntil you invoke the conn.commit() statement.

Lines 110–114: Create a set of batch operations. The order of these batch operations is important,and the batch works as a queue (first-in, first-out).

Lines 124–165: Start the transaction for batch updates. No SQL operation will be committeduntil you invoke the conn.commit() statement.

Lines 153–157: Create a set of batch operations. The order of these batch operations is important,and the batch works as a queue (first-in, first-out).

Deleting an Existing Subtree (DeleteSubtree.java)The objective of the DeleteSubtree class is for a given node to delete an existing subtree from a tree.To delete an existing subtree, you need to supply the root ID of the subtree being deleted. To under-stand DeleteSubtree, refer to Figure 11-5; assume that the original tree is Tree-41, and you want todelete subtree with the root node of B. The resultant tree will become Tree-42.

Figure 11-5. Before and after deleting a subtree

5203ch11.qxd 8/11/05 4:33 PM Page 427

Page 457: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT428

Setting Up the DatabaseThe tree structure before the deletion of a subtree is as follows:

mysql> select * from folders;+--------+--------+------+------+| id | parent | lft | rgt |+--------+--------+------+------+| Albert | NULL | 1 | 18 || Bert | Albert | 4 | 9 || Chuck | Albert | 10 | 17 || Donna | Chuck | 11 | 12 || Eddie | Chuck | 13 | 14 || Fred | Chuck | 15 | 16 || Mike | Bert | 7 | 8 || Mary | Bert | 5 | 6 || Jeff | Albert | 2 | 3 |+--------+--------+------+------+9 rows in set (0.00 sec)

To delete the subtree rooted by Bert, use this code:

$ java DeleteSubtree Bert------DeleteSubtree begin---------conn=com.mysql.jdbc.Connection@750159ID=Bert------DeleteSubtree end---------

mysql> select * from folders;+--------+--------+------+------+| id | parent | lft | rgt |+--------+--------+------+------+| Albert | NULL | 1 | 12 || Chuck | Albert | 4 | 11 || Donna | Chuck | 5 | 6 || Eddie | Chuck | 7 | 8 || Fred | Chuck | 9 | 10 || Jeff | Albert | 2 | 3 |+--------+--------+------+------+6 rows in set (0.00 sec)

DeleteSubtree.javaThis shows DeleteSubtree.java:

1 import java.util.*;2 import java.io.*;3 import java.sql.*;45 public class DeleteSubtree {67 public static Connection getConnection() throws Exception {8 String driver = "org.gjt.mm.mysql.Driver";9 String url = "jdbc:mysql://localhost/snipit";10 String username = "root";11 String password = "root";12 Class.forName(driver); // load MySQL driver13 return DriverManager.getConnection(url, username, password);15 }16

5203ch11.qxd 8/11/05 4:33 PM Page 428

Page 458: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT 429

17 public static void usage() {18 System.out.println("usage: DeleteSubtree ");19 System.out.println("Example-1: DeleteSubtree Bob");20 System.exit(1);21 }2223 public static void main(String[] args) {24 Connection conn = null;25 PreparedStatement pstmt = null;26 Statement stmt = null;27 try {28 System.out.println("------DeleteSubtree begin---------");2930 if (args.length != 1) {31 usage();32 }3334 conn = getConnection();35 System.out.println("conn="+conn);3637 // subtree/node with value of ID will be deleted38 String ID = args[0];39 System.out.println("ID="+ID);40 String getLeftRight =41 "select lft, rgt from folders where id = ?";42 pstmt = conn.prepareStatement(getLeftRight);43 pstmt.setString(1, ID);44 ResultSet rs = pstmt.executeQuery();45 rs.next();46 int delLeft = rs.getInt(1);47 int delRight = rs.getInt(2);48 int delta = delRight - delLeft + 1;4950 // start transaction for batch updates51 conn.setAutoCommit(false);52 stmt = conn.createStatement();5354 String deleteID = "delete from folders "+

"where lft between "+delLeft+" and "+delRight;55 String update1 = "update folders set lft = lft - "+delta+

" where lft > "+delLeft;56 String update2 = "update folders set rgt = rgt - "+delta+

" where rgt > "+delRight;57 stmt.addBatch(deleteID);58 stmt.addBatch(update1);59 stmt.addBatch(update2);6061 // send batch operations to the database server62 int[] batchUpdateCounts = stmt.executeBatch();6364 // commit transaction for batch updates65 conn.commit();66 conn.setAutoCommit(true);67 System.out.println("------DeleteSubtree end---------");68 }69 catch (Exception e) {70 e.printStackTrace();

5203ch11.qxd 8/11/05 4:33 PM Page 429

Page 459: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 11 ■ EXPLORING THE STATEMENT430

71 System.exit(1);72 }73 finally {74 // release database resources75 }76 }77 }

Discussing DeleteSubtree.javaThe following explains the program:

Lines 1–3: Import the required classes and interfaces.

Lines 7–14: The getConnection() method returns a java.sql.Connection object.

Lines 41–48: Here, you retrieve the left and right of the root node to be deleted. These valueswill be used for further processing.

Lines 54–57: These lines prepare SQL queries for batch updates.

Line 51: Starts the transaction for batch updates. No SQL operation will be committed until youinvoke the conn.commit() statement.

Line 52: Creates a generic Statement object to be used for batch updates.

Lines 54–59: Create a set of batch operations. The order of these batch operations is important,and the batch works as a queue (first-in, first-out).

Line 62: Sends batch operations to the database server (operations are not committed yet!).

Line 65: Commits the transaction for batch updates (either all succeed or all fail—no partialsuccess).

Lines 73–75: Release database resources (to free up memory and data structures used by theJDBC driver and database server).

5203ch11.qxd 8/11/05 4:33 PM Page 430

Page 460: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Working with the PreparedStatement

This chapter describes one of the most important interfaces from the java.sql package, java.sql.PreparedStatement (which inherits from java.sql.Statement). The java.sql.PreparedStatementinterface formulates the dynamic statements executed by the database engine.

Statement and PreparedStatement objects can execute SQL statements and return the results(as a ResultSet object or another type) to the client. CallableStatement can execute a stored proce-dure/function and return the results (as a ResultSet object or another type) to the client. You cancreate all three interfaces using a Connection (java.sql.Connection) object. Connection is a factoryobject for creating other JDBC objects (such as Statement, PreparedStatement, and CallableStatement).

When the database “prepares” your SQL statement, it creates two query plans: physical andlogical (see Figure 12-1). The DBMS engine reads your SQL query, and then the DBMS parser cre-ates a logical query plan. Finally, the DBMS Query Optimizer receives the logical query plan andcreates the physical query plan. The main job of the Query Optimizer is to choose the most efficientplan (with the least cost) for a given query. Query plans determine which indexes to use and how toexecute your SQL query. For more details about query plans, see C. J. Date’s An Introduction toDatabase Systems, Eighth Edition (Addison-Wesley, 2003) and Jeffrey Ullman’s website at http://www-db.stanford.edu/~ullman/dbsi/win99-346/query1.txt.

431

C H A P T E R 1 2

■ ■ ■

Figure 12-1. DBMS query plans

5203ch12.qxd 8/11/05 4:35 PM Page 431

Page 461: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT432

The main difference between Statement and PreparedStatement is this: Statement prepares andexecutes the query plan each time, while PreparedStatement prepares the query plan once and thenjust reuses it. (Preparing a statement is also referred to as precompiling a SQL statement.) So, usePreparedStatement objects when you are going to repeat a SQL query/update often.

For example, if you are going to retrieve books from a database (using the table books_table)based on the name of the author, then you might write this:

Connection conn = getConnection(); // get a database Connection object// here "author" column is parameterizedString query = "select isbn, title, price from books_table where author=?";PreparedStatement pstmt = conn.prepareStatement(query);

Then you can use pstmt (a PreparedStatement object) as many times as necessary with differentauthor information. You can consider the PreparedStatement object to be an independent staticJava method: the result depends on the parameters passed (in this case, the parameter is the nameof the author).

Here is how you can pass different authors to get your results from the database:

pstmt.setString(1, "don knuth"); // author is Don KnuthResultSet rs1 = pstmt.executeQuery(); // create a result set// now iterate result set rs1while (rs1.next()) {

...}

pstmt.setString(1, "niklus wirth"); // author is Niklus WirthResultSet rs2 = pstmt.executeQuery(); // create a result set// now iterate result set rs2while (rs2.next()) {

...}

The main point is that you can use a single PreparedStatement object many times toquery/update the database. If you are going to use a PreparedStatement object only once or twice,then it has greater initial overhead from precompiling the SQL statement (that is, creating an opti-mized query plan) than a Statement object. But if you are going to use a PreparedStatement objectmany times, then it has better performance than a Statement object in future executions.

By using a PreparedStatement object, you can execute SQL statements and pass input parame-ters to your statement. Like the Statement interface (since PreparedStatement inherits fromjava.sql.Statement), PreparedStatement enables you to execute both DDL (such as creating a tableor index) and DML (such as inserting a new record, deleting an existing record, or updating anexisting record). (In typical database applications, most DDLs are executed by SQL scripts.) ThePreparedStatement interface provides the following methods for executing SQL requests:

• execute(): Executes the SQL statement in this PreparedStatement object, which may be anykind of SQL statement (such as DDL and DML).

• executeQuery(): Executes the SQL query in this PreparedStatement object and returns theResultSet object generated by the query.

• executeUpdate(): Executes the SQL statement in this PreparedStatement object (which mustbe a SQL INSERT, UPDATE, or DELETE statement) or a SQL statement that returns nothing, suchas a DDL statement (for creating a table).

5203ch12.qxd 8/11/05 4:35 PM Page 432

Page 462: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 433

• addBatch(String sql): Adds the given SQL command to the current list of commands forthis PreparedStatement object. The commands in this list can be executed as a batch by call-ing the method executeBatch.

• execteBatch(): Submits a batch of commands to the database for execution and, if all com-mands execute successfully, returns an array of update counts.

Passing input values of different data types to the PreparedStatement object is discussed indetail in Chapter 13.

12-1. What Is a PreparedStatement Object?When your SQL query is parameterized, you should use a PreparedStatement object. APreparedStatement object is what sends your dynamic SQL statement (for creating tables, selectingrows from a table, or inserting new records) to the database server. A PreparedStatement objectenables you to pass input parameters to the SQL statement before sending it to the database serverfor execution.

You simply create a PreparedStatement object, pass your input parameters, and then execute it,supplying the appropriate execute method with the SQL statement you want to send. For a SELECTstatement, the method to use is executeQuery(). For statements that create or modify tables, themethod to use is executeUpdate().

JDK 1.5 defines the PreparedStatement as follows:

public interface PreparedStatement extends Statement

JDK 1.5 also says this:

An object that represents a precompiled SQL statement. A SQL statement is precompiled andstored in a PreparedStatement object. This object can then be used to efficiently execute thisstatement multiple times. Note: The setter methods (setShort, setString, and so on) for settingIN parameter values must specify types that are compatible with the defined SQL type of theinput parameter. For instance, if the IN parameter has SQL type INTEGER, then the methodsetInt should be used. If arbitrary parameter type conversions are required, the methodsetObject should be used with a target SQL type. In the following example of setting a param-eter, conn represents an active connection:

PreparedStatement pstmt = conn.prepareStatement("UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?");

pstmt.setBigDecimal(1, 153833.00); // set parameter 1 (SALARY)pstmt.setInt(2, 110592); // set parameter 2 (ID)

12-2. How Do You Create a PreparedStatement Object?The following sections show you how to create a PreparedStatement object and then use it to exe-cute SQL queries and updates.

How It WorksThe java.sql.Connection object is a factory for creating PreparedStatement objects. The Connectioninterface has six methods for creating PreparedStatement objects, as shown in Table 12-1.

5203ch12.qxd 8/11/05 4:35 PM Page 433

Page 463: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT434

Table 12-1. The Connection Interface’s Methods for Creating PreparedStatement

Return Type Method Description

PreparedStatement prepareStatement(String sql) Creates a PreparedStatement objectfor sending parameterized SQLstatements to the database

PreparedStatement prepareStatement(String sql, Creates a default PreparedStatementint autoGeneratedKeys) object that has the capability to

retrieve autogenerated keys

PreparedStatement prepareStatement Creates a default PreparedStatement(String sql, int[] object capable of returning the columnIndexes) autogenerated keys designated by the

given array

PreparedStatement prepareStatement(String sql, Creates a PreparedStatement object int resultSetType, int that will generate ResultSet objects resultSetConcurrency) with the given type and concurrency

PreparedStatement prepareStatement(String sql, Creates a PreparedStatement object int resultSetType, that will generate ResultSet objects int resultSetConcurrency, with the given type, concurrency, int resultSetHoldability) and holdability

PreparedStatement prepareStatement(String sql, Creates a default PreparedStatementString[] columnNames) object capable of returning the

autogenerated keys designated by thegiven array

Defining the Connection.prepareStatement() MethodThe JDK defines the Connection.prepareStatement() method as follows:

public PreparedStatement prepareStatement(String sql)throws SQLException

This creates a PreparedStatement object for sending parameterized SQL statements to the data-base. A SQL statement with or without IN (input) parameters can be precompiled and stored ina PreparedStatement object. You can then use this object to efficiently execute this statement multi-ple times.

Note that this method is optimized for handling parameterized SQL statements that benefitfrom precompilation. If the driver supports precompilation, the method prepareStatement will sendthe statement to the database for precompilation. Some drivers may not support precompilation. Inthis case, the statement may not be sent to the database until the PreparedStatement object is exe-cuted. This has no direct effect on users; however, it does affect which methods throw certainSQLException objects.

Result sets created using the returned PreparedStatement object will by default be typeTYPE_FORWARD_ONLY and have a concurrency level of CONCUR_READ_ONLY.

The parameter to this method is sql, which is a SQL statement that may contain one or more? IN parameter placeholders.

This method returns a new default PreparedStatement object containing the precompiled SQLstatement.

It throws SQLException if a database access error occurs.

SolutionIn the following sections, I will provide several examples for creating PreparedStatement objects. Toillustrate how to create PreparedStatement objects, I will use the following table definition:

5203ch12.qxd 8/11/05 4:35 PM Page 434

Page 464: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 435

create table books_table (isbn VARCHAR(12) not null PRIMARY KEY,author VARCHAR(64) not null,publisher VARCHAR(64)

);

The java.sql.Connection interface has factory methods for creating Statement and PreparedStatement objects. You can create a PreparedStatement object using the Connectionmethod prepareStatement(). I will provide three examples in the next sections:

• Example 1: Creating a PreparedStatement object with one parameter marker

• Example 2: Creating a PreparedStatement object with two parameter markers

• Example 3: Creating a PreparedStatement object with no parameter markers

As you will notice in the following examples, you can use a PreparedStatement object any num-ber of times after you create it (in all of the next three examples, I will show the PreparedStatementobjects being used twice).

Example 1: Creating a PreparedStatement Object with One Parameter Marker

This shows how to create a PreparedStatement object with one parameter marker:

import jcb.util.DatabaseUtil;...ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;String query = null;try {

conn = getConnection(); // get a Connection object// create query object: this query has only one// parameter marker, which denotes an ISBNquery = "select author, publisher from books_table where isbn = ?";

// create PreparedStatement objectpstmt = conn.prepareStatement(query);

// this query has only one parameter marker;// we have to "pass in" the values for parameterspstmt.setString(1, "111222333");

// execute the query, and create a result setrs = pstmt.executeQuery();

// iterate the ResultSet object, and get all the datawhile (rs.next()) {

String author = rs.getString(1);String publisher = rs.getString(2);

}

// now we can use the same PreparedStatement object// (i.e., pstmt) to execute another SQL query:// this query has only one parameter marker;// we have to "pass in" the values for parameterspstmt.setString(1, "1112224444");

5203ch12.qxd 8/11/05 4:35 PM Page 435

Page 465: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT436

// execute the query, and create a result setrs = pstmt.executeQuery();

// iterate the ResultSet object, and get all the datawhile (rs.next()) {

String author = rs.getString(1);String publisher = rs.getString(2);

}}catch (SQLException e) {

// could not create a PreparedStatement object,// or other problems happened.// handle the exception

}finally {

// close ResultSet, PreparedStatement, and Connection objectsDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}

Example 2: Creating a PreparedStatement Object with Two Parameter Markers

This shows how to create a PreparedStatement object with two parameter markers:

import jcb.util.DatabaseUtil;...ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;String query = null;try {

conn = getConnection(); // get a Connection object// create query object: this query has only one// parameter marker, which denotes an ISBNquery = "select publisher from books_table where isbn = ? and author = ?";

// create PreparedStatement objectpstmt = conn.prepareStatement(query);

// this query has only two parameter markers;// we have to "pass in" the values for both parameterspstmt.setString(1, "111222333"); // sets the isbnpstmt.setString(2, "knuth"); // sets the author

// execute the query, and create a result setrs = pstmt.executeQuery();

// iterate the ResultSet object, and get all the datawhile (rs.next()) {

String publisher = rs.getString(2);}

// now we can use the same PreparedStatement object// (i.e., pstmt) to execute another SQL query:// this query has two parameter markers;// we have to "pass in" the values for parameters

5203ch12.qxd 8/11/05 4:35 PM Page 436

Page 466: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 437

pstmt.setString(1, "111555666"); // sets the isbnpstmt.setString(2, "don knuth"); // sets the author

// execute the query, and create a result setrs = pstmt.executeQuery();

// iterate the ResultSet object, and get all the datawhile (rs.next()) {

String publisher = rs.getString(2);}

}catch (SQLException e) {

// could not create a PreparedStatement object,// or other problems happened; handle the exception

}finally {

// close ResultSet, PreparedStatement, and Connection objectsDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}

Example 3: Creating a PreparedStatement Object with No Parameter Markers

When your SQL query does not have any parameters, you should use a Statement instead ofa PreparedStatement object (unless you are going to use it many times), as shown here:

import jcb.util.DatabaseUtil;...ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;String query = null;try {

conn = getConnection(); // get a Connection object// create query object: this query has no parameter markerquery = "select isbn, author, publisher from books_table";

// create PreparedStatement objectpstmt = conn.prepareStatement(query);

// this query has no parameter marker;// no need to "pass in" any parameters

// execute the query, and create a result setrs = pstmt.executeQuery();

// iterate the ResultSet object, and get all the datawhile (rs.next()) {

String isbn = rs.getString(1);String author = rs.getString(2);String publisher = rs.getString(3);

}

// now we can use the same PreparedStatement object// (i.e., pstmt) to execute another SQL query:

5203ch12.qxd 8/11/05 4:35 PM Page 437

Page 467: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT438

// execute the query, and create a result setrs = pstmt.executeQuery();

// iterate the ResultSet object, and get all the datawhile (rs.next()) {

String isbn = rs.getString(1);String author = rs.getString(2);String publisher = rs.getString(3);

}}catch (SQLException e) {

// could not create a PreparedStatement object,// or other problems happened; handle the exception

}finally {

// close ResultSet, PreparedStatement, and Connection objectsDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}

12-3. What Are the Differences Between Statement and PreparedStatement?You can use both Statement and PreparedStatement objects to execute SQL statements. A Statementobject executes simple SQL statements with no parameters, while a PreparedStatement can executeSQL statements with any number of input parameters. From a performance point of view, if the sameSQL statement is executed many times, it may be more efficient to use a PreparedStatement object.(You can create a single PreparedStatement object and use it any number of times by setting differ-ent input parameter values.)

When a database server parses your SQL query/statement for execution, it creates an optimizedquery plan (how to execute your SQL statement). When the database prepares a statement, it createsa query plan. Statement prepares and executes the query plan each time, while PreparedStatementprepares the query plan once and then reuses the same query plan. This is the main differencebetween Statement and PreparedStatement.

The database optimizer’s job is to determine the most efficient method of accessing the dataand passing on this information for the execution of the query. The query plan is the set of informa-tion/steps/APIs of how to execute the query, which is produced by the database optimizer. Therefore,the query plan contains a series of specific instructions on how to most efficiently retrieve the requireddata from the tables/views/indexes.

So, the PreparedStatement object differs from the Statement object in two ways:

The precompiled statement: Instances of PreparedStatement contain a SQL statement that hasalready been compiled (it has a determined and prepared query plan). This is what makesa statement “prepared.”

The input parameters: The SQL statement contained in a PreparedStatement object may haveone or more IN (input) parameters. An IN parameter is a parameter whose value is not specifiedwhen the SQL statement is created. Instead, the statement has a question mark (?) as a placeholderfor each IN parameter. The ? is also known as a parameter marker or parameter placeholder.A client application must set a value for each parameter marker in a PreparedStatement beforeexecuting the prepared statement.

5203ch12.qxd 8/11/05 4:35 PM Page 438

Page 468: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 439

12-4. What Type Conversions Are Supported by MySQL?MySQL Connector/J is an implementation of Sun’s JDBC 3.0 API for the MySQL relational databaseserver. It strives to conform as much as possible to the JDBC API. This is according to the MySQLConnector/J documentation (http://www.mysql.com/documentation/connector-j/index.html):

MySQL Connector/J is flexible in the way it handles conversions between MySQL data typesand Java data types. In general, any MySQL data type can be converted to a java.lang.String,and any numerical type can be converted to any of the Java numerical types, althoughround-off, overflow, or loss of precision may occur.

According to the documentation, the conversions listed in Table 12-2 are guaranteed to work.

Table 12-2. MySQL Conversion Table

These MySQL Data Types... Can Always be Converted to These Java Types...

CHAR, VARCHAR, BLOB, TEXT, ENUM, and SET java.lang.String, java.io.InputStream,java.io.Reader, java.sql.Blob, and java.sql.Clob

FLOAT, REAL, DOUBLE PRECISION, NUMERIC, java.lang.String, java.lang.Short, .lang.Integer, DECIMAL, TINYINT, SMALLINT, MEDIUMINT, java.lang.Long, java.lang.Double, and INTEGER, and BIGINT java.math.BigDecimal (Note: Round-off, overflow, or

loss of precision may occur if you choose a Java numericdata type that has less precision or capacity than theMySQL data type you are converting to/from.)

DATE, TIME, DATETIME, and TIMESTAMP java.lang.String, java.sql.Date, andjava.sql.Timestamp

12-5. What Type Conversions Are Supported by Oracle?The Oracle JDBC drivers support standard JDBC 1.0 and 2.0 types as well as Oracle-specific (that is,proprietary) BFILE and ROWID data types and types of the REF CURSOR category. This section docu-ments standard and Oracle-specific SQL-Java default type mappings.

Table 12-3 shows the default mappings between SQL data types, JDBC type codes, standard Javatypes, and Oracle extended types. (This information comes from the Oracle 9i JDBC Developer’s Guideand Reference, Release 2.) Specifically, note the following about the table:

• The SQL Data Types column lists the SQL types that exist in the Oracle database.

• The JDBC Type Codes column lists the data type codes supported by the JDBC standard anddefined in the java.sql.Types class or defined by Oracle in the oracle.jdbc.OracleTypes class.For standard type codes, the codes are identical in these two classes.

• The Standard Java Types column lists standard types defined in the Java language.

• The Oracle Extension Java Types column lists the oracle.sql.* Java types that correspondto each SQL data type in the database.

5203ch12.qxd 8/11/05 4:35 PM Page 439

Page 469: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT440

Table 12-3. Oracle’s Default Mappings Between SQL Types and Java Types

SQL Data Types JDBC Type Codes Standard Java Types Oracle Extension Java Types

Standard JDBC 1.0 Types

CHAR java.sql.Types.CHAR java.lang.String oracle.sql.CHAR

VARCHAR2 java.sql.Types.VARCHAR java.lang.String oracle.sql.CHAR

LONG java.sql.Types.LONGVARCHAR java.lang.String oracle.sql.CHAR

NUMBER java.sql.Types.NUMERIC java.math.BigDecimal oracle.sql.NUMBER

NUMBER java.sql.Types.DECIMAL java.math.BigDecimal oracle.sql.NUMBER

NUMBER java.sql.Types.BIT boolean oracle.sql.NUMBER

NUMBER java.sql.Types.TINYINT byte oracle.sql.NUMBER

NUMBER java.sql.Types.SMALLINT short oracle.sql.NUMBER

NUMBER java.sql.Types.INTEGER int oracle.sql.NUMBER

NUMBER java.sql.Types.BIGINT long oracle.sql.NUMBER

NUMBER java.sql.Types.REAL float oracle.sql.NUMBER

NUMBER java.sql.Types.FLOAT double oracle.sql.NUMBER

NUMBER java.sql.Types.DOUBLE double oracle.sql.NUMBER

RAW java.sql.Types.BINARY byte[] oracle.sql.RAW

RAW java.sql.Types.VARBINARY byte[] oracle.sql.RAW

LONG RAW java.sql.Types.LONGVARBINARY byte[] oracle.sql.RAW

DATE java.sql.Types.DATE java.sql.Date oracle.sql.DATE

DATE java.sql.Types.TIME java.sql.Time oracle.sql.DATE

DATE java.sql.Types.TIMESTAMP javal.sql.Timestamp oracle.sql.DATE

Standard JDBC 2.0 Types

BLOB java.sql.Types.BLOB java.sql.Blob oracle.sql.BLOB

CLOB java.sql.Types.CLOB java.sql.Clob oracle.sql.CLOB

user-defined java.sql.Types.STRUCT java.sql.Struct oracle.sql.STRUCTobject

user-defined java.sql.Types.REF java.sql.Ref oracle.sql.REFreference

user-defined java.sql.Types.ARRAY java.sql.Array oracle.sql.ARRAYcollection

Oracle Extensions

BFILE oracle.jdbc.OracleTypes.BFILE oracle.sql.BFILE

ROWID oracle.jdbc.OracleTypes.ROWID oracle.sql.ROWID

REF CURSOR oracle.jdbc.OracleTypes.CURSOR java.sql.ResultSet oracle.jdbc.type OracleResultSet

TS oracle.jdbc.OracleTypes.TIMESTAMP oracle.sql.TIMESTAMP

TSTZ oracle.jdbc.OracleTypes.TIMESTAMPTZ oracle.sql.TIMESTAMPTZ

TSLTZ oracle.jdbc.OracleTypes.TIMESTAMPLTZ oracle.sql.TIMESTAMPLTZ

5203ch12.qxd 8/11/05 4:35 PM Page 440

Page 470: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 441

12-6. How Do You Use Batch Multiple Updates with Prepared-Statement?The following sections show how to execute a set of SQL statements as a batch multiple updateusing a PreparedStatement object.

How It WorksThe java.sql package provides the ability to send multiple updates (for updating records or creat-ing new records) to the database server for execution as a batch. The PreparedStatement.addbatch()method enables you to accomplish this task, which might improve the performance of the entiretransaction. You can reduce the amount of time it takes to perform repetitive inserts and updates ifyou batch them using the PreparedStatement object’s addBatch() method.

Both the Oracle and MySQL databases support the PreparedStatement.addBatch() method,which sends multiple updates to the database server. The signature of addBatch() is as follows:

public void addBatch() throws SQLException

This adds a set of parameters to the PreparedStatement object’s batch of commands. Thisthrows a SQLException if a database access error occurs.

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> create table add_batch_table(string_column varchar(10),int_column number);

SQL> desc add_batch_table;Name Null? Type--------------- -------- --------------STRING_COLUMN VARCHAR2(10)INT_COLUMN NUMBER

SQL> insert into add_batch_table(string_column,int_column) values('id-55', 55);SQL> insert into add_batch_table(string_column,int_column) values('id-66', 66);SQL> commit;Commit complete.

SQL> select * from add_batch_table;

STRING_COLUMN INT_COLUMN------------- ----------id-55 55id-66 66

SolutionThis solution (the Demo_PreparedStatement_AddBatch class) will insert three records—('id-1',100), ('id-2', 200), and ('id-3', 300)—into the add_batch_table using the addBatch() method. Itis important to note that you can call addBatch() any number of times without any parameter

5203ch12.qxd 8/11/05 4:35 PM Page 441

Page 471: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT442

changes or without even calling the setXXX( ) methods before calling addBatch(). If your batchstatements violate database integrity rules (such as having duplicate keys), then you will get a run-time exception.

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_AddBatch {

public static void checkUpdateCounts(int[] updateCounts) {for (int i=0; i<updateCounts.length; i++) {

if (updateCounts[i] >= 0) {// Successfully executed; the number// represents number of affected rowsSystem.out.println("OK; updateCount="+updateCounts[i]);

}else if (updateCounts[i] == Statement.SUCCESS_NO_INFO) {

// Successfully executed; number of// affected rows not availableSystem.out.println("OK; updateCount=Statement.SUCCESS_NO_INFO");

}else if (updateCounts[i] == Statement.EXECUTE_FAILED) {

// Failed to executeSystem.out.println("Failure; updateCount=Statement.EXECUTE_FAILED");

}}

}

public static void main(String[] args) {Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Demo_PreparedStatement_AddBatch begin--");String dbVendor = args[0];conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// Disable autocommitconn.setAutoCommit(false);

// prepare queryString query = "insert into add_batch_table"+

" (string_column, int_column) values(?, ?)";

// create PreparedStatement objectpstmt = conn.prepareStatement(query);

// add the first batchpstmt.setString(1, "id-1");pstmt.setInt(2, 100);pstmt.addBatch();

5203ch12.qxd 8/11/05 4:35 PM Page 442

Page 472: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 443

// add the second batchpstmt.setString(1, "id-2");pstmt.setInt(2, 200);pstmt.addBatch();

// add the third batchpstmt.setString(1, "id-3");pstmt.setInt(2, 300);pstmt.addBatch();

// execute the batchint[] updateCounts = pstmt.executeBatch();

// all statements were successfully executed.// updateCounts contains one element for each// batched statement. The updateCounts[i] contains// the number of rows affected by that statement.checkUpdateCounts(updateCounts);

// since there were no errors, commitconn.commit();

}catch (BatchUpdateException e) {

// Not all of the statements were successfully executedint[] updateCounts = e.getUpdateCounts();

// Some databases will continue to execute after one// fails. If so, updateCounts.length will equal the// number of batched statements. If not, updateCounts.length// will equal the number of successfully executed statementscheckUpdateCounts(updateCounts);

// Either commit the successfully executed statements// or roll back the entire batchtry {

conn.rollback();}catch (Exception e2) {

e.printStackTrace();System.exit(1);

}}catch (Exception e) {

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

5203ch12.qxd 8/11/05 4:35 PM Page 443

Page 473: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT444

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_AddBatch.java$ java Demo_PreparedStatement_AddBatch oracle--Demo_PreparedStatement_AddBatch begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------Successfully executed; updateCount=Statement.SUCCESS_NO_INFOSuccessfully executed; updateCount=Statement.SUCCESS_NO_INFOSuccessfully executed; updateCount=Statement.SUCCESS_NO_INFO$

Viewing the Oracle Database After Running the ProgramThis shows the Oracle database after running the program:

SQL> select * from add_batch_table;

STRING_COLUMN INT_COLUMN------------- ----------id-55 55id-66 66id-1 100id-2 200id-3 300

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> create table add_batch_table (-> string_column varchar(10),-> int_column integer );

mysql> desc add_batch_table;+---------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+---------------+-------------+------+-----+---------+-------+| string_column | varchar(10) | YES | | NULL | || int_column | int(11) | YES | | NULL | |+---------------+-------------+------+-----+---------+-------+2 rows in set (0.03 sec)

mysql> insert into add_batch_table(string_column,int_column) values('id-55', 55);mysql> insert into add_batch_table(string_column,int_column) values('id-66', 66);mysql> select * from add_batch_table;+---------------+------------+| string_column | int_column |+---------------+------------+| id-55 | 55 || id-66 | 66 |+---------------+------------+2 rows in set (0.00 sec)

5203ch12.qxd 8/11/05 4:35 PM Page 444

Page 474: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 445

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_AddBatch.java$ java Demo_PreparedStatement_AddBatch mysql--Demo_PreparedStatement_AddBatch begin--conn=com.mysql.jdbc.Connection@1e4cbc4---------------Successfully executed; updateCount=1Successfully executed; updateCount=1Successfully executed; updateCount=1

Viewing the MySQL Database After Running the ProgramThis shows the MySQL database after running the program:

mysql> select * from add_batch_table;+---------------+------------+| string_column | int_column |+---------------+------------+| id-55 | 55 || id-66 | 66 || id-1 | 100 || id-2 | 200 || id-3 | 300 |+---------------+------------+5 rows in set (0.01 sec)

12-7. How Do You Execute a SQL Statement Using a Prepared-Statement Object?The following sections show you how to execute SQL queries and statements using aPreparedStatement object.

How It WorksThe PreparedStatement (a parameterized SQL statement) is derived from the interface Statement.When you pass input parameter values to your SQL query, then it becomes more convenient andefficient to use a PreparedStatement object for sending SQL statements to the database.

When to Use a PreparedStatement ObjectIf you want to execute a SQL query many times, you can usually reduce the execution time by usinga PreparedStatement object instead. The Statement object is compiled for each execution, while thePreparedStatement object is compiled only once. In general, compiling SQL queries is expensive.SQL query compilation involves checking that the database user has access to the database objects(such as tables, views, and columns) used in the query. Before the compilation is completed, thequery is optimized. The compiler needs to find the optimal search path for queries and statements.

Creating a PreparedStatement ObjectYou can create a PreparedStatement object using a Connection factory object. For example, if youhave a SQL query with two input parameters, you might write the following (note that ? is a place-holder for an input parameter, and the following example has two input parameters):

5203ch12.qxd 8/11/05 4:35 PM Page 445

Page 475: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT446

Connection conn = getConnection(); // get a Connection objectString insertQuery = "INSERT into cats_tricks(name, trick) values(?, ?)";PreparedStatement pstmt = conn.prepareStatement(insertQuery);

// at this point the PreparedStatement object (i.e., pstmt)// is precompiled and can be used any number of times to create// new records for the cats_tricks table; you just need to pass two// parameter values: value for name and value for trick.

Supplying Values for PreparedStatement ParametersTo execute your PreparedStatement object, you need to supply values to be used instead of the ?placeholders before you can execute a PreparedStatement object. PreparedStatement.setXXX()methods enable you to supply values for PreparedStatement input parameters. For example, if thevalue you want to substitute for a ? is a Java int, you call the method setInt(). If the value you wantto substitute for a question mark is a Java String, you call the method setString(). There isa setXXX() method for each type in the Java programming language. Continuing with the precedingexample, you can supply values for input parameters like so:

Connection conn = getConnection(); // get a Connection objectString insertQuery = "INSERT into cats_tricks(name, trick) values(?, ?)";PreparedStatement pstmt = conn.prepareStatement(insertQuery);

// at this point the PreparedStatement object (i.e., pstmt)// is compiled and can be used any number of times to create// new records for the cats_tricks table.

// create first record with values ("mono", "rollover")// Supplying Values for PreparedStatement Parameterspstmt.setString(1, "mono"); // value for "name" columnpstmt.setString(2, "rollover"); // value for "trick" column// create the record (update the database)pstmt.executeUpdate():

// create second record with values ("ginger", "jump")// Supplying Values for PreparedStatement Parameterspstmt.setString(1, "ginger"); // value for "name" columnpstmt.setString(2, "jump"); // value for "trick" column// create the record (update the database)pstmt .executeUpdate():

Using the PreparedStatement object pstmt, the following line of code sets the first ? placeholderto a Java String with a value of "mono":

pstmt.setString(1, "mono"); // value for "name" column

Using the PreparedStatement object pstmt, the following line of code sets the ? placeholder toa Java String with a value of "rollover":

pstmt.setString(2, "rollover"); // value for "trick" column

Using a Loop to Set ValuesYou can make coding easier by using a for loop or a while loop to set values for thePreparedStatement object’s input parameters. The following example demonstrates how to usea loop to set values:

5203ch12.qxd 8/11/05 4:35 PM Page 446

Page 476: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 447

Connection conn = getConnection(); // get a Connection objectString insertQuery = "INSERT into cats_tricks(name, trick) values(?, ?)";PreparedStatement pstmt = conn.prepareStatement(insertQuery);

// at this point the PreparedStatement object (i.e., pstmt)// is compiled and can be used any number of times to create// new records for the cats_tricks table.

// create two arrays: cats and tricks (will be used as input parameters)String[] cats = {"mono", "mono", "ginger", "lola", "pishi", "pishi"};String[] tricks = {"jump", "rollover", "sleep", "talk", "hop", "jump"};

// now loop and create 6 records (cats[i] is associated// with the tricks[i] (i=0, 1, ..., 5)for (int i = 0; i < cats.length; i++) {

pstmt.setString(1, cats[i]); // value for "name" columnpstmt.setString(2, tricks[i]); // value for "trick" column// create the record (update the database)updateSales.executeUpdate()

}

Executing SQL Queries and Statements Using PreparedStatementThe PreparedStatement interface provides sufficient methods for executing SQL statements. To exe-cute SQL statements, follow these steps:

1. Get a java.sql.Connection object.

2. Define your SQL query with placeholders marked as ?.

3. Create a PreparedStatement object by using Connection.prepareStatement(your-SQL-query).

4. Set all input parameters by using the PreparedStatement.setXXX() method.

5. Execute your SQL statement using a PreparedStatement object, and generate ResultSetobjects. (Note that if a method does not return a ResultSet object explicitly, then you cancall the PreparedStatement.getResultSet() method to get the desired ResultSet object.)

6. Extract the values from the generated ResultSet objects.

The PreparedStatement interface provides the following methods for executing SQL statements(note that PreparedStatement extends Statement, which inherits all the methods from the Statementinterface):

• boolean execute(): Executes the SQL statement in this PreparedStatement object, whichmay be any kind of SQL statement

• ResultSet executeQuery(): Executes the SQL query in this PreparedStatement object andreturns the ResultSet object generated by the query

• int executeUpdate(): Executes the SQL statement in this PreparedStatement object (whichmust be a SQL INSERT, UPDATE, or DELETE statement) or a SQL statement that returns nothing,such as a DDL statement

• int[] executeBatch(): Submits a batch of commands to the database for execution and, ifall commands execute successfully, returns an array of update counts

5203ch12.qxd 8/11/05 4:35 PM Page 447

Page 477: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT448

12-8. How Do You Retrieve Automatically Generated Keys UsingPreparedStatement (MySQL)?The following sections show how to retrieve automatically generated keys using thePreparedStatement object. In general, you want to retrieve automatically generated keys when youuse the AUTO_INCREMENT attribute (in MySQL) for table columns.

How It WorksThe Oracle and MySQL databases allow for certain columns to be given automatically generated keyvalues. When using automatically generated key values, an insert statement is responsible for sup-plying a value for the column. The database generates a unique value for the column and inserts thevalue. You can use this technique for generating unique primary keys.

The MySQL database uses the AUTO_INCREMENT attribute for generating key values; on the otherhand, an Oracle database uses a SEQUENCE concept for generating key values. In the following sec-tions, you will look at these two databases for automatically generating key values before retrievingthem.

Setting Up the MySQL DatabaseConsider the following table definition; you can use the AUTO_INCREMENT attribute to generatea unique identity for new rows:

mysql> use octopus;Database changedmysql> CREATE TABLE animals_table (

-> id INT NOT NULL AUTO_INCREMENT,-> name VARCHAR(32) NOT NULL,-> PRIMARY KEY (id) );

Query OK, 0 rows affected (0.03 sec)

mysql> desc animals_table;+-------+-------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+----------------+| id | int(11) | | PRI | NULL | auto_increment || name | varchar(32) | | | | |+-------+-------------+------+-----+---------+----------------+2 rows in set (0.01 sec)

mysql> insert into animals_table(name) values('dog');mysql> insert into animals_table(name) values('cat');mysql> insert into animals_table(name) values('rabbit');mysql> select id, name from animals_table;+----+--------+| id | name |+----+--------+| 1 | dog || 2 | cat || 3 | rabbit |+----+--------+3 rows in set (0.00 sec)

5203ch12.qxd 8/11/05 4:35 PM Page 448

Page 478: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 449

SolutionThe JDBC 3.0 specification proposes a functional Statement interface that provides access to auto-matically generated key values after an insert. The following solution demonstrates how to retrieveAUTO_INCREMENT values using the new JDBC 3.0 method getGeneratedKeys(), which is now the pre-ferred method to use if you need to retrieve AUTO_INCREMENT keys. Oracle’s JDBC driver does notsupport getGeneratedKeys(), so you have to use the select <sequence-name>.currval from dualquery to access automatically generated key values after an insert.

The following is the complete solution. You should note that, for the MySQL database, if a col-umn is an AUTO_INCREMENT, you do not need to pass any value; however, for the Oracle database, youneed to pass <sequence-name>.nextval (which gives you the next value of <sequence-name>).

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.VeryBasicConnectionManager;import jcb.util.DatabaseUtil;

public class GetGeneratedKeys {public static void main(String[] args) {

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--GetGeneratedKeys begin--");String dbVendor = args[0]; // database vendorString name = args[1]; // animal nameconn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);

// insert a record into the animals_table using PreparedStatement// note that the SQL INSERT is different for each vendorString insert = null;if (dbVendor.equalsIgnoreCase("mysql")) {

insert = "insert into animals_table(name) values(?)";}else if (dbVendor.equalsIgnoreCase("oracle")) {

insert = "insert into animals_table(id, name) "+"values(ANIMAL_ID_SEQ.nextval, ?)";

}

pstmt = conn.prepareStatement(insert); // create a PreparedStatementpstmt.setString(1, name); // set input valuespstmt.executeUpdate(); // insert the record

if (dbVendor.equalsIgnoreCase("mysql")) {rs = stmt.getGeneratedKeys();

}else if (dbVendor.equalsIgnoreCase("oracle")) {

rs = stmt.executeQuery("select ANIMAL_ID_SEQ.currval from dual");}

while (rs.next()) {ResultSetMetaData rsMetaData = rs.getMetaData();int columnCount = rsMetaData.getColumnCount();

5203ch12.qxd 8/11/05 4:35 PM Page 449

Page 479: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT450

for (int i = 1; i <= columnCount; i++) {String key = rs.getString(i);System.out.println("key " + i + " is " + key);

}}System.out.println("--GetGeneratedKeys end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Viewing the MySQL Database Before Running the SolutionThis is the MySQL database before running the solution:

mysql> select * from animals_table;+----+--------+| id | name |+----+--------+| 1 | dog || 2 | cat || 3 | rabbit |+----+--------+3 rows in set (0.00 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution (MySQL):

$ javac GetGeneratedKeys.java$ java GetGeneratedKeys mysql duck-- GetGeneratedKeys begin --conn=com.mysql.jdbc.Connection@15c7855---------------key 1 is 4-- GetGeneratedKeys end --

Viewing the MySQL Database After Running the SolutionThis is the MySQL database after running the solution:

mysql> select * from animals_table;+----+---------+| id | name |+----+---------+| 1 | dog || 2 | cat || 3 | rabbit || 4 | duck |+----+---------+

5203ch12.qxd 8/11/05 4:35 PM Page 450

Page 480: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 451

12-9. How Do You Retrieve Automatically Generated Keys UsingPreparedStatement (Oracle)?The following sections show how to retrieve automatically generated keys using a PreparedStatementobject.

How It WorksThe Oracle and MySQL databases allow certain columns to be given automatically generated keyvalues. When using automatically generated key values, an insert statement is not responsible forsupplying a value for the column. The database generates a unique value for the column and insertsthe value. You can use this technique for generating unique primary keys. The Oracle database usesthe SEQUENCE objects for generating key values. What is a SEQUENCE? SEQUENCE is a Oracle databaseobject that does the following:

• Automatically generates unique numbers

• Is a sharable object

• Is typically used to create a primary key value

• Replaces application code

• Speeds up the efficiency of accessing sequence values when cached in memory

Setting Up the Oracle DatabaseHow do you create an Oracle SEQUENCE? Use the CREATE SEQUENCE statement. The following is thegeneral format for defining a SEQUENCE to generate sequential numbers automatically:

CREATE SEQUENCE sequence-name[INCREMENT BY n][START WITH n][{MAXVALULE n | NOMAXVALUE}][{MINVALUE n | NOMINVALUE}][{CYCLE | NOCYCLE}][{CACHE n | NOCACHE}];

This is an example of creating a SEQUENCE:

CREATE SEQUENCE ANIMAL_ID_SEQINCREMENT BY 1START WITH 1MAXVALUE 10000NOCACHENOCYCLE;

Note that Oracle SEQUENCE objects cannot be accessed directly and can be retrieved only usingthe CURRVAL (current value) and NEXTVAL (next value) pseudocolumns. Also, you must first accessa SEQUENCE with NEXTVAL before using CURRVAL. For example, if a sequence name is ANIMAL_ID_SEQ(defined in the next section), then ANIMAL_ID_SEQ.NEXTVAL will generate the next sequence numberand ANIMAL_ID_SEQ.CURRVAL will provide the last generated sequence number.

SQL> CREATE SEQUENCE ANIMAL_ID_SEQ2 INCREMENT BY 13 START WITH 14 MAXVALUE 100005 NOCACHE6 NOCYCLE;

5203ch12.qxd 8/11/05 4:35 PM Page 451

Page 481: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT452

Sequence created.

SQL> desc user_sequences;Name Null? Type----------------------------------------- -------- -------------SEQUENCE_NAME NOT NULL VARCHAR2(30)MIN_VALUE NUMBERMAX_VALUE NUMBERINCREMENT_BY NOT NULL NUMBERCYCLE_FLAG VARCHAR2(1)ORDER_FLAG VARCHAR2(1)CACHE_SIZE NOT NULL NUMBERLAST_NUMBER NOT NULL NUMBER

SQL> select SEQUENCE_NAME, MIN_VALUE, MAX_VALUE,INCREMENT_BY, LAST_NUMBERfrom user_sequences;

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY LAST_NUMBER--------------- ---------- ---------- ------------ -----------ANIMAL_ID_SEQ 1 10000 1 1

To create an Oracle table and populate it with ANIMAL_ID_SEQ, use this code:

SQL> CREATE TABLE animals_table (2 id INT NOT NULL,3 name VARCHAR(32) NOT NULL,4 PRIMARY KEY (id)5 );

Table created.

SQL> desc animals_table;Name Null? Type---------------- -------- ------------ID NOT NULL NUMBER(38)NAME NOT NULL VARCHAR2(32)

SQL> insert into animals_table(id, name)values(ANIMAL_ID_SEQ.nextval, 'dog');

SQL> insert into animals_table(id, name)values(ANIMAL_ID_SEQ.nextval, 'cat');

SQL> insert into animals_table(id, name)values(ANIMAL_ID_SEQ.nextval, 'rabbit');

SQL> commit;Commit complete.

SQL> select * from animals_table;

ID NAME---------- ---------

1 dog2 cat3 rabbit

SolutionThe JDBC 3.0 specification proposes a functional Statement interface that provides access to auto-

wing sections demonstrate how to retrieve

5203ch12.qxd 8/11/05 4:35 PM Page 452

Page 482: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 453

Oracle’s SEQUENCE values using the new JDBC 3.0 method getGeneratedKeys(), which is now the pre-ferred method to use if you need to retrieve automatically generated keys.

■Caution The Oracle JDBC driver does not support access to automatically generated key values after aninsert; that is, Oracle’s driver does not implement Statement.getGeneratedKeys());. Instead, you have to usethe Statement.executeQuery(); method as follows:

rs = stmt.executeQuery("select ANIMAL_ID_SEQ.currval from dual");

Viewing the Oracle Database Before Running the SolutionThis is the Oracle database before running the solution:

SQL> select * from animals_table;

ID NAME---------- ----------

1 dog2 cat3 rabbit

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac GetGeneratedKeys.java$ java GetGeneratedKeys oracle duck--GetGeneratedKeys begin--conn=oracle.jdbc.driver.OracleConnection@18fb2f7---------------key 1 is 4--GetGeneratedKeys end--

Viewing the Oracle Database After Running the SolutionThis is the Oracle database after running the solution:

SQL> select * from animals_table;

ID NAME---------- -----------

1 dog2 cat3 rabbit4 duck

12-10. How Do You Check for a SQL Warning UsingPreparedStatement?SQLWarning is an exception that provides information about database access warnings. This sectionshows you how to use a PreparedStatement object to learn whether a SQL warning has occurred.

Some database operations (such as SQL’s SELECT, INSERT, and UPDATE) can cause a warning thatis not handled by an exception. You must explicitly check for these warnings. An example of a warn-ing is a data truncation error during a read operation. You should check for a warning in three

5203ch12.qxd 8/11/05 4:35 PM Page 453

Page 483: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT454

places: on a Connection object, a PreparedStatement object, and a ResultSet object. A SQL Warning(in JDBC defined by the java.sql.SQLWarning class) is an exception that provides information aboutdatabase access warnings. Warnings are silently chained to the object whose method caused it to bereported.

The following example demonstrates how to check for a warning on a PreparedStatementobject:

ResultSet rs = null;Connection conn = null;SQLWarning warning = null;PreparedStatement pstmt = null;try {

conn = getConnection(); // get a Connection object

// Create a PreparedStatement objectString query = "select column1, column2 from table_x where column3 = ?";pstmt = conn.prepareStatement(query);pstmt.setString(1, "some-value");// Use the pstmt...such as selecting// some rows from a tablers = pstmt.executeQuery();

// ...more JDBC operations

// Get warnings on PreparedStatement objectwarning = pstmt.getWarnings();while (warning != null) {

// Process statement warnings...String message = warning.getMessage();String sqlState = warning.getSQLState();int errorCode = warning.getErrorCode();warning = warning.getNextWarning();

}}catch (SQLException e) {

// handle exception...}finally {

// close JDBC/Database resources such as// ResultSet, ...

}

12-11. How Does PreparedStatement Support ScrollableResultSets?java.sql.PreparedStatement has two methods that support scrollable result sets. The signature ofthe PreparedStatement.prepareStatement() method that supports scrollability and updatability isas follows:

Signature:PreparedStatement prepareStatement(String sql,

int resultSetType,int resultSetConcurrency)throws SQLException

5203ch12.qxd 8/11/05 4:35 PM Page 454

Page 484: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 455

Description:Creates a PreparedStatement object that will generate ResultSetobjects with the given type and concurrency. This method is thesame as the prepareStatement method above, but it allows thedefault result set type and concurrency to be overridden.

Parameters:sql - a String object that is the SQL statement to be sent to the

database; may contain one or more ? IN parameters

resultSetType - a result set type; one ofResultSet.TYPE_FORWARD_ONLY,ResultSet.TYPE_SCROLL_INSENSITIVE, orResultSet.TYPE_SCROLL_SENSITIVE

resultSetConcurrency - a concurrency type; one ofResultSet.CONCUR_READ_ONLY orResultSet.CONCUR_UPDATABLE

Returns:a new PreparedStatement object containing the pre-compiledSQL statement that will produce ResultSet objects with thegiven type and concurrency

Throws:SQLException - if a database access error occurs or thegiven parameters are not ResultSet constants indicatingtype and concurrency

The signature of the PreparedStatement.prepareStatement() method that supports scrollabil-ity, updatability, and holdability is as follows:

Signature:PreparedStatement prepareStatement(String sql,

int resultSetType,int resultSetConcurrency,int resultSetHoldability)throws SQLException

Description:Creates a PreparedStatement object that will generate ResultSetobjects with the given type, concurrency, and holdability. Thismethod is the same as the prepareStatement method above, but itallows the default result set type, concurrency, and holdabilityto be overridden.

Parameters:sql - a String object that is the SQL statement to be sent to the

database; may contain one or more ? IN parameters

resultSetType - one of the following ResultSet constants:ResultSet.TYPE_FORWARD_ONLY,ResultSet.TYPE_SCROLL_INSENSITIVE, orResultSet.TYPE_SCROLL_SENSITIVE

resultSetConcurrency - one of the following ResultSet constants:ResultSet.CONCUR_READ_ONLY orResultSet.CONCUR_UPDATABLE

5203ch12.qxd 8/11/05 4:35 PM Page 455

Page 485: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT456

resultSetHoldability - one of the following ResultSet constants:ResultSet.HOLD_CURSORS_OVER_COMMIT orResultSet.CLOSE_CURSORS_AT_COMMIT

Returns:a new PreparedStatement object, containing the pre-compiled SQLstatement, that will generate ResultSet objects with the giventype, concurrency, and holdability

Throws:SQLException - if a database access error occurs or the givenparameters are not ResultSet constants indicating type,concurrency, and holdability

12-12. What Are Updatability, Scrollability, and Holdability forResultSet Objects?The following sections define updatability, scrollability, and holdability in relation to ResultSetobjects in JDBC applications.

How It WorksIn most JDBC applications, using the ResultSet object, you move forward one row at a time (untilyou visit all of the rows returned). In addition to moving forward one row at a time througha ResultSet, you might want to do the following operations:

• Move backward (move from row j to row i, where j is greater than i)

• Go directly to a specific row

• Update rows of a ResultSet

• Delete rows of a ResultSet

• Leave the ResultSet open after a database commit operation

Defining Updatability, Scrollability, and HoldabilityThe following database terms describe the characteristics of a ResultSet object:

• Scrollability: Whether the cursor can move forward, can move backward, or can move toa specific row

• Updatability: Whether the cursor can be used to update or delete rows

• Holdability: Whether the cursor stays open after a database commit operation

The ResultSet type values are defined in the ResultSet interface as static integers:

• ResultSet.TYPE_FORWARD_ONLY: The constant indicating the type for a ResultSet object whosecursor may move only forward

• ResultSet.TYPE_SCROLL_INSENSITIVE: The constant indicating the type for a ResultSet objectthat is scrollable but generally not sensitive to changes made by others

• ResultSet.TYPE_SCROLL_SENSITIVE: The constant indicating the type for a ResultSet objectthat is scrollable and generally sensitive to changes made by others

5203ch12.qxd 8/11/05 4:35 PM Page 456

Page 486: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 457

The ResultSet concurrency values are defined in the ResultSet interface as static integers:

• ResultSet.CONCUR_READ_ONLY: The constant indicating the concurrency mode for a ResultSetobject that may not be updated

• ResultSet.CONCUR_UPDATABLE: The constant indicating the concurrency mode for a ResultSet

object that may be updated

The ResultSet holdability values are defined in the ResultSet interface as static integers.ResultSet holdability has two possible values: HOLD_CURSORS_OVER_COMMIT andCLOSE_CURSORS_AT_COMMIT. You can specify either of these values with any valid combination ofResultSet concurrency and ResultSet holdability. The value that you set overrides the default hold-ability for the connection.

• ResultSet.CLOSE_CURSORS_AT_COMMIT: The constant indicating that ResultSet objects shouldbe closed when the method Connection.commit is called

• ResultSet.HOLD_CURSORS_OVER_COMMIT: The constant indicating that ResultSet objects shouldnot be closed when the method Connection.commit is called

Table 12-4 lists the valid combinations of ResultSet type and ResultSet concurrency for scroll-able ResultSet objects.

Table 12-4. Valid Combinations of Scrollable ResultSet Type/Concurrency

ResultSet Type Value ResultSet Concurrency Value

TYPE_FORWARD_ONLY CONCUR_READ_ONLY

TYPE_FORWARD_ONLY CONCUR_UPDATABLE

TYPE_SCROLL_INSENSITIVE CONCUR_READ_ONLY

TYPE_SCROLL_SENSITIVE CONCUR_READ_ONLY

TYPE_SCROLL_SENSITIVE CONCUR_UPDATABLE

12-13. How Do You Create a Scrollable and Updatable ResultSetObject?The following sections show how to create a scrollable and updatable ResultSet using aPreparedStatement object.

How It WorksThis is according to Sun’s JDBC tutorial (http://java.sun.com/docs/books/tutorial/jdbc/jdbc2dot0/cursor.html):

One of the new features in the JDBC 2.0 API is the ability to move a result set's cursor back-ward as well as forward. There are also methods that let you move the cursor to a particularrow and check the position of the cursor. Scrollable result sets make it possible to create a GUI(graphical user interface) tool for browsing result sets, which will probably be one of the mainuses for this feature. Another use is moving to a row in order to update it.

Table 12-5 lists the ResultSet methods for positioning a scrollable cursor, according to Sun.

5203ch12.qxd 8/11/05 4:35 PM Page 457

Page 487: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT458

Table 12-5. ResultSet Methods for Scrolling

Method Positions the Cursor...

first() On the first row of the ResultSet.

last() On the last row of the ResultSet.

next() On the next row of the ResultSet.

previous() On the previous row of the ResultSet.

absolute(int n) If n>0, on row n of the ResultSet. If n<0 and m is the number of rows inthe ResultSet, on row m+n+1 of the ResultSet.

relative(int n) If n>0, on the row that is n rows after the current row. If n<0, on the rowthat is n rows before the current row. If n=0, on the current row.

afterLast() After the last row in the ResultSet.

beforeFirst() Before the first row in the ResultSet.

SolutionThe following code shows how to create a scrollable and updatable ResultSet object usingPreparedStatement:

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;String authorLastName = "Knuth";try {

conn = getConnection(); // get-a-valid-connection;...// prepare SQL queryString query = "select isbn from books_table where author = ?";

// create a PreparedStatement object for// a scrollable, updatable ResultSetpstmt = conn.prepareStatement(query,

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

// fill in input parameterspstmt.setString(1, authorLastName);

// execute query, and create the ResultSetrs = pstmt.executeQuery();

// position the cursor at the end of the ResultSetrs.afterLast();

// Position the cursor backwardwhile (rs.previous()) {

String bookISBN = rs.getString(1);System.out.println("bookISBN= " + bookISBN);// Look for isbn 1122334455if (bookISBN.equals("1122334455")) {

updateBookTitle("1122334455","new title");updateTheRow();

}}

5203ch12.qxd 8/11/05 4:35 PM Page 458

Page 488: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 459

}catch(SQLException e) {

// handle the exceptione.printStacktrace();...

}finally {

// cleanup time// close ResultSet, Statement, and Connection objects

}

12-14. How Do You Create a Scrollable ResultSet Object?The following sections show how to create a scrollable ResultSet using a PreparedStatement object.

How It WorksWhen you use Connection.prepareStatement(), it creates a PreparedStatement object, which cancreate result set objects that are scrollable. By passing some parameters, you can create scrollableresult sets. A scrollable result set allows the cursor to be moved to any row in the result set. Thiscapability is useful for GUI tools for browsing result sets.

SolutionThe following example shows how to create an insensitive scrollable ResultSet:

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

conn = getConnection(); // get-a-connection-object// create a statement that creates// insensitive scrollable result setString query = "SELECT last_name FROM employee_table where first_name = ?";pstmt = conn.prepareStatement(

query,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

// fill in all input parameterspstmt.setString(1, "alex");

// create a result setrs = pstmt.executeQuery();

// iterate the ResultSet object and get all of the datawhile (rs.next()) {

String lastName = rs.getString(1);}

}catch (SQLException e) {

// could not create a PreparedStatement object,// or other problems happened.// handle the exception

}

5203ch12.qxd 8/11/05 4:35 PM Page 459

Page 489: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT460

finally {// cleanup time// close Connection object// close PreparedStatement object// close ResultSet object

}

The following example shows how to create a sensitive scrollable ResultSet:

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

conn = getConnection(); // get-a-connection-object// create a statement that creates// a sensitive scrollable result setString query = "SELECT last_name FROM employee_table where first_name = ?";pstmt = conn.prepareStatement(

query,ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);

// fill in all input parameterspstmt.setString(1, "alex");

// create a result setrs = stmt.executeQuery();

// iterate the ResultSet object, and get all the datawhile (rs.next()) {

String lastName = rs.getString(1);}

}catch (SQLException e) {

// could not create a PreparedStatement object,// or other problems happened.// handle the exception

}finally {

// cleanup time// close Connection object// close PreparedStatement object// close ResultSet object

}

DiscussionBefore creating scrollable ResultSet objects, you need to determine whether your database sup-ports them. A scrollable ResultSet allows the cursor to move to any row in the result set. Two typesof scrollable result sets exist. An insensitive scrollable result set is one where the values captured inthe result set never change, even if changes are made to the table from which the data wasretrieved. A sensitive scrollable result set is one where the current values in the table are reflected inthe result set. So, if a change is made to a row in the table, the result set will show the new data whenthe cursor is moved to that row.

5203ch12.qxd 8/11/05 4:35 PM Page 460

Page 490: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 461

The following code can check whether your database supports scrollable ResultSet objects:

Connection conn = null;try {conn = getConnection(); // get-a-connection-object

DatabaseMetaData dbmd = conn.getMetaData();if (dbmd == null) {

// database metadata NOT supported...// you should throw an exception or...stop here

}

if (dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)) {// insensitive scrollable result sets are supported

}

if (dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)) {// Sensitive scrollable result sets are supported

}

if (!dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)&& !dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)) {// updatable result sets are not supported

}}catch (SQLException e) {

// handle the exception}finally {

// cleanup time// close Connection object}

12-15. How Do You Create an Updatable ResultSet Object?The following sections show how to create an updatable ResultSet using a PreparedStatementobject.

How It WorksAn updatable result set allows you to modify data in a table through the ResultSet object. If thedatabase does not support updatable result sets, the result sets returned from executeQuery() willbe read-only. To get updatable results, the PreparedStatement object used to create the result setsmust have the concurrency type of ResultSet.CONCUR_UPDATABLE. The query of an updatable ResultSetmust specify the primary key as one of the selected columns and select from only one table. For somedrivers, the following query will return a read-only ResultSet:

SELECT * FROM <table-name>

Therefore, make sure you specify the column names.According to the J2SE documentation, you can use the ResultSet object’s updater methods in

two ways:

To update a column value in the current row: In a scrollable ResultSet object, the cursor canmove backward and forward, to an absolute position, or to a position relative to the current row.The following code fragment updates the NAME column in the fifth row of the ResultSet object rsand then uses the method updateRow to update the data source table from which rs was derived.

5203ch12.qxd 8/11/05 4:35 PM Page 461

Page 491: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT462

rs.absolute(5); // moves the cursor to the fifth row of rsrs.updateString("NAME", "AINSWORTH"); // updates the

// NAME column of row 5 to be AINSWORTHrs.updateRow(); // updates the row in the data source

To insert column values into the insert row: An updatable ResultSet object has a special rowassociated with it that serves as a staging area for building a row. The following code fragmentmoves the cursor to the insert row, builds a three-column row, and inserts it into rs and intothe data source table using the method insertRow:

rs.moveToInsertRow(); // moves cursor to the insert rowrs.updateString(1, "AINSWORTH"); // updates the

// first column of the insert row to be AINSWORTHrs.updateInt(2,35); // updates the second column to be 35rs.updateBoolean(3, true); // updates the third column to truers.insertRow();rs.moveToCurrentRow();

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> use octopus;Database changedmysql> select * from employees;+-----+--------------+------+| id | name | age |+-----+--------------+------+| 11 | Alex Smith | 25 || 22 | Don Knuth | 65 || 33 | Mary Kent | 35 || 44 | Monica Seles | 30 || 777 | Donald Duck | NULL || 99 | Alex Edison | NULL |+-----+--------------+------+6 rows in set (0.00 sec)

Running the Solution for MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac DemoUpdatableResultSet.java$ java DemoUpdatableResultSet mysql 20--DemoUpdatableResultset begin--conn=com.mysql.jdbc.Connection@1c6f579ageLimit=20--DemoUpdatableResultset end--

MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from employees;

5203ch12.qxd 8/11/05 4:35 PM Page 462

Page 492: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 463

+------+------------------+------+| id | name | age |+------+------------------+------+| 11 | Alex Smith | 25 || 22 | NEW-NAME | 65 || 33 | Mary Kent | 35 || 44 | Monica Seles | 30 || 5000 | NEW-NAME-IS-HERE | 99 || 777 | Donald Duck | NULL || 99 | Alex Edison | NULL |+------+------------------+------+7 rows in set (0.00 sec)

Setting Up the Oracle 10g DatabaseThis shows how to set up the Oracle 10g database:

SQL> select * from employees;

ID NAME AGE---------- -------------------- ----------777 Donald Duck11 Alex Smith 2522 Don Knuth 6533 Mary Kent 3544 Monica Seles 3099 Alex Edison

6 rows selected.

Running the Solution for the Oracle 10g DatabaseThis shows how to run the solution for the Oracle 10g database:

$ javac DemoUpdatableResultSet.java$ java DemoUpdatableResultSet oracle 20--DemoUpdatableResultset begin--conn=oracle.jdbc.driver.T4CConnection@2ce908ageLimit=20--DemoUpdatableResultset end--

Viewing the Oracle 10g Database After Running the SolutionThis shows the Oracle 10g database after running the solution:

SQL> select * from employees;

ID NAME AGE---------- -------------------- ----------5000 NEW-NAME-IS-HERE 99777 Donald Duck11 Alex Smith 2522 NEW-NAME 6533 Mary Kent 3544 Monica Seles 3099 Alex Edison

7 rows selected.

5203ch12.qxd 8/11/05 4:35 PM Page 463

Page 493: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT464

SolutionThe following code creates a PreparedStatement object that will return updatable ResultSet objects:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.db.VeryBasicConnectionManager;import jcb.util.DatabaseUtil;

public class DemoUpdatableResultSet {public static void main(String[] args) {

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--DemoUpdatableResultset begin--");// read command line argumentsString dbVendor = args[0]; // database vendorint ageLimit = Integer.parseInt(args[1]); // age limit

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("ageLimit="+ageLimit);String query = "select id, name, age from employees where age > ?";

// create a PreparedStatement, which will// create an updatable ResultSet objectpstmt = conn.prepareStatement(query,

ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

pstmt.setInt(1, ageLimit); // set input valuesrs = pstmt.executeQuery(); // create an updatable ResultSet

//// update a column value in the current row.//// moves the cursor to the 2nd row of rsrs.absolute(2);// updates the NAME column of row 2 to be NEW-NAMErs.updateString("NAME", "NEW-NAME");// updates the row in the data sourcers.updateRow();

//// insert column values into the insert row.//rs.moveToInsertRow(); // moves cursor to the insert rowrs.updateInt(1, 5000); // 1st column id=5000rs.updateString(2, "NEW-NAME-IS-HERE"); // updates the 2nd columnrs.updateInt(3, 99); // updates the 3rd column to 99rs.insertRow();rs.moveToCurrentRow();

System.out.println("--DemoUpdatableResultset end--");}catch(Exception e){

5203ch12.qxd 8/11/05 4:35 PM Page 464

Page 494: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 465

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

DiscussionBefore creating an updatable ResultSet object, you need to determine whether your database sup-ports updatable ResultSet objects. An updatable ResultSet object allows you to modify data ina table through the result set.

You can use the following code to determine whether your database supports updatableResultSet objects:

Connection conn = null;try {

conn = getConnection(); // get-a-connection-objectDatabaseMetaData dbmd = conn.getMetaData();if (dbmd == null) {

// database metadata NOT supported...// you should throw an exception or...stop here

}

if (dbmd.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE)) {

// Updatable result sets are supported}else {

// Updatable result sets are not supported}

}catch (SQLException e) {

// handle the exception}finally {

// cleanup time// close Connection object

}

12-16. How Do You Create a Table Using PreparedStatement?The following sections show how to create a table using a PreparedStatement object.

How It WorksAlthough you can create a database table by using the PreparedStatement object, this is not rec-ommended. Instead, you should use a Statement object to create a database table. (UsingPreparedStatement objects are ideal if you are going to use the same object many times, not just

5203ch12.qxd 8/11/05 4:35 PM Page 465

Page 495: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT466

once for creating a table.) To create a database table, you need to pass the table definition to theexecuteUpdate() method of a Statement object.

SolutionThe following example creates a table called employee_table with two columns:

import jcb.util.DatabaseUtil;...Connection conn = null;PreparedStatement pstmt = null;try {

conn = getConnection(); // get-a-valid-connection;

// define your table: employee_tableString tableDefinition = "CREATE TABLE employee_table" +

" (badge_number VARCHAR(10), last_name VARCHAR(64))";pstmt = conn.prepareStatement(tableDefinition);pstmt.executeUpdate();// table creation was successful

}catch (SQLException e) {

// table creation failed.// handle the exception

}finally {

// cleanup timeDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}

12-17. How Do You Drop a Table Using PreparedStatement?The following sections show how to drop a table using a PreparedStatement object.

How It WorksEven though you can drop a database table by using a PreparedStatement object (that is, deletea table’s structure and contents), this is not recommended. Instead, you should use a Statementobject to delete a database table. (Using PreparedStatement objects are ideal if you are going to usethe same object many times—not just once for deleting a table.) To delete a database table, youneed to pass the table name to the executeUpdate() method of a Statement object. You might ask,if I want to drop couple of tables, can I use the SQL query drop table and then pass the table nameas a parameter (using a loop) to a PreparedStatement object? The answer is no. When you passa parameter to a PreparedStatement object, it automatically surrounds it with a single quote, andthe database server will not understand the SQL command:

drop table 'table-name'

In addition, it will complain that the table name table-name is not found.

SolutionThe following example drops/deletes a given table:

5203ch12.qxd 8/11/05 4:35 PM Page 466

Page 496: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 467

import jcb.util.DatabaseUtil;...public static void deleteTable(Connection conn, String tableName)

throws SQLException {if (conn == null) {return;

}

PreparedStatement pstmt = null;try {

// create table definition called employee_tableString tableDeletion = "DROP TABLE "+ tableName;pstmt = conn.prepareStatement(tableDeletion);pstmt.executeUpdate();// table deletion was successful

}finally {// cleanup timeDatabaseUtil.close(pstmt);

}}

12-18. How Do You Set the Number of Rows to Prefetch UsingPreparedStatement?The following sections show how to set the number of rows to prefetch using a PreparedStatementobject.

How It WorksJDBC enables you to set the number of rows to prefetch when executing a SQL query. This is a wayto control how many rows are returned. When a SQL query is executed, the number of rows of datathat a driver physically copies from the database to the client is called the fetch size. If you areperformance-tuning a particular query, you might be able to improve performance by adjusting thefetch size to better match the query.

You can set the fetch size on a PreparedStatement object, in which case all result sets createdfrom that statement will use that fetch size. You can also set the fetch size on a result set at any time.In this case, the next time data needs to be fetched from the database, the driver will copy over asmany rows as specified by the current fetch size.

SolutionThe following example demonstrates how to set a fetch size using a PreparedStatement object:

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

conn = getConnection(); // get-a-valid-connection;String query = "select id from my_table where id > ?";// Get the fetch size of a statementpstmt = conn.prepareStatement(query);pstmt.setInt(1, 1200);int fetchSize = pstmt.getFetchSize();

5203ch12.qxd 8/11/05 4:35 PM Page 467

Page 497: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT468

// Set the fetch size on the statementpstmt.setFetchSize(200);

// Create a result setrs = pstmt.executeQuery();

// Change the fetch size on the result setrs.setFetchSize(400);

}catch (SQLException e) {

// handle the exception...

}finally {

// cleanup time// close result set, statement, and Connection objects

}

12-19. How Do You Create a MySQL Table to Store Java TypesUsing PreparedStatement?The following sections show how to create a MySQL table to store Java data types using aPreparedStatement object.

How It WorksIn general, you should use a Statement object instead of a PreparedStatement object for the one-time use of SQL statements.

SolutionThe following example creates a MySQL table called mysql_all_types to store Java types:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_Create_Table_All_Types_MySQL {

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Create_Table_All_Types_MySQL begin--");StringBuffer buffer = new StringBuffer();buffer.append("CREATE TABLE mysql_all_types(");// Column Name MySQL Type Java Typebuffer.append("column_boolean BOOL, "); // booleanbuffer.append("column_byte TINYINT, "); // bytebuffer.append("column_short SMALLINT, "); // shortallTypesTable.append("column_int INTEGER, "); // intbuffer.append("column_long BIGINT, "); // long

5203ch12.qxd 8/11/05 4:35 PM Page 468

Page 498: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 469

buffer.append("column_float FLOAT, "); // floatbuffer.append("column_double DOUBLE PRECISION, "); // doublebuffer.append("column_bigdecimal DECIMAL(13,0), "); // BigDecimalbuffer.append("column_string VARCHAR(254), "); // Stringbuffer.append("column_date DATE, "); // Datebuffer.append("column_time TIME, "); // Timebuffer.append("column_timestamp TIMESTAMP, "); // Timestampbuffer.append("column_clob1 TINYTEXT, "); // Clob (< 2^8 bytes)buffer.append("column_clob2 TEXT, "); // Clob (< 2^16 bytes)buffer.append("column_clob3 MEDIUMTEXT, "); // Clob (< 2^24 bytes)buffer.append("column_clob4 LONGTEXT, "); // Clob (< 2^32 bytes)buffer.append("column_blob1 TINYBLOB, "); // Blob (< 2^8 bytes)buffer.append("column_blob2 BLOB, "); // Blob (< 2^16 bytes)buffer.append("column_blob3 MEDIUMBLOB, "); // Blob (< 2^24 bytes)buffer.append("column_blob4 LONGBLOB)"); // Blob (< 2^32 bytes)

conn = VeryBasicConnectionManager.getConnection(dbVendor);pstmt = conn.prepareStatement(buffer.toString());pstmt.executeUpdate();// creation of table ok.System.out.println("Create_Table_All_Types_MySQL(): end");

}catch (Exception e) {

// creation of table failed.// handle the exceptione.printStackTrace();

}finally {

DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Create_Table_All_Types_MySQL.java$ java Create_Table_All_Types_MySQL mysqlCreate_Table_All_Types_MySQL(): beginCreate_Table_All_Types_MySQL(): end

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> desc mysql_all_types;+---------------------+---------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+---------------------+---------------+------+-----+---------+-------+| column_boolean | tinyint(1) | YES | | NULL | || column_byte | tinyint(4) | YES | | NULL | || column_short | smallint(6) | YES | | NULL | || column_int | int(11) | YES | | NULL | || column_long | bigint(20) | YES | | NULL | || column_float | float | YES | | NULL | || column_double | double | YES | | NULL | |

5203ch12.qxd 8/11/05 4:35 PM Page 469

Page 499: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT470

| column_bigdecimal | decimal(13,0) | YES | | NULL | || column_string | varchar(254) | YES | | NULL | || column_date | date | YES | | NULL | || column_time | time | YES | | NULL | || column_timestamp | timestamp | YES | | NULL | || column_clob1 | tinytext | YES | | NULL | || column_clob2 | text | YES | | NULL | || column_clob3 | mediumtext | YES | | NULL | || column_clob4 | longtext | YES | | NULL | || column_blob1 | tinyblob | YES | | NULL | || column_blob2 | blob | YES | | NULL | || column_blob3 | mediumblob | YES | | NULL | || column_blob4 | longblob | YES | | NULL | |+---------------------+---------------+------+-----+---------+-------+20 rows in set (0.13 sec)

12-20. How Do You Create an Oracle Table to Store Java TypesUsing PreparedStatement?The following sections show how to create an Oracle table to store Java data types using aPreparedStatement object.

How It WorksIn general, you should use a Statement object instead of a PreparedStatement object for the one-time use of SQL statements.

SolutionThe following example creates an Oracle table called oracle_all_types to store Java types:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_Create_Table_All_Types_Oracle {

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Create_Table_All_Types_Oracle begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);

// create an varray typepstmt = conn.prepareStatement("CREATE TYPE varray_type is " +

"VARRAY(5) OF VARCHAR(10)");pstmt.executeUpdate();

// create an OBJECT typepstmt = conn.prepareStatement("CREATE TYPE oracle_object is " +

"OBJECT(column_string VARCHAR(128), column_integer INTEGER)");pstmt.executeUpdate();

5203ch12.qxd 8/11/05 4:35 PM Page 470

Page 500: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 471

StringBuffer buffer = new StringBuffer();buffer.append("CREATE TABLE oracle_all_types(");// Column Name Oracle Type Java Typebuffer.append("column_short SMALLINT, "); // shortbuffer.append("column_int INTEGER, "); // intbuffer.append("column_float REAL, "); // floatbuffer.append("column_double DOUBLE PRECISION, "); // doublebuffer.append("column_bigdecimal DECIMAL(13,0), "); // BigDecimalbuffer.append("column_string VARCHAR2(254), "); // Stringbuffer.append("column_characterstream LONG, "); // CharacterStreambuffer.append("column_bytes RAW(2000), "); // byte[];buffer.append("column_binarystream RAW(2000), "); // BinaryStreambuffer.append("column_timestamp DATE, "); // Timestampbuffer.append("column_clob CLOB, "); // Clobbuffer.append("column_blob BLOB, "); // Blob or BFILEbuffer.append("column_bfile BFILE, "); // oracle.sql.BFILEbuffer.append("column_array varray_type, "); // oracle.sql.ARRAYbuffer.append("column_object oracle_object)"); // oracle.sql.OBJECTpstmt.executeUpdate(buffer.toString());// when you are at this point, creation of table ok.System.out.println("Create_Table_All_Types_Oracle(): end");

}catch (Exception e) {

// creation of table failed.// handle the exceptione.printStackTrace();

}finally {

DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Running Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Create_Table_All_Types_Oracle.java$ java Create_Table_All_Types_Oracle oracleCreate_Table_All_Types_Oracle(): beginCreate_Table_All_Types_Oracle(): end

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> desc varray_type;varray_type VARRAY(5) OF VARCHAR2(10)

SQL> desc oracle_object;Name Null? Type------------------------------ -------- -------------COLUMN_STRING VARCHAR2(128)COLUMN_INTEGER NUMBER(38)

5203ch12.qxd 8/11/05 4:35 PM Page 471

Page 501: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT472

SQL> desc oracle_all_types;Name Null? Type------------------------------ -------- ---------------COLUMN_SHORT NUMBER(38)COLUMN_INT NUMBER(38)COLUMN_FLOAT FLOAT(63)COLUMN_DOUBLE FLOAT(126)COLUMN_BIGDECIMAL NUMBER(13)COLUMN_STRING VARCHAR2(254)COLUMN_CHARACTERSTREAM LONGCOLUMN_BYTES RAW(2000)COLUMN_BINARYSTREAM RAW(2000)COLUMN_TIMESTAMP DATECOLUMN_CLOB CLOBCOLUMN_BLOB BLOBCOLUMN_BFILE BINARY FILE LOBCOLUMN_ARRAY VARRAY_TYPECOLUMN_OBJECT ORACLE_OBJECT

12-21. How Do You Get Rows/Records from a Table Using Pre-paredStatement?The following sections show how to retrieve selected rows/records from a table using aPreparedStatement object.

How It WorksThe following example creates a PreparedStatement object to retrieve selected records from the depttable. A SQL SELECT query gets data from a table. The result of the SELECT query is called a result set.This example executes a SQL SELECT query using PreparedStatement.executeQuery() and createsa ResultSet object. (Once a ResultSet object is created, then you can iterate on it by using theResultSet.next() method.)

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Select_Records_Using_PreparedStatement {

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }int deptNumber = Integer.parseInt(args[1]);

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Select_Records_... begin--");

5203ch12.qxd 8/11/05 4:35 PM Page 472

Page 502: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 473

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("deptNumber="+ deptNumber);System.out.println("---------------");

// prepare queryString query = "select DEPT_NUM, DEPT_NAME, DEPT_LOC " +

"from DEPT where DEPT_NUM > ?";

pstmt = conn.prepareStatement(query); // create a statementpstmt.setInt(1, deptNumber); // set input parameter// execute query and return result as a ResultSetrs = pstmt.executeQuery();

// extract data from the ResultSetwhile (rs.next()) {

int dbDeptNumber = rs.getInt(1);String dbDeptName = rs.getString(2);String dbDeptLocation = rs.getString(3);System.out.println(dbDeptNumber +"\t"+ dbDeptName +

"\t"+ dbDeptLocation);}System.out.println("---------------");System.out.println("--Select_Records_... end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> desc dept;Name Null? Type------------------- -------- --------------DEPT_NUM NOT NULL NUMBER(2)DEPT_NAME VARCHAR2(14)DEPT_LOC VARCHAR2(13)

SQL> select * from dept;

DEPT_NUM DEPT_NAME DEPT_LOC---------- -------------- ---------

10 ACCOUNTING NEW YORK20 RESEARCH DALLAS30 SALES CHICAGO40 OPERATIONS BOSTON

5203ch12.qxd 8/11/05 4:35 PM Page 473

Page 503: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT474

SQL> select DEPT_NUM, DEPT_NAME, DEPT_LOC from dept where DEPT_NUM > 20;

DEPT_NUM DEPT_NAME DEPT_LOC---------- -------------- ---------

30 SALES CHICAGO40 OPERATIONS BOSTON

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Select_Records_Using_PreparedStatement.java$ java Select_Records_Using_PreparedStatement oracle 10--Select_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2deptNumber=10---------------20 RESEARCH DALLAS30 SALES CHICAGO40 OPERATIONS BOSTON-----------------Select_Records_... end--

$ java Select_Records_Using_PreparedStatement oracle 20--Select_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2deptNumber=20---------------30 SALES CHICAGO40 OPERATIONS BOSTON-----------------Select_Records_... end--

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> desc dept;+-----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-----------+-------------+------+-----+---------+-------+| dept_num | int(11) | | PRI | 0 | || dept_name | varchar(14) | YES | | NULL | || dept_loc | varchar(14) | YES | | NULL | |+-----------+-------------+------+-----+---------+-------+3 rows in set (0.00 sec)

mysql> select * from dept;+----------+------------+----------+| dept_num | dept_name | dept_loc |+----------+------------+----------+| 10 | Accounting | New York || 20 | Research | Dallas || 30 | Sales | Chicago || 40 | Operations | Boston |+----------+------------+----------+4 rows in set (0.00 sec)

5203ch12.qxd 8/11/05 4:35 PM Page 474

Page 504: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 475

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Select_Records_Using_PreparedStatement.java$ java Select_Records_Using_PreparedStatement mysql 10--Select_Records_... begin--conn=com.mysql.jdbc.Connection@8fce95deptNumber=10---------------20 Research Dallas30 Sales Chicago40 Operations Boston-----------------Select_Records_... end--$ java Select_Records_Using_PreparedStatement mysql 20--Select_Records_... begin--conn=com.mysql.jdbc.Connection@8fce95deptNumber=10---------------30 Sales Chicago40 Operations Boston-----------------Select_Records_... end--

12-22. What Are the Steps for Inserting a New Record Using Pre-paredStatement?The following sections show the specific steps for inserting a new record into an existing table usinga PreparedStatement object.

How It WorksTo insert/add new rows/records into a database table, you need to prepare a SQL INSERT statement.If you have a SQL INSERT statement that needs to be executed many times but with different values,you can use a PreparedStatement object to improve performance. (Also, you can use the samePreparedStatement object to insert any number of records.) A PreparedStatement object is a pre-compiled SQL statement, and using it saves the database from repeatedly having to compile theSQL statement each time it is executed. A SQL query (such as INSERT or SELECT) in a PreparedStatementcontains placeholders (represented by the ? character) instead of explicit values. You set values for theseplaceholders and then execute them by invoking the PreparedStatement.executeUpdate() method.

The following steps are required for inserting a new row/record into an existing database table:

• Step 1: Creating a PreparedStatement object

• Step 2: Supplying values for PreparedStatement parameters

• Step 3: Executing the PreparedStatement object

Step 1: Creating a PreparedStatement ObjectYou create PreparedStatement objects with a Connection object. You might write code such as thefollowing to create a PreparedStatement object that takes two input parameters:

java.sql.Connection conn = null;java.sql.PreparedStatement pstmt = null;

5203ch12.qxd 8/11/05 4:35 PM Page 475

Page 505: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT476

try {// get a database connectionconn = getConnection();

// prepare your SQL insert queryString insertStatement =

"insert into my_table(id, name) values(?, ?)";

// create PreparedStatement objectpstmt = conn.prepareStatement(insertStatement);

// at this point your PreparedStatement object is ready// to be used for inserting records into my_table.

// insert new records...}catch(Exception e) {

// could not create a PreparedStatement object// handle the exception

}finally {

// cleanup time// close prepared statement and Connection objects}

Step 2: Supplying Values for PreparedStatement ParametersThe variable psmt (the PreparedStatement object) now contains the SQL INSERT statement insertinto my_table(id, name) values(?, ?), which has been sent to the DBMS and precompiled.Before you can insert records into the database table (in this case my_table), you need to supply val-ues to be used in place of the question mark placeholders, if there are any, before you can executea PreparedStatement object. You do this by calling one of the PreparedStatement.setXXX() methods(XXX refers to the data type of columns for a given table). If the value you want to substitute fora question mark is a Java int, you call the method setInt(columnsPosition, integerValue). If thevalue you want to substitute for a question mark is a Java String, you call the methodsetString(columnsPosition, columnsStringValue), and so on. The PreparedStatement interfaceprovides a setXXX() method for each type in the Java programming language.

The following example shows the revised code that supplies values for placeholders. Thisshows how to prepare the table my_table:

mysql> create table my_table(id int, name varchar(20));Query OK, 0 rows affected (0.02 sec)mysql> desc my_table;+-------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+-------+| id | int(11) | YES | | NULL | || name | varchar(20) | YES | | NULL | |+-------+-------------+------+-----+---------+-------+2 rows in set (0.00 sec)

mysql> insert into my_table(id, name) values(100, 'alex');mysql> insert into my_table(id, name) values(200, 'mary');mysql> commit;mysql> select * from my_table;

5203ch12.qxd 8/11/05 4:35 PM Page 476

Page 506: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 477

+------+------+| id | name |+------+------+| 100 | alex || 200 | mary |+------+------+2 rows in set (0.00 sec)

Using the PreparedStatement object pstmt from the previous example, the following code setsthe first question mark placeholder to a Java int with a value of 300 and the second question markplaceholder to a Java String with value of "joe":

pstmt.setInt(1, 300);pstmt.setString(2, "joe");

After you have set these values for the two input parameters, the SQL statement in pstmt will beequivalent to the SQL statement in the String object insertStatement that was used in the previousupdate example. Therefore, the next code fragment:

String insertStatement = "insert into my_table(id, name) values(300, 'joe')";pstmt = conn.prepareStatement(insertStatement);pstmt.executeUpdate();

accomplishes the same thing as the following one (note that the method PreparedStatement.executeUpdate() executes the PreparedStatement pstmt object):

String insertStatement = "insert into my_table(id, name) values(?, ?)";pstmt = conn.prepareStatement(insertStatement);pstmt.setInt(1, 300);pstmt.setString(2, "joe");pstmt.executeUpdate():

Step 3: Executing the PreparedStatement ObjectIf your SQL statement is SELECT, then you use executeQuery(), which returns a ResultSet object.However, if your SQL statement is INSERT, UPDATE, or DELETE, then you use executeUpdate(), whichreturns an integer. (This number indicates how many rows were affected by your updatestatement.)

12-23. How Do You Insert a New Record into a Table Using Pre-paredStatement?The following sections show how to insert a new record into an existing table using aPreparedStatement object.

How It WorksThe following example creates a PreparedStatement object to insert a new record into the dept table.You can use a SQL INSERT statement to insert a new record into an existing dept table.

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

5203ch12.qxd 8/11/05 4:35 PM Page 477

Page 507: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT478

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Insert_Records_Using_PreparedStatement {

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }int deptNumber = Integer.parseInt(args[1]);String deptName = args[2];String deptLocation = args[3];

Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Insert_Records_... begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("deptNumber= "+ deptNumber);System.out.println("deptName= "+ deptName);System.out.println("deptLocation= "+ deptLocation);

// prepare queryString query = "insert into dept(DEPT_NUM, DEPT_NAME, DEPT_LOC) " +

"values(?, ?, ?)";

pstmt = conn.prepareStatement(query); // create a statementpstmt.setInt(1, deptNumber); // set input parameter 1pstmt.setString(2, deptName); // set input parameter 2pstmt.setString(3, deptLocation); // set input parameter 3pstmt.executeUpdate(); // execute insert statementSystem.out.println("--Insert_Records_... end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> desc dept;Name Null? Type------------------- -------- --------------DEPT_NUM NOT NULL NUMBER(2)DEPT_NAME VARCHAR2(14)DEPT_LOC VARCHAR2(13)

SQL> select * from dept;

5203ch12.qxd 8/11/05 4:35 PM Page 478

Page 508: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 479

DEPT_NUM DEPT_NAME DEPT_LOC---------- -------------- ---------

10 ACCOUNTING NEW YORK20 RESEARCH DALLAS30 SALES CHICAGO40 OPERATIONS BOSTON

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Insert_Records_Using_PreparedStatement.java$ java Insert_Records_Using_PreparedStatement oracle 60 Marketing "Los Gatos"--Insert_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2deptNumber= 60deptName= MarketingdeptLocation= Los Gatos--Insert_Records_... end--

$ java Insert_Records_Using_PreparedStatement oracle 70 Sports Cupertino--Insert_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2deptNumber= 70deptName= SportsdeptLocation= Cupertino--Insert_Records_... end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from dept;

DEPT_NUM DEPT_NAME DEPT_LOC---------- -------------- ---------

10 ACCOUNTING NEW YORK20 RESEARCH DALLAS30 SALES CHICAGO40 OPERATIONS BOSTON60 Marketing Los Gatos70 Sports Cupertino

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> desc dept;+-----------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-----------+-------------+------+-----+---------+-------+| dept_num | int(11) | | PRI | 0 | || dept_name | varchar(14) | YES | | NULL | || dept_loc | varchar(14) | YES | | NULL | |+-----------+-------------+------+-----+---------+-------+3 rows in set (0.00 sec)

5203ch12.qxd 8/11/05 4:35 PM Page 479

Page 509: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT480

mysql> select * from dept;+----------+------------+----------+| dept_num | dept_name | dept_loc |+----------+------------+----------+| 10 | Accounting | New York || 20 | Research | Dallas || 30 | Sales | Chicago || 40 | Operations | Boston |+----------+------------+----------+4 rows in set (0.00 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Insert_Records_Using_PreparedStatement.java$ java Insert_Records_Using_PreparedStatement mysql 60 Marketing "Los Gatos"--Select_Records_... begin--conn=com.mysql.jdbc.Connection@8fce99deptNumber= 60deptName= MarketingdeptLocation= Los Gatos--Select_Records_... end--

$ java Insert_Records_Using_PreparedStatement mysql 70 Sports Cupertino--Select_Records_... begin--conn=com.mysql.jdbc.Connection@8fce99deptNumber= 70deptName= SportsdeptLocation= Cupertino

12-23. How Do You Update an Existing Record Using Prepared-Statement?The following sections show how to update an existing record of a table using a PreparedStatementobject.

How It WorksThe following example creates a PreparedStatement object to update an existing record of the depttable (this program updates department locations for a given department number). The exampleuses a SQL UPDATE statement to update data for a table.

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Update_Records_Using_PreparedStatement {

5203ch12.qxd 8/11/05 4:35 PM Page 480

Page 510: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 481

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }int deptNumber = Integer.parseInt(args[1]);String deptLocation = args[2];

Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Update_Records_... begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("deptNumber= "+ deptNumber);System.out.println("deptLocation= "+ deptLocation);

// prepare queryString query = "update dept set DEPT_LOC = ? where DEPT_NUM = ? ";pstmt = conn.prepareStatement(query); // create a statementpstmt.setString(1, deptLocation); // set input parameter 1pstmt.setInt(2, deptNumber); // set input parameter 2pstmt.executeUpdate(); // execute update statementSystem.out.println("--Update_Records_... end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> select * from dept;

DEPT_NUM DEPT_NAME DEPT_LOC---------- -------------- ---------

10 ACCOUNTING NEW YORK20 RESEARCH DALLAS30 SALES CHICAGO40 OPERATIONS BOSTON

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Update_Records_Using_PreparedStatement.java$ java Insert_Records_Using_PreparedStatement oracle 30 "Los Angeles"--Update_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2deptNumber= 40deptLocation= Los Angeles--Update_Records_... end--

5203ch12.qxd 8/11/05 4:35 PM Page 481

Page 511: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT482

$ java Update_Records_Using_PreparedStatement oracle 40 Saratoga--Select_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2deptNumber= 40deptLocation= Saratoga--Update_Records_... end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from dept;

DEPT_NUM DEPT_NAME DEPT_LOC---------- -------------- ---------

10 ACCOUNTING NEW YORK20 RESEARCH DALLAS30 SALES Los Angeles40 OPERATIONS Saratoga

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> select * from dept;+----------+------------+----------+| dept_num | dept_name | dept_loc |+----------+------------+----------+| 10 | Accounting | New York || 20 | Research | Dallas || 30 | Sales | Chicago || 40 | Operations | Boston |+----------+------------+----------+4 rows in set (0.00 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Update_Records_Using_PreparedStatement.java$ java Insert_Records_Using_PreparedStatement mysql 30 "Los Angeles"--Update_Records_... begin--conn=com.mysql.jdbc.Connection@8fce99deptNumber= 40deptLocation= Los Angeles--Update_Records_... end--

$ java Update_Records_Using_PreparedStatement mysql 40 Saratoga--Select_Records_... begin--conn=com.mysql.jdbc.Connection@8fce99deptNumber= 40deptLocation= Saratoga--Update_Records_... end--

5203ch12.qxd 8/11/05 4:35 PM Page 482

Page 512: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 483

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from dept;+----------+------------+-------------+| dept_num | dept_name | dept_loc |+----------+------------+-------------+| 10 | Accounting | New York || 20 | Research | Dallas || 30 | Sales | Los Angeles || 40 | Operations | Saratoga |+----------+------------+-------------+4 rows in set (0.00 sec)

12-24. How Do You Delete All Rows from a Table Using Pre-paredStatement?The following sections show how to delete all existing records of a table (that is, how to delete all ofa table’s data but not the table’s structure) using a PreparedStatement object.

How It WorksThe following example creates a PreparedStatement object to delete all rows from a database table(called the dept table). The example uses a SQL DELETE query (DELETE FROM my_dept) to delete allrows from the my_dept table. Note that the data is deleted only from the table, not from the tabledefinition. If you want to delete the table (structure and content), then you should use the DROP<table-name> statement. In the MySQL and Oracle databases, you can use an alternative commandto delete all rows from a table:

TRUNCATE TABLE <table-name>

This is according to Oracle 9i SQL Reference, Release 2:

Use the TRUNCATE statement to remove all rows from a table or cluster. By default, Oraclealso deallocates all space used by the removed rows except that specified by the MINEXTENTSstorage parameter and sets the NEXT storage parameter to the size of the last extent removedfrom the segment by the truncation process. Removing rows with the TRUNCATE statementcan be more efficient than dropping and re-creating a table. Dropping and re-creating a tableinvalidates the table's dependent objects, requires you to regrant object privileges on thetable, and requires you to re-create the table's indexes, integrity constraints, and triggers andrespecify its storage parameters. Truncating has none of these effects. You cannot roll backa TRUNCATE statement.

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

5203ch12.qxd 8/11/05 4:35 PM Page 483

Page 513: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT484

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Delete_Records_Using_PreparedStatement {

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }String tableName = args[1]; // table to be deletedConnection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Delete_Records_... begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);

// prepare queryString query = "delete from" + tableName;pstmt = conn.prepareStatement(query); // create a statementpstmt.executeUpdate(); // execute delete statementSystem.out.println("--Delete_Records_... end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> select * from dept;

DEPT_NUM DEPT_NAME DEPT_LOC---------- -------------- ---------

10 ACCOUNTING NEW YORK20 RESEARCH DALLAS30 SALES CHICAGO40 OPERATIONS BOSTON

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Delete_Records_Using_PreparedStatement.java$ java Delete_Records_Using_PreparedStatement oracle dept--Delete_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2--Delete_Records_... end--

5203ch12.qxd 8/11/05 4:35 PM Page 484

Page 514: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 485

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from dept;no rows selected

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> select * from dept;+----------+------------+----------+| dept_num | dept_name | dept_loc |+----------+------------+----------+| 10 | Accounting | New York || 20 | Research | Dallas || 30 | Sales | Chicago || 40 | Operations | Boston |+----------+------------+----------+4 rows in set (0.00 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Delete_Records_Using_PreparedStatement.java$ java Delete_Records_Using_PreparedStatement mysql dept--Delete_Records_... begin--conn=com.mysql.jdbc.Connection@8fce99--Delete_Records_... end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from dept;Empty set (0.00 sec)

12-25. How Do You Get the Number of Rows for a Table UsingPreparedStatement?The following sections show how to get the total number of records for an existing table using aPreparedStatement object.

How It WorksTo get the number of rows/records for a database table, you should use the Statement object ratherthan a PreparedStatement object. (For queries that will be executed more than once with differentarguments, PreparedStatement can work better.) However, this solution uses the PreparedStatementobject.

The following example gets the number of rows in a table using the SQL statement SELECTCOUNT(*). In the MySQL and Oracle databases, you can use the following SQL statement to get thenumber of rows/records in a database table:

SELECT COUNT(*) from <table-name>

5203ch12.qxd 8/11/05 4:35 PM Page 485

Page 515: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT486

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Count_Records_Using_PreparedStatement {

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }String tableName = args[1]; // table to be counted

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Count_Records_... begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);

// prepare queryString query = "select count(*) from " + tableName;pstmt = conn.prepareStatement(query); // create a statementrs = pstmt.executQuery(); // count records// get the number of rows from result setif (rs.next()) {

int numberOfRows = rs.getInt(1);System.out.println("numberOfRows= "+numberOfRows);

}else {

System.out.println("error: could not get the record counts");System.exit(1);

}

System.out.println("--Count_Records_... end--");}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

5203ch12.qxd 8/11/05 4:35 PM Page 486

Page 516: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 487

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> select count(*) from dept;COUNT(*)

----------6

SQL> select count(*) from emp;COUNT(*)

----------14

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Count_Records_Using_PreparedStatement.java$ java Count_Records_Using_PreparedStatement oracle dept--Count_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2numOfRecords=6--Count_Records_... end--$ java Count_Records_Using_PreparedStatement oracle emp--Count_Records_... begin--conn=oracle.jdbc.driver.OracleConnection@edc3a2numOfRecords=14--Count_Records_... end--

Viewing the Oracle Database After Running the SolutionThere are no changes in the Oracle database after running the solution.

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> select count(*) from dsns;+----------+| count(*) |+----------+| 30 |+----------+1 row in set (0.00 sec)

mysql> select count(*) from connectors;+----------+| count(*) |+----------+| 7 |+----------+1 row in set (0.00 sec)

5203ch12.qxd 8/11/05 4:35 PM Page 487

Page 517: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT488

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Count_Records_Using_PreparedStatement.java$ java Count_Records_Using_PreparedStatement mysql dsns--Count_Records_... begin--conn=com.mysql.jdbc.Connection@8fce76numOfRecords=30--Count_Records_... end--$ java Count_Records_Using_PreparedStatement mysql connectors--Count_Records_... begin--conn=com.mysql.jdbc.Connection@8fce76numOfRecords=7--Count_Records_... end--

Viewing the MySQL Database After Running the SolutionThere are no changes in the MySQL database after running the solution.

12-26. What Is the Caching of PreparedStatement Objects?In general, caching PreparedStatement objects improves the performance of database applications.The following sections show how to cache PreparedStatement objects.

How It WorksYou use PreparedStatement objects when you query, insert, and update records. If you use the samePreparedStatement object over a range of input data, then you have cached your PreparedStatementobject. The next two examples show how to use PreparedStatement with and without a cache.

Using PreparedStatement Without CachingIn this example, a PreparedStatement object is created for every iteration of the loop. JDBC will cre-ate 100 PreparedStatement objects.

Connection conn = getConnection();...for(int num = 0; num < 100; num++) {

PreparedStatement ps =conn.prepareStatement("select x from t where c = " + num);

ResultSet rs = ps.executeQuery();// process result setrs.close();ps.close();

}

Using PreparedStatement with CachingIn this example, a PreparedStatement object is created for the entire loop. JDBC will create only onePreparedStatement object.

Connection conn = ...;...PreparedStatement ps =

conn.prepareStatement("select x from t where c = ?");for(int num = 0; num < 100; num++) {

5203ch12.qxd 8/11/05 4:35 PM Page 488

Page 518: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 489

ps.setInt(num);ResultSet rs = ps.executeQuery();// process result setrs.close();

}ps.close();

12-27. What Is the Pooling of PreparedStatement Objects?In general, pooling PreparedStatement objects improves performance of database applications. Thefollowing sections show how to pool PreparedStatement objects.

How It WorksConnection object pooling improves the performance of database applications, so this is anotherway to improve the performance of user queries. By pooling objects, you do not need to create anddelete them for every request. The object pool is a container, which not only allows objects to beborrowed from and returned but also creates them on the fly, whenever you want to draw moreobjects than you have at the pool. Typically, object pools are used to manage the sharing of objectsbetween multiple clients.

The main reason for creating a PreparedStatement object is to improve the performance of SQLstatements that will be executed many times between multiple clients. You can enhance the per-formance of database applications using JDBC by pooling PreparedStatement objects, which theJDBC 3.0 specification makes possible. When a PreparedStatement object is pooled, it is notdestroyed (garbage collected) when it is executed. Instead, it is returned to a pool so it can be usedagain and again, thus saving the overhead of creating a new statement each time it is used.

This is according to the JDBC API Tutorial and Reference:

From the developer’s viewpoint, using a PreparedStatement object that is pooled is exactlythe same as using one that is not pooled. An application creates a PreparedStatement object,sets its parameters (if any), executes it, and closes it in exactly the same way. This means thatafter a PreparedStatement object is closed, even if it is being pooled, it must be created againfor the next use. The only difference is that there should be an improvement in efficiencywhen a pooled statement is used multiple times.

A JDBC application can check (see the following code segment) to see if the driver supportsstatement pooling by calling the DatabaseMetaData method supportsStatementPooling. If the returnvalue is true, the application can use PreparedStatement objects armed with that knowledge.

SolutionHere’s the solution:

import java.sql.*;import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class CheckStatementPooling {public static boolean supportsStatementPooling(Connection conn)

throws SQLException {if ((conn == null) || (conn.isClosed())) {

return false;}

5203ch12.qxd 8/11/05 4:35 PM Page 489

Page 519: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT490

DatabaseMetaData dbmd = conn.getMetaData();if (dbmd == null) {

// database metadata NOT supported...// you should throw an exception or...stop hereSystem.out.println("can not determine if statement "+

"pooling is supported or not.");return false;

}

if (dbmd.supportsStatementPooling ()) {// statement pooling is supportedreturn true;

}else {

// statement pooling is NOT supportedreturn false;

}}

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }Connection conn = null;try {

System.out.println("--CheckStatementPooling begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("supportsStatementPooling="+

supportsStatementPooling(conn));System.out.println("--CheckStatementPooling end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(conn);

}}

}

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac CheckStatementPooling.java$ java CheckStatementPooling oracle--CheckStatementPooling begin--supportsStatementPooling=true--CheckStatementPooling end--

As you can see, Oracle’s JDBC driver supports the pooling of PreparedStatement objects, and itsupports implicit and explicit statement caching. For details, see the Oracle Database JDBC Devel-oper’s Guide and Reference 10g.

5203ch12.qxd 8/11/05 4:35 PM Page 490

Page 520: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT 491

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac CheckStatementPooling.java$ java CheckStatementPooling mysql--CheckStatementPooling begin--supportsStatementPooling=false--CheckStatementPooling end--

As you can see, MySQL’s JDBC driver does not support the pooling of PreparedStatementobjects.

DiscussionYou should note that the pooling of PreparedStatement objects takes place behind the scenes and isavailable only if connection pooling is available.

Table 12-6 has been adapted from the JDBC API Tutorial and Reference; it shows the standardproperties that a ConnectionPoolDataSource implementation may set for a PooledConnection object.

Table 12-6. Standard Connection Pool Properties

Property Type Description

maxStatements int The total number of statements the pool should keep open. 0(zero) indicates that caching of statements is disabled.

initialPoolSize int The number of physical connections the pool should containwhen it is created.

minPoolSize int The minimum number of physical connections in the pool.

maxPoolSize int The maximum number of physical connections the poolshould contain. 0 (zero) indicates no maximum size.

maxIdleTime int The number of seconds that a physical connection shouldremain unused in the pool before it is closed. 0 (zero) indicatesno time limit.

propertyCycle int The interval, in seconds, that the pool should wait beforeenforcing the policy defined by the values currently assigned tothese connection pool properties.

The JDBC API Tutorial and Reference also says this:

An application server that is managing a pool of PooledConnection objects uses these proper-ties to determine how to manage the pool. Because the getter and setter methods for propertiesare defined in ConnectionPoolDataSource implementations and are not part of the JDBC API,they are not available to clients. If there is a need to access the properties, such as, for example,when a tool is generating a list of them, they can be obtained through introspection.

For example, if a vendor XXX wrote a class that implemented the ConnectionPoolDataSourceinterface, the code for creating the ConnectionPoolDataSource object and setting its propertiesmight look like this:

5203ch12.qxd 8/11/05 4:35 PM Page 491

Page 521: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

XXXConnectionPoolDataSource connPDS = new XXXConnectionPoolDataSource ();connPDS.setMaxStatements(10);connPDS.setInitialPoolSize(5);connPDS.setMinPoolSize(1);connPDS.setMaxPoolSize(0); // no upper limit on pool sizeconnPDS.setMaxIdleTime(0); // no limit

connPDS.setPropertyCycle(300);

If a PreparedStatement is a pooled statement, then PreparedStatement.close() will return theobject to the pool rather than actually close the object (this is called a soft close).

CHAPTER 12 ■ WORKING WITH THE PREPAREDSTATEMENT492

5203ch12.qxd 8/11/05 4:35 PM Page 492

Page 522: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Passing Input Parameters toPreparedStatement

By using a PreparedStatement object, you can execute dynamic parameterized SQL statementsand pass input parameters at runtime to your statements. This chapter will show you how to passinput values of different data types (such as Timestamp, CLOB, BLOB, URL, String, InputStream, and so on)to a PreparedStatement object.

13-1. How Do You Pass Input Parameters to a PreparedStatementObject?You can pass many different data types (such as String, Blob, Clob, Float, and so on) to aPreparedStatement object.

A PreparedStatement object can have zero, one, or more parameter markers. Before sending a SQLstatement for execution, you must set all the parameter markers to the appropriate values (dependingon the data type of the parameters, represented by ?, a question mark). If a PreparedStatement objecthas zero parameter markers, then you don’t have to set any input parameters. You set parameters bycalling the appropriate setter method (PreparedStatement.setXXX()) for the type of the value being set,such as setString(), setClob(), setString(), and so on.

The PreparedStatement.setXXX() method has the following signature:

void setXXX(int parameterIndex, XXX value)

where parameterIndex is the ordinal position of the parameter. (Possible ordinal position values are1, 2, 3, and so on.) The second parameter is the value of the parameter. Note that the order of theinput parameters is not important. For example, if you have two input parameters, then the follow-ing two statements (where pstmt is a PreparedStatement object):

pstmt.setXXX(1, value-1);pstmt.setXXX(2, value-2);

are semantically equivalent to the following two statements:

pstmt.setXXX(2, value-2);pstmt.setXXX(1, value-1);

PreparedStatement enables you to have input parameters of almost any data type. For example,if the parameter is of type double in the Java programming language, the setter method to use issetDouble(). If the parameter is of type String in the Java programming language, the setter methodto use is setString().

Table 13-1 lists the signatures of the PreparedStatement.setXXX() methods.

493

C H A P T E R 1 3

■ ■ ■

5203ch13.qxd 8/14/05 5:44 PM Page 493

Page 523: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT494

Table 13-1. PreparedStatement.setXXX() Method Summary

Return Type Method

void setArray(int i, Array x) Sets the designated parameter to the givenArray object.

void setAsciiStream Sets the designated parameter to the given (int parameterIndex, input stream, which will have the specified InputStream x, int length) number of bytes. (By using the int data

type for length, the JDBC API assumes thatthe stream will be up to 2GB.)

void setBigDecimal Sets the designated parameter to the given(int parameterIndex,BigDecimal x) java.math.BigDecimal value.

void setBinaryStream Sets the designated parameter to the given(int parameterIndex, input stream, which will have the specifiedInputStream x, int length) number of bytes. (By using the int data

type for length, the JDBC API assumes thatthe stream will be up to 2GB.)

void setBlob(int i, Blob x) Sets the designated parameter to the givenBlob object.

void setBoolean Sets the designated parameter to the given (int parameterIndex, boolean x) Java boolean value.

void setByte Sets the designated parameter to the given(int parameterIndex, byte x) Java byte value.

void setBytes Sets the designated parameter to the given(int parameterIndex, byte[] x) Java array of bytes.

void setCharacterStream Sets the designated parameter to the given(int parameterIndex, Reader object, which is the given numberReader reader, int length) of characters long.

void setClob(int i, Clob x) Sets the designated parameter to the givenClob object.

void setDate Sets the designated parameter to the given(int parameterIndex, Date x) java.sql.Date value.

void setDate(int parameterIndex, Sets the designated parameter to the givenDate x, Calendar cal) java.sql.Date value, using the given

Calendar object.

void setDouble Sets the designated parameter to the given(int parameterIndex, double x) Java double value.

void setFloat Sets the designated parameter to the given(int parameterIndex, float x) Java float value.

void setInt Sets the designated parameter to the given(int parameterIndex, int x) Java int value.

void setLong Sets the designated parameter to the given(int parameterIndex, long x) Java long value.

void setNull Sets the designated parameter to SQL NULL.(int parameterIndex, int sqlType)

void setNull(int paramIndex, Sets the designated parameter to SQL NULL.int sqlType, String typeName)

void setObject(int parameterIndex, Sets the value of the designated parameter Object x) using the given object.

void setObject(int parameterIndex, Sets the value of the designated parameter Object x, int targetSqlType) with the given object.

5203ch13.qxd 8/12/05 12:22 PM Page 494

Page 524: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 495

Table 13-1. PreparedStatement.setXXX() Method Summary

Return Type Method

void setObject(int parameterIndex, Sets the value of the designated parameterObject x, int targetSqlType, with the given object.int scale)

void setRef(int i, Ref x) Sets the designated parameter to the givenREF(<structured-type>) value.

void setShort(int parameterIndex, Sets the designated parameter to the givenshort x) Java short value.

void setString(int parameterIndex, Sets the designated parameter to the givenString x) Java String value.

void setTime(int parameterIndex, Sets the designated parameter to the givenTime x) java.sql.Time value.

void setTime(int parameterIndex, Sets the designated parameter to the givenTime x, Calendar cal) java.sql.Time value, using the given

Calendar object.

void setTimestamp(int parameterIndex, Sets the designated parameter to the givenTimestamp x) java.sql.Timestamp value.

void setTimestamp(int parameterIndex, Sets the designated parameter to the givenTimestamp x, Calendar cal) java.sql.Timestamp value, using the given

Calendar object.

void setURL(int parameterIndex, URL x) Sets the designated parameter to the givenjava.net.URL value.

13-2. How Do You Use PreparedStatement.setArray()?The following sections show how to pass a java.sql.Array object (as an input parameter) toa PreparedStatement object.

How It WorksThe signature of PreparedStatement.setArray() is as follows:

void setArray(int parameterIndex, java.sql.Array array)throws SQLException

This sets the designated parameter to the given Array object. The driver converts this to a SQLARRAY value when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• array: This is a java.sql.Array object, which must be implemented by a class.

What is java.sql.Array? This is the mapping in the Java programming language for the SQLtype ARRAY. By default, an Array value is a transaction-duration reference to a SQL ARRAY value. Bydefault, an Array object is implemented using a SQL LOCATOR (array) internally, which means that anArray object contains a logical pointer to the data in the SQL ARRAY value rather than containing theARRAY value’s data.

The Array interface provides methods for bringing a SQL ARRAY value’s data to the client aseither an array or a ResultSet object. If the elements of the SQL ARRAY are a user-defined type, theymay be custom mapped. To create a custom mapping, follow these steps:

5203ch13.qxd 8/12/05 12:22 PM Page 495

Page 525: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT496

1. Create a class that implements the SQLData interface for the user-defined type to be custommapped.

2. Make an entry in a type map that contains the following:

• The fully qualified SQL type name of the user-defined type

• The Class object for the class that implements SQLData

When a type map with an entry for the base type is supplied to the methods getArray andgetResultSet, the mapping it contains will map the elements of the ARRAY value. If no type map issupplied, which will typically be the case, the connection’s type map is used by default. If the con-nection’s type map, or a type map supplied to a method, has no entry for the base type, the elementsare mapped according to the standard mapping.

The Oracle database supports the ARRAY feature, but the MySQL database does not. (The featureis not implemented in the MySQL database.) The java.sql.Array implementation is provided bythe driver.

In the following sections, I will provide an example of using PreparedStatement.setArray() foran Oracle database. To complete this example, first you need to set up some Oracle database objects,as shown in the next section.

Setting Up the Oracle DatabaseUsing Oracle, you can define type objects such as the VARRAY type, which is an array of data types.The following defines a new type, CHAR_ARRAY, which is an array of CHAR(2). In this example, arrayelement types are CHAR(2), but these can be any valid data type. Therefore, you can use CHAR_ARRAYto represent the column type within the table definition.

SQL> CREATE OR REPLACE TYPE CHAR_ARRAY AS VARRAY(10) OF CHAR(2);2 /

Type created.

SQL> desc CHAR_ARRAY;CHAR_ARRAY VARRAY(10) OF CHAR(2)

Now, create a table that uses the VARRAY type in the Oracle database. Using Oracle, you candefine a table that has columns of VARRAY type, as shown here:

SQL> create table CHAR_ARRAY_TABLE(id varchar(10), array CHAR_ARRAY);Table created.

SQL> desc CHAR_ARRAY_TABLE;Name Null? Type----------- -------- ------------ID VARCHAR2(10)ARRAY CHAR_ARRAY

SQL> insert into CHAR_ARRAY_TABLE(id, array)2 values('id100', CHAR_ARRAY('aa', 'bb', 'cc'));

SQL> insert into CHAR_ARRAY_TABLE(id, array)2 values('id100', CHAR_ARRAY('aa', 'dd', 'pp'));

SQL> commit;Commit complete.

5203ch13.qxd 8/12/05 12:22 PM Page 496

Page 526: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 497

SQL>SQL> select * from CHAR_ARRAY_TABLE;

ID ARRAY----- ------------------------------id100 CHAR_ARRAY('aa', 'bb', 'cc')id100 CHAR_ARRAY('aa', 'dd', 'pp')

SolutionThe following program demonstrates how to use PreparedStatement.setArray(). This is accordingto Oracle (the JDBC Developer’s Guide and Reference, Release 2):

An ArrayDescriptor is an object of the oracle.sql.ArrayDescriptor class and describes the SQLtype of an array. Only one array descriptor is necessary for any one SQL type. The drivercaches ArrayDescriptor objects to avoid re-creating them if the SQL type has already beenencountered. You can reuse the same descriptor object to create multiple instances of anoracle.sql.ARRAY object for the same array type.

Here’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import oracle.sql.ArrayDescriptor;import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetArray {

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }Connection conn = null;PreparedStatement pstmt = null;java.sql.Array sqlArray = null;try {

System.out.println("--Demo_PreparedStatement_SetArray begin--");

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// For oracle you need an array descriptor specifying// the type of the array and a connection to the database// the first parameter must match with the SQL ARRAY type createdArrayDescriptor arrayDescriptor =

ArrayDescriptor.createDescriptor("CHAR_ARRAY", conn);// then obtain an Array filled with the content belowString[] content = { "v1", "v2", "v3", "v4" };sqlArray= new oracle.sql.ARRAY(arrayDescriptor, conn, content);

// prepare queryString query = "insert into CHAR_ARRAY_TABLE(id, array) values(?, ?)";

5203ch13.qxd 8/12/05 12:22 PM Page 497

Page 527: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT498

// create PrepareStatement objectpstmt = conn.prepareStatement(query);// set input parameters to PreparedStatement object// the order of setting input parameters is not importantpstmt.setString(1, "id300");pstmt.setArray(2, sqlArray);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_SetArray end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetArray.java$ java Demo_PreparedStatement_SetArray oracle--Demo_PreparedStatement_SetArray begin--conn=oracle.jdbc.driver.OracleConnection@1edc073---------------rowCount=1--Demo_PreparedStatement_SetArray end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from CHAR_ARRAY_TABLE;ID ARRAY-------- -----------------------------------id100 CHAR_ARRAY('aa', 'bb', 'cc')id100 CHAR_ARRAY('aa', 'dd', 'pp')id300 CHAR_ARRAY('v1', 'v2', 'v3', 'v4')

13-3. How Do You Use PreparedStatement.setAsciiStream()?The following sections show you how to pass an InputStream object (as an input parameter) toa PreparedStatement object.

How It WorksYou can use setAsciiStream() on both the Oracle and MySQL databases. In Oracle, when a columndata type is LONG, VARCHAR, or CLOB, then you can use setAsciiStream(). In MySQL, when a column datatype is TINYTEXT, TEXT, MEDIUMTEXT, or LONGTEXT, then you can use setAsciiStream().

5203ch13.qxd 8/12/05 12:22 PM Page 498

Page 528: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 499

The signature of PreparedStatement.setAsciiStream() is as follows:

public void setAsciiStream(int parameterIndex,InputStream stream,int length)

throws SQLException

This sets the designated parameter to the given input stream, which will have the specifiednumber of bytes. When a very large ASCII value is input to a LONGVARCHAR parameter, it may be morepractical to send it via java.io.InputStream. Data will be read from the stream as needed untilreaching the end of the file. The JDBC driver will do any necessary conversion from ASCII to thedatabase char format.

This stream object can either be a standard Java stream object or be your own subclass thatimplements the standard interface.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• stream: The Java input stream that contains the ASCII parameter value.

• length: The number of bytes in the stream.

This throws a SQLException if a database access error occurs.

SolutionThe following solution uses PreparedStatement.setAsciiStream() to solve the problem. This class(Demo_PreparedStatement_SetAsciiStream) will read a text file and then insert the name and contentof the file as a record to the LONG_VARCHAR_TABLE table.

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetAsciiStream {

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }String fileName = args[1];Connection conn = null;PreparedStatement pstmt = null;String query = null;try {

System.out.println("--Demo_PreparedStatement_setAsciiStream begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare text streamFile file = new File(fileName);int fileLength = (int) file.length();InputStream stream = (InputStream) new FileInputStream(file);

// prepare SQL queryquery = "insert into LONG_VARCHAR_TABLE(id, stream) values(?, ?)";

5203ch13.qxd 8/12/05 12:22 PM Page 499

Page 529: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT500

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, fileName);pstmt.setAsciiStream(2, stream, fileLength);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_setAsciiStream end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseUsing Oracle, define a table that has a column of the LONG VARCHAR type (up to 2.14GB), as shown here:

SQL> create table LONG_VARCHAR_TABLE(id varchar(12), stream LONG VARCHAR);Table created.

SQL> desc LONG_VARCHAR_TABLE;Name Null? Type----------- -------- ------------ID VARCHAR2(12)STREAM LONG

Setting Up the Data FileUse the following text file as input to the program:

$ cat file1.txtthis is line 1.this is line two.This is the last line.

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetAsciiStream.java$ java Demo_PreparedStatement_SetAsciiStream oracle file1.txt--Demo_PreparedStatement_setAsciiStream begin--conn=oracle.jdbc.driver.OracleConnection@d251a3---------------rowCount=1--Demo_PreparedStatement_setAsciiStream end--

5203ch13.qxd 8/12/05 12:22 PM Page 500

Page 530: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 501

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from LONG_VARCHAR_TABLE;ID STREAM--------- -----------------------file1.txt this is line 1.

this is line two.This is the last line.

Setting Up the MySQL DatabaseUsing MySQL, define a table that has a column of the TEXT type, up to a maximum length of65,535 (2^16 - 1) characters.

mysql> create table LONG_VARCHAR_TABLE (id VARCHAR(12), stream TEXT);Query OK, 0 rows affected (0.10 sec)

mysql> desc LONG_VARCHAR_TABLE;+--------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+--------+-------------+------+-----+---------+-------+| id | varchar(12) | YES | | NULL | || stream | text | YES | | NULL | |+--------+-------------+------+-----+---------+-------+2 rows in set (0.01 sec)

Setting Up the Data FileUse the following text file as input to the program:

$ cat file1.txtthis is line 1.this is line two.This is the last line.

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ jvac Demo_PreparedStatement_SetAsciiStream.java$ java Demo_PreparedStatement_SetAsciiStream mysql file1.txt--Demo_PreparedStatement_setAsciiStream begin--conn=com.mysql.jdbc.Connection@15c7850---------------rowCount=1--Demo_PreparedStatement_setAsciiStream end--

5203ch13.qxd 8/12/05 12:22 PM Page 501

Page 531: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT502

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from LONG_VARCHAR_TABLE;+-----------+----------------------------+| id | stream |+-----------+----------------------------+| file1.txt | this is line 1. || | this is line two. || | This is the last line. |+-----------+----------------------------+1 row in set (0.02 sec)

13-4. How Do You Use PreparedStatement.setBigDecimal()?The following sections show how to pass a BigDecimal object (as an input parameter) toa PreparedStatement object.

How It WorksYou can use setBigDecimal() on both the Oracle and MySQL databases. In Oracle, when a columndata type is NUMBER, then you can use setBigDecimal(). In MySQL, when a column data type isFLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT, SMALLINT, MEDIUMINT, INTEGER, or BIGINT,then you can use setBigDecimal().

The signature of PreparedStatement.setBigDecimal() is as follows:

public void setBigDecimal(int parameterIndex,java.math.BigDecimal bigDecimal)

throws SQLException

This sets the designated parameter to the given java.math.BigDecimal value (immutable,arbitrary-precision, signed decimal numbers; a BigDecimal consists of an arbitrary-precision integerthat is an unscaled value and a non-negative 32-bit integer scale, which represents the number ofdigits to the right of the decimal point. The driver converts this to a SQL NUMERIC value when it sendsit to the database.

These are the parameters:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• bigDecimal: The parameter value.

This throws SQLException if a database access error occurs.

SolutionThe main() method of this class will read three values (dbVendor, id, and a big decimal number) andthen insert a new record by using PreparedStatement.setBigDecimal():

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetBigDecimal {

5203ch13.qxd 8/12/05 12:22 PM Page 502

Page 532: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 503

public static void main(String[] args) {String dbVendor = args[0]; // { "mysql", "oracle" }String id = args[1];java.math.BigDecimal bigDecimal = new java.math.BigDecimal(args[2]);Connection conn = null;PreparedStatement pstmt = null;String query = null;try {

System.out.println("--Demo_PreparedStatement_setBigDecimal begin--");

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare SQL queryquery = "insert into BIG_DECIMAL_TABLE(id, big_decimal) values(?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, id);pstmt.setBigDecimal(2, bigDecimal);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_setBigDecimal end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseUsing Oracle, define a table that has a column of the NUMBER type, as shown next. Oracle’s data typeNUMBER(p, s) denotes a number having the precision p and scale s. The precision p can rangefrom 1 to 38, and the scale s can range from -84 to 127.

SQL> create table BIG_DECIMAL_TABLE(id VARCHAR(12), big_decimal NUMBER(16, 5));

Table created.

SQL> desc BIG_DECIMAL_TABLE;Name Null? Type---------------- -------- -------------ID VARCHAR2(12)BIG_DECIMAL NUMBER(16,5)

5203ch13.qxd 8/12/05 12:22 PM Page 503

Page 533: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT504

SQL> insert into BIG_DECIMAL_TABLE(id, big_decimal)values ('id-1', 1234567.123456);

SQL> insert into BIG_DECIMAL_TABLE(id, big_decimal)values ('id-2', 99994567.778899);

SQL> insert into BIG_DECIMAL_TABLE(id, big_decimal)values ('id-3', 123.4455);

SQL> insert into BIG_DECIMAL_TABLE(id, big_decimal)values ('id-4', 12.123456789);

SQL> commit;SQL> select * from BIG_DECIMAL_TABLE;

ID BIG_DECIMAL--------- -----------id-1 1234567.12id-2 99994567.8id-3 123.4455id-4 12.12346

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetBigDecimal.java$ java Demo_PreparedStatement_SetBigDecimal oracle id-44 98765.1234--Demo_PreparedStatement_setBigDecimal begin--conn=oracle.jdbc.driver.OracleConnection@1edc073---------------rowCount=1--Demo_PreparedStatement_setBigDecimal end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from BIG_DECIMAL_TABLE;

ID BIG_DECIMAL--------- -----------id-1 1234567.12id-2 99994567.8id-3 123.4455id-4 12.12346id-44 98765.1234

Setting Up the MySQL DatabaseUsing MySQL, define a table that has a column of the DECIMAL type, as shown next. For example,using DECIMAL(5, 2) , the precision (5) represents the number of significant decimal digits that willbe stored for values, and the scale (2) represents the number of digits that will be stored followingthe decimal point. In this case, therefore, the range of values that can be stored in this column is from-99.99 to 99.99.

mysql> create table big_decimal_table(id varchar(12), big_decimal decimal(15, 5));Query OK, 0 rows affected (0.04 sec)

5203ch13.qxd 8/12/05 12:22 PM Page 504

Page 534: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 505

mysql> desc big_decimal_table;+-------------+---------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------------+---------------+------+-----+---------+-------+| id | varchar(12) | YES | | NULL | || big_decimal | decimal(15,5) | YES | | NULL | |+-------------+---------------+------+-----+---------+-------+2 rows in set (0.00 sec)

mysql> insert into big_decimal_table(id, big_decimal)values('id-1', 123456789.12345);

mysql> insert into big_decimal_table(id, big_decimal)values('id-2', 123456789.12345678);

mysql> insert into big_decimal_table(id, big_decimal)values('id-3', 1234567890123.12345);

mysql> insert into big_decimal_table(id, big_decimal)values('id-4', 123.123456789);

mysql> select * from big_decimal_table;+------+-------------------+| id | big_decimal |+------+-------------------+| id-1 | 123456789.12345 || id-2 | 123456789.12346 || id-3 | 99999999999.99999 || id-4 | 123.12346 |+------+-------------------+4 rows in set (0.00 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetBigDecimal.java$ java Demo_PreparedStatement_SetBigDecimal mysql id-99 5678.1234--Demo_PreparedStatement_setBigDecimal begin--conn=com.mysql.jdbc.Connection@1ded0fd---------------rowCount=1--Demo_PreparedStatement_setBigDecimal end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from big_decimal_table;+-------+-------------------+| id | big_decimal |+-------+-------------------+| id-3 | 99999999999.99999 || id-2 | 123456789.12346 || id-1 | 123456789.12345 || id-4 | 123.12346 || id-99 | 5678.12340 |+-------+-------------------+5 rows in set (0.00 sec)

5203ch13.qxd 8/12/05 12:22 PM Page 505

Page 535: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT506

13-5. How Do You Use PreparedStatement.setBinaryStream()?The following sections show how to pass a binary stream (InputStream represents binary data as aninput parameter) to a PreparedStatement object.

How It WorksYou can use PreparedStatement.setBinaryStream() on both the Oracle and MySQL databases.

In Oracle, when a column data type is RAW or LONG RAW, then you can use setBinaryStream().Oracle’s RAW( length ) represents raw binary data of length bytes. The maximum size is 2,000 bytes.You must specify a size for a RAW value; Oracle’s LONG RAW represents raw binary data of a variablelength up to 2GB.

In MySQL, when a column data type is TINYBLOB, BLOB, MEDIUMBLOB, or LONGBLOB, then you canuse setBinaryStream().

The signature of setBinaryStream() is as follows:

public void setBinaryStream(int parameterIndex,InputStream stream,int length) throws SQLException

This sets the designated parameter to the given input stream, which will have the specifiednumber of bytes. When a large binary value is input to a LONGVARBINARY parameter, it may be morepractical to send it via a java.io.InputStream object. The data will be read from the stream as neededuntil the end of the file is reached. This stream object can be either a standard Java stream object oryour own subclass that implements the standard interface.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• stream: The Java input stream that contains the binary parameter value.

• length: The number of bytes in the stream.

This throws a SQLException if a database access error occurs.

Setting Up the Oracle DatabaseUsing Oracle, define a table that has the RAW and LONG RAW types, as shown next:

SQL> create table binary_table(2 id VARCHAR(12),3 raw_column RAW(2000),4 long_raw_column LONG RAW);

Table created.

SQL> desc binary_table;Name Null? Type---------------- -------- -------------ID VARCHAR2(12)RAW_COLUMN RAW(2000)LONG_RAW_COLUMN LONG RAW

SolutionThis solution uses PreparedStatement.setBinaryStream() and inserts a new record intobinary_table:

5203ch13.qxd 8/12/05 12:22 PM Page 506

Page 536: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 507

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetBinaryStream {

public static void main(String[] args) {// set up input parameters from command line:String dbVendor = args[0]; // { "mysql", "oracle" }String id = args[1];String smallFileName = args[2];String largeFileName = args[3];Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Demo_PreparedStatement_setBinaryStream begin--");// get a database Connection objectconn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare small binary streamFile smallFile = new File(smallFileName);int smallFileLength = (int) smallFile.length();InputStream smallStream = (InputStream) new FileInputStream(smallFile);

// prepare large binary streamFile largeFile = new File(largeFileName);int largeFileLength = (int) largeFile.length();InputStream largeStream = (InputStream) new FileInputStream(largeFile);

// prepare SQL queryString query = "insert into binary_table" +

"(id, raw_column, long_raw_column) values(?, ?, ?)";

// begin transactionconn.setAutoCommit(false);

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, id);pstmt.setBinaryStream(2, smallStream, smallFileLength);pstmt.setBinaryStream(3, largeStream, largeFileLength);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);

// end transactionconn.commit();System.out.println("--Demo_PreparedStatement_setBinaryStream end--");

}

5203ch13.qxd 8/12/05 12:22 PM Page 507

Page 537: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT508

catch(Exception e){e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Data FileI will use the binary tomcat_logo.gif file (1,934 bytes) and the binary tomcat_f14.gif file (37,454bytes) as inputs to the program. Figure 13-1 shows the file thumbnails, and Figure 13-2 shows thefiles and their associated sizes.

Figure 13-1. Binary files to be inserted as a binary stream

Figure 13-2. Binary files and their associated sizes

5203ch13.qxd 8/12/05 12:22 PM Page 508

Page 538: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 509

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetBinaryStream.java$ java Demo_PreparedStatement_SetBinaryStream oracle id-100 \c:/temp/tomcat/tomcat_logo.gif c:/temp/tomcat/tomcat_f14.gif

--Demo_PreparedStatement_setBinaryStream begin--conn=oracle.jdbc.driver.OracleConnection@d251a3---------------rowCount=1--Demo_PreparedStatement_setBinaryStream_Oracle end--

Viewing the Oracle Database After Running the SolutionTo verify that you have inserted the binary data correctly, use a small Java program to display thebinary data inserted:

java DemoDisplayBinary oracle id-100

Running the program generates the image shown in Figure 13-3.

Figure 13-3. Retrieving the binary data from the database

Here is a program to display binary data:

import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

/*** This class displays binary objects in a JFrame*/public class DemoDisplayBinary extends JPanel {

/*** Constructor to display BLOB object.* @param dbVendor database vendor.

5203ch13.qxd 8/12/05 12:22 PM Page 509

Page 539: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT510

* @param id the primary key to the MyPictures table*/public DemoDisplayBinary(String dbVendor, String id) throws Exception {

// materialize BLOB onto clientObject[] binaryData = getBinaryData(dbVendor, id);setLayout(new GridLayout(1, 2));ImageIcon icon1 = new ImageIcon((byte[])binaryData[0]) ;JLabel photoLabel1 = new JLabel(icon1) ;add(photoLabel1);ImageIcon icon2 = new ImageIcon((byte[])binaryData[1]) ;JLabel photoLabel2 = new JLabel(icon2) ;add(photoLabel2);

}

/*** Extract and return the BLOB object.* @param dbVendor database vendor.* @param id the primary key to the BLOB object.*/public static Object[] getBinaryData(String dbVendor, String id)

throws Exception {Connection conn = null ;ResultSet rs = null;PreparedStatement pstmt = null;String query = "SELECT raw_column, long_raw_column "+

"FROM binary_table WHERE id = ?";try {

conn = VeryBasicConnectionManager.getConnection(dbVendor);Object[] results = new Object[2];pstmt = conn.prepareStatement(query) ;pstmt.setString(1, id);rs = pstmt.executeQuery();rs.next();// materialize binary data onto clientresults[0] = rs.getBytes("RAW_COLUMN");results[1] = rs.getBytes("LONG_RAW_COLUMN");return results;

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

public static void main(String args[]) throws Exception {String dbVendor = args[0]; // { "mysql", "oracle" }String id = args[1];UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel") ;JFrame frame = new JFrame("Binary Demo for" + dbVendor + " Database");frame.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {System.exit(0);

}});

5203ch13.qxd 8/12/05 12:22 PM Page 510

Page 540: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 511

Figure 13-4. Binary files to be inserted as a binary stream

frame.setContentPane(new DemoDisplayBinary(dbVendor, id)) ;frame.pack();frame.setVisible(true);

}}

Setting Up the MySQL DatabaseUsing MySQL, define a table that has BLOB and MEDIUMBLOB types, as shown next. A BLOB column canhold a maximum length of 65,535 (2^16 - 1) characters, and a MEDIUMBLOB column can hold a maxi-mum length of 16,777,215 (2^24 - 1) characters.

mysql> create table binary_table(-> id VARCHAR(12),-> raw_column BLOB,-> long_raw_column MEDIUMBLOB);

Query OK, 0 rows affected (0.04 sec)

mysql> desc binary_table;+-----------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-----------------+-------------+------+-----+---------+-------+| id | varchar(12) | YES | | NULL | || raw_column | blob | YES | | NULL | || long_raw_column | mediumblob | YES | | NULL | |+-----------------+-------------+------+-----+---------+-------+3 rows in set (0.01 sec)

Setting Up the Data File

I will use the binary files anna-1.jpg (5,789 bytes) and anna-2.jpg (112,375 bytes) as input to the program.Figure 13-4 shows the file thumbnails, and Figure 13-5 shows the files and their associated sizes.

5203ch13.qxd 8/12/05 12:22 PM Page 511

Page 541: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT512

Figure 13-6. Retrieving binary data from the database

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetBinaryStream.java$ java Demo_PreparedStatement_SetBinaryStream mysql id-100c:/temp/anna/anna-1.jpg c:/temp/anna/anna-2.jpg--Demo_PreparedStatement_setBinaryStream_MySQL begin--conn=com.mysql.jdbc.Connection@15c7850---------------rowCount=1--Demo_PreparedStatement_setBinaryStream_MySQL end--

Viewing the MySQL Database After Running the SolutionTo verify that you have inserted the binary data correctly, you can use a small Java program to dis-play the binary data inserted:

java DemoDisplayBinary mysql id-100

This generates the image shown in Figure 13-6 (I have made the image smaller to fit the page).

Figure 13-5. Binary files and their associated sizes

5203ch13.qxd 8/12/05 12:22 PM Page 512

Page 542: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 513

13-6. How Do You Use PreparedStatement.setBlob()?In RDBMS systems, BLOB is a Binary Large OBject. java.sql.Blob is the mapping in the Java program-ming language of a SQL BLOB value. The following sections show how to pass a java.sql.Blob objectto a PreparedStatement object.

How It WorksYou can use PreparedStatement.setBlob() on both the Oracle and MySQL databases. Using the Oracledatabase, when a column data type is BLOB, then you can use setBlob(). Oracle’s BLOB represents rawbinary data of a variable length up to 2GB. Using the MySQL database, when a column data type isTINYBLOB with a maximum length of 255 (2^8 - 1) characters, BLOB with a maximum length of65,535 (2^16 - 1) characters, MEDIUMBLOB with a maximum length of 16,777,215 (2^24 - 1) characters, orLONGBLOBwith a maximum length of 4,294,967,295 or 4G (2^32 - 1) characters, then you can use setBlob().

The signature of setBlob() is as follows:

public void setBlob(int parameterIndex, java.sql.Blob blob)throws SQLException

This sets the designated parameter to the given Blob object. The driver converts this to a SQL BLOBvalue when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• blob: A Blob object that maps a SQL BLOB value.

This throws a SQLException if a database access error occurs.

SolutionThe following program uses PreparedStatement.setBlob() and inserts a new record into binary_table.Note that in the JDBC API, there is no constructor to build a java.sql.Blob object directly; there-fore, to use the setBlob() method, you need to create a ResultSet object, extract a Blob object, andthen pass it to the PreparedStatement.setBlob() method. I will show how to extract a Blob frombinary_table and then insert it into the blob_table table using the PreparedStatement.setBlob()method.

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetBlob {

public static void main(String[] args) {// set up input parameters from command line:String dbVendor = args[0]; // { "mysql", "oracle" }String id = args[1];

Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;java.sql.Blob blob = null;try {

5203ch13.qxd 8/12/05 12:22 PM Page 513

Page 543: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT514

// get a database Connection objectconn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare blob object from an existing binary columnString query1 = "select photo from my_pictures where id = ?";pstmt = conn.prepareStatement(query1);pstmt.setString(1, id);rs = pstmt.executeQuery();rs.next();blob = rs.getBlob(1);

// prepare SQL query for inserting a new row using setBlob()String query = "insert into blob_table(id, blob_column) values(?, ?)";

// begin transactionconn.setAutoCommit(false);

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, id);pstmt.setBlob(2, blob);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);

// end transactionconn.commit();System.out.println("--Demo_PreparedStatement_SetBlob end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseUsing Oracle, define a table that has a BLOB type, as shown here:

SQL> create table blob_table (2 id VARCHAR(12) NOT NULL PRIMARY KEY,3 blob_column BLOB default empty_blob()4 );

Table created.

5203ch13.qxd 8/12/05 12:22 PM Page 514

Page 544: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 515

SQL> desc blob_table;Name Null? Type--------------------- -------- -------------ID NOT NULL VARCHAR2(12)BLOB_COLUMN BLOB

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetBlob.java$ java Demo_PreparedStatement_SetBlob oracle tiger1--Demo_PreparedStatement_SetBlob begin--conn=oracle.jdbc.driver.OracleConnection@d251a3---------------rowCount=1--Demo_PreparedStatement_SetBlob end--

Viewing the Oracle Database After Running the SolutionTo verify that you have inserted the binary data correctly, you can use a small Java program to dis-play the binary data inserted:

java DemoDisplayBlob oracle tiger1

Running this program generates the image shown in Figure 13-7.

Here is a Java program to display Blob data:

import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

Figure 13-7. Retrieving binary data from an Oracle database

5203ch13.qxd 8/12/05 12:22 PM Page 515

Page 545: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT516

/*** This class displays blob objects in a JFrame*/public class DemoDisplayBlob extends JPanel {

/*** Constructor to display BLOB object.* @param dbVendor database vendor* @param id the primary key to the MyPictures table*/public DemoDisplayBlob(String dbVendor, String id) throws Exception {

// materialize BLOB onto clientjava.sql.Blob blob = getBlob(dbVendor, id);byte[] data = blob.getBytes(1, (int)blob.length());

// add blob to framesetLayout(new GridLayout(1, 1));JLabel label = new JLabel(new ImageIcon(data)) ;add(label);

}

/*** Extract and return the BLOB object.* @param dbVendor database vendor* @param id the primary key to the BLOB object.*/public static java.sql.Blob getBlob(String dbVendor, String id)

throws Exception {Connection conn = null ;ResultSet rs = null;PreparedStatement pstmt = null;String query = "SELECT blob_column FROM blob_table WHERE id = ?";

try {conn = VeryBasicConnectionManager.getConnection(dbVendor);pstmt = conn.prepareStatement(query) ;pstmt.setString(1, id);rs = pstmt.executeQuery();rs.next();// materialize binary data onto clientjava.sql.Blob blob = rs.getBlob(1);return blob;

}finally {

DatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

public static void main(String args[]) throws Exception {UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel") ;JFrame frame = new JFrame("Blob Demo for Oracle Database");frame.addWindowListener(new WindowAdapter() {

public void windowClosing(WindowEvent e) {System.exit(0);

}});

5203ch13.qxd 8/12/05 12:22 PM Page 516

Page 546: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 517

String dbVendor = args[0]; // { "mysql", "oracle" }String id = args[1];frame.setContentPane(new DemoDisplayBlob(dbVendir, id)) ;frame.pack();frame.setVisible(true);

}}

Setting Up the MySQL DatabaseUsing MySQL, define a table that has the BLOB and MEDIUMBLOB types, as shown next. A BLOB columncan hold a maximum length of 65,535 (2^16 - 1) characters, and a MEDIUMBLOB column can hold a maxi-mum length of 16,777,215 (2^24 - 1) characters.

mysql> create table blob_table (-> id VARCHAR(12) NOT NULL PRIMARY KEY,-> blob_column BLOB-> );

Query OK, 0 rows affected (0.10 sec)

mysql> desc blob_table;+-------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------------+-------------+------+-----+---------+-------+| id | varchar(12) | | PRI | | || blob_column | blob | YES | | NULL | |+-------------+-------------+------+-----+---------+-------+2 rows in set (0.01 sec)

I will show how to use existing binary data in a database to create a java.sql.Blob object. To dothis, I will use the binary column shown in Figure 13-8 to create a java.sql.Blob object and then insertit into blob_table table using the PreparedStatement.setBlob() method.

Figure 13-8. Displaying java.sql.Blob data from a MySQL database

5203ch13.qxd 8/12/05 12:22 PM Page 517

Page 547: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT518

Figure 13-9. Displaying binary data (java.sql.Blob) from a MySQL database

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetBlob.java$ java Demo_PreparedStatement_SetBlob mysql id-100--Demo_PreparedStatement_SetBlob begin--conn=com.mysql.jdbc.Connection@15c7850---------------rowCount=1--Demo_PreparedStatement_SetBlob end--

Viewing the MySQL Database Content After Running the Solution (First Method)Running the solution will create Figure 13-9.

Viewing the MySQL Database Content After Running the Solution (Second Method)To verify that you have inserted the binary data correctly, you can use a small Java program to displaythe binary data inserted:

java DemoDisplayBlob mysql id-100

Running this program generates the image shown in Figure 13-10 (I have made this image smallerto fit the page).

5203ch13.qxd 8/12/05 12:22 PM Page 518

Page 548: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 519

13-7. How Do You Use PreparedStatement.setBoolean()?The following sections show how to pass a boolean value (a Java primitive data type representingtrue and false) to a PreparedStatement object.

How It WorksYou can use setBoolean() on both the Oracle and MySQL databases. Oracle does not have a built-inboolean data type; in Oracle, when a column data type is NUMBER, then you can use the setBoolean()method. A TINYINT is the usual type to use when storing a boolean in MySQL. You can use theResultSet.get and setBoolean(int, boolean) methods when using TINYINT. PreparedStatement.setBoolean() will use 1/0 for values if your MySQL version is greater than or equal to 3.21.23.

The signature of setBoolean() is as follows:

public void setBoolean(int parameterIndex,boolean bool)

throws SQLException

This sets the designated parameter to the given Java boolean value. The driver converts this toa SQL BIT value when it sends it to the database.

The following are the parameters:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• bool: The parameter value.

This throws a SQLException if a database access error occurs.

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetBoolean {

public static void main(String[] args) {String dbVendor = args[0]; // values are: { "mysql", "oracle" }String idValue = args[1];boolean booleanValue;if (args[2].equals("0")) {

booleanValue = false;}else {

booleanValue = true;}

Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Demo_PreparedStatement_setBoolean begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);

5203ch13.qxd 8/12/05 12:22 PM Page 519

Page 549: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT520

System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query ="insert into boolean_table(id, boolean_column) values(?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, idValue);pstmt.setBoolean(2, booleanValue);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_setBoolean end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseUsing Oracle, define a table that has a column of the NUMBER type, as shown next. Oracle’s data typeNUMBER(p, s)denotes a number having the precision p and scale s. The precision p can range from 1 to 38,and the scale s can range from -84 to 127.

SQL> create table boolean_table(id varchar(12), boolean_column NUMBER(1));

Table created.

SQL> desc boolean_table;Name Null? Type-------------------------------- -------- -------------ID VARCHAR2(12)BOOLEAN_COLUMN NUMBER(1)

SQL> insert into boolean_table(id, boolean_column) values('id-1', 1);SQL> insert into boolean_table(id, boolean_column) values('id-2', 0);SQL> commit;

SQL> select * from boolean_table;

ID BOOLEAN_COLUMN------------ --------------id-1 1id-2 0

5203ch13.qxd 8/12/05 12:22 PM Page 520

Page 550: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 521

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetBoolean.java$ java Demo_PreparedStatement_SetBoolean oracle id-200 0--Demo_PreparedStatement_setBoolean begin--conn=oracle.jdbc.driver.OracleConnection@ece65---------------rowCount=1--Demo_PreparedStatement_setBoolean end--

$ java Demo_PreparedStatement_SetBoolean oracle id-400 1--Demo_PreparedStatement_setBoolean begin--conn=oracle.jdbc.driver.OracleConnection@ece65---------------rowCount=1--Demo_PreparedStatement_setBoolean_Oracle end--

Viewing the Oracle Database After Running the ProgramThis shows the Oracle database after running the program:

SQL> select * from boolean_table;

ID BOOLEAN_COLUMN--------- --------------id-1 1id-2 0id-200 0id-400 1

Setting Up the MySQL DatabaseUsing MySQL 4.1.7, define a table that has a column of the BOOLEAN (or TINYINT) type, as shown here:

mysql> use octopus;Database changedmysql> create table boolean_table(id varchar(12), boolean_column BOOLEAN);Query OK, 0 rows affected (0.09 sec)

mysql> desc boolean_table;+----------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------------+-------------+------+-----+---------+-------+| id | varchar(12) | YES | | NULL | || boolean_column | tinyint(1) | YES | | NULL | |+----------------+-------------+------+-----+---------+-------+2 rows in set (0.00 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ java Demo_PreparedStatement_SetBoolean mysql id-100 0--Demo_PreparedStatement_setBoolean begin--conn=com.mysql.jdbc.Connection@8fce95---------------

5203ch13.qxd 8/12/05 12:22 PM Page 521

Page 551: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT522

rowCount=1--Demo_PreparedStatement_setBoolean end--

$ java Demo_PreparedStatement_SetBoolean mysql id-400 1--Demo_PreparedStatement_setBoolean begin--conn=com.mysql.jdbc.Connection@8fce95---------------rowCount=1--Demo_PreparedStatement_setBoolean end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> use octopus;Database changedmysql> select * from boolean_table;+--------+----------------+| id | boolean_column |+--------+----------------+| id-100 | 0 || id-400 | 1 |+--------+----------------+2 rows in set (0.01 sec)

13-8. How Do You Use PreparedStatement’s setByte(), setShort(),setInt(), and setLong()?The following sections show how to pass an integer value (which includes the Java primitive data typesbyte, short, int, and long) to a PreparedStatement object.

How It WorksHere I will show how to use the PreparedStatement object’s setByte(), setShort(), setInt(), andsetLong() methods. The signatures of these methods are as follows:

public void setByte(int parameterIndex, byte x) throws SQLExceptionpublic void setShort(int parameterIndex, short x) throws SQLExceptionpublic void setInt(int parameterIndex, int x) throws SQLExceptionpublic void setLong(int parameterIndex, long x) throws SQLException

This sets the designated parameter to the given Java primitive value (byte, short, int, or long).The driver converts byte to SQL TINYINT, short to SQL SMALLINT, int to SQL INTEGER, and long toSQL BIGINT when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The parameter value.

This throws a SQLException if a database access error occurs.You can use all four methods (setByte(), setShort(), setInt(), and setLong()) on both the

Oracle and MySQL databases.The Oracle data types CHAR, VARCHAR2, LONG, NUMBER, RAW, and LONG RAW can be materialized as

a Java primitive type byte, short, int, or long. In practice, it makes sense to map Oracle’s SQL NUMBERtype to a Java primitive type byte, short, int, or long, and then you can use setByte(), setShort(),

5203ch13.qxd 8/12/05 12:22 PM Page 522

Page 552: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 523

setInt(), or setLong() accordingly to set the proper value. Note that you can use Oracle’s NUMBER tosupport most of the Java primitive data types.

In MySQL, when a column data type is TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT, CHAR, or VARCHAR,then you can use the setByte() method. In practice, it makes sense to map MySQL’s TINYINT to theJava primitive type byte, MySQL’s SMALLINT to the Java primitive type short, MySQL’s INT to the Javaprimitive type int, MySQL’s BIGINT to the Java primitive type long and then use the setByte(),setShort(), setInt(), or setLong() method accordingly to set the proper value.

SolutionThis class will read an ID followed by four numbers (byte, short, int, and long) and then insert thesevalues as a record of integer_table:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetIntegers {

public static void main(String[] args) {String dbVendor = args[0]; // {"mysql", "oracle" }String id = args[1];byte byteValue = Byte.parseByte(args[2]);short shortValue = Short.parseShort(args[3]);int intValue = Integer.parseInt(args[4]);long longValue = Long.parseLong(args[5]);

Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Demo_PreparedStatement_SetIntegers begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "insert into integer_table(id, byte_column, " +

"short_column, int_column, long_column) values(?, ?, ?, ?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, id);pstmt.setByte(2, byteValue);pstmt.setShort(3, shortValue);pstmt.setInt(4, intValue);pstmt.setLong(5, longValue);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_SetIntegers end--");

}

5203ch13.qxd 8/12/05 12:22 PM Page 523

Page 553: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT524

catch(Exception e){e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseUsing Oracle, define a table that has a column of the NUMBER type, as shown next. Oracle’s data typeNUMBER(p, s)denotes a number having the precision p and scale s. The precision p can range from 1 to 38,and the scale s can range from -84 to 127.

SQL> create table integer_table(2 id varchar(12),3 byte_column NUMBER,4 short_column NUMBER,5 int_column NUMBER,6 long_column NUMBER7 );

Table created.

SQL> desc integer_table;Name Null? Type------------- -------- ------------ID VARCHAR2(12)BYTE_COLUMN NUMBERSHORT_COLUMN NUMBERINT_COLUMN NUMBERLONG_COLUMN NUMBER

SQL> insert into integer_table2 (id, byte_column, short_column, int_column, long_column)3 values('id-1', 12, 1234, 1234567890, 12345678901234567890);

SQL> insert into integer_table2 (id, byte_column, short_column, int_column, long_column)3 values('id-2', 12, 1234, 1234567890, 1234567890123456);

SQL> commit;Commit complete.

SQL> set numwidth 25;SQL> select * from integer_table;ID BYTE_COLUMN SHORT_COLUMN INT_COLUMN LONG_COLUMN---- ----------- ------------ ---------- --------------------id-1 12 1234 1234567890 12345678901234567890id-2 12 1234 1234567890 1234567890123456

5203ch13.qxd 8/12/05 12:22 PM Page 524

Page 554: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 525

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetIntegers.java$ java Demo_PreparedStatement_SetIntegers oracle \

id-33 44 4455 44556677 2233445566778899--Demo_PreparedStatement_SetIntegers begin--conn=oracle.jdbc.driver.OracleConnection@860d49---------------rowCount=1--Demo_PreparedStatement_SetIntegers end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from integer_table;ID BYTE_COLUMN SHORT_COLUMN INT_COLUMN LONG_COLUMN---- ----------- ------------ ---------- --------------------id-1 12 1234 1234567890 12345678901234567890id-2 12 1234 1234567890 1234567890123456id-33 44 4455 44556677 2233445566778899

Setting Up the MySQL DatabaseUsing MySQL, define a table that has the following columns: TINYINT (maps to byte), SMALLINT(maps to short), INT (maps to int), and BIGINT (maps to long), as follows:

mysql> create table integer_table(-> id varchar(12),-> byte_column TINYINT,-> short_column SMALLINT,-> int_column INT,-> long_column BIGINT-> );

Query OK, 0 rows affected (0.25 sec)

mysql> desc integer_table;+--------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+--------------+-------------+------+-----+---------+-------+| id | varchar(12) | YES | | NULL | || byte_column | tinyint(4) | YES | | NULL | || short_column | smallint(6) | YES | | NULL | || int_column | int(11) | YES | | NULL | || long_column | bigint(20) | YES | | NULL | |+--------------+-------------+------+-----+---------+-------+5 rows in set (0.03 sec)

mysql> insert into integer_table-> (id, byte_column, short_column, int_column, long_column)-> values('id-1', 12, 1234, 1234567890, 12345678901234567890);

mysql> insert into integer_table-> (id, byte_column, short_column, int_column, long_column)-> values('id-2', 12, 1234, 1234567890, 12345678901234567);

5203ch13.qxd 8/12/05 12:23 PM Page 525

Page 555: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT526

mysql> commit;mysql> select * from integer_table;+------+-------------+--------------+------------+----------------------+| id | byte_column | short_column | int_column | long_column |+------+-------------+--------------+------------+----------------------+| id-1 | 12 | 1234 | 1234567890 | -6101065172474983726 || id-2 | 12 | 1234 | 1234567890 | 12345678901234567 |+------+-------------+--------------+------------+----------------------+2 rows in set (0.02 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetIntegers.java$ java Demo_PreparedStatement_SetIntegers mysql \

id-22 34 34567 1222333444 1222333444555Exception in thread "main" java.lang.NumberFormatException:Value out of range. Value:"34567" Radix:10

at java.lang.Short.parseShort(Short.java:122)at java.lang.Short.parseShort(Short.java:78)at Demo_PreparedStatement_SetIntegers.main(Demo_PreparedStatement_SetIntegers.java:14)

$ java Demo_PreparedStatement_SetIntegers mysql \id-22 34 3456 1222333444 1222333444555

--Demo_PreparedStatement_SetIntegers begin--conn=com.mysql.jdbc.Connection@1546e25---------------rowCount=1--Demo_PreparedStatement_SetIntegers end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from integer_table;+-------+-------------+--------------+------------+----------------------+| id | byte_column | short_column | int_column | long_column |+-------+-------------+--------------+------------+----------------------+| id-1 | 12 | 1234 | 1234567890 | -6101065172474983726 || id-2 | 12 | 1234 | 1234567890 | 12345678901234567 || id-22 | 34 | 3456 | 1222333444 | 1222333444555 |+-------+-------------+--------------+------------+----------------------+3 rows in set (0.00 sec)

13-9. How Do You Use PreparedStatement.setBytes()?The following sections show how to pass an array of bytes (representing binary data) to aPreparedStatement object.

How It WorksThe Oracle and MySQL databases both support PreparedStatement.setBytes(). In general, themethod setBytes() is capable of sending unlimited amounts of data. But each database driver canset limitations on the maximum number of bytes sent. For example, in the Oracle database, there

PreparedStatement class’s

5203ch13.qxd 8/12/05 12:23 PM Page 526

Page 556: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 527

setBytes() method. In Oracle 8 and higher, the maximum size for setBytes() is 2,000 bytes (in Oracle 7,the maximum size is 255).

The signature of setBytes() is as follows:

void setBytes(int parameterIndex,byte[] x) throws SQLException

This sets the designated parameter to the given Java array of bytes. The driver converts this toa SQL VARBINARY or LONGVARBINARY (depending on the argument’s size relative to the driver’s limitson VARBINARY values) when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The parameter value (as an array of byte).

This throws a SQLException if a database access error occurs.

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetBytes {

public static void main(String[] args) {// read inputs from command lineString dbVendor = args[0]; // {"mysql", "oracle" }String id = args[1];byte[] shortData = args[2].getBytes();byte[] longData = args[3].getBytes();

Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Demo_PreparedStatement_SetBytes begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "insert into bytes_table" +

" (id, short_data, long_data) values(?, ?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, id);pstmt.setBytes(2, shortData);pstmt.setBytes(3, longData);

5203ch13.qxd 8/12/05 12:23 PM Page 527

Page 557: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT528

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_SetBytes end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis example uses a table that has columns of the RAW and LONG RAW data types. The maximum size forthe RAW data type is 2,000 bytes, and you must specify a size for a RAW value. The maximum size for theLONG RAW data type is up to 2GB.

SQL> create table bytes_table(2 id varchar2(10),3 short_data RAW(1000),4 long_data LONG RAW5 );

Table created.

SQL> desc bytes_table;Name Null? Type------------ -------- --------------ID VARCHAR2(10)SHORT_DATA RAW(1000)LONG_DATA LONG RAW

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetBytes.java$ java Demo_PreparedStatement_SetBytes oracle id11 "abcd" "longrawdata"--Demo_PreparedStatement_SetBytes begin--conn=oracle.jdbc.driver.OracleConnection@1a125f0---------------rowCount=1--Demo_PreparedStatement_SetBytes end--

$ java Demo_PreparedStatement_SetBytes oracle id22 "abcdef" "longrawdata2222"--Demo_PreparedStatement_SetBytes begin--conn=oracle.jdbc.driver.OracleConnection@1a125f0---------------rowCount=1--Demo_PreparedStatement_SetBytes end--

5203ch13.qxd 8/12/05 12:23 PM Page 528

Page 558: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 529

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select id, short_data from bytes_table;

ID SHORT_DATA---- -----------id11 61626364id22 616263646566

Setting Up the MySQL DatabaseUsing MySQL database, you can use PreparedStatement.setBytes() for the VARCHAR BINARY,TINYBLOB, BLOB, MEDIUMBLOB, and LONGBLOB data types. For this example, use a table that has columnsof the VARCHAR BINARY and TINYBLOB data types. The maximum size for the TINYBLOB data type is 255bytes.

mysql> create table bytes_table(-> id varchar(10),-> short_data VARCHAR(16) BINARY,-> long_data TINYBLOB-> );

Query OK, 0 rows affected (0.16 sec)

mysql> desc bytes_table;+----------------+--------------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------------+--------------------+------+-----+---------+-------+| id | varchar(10) | YES | | NULL | || short_data | varchar(16) binary | YES | | NULL | || long_data | tinyblob | YES | | NULL | |+----------------+--------------------+------+-----+---------+-------+3 rows in set (0.04 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetBytes.java$ java Demo_PreparedStatement_SetBytes mysql id22 "abcd" "abcd1122"--Demo_PreparedStatement_SetBytes begin--conn=com.mysql.jdbc.Connection@14ed9ff---------------rowCount=1--Demo_PreparedStatement_SetBytes end--

$ java Demo_PreparedStatement_SetBytes mysql id44 "abcdef" "abcd11223344"--Demo_PreparedStatement_SetBytes begin--conn=com.mysql.jdbc.Connection@14ed9ff---------------rowCount=1--Demo_PreparedStatement_SetBytes end--

MySQL Database after Running SolutionThis shows the MySQL database after running the solution:

5203ch13.qxd 8/12/05 12:23 PM Page 529

Page 559: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT530

mysql> select * from binary_table;+------+-------------+--------------+| id | short_data | long_data |+------+-------------+--------------+| id22 | abcd | abcd1122 || id44 | abcdef | abcd11223344 |+------+-------------+--------------+2 rows in set (0.02 sec)

13-10. How Do You Use PreparedStatement.setCharacterStream()?The following sections show how to pass a character stream (represented as a java.io.Reader) toa PreparedStatement object.

How It WorksYou can use PreparedStatement.setCharacterStream() on both the Oracle and MySQL databases.

In the Oracle database, when a column data type is LONG (character large object), then you canuse setCharacterStream(). Oracle’s LONG represents character data of a variable length up to 2GB.Oracle’s LONG maps to java.sql.Types.LONGVARCHAR, which maps to java.lang.String.

In the MySQL database, when a column data type is TINYTEXTwith a maximum length of 255 (2^8 - 1)characters, TEXT with a maximum length of 65,535 (2^16 - 1) characters, MEDIUMTEXT with a maximumlength of 16,777,215 (2^24 - 1) characters, or LONGTEXT with a maximum length of 4,294,967,295 or4G (2^32 - 1) characters, then you can use setCharacterStream().

The signature of setCharacterStream() is as follows:

public void setCharacterStream(int parameterIndex,java.io.Reader reader,int length)

throws SQLException

This sets the designated parameter to the given Reader object, which is the given number ofcharacters long. When a large Unicode value is input to a LONGVARCHAR parameter, it may be morepractical to send it via a java.io.Reader object. The data will be read from the stream as needed untilreaching the end of the file. The JDBC driver will do any necessary conversion from Unicode to thedatabase char format.

This stream object can either be a standard Java stream object or be your own subclass that imple-ments the standard interface.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• reader: The java.io.Reader object that contains the Unicode data.

• length: The number of characters in the stream.

This throws a SQLException if a database access error occurs.

SolutionThis solution will read a text file and then insert its name (as an ID) and content (as a char_stream_column)into the char_stream_table:

import java.util.*;import java.io.*;import java.sql.*;

5203ch13.qxd 8/12/05 12:23 PM Page 530

Page 560: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 531

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetCharacterStream {

public static void main(String[] args) {System.out.println("--Demo_PreparedStatement_SetCharacterStream begin--");// read inputs from command lineString dbVendor = args[0];String fileName = args[1];Reader fileReader = null;long fileLength = 0;try {

File file = new File(fileName);fileLength = file.length();fileReader = (Reader) new BufferedReader(new FileReader(file));System.out.println("fileName="+fileName);System.out.println("fileLength="+fileLength);

}catch(Exception e) {

e.printStackTrace();System.exit(1);

}

Connection conn = null;PreparedStatement pstmt = null;try {

// get a database connection objectconn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// begin transactionconn.setAutoCommit(false);

// prepare SQL query for inserting a new row using SetCharacterStream()String query = "insert into char_stream_table" +

" (id, char_stream_column) values(?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, fileName);pstmt.setCharacterStream(2, fileReader, (int)fileLength);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);

// end transactionconn.commit();System.out.println("--Demo_PreparedStatement_SetCharacterStream end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

5203ch13.qxd 8/12/05 12:23 PM Page 531

Page 561: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT532

DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

$ sqlplus octopus/octopusSQL> create table char_stream_table (2 id VARCHAR(16) NOT NULL PRIMARY KEY,3 char_stream_column LONG4 );

Table created.

SQL> desc char_stream_table;Name Null? Type---------------- -------- -----------ID NOT NULL VARCHAR2(16)CHAR_STREAM_COLUMN LONG

SQL> insert into char_stream_table(id, char_stream_column)2 values('id-1', 'abcdef');

SQL> insert into char_stream_table(id, char_stream_column)2 values('id-2', '0123456789');

SQL> commit;Commit complete.

SQL> select * from char_stream_table;

ID CHAR_STREAM_COLUMN---- ------------------id-1 abcdefid-2 0123456789

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ cat sample.txtthis is line1.this is the last line.

$ javac Demo_PreparedStatement_SetCharacterStream.java$ java Demo_PreparedStatement_SetCharacterStream oracle sample.txt--Demo_PreparedStatement_SetCharacterStream begin--fileName=sample.txtfileLength=38conn=oracle.jdbc.driver.OracleConnection@ae506e---------------rowCount=1--Demo_PreparedStatement_SetCharacterStream end--

5203ch13.qxd 8/12/05 12:23 PM Page 532

Page 562: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 533

Viewing the Oracle Database After Running the ProgramThis shows the Oracle database after running the program:

SQL> select * from char_stream_table;

ID CHAR_STREAM_COLUMN---- ------------------id-1 abcdefid-2 0123456789sample.txt this is line1.

this is the last line.

Setting Up the MySQL DatabaseUsing MySQL, define a table that has a TEXT data type, as shown next. A TEXT data type is semanticallyequivalent to a SQL CLOB data type and can hold a maximum length of 65,535 (2^16 - 1) characters.

mysql> use tiger;Database changed

mysql> create table char_stream_table(-> id VARCHAR(16) NOT NULL PRIMARY KEY,-> char_stream_column TEXT-> );

Query OK, 0 rows affected (0.07 sec)

mysql> desc char_stream_table;+--------------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+--------------------+-------------+------+-----+---------+-------+| id | varchar(16) | | PRI | | || char_stream_column | text | YES | | NULL | |+--------------------+-------------+------+-----+---------+-------+2 rows in set (0.02 sec)

mysql> insert into char_stream_table(id, char_stream_column)-> values('id-1', 'abcde1234');

mysql> insert into char_stream_table(id, char_stream_column)-> values('id-2', 'zzzzzzz1234');

mysql> select * from char_stream_table;+------+--------------------+| id | char_stream_column |+------+--------------------+| id-1 | abcde1234 || id-2 | zzzzzzz1234 |+------+--------------------+2 rows in set (1.45 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetCharacterStream.java$ java Demo_PreparedStatement_SetCharacterStream mysql sample.txt--Demo_PreparedStatement_SetCharacterStream begin--

5203ch13.qxd 8/12/05 12:23 PM Page 533

Page 563: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT534

fileName=sample.txtfileLength=38conn=com.mysql.jdbc.Connection@1fdc96c---------------rowCount=1--Demo_PreparedStatement_SetCharacterStream end--

MySQL Database after Running SolutionThis shows the MySQL database after running the solution:

mysql> select * from char_stream_table;+------------+----------------------------------------+| id | char_stream_column |+------------+----------------------------------------+| id-1 | abcde1234 |+------------+----------------------------------------+| id-2 | zzzzzzz1234 |+------------+----------------------------------------+| sample.txt | this is line1. || | this is the last line. |+------------+----------------------------------------+3 rows in set (0.00 sec)

13-11. How Do You Use PreparedStatement.setClob()?In RDBMSs, CLOB is a Character Large OBject. The following sections show how to pass a java.sql.Clobobject to a PreparedStatement object.

How It Worksjava.sql.Clob is the mapping in the Java programming language for the SQL CLOB type. A SQL CLOBis a built-in type that stores a character large object as a column value in a row of a database table.You can use PreparedStatement.setClob() on both the Oracle and MySQL databases.

In Oracle, when a column data type is CLOB, then you can use setClob(). Oracle’s CLOB representscharacter data of a variable length up to 2GB.

In MySQL, when a column data type is TINYTEXT with a maximum length of 255 (2^8 - 1) characters,TEXT with a maximum length of 65,535 (2^16 - 1) characters, MEDIUMTEXT with a maximum length of16,777,215 (2^24 - 1) characters, or LONGTEXT with a maximum length of 4,294,967,295 or 4G (2^32 - 1)characters, then you can use setClob().

The signature of setClob() is as follows:

public void setClob(int parameterIndex,java.sql.Clob clob) throws SQLException

This sets the designated parameter to the given Clob object. The driver converts this to a SQLCLOB value when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• clob: A java.sql.Clob object that maps a SQL CLOB value.

This throws a SQLException if a database access error occurs.

5203ch13.qxd 8/12/05 12:23 PM Page 534

Page 564: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 535

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> create table clob_table (2 id VARCHAR(12) NOT NULL PRIMARY KEY,3 clob_column CLOB default empty_clob()4 );

Table created.

SQL> desc clob_table;Name Null? Type---------------- -------- ------------ID NOT NULL VARCHAR2(12)CLOB_COLUMN CLOB

SQL> insert into clob_table(id, clob_column)values('id-1', 'value-111111');

SQL> insert into clob_table(id, clob_column)values('id-2', 'value-22222222222');

SQL> commit;SQL> select * from clob_table;

ID CLOB_COLUMN---- -----------------id-1 value-111111id-2 value-22222222222

SolutionThis class uses PreparedStatement.setClob() to insert a new record into a SQL CLOB column:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetClob {

public static void main(String[] args) {System.out.println("--Demo_PreparedStatement_SetCharacterStream begin--");// read inputs from command lineString dbVendor = args[0]; // {"mysql", "oracle" }String id = args[1];String newID = args[2];

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--Demo_PreparedStatement_SetClob begin--");

5203ch13.qxd 8/12/05 12:23 PM Page 535

Page 565: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT536

// get a database connection objectconn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// begin transactionconn.setAutoCommit(false);

// prepare blob object from an existing binary columnString query1 = "select clob_column from clob_table where id = ?";pstmt = conn.prepareStatement(query1);pstmt.setString(1, id);rs = pstmt.executeQuery();rs.next();java.sql.Clob clob = (java.sql.Clob) rs.getObject(1);// prepare SQL query for inserting a new row using setClob()String query = "insert into clob_table(id, clob_column) values(?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, newID);pstmt.setClob(2, clob);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);

// end transactionconn.commit();System.out.println("--Demo_PreparedStatement_SetClob end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetClob.java$ java Demo_PreparedStatement_SetClob oracle id-1 id-111--Demo_PreparedStatement_SetClob_Oracle begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------rowCount=1--Demo_PreparedStatement_SetClob end--

5203ch13.qxd 8/12/05 12:23 PM Page 536

Page 566: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 537

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from clob_table;

ID CLOB_COLUMN---- -----------------id-1 value-111111id-2 value-22222222222id-111 value-111111

Setting Up the MySQL DatabaseUsing MySQL, define a table that has a TEXT data type, as shown next. A TEXT data type is semanticallyequivalent to a SQL CLOB data type and can hold a maximum length of 65,535 (2^16 - 1) characters.

mysql> create table clob_table (-> id VARCHAR(12) NOT NULL PRIMARY KEY,-> clob_column TEXT);

mysql> desc clob_table;+-------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------------+-------------+------+-----+---------+-------+| id | varchar(12) | | PRI | | || clob_column | text | YES | | NULL | |+-------------+-------------+------+-----+---------+-------+2 rows in set (0.03 sec)

mysql> insert into clob_table(id, clob_column)-> values('id-1', '123abc');

mysql> insert into clob_table(id, clob_column)-> values('id-2', '444zzzzz');

mysql> select * from clob_table;+------+-------------+| id | clob_column |+------+-------------+| id-1 | 123abc || id-2 | 444zzzzz |+------+-------------+2 rows in set (0.00 sec)

Running the Solution for the MySQL DatabaseIn the JDBC API, there is no constructor to build a java.sql.Clob object directly; therefore, to usethe setClob() method, you need to create a ResultSet object, extract a Clob object, and then pass itto the PreparedStatement.setClob() method. I will show how to extract a Clob from clob_table andthen insert it into clob_table using the PreparedStatement.setClob() method.

$ javac Demo_PreparedStatement_SetClob.java$ java Demo_PreparedStatement_SetClob mysql id-1 id-1000--Demo_PreparedStatement_SetClob begin--conn=com.mysql.jdbc.Connection@1e4cbc4---------------

5203ch13.qxd 8/12/05 12:23 PM Page 537

Page 567: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT538

clob.length()=6rowCount=1--Demo_PreparedStatement_SetClob end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from clob_table;+---------+-------------+| id | clob_column |+---------+-------------+| id-1 | 123abc || id-2 | 444zzzzz || id-1000 | 123abc |+---------+-------------+3 rows in set (0.00 sec)

13-12. How Do You Use PreparedStatement.setDate()?Java.sql.Date is a thin wrapper around a millisecond value that allows JDBC to identify it as a SQLDATE value. You want to pass a java.sql.Date (character large object) object to a PreparedStatementobject.

How It WorksYou can use setDate() on both the Oracle and MySQL databases. In Oracle, when a column data typeis DATE, then you can use the setDate() method. Note that in Oracle the DATE data type can representthree distinct data types: Date, Time, and Timestamp. In MySQL, when a column data type is DATE, thenyou can use the setDate() method.

The signature of setDate() is as follows:

public void setDate(int parameterIndex,java.sql.Date date) throws SQLException

This sets the designated parameter to the given java.sql.Date value. The driver converts this toa SQL DATE value when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• date: The parameter value.

This throws a SQLException if a database access error occurs.

SolutionThis solution will use the PreparedStatement.setDate() method:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetDate {public static java.sql.Date getCurrentJavaSqlDate() {

5203ch13.qxd 8/12/05 12:23 PM Page 538

Page 568: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 539

java.util.Date today = new java.util.Date();return new java.sql.Date(today.getTime());

}public static void main(String[] args) {

System.out.println("--Demo_PreparedStatement_SetDate begin--");// read inputs from command lineString dbVendor = args[0];String id = args[1];Connection conn = null;PreparedStatement pstmt = null;try {

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "insert into date_table(id, date_column) values(?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, id);java.sql.Date date = getCurrentJavaSqlDate();pstmt.setDate(2, date);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_SetDate end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> create table date_table(id VARCHAR(12), date_column DATE);Table created.SQL> desc date_table;Name Null? Type----------------------------------- -------- ------------ID VARCHAR2(12)DATE_COLUMN DATE

SQL> insert into date_table(id, date_column) values('id-1', '12-JUN-2003');SQL> insert into date_table(id, date_column) values('id-2', '12-DEC-2003');SQL> commit;SQL> select * from date_table;

5203ch13.qxd 8/12/05 12:23 PM Page 539

Page 569: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT540

ID DATE_COLUMN-------- --------------id-1 12-JUN-03id-2 12-DEC-03

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetDate.java$ java Demo_PreparedStatement_SetDate oracle id-200--Demo_PreparedStatement_SetDate begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------rowCount=1--Demo_PreparedStatement_SetDate end--

$ java Demo_PreparedStatement_SetDate oracle id-400--Demo_PreparedStatement_SetDate begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------rowCount=1--Demo_PreparedStatement_SetDate end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from date_table;ID DATE_COLU-------- ---------id-1 12-JUN-03id-2 12-DEC-03id-200 07-NOV-03id-400 07-NOV-03

Setting Up the MySQL DatabaseUsing Oracle, define a table that has a column of the DATE type, as shown next. The Oracle data-base accepts dates in the dd-MMM-yyyy format, while the MySQL database accepts dates in theyyyy-mm-dd format.

mysql> create table date_table(id VARCHAR(12), date_column DATE);Query OK, 0 rows affected (0.14 sec)

mysql> desc date_table;+-------------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------------+-------------+------+-----+---------+-------+| id | varchar(12) | YES | | NULL | || date_column | date | YES | | NULL | |+-------------+-------------+------+-----+---------+-------+2 rows in set (0.06 sec)

mysql> insert into date_table(id, date_column) values('id-1', '12-JUN-2003');Query OK, 1 row affected (0.01 sec)

mysql> insert into date_table(id, date_column) values('id-2', '12-DEC-2003');

5203ch13.qxd 8/12/05 12:23 PM Page 540

Page 570: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 541

Query OK, 1 row affected (0.00 sec)

mysql> select * from date_table;+------+-------------+| id | date_column |+------+-------------+| id-1 | 0000-00-00 || id-2 | 0000-00-00 |+------+-------------+2 rows in set (0.13 sec)

mysql> insert into date_table(id, date_column) values('id-3', '2003-06-12');mysql> insert into date_table(id, date_column) values('id-3', '2003-12-12');mysql> select * from date_table;+------+-------------+| id | date_column |+------+-------------+| id-1 | 0000-00-00 || id-2 | 0000-00-00 || id-3 | 2003-06-12 || id-4 | 2003-12-12 |+------+-------------+4 rows in set (0.00 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetDate.java$ java Demo_PreparedStatement_SetDate mysql id-300--Demo_PreparedStatement_SetDate begin--conn=com.mysql.jdbc.Connection@cd2c3c---------------rowCount=1--Demo_PreparedStatement_SetDate_MySQL end--

$ java Demo_PreparedStatement_SetDate mysql id-500--Demo_PreparedStatement_SetDate begin--conn=com.mysql.jdbc.Connection@cd2c3c---------------rowCount=1--Demo_PreparedStatement_SetDate_MySQL end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from date_table;+--------+-------------+| id | date_column |+--------+-------------+| id-1 | 0000-00-00 || id-2 | 0000-00-00 || id-3 | 2003-06-12 || id-4 | 2003-12-12 || id-300 | 2003-11-07 || id-500 | 2003-11-07 |+--------+-------------+6 rows in set (0.00 sec)

5203ch13.qxd 8/12/05 12:23 PM Page 541

Page 571: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT542

13-13. How Do You Use PreparedStatement’s setFloat() and setDouble()?The following sections show how to pass a floating-point value (the Java primitive data types floatand double) to a PreparedStatement object.

How It WorksYou can use the PreparedStatement object’s setFloat() and setDouble() methods on both the Oracleand MySQL databases. In Oracle, when a column data type is NUMBER, then you can use setFloat()and setDouble(). Oracle uses the NUMBER type for integers and floating-point numbers. In MySQL,when a column data type is FLOAT, REAL, DOUBLE PRECISION, NUMERIC, or DECIMAL, then you can usesetFloat() and setDouble().

The signature of setDouble() is as follows:

public void setDouble(int parameterIndex,double x) throws SQLException

This sets the designated parameter to the given Java double value. The driver converts this toa SQL DOUBLE value when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The parameter value.

This throws a SQLException if a database access error occurs.The signature of setFloat() is as follows:

public void setFloat(int parameterIndex,float x) throws SQLException

This sets the designated parameter to the given Java float value. The driver converts this toa SQL FLOAT value when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The parameter value.

This throws a SQLException if a database access error occurs.

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetFloatAndDouble {public static void main(String[] args) {

System.out.println("--Demo_PreparedStatement_SetFloatAndDouble begin--");// read inputs from command lineString dbVendor = args[0];String stringValue = args[1];

5203ch13.qxd 8/12/05 12:23 PM Page 542

Page 572: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 543

float floatValue = Float.parseFloat(args[2]);double doubleValue = Double.parseDouble(args[3]);Connection conn = null;PreparedStatement pstmt = null;try {

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "insert into double_table( " +

"id, float_column, double_column) values(?, ?, ?)";// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, stringValue);pstmt.setFloat(2, floatValue);pstmt.setDouble(3, doubleValue);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_SetFloatAndDouble end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseUsing Oracle, define a table that has a column of the NUMBER type, as shown next. Oracle’s data typeNUMBER(p, s)denotes a number having the precision p and scale s. The precision p can range from 1 to 38,and the scale s can range from -84 to 127.

$ sqlplus octopus/octopusSQL> create table double_table (2 id VARCHAR(4),3 float_column NUMBER(6, 2),4 double_column NUMBER(16, 4) );

Table created.

SQL> desc double_table;Name Null? Type----------------------------------------- -------- ------------ID VARCHAR2(4)FLOAT_COLUMN NUMBER(6,2)DOUBLE_COLUMN NUMBER(16,4)

5203ch13.qxd 8/12/05 12:23 PM Page 543

Page 573: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT544

SQL> insert into double_table(id, float_column, double_column)values('id-1', 123.34, 12.123);SQL> insert into double_table(id, float_column, double_column)values('id-2', 555.1, 89.1122);sql> commit;SQL> select * from double_table;ID FLOAT_COLUMN DOUBLE_COLUMN---- ------------- -------------id-1 123.34 12.123id-2 555.1 89.1122

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetDouble.java$ java Demo_PreparedStatement_SetFloatAndDouble oracle id-5 22.34 224455.678--Demo_PreparedStatement_SetFloatAndDouble begin--conn=oracle.jdbc.driver.OracleConnection@228a02---------------rowCount=1--Demo_PreparedStatement_SetFloatAndDouble end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from double_table;ID FLOAT_COLUMN DOUBLE_COLUMN---- ------------- -------------id-1 123.34 12.123id-2 555.1 89.1122id-5 22.34 224455.678

Setting Up the MySQL DatabaseUsing MySQL, you can use the FLOAT and DOUBLE data types to store Java’s float and double primitivedata type values. This shows how to set up the MySQL database:

mysql> use tiger;Database changedmysql> create table double_table (

-> id varchar(4),-> float_column FLOAT,-> double_column DOUBLE );

mysql> desc double_table;+---------------+------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+---------------+------------+------+-----+---------+-------+| id | varchar(4) | YES | | NULL | || float_column | float | YES | | NULL | || double_column | double | YES | | NULL | |+---------------+------------+------+-----+---------+-------+

5203ch13.qxd 8/12/05 12:23 PM Page 544

Page 574: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 545

mysql> insert into double_table(id, float_column, double_column)> values('id-1', 123.45, 123.45);mysql> insert into double_table(id, float_column, double_column)> values('id-2', 1.2345, 1.2345);mysql> select * from double_table;+------+--------------+--------------+| id | float_column |double_column |+------+--------------+--------------+| id-1 | 123.45 | 123.45 || id-2 | 1.2345 | 1.2345 |+------+--------------+--------------+2 rows in set (0.01 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetFloatAndDouble.java$ java Demo_PreparedStatement_SetFloatAndDouble mysql id-7 11.22 1122334455.66--Demo_PreparedStatement_SetFloatAndDouble begin--conn=com.mysql.jdbc.Connection@b2fd8f---------------rowCount=1--Demo_PreparedStatement_SetFloatAndDouble end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from double_table;+------+---------------+---------------+| id | float_column | double_column |+------+---------------+---------------+| id-1 | 123.45 | 123.45 || id-2 | 1.2345 | 1.2345 || id-7 | 11.22 | 1122334455.66 |+------+---------------+---------------+3 rows in set (0.00 sec)

13-14. How Do You Use PreparedStatement.setNull()?How do you set the designated parameter to SQL NULL? The following sections explain how to do this.

How It WorksBoth the Oracle and MySQL databases support the PreparedStatement.setNull() method, whichsets the designated parameter to SQL NULL. The signature of setNull() is as follows:

public void setNull(int parameterIndex,int sqlType) throws SQLException

This sets the designated parameter to SQL NULL. You must specify the parameter’s SQL type.The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• sqlType: The SQL type code defined in java.sql.Types.

This throws a SQLException if a database access error occurs.

5203ch13.qxd 8/12/05 12:23 PM Page 545

Page 575: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT546

SolutionThis solution inserts a record with SQL NULL values:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetNull {public static void main(String[] args) {

System.out.println("--Demo_PreparedStatement_SetNull begin--");// read inputs from command lineString dbVendor = args[0];String idValue = args[1];Connection conn = null;PreparedStatement pstmt = null;try {

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "insert into nullable_table(id, " +

"string_column, int_column) values(?, ?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, idValue);pstmt.setNull(2, java.sql.Types.VARCHAR);pstmt.setNull(3, java.sql.Types.INTEGER);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_SetNull end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> create table nullable_table(2 id varchar(4),3 string_column VARCHAR(10),

5203ch13.qxd 8/12/05 12:23 PM Page 546

Page 576: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 547

4 int_column NUMBER5 );

Table created.

SQL> desc nullable_table;Name Null? Type---------------- -------- -------------ID VARCHAR2(4)STRING_COLUMN VARCHAR2(10)INT_COLUMN NUMBER

SQL> insert into set_null_table(id, string_column, int_column)2 values('id-1', 'abcde', 123);

SQL> insert into set_null_table(id, string_column, int_column)2 values('id-2', 'zzzzzzz', 777);

SQL> commit;Commit complete.

SQL> select * from set_null_table;

ID STRING_COLUMN INT_COLUMN---- ------------- ----------id-1 abcde 123id-2 zzzzzzz 777

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetNull.java$ java Demo_PreparedStatement_SetNull oracle id-3--Demo_PreparedStatement_SetNull begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------rowCount=1--Demo_PreparedStatement_SetNull end--

$ java Demo_PreparedStatement_SetNull oracle id-4--Demo_PreparedStatement_SetNull begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------rowCount=1--Demo_PreparedStatement_SetNull end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from nullable_table;ID STRING_COL INT_COLUMN---- ---------- ----------id-1 abcde 123id-2 zzzzzzz 777id-3id-4

5203ch13.qxd 8/12/05 12:23 PM Page 547

Page 577: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT548

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

create table nullable_table(id varchar(4),string_column VARCHAR(10),int_column INTEGER,

);

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetNull.java$ java Demo_PreparedStatement_SetNull mysql id-3--Demo_PreparedStatement_SetNull begin--conn=com.mysql.jdbc.Connection@1e4cbc4---------------rowCount=1--Demo_PreparedStatement_SetNull end--

$ java Demo_PreparedStatement_SetNull mysql id-4--Demo_PreparedStatement_SetNull begin--conn=com.mysql.jdbc.Connection@1e4cbc4---------------rowCount=1--Demo_PreparedStatement_SetNull end--

Viewing the MySQL Database After Running the ProgramThis shows the MySQL database after running the solution:

mysql> select * from set_null_table;+------+---------------+------------+| id | string_column | int_column |+------+---------------+------------+| id-3 | NULL | NULL || id-4 | NULL | NULL |+------+---------------+------------+2 rows in set (0.00 sec)

13-15. How Do You Use PreparedStatement.setObject()?The following sections show how to pass a Java Object (an instance of java.lang.Object) toa PreparedStatement object.

How It WorksBoth the Oracle and MySQL databases support the PreparedStatement.setObject() method, whichsets the designated parameter to SQL’s data types. (The target data types can be different dependingon the database vendor.) The PreparedStatement.setObject() method uses reflection to figure outa Java object’s type at runtime before converting it to an appropriate SQL data type. The setObject()method converts Java types to SQL types using a standard JDBC map. If no match is found on themap, the method throws a SQLException.

The signature of setObject() is as follows:

5203ch13.qxd 8/12/05 12:23 PM Page 548

Page 578: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 549

void setObject(int parameterIndex,Object x)throws SQLException

This sets the value of the designated parameter using the given object. The second parametermust be of type Object; therefore, you should use the java.lang equivalent objects for built-in types.

The JDBC specification specifies a standard mapping from Java Object types to SQL types. Thegiven argument will be converted to the corresponding SQL type before being sent to the database.

You can use this method to pass database-specific abstract data types by using a driver-specificJava type. If the object is of a class implementing the interface SQLData, the JDBC driver should callthe method SQLData.writeSQL to write it to the SQL data stream. If, on the other hand, the object isof a class implementing Ref, Blob, Clob, Struct, or Array, the driver should pass it to the database asa value of the corresponding SQL type.

This method throws an exception if there is an ambiguity, for example, if the object is of a classimplementing more than one of the interfaces named previously.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The object containing the input parameter value.

This throws a SQLException if a database access error occurs or the type of the given object isambiguous.

void setObject(int parameterIndex,Object x,int targetSqlType)throws SQLException

This sets the value of the designated parameter with the given object. This method is like themethod setObject, except that it assumes a scale of zero.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The object containing the input parameter value.

• targetSqlType: The SQL type (as defined in java.sql.Types) to be sent to the database.

This throws a SQLException if a database access error occurs.

void setObject(int parameterIndex,Object x,int targetSqlType,int scale)throws SQLException

This sets the value of the designated parameter with the given object. The second argument mustbe an object type; for integral values, you should use the java.lang equivalent objects.

The given Java object will be converted to the given targetSqlType before being sent to thedatabase. If the object has a custom mapping (is of a class implementing the interface SQLData), theJDBC driver should call the method SQLData.writeSQL to write it to the SQL data stream. If, on theother hand, the object is of a class implementing Ref, Blob, Clob, Struct, or Array, the driver shouldpass it to the database as a value of the corresponding SQL type.

Note that you can use this method to pass database-specific abstract data types.The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The object containing the input parameter value.

5203ch13.qxd 8/12/05 12:23 PM Page 549

Page 579: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT550

• targetSqlType: The SQL type (as defined in java.sql.Types) to be sent to the database. Thescale argument may further qualify this type.

• scale: For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types, this is the number ofdigits after the decimal point. For all other types, this value will be ignored.

This throws a SQLException if a database access error occurs.

Setting Up the MySQL DatabaseThis shows how to set up the MySQL database:

mysql> use octopus;Database changedmysql> CREATE TABLE resume (

-> id BIGINT(20) PRIMARY KEY,-> name VARCHAR(255),-> content TEXT, -- CLOB data type-> date_created DATETIME-> );

Query OK, 0 rows affected (0.23 sec)

mysql> desc resume;+--------------+--------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+--------------+--------------+------+-----+---------+-------+| id | bigint(20) | | PRI | 0 | || name | varchar(255) | YES | | NULL | || content | text | YES | | NULL | || date_created | datetime | YES | | NULL | |+--------------+--------------+------+-----+---------+-------+4 rows in set (0.01 sec)

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

$ sqlplus scott/tigerSQL*Plus: Release 10.1.0.2.0 - Production on Mon Jul 11 14:14:13 2005SQL> CREATE TABLE resume (2 id NUMBER(20) PRIMARY KEY,3 name VARCHAR2(255),4 content CLOB,5 date_created DATE6 );

Table created.

SQL> desc resume;Name Null? Type----------------------------------------- -------- -------------

ID NOT NULL NUMBER(20)NAME VARCHAR2(255)CONTENT CLOBDATE_CREATED DATE

5203ch13.qxd 8/12/05 12:23 PM Page 550

Page 580: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 551

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

/*** @author: Mahmoud Parsian*/public class Demo_PreparedStatement_SetObject {

public static void main(String[] args) {

System.out.println("-- Demo_PreparedStatement_SetObject begin--");ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;PreparedStatement pstmt2 = null;try {

String dbVendor = args[0]; // { "mysql", "oracle" }conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// table column namesString [] columnNames = {"id", "name", "content", "date_created"};

// inputValues contains the data to put in the databaseObject [] inputValues = new Object[columnNames.length];

// fill input valuesinputValues[0] = new java.math.BigDecimal(100);inputValues[1] = new String("Alex Taylor");inputValues[2] = new String("This is my resume.");inputValues[3] = new Timestamp ( (new java.util.Date()).getTime() );

// prepare blob object from an existing binary columnString insert = "insert into resume (id, name, content, date_created)"+

"values(?, ?, ?, ?)";pstmt = conn.prepareStatement(insert);

// set input parameter valuespstmt.setObject(1, inputValues[0]);pstmt.setObject(2, inputValues[1]);pstmt.setObject(3, inputValues[2]);pstmt.setObject(4, inputValues[3]);

// execute SQL INSERT statementpstmt.executeUpdate();

5203ch13.qxd 8/12/05 12:23 PM Page 551

Page 581: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT552

// now retrieve the inserted record from dbString query ="select id, name, content, date_created from resume where id=?";

// create PrepareStatement objectpstmt2 = conn.prepareStatement(query);pstmt2.setObject(1, inputValues[0]);rs = pstmt2.executeQuery();Object [] outputValues = new Object[columnNames.length];if (rs.next()) {

// outputValues contains the data retrieved from the databasefor ( int i = 0; i < columnNames.length; i++) {

outputValues[i] = rs.getObject(i+1);}

}

//// display retrieved data//if (dbVendor.equalsIgnoreCase("oracle")) {

System.out.println("id="+((java.math.BigDecimal) outputValues[0]).toString());

System.out.println("name="+ ((String) outputValues[1]));System.out.println("content="+

((Clob) outputValues[2]));System.out.println("date_created="+

((java.sql.Date) outputValues[3]).toString());}else if (dbVendor.equalsIgnoreCase("mysql")) {

System.out.println("id="+ ((Long) outputValues[0]).toString());System.out.println("name="+ ((String) outputValues[1]));System.out.println("content="+

((String) outputValues[2]));System.out.println("date_created="+

((java.sql.Timestamp) outputValues[3]).toString());}

System.out.println("---------------");System.out.println("-- Demo_PreparedStatement_SetObject end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(rs);DatabaseUtil.close(pstmt);DatabaseUtil.close(pstmt2);DatabaseUtil.close(conn);

}}

}

5203ch13.qxd 8/12/05 12:23 PM Page 552

Page 582: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 553

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ java Demo_PreparedStatement_SetObject oracle-- Demo_PreparedStatement_SetObject begin--conn=oracle.jdbc.driver.T4CConnection@341960---------------id=100name=Alex Taylorcontent=oracle.sql.CLOB@ccc588date_created=2005-07-11----------------- Demo_PreparedStatement_SetObject end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select * from resume;ID NAME CONTENT DATE_CREATED100 Alex Taylor This is my resume. 11-JUL-05

SQL>

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ java Demo_PreparedStatement_SetObject mysql-- Demo_PreparedStatement_SetObject begin--conn=com.mysql.jdbc.Connection@1dd46f7---------------id=100name=Alex Taylorcontent=This is my resume.date_created=2005-07-11 14:44:29.0----------------- Demo_PreparedStatement_SetObject end--

Viewing the MySQL Database After Running the ProgramThis shows the MySQL database after running the solution:

mysql> select * from resume;+-----+-------------+--------------------+---------------------+| id | name | content | date_created |+-----+-------------+--------------------+---------------------+| 100 | Alex Taylor | This is my resume. | 2005-07-11 13:56:55 |+-----+-------------+--------------------+---------------------+1 row in set (0.00 sec)

13-16. How Do You Use PreparedStatement.setRef()?A REF CURSOR is a ResultSet object that represents the result of a SQL query (such as select last_name,badge_number from employee) returned one row at a time. The following sections show how to passa java.sql.Ref (a REF CURSOR) object to a PreparedStatement object.

5203ch13.qxd 8/12/05 12:23 PM Page 553

Page 583: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT554

How It WorksThe Oracle database supports the REF data type, and you can use the setRef() method on Oracle data-bases. The MySQL database does not support the setRef() method. setRef() is not implemented inthe MySQL server, according to the MySQL Connector/J documentation. In fact, the following methodsin the MySQL driver have not been implemented yet. They rely on functionality that is not currentlypresent in the MySQL server.

PreparedStatement.setRef()ResultSet.getRef(int)ResultSet.getRef(String)ResultSet.updateRef(int, Ref)ResultSet.updateRef(String, Ref)

The signature of setRef() is as follows:

public void setRef(int index,java.sql.Ref ref) throws SQLException

This sets the designated parameter to the given REF(<structured-type>) value. The driver convertsthis to a SQL REF value when it sends it to the database.

The parameters are as follows:

• index: The first parameter is 1, the second is 2, and so on.

• ref: A SQL REF value.

This throws a SQLException if a database access error occurs.The java.sql.Ref interface is defined as follows:

public interface Ref

This is according to the JDK:

The mapping in the Java programming language of a SQL REF value, which is a reference toa SQL structured type value in the database. SQL REF values are stored in a table that containsinstances of a referenceable SQL structured type, and each REF value is a unique identifier forone instance in that table. A SQL REF value may be used in place of the SQL structured type itreferences, either as a column value in a table or an attribute value in a structured type.

Because a SQL REF value is a logical pointer to a SQL structured type, a Ref object is by defaultalso a logical pointer. Thus, retrieving a SQL REF value as a Ref object does not materialize theattributes of the structured type on the client. A Ref object (see Table 13-2) can be stored in thedatabase using the PreparedStatement.setRef method.

Table 13-2. Method Summary for java.sql.Ref Interface

Return Type Method

String getBaseTypeName() Retrieves the fully qualified SQL name of the SQLstructured type that this Ref object references

Object getObject() Retrieves the SQL structured type instancereferenced by this Ref object

Object getObject(Map map) Retrieves the referenced object and maps it to a Javatype using the given type map

void setObject(Object value) Sets the structured type value that this Ref objectreferences to the given instance of Object

5203ch13.qxd 8/12/05 12:23 PM Page 554

Page 584: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 555

SolutionThis class will read a REF (a reference of a manager column) from dept_table and then use that REFto create a new dept_table record:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetRef {public static void main(String[] args) {

System.out.println("--Demo_PreparedStatement_SetRef begin--");// read database vendorString dbVendor = args[0];// prepare arguments for dept_tableString deptName = args[0];String newDeptName = args[1];

ResultSet rs = null;Connection conn = null;PreparedStatement pstmt = null;PreparedStatement pstmt2 = null;try {

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare query for getting a REF object and PrepareStatement objectString refQuery = "select manager from dept_table where dept_name=?";pstmt = conn.prepareStatement(refQuery);pstmt.setString(1, deptName);rs = pstmt.executeQuery();java.sql.Ref ref = null;if (rs.next()) {

ref = rs.getRef(1);}

if (ref == null) {System.out.println("error: could not get a reference for manager.");System.exit(1);

}

// prepare query and create PrepareStatement objectString query = "INSERT INTO dept_table(dept_name, manager) "+

"values(?, ?)";pstmt2 = conn.prepareStatement(query);pstmt2.setString(1, newDeptName);pstmt2.setRef(2, ref);

// execute query, and return number of rows createdint rowCount = pstmt2.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_setRef end--");

}

5203ch13.qxd 8/12/05 12:23 PM Page 555

Page 585: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT556

catch(Exception e){e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(pstmt2);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseUsing Oracle, define a table that has a column of the REF type, as shown next:

SQL> desc employee_type;Name Null? Type---------------- -------- ------------NAME VARCHAR2(32)BADGE_NUMBER NUMBER

SQL> CREATE TABLE employee_table OF employee_type2 (primary key (badge_number));

SQL> desc employee_table;Name Null? Type----------------- -------- ------------NAME VARCHAR2(32)BADGE_NUMBER NOT NULL NUMBER

SQL> CREATE TABLE dept_table2 (dept_name varchar2(10),3 manager REF employee_type SCOPE IS employee_table);

SQL> desc dept_table;Name Null? Type--------------------- -------- ---------------------DEPT_NAME VARCHAR2(10)MANAGER REF OF EMPLOYEE_TYPENAME VARCHAR2(32)BADGE_NUMBER NUMBER

SQL> INSERT INTO employee_table(name, badge_number) VALUES ('alex', '111');SQL> INSERT INTO employee_table(name, badge_number) VALUES ('jane', '222');SQL> INSERT INTO employee_table(name, badge_number) VALUES ('mary', '333');SQL> select * from employee_table;

NAME BADGE_NUMBER--------- ------------alex 111jane 222mary 333

SQL> INSERT INTO dept_table2 select 'software', REF(e) FROM employee_table e

5203ch13.qxd 8/12/05 12:23 PM Page 556

Page 586: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 557

3 where badge_number='111';

SQL> select * from dept_table;

DEPT_NAME MANAGER---------- -------------------------------------------------------------------software 00002202089C70D37E33D943BEABDB84570C8588E4679E191E70DE4A00B8F64B3FA2FF8389

SQL> select dept_name, DEREF(manager) from dept_table;

DEPT_NAME DEREF(MANAGER)(NAME, BADGE_NUMBER)--------- ----------------------------------software EMPLOYEE_TYPE('alex', 111)

SQL> INSERT INTO dept_table2 select 'marketing', REF(e) FROM employee_table e3 where badge_number='222';

SQL> select * from dept_table;

DEPT_NAME MANAGER---------- -------------------------------------------------------------------software 00002202089C70D37E33D943BEABDB84570C8588E4679E191E70DE4A00B8F64B3FA2FF8389marketing 0000220208AE8D0652916243378F27890BA1937A40679E191E70DE4A00B8F64B3FA2FF8389

SQL> select dept_name, DEREF(manager) from dept_table;

DEPT_NAME DEREF(MANAGER)(NAME, BADGE_NUMBER)--------- ----------------------------------software EMPLOYEE_TYPE('alex', 111)marketing EMPLOYEE_TYPE('jane', 222)

SQL> commit;Commit complete.

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetRef.java$ java Demo_PreparedStatement_SetRef oracle software sales--Demo_PreparedStatement_setRef begin--conn=oracle.jdbc.driver.OracleConnection@860d49---------------rowCount=1--Demo_PreparedStatement_setRef end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select dept_name, DEREF(manager) from dept_table;DEPT_NAME DEREF(MANAGER)(NAME, BADGE_NUMBER)--------- ----------------------------------software EMPLOYEE_TYPE('alex', 111)marketing EMPLOYEE_TYPE('jane', 222)sales EMPLOYEE_TYPE('alex', 111)

5203ch13.qxd 8/12/05 12:23 PM Page 557

Page 587: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT558

13-17. How Do You Use PreparedStatement.setString()?The String class in Java is the most commonly used class in most database applications. The follow-ing sections show how to pass a String object to a PreparedStatement object.

How It WorksThe setString() method is the most commonly used method in PreparedStatement. You can usesetString() in both the Oracle and MySQL databases. In Oracle, when a column data type is VARCHARor VARCHAR2, then you can use setString(). In MySQL, when a column data type is VARCHAR, TINYTEXT,TEXT, MEDIUMTEXT, or LONGTEXT, then you can use setString().

The signature of setString() is as follows:

public void setString(int parameterIndex,java.lang.String x) throws SQLException

This sets the designated parameter to the given Java String value. The driver converts this toa SQL VARCHAR or LONGVARCHAR value (depending on the argument’s size relative to the driver’s limitson VARCHAR values) when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The parameter value.

This throws a SQLException if a database access error occurs.

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetString {public static void main(String[] args) {

System.out.println("--Demo_PreparedStatement_SetString begin--");String dbVendor = args[0]; // database vendor = { "mysql", "oracle" }String stringValue = args[1]; // value to be insertedConnection conn = null;PreparedStatement pstmt = null;try {

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");// prepare queryString query = "insert into string_table(string_column) values(?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, stringValue);

5203ch13.qxd 8/12/05 12:23 PM Page 558

Page 588: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 559

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--Demo_PreparedStatement_SetString end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> create table string_table(2 string_column VARCHAR(300)3 );

SQL> desc string_table;Name Null? Type--------------------- -------- --------------STRING_COLUMN VARCHAR2(300)

SQL> insert into string_table(string_column) values('abc');SQL> insert into string_table(string_column) values('1234567890');SQL> commit;Commit complete.SQL> select * from string_table;STRING_COLUMN-------------------abc1234567890

Running the Solution for the Oracle DatabaseThis show how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetString.java$ java Demo_PreparedStatement_SetString oracle "hello friend!"--Demo_PreparedStatement_SetString begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------rowCount=1--Demo_PreparedStatement_setString_Oracle end--$ java Demo_PreparedStatement_SetString oracle "hello oracle world."--Demo_PreparedStatement_SetString begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------rowCount=1--Demo_PreparedStatement_SetString end--

5203ch13.qxd 8/12/05 12:23 PM Page 559

Page 589: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT560

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the program:

SQL> select * from string_table;

STRING_COLUMN------------------------------------abc1234567890hello friend!hello oracle world.

Setting Up the MySQL DatabaseUsing MySQL, define a table that has a single column of the VARCHAR type. (In MySQL, the VARCHARdata type has a limitation size of 255 characters: see the following error from the MySQL server.)

mysql> create table string_table(-> string_column VARCHAR(255)-> );

mysql> desc string_table;+---------------+--------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+---------------+--------------+------+-----+---------+-------+| string_column | varchar(255) | YES | | NULL | |+---------------+--------------+------+-----+---------+-------+1 row in set (0.02 sec)

mysql> insert into string_table(string_column) values('abcdef');mysql> insert into string_table(string_column) values('hello MySQL!');mysql> commit;mysql> select * from string_table;+------------------+| string_column |+------------------+| abcdef || hello MySQL! |+------------------+2 rows in set (0.02 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetString.java$ java Demo_PreparedStatement_SetString mysql "this is it."--Demo_PreparedStatement_SetString begin--conn=com.mysql.jdbc.Connection@124bbbf---------------rowCount=1--Demo_PreparedStatement_SetString end--

$ java Demo_PreparedStatement_SetString mysql "hello MySQL!!!"--Demo_PreparedStatement_SetString begin--conn=com.mysql.jdbc.Connection@124bbbf---------------rowCount=1

5203ch13.qxd 8/12/05 12:23 PM Page 560

Page 590: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 561

Viewing the MySQL Database After Running the ProgramThis shows the MySQL database after running the program:

mysql> select * from string_table;+------------------+| string_column |+------------------+| abcdef || hello MySQL! || this is it. || hello MySQL!!! |+------------------+4 rows in set (0.00 sec)

13-18. How Do You Use PreparedStatement’s setTime() andsetTimestamp()?The following sections show how to pass time-related objects (java.sql.Time and java.sql.Timestamp)to a PreparedStatement object.

How It WorksYou can use the setTime() and setTimestamp() methods on both the Oracle and MySQL databases.In Oracle, when a column data type is DATE, then you can use the setTime() and setTimestamp()methods.Note that in Oracle the DATE data type can represent three distinct data types: Date, Time, and Timestamp.In MySQL, when a column data type is TIME, then you can use the setTime()method, and when a columndata type is TIMESTAMP, then you can use the setTimestamp()method.

The signatures of setTime() is as follows:

public void setTimestamp(int parameterIndex,java.sql.Timestamp x) throws SQLException

This sets the designated parameter to the given java.sql.Timestamp value. The driver convertsthis to a SQL TIMESTAMP value when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The parameter value.

This throws a SQLException if a database access error occurs.The signature of setTimestamp() is as follows:

public void setTime(int parameterIndex,java.sql.Time x) throws SQLException

This sets the designated parameter to the given java.sql.Time value. The driver converts thisto a SQL TIME value when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• x: The parameter value.

This throws a SQLException if a database access error occurs.

5203ch13.qxd 8/12/05 12:23 PM Page 561

Page 591: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT562

SolutionHere’s the solution:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetTimeAndTimestamp {public static java.sql.Timestamp getCurrentJavaSqlTimestamp() {

java.util.Date date = new java.util.Date();return new java.sql.Timestamp(date.getTime());

}public static java.sql.Time getCurrentJavaSqlTime() {

java.util.Date date = new java.util.Date();return new java.sql.Time(date.getTime());

}public static void main(String[] args) {

System.out.println("--SetTimeAndTimestamp begin--");String dbVendor = args[0]; // database vendor = { "mysql", "oracle" }String id = args[1];Connection conn = null;PreparedStatement pstmt = null;try {

conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");// prepare queryString query = "insert into time_table(id, "+

"time_column, timestamp_column) values(?, ?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, id);java.sql.Time time = getCurrentJavaSqlTime();System.out.println("time="+time);pstmt.setTime(2, time);java.sql.Timestamp timestamp = getCurrentJavaSqlTimestamp();System.out.println("timestamp="+timestamp);pstmt.setTimestamp(3, timestamp);

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--SetTimeAndTimestamp end--");

}catch(Exception e){

e.printStackTrace();System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}

5203ch13.qxd 8/12/05 12:23 PM Page 562

Page 592: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 563

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> create table time_table(id VARCHAR(12), time_column DATE, timestamp_column DATE);

SQL> insert into time_table(id, time_column, timestamp_column)values('id-1', '12-JUN-2003', '12-JUN-2003');

SQL> insert into time_table(id, time_column, timestamp_column)values('id-2', '01-JAN-2001',

to_date('01/01/2001 09:30:00','dd/mm/yyyy hh24:mi:ss'));

SQL> select * from time_table;ID TIME_COLUMN TIMESTAMP_COLUMN-------- ----------- ----------------id-1 12-JUN-03 12-JUN-03id-2 01-JAN-01 01-JAN-01

SQL> select id , to_char(time_column,'HH24:MI:SS') as time,to_char(timestamp_column,'DD-MM-YYYY HH24:MI:SS') as timestamp from time_table;ID TIME TIMESTAMP-------- -------- -------------------id-1 00:00:00 12-06-2003 00:00:00id-2 09:30:00 01-01-2001 09:30:00SQL> commit;Commit complete.

Running the Solution for the Oracle DatabaseThis shows how to run the solution for the Oracle database:

$ javac Demo_PreparedStatement_SetTimeAndTimestamp.java$ java Demo_PreparedStatement_SetTimeAndTimestamp oracle id-999--SetTimeAndTimestamp begin--conn=oracle.jdbc.driver.OracleConnection@860d49---------------time=09:57:42timestamp=Wed Nov 12 09:57:42 PST 2003rowCount=1--SetTimeAndTimestamp end--

Viewing the Oracle Database After Running the SolutionThis shows the Oracle database after running the solution:

SQL> select id, to_char(time_column,'HH24:MI:SS') as time,to_char(timestamp_column,'DD-MM-YYYY HH24:MI:SS') as timestampfrom time_table;ID TIME TIMESTAMP------- --------- -------------------id-1 00:00:00 12-06-2003 00:00:00id-2 09:30:00 01-01-2001 09:30:00id-999 09:57:42 11-12-2003 09:57:42

5203ch13.qxd 8/12/05 12:23 PM Page 563

Page 593: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT564

Setting Up the MySQL DatabaseUsing MySQL, define a table that has a column of the TIME and TIMESTAMP types, as shown next. TheMySQL database accepts times in the HH:MM:SS format.

mysql> create table time_table(id VARCHAR(12),time_column TIME, timestamp_column TIMESTAMP);

mysql> desc time_table;+------------------+--------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+------------------+--------------+------+-----+---------+-------+| id | varchar(12) | YES | | NULL | || time_column | time | YES | | NULL | || timestamp_column | timestamp(14)| YES | | NULL | |+------------------+--------------+------+-----+---------+-------+3 rows in set (0.03 sec)

mysql> insert into time_table(id, time_column, timestamp_column)values('id-1', '10:23:45', '2002-10-25 10:23:45');

mysql> insert into time_table(id, time_column, timestamp_column)values('id-2', '23:10:55', '2004-10-25 10:23:45');

mysql> select * from time_table;+------+-------------+------------------+| id | time_column | timestamp_column |+------+-------------+------------------+| id-1 | 10:23:45 | 20021025102345 || id-2 | 23:10:55 | 20041025102345 |+------+-------------+------------------+2 rows in set (0.03 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetTimeAndTimestamp.java$ java Demo_PreparedStatement_SetTimeAndTimestamp mysql id-5--SetTimeAndTimestamp begin--conn=com.mysql.jdbc.Connection@1546e25---------------time=11:11:10timestamp=Wed Nov 12 11:11:10 PST 2003rowCount=1--SetTimeAndTimestamp end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from time_table;+------+-------------+------------------+| id | time_column | timestamp_column |+------+-------------+------------------+| id-1 | 10:23:45 | 20021025102345 || id-2 | 23:10:55 | 20041025102345 || id-5 | 11:11:10 | 20031112111110 |+------+-------------+------------------+3 rows in set (0.03 sec)

5203ch13.qxd 8/12/05 12:23 PM Page 564

Page 594: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

13-19. How Do You Use PreparedStatement.setURL()?A URL is a reference (an address) to a resource on the Internet. The following sections show how topass a URL (a java.net.URL object) to a PreparedStatement object.

How It WorksYou cannot use setURL() on the Oracle database because it is an unsupported feature, as I will illus-trate with an example. In MySQL, when a column data type is VARCHAR, then you can use setURL().MySQL does not have a URL data type, but when you pass a java.net.URL object, the driver convertsit to a String object.

The signature of setURL() is as follows:

public void setURL(int parameterIndex,java.net.URL url) throws SQLException

This sets the designated parameter to the given java.net.URL value. The driver converts this toa SQL DATALINK value when it sends it to the database.

The parameters are as follows:

• parameterIndex: The first parameter is 1, the second is 2, and so on.

• url: The java.net.URL object to be set.

This throws a SQLException if a database access error occurs.

SolutionThis class will read two values (an ID and a URL as a string) and then create a new record with thesevalues:

import java.util.*;import java.io.*;import java.sql.*;

import jcb.util.DatabaseUtil;import jcb.db.VeryBasicConnectionManager;

public class Demo_PreparedStatement_SetURL {public static void main(String[] args) {

System.out.println("--SetURL begin--");String dbVendor = args[0]; // database vendor = { "mysql", "oracle" }String idValue = args[1];String urlValue = args[2];Connection conn = null;PreparedStatement pstmt = null;try {

System.out.println("--SetURL begin--");conn = VeryBasicConnectionManager.getConnection(dbVendor);System.out.println("conn="+conn);System.out.println("---------------");

// prepare queryString query = "insert into url_table(id, url) values(?, ?)";

// create PrepareStatement objectpstmt = conn.prepareStatement(query);pstmt.setString(1, idValue);pstmt.setURL(2, new java.net.URL(urlValue));

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 565

5203ch13.qxd 8/12/05 12:23 PM Page 565

Page 595: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT566

// execute query, and return number of rows createdint rowCount = pstmt.executeUpdate();System.out.println("rowCount="+rowCount);System.out.println("--SetURL end--");

}catch(Exception e){

System.out.println("ERROR: "+ e.getMessage());System.exit(1);

}finally {

// release database resourcesDatabaseUtil.close(pstmt);DatabaseUtil.close(conn);

}}

}

Setting Up the Oracle DatabaseThis shows how to set up the Oracle database:

SQL> create table url_table(id varchar(10), url varchar(255));SQL> desc url_table;Name Null? Type--------------- -------- ---------------ID VARCHAR2(10)URL VARCHAR2(255)

SQL> insert into url_table(id, url) values('ASKJ', 'http://www.askjeeves.com');SQL> insert into url_table(id, url) values('IBM', 'http://www.ibm.com');SQL> commit;Commit complete.SQL> select * from url_table;ID URL------- ---------------------------ASKJ http://www.askjeeves.comIBM http://www.ibm.com

Running the Solution for the Oracle DatabaseThis shows that setURL() is unsupported:

$ javac Demo_PreparedStatement_SetURL.java$ java Demo_PreparedStatement_SetURL oracle SUNW "http://www.sun.com"--SetURL begin--conn=oracle.jdbc.driver.OracleConnection@6e70c7---------------ERROR: java.sql.SQLException: Unsupported feature

Viewing the Oracle Database After Running the SolutionThe Oracle database has no changes, because setURL() is unsupported.

5203ch13.qxd 8/12/05 12:23 PM Page 566

Page 596: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 13 ■ PASSING INPUT PARAMETERS TO PREPAREDSTATEMENT 567

Setting Up the MySQL DatabaseThe MySQL database does not have a URL data type; however, if you pass a java.net.URL object, thedriver converts it to a String object. Using MySQL, define a table that has a column of the VARCHAR type,as shown next:

mysql> create table url_table (-> id varchar(10),-> url varchar(255) );

mysql> desc url_table;+-------+--------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+--------------+------+-----+---------+-------+| id | varchar(10) | YES | | NULL | || url | varchar(255) | YES | | NULL | |+-------+--------------+------+-----+---------+-------+2 rows in set (0.02 sec)

mysql> insert into url_table(id, url) values('ASKJ', 'http://www.askjeeves.com');mysql> insert into url_table(id, url) values('IBM', 'http://www.ibm.com');mysql> commit;mysql> select * from url_table;+------+--------------------------+| id | url |+------+--------------------------+| ASKJ | http://www.askjeeves.com || IBM | http://www.ibm.com |+------+--------------------------+2 rows in set (0.04 sec)

Running the Solution for the MySQL DatabaseThis shows how to run the solution for the MySQL database:

$ javac Demo_PreparedStatement_SetURL.java$ java Demo_PreparedStatement_SetURL mysql SUNW "http://www.sun.com"--SetURL begin--conn=com.mysql.jdbc.Connection@124bbbf---------------rowCount=1--SetURL end--

Viewing the MySQL Database After Running the SolutionThis shows the MySQL database after running the solution:

mysql> select * from url_table;+------+--------------------------+| id | url |+------+--------------------------+| ASKJ | http://www.askjeeves.com || IBM | http://www.ibm.com || SUNW | http://www.sun.com |+------+--------------------------+3 rows in set (0.00 sec)

5203ch13.qxd 8/12/05 12:23 PM Page 567

Page 597: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch13.qxd 8/12/05 12:23 PM Page 568

Page 598: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Exploring JDBC Utilities

What is a JDBC utility? A JDBC utility is a small Java component (such as a package, class, inter-face, or method) that does something useful and should have the following characteristics:

• It has small, clean, and fast code.

• It is commonly used by most of the JDBC application programs.

• Is a safe call (has no possibility for resource leaks).

• It improves the readability and maintenance of the code.

• It is portable across databases as well as operating systems (as much as possible).

• It avoids repetition and improves reliability.

This chapter provides a set of JDBC utilities (classes/methods). In general, a utility methodshould be a public static method, should be as independent as possible (not relying on globaldata structures and values), and should be defined inside a helper/utility class. For example,javax.imageio.ImageIO is a utility class; it contains static convenience methods for locatingImageReader and ImageWriter objects and performing simple encoding and decoding. Anotherexample is the DbUtils package from Apache (http://jakarta.apache.org/commons/dbutils/); itcontains a small set of classes designed to make working with JDBC easier.

14-1. What Are JDBC Utilities?JDBC utilities are useful, fast, independent, small, and well-tested classes/methods that are used inmost JDBC applications and are the commonly used methods in many classes. The JDBC classes/methods make working with JDBC easier. For example, closing a Connection object and closinga ResultSet object are considered utility tasks. (These actions will occur in most of the JDBC classes/methods.) Another example is getting a current date as a java.sql.Date object. You need to organ-ize the utilities (in a helper package/class) and then use them in a consistent fashion in yourapplication or framework.

14-2. How Do You Close a java.sql.Connection Object?java.sql.Connection represents a database Connection object. In general, you should close a data-base connection after its usage (in order to release database and JDBC resources, such as memoryand data structures, to other users). If you obtain a connection from a connection pool, then youmust return it to the pool; otherwise, you have to properly close it. In the following sections, I willshow you several solutions for closing a Connection object.

569

C H A P T E R 1 4

■ ■ ■

5203ch14.qxd 8/12/05 12:24 PM Page 569

Page 599: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

CHAPTER 14 ■ EXPLORING JDBC UTILITIES570

Soft Closing a Connection (Using a Connection Pool Manager)This shows how to soft close a connection:

/*** Soft close a connection;* return the connection to the pool manager.* @param conn a java.sql.Connection object.*/public static void closeQuietly(java.sql.Connection conn) {

if (conn == null) {return;

}

// "soft" close of a Connection object; instead of actual closing// a Connection object, it is returned to the pool for reuse.// you may replace ConnectionPoolManager with your desired connection// pool manager class.ConnectionPoolManager.close(conn);

}

Closing a Connection and Not Reporting ExceptionsThis shows how to close a connection and not report exceptions:

/*** Close a connection; avoid closing if null, and* hide any SQLExceptions that occur.* @param conn a java.sql.Connection object.*/public static void closeQuietly(java.sql.Connection conn) {

if (conn == null) {return;

}

try {if(!conn.isClosed()) {

// releases this Connection object's database and// JDBC resources immediately instead of waiting// for them to be automatically released.conn.close();

}}catch (Exception e) {

// ignore exceptionse.printStackTrace();

}}

Closing a Connection and Logging ExceptionsThis shows how to close a connection and log exceptions:

/*** Close a connection; avoid closing if null, and* hide any SQLExceptions that occur.* @param conn a java.sql.Connection object.* @param logger a Logger object is used to log messages

5203ch14.qxd 8/12/05 12:24 PM Page 570

Page 600: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

*/public static void closeQuietly(java.sql.Connection conn,

java.util.logging.Logger logger) {if (conn == null) {

return;}

try {if(!conn.isClosed()) {

// releases this Connection object's database and// JDBC resources immediately instead of waiting// for them to be automatically released.conn.close();

}}catch (Exception e) {

// handle the exception and log ite.printStackTrace();if (logger != null) {

logger.warning("closeQuietly: could not close connection object");}

}}

Closing a Connection and Reporting ExceptionsThis shows how to close a connection and report exceptions:

/*** Close a connection; avoid closing if null, and* report any SQLExceptions that occur.* @param conn a java.sql.Connection object.* @throws SQLException failed to close the connection*/public static void close(java.sql.Connection conn)

throws java.sql.SQLException {

if (conn == null) {return;

}

if(!conn.isClosed()) {// releases this Connection object's database and// JDBC resources immediately instead of waiting// for them to be automatically released.conn.close();

}}

14-3. How Do You Commit and Close a Connection Object?In general, you should close a database connection after its usage. If you obtain a connection froma connection pool, then you must return it to the pool; otherwise, you have to properly close it. Inthe following sections, I will show you several solutions for closing a Connection object.

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 571

5203ch14.qxd 8/12/05 12:24 PM Page 571

Page 601: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Committing and Closing a Connection and Not Reporting ExceptionsThis shows how to commit and close a connection and not report exceptions:

/*** Commit and close a connection; avoid closing if* null and hide any SQLExceptions that occur.* @param conn a java.sql.Connection object.*/public static void commitAndCloseQuietly(java.sql.Connection conn) {

if (conn == null) {return;

}

if(conn.isClosed()) {return;

}

try {conn.commit();conn.close();// hint the "garbage collector"conn = null;

}catch (SQLException e) {

// ignore the exception}

}

Committing and Closing a Connection and Logging ExceptionsThis shows how to commit and close a connection and log exceptions:

/*** Commit and close a connection; avoid closing* if null, and hide any SQLExceptions that occur.* @param conn a java.sql.Connection object.* @param logger a Logger object is used to log messages.*/public static void commitAndCloseQuietly(java.sql.Connection conn,

java.util.logging.Logger logger) {if(conn == null) {

return;}

if(conn.isClosed()) {return;

}

try {conn.commit();conn.close();// hint the "garbage collector"conn = null;

}catch( SQLException e ) {

// handle the exception and log ite.printStackTrace();

CHAPTER 14 ■ EXPLORING JDBC UTILITIES572

5203ch14.qxd 8/12/05 12:24 PM Page 572

Page 602: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

if (logger != null) {logger.warning("commitAndCloseQuietly: "+e.getMessage());

}}

}

Committing and Closing Connection and Reporting ExceptionsThis shows how to commit and close a connection and report exceptions:

/*** Commit and close a connection; avoid closing if null, and* report any SQLExceptions that occur.* @param conn a java.sql.Connection object.* @throws SQLException failed to commit and close the connection*/public static void commitAndClose(java.sql.Connection conn)

throws java.sql.SQLException {

if( conn == null ) {return;

}

if( conn.isClosed()) {throw new SQLException("Connection closed.");

}

try {conn.commit();conn.close();// hint the "garbage collector"conn = null;

}catch( SQLException e ) {

e.printStackTrace();throw e;

}}

14-4. How Do You Roll Back and Close a Connection Object?In general, you should close database connections after their usage. If you obtain a connection froma connection pool, then you must return it to the pool; otherwise, you have to properly close it. Inthe following sections, I will show you several solutions for closing a Connection object.

The Connection.rollback() statement in SQL cancels the proposed changes in a pending data-base transaction. According to the JDK, Connection.rollback() undoes all changes made in thecurrent transaction and releases any database locks currently held by this Connection object. Youshould use this method only when autocommit mode has been disabled.

Rolling Back and Closing a Connection and Not Reporting ExceptionsThis shows how to roll back and close a connection and not report exceptions:

/*** Roll back and close a connection; avoid closing* if null, and hide any SQLExceptions that occur.

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 573

5203ch14.qxd 8/12/05 12:24 PM Page 573

Page 603: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

* @param conn a java.sql.Connection object.*/public static void rollbackAndCloseQuietly(java.sql.Connection conn) {

try {if(conn == null) {

return;}

if(conn.isClosed()) {return;

}

conn.rollback();conn.close();// hint the "garbage collector"conn = null;

}catch(SQLException e) {

// ignore}

}

Rolling Back and Closing a Connection and Logging ExceptionsThis shows how to roll back and close a connection and log exceptions:

/*** Roll back and close a connection; avoid closing* if null, and hide any SQLExceptions that occur.* @param conn a java.sql.Connection object.* @param logger a Logger object is used to log messages.*/public static void rollbackAndCloseQuietly(java.sql.Connection conn,

java.util.logging.Logger logger) {try {

if(conn == null) {return;

}

if(conn.isClosed()) {return;

}

conn.rollback();conn.close();// hint the "garbage collector"conn = null;

}catch(SQLException e) {

// handle the exception and log ite.printStackTrace();if (logger != null) {

logger.warning("rollbackAndCloseQuietly: "+e.getMessage());}

}}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES574

5203ch14.qxd 8/12/05 12:24 PM Page 574

Page 604: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Rolling Back and Closing Connection and Reporting ExceptionsThis shows how to roll back and close a connection and report exceptions:

/*** Roll back and close a connection; avoid closing if null, and* report any SQLExceptions that occur.* @param conn a java.sql.Connection object.* @throws SQLException failed to roll back and close the connection*/public static void rollbackAndClose(java.sql.Connection conn)

throws SQLException {try {

if(conn == null) {return;

}

if(conn.isClosed()) {throw new SQLException("connection is closed.");

}

conne.rollback();conn.close();// hint the "garbage collector"conn = null;

}catch(SQLException e) {

e.printStackTrace();throw e;

}}

14-5. How Do You Close a ResultSet Object?ResultSet.close() releases the ResultSet object’s database and JDBC resources immediatelyinstead of waiting for this to happen automatically when it is closed.

Closing a ResultSet and Not Reporting ExceptionsThis shows how to close a ResultSet and not report exceptions:

/*** Close a ResultSet; avoid closing if null, and* hide any SQLExceptions that occur.* @param rs a java.sql.ResultSet object.*/public static void closeQuietly(java.sql.ResultSet rs) {

if (rs == null) {return;

}

try {// releases this ResultSet object's database and// JDBC resources immediately instead of waiting// for this to happen when it is automatically closed.rs.close();

}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 575

5203ch14.qxd 8/12/05 12:24 PM Page 575

Page 605: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

catch (Exception e) {//ignoree.printStackTrace();

}}

Closing a ResultSet and Logging ExceptionsThis shows how to close a ResultSet and log exceptions:

/*** Close a ResultSet; avoid closing if null, and* hide any SQLExceptions that occur.* @param rs a java.sql.ResultSet object.* @param logger a Logger object is used to log messages.*/public static void closeQuietly(java.sql.ResultSet rs,

java.util.logging.Logger logger) {if (rs == null) {

return;}

try {rs.close();

}catch (Exception e) {

// handle the exception and log ite.printStackTrace();if (logger != null) {

logger.warning("closeQuietly: "+e.getMessage());}

}}

Closing ResultSet and Reporting ExceptionsThis shows how to close a ResultSet and report exceptions:

/*** Close a ResultSet; avoid closing if null, and* report any SQLExceptions that occur.* @param rs a java.sql.ResultSet object.* @throws SQLException failed to close the ResultSet*/public static void close(java.sql.ResultSet rs)

throws java.sql.SQLException {if (rs != null) {

rs.close();}

}

14-6. How Do You Close a Statement Object?Statement.close() releases the Statement object’s database and JDBC resources immediatelyinstead of waiting for this to happen automatically when it is closed.

CHAPTER 14 ■ EXPLORING JDBC UTILITIES576

5203ch14.qxd 8/12/05 12:24 PM Page 576

Page 606: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Closing a Statement and Not Reporting ExceptionsThis shows how to close a Statement and not report exceptions:

/*** Close a Statement; avoid closing if null, and* hide any SQLExceptions that occur.* @param stmt a java.sql.Statement object.*/public static void closeQuietly(java.sql.Statement stmt) {

if (stmt == null) {return;

}

try {// releases this Statement object's database and// JDBC resources immediately instead of waiting// for this to happen when it is automatically closed.stmt.close();

}catch (Exception e) {

//ignoree.printStackTrace();

}}

Closing a Statement and Logging ExceptionsThis shows how to close a Statement and log exceptions:

/*** Close a Statement; avoid closing if null,* and hide any SQLExceptions that occur.* @param stmt a java.sql.Statement object.* @param logger a Logger object is used to log messages.*/public static void closeQuietly(java.sql.Statement stmt,

java.util.logging.Logger logger) {if (stmt == null) {

return;}

try {stmt.close();

}catch (Exception e) {

//ignore// handle the exception and log ite.printStackTrace();if (logger != null) {

logger.warning("closeQuietly: "+e.getMessage());}

}}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 577

5203ch14.qxd 8/12/05 12:24 PM Page 577

Page 607: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Closing a Statement and Reporting ExceptionsThis shows how to close a Statement and report exceptions:

/*** Close a Statement; avoid closing if null,* and report any SQLExceptions that occur.* @param stmt a java.sql.Statement object.* @throws SQLException failed to close the Statement*/public static void close(java.sql.Statement stmt)

throws java.sql.SQLException {if (stmt != null) {

stmt.close();}

}

14-7. How Do You Close a PreparedStatement Object?PreparedStatement.close() releases the PreparedStatement object’s database and JDBC resourcesimmediately instead of waiting for this to happen automatically when it is closed.

Closing a PreparedStatement and Not Reporting ExceptionsThis shows how to close a PreparedStatement and not report exceptions:

/*** Close a PreparedStatement; avoid closing if null, and* hide any SQLExceptions that occur.* @param pstmt a java.sql.PreparedStatement object.*/public static void closeQuietly(java.sql.PreparedStatement pstmt) {

try {if (pstmt != null) {

pstmt.close();}

}catch (Exception e) {

//ignoree.printStackTrace();

}}

Closing a PreparedStatement and Logging ExceptionsThis shows how to close a PreparedStatement and log exceptions:

/*** Close a PreparedStatement; avoid closing if null, and* hide any SQLExceptions that occur.* @param pstmt a java.sql.PreparedStatement object.* @param logger a Logger object is used to log messages .*/public static void closeQuietly(java.sql.PreparedStatement pstmt,

java.util.logging.Logger logger) {try {

if (pstmt != null) {

CHAPTER 14 ■ EXPLORING JDBC UTILITIES578

5203ch14.qxd 8/12/05 12:24 PM Page 578

Page 608: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

pstmt.close();}

}catch (Exception e) {

// handle the exception and log ite.printStackTrace();if (logger != null) {

logger.warning("closeQuietly: "+e.getMessage());}

}}

Closing PreparedStatement and Reporting ExceptionsThis shows how to close a PreparedStatement and report exceptions:

/*** Close a PreparedStatement; avoid closing if null, and* report any SQLExceptions that occur.* @param pstmt a java.sql.PreparedStatement object.* @throws SQLException failed to close the PreparedStatement*/public static void close(java.sql.PreparedStatement pstmt)

throws java.sql.SQLException {if (pstmt != null) {

pstmt.close();}

}

14-8. How Do You Close Statement and Connection ObjectsTogether?The following utility method closes a Statement object and a Connection object where Statement isderived from the Connection object.

Closing Statement and Connection Objects and Ignoring ExceptionsThis shows how to close Statement and Connection objects and ignore exceptions:

/*** Close a statement and connection.* @param stmt a Statement object (derived from conn object)* @param conn a Connection object*/public static void closeQuietly(Statement stmt, Connection conn) {

if (stmt != null) {try {

// close the statementstmt.close();

}catch (Exception ignored1) {}

}

if (conn != null) {try {

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 579

5203ch14.qxd 8/12/05 12:24 PM Page 579

Page 609: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

// close the connectionif (!conn.isClosed()) {

conn.close();}

}catch (Exception ignored2) {}

}}

Closing Statement and Connection Objects and Reporting ExceptionsThis shows how to close Statement and Connection objects and report exceptions:

/*** Close a statement and connection.* @param stmt a Statement object (derived from conn object)* @param conn a Connection object* @throws SQLException failed to close statement and connection.*/public static void close(Statement stmt, Connection conn)

throws SQLException {

if (stmt != null) {// close the statementstmt.close();

}

if (conn != null) {// close the connectionif (!conn.isClosed()) {

conn.close();}

}}

14-9. How Do You Close ResultSet, Statement, and ConnectionObjects Together?The following utility method closes ResultSet, Statement, and Connection objects where Statementis derived from the Connection object and ResultSet is derived from the Statement object.

Closing ResultSet, Statement, and Connection Objects and Ignoring ExceptionsThis shows how to close ResultSet, Statement, and Connection objects and ignore exceptions:

/*** Close ResultSet, Statement, and Connection objects.* @param rs a ResultSet object (derived from stmt object)* @param stmt a Statement object (derived from conn object)* @param conn a Connection object*/public static void closeQuietly(ResultSet rs, Statement stmt, Connection conn) {

if (rs != null) {try {

CHAPTER 14 ■ EXPLORING JDBC UTILITIES580

5203ch14.qxd 8/12/05 12:24 PM Page 580

Page 610: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

// close the statementrs.close();

}catch (Exception ignored1) {}

}

if (stmt != null) {try {

// close the statementstmt.close();

}catch (Exception ignored2) {}

}if (conn != null) {

try {// close the connectionif (!conn.isClosed()) {

conn.close();}

}catch (Exception ignored3) {}

}}

Closing ResultSet, Statement, and Connection Objects and Reporting ExceptionsThis shows how to close ResultSet, Statement, and Connection objects and report exceptions:

/*** Close ResultSet, Statement, and Connection objects.* @param rs a ResultSet object (derived from stmt object)* @param stmt a Statement object (derived from conn object)* @param conn a Connection object* @throws SQLException failed to close statement and connection.*/public static void close(ResultSet rs, Statement stmt, Connection conn)

throws SQLException {

if (rs != null) {// close the result setrs.close();

}

if (stmt != null) {// close the statementstmt.close();

}

if (conn != null) {// close the connectionif (!conn.isClosed()) {

conn.close();}

}}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 581

5203ch14.qxd 8/12/05 12:24 PM Page 581

Page 611: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

14-10. How Do You Return a Long Text Column/Field froma Database?The following utility can apply to a long column/field such as a CLOB or LongVarChar data type.

Returning a Larger Text Column/Field Using a Column IndexThis shows how to return a larger text column/field using a column index:

/*** Return a larger text field from the database.* @param rs a ResultSet object* @param columnIndex index column of a long column/field* @return a larger text field from the database.* @throws SQLException failed to get a long string field/column.*/public static String getLargerString(ResultSet rs, int columnIndex)

throws SQLException {if ((rs == null) || (columnIndex < 1)) {

return null;}

// buffer size when reading long stringsInputStream in = null;int BUFFER_SIZE = 1024;try {

// retrieves the value of the designated column in the// current row of this ResultSet object as a stream of// ASCII characters. The value can then be read in chunks// from the stream. This method is particularly suitable// for retrieving large LONGVARCHAR values. The JDBC driver// will do any necessary conversion from the database// format into ASCIIin = rs.getAsciiStream(columnIndex);if (in == null) {

return "";}

byte[] arr = new byte[BUFFER_SIZE];StringBuffer buffer = new StringBuffer();int numRead = in.read(arr);while (numRead != -1) {

buffer.append(new String(arr, 0, numRead));numRead = in.read(arr);

}return buffer.toString();

}catch (Exception e) {

e.printStackTrace();throw new SQLException(e.getMessage());

}}

Returning a Larger Text Column/Field Using a Column NameThis shows how to return a larger text column/field using a column name:

CHAPTER 14 ■ EXPLORING JDBC UTILITIES582

5203ch14.qxd 8/12/05 12:24 PM Page 582

Page 612: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

/*** Return a larger text field from the database.* @param rs a ResultSet object* @param columnName name of column a long column/field* @return a long text field from the database.* @throws SQLException failed to get a long string field/column.*/public static String getLargerString(ResultSet rs, String columnName)

throws SQLException {if ((rs == null) || (columnName == null)) {

return null;}

// buffer size when reading long stringsInputStream stream = null;int BUFFER_SIZE = 1024;try {// retrieves the value of the designated column in the// current row of this ResultSet object as a stream of// ASCII characters. The value can then be read in chunks// from the stream. This method is particularly suitable// for retrieving large LONGVARCHAR values. The JDBC driver// will do any necessary conversion from the database// format into ASCII

stream = rs.getAsciiStream(columnName);if (stream == null) {

return "";}

byte[] arr = new byte[BUFFER_SIZE];StringBuffer buffer = new StringBuffer();int numRead = stream.read(arr);while (numRead != -1) {

buffer.append(new String(arr, 0, numRead));numRead = stream.read(arr);

}return buffer.toString();

}catch (Exception e) {

e.printStackTrace();throw new SQLException(e.getMessage());

}}

14-11. How Do You Store a Long Text Field in a Database?This utility stores a long text field in a database:

/*** Store a long text field in the database. For some strings,* for example, a message's text will be quite long and cannot* be stored using JDBC's setString() method.** @param pstmt a PreparedStatement object.* @param parameterIndex the first parameter is 1, the second is 2, ...* @param data string to be set* @throws Exception failed to Store a long text field in the database.

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 583

5203ch14.qxd 8/12/05 12:24 PM Page 583

Page 613: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

*/public static void setLongString(PreparedStatement pstmt,

int parameterIndex,String data) throws Exception {

if (pstmt == null) {return;

}

if (data == null) {// sets the designated parameter to SQL NULL.pstmt.setNull(parameterIndex, java.sql.Types.LONGVARCHAR);

}else if (data.length() > 0) {

// possibly a long stringpstmt.setAsciiStream(

parameterIndex,new ByteArrayInputStream(data.getBytes()),data.length());

}else {

// empty stringpstmt.setString(parameterIndex, "");

}}

14-12. How Do You Get Table Names from a Database?The following utility returns table names from a database.

Viewing the Constants RequiredThese are the constants required:

final static String TABLE_NAME = "TABLE_NAME";final static String TABLE_SCHEMA = "TABLE_SCHEM";final static String[] TABLE_TYPES = {"TABLE"};final static String[] VIEW_TYPES = {"VIEW"};final static String[] TABLE_AND_VIEW_TYPES = {"TABLE","VIEW"};

Getting Table Names from a DatabaseThis shows how to get the table names from a database:

public java.util.Map getTables(Connection conn) throws Exception {if ( (conn == null) || (conn.isClosed()) ) {

return null;}

DatabaseMetaData dbmd = conn.getMetaData();if (dbmd == null) {

throw new Exception("metadata not supported by vendor");}java.util.Map result = new HashMap();ResultSet tables = dbmd.getTables(null,null,null,TABLE_TYPES);while(tables.next()){

result.put(tables.getString(TABLE_NAME),

CHAPTER 14 ■ EXPLORING JDBC UTILITIES584

5203ch14.qxd 8/12/05 12:24 PM Page 584

Page 614: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

tables.getString(TABLE_SCHEMA));}closeQuietly(tables);return result;

}

In general, if you know the catalog or schema of your database, you should pass these—insteadof passing null—in the getTables() method, which can improve the performance of your query (forgetting table names).

14-13. How Do You Get View Names from a Database?The following utility returns view names from a database:

Viewing the Constants RequiredThese are the constants required:

final static String TABLE_NAME = "TABLE_NAME";final static String TABLE_SCHEMA = "TABLE_SCHEM";final static String[] TABLE_TYPES = {"TABLE"};final static String[] VIEW_TYPES = {"VIEW"};final static String[] TABLE_AND_VIEW_TYPES = {"TABLE","VIEW"};

Getting View Names from a DatabaseThis shows how to get view names from a database:

public java.util.Map getViews(Connection conn) throws Exception {if ( (conn == null) || (conn.isClosed()) ) {

return null;}

DatabaseMetaData dbmd = conn.getMetaData();if (dbmd == null) {

throw new Exception("metadata not supported by vendor");}java.util.Map result = new HashMap();ResultSet views = dbmd.getTables(null,null,null,VIEW_TYPES);while(views.next()){

result.put(views.getString(TABLE_NAME),views.getString(TABLE_SCHEMA));

}

closeQuietly(views);return result;

}

In general, if you know the catalog or schema of your database, you should pass these—insteadof passing null—in the getTables() method, which can improve the performance of your query (forgetting table names).

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 585

5203ch14.qxd 8/12/05 12:24 PM Page 585

Page 615: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

14-14. How Do You Get Table and View Names from a Database?The following utility returns table and view names from a database.

Viewing the Constants RequiredThese are the constants required:

final static String TABLE_NAME = "TABLE_NAME";final static String TABLE_SCHEMA = "TABLE_SCHEM";final static String[] TABLE_TYPES = {"TABLE"};final static String[] VIEW_TYPES = {"VIEW"};final static String[] TABLE_AND_VIEW_TYPES = {"TABLE","VIEW"};

Getting Table and View Names from a DatabaseThis shows how to get table and view names from a database:

public java.util.Map getTablesAndViews(Connection conn) throws Exception {if ( (conn == null) || (conn.isClosed()) ) {

return null;}

DatabaseMetaData dbmd = conn.getMetaData();if (dbmd == null) {

throw new Exception("metadata not supported by vendor");}java.util.Map result = new HashMap();ResultSet rs = dbmd.getTables(null,

null,null,TABLE_AND_VIEW_TYPES);while(rs.next()){

result.put(rs.getString(TABLE_NAME), rs.getString(TABLE_SCHEMA));}

closeQuietly(rs);return result;

}

In general, if you know the catalog or schema of your database, you should pass these—insteadof passing null—in the getTables() method, which can improve the performance of your query (forgetting table names).

14-15 How Do You Convert an InputStream Object to a Byte Array?This utility reads the entire input stream provided (as an InputStream object) and returns its contentas a byte array:

/*** Read the entire input stream provided and* return its content as a byte array.** @param input the InputStream from* which a result is being retrieved.* @return a byte array containing the content of the input stream* @throws Exception Failed to read the entire input stream provided*/public static byte[] getByteArray(InputStream input) throws Exception {

if (input == null) {return null;

CHAPTER 14 ■ EXPLORING JDBC UTILITIES586

5203ch14.qxd 8/12/05 12:24 PM Page 586

Page 616: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

byte[] result = null;try {

int length = in.available();result = new byte[length];in.read(result, 0, length);return result;

}catch (Exception e) {

throw new Exception("could not read InputStream: "+e.getMessage());}

}

14-16. How Do You Get a Current java.sql.Date Object?This is how you get a current java.sql.Date:

public static java.sql.Date getJavaSqlDate() {java.util.Date date = new java.util.Date();return new java.sql.Date(date.getTime());

}

14-17. How Do You Get a Trimmed String from a Database Column?When creating a new table, when you use a CHAR(n) instead of a VARCHAR(n), then the databaseserver will pad your data with spaces. You can use the following method to get rid of redundantspaces:

/*** Get a trimmed string from a database column.* @param rs a ResultSet object.* @param index column's index* @throws SQLException failed to get a trimmed string* from a database column.*/public static String getTrimmedString(ResultSet rs, int index)

throws SQLException {String value = rs.getString(index);

if (value != null) {value = value.trim();}

return value;}

/*** Get a trimmed string from a database column.* @param rs a ResultSet object.* @param columnName column's name* @throws SQLException failed to get a trimmed string* from a database column.*/public static String getTrimmedString(ResultSet rs , String columnName)

throws SQLException {String value = rs.getString(columnName);

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 587

5203ch14.qxd 8/12/05 12:24 PM Page 587

Page 617: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

if (value != null) {value = value.trim();

}

return value;}

14-18. How Do You Load a JDBC Driver?This shows how to load a JDBC driver:

/*** Loads and registers a database driver class. If this* succeeds, it returns true, else it returns false.** @param driverClassName name of JDBC driver** @return true if driver is loaded successfully,* otherwise return false.*/public static boolean loadDriver(java.lang.String driverClassName)

try {Class.forName(driverClassName);

//// loaded driver successfully//return true;

}catch(Exception e) {

//// could not load a driver.//return false;

}}

14-19. How Do You Format a String?You can format a String in three ways, as shown in the following sections.

First Solution: Using String ConcatenationThis solution will generate lots of unneeded String objects (and is not an efficient solution):

/*** Format a string and return it with the desired length.* @param str input string* @param finalLength final length desired* @return a formatted string with the desired length* @throws Exception failed to format a string*/public static String format(String str, int finalLength) throws Exception {if (str == null) {

return null;}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES588

5203ch14.qxd 8/12/05 12:24 PM Page 588

Page 618: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

String result=null;if (finalLength <= str.length()){result = str.substring(0, finalLength);

}else {

result = str;for (int i = str.length(); i < finalLength; i++) {// pad with spacesresult = result + " ";

}}return (result);

}

Second Solution: Using StringBuffer’s append()This solution is an efficient solution and will not generate lots of unneeded String objects:

/*** Format a string and return it with the desired length.* @param str input string* @param finalLength final length desired* @return a formatted string with the desired length* @throws Exception failed to format a string*/public static String format(String str, int finalLength) throws Exception {

if (str == null) {return null;

}

String result=null;if (finalLength <= str.length()){

result = str.substring(0, finalLength);return result;

}else {

StringBuffer buffer = new StringBuffer(str);for (int i = str.length(); i < finalLength; i++) {

// pad with spacesbuffer.append(" ");

}return buffer.toString();

}}

Third Solution: Using the java.lang.StringBuilder ClassYou can use the StringBuilder class (since JDK 1.5) to efficiently format String objects. This solu-tion is an efficient solution and will not generate lots of unneeded String objects:

/*** Format a string and return it with the desired length.* @param str input string* @param finalLength final length desired* @return a formatted string with the desired length* @throws Exception failed to format a string*/

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 589

5203ch14.qxd 8/12/05 12:24 PM Page 589

Page 619: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

public static String format(String str, int finalLength) throws Exception {if (str == null) {

return null;}

String result=null;if (finalLength <= str.length()){

result = str.substring(0, finalLength);return result;

}else {

StringBuilder buffer = new StringBuilder(str);for (int i = str.length(); i < finalLength; i++) {

// pad with spacesbuffer.append(" ");

}return buffer.toString();

}}

14-20. How Do You Format an Integer (int Data Type)?You can format an integer in two ways, as shown in the next sections.

First Solution: Using String ConcatenationThis solution will generate lots of unneeded String objects:

/*** Format an integer and return it with the desired length.* @param intData an integer input* @param finalLength final length desired* @return a formatted integer with the desired length* @throws Exception failed to format an integer*/public static String format(int intData, int finalLength) throws Exception {

String strData = String.valueOf(intData);String result=null;if (finalLength <= strData.length()) {result = strData.substring(0, finalLength);

}else {result = "";for (int i = 0; i < finalLength - strData.length(); i++) {result = result + " ";

}result = result + strData;

}return (result);

}

Second Solution: Using StringBuffer’s append()This solution is an efficient solution and will not generate lots of unneeded String objects:

CHAPTER 14 ■ EXPLORING JDBC UTILITIES590

5203ch14.qxd 8/12/05 12:24 PM Page 590

Page 620: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

/*** Format an integer and return it with the desired length.* @param intData an integer input* @param finalLength final length desired* @return a formatted integer with the desired length* @throws Exception failed to format an integer*/public static String format(int intData, int finalLength) throws Exception {

String strData = String.valueOf(intData);String result=null;if (finalLength <= strData.length()) {result = strData.substring(0, finalLength);return result;

}else {StringBuffer result = new StringBuffer("");for (int i = 0; i < finalLength - strData.length(); i++) {result.append(" ");

}result.append(strData);return (result.toString());

}}

14-21. How Do You Format an Integer Object?This shows how to format an Integer object:

/*** Format an Integer object to a desired length.* @param integerData an integer input* @param finalLength final length desired* @return a formatted integer with the desired length* @throws Exception failed to format an integer*/*public static String format(Integer integerData, int finalLength)

throws Exception {if (integerData == null) {return null;

}

return format(integerData.intValue(), finalLength);}

14-22. How Do You Format a Double Data Type?You can format a Double data type in two ways, as shown in the following sections.

First Solution: Using StringThis solution will generate lots of unneeded String objects:

/*** Format a double and return it with the desired precision and scale.* @param doubleData a double input* @param precision the desired precision

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 591

5203ch14.qxd 8/12/05 12:24 PM Page 591

Page 621: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

* @param scale the desired scale* @return a formatted double with the desired length* @throws Exception failed to format a double*/public static String format(double doubleData, int precision, int scale)throws Exception {java.math.BigDecimal bigDecimal = new java.math.BigDecimal(doubleData);bigDecimal = bigDecimal.setScale(scale, BigDecimal.ROUND_HALF_EVEN);String strData = big.toString();

// prepare the final stringint finalLength = precision + 1;String result=null;if (finalLen <= strData.length()) {result = strData.substring(0, finalLength);

}else {result = "";for (int i = 0; i < finalLen - strData.length(); i++){result = result + " ";

}result = result + strData;

}

return result;}

Second Solution: Using StringBufferThis solution is an efficient solution and will not generate lots of unneeded String objects:

/*** Format a double, and return it with the desired precision and scale.* @param doubleData a double input* @param precision the desired precision* @param scale the desired scale* @return a formatted double with the desired length* @throws Exception failed to format a double*/public static String format(double doubleData, int precision, int scale)

throws Exception {java.math.BigDecimal bigDecimal = new java.math.BigDecimal(doubleData);bigDecimal = bigDecimal.setScale(scale, BigDecimal.ROUND_HALF_EVEN);String strData = big.toString();

// prepare the final stringint finalLength = precision + 1;String result=null;if (finalLen <= strData.length()) {result = strData.substring(0, finalLength);return result;

}else {StringBuffer result = new StringBuffer("");for (int i = 0; i < finalLength - strData.length(); i++){result.append(" ");

}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES592

5203ch14.qxd 8/12/05 12:24 PM Page 592

Page 622: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

result.append(strData);return result.toString();

}}

14-23. How Do You Format a java.math.BigDecimal Object?You can format a java.math.BigDecimal object in two ways, as shown in the following sections.

First Solution: Using StringThis solution will generate lots of unneeded String objects:

/*** Format a double and return it with the desired precision and scale.* @param bigDecimal a java.math.BigDecimal object* @param precision the desired precision* @param scale the desired scale* @return a formatted "big decimal" with the desired length* @throws Exception failed to format a "big decimal"*/public static String format(java.math.BigDecimal bigDecimal,

int precision,int scale) throws Exception {

if (bigDecimal == null) {return null;

}

bigDecimal = bigDecimal.setScale(scale,java.math.BigDecimal.ROUND_HALF_EVEN);

String strData = bigDecimal.toString();

// prepare the final stringint finalLength = precision + 1;

// use a utility method defied earlier (code reuse)return format(strData, finalLength);

}

Second Solution: Use StringBufferThis solution is an efficient solution and will not generate lots of unneeded String objects:

/*** Format a double and return it with the desired precision and scale.* @param bigDecimal a java.math.BigDecimal object* @param precision the desired precision* @param scale the desired scale* @return a formatted "big decimal" with the desired length* @throws Exception failed to format a "big decimal"*/public static String format(java.math.BigDecimal bigDecimal,

int precision,int scale) throws Exception {

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 593

5203ch14.qxd 8/12/05 12:24 PM Page 593

Page 623: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

if (bigDecimal == null) {return null;

}

bigDecimal = bigDecimal.setScale(scale, java.math.BigDecimal.ROUND_HALF_EVEN);String strData = bigDecimal.toString();

// prepare the final stringint finalLength = precision + 1;

// use a utility method defined earlier (code reuse)return format(strData, finalLength);

}

14-24. How Do You Format a Double Object?This shows how to format a Double object:

/*** Format a double and return it with the desired precision and scale.* @param doubleData a Double object* @param precision the desired precision* @param scale the desired scale*/public static String format(Double doubleData, int precision, int scale)throws Exception {

if (doubleData == null) {return null;

}

return format(doubleData.doubleValue(), precision, scale);}

14-25. How Do You Build a URL in the Format Needed by theJDBC Drivers?The JDBC specification has a standard for database URLs, but each vendor has its own format. Ingeneral, each database has a unique URL format.

Viewing the JDBC URL Format for MySQLThe JDBC URL format for MySQL Connector/J is as follows, with items in square brackets ([, ])being optional (note that the JDBC URL is a single line):

jdbc:mysql://[host][,failoverhost...][:port]/[database][?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...

If the hostname is not specified, it defaults to 127.0.0.1. If the port is not specified, it defaults to3306, which is the default port number for MySQL servers.

Viewing the JDBC URL Format for OracleThe Oracle URL format is as follows:

jdbc:oracle:thin:@[hostName]:[portNumber]:[databaseName]

CHAPTER 14 ■ EXPLORING JDBC UTILITIES594

5203ch14.qxd 8/12/05 12:24 PM Page 594

Page 624: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

The following example connects the user scott with a password of tiger to a database withINSTANCE_NAME orcl through port number 1521 of host myhost by using the Thin driver:

String dbURL = "jdbc:oracle:thin:@myhost:1521:orcl";Connection conn = DriverManager.getConnection(dbURL, "scott", "tiger");

The following method, makeURL(), accepts required parameters and then builds a databaseURL for MySQL and Oracle:

// default port numberspublic static final int DEFAULT_PORT_NUMBER_MYSQL = 3306;public static final int DEFAULT_PORT_NUMBER_ORACLE = 1521;

// database vendorspublic static final String DATABASE_VENDOR_MYSQL = "mysql";public static final String DATABASE_VENDOR_ORACLE = "oracle";

/*** Build a URL in the format needed by the JDBC drivers.* @param hostname of the host* @param dbName name of the database* @param vendor name of the vendor* @return a URL in the format needed by the JDBC drivers.* @throws Exception failed to build a database URL.*/public static String makeURL(String host,

String dbName,String vendor) throws Exception {

if (vendor.equalsIgnoreCase(DATABASE_VENDOR_ORACLE)) {return("jdbc:oracle:thin:@" + host + ":"+

DEFAULT_PORT_NUMBER_ORACLE+":" + dbName);}else if (vendor.equalsIgnoreCase(DATABASE_VENDOR_MYSQL)) {

return("jdbc:mysql://" + host + ":"+DEFAULT_PORT_NUMBER_MYSQL+"/" + dbName);

}else {

throw new Exception("makeURL: database vendor undefined.")}

}

/*** Build a URL in the format needed by the JDBC drivers.* @param host name of the host* @param dbName name of the database* @param vendor name of the vendor* @param properties additional database URL properties* @return a URL in the format needed by the JDBC drivers.* @throws Exception failed to build a database URL.*/public static String makeURL(String host,

String dbName,String vendor,java.util.Map properties) throws Exception {

if ((properties == null) || (properties.size() == 0)) {return makeURL(host, dbName, vendor);

}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 595

5203ch14.qxd 8/12/05 12:24 PM Page 595

Page 625: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

if (vendor.equalsIgnoreCase(DATABASE_VENDOR_ORACLE)) {return("jdbc:oracle:thin:@" + host + ":"+

DEFAULT_PORT_NUMBER_ORACLE+":" + dbName);}else if (vendor.equalsIgnoreCase(DATABASE_VENDOR_MYSQL)) {

StringBuffer buffer = new StringBuffer("jdbc:mysql://");buffer.append(host);buffer.append(":");buffer.append(DEFAULT_PORT_NUMBER_MYSQL);buffer.append("/");buffer.append(dbName);Iterator iter = properties.iterator();while (iter.hasNext()) {

String propertyName = (String) iter.next();String propertyValue = (String) properties.get(propertyName);buffer.append("?");buffer.append(propertyName);buffer.append("=");buffer.append(propertyValue);

}return buffer.toString();

}else {

throw new Exception("makeURL: database vendor undefined.")}

}

/*** Build a URL in the format needed by the JDBC drivers.* @param host name of the host* @param dbName name of the database* @param vendor name of the vendor* @param port the port number* @return a URL in the format needed by the JDBC drivers.* @throws Exception failed to build a database URL.*/public static String makeURL(String host,

String dbName,String vendor,int port) throws Exception {

if (vendor.equalsIgnoreCase(DATABASE_VENDOR_ORACLE)) {return("jdbc:oracle:thin:@" + host + ":"+port+":" + dbName);

}else if (vendor.equalsIgnoreCase(DATABASE_VENDOR_MYSQL)) {

return("jdbc:mysql://" + host + ":"+port+"/" + dbName);}else {

throw new Exception("makeURL: database vendor undefined.")}

}

/*** Build a URL in the format needed by the JDBC drivers.* @param host name of the host* @param dbName name of the database

CHAPTER 14 ■ EXPLORING JDBC UTILITIES596

5203ch14.qxd 8/12/05 12:24 PM Page 596

Page 626: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

* @param vendor name of the vendor* @param port the port number* @param properties additional database URL properties* @return a URL in the format needed by the JDBC drivers.* @throws Exception failed to build a database URL.*/public static String makeURL(String host,

String dbName,String vendor,int port,java.util.Map properties) throws Exception {

if ((properties == null) || (properties.size() == 0)) {return makeURL(host, dbName, vendor, port);

}if (vendor.equalsIgnoreCase(DATABASE_VENDOR_ORACLE)) {

return("jdbc:oracle:thin:@" + host + ":"+port+":" + dbName);}else if (vendor.equalsIgnoreCase(DATABASE_VENDOR_MYSQL)) {

StringBuffer buffer = new StringBuffer("jdbc:mysql://");buffer.append(host);buffer.append(":");buffer.append(port);buffer.append("/");buffer.append(dbName);Iterator iter = properties.iterator();while (iter.hasNext()) {

String propertyName = (String) iter.next();String propertyValue = (String) properties.get(propertyName);buffer.append("?");buffer.append(propertyName);buffer.append("=");buffer.append(propertyValue);

}return buffer.toString();

}else {

throw new Exception("makeURL: database vendor undefined.")}

}

14-26. How Do You Get the Version Number of a JDBC Driver?DatabaseMetaData.getDriverVersion() retrieves the version number of a JDBC driver as a String:

public static String getDriverVersion(Connection conn)throws SQLException {

if ((conn == null) || (conn.isClosed())) {return null;

}

DatabaseMetaData meta = conn.getMetaData();if (meta == null) {

return null;}

return meta.getDriverVersion();}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 597

5203ch14.qxd 8/12/05 12:24 PM Page 597

Page 627: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

14-27. What Is a JDBC Utility Component (DbUtils)?DbUtils is a subproject of Apache Jakarta project. It is a small set of classes designed to make work-ing with JDBC easier. Writing JDBC resource cleanup code is a mundane, error-prone task, so theseclasses abstract all the cleanup tasks from your code. This leaves you with what you really want todo with JDBC in the first place: query and update data. DbUtils is available from the Apache SoftwareFoundation as a free download.

DbUtils PointersHere are some helpful DbUtils links:

• Home page: http://jakarta.apache.org/commons/dbutils/

• Java docs: http://jakarta.apache.org/commons/dbutils/apidocs/index.html

• Examples: http://jakarta.apache.org/commons/dbutils/examples.html

DbUtils Design GoalsDbUtils is designed to be the following:

• Small: You should be able to understand the whole package in a short amount of time.

• Transparent: DbUtils doesn’t do any magic behind the scenes. You give it a query, and then itexecutes the query and cleans up for you.

• Fast: You don’t need to create a million temporary objects to work with DbUtils.

DbUtils Package ComponentsDbUtils (DbUtils 1.1-dev) has three packages:

• org.apache.commons.dbutils

• org.apache.commons.dbutils.handlers

• org.apache.commons.dbutils.wrappers

DbUtils Core Classes/InterfacesThe core classes/interfaces in DbUtils are QueryRunner and ResultSetHandler. You don’t need toknow about any other DbUtils classes to benefit from using the library. The following exampledemonstrates how you use these classes together.

• org.apache.commons.dbutils.DbUtils: DbUtils is a class that provides utility methods to doroutine tasks such as closing connections and loading JDBC drivers. DbUtils is collection ofJDBC helper methods. This class is thread-safe, and all the methods are static.

• org.apache.commons.dbutils.QueryRunner: This class executes SQL queries with pluggablestrategies for handling ResultSet objects. This class is thread-safe.

• org.apache.commons.dbutils.ResultSetHandler: Implementations of this interface convertResultSet objects into other objects.

Using the DbUtils Package (Example 1)In these examples, you will use some of the core classes and interfaces from the DbUtils package.You will issue a query to read the entire animals_table table and convert every row to a JavaBeanobject (called AnimalBean).

CHAPTER 14 ■ EXPLORING JDBC UTILITIES598

5203ch14.qxd 8/12/05 12:24 PM Page 598

Page 628: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Step 1: Preparing the Database (Example 1)

For these examples, you will use a table called animals_table (which is a simple table with twocolumns):

mysql> use octopus;Database changedmysql> desc animals_table;+-------+-------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+----------------+| id | int(11) | | PRI | NULL | auto_increment || name | varchar(10) | | | | |+-------+-------------+------+-----+---------+----------------+2 rows in set (0.00 sec)

mysql> select * from animals_table;+-----+--------+| id | name |+-----+--------+| 111 | ginger || 222 | lola || 333 | freddy |+-----+--------+3 rows in set (0.01 sec)

Step 2: Preparing the JavaBean (Example 1)

Suppose you want to convert the ResultSet into a java.util.List of a specific bean class. Here youspecify the bean class to be AnimalBean, as shown next. The DbUtils package’s ResultSetHandlerclass converts a ResultSet into a java.util.List of beans.

public class AnimalBean {

private int id;private String name;

public AnimalBean() {}

public void setName(String name) {this.name = name;

}

public String getName() {return this.name;

}

public void setId(int id) {this.id = id;

}

public int getId() {return this.id;

}}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 599

5203ch14.qxd 8/12/05 12:24 PM Page 599

Page 629: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Step 3: Using BeanListHandler (Example 1)

This shows how to use BeanListHandler:

1 import org.apache.commons.dbutils.DbUtils;2 import org.apache.commons.dbutils.QueryRunner;3 import org.apache.commons.dbutils.handlers.BeanListHandler;45 import java.sql.Connection;6 import java.sql.DriverManager;7 import java.sql.SQLException;89 import java.util.List;1011 public class DbUtils_UseBean_MySQL {1213 public static void main(String[] args) {1415 Connection conn = null;16 String jdbcURL = "jdbc:mysql://localhost/octopus";17 String jdbcDriver = "com.mysql.jdbc.Driver";18 String user = "root";19 String password = "root";2021 try {22 DbUtils.loadDriver(jdbcDriver);23 conn = DriverManager.getConnection(jdbcURL, user, password);2425 QueryRunner qRunner = new QueryRunner();26 System.out.println("MySQL: begin using BeanListHandler...");2728 List beans = (List) qRunner.query(conn,29 "select id, name from animals_table",30 new BeanListHandler(AnimalBean.class));3132 for (int i = 0; i < beans.size(); i++) {33 AnimalBean bean = (AnimalBean) beans.get(i);34 bean.print();35 }36 System.out.println("DbUtils_UseBean_MySQL: end.");3738 }39 catch (SQLException e) {40 // handle the exception41 e.printStackTrace();42 }43 finally {44 DbUtils.closeQuietly(conn);45 }46 }47 }

Step 4: Running the Test Program (Example 1)

Before you run the test program, you have to add the MySQL driver’s .jar file and the DbUtils pack-age’s .jar file to your CLASSPATH, as shown here:

CHAPTER 14 ■ EXPLORING JDBC UTILITIES600

5203ch14.qxd 8/12/05 12:24 PM Page 600

Page 630: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

$ javac DbUtils_UseBean_MySQL.java$ java DbUtils_UseBean_MySQLbegin using BeanListHandler...id=111 name=gingerid=222 name=lolaid=333 name=freddyDbUtils_UseBean_MySQL: end.

Discussing DbUtils_UseBean_MySQL (Example 1)

This breaks down the program:

• Lines 1–9: Import the required Java classes and interfaces.

• Lines 15–23: Load the JDBC driver class, and get a database Connection object using theDriverManager class.

• Lines 25–25: Instantiate the QueryRunner class. (QueryRunner executes SQL queries with plug-gable strategies for handling ResultSet objects. This class is thread-safe.)

• Lines 28–30: Here you use the QueryRunner to execute a SQL query. You pass the BeanListHandlerobject (as a parameter), which is a ResultSetHandler that can convert the ResultSet intoa List of a specific bean. (The bean class will be AnimalBean.)

• Lines 32–35: You iterate through the List of beans retrieved and invoke AnimalBean.print()on each bean.

Using the DbUtils Package (Example 2)In the following examples, you will use some of the core classes and interfaces from the DbUtilspackage. You will issue a query to read the entire animals_table table and convert every row toa java.util.Map object.

Step 1: Preparing the Database (Example 2)

Use a table called animals_table (which is a simple table with two columns):

mysql> use octopus;Database changedmysql> desc animals_table;+-------+-------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+----------------+| id | int(11) | | PRI | NULL | auto_increment || name | varchar(10) | | | | |+-------+-------------+------+-----+---------+----------------+2 rows in set (0.00 sec)

mysql> select * from animals_table;+-----+--------+| id | name |+-----+--------+| 111 | ginger || 222 | lola || 333 | freddy |+-----+--------+3 rows in set (0.01 sec)

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 601

5203ch14.qxd 8/12/05 12:24 PM Page 601

Page 631: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Step 2: Using MapListHandler (Example 2)

The MapListHandler class implements ResultSetHandler, which is defined as follows (for details, seehttp://jakarta.apache.org/commons/dbutils/):

package org.apache.commons.dbutils;public interface ResultSetHandler {

// Implementations of this interface convert// ResultSets into other objects....

}

package org.apache.commons.dbutils.handlers;public class MapListHandler

extends java.lang.Objectimplements ResultSetHandler {// ResultSetHandler implementation that converts a ResultSet// object into a List of Maps. This class is thread-safe....

}

The following example show how to use a MapListHandler object:

1 import org.apache.commons.dbutils.DbUtils;2 import org.apache.commons.dbutils.QueryRunner;3 import org.apache.commons.dbutils.handlers.MapListHandler;45 import java.sql.Connection;6 import java.sql.DriverManager;7 import java.sql.SQLException;89 import java.util.Map;10 import java.util.List;1112 public class DbUtils_UseMap_MySQL {1314 public static void main(String[] args) {1516 Connection conn = null;17 String jdbcURL = "jdbc:mysql://localhost/octopus";18 String jdbcDriver = "com.mysql.jdbc.Driver";19 String user = "root";20 String password = "root";2122 try {23 DbUtils.loadDriver(jdbcDriver);24 conn = DriverManager.getConnection(jdbcURL, user, password);2526 QueryRunner qRunner = new QueryRunner();27 System.out.println("begin using MapListHandler...");2829 List mapList = (List) qRunner.query(conn,30 "select id, name from animals_table",31 new MapListHandler());3233 for (int i = 0; i < mapList.size(); i++) {34 Map map = (Map) mapList.get(i);35 System.out.println("id=" + map.get("id"));

CHAPTER 14 ■ EXPLORING JDBC UTILITIES602

5203ch14.qxd 8/12/05 12:24 PM Page 602

Page 632: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

36 System.out.println("name=" + map.get("name"));37 System.out.println("-----------------");38 }3940 System.out.println("DbUtils_UseMap_MySQL: end.");4142 }43 catch (SQLException e) {44 // handle the exception45 e.printStackTrace();46 }47 finally {48 DbUtils.closeQuietly(conn);49 }50 }51 }

Step 3: Running the Test Program (Example 2)

Before you run the test program, you have to add the MySQL driver’s .jar file and the DbUtils pack-age’s .jar file to your CLASSPATH, as shown here:

$ javac DbUtils_UseMap_MySQL.java$ java DbUtils_UseMap_MySQLbegin using MapListHandler...id=111name=ginger-----------------id=222name=lola-----------------id=333name=freddy-----------------DbUtils_UseMap_MySQL: end.

Discussing DbUtils_UseMap_MySQL (Example 2)

This breaks down the program:

• Lines 1–10: Import the required Java classes and interfaces.

• Lines 16–24: Load the JDBC driver class, and get a database Connection object using theDriverManager class.

• Lines 25–25: Instantiate the QueryRunner class. (QueryRunner executes SQL queries with plug-gable strategies for handling ResultSet objects. This class is thread-safe.)

• Lines 29–31: Here you use the QueryRunner to execute a SQL query. You pass theMapListHandler object (as a parameter), which is a ResultSetHandler that can convert theResultSet into a List of Map objects (each result row is represented as a java.util.Map object).

• Lines 33–38: You iterate through the List of Map objects retrieved.

DbUtils Generic Example from the Apache Software FoundationThe following examples are from the Web site at http://jakarta.apache.org/commons/dbutils/examples.html:

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 603

5203ch14.qxd 8/12/05 12:24 PM Page 603

Page 633: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

// Create a ResultSetHandler implementation to// convert the first row into an Object[].ResultSetHandler h = new ResultSetHandler() {

public Object handle(ResultSet rs) throws SQLException {if (!rs.next()) {

return null;}

ResultSetMetaData meta = rs.getMetaData();int cols = meta.getColumnCount();Object[] result = new Object[cols];

for (int i = 0; i < cols; i++) {result[i] = rs.getObject(i + 1);

}

return result;}

};

// Create a QueryRunner that will use connections// from the given DataSourceQueryRunner run = new QueryRunner(dataSource);

// Execute the query and get the results back from the handlerObject[] result = (Object[]) run.query(

"SELECT * FROM Person WHERE name=?", "John Doe", h);

You could also perform the previous query using a java.sql.Connection object instead ofa DataSource. Notice that you are responsible for closing the Connection in this example.

ResultSetHandler h = ... // Define a handler the same as the previous example

// No DataSource, so you must handle Connections manuallyQueryRunner run = new QueryRunner();

Connection conn = ... // open a connectiontry{

Object[] result = (Object[]) run.query(conn, "SELECT * FROM Person WHERE name=?", "John Doe", h);

// do something with the result

}finally {

// Use this helper method, so you do not have to check for nullDbUtils.close(conn);

}

In the previous examples, you implemented the ResultSetHandler interface to turn the first rowof the ResultSet into an Object[]. This is a fairly generic implementation that can be reused acrossmany projects. In recognition of this, DbUtils provides a set of ResultSetHandler implementationsin the org.apache.commons.dbutils.handlers package that perform common transformations intoarrays, Map objects, and JavaBeans. Each implementation has a version that converts just the firstrow and another that converts all rows in the ResultSet.

QueryRunner run = new QueryRunner(dataSource);

CHAPTER 14 ■ EXPLORING JDBC UTILITIES604

5203ch14.qxd 8/12/05 12:24 PM Page 604

Page 634: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

14-28. How Do You Debug/Display a SQLException Object?Sometimes during application development, you might get a SQLException that you are not familiarwith. In this case, you can use the following method to get some detailed information:

/*** This method checks for sql exceptions and displays* error information; note that multiple exception* objects could be chained together.* @param e The SQLException object.*/public static printSQLExceptions(SQLException e) {

if (e == null) {System.out.println("printSQLExceptions: exception is null.");return;

}

System.out.println("--- printSQLExceptions: SQLException caught ---");while (e != null) {

System.out.println("printSQLExceptions: SQLState: " + e.getSQLState());System.out.println("printSQLExceptions: Message: " + e.getMessage());System.out.println("printSQLExceptions: Vendor: " + e.getErrorCode());System.out.println("");e = e.getNextException();

}}

/*** This method checks for sql exceptions and displays* error information; note that multiple exception* objects could be chained together.* @param methodName The name of a method.* @param e The SQLException object.*/public static printSQLExceptions(SQLException e, String methodName) {

if (e == null) {System.out.println("printSQLExceptions: exception is null.");return;

}

System.out.println("--- SQLException caught in method "+methodName);while (e != null) {

System.out.println("printSQLExceptions: SQLState: " + e.getSQLState());System.out.println("printSQLExceptions: Message: " + e.getMessage());System.out.println("printSQLExceptions: Vendor: " + e.getErrorCode());System.out.println("");e = e.getNextException();

}}

14-29. How Do You Debug/Display a SQLWarning Object?Sometimes during application development, you might get a SQLWarning that you are not familiarwith. In that case, you can use the following method to get some detailed information:

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 605

5203ch14.qxd 8/12/05 12:24 PM Page 605

Page 635: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

/*** This method checks for sql warnings and displays* error information; note that multiple exception* objects could be chained together.* @param w The SQLWarning object.*/public static printSQLWarnings(SQLWarning w) {

if (w == null) {System.out.println("printSQLWarnings: warning is null.");return;

}

System.out.println("--- printSQLWarnings: Warning ---");while (w != null) {

System.out.println("printSQLWarnings: SQLState: " + w.getSQLState());System.out.println("printSQLWarnings: Message: " + w.getMessage());System.out.println("printSQLWarnings: Vendor: " + w.getErrorCode());System.out.println("");w = w.getNextWarning();

}}

14-30. How Do You Debug/Display a ResultSet Object?When debugging ResultSet objects, you may use the following two methods to print the content ofthe ResultSet object. Note that these methods will not work for some data types, such as BLOB.

displayResultSet()This is displayResultSet():

/*** This method displays the content of a given ResultSet object,* which contains all rows and columns in the given result set.* @param rs The result set to be displayed/debugged.* @return None.* @exception SQLException, failed to display the result set object.*/public static void displayResultSet(ResultSet rs)throws SQLException {if (rs == null) {

System.out.print("displayResultSet: result set is null.");return;

}

// get the ResultSetMetaData, which will// be used for the column headingsResultSetMetaData metaData = rs.getMetaData();if (metaData == null) {

System.out.print("displayResultSet: metadata for result set is null.");return;

}

// get the number of columns for the given ResultSet objectint numberOfColumns = metaData.getColumnCount();

CHAPTER 14 ■ EXPLORING JDBC UTILITIES606

5203ch14.qxd 8/12/05 12:24 PM Page 606

Page 636: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

// display column headingsfor (int i = 1; i <= numberOfColumns; i++) {

if (i > 1) {System.out.print(",");

}System.out.print(metaData.getColumnLabel(i));

}System.out.println("\n-------------------------------------");

//// display data, iterate the ResultSet object,// fetching until end of the result set//while (rs.next()) {

// loop through each column, getting// the column data and displayingfor (int i = 1; i <= numberOfColumns; i++) {

if (i > 1) {System.out.print(",");

}System.out.print(rs.getString(i));

}System.out.println("");

}}

resultSetAsTable()This is resultSetAsTable():

/*** Provide a readable view of a JDBC ResultSet object.** Here is sample output, showing two data rows and the column names.** [* [column-name-1, column-name-2, ...],* [data-1, data-2, ...],* [data-1, data-2, ...]* ...* ]** @param rs The result set to be displayed/debugged.* @return A string representation that looks like a List of Lists.* @throws SQLException failed to display result set object.*/public static java.util.List resultSetAsTable(ResultSet rs)

throws SQLException {if (rs == null) {

return null;}

java.util.List rows = new java.util.ArrayList();ResultSetMetaData rsMetaData = rs.getMetaData();if (rsMetaData == null) {

// JDBC driver does not support metadatareturn null;

}

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 607

5203ch14.qxd 8/12/05 12:24 PM Page 607

Page 637: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

int columnCount = rsMetaData.getColumnCount();java.util.List columnNames = new java.util.ArrayList();

// add column namesfor (int i = 1; i <= columnCount; i++) {

String columnName = rsMetaData.getColumnName(i);columnNames.add(columnName);

}

rows.add(columnNames);

// add actual datawhile (rs.next()) {

java.util.List rowData = new java.util.ArrayList();for (int i = 1; i <= columnCount; i++) {

Object columnData = rs.getObject(i);rowData.add(columnData);

}rows.add(rowData);

}

return rows;}

14-31. What Is the Best Way to Generate a Random GUID?In many database applications, you do need to create globally unique identifier (GUID) primarykeys. For example, you might define a table such as this:

create table employee_table (internal_id varchar(32),...

);

where internal_id is a GUID. But how do you create these GUIDs at runtime (when you want tocreate a new record of employee_table)? In the multitude of Java GUID generators, I found one thatguarantees the randomness of the GUID generated. The GUID generator is from http://www.JavaExchange.com and is open-source.

Producing a Random GUID GeneratorThis shows how to code the random GUID generator:

/** RandomGUID from http://www.javaexchange.com/aboutRandomGUID.html* @version 1.2.1 11/05/02* @author Marc A. Mnich** From www.JavaExchange.com, Open Software licensing** 11/05/02 -- Performance enhancement from Mike Dubman.* Moved InetAddr.getLocal to static block. Mike has measured* a tenfold improvement in runtime.* 01/29/02 -- Bug fix: Improper seeding of nonsecure Random object* caused duplicate GUIDs to be produced. Random object* is now created only once per JVM.* 01/19/02 -- Modified random seeding and added new constructor

CHAPTER 14 ■ EXPLORING JDBC UTILITIES608

5203ch14.qxd 8/12/05 12:24 PM Page 608

Page 638: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

* to allow secure random feature.* 01/14/02 -- Added random function seeding with JVM runtime** NOTE:** 06/24/2004 -- adopted by Mahmoud Parsian.*/

import java.net.InetAddress;import java.net.UnknownHostException;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.security.SecureRandom;import java.util.Random;/** In the multitude of java GUID generators, I found none that* guaranteed randomness. GUIDs are guaranteed to be globally unique* by using Ethernet MACs, IP addresses, time elements, and sequential* numbers. GUIDs are not expected to be random and most often are* easy/possible to guess given a sample from a given generator.* SQL Server, for example, generates GUID that are unique but* sequential within a given instance.** GUIDs can be used as security devices to hide things such as* files within a file system where listings are unavailable (e.g., files* that are served up from a Web server with indexing turned off).* This may be desirable in cases where standard authentication is not* appropriate. In this scenario, the RandomGUIDs are used as directories.* Another example is using GUIDs for primary keys in a database* where you want to ensure that the keys are secret. Random GUIDs can* then be used in a URL to prevent hackers (or users) from accessing* records by guessing or simply by incrementing sequential numbers.** There are many other possibilities of using GUIDs in the realm of* security and encryption where the element of randomness is important.* This class was written for these purposes but can also be used as a* general-purpose GUID generator as well.** RandomGUID generates truly random GUIDs by using the system's* IP address (name/IP), system time in milliseconds (as an integer),* and a very large random number joined together in a single String* that is passed through an MD5 hash. The IP address and system time* make the MD5 seed globally unique, and the random number guarantees* that the generated GUIDs will have no discernable pattern and* cannot be guessed given any number of previously generated GUIDs.* It is generally not possible to access the seed information (IP, time,* random number) from the resulting GUIDs, as the MD5 hash algorithm* provides one-way encryption.** ----> Security of RandomGUID: <-----* RandomGUID can be called one of two ways -- with the basic java Random* number generator or a cryptographically strong random generator* (SecureRandom). The choice is offered because the secure random* generator takes about 3.5 times longer to generate its random numbers,* and this performance hit may not be worth the added security* especially considering the basic generator is seeded with a* cryptographically strong random seed.

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 609

5203ch14.qxd 8/12/05 12:24 PM Page 609

Page 639: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

* Seeding the basic generator in this way effectively decouples* the random numbers from the time component, making it virtually impossible* to predict the random number component even if one had absolute knowledge* of the System time. Thanks to Ashutosh Narhari for the suggestion* of using the static method to prime the basic random generator.** Using the secure random option, this class complies with the statistical* random number generator tests specified in FIPS 140-2, Security* Requirements for Cryptographic Modules, section 4.9.1.** I converted all the pieces of the seed to a String before handing* it over to the MD5 hash so that you could print it out to make* sure it contains the data you expect to see and to give a nice* warm fuzzy. If you need better performance, you may want to stick* to byte[] arrays.** I believe that it is important that the algorithm for* generating random GUIDs be open for inspection and modification.* This class is free for all uses.*** - Marc*/public class RandomGUID extends Object {

public String valueBeforeMD5 = "";public String valueAfterMD5 = "";private static Random myRand;private static SecureRandom mySecureRand;

private static String s_id;

/** Static block to take care of one time secureRandom seed.* It takes a few seconds to initialize SecureRandom. You might* want to consider removing this static block or replacing* it with a "time since first loaded" seed to reduce this time.* This block will run only once per JVM instance.*/

static {mySecureRand = new SecureRandom();long secureInitializer = mySecureRand.nextLong();myRand = new Random(secureInitializer);try {

s_id = InetAddress.getLocalHost().toString();} catch (UnknownHostException e) {

e.printStackTrace();}

}

/** Default constructor. With no specification of security option,* this constructor defaults to lower security, higher performance.*/

CHAPTER 14 ■ EXPLORING JDBC UTILITIES610

5203ch14.qxd 8/12/05 12:24 PM Page 610

Page 640: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

public RandomGUID() {getRandomGUID(false);

}

/** Constructor with security option. Setting secure true* enables each random number generated to be cryptographically* strong. Secure false defaults to the standard Random function seeded* with a single cryptographically strong random number.*/public RandomGUID(boolean secure) {

getRandomGUID(secure);}

/** Method to generate the random GUID*/private void getRandomGUID(boolean secure) {

MessageDigest md5 = null;StringBuffer sbValueBeforeMD5 = new StringBuffer();

try {md5 = MessageDigest.getInstance("MD5");

} catch (NoSuchAlgorithmException e) {System.out.println("Error: " + e);

}

try {long time = System.currentTimeMillis();long rand = 0;

if (secure) {rand = mySecureRand.nextLong();

} else {rand = myRand.nextLong();

}

// This StringBuffer can be as long as you need; the MD5// hash will always return 128 bits. You can change// the seed to include anything you want here.// You could even stream a file through the MD5, making// the odds of guessing it at least as great as that// of guessing the contents of the file!sbValueBeforeMD5.append(s_id);sbValueBeforeMD5.append(":");sbValueBeforeMD5.append(Long.toString(time));sbValueBeforeMD5.append(":");sbValueBeforeMD5.append(Long.toString(rand));

valueBeforeMD5 = sbValueBeforeMD5.toString();md5.update(valueBeforeMD5.getBytes());

byte[] array = md5.digest();StringBuffer sb = new StringBuffer();for (int j = 0; j < array.length; ++j) {

int b = array[j] & 0xFF;if (b < 0x10) sb.append('0');

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 611

5203ch14.qxd 8/12/05 12:24 PM Page 611

Page 641: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

sb.append(Integer.toHexString(b));}

valueAfterMD5 = sb.toString();

} catch (Exception e) {System.out.println("Error:" + e);

}}

/** Convert to the standard format for GUID* (Useful for SQL Server UniqueIdentifiers, etc.)* Example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6*/public String toString() {

String raw = valueAfterMD5.toUpperCase();StringBuffer sb = new StringBuffer();sb.append(raw.substring(0, 8));sb.append("-");sb.append(raw.substring(8, 12));sb.append("-");sb.append(raw.substring(12, 16));sb.append("-");sb.append(raw.substring(16, 20));sb.append("-");sb.append(raw.substring(20));

return sb.toString();}

/*** Return a random GUID.*/public static String getGUID() {

RandomGUID myGUID = new RandomGUID();return myGUID.valueAfterMD5;

}

/** For Debugging purposes only.* Demonstraton and self-test of class.*/public static void main(String args[]) {

// 64A4DD34-78C2-FB3C-7075-5198CB9C7868// 64a4dd3478c2fb3c70755198cb9c7868long start = System.currentTimeMillis();for (int i=0; i< 10; i++) {

RandomGUID myGUID = new RandomGUID();//System.out.println("Seeding String=" + myGUID.valueBeforeMD5);System.out.println("rawGUID=" + myGUID.valueAfterMD5);//System.out.println("RandomGUID=" + myGUID.toString());

}long end = System.currentTimeMillis();long time = end - start;System.out.println("time="+time);

CHAPTER 14 ■ EXPLORING JDBC UTILITIES612

5203ch14.qxd 8/12/05 12:24 PM Page 612

Page 642: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

//String oneMoreGUID = RandomGUID.getGUID();//System.out.println("oneMoreGUID=" + oneMoreGUID);

}}

Testing the Random GUID GeneratorThis tests the random GUID generator:

$ javac RandomGUID.java$ java RandomGUIDrawGUID=bdc8f9750d2c7dd06670082019a6d7fdrawGUID=a5ad65c353abc36a28e689465ac7c1c4rawGUID=c7be28ef811efa14704f0f7bfd455e22rawGUID=1e2ddf6453ac6ce65333cc52b8dc9910rawGUID=36a7903c45fabb3f1ce398effc102594rawGUID=4aec56173827af462050e8f88979ce3brawGUID=fc0c91bddd4ef325d5a842fd10a1e2fdrawGUID=60ae4f6d558d3d9db5a01f7e618c67dcrawGUID=c668c167b6d79e14d0e36374b524b736rawGUID=d6141ddb58b96a75d94ceac49bf98b51time=20

As you can see, RandomGUID is fast: it created ten GUIDs in twenty milliseconds.

CHAPTER 14 ■ EXPLORING JDBC UTILITIES 613

5203ch14.qxd 8/12/05 12:24 PM Page 613

Page 643: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

5203ch14.qxd 8/12/05 12:24 PM Page 614

Page 644: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

& (ampersand), specifying connection properties,121

? (question mark), specifying connectionproperties, 121

■Aabsolute(int) method, scrollable ResultSets, 215Access URL formats, 94ACID (atomicity, consistency, isolation, and

durability), 47addBatch(String sql) method (Statement object),

403, 408afterLast( ) method, scrollable ResultSets, 210, 215ALTER command, 14ampersand (&), specifying connection properties,

121ANSI (American National Standards Institute), 13Apache

DbUtils package, 598–604core classes/interfaces, 598design goals, 598example usage, 598–604package components, 598Web sites, 598, 602–603

Avalon/Excalibur, 51connection pool, 55datasource, 55

Commons Pool project, 55DBCP component, 51

API, 26debugging, 75–76environment settings, 73

application component (ODBC), 7application server tier (three-tier model), 84application performance improvement, 51–63

avoiding generic search patterns, 57avoiding memory leaks, 61–63caching PreparedStatement objects, 61driver selection, 52managing database Connection objects, 53–57minimizing database metadata methods usage,

52retrieving only required data, 58–61simplifying SQL queries, 52

architectureBCM (Basic Connection Management), 108JDBC

API, 26detailed architecture, 3–5high-level architecture, 3

JDBC-ODBC bridge, 8ODBC architecture, 6–7

ArrayDescriptor, 497arrays

materializing BLOB data, 235passing to PreparedStatement, 495–498

byte arrays, 526–530Oracle, 496–498

AsciiStream, 498. See also InputStreamgetAsciiStream( ) method (Java.sql.Clob

interface), 258setAsciiStream(long pos) method (Java.sql.Clob

interface), 258atomic, 46atomicity, consistency, isolation, and durability

(ACID), 47attributes, user-defined types (UDTs), 134autocommit mode, 48, 88

autocommit rules, 134choosing optimal transaction isolation levels, 57Connection object, 132–134disabling autocommits, 118–119, 133enabling autocommits, 133Oracle transaction support, 78starting/ending transactions, 48–49turning on/off, 48

autoReconnect parameter (Connection object),130–131

AUTO_INCREMENT (Statement), 388–391Avalon, 51

connection pool, 55datasource, 55

■BBasic Connection Management (BCM), 108–113

accessingmethod 1, 109–110method 2, 110–112

architecture, 108connection factories, 113ConnectionManager, 112package features, 112using with your own values, 109

BasicConnectionManager classMySQL, 101–102Oracle, 98–100

batch statements, executing with Statement,403–408

mapping trees into SQL, 415–430deleting existing nodes, 420–427deleting existing subtrees, 427–430inserting new nodes, 416–420

MySQL, 407–408Oracle, 405–407

Index

615

5203chIDX.qxd 8/12/05 4:03 PM Page 615

Page 645: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

batch updates, 59–61. See alsoBatchUpdateException; batch statements

JDBC batch update support, 414–415BatchUpdateException, 43, 341, 364–373

constructors, 365method, 365MySQL, 368, 372Oracle, 369, 373

BCM (Basic Connection Management), 108–113accessing

method 1, 109–110method 2, 110–112

architecture, 108connection factories, 113ConnectionManager, 112package features, 112using with your own values, 109

BDB tables, 77BEA WebLogic Server, 84beforeFirst( ) method, scrollable ResultSets, 215BigDecimal

formatting, 593–594passing to PreparedStatement, 502–505

MySQL, 504–505Oracle, 502–504

binary dataBinary Large OBjects. See BLOBsgetting, using Statement, 402inserting, using Statement, 401–402

Binary Large OBjects. See BLOBsbinary streams, passing to PreparedStatement,

506–512MySQL, 511–512Oracle, 506–511

BLOBs (Binary Large OBjects), 194, 231–233byte[ ] vs. java.sql.Blob, 256creating objects, 234–235deleting

MySQL, 245–246Oracle, 243–245

java.sql.Blob interface, 231–233materializing data, 235–236MySQL BLOBs, 233Oracle BLOBs, 233passing to PreparedStatement, 513–518

MySQL, 517–518Oracle, 514–517

restrictions, 234retrieving BLOB data types, 240–243serializing objects

MySQL, 252–256Oracle, 246–252

tablescreating, 233–234defining BLOB data types, 233getting BLOB data, 194–198inserting records, 236–237, 240

BlobSelect( ), 243books about JDBC topics, 74boolean values, passing to PreparedStatement,

519–522MySQL, 521–522Oracle, 520–521

bootstrap process, 18borrowing Connection objects from connection

pool managers, 131–132bridges, 6

JDBC-ODBC bridge, 8–13architecture, 8debugging, 13JDBC 2.0 API features supported, 13packages, 8, 10–13using, 9–13

byte arraysconverting input streams to byte arrays,

586–587materializing BLOB data, 235passing to PreparedStatement, 526–530

MySQL, 529–530Oracle, 528–529

byte data type, passing to PreparedStatement,522–526

MySQL, 525–526Oracle, 524–525

byte[ ] versus java.sql.Blob, 256

■Ccaching PreparedStatement objects, 61Calendar. See also Date

conversion of Java Date classes to Calendar,327

creating java.util.Date objects, 315getting dates for specific time zones, 340making java.sql.Timestamp objects, 339–340

CallableStatement object, 27batch updates, 60–61

cancelRowUpdates( ) method, updatableResultSets, 221–222

catch blocks, 43catch clauses, 343–344certification program for JDBC drivers, 82–83chaining (SQLException), 350–351Character Large OBject. See CLOBscharacter streams, passing to PreparedStatement,

530–534MySQL, 533–534Oracle, 532–533

checked vs. unchecked exceptions, 346CheckJDBCInstallation, 148–151Class.forName( ), loading JDBC drivers, 29classes

BasicConnectionManagerMySQL, 101–102Oracle, 98–100

ConnectionManager, 108–109core JDBC classes/interfaces, 26–27DbUtils core classes/interfaces, 598DriverManager

creating connections, 91–95function of, 90getConnection( ) method, 91getDrivers( ) method, 95–97

DriverPropertyInfo, 103–106java.lang.Error, 347java.lang.Exception, 341–344, 347java.lang.Throwable, 347

■INDEX616

5203chIDX.qxd 8/12/05 4:03 PM Page 616

Page 646: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

java.sql.BatchUpdateException, 341, 364–373constructors, 365method, 365MySQL, 368, 372Oracle, 369, 373

java.sql.DataTruncation, 341, 373–375constructor, 375creating/using DataTruncation objects,

375–379determining whether DataTruncation has

occurred, 374methods, 375using with ResultSet, 379

java.sql.Date. See Datejava.sql.SQLWarning, 341, 352–356. See also

warningsconstructors, 352creating/traversing warnings, 355–356determining whether warnings have

occurred, 353–355getting all instances, 353methods, 353

java.sql.Time. See Timejava.sql.Timestamp. See Timestampjava.text.SimpleDateFormat, 323java.util.Date. See DateJDBC detailed architecture, 5MapListHandler, 602–603MysqlConnectionFactory (BCM), 109, 112–113OdbcConnectionFactory (BCM), 109, 112OracleConnectionFactory (BCM), 109, 112–113singleton classes, 109SQLException, 341, 344–345

chaining, 350–351checked vs. unchecked exceptions, 346constructors, 345example usage, 348–349getting all instances using

printSQLExceptions( ), 351–352getting details, 349–350methods, 345relationship to other classes, 346–347

utilities. See utilitiesCLASSPATH environmental variable, 73client tier (three-tier model), 84CLOBs (Character Large OBjects), 199, 257–259

ClobSelectMySQL( ), 273creating objects, 261–262deleting, using servlets

MySQL, 303–305Oracle, 300–303

inserting, using servletsMySQL, 290–292Oracle, 285–290

java.lang.String vs. java.sql.Clob, 305materializing data, 262–265MySQL, 259

defining, 260Oracle, 259

defining, 259–260passing to PreparedStatement, 534–538

MySQL, 537–538Oracle, 535–537

restrictions for using, 261selecting/displaying CLOBs

JFrames, 270–274servlets, MySQL, 277–279, 284–285servlets, Oracle, 274–277, 280–283

tablescreating, 260defining CLOB data types, 259–260getting CLOB data, 199inserting records, 265–270

updating, using servletsMySQL, 297–300Oracle, 293–297

closingConnection objects, 25, 131–132, 569–575

closing ResultSet, Statement, andConnection together, 580–581

closing Statement and Connection together,579–580

committing and closing, 571–573logging exceptions, 570–574not reporting exceptions, 570–574reporting exceptions, 571–575rolling back and closing, 573–575soft closing using pool managers, 570

PreparedStatement objects, 24, 578–579resources, avoiding memory leaks, 61–63ResultSet objects, 23, 575–576

closing ResultSet, Statement, andConnection together, 580–581

Statement objects, 24, 576–578closing ResultSet, Statement, and

Connection together, 580–581closing Statement and Connection together,

579–580columns, 6. See also tables

CLOBs, restrictions for using, 261returning long columns, using utilities, 582–583

command lines, loading JDBC drivers usingSystem.properties( ), 32

commands, SQL commands, 13–14commit. See also Autocommit

COMMIT command, 14commit method (Connection object), 48controlling commit updates, 118–119handling commit( ) when exceptions occur, 44

committing and closing Connection objects,571–573

concurrency, (ResultSets), 180getting/setting, 181

connect( ) method (Driver interface), 106–108connecting

connecting to databasesMySQL, 100–102Oracle, 97–100SQL Server, 94

creating connections using DataSource,153–156

obtaining connections with JNDI, 166–167obtaining connections without JNDI, 165–166

driversMySQL JDBC drivers, 66–69Oracle JDBC drivers, 64–65

■INDEX 617

5203chIDX.qxd 8/12/05 4:03 PM Page 617

Page 647: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

existing databases, 17–18maintaining connections (autoReconnect

parameter, Connection object), 130–131maximum connections limits, 145

restricting max connections in MySQLdatabases, 146–147

restricting max connections in Oracledatabases, 145–146

multiple database connections, 124–125SYSDBA and SYSOPER connections (Oracle),

147URL syntax, 38–39

Connection object, 88application optimization

choosing optimal transaction isolationlevels, 56–57

controlling transactions, 55–56setting optimal Connection properties, 53using connection pools, 54

autocommit mode, 48, 88, 132–134autocommit rules, 134disabling autocommits, 118–119, 133enabling autocommits, 133

autoReconnect parameter, 130–131Basic Connection Management (BCM),

108–113accessing, method 1, 109–110accessing, method 2, 110–112architecture, 108ConnectionManager, 112MysqlConnectionFactory, 113OracleConnectionFactory, 113package features, 112using with your own values, 109

borrowing Connection objects from connectionpool managers, 131–132

checking for SQL warnings, 119–120, 354closing, 25, 131–132, 569–575

closing ResultSet, Statement, andConnection together, 580–581

closing Statement and Connection together,579–580

committing and closing, 571–573logging exceptions, 570–574not reporting exceptions, 570–574reporting exceptions, 571–575rolling back and closing, 573–575soft closing using pool managers, 570

commit method, 48connecting to databases

MySQL, 100–102Oracle, 97–100SQL Server, 94

connection pooling. See connection poolingcontrolling commit and rollback updates,

118–119creating connections using DataSource,

153–156obtaining connections with JNDI, 166–167obtaining connections without JNDI,

165–166creating, using Driver interface, 106–108

creating, using DriverManager class, 91–95loading drivers, 91–92making connections, method 1, 92–94making connections, method 2, 94–95making connections, method 3, 95

determining the driver that created aconnection, 117–118

determining whether databases accepttransactions, 113–115

DriverManager classcreating connections, 91–95function of, 90getting list of loaded drivers, 95–97

five ways to create Connection objects, 89–90function of DriverManager class, 90limiting rows returned from SQL servers,

115–117listing available parameters for creating

connections, 103–106managing connection objects for application

optimization, 53–57optimal connection properties, 53using connection pools, 54–55

MySQL connection propertiespassing using database URLs, 122passing using java.util.Properties, 121–122passing using java.util.Properties and

database URLs, 122–123preventing timeouts using autoReconnect,

130–131Oracle connection properties

connecting as SYSDBA and SYSOPER, 147passing using DriverManager

getConnection( ) method, 123–124relationship of Connection to other objects,

88–89ResultSet holdability

getting, 183setting, 182

rollback method, 48testing connection validity, 125–126, 129type mapping. See type mapping

connection pool managers, 1connection pooling, 1, 49–51

application optimization, 54–55pool managers, 49

borrowing Connection objects from, 131–132soft closing Connection, 570

Connection.createStatement( ), 383ConnectionManager class, 108–109Connector/J, JDBC URL format, 594–597constructors

BatchUpdateException, 365java.sql.DataTruncation, 375java.sql.SQLWarning, 352SQLException, 345

converting input streams to byte arrays, 586–587counting rows, using Statement, 401CREATE command, 14CREATE DATABASE statement, 15CREATE TABLE statement, 19creating databases, 15–16

■INDEX618

5203chIDX.qxd 8/12/05 4:03 PM Page 618

Page 648: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

creating DataSource objects, 156MySQL, 156–157

without JNDI, 165–166Oracle, 156

without JNDI, 165using data source factory (DSF) objects, 160using relational databases, 157–159without JNDI, 165–166

creating simple JDBC programs, 11–13cursors, 175

choosing, 58–59REF CURSOR objects, passing to

PreparedStatement, 553–557Oracle, 556–557

ResultSetsgetting cursor position, 216–217getting number of rows, 217–218moving (scrolling methods), 214–216

troubleshooting running out of cursors, 61–63custom type mapping, 136–144

implementing SQLData, 137–138Oracle database preparation, 136–137using a Connection’s type map, 138writing new records, 138–144

■DData Definition Language (DDL), 13–14Data Manipulation Language (DML), 13–14Data Source Administrator, 11data source factory (DSF) objects, 160data sources, ODBC, 7data types

BLOBs. See BLOBsCLOBs. See CLOBsDate. See DateDATETIME (MySQL), 310Double data type

formatting, 591–594passing to PreparedStatement, 542–545

int, formatting, 590–591OBJECT

deleting OBJECTs from Oracle tables,413–414

inserting OBJECT values into Oracle tables,410–411

retrieving OBJECT values from Oracle tables,411–413

passing input parameters toPreparedStatement. See passing inputparameters to PreparedStatement

Time. See TimeTimestamp. See Timestampuser-defined types (UDTs), 134. See also type

mappingdata-server tier (three-tier model), 84database management system (DBMS), 6database metadata, 45–46

DatabaseMetaData.getDriverVersion( ), 597DatabaseMetaDataTool, 45determining database support for scrollable

ResultSets, 212generic search patterns, 57

maximum connections limits, 145MySQL, 146–147Oracle, 145–146

minimizing methods usage, 52database transactions, 47databases, 6

connecting to. See connections; Connectionobject

controlling commit and rollback updates,118–119

creating, 15–16creating DataSource objects, 157–160determining whether databases accept

transactions, 113–115disconnecting, 131–132dropping, 16key JDBC concepts

MySQL databases, 72–73Oracle databases, 69–71

Northwind, 10populating, 22registering, 11relational databases, 6relationship with JDBC drivers, 37

DataSource object, 153creating connections, 153–156

with JNDI, 166–167without JNDI, 165–166

creating DataSource objects, 156MySQL, 156–157, 165–166Oracle, 156, 165using data source factory (DSF) objects, 160using relational databases, 157–159without JNDI, 165–166

deploying/registering, 161registering objects using file-based systems,

162–164retrieving deployed/registered DataSource

objects, 164–165interface implementations, 154invoking getDataSource( ) with specific

users/passwords, 157–158invoking getDataSource( ) without specific

users/passwords, 158–159life cycle of DataSource objects, 155obtaining connections with JNDI, 166–167obtaining connections without JNDI, 165–166properties, 154, 160–161

DataTruncation, 43, 341, 373–375constructor, 375creating/using DataTruncation objects, 375–379determining whether DataTruncation has

occurred, 374methods, 375using with ResultSet, 379

Date, 307–308. See also Time; Timestampadding/subtracting days for given dates,

327–328checking for leap years, 325–326converting

conversion of Java Date classes, 326–327current time to java.sql.Date objects, 322

■INDEX 619

5203chIDX.qxd 8/12/05 4:03 PM Page 619

Page 649: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

Java Date classes to Calendar, 327java.sql.Date to java.util.Date, 322–323java.sql.Timestamp objects to java.util.Date

objects, 335java.util.Date objects to java.sql.Date

objects, 316–317java.util.Date to HH:MM:SS strings, 325String dates to java.sql.Date objects,

319–320String dates to java.util.Date objects, 317Timestamp to Month-Day-Year strings,

331–332creating java.util.Date objects, 314–315

from Year, Month, and Day format, 318–319using java.sql.Date.valueOf(), 315using java.util.Calendar, 315using java.util.Date, 314using java.util.GregorianCalendar, 315

creating yesterday’s date from String dates,317–318

determining day of week from java.util.Dateobjects, 322

determining validity of format patterns forSimpleDateFormat, 332

finding difference between two given dates,328–331

getting current dateas a java.util.Date object, 314using utilities, 587

getting date labels from java.sql.Timestampobjects, 333–335

getting dates for specific time zones, 340java.sql.Date.valueOf( ) method, 315leap years, checking for, 325–326mapping, 308MySQL, 310normalization, 335–339Oracle, 310–314passing to PreparedStatement, 538–541

MySQL, 540–541Oracle, 539–540

retrieving, 309–310SimpleDateFormat, 323

DATETIME (MySQL), 310DBMS (database management system), 6DbUtils package, 598–604

core classes/interfaces, 598design goals, 598example usage, 598–604package components, 598Web sites, 598, 602–603

DDL (Data Definition Language), 13–14debugging

debugging supported by JDBC-ODBC bridge, 13exceptions

getting all SQLException instancesusingprintSQLExceptions( ), 351–352

getting details of SQLException, 349–350JDBC programs, 75–76ResultSet objects, 606–608SQLException objects, 605SQLWarning objects, 605–606

defining tablesBLOBs, 233CLOBs, 259–260Date/Time/Timestamp (Oracle), 311

deleteRow( ) method, updatable ResultSets,226–229

deletingbatch updates, 59–61BLOBs, 243–245

MySQL databases, 245–246Oracle databases, 243–245

CLOBs, using servletsMySQL, 303–305Oracle, 300–303

DELETE, 14nodes, 420–423, 426–427OBJECTs from Oracle tables, 413–414rows, using Statement, 400subtrees, 427–430

deploying DataSource objects, 161problems with file-based DataSource objects,

163–164registering objects using file-based systems,

162–163retrieving deployed/registered DataSource

objects, 164–165deserializing objects, 246–248, 252detailed architecture of JDBC, 3–5disabling autocommits, 118–119, 133disconnecting databases, 131–132displaying

BLOBs, 240–243CLOBs

JFrames, 270–274servlets, MySQL, 277–279, 284–285servlets, Oracle, 274–277, 280–283

ResultSet objects, 606–608SQLException objects, 605SQLWarning objects, 605–606

distributed transactionsMySQL support, 77Oracle support, 78

DML (Data Manipulation Language), 13–14

doGet( ), CLOBs, 283, 288, 296, 302doPost( ), CLOBs, 303Double data type

formatting, 591–594passing to PreparedStatement, 542–545

MySQL, 544–545Oracle, 543–544

downloading JDBC drivers, 34Driver interface

creating connections, 106–108loading JDBC drivers, 30

DriverManager, 7, 18, 27creating connections, 91–95

loading drivers, 29, 91–92making connections, method 1, 92–94making connections, method 2, 94–95making connections, method 3, 95

function of, 90

■INDEX620

5203chIDX.qxd 8/12/05 4:03 PM Page 620

Page 650: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

getConnection( ) method, 91passing additional Oracle connection

properties to drivers, 123–124getDrivers( ) method, 95–97tracing JDBC operations, 75–76

DriverManager.registerDriver( ), loading JDBCdrivers, 29

DriverPropertyInfo class, 103–106drivers. See also DriverManager

building URLs in JDBC driver format, 594, 597

connecting to existing databases, 17–18core functionality, 79

MySQL, 79–81Oracle, 81–82

criteria for selection, 37–38determining the driver that created a

connection, 117–118driver names, 38driver relationship with databases, 37driver types, 34–37getting JDBC driver version number, 597JDBC, 13, 27–38

driver certification program, 82–83driver database, 83

loading, 29–32, 91–92getting list of loaded drivers, 95–97using utilities, 588

MySQL, 65–69connection properties, 121–123core functionality, 79–81

obtaining, 34ODBC, 6–7ODBC bridges, 6Oracle, 63–65

connection properties, 123–124, 147core functionality, 81–82

selecting the right JDBC driver, 52testing installations, 32–34types of drivers, 52Web site listing, 32writing, 82

DROP command, 14dropping

databases, 16tables, using Statement, 388

DSF (data source factory) objects, creatingDataSource objects, 160

■Eempty_clob( ) function, 270enabling autocommits, 133ending transactions, 48–49environment settings for JDBC APIs, 73Error class, 347errors. See also exceptions; warnings

handling, 42–44java.lang.Error, 347

Excalibur, 51, 55Excel

reading/extracting data from, 200–203writing data to, 203–205

exceptions, 341–342closing Connection

logging exceptions, 570–574not reporting exceptions, 570–574reporting exceptions, 571–575

closing PreparedStatementlogging exceptions, 578–579not reporting exceptions, 576–578reporting exceptions, 579

closing ResultSetlogging exceptions, 576not reporting exceptions, 575–576reporting exceptions, 576

closing ResultSet, Statement, and Connectiontogether

not reporting exceptions, 580–581reporting exceptions, 581

closing Statementlogging exceptions, 577not reporting exceptions, 577reporting exceptions, 577–578

closing Statement and Connection together

not reporting exceptions, 579–580reporting exceptions, 580

common exceptions, 342debugging/displaying SQLException objects,

605exception object components, 344handling, 42–44, 343–344java.lang.Exception, 341–344, 347java.sql.BatchUpdateException, 341,

364–373constructors, 365method, 365MySQL, 368, 372Oracle, 369, 373

java.sql.DataTruncation, 341, 373–375constructor, 375creating/using DataTruncation objects,

375–379determining whether DataTruncation has

occurred, 374methods, 375using with ResultSet, 379

java.sql.SQLWarning, 341, 352–353constructors, 352creating/traversing warnings, 355–356determining whether warnings have

occurred, 353–355getting all instances, 353methods, 353

SQLException, 341, 344–345chaining, 350–351checked vs. unchecked exceptions, 346constructors, 345example usage, 348–349getting all instances using

printSQLExceptions( ), 351–352getting details, 349–350methods, 345relationship to other classes, 346–347

■INDEX 621

5203chIDX.qxd 8/12/05 4:03 PM Page 621

Page 651: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

SQLState codes returned bySQLException.getSQLState( ), 358–364

code formats, 358MySQL codes, 364Oracle codes, 359–363

stack traces, 344wrapping, 357–358

executeBatch( ) method (Statement object), 403,414–415

executing statementsbatch statements, using Statement, 403–405

JDBC batch update support, 414–415mapping trees into SQL. See mapping trees

into SQL using batch updatesMySQL, 407–408Oracle, 405–407

executing SQL statements, using Statement,386–387

explicit transactions, 49extracting

BLOBs, 240–243CLOBs

JFrames, 270–274servlets, MySQL, 277–279, 284–285servlets, Oracle, 274–277, 280–283

■Ffat client, 84fetch size, 115

getting, 116–117setting, 116

using Statement, 395–396fields

returning long columns/fields using utilities,582–583

storing long text fields using utilities, 583–584file-based systems, registering DataSource objects,

162–163problems with file-based DataSource objects,

163–164retrieving deployed/registered DataSource

objects, 164–165first( ) method, scrollable ResultSets, 215float data type, passing to PreparedStatement,

542–545MySQL, 544–545Oracle, 543–544

formattingDouble

data types, 591–593objects, 594

integersint data type, 590–591Integer objects, 591

java.math.BigDecimal objects, 593–594strings, 588–590

FreeTDS, 82functional problems, 51

■Ggenerating random GUIDs, 608, 613getAsciiStream( ), Java.sql.Clob, 258

getBinaryStream( ), Java.sql.Blob, 232getBLOB( ), 242

getBlob(int columnPosition), 234–235getBlob(String columnName), 235

getBytes(long pos, int length), Java.sql.Blobinterface, 232

getCharacterStream( ), Java.sql.Clob interface, 258

getCLOB( ), 199, 261–262, 273getClobAsURL( ), 282

getClobsContentAsString( ), 297getConcurrency( ), 219–220getConnection( )

CLOBs, 272, 282, 288, 292, 296, 300, 302DriverManager, 91

passing additional Oracle connectionproperties to drivers, 123–124

getDataSize( ), java.sql.DataTruncation, 375getDataSource( ). See also DataSource object

invoking with specific users/passwords,157–158

invoking without specific users/passwords,158–159

getDate( ), 309getDrivers( ), DriverManager class, 95–97getIndex( ), java.sql.DataTruncation, 375getNextWarning( ), 353getParameter( ), java.sql.DataTruncation, 375getRead( ), java.sql.DataTruncation, 375getSqlDriver method, 18getSubString(long pos, int length), Java.sql.Clob

interface, 258getTime( ), 309getTimestamp( ), 310getting. See retrievinggetTransferSize( ), java.sql.DataTruncation, 375getType( ), 214getUpdateCounts( ), BatchUpdateException, 365global transactions

MySQL support, 77Oracle support, 78

globally unique identifier (GUID) primary keys,generating, 608, 613

GRANT command, 14GregorianCalendar

checking for leap years, 326creating java.util.Date objects, 315making java.sql.Timestamp objects for a given

Year, Month, Day, Hour, 339–340grouping restrictions

BLOBs, 234CLOBs, 261

■Hhandling errors/exceptions, 42–44, 343–344. See

also exceptionsHEAP tables, 77high-level architecture of JDBC, 3holdability (ResultSets), 181

checking, 182–183getting, 183setting, 182

■INDEX622

5203chIDX.qxd 8/12/05 4:03 PM Page 622

Page 652: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

■IIBM URL formats, 94IBM WebSphere, 84InnoDB tables, 77input streams

converting to byte arrays, 586–587materializing

BLOB data, 235CLOB data, 263

passing InputStream to PreparedStatement,498–502

MySQL, 501–502Oracle, 500–501

insensitive scrollable ResultSets, 213creating, 383–384

insertingbatch updates, 59–61binary data, using Statement, 401–402CLOBs, using servlets

MySQL, 290–292Oracle, 285–290

INSERT command, 14nodes, 416–420OBJECT values into Oracle tables

using JDBC, 410–411using SQL*Plus, 410

recordsBLOBs, 236–237, 240CLOBs, 265–270Oracle Date/Time types, 312

rows, using Statement, 399SQL table data, 15

insertRow( ) method, updatable ResultSets,222–226

installations, checking, 148–151integers

formattingint data type, 590–591Integer objects, 591

passing int data type to PreparedStatement,522–526

MySQL, 525–526Oracle, 524–525

interfacescore JDBC classes/interfaces, 26–27DbUtils core classes/interfaces, 598JDBC detailed architecture, 5utilities. See utilities

International Organization for Standardization(ISO), 13

Internet architecture, 85–86ISAM tables, 77ISO (International Organization for

Standardization), 13

■JJakarta DbUtils package, 598–604

core classes/interfaces, 598design goals, 598example usage, 598–604package components, 598Web sites, 598, 602–603

java.lang.Error, 347java.lang.Exception, 341–344, 347java.lang.String vs. java.sql.Clob, 305java.lang.Throwable, 347java.math.BigDecimal

formatting, 593–594passing to PreparedStatement, 502–505

MySQL, 504–505Oracle, 502–504

java.sql, 1java.sql.Array. See arraysjava.sql.BatchUpdateException, 341, 364–373

constructors, 365method, 365MySQL, 368, 372Oracle, 369, 373

java.sql.Blob interface, 231–233. See also BLOBsmethods, 232MySQL BLOBs, 233Oracle BLOBs, 233versus byte[ ], 256

java.sql.Connection object. See Connection object;connections

java.sql.DatabaseMetaData, 45java.sql.DataTruncation, 341, 373–375

constructor, 375creating/using DataTruncation objects, 375–379determining whether DataTruncation has

occurred, 374methods, 375using with ResultSet, 379

java.sql.Date. See Datajava.sql.Driver. See Driver interfacejava.sql.ParameterMetaData, 45java.sql.ResultSetMetaData, 45java.sql.SQLException. See SQLExceptionjava.sql.SQLWarning, 341, 352–353

constructors, 352creating/traversing warnings, 355–356debugging/displaying SQLWarning objects,

605–606determining whether warnings have occurred,

353–355getting all instances, 353methods, 353

java.sql.Time. See Timejava.sql.Timestamp. See Timestampjava.util.Calendar, 315java.util.Date. See Datajava.util.GregorianCalendar, 315java.util.Map object. See type mappingjava.util.Properties, passing additional properties

to JDBC drivers (MySQL), 121–123JavaBeans, DbUtils package, 599–601javax.sql, 2javax.sql.RowSetMetaData, 45jcb.bcm. See BCM (Basic Connection

Management)JDBC, 1–3

architecturedetailed, 3–5high-level, 3

core JDBC classes/interfaces, 26–27

■INDEX 623

5203chIDX.qxd 8/12/05 4:03 PM Page 623

Page 653: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

defined, 1–2environment settings for APIs, 73error/exception handling, 42–44mapping between Java to JDBC SQL types,

39–42OBJECT

creating Oracle OBJECTs, 409–410inserting OBJECT values into Oracle tables,

410–411retrieving OBJECT values from Oracle tables,

411–413utilities. See utilitiesversions, 78–79Web site, 2

JDBC 1.0, 79JDBC 2.0, 79JDBC 3.0, 79JDBC 4.0, 79JDBC API, 26JDBC Driver Certification Program, 82–83JDBC driver database, 83JDBC drivers, 13, 27–38

building URLs in JDBC driver format, 594, 597connecting to existing databases, 17–18core functionality, 79

MySQL, 79–81Oracle, 81–82

criteria for selection, 37–38driver names, 38driver relationship with databases, 37driver types, 34–37getting driver version number, 597loading, 29–32

creating an instance of a Driver class, 30using a Thread class, 32using Class.forName( ), 29using DriverManager.registerDriver( ), 29using System.properties( ) from command

lines, 32using System.properties( ) inside programs,

30–32using utilities, 588

MySQL, 65–66connecting with, 66–69core functionality, 79–81registering, 66

MySQL connection propertiespassing additional properties using

java.util.Properties, 121–122passing additional properties using

java.util.Properties and database URLs,122–123

obtaining, 34Oracle, 63–65

core functionality, 81–82KPRB driver, 65OCI driver, 64–65Thin driver, 63–65

Oracle connection propertiesconnecting as SYSDBA and SYSOPER, 147passing additional properties using

DriverManager’s getConnection( )method, 123–124

selecting the right driver, 52testing installations

MySQL, 32–33Oracle, 33–34

types of drivers, 52Web site listing, 32writing, 82

JDBC installations, checking, 148, 150–151JDBC Internet architecture, 85–86JDBC programming, 15–25

closing objects, 23Connection, 25PreparedStatement, 24result sets, 23Statement, 24

connecting to existing databases, 17–18creating

databases, 15–16tables, 19–21

dropping databases, 16populating databases, 22processing result sets, 23retrieving table records, 22–23

JDBC resources, 74JDBC Thin driver, 39JDBC Utility Component (DbUtils), 598–604

core classes/interfaces, 598design goals, 598example usage, 598–604package components, 598Web sites, 598, 602–603

JDBC-ODBC bridge, 8–13architecture, 8debugging, 13JDBC 2.0 API features supported, 13packages, 8

using sun.jdbc.odbc, 10–13using, 9–13

JFramesdisplaying BLOBs, 240–243displaying CLOBs, 270–274

JNDIobtaining connections

with JNDI, 166–167without JNDI, 165–166

obtaining DataSource objects, 153registering DataSource objects, 162–163

problems with file-based DataSourceobjects, 163–164

retrieving deployed/registered DataSourceobjects, 164–165

jxDBCon open-source JDBC driver framework, 82

■K-Lkeys

primary keys, generating random GUIDs, 608,613

retrieving automatically generated keys usingStatement

MySQL, 388–391Oracle, 392–394

KPRB driver (Oracle), 65last( ) method, scrollable ResultSets, 215

■INDEX624

5203chIDX.qxd 8/12/05 4:03 PM Page 624

Page 654: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

leap years, 325–326length( ) method

Java.sql.Blob interface, 232Java.sql.Clob interface, 258

limiting maximum connections, 145MySQL, 146–147Oracle, 145–146

Linux CLASSPATH environmental variablesettings, 73

listing available parameters for creatingconnections, 103–106

loading drivers, 29–32, 91–92creating an instance of a Driver class, 30getting a list of loaded drivers, 95–97using

Class.forName( ), 29a Driver class, 32DriverManager.registerDriver( ), 29System.properties( ) from command

lines, 32System.properties( ) inside programs,

30–32utilities, 588

logging exceptionsclosing Connection, 570–574closing PreparedStatement, 578–579closing ResultSet, 576closing Statement, 577

logons, SYS logon (Oracle), 123–124long columns/fields, returning using utilities,

582–583long data type, passing to PreparedStatement,

522–526MySQL, 525–526Oracle, 524–525

long text fields, storing using utilities, 583–584

■Mmaintaining connections (autoReconnect

parameter, Connection object), 130–131Map object. See type mappingMapListHandler class, 602–603mapping between Java to JDBC SQL types, 39–42mapping trees into SQL using batch updates,

415–423, 427–430deleting existing nodes, 420–423, 426–427deleting existing subtrees, 427–430inserting new nodes, 416–420

matching wildcards in SQL statements, 200materializing data

BLOBs, 235–236CLOBs, 262–265

math, BigDecimalformatting, 593–594passing to PreparedStatement, 502–505

maximum connections limits, 145restricting max connections in MySQL

databases, 146–147restricting max connections in Oracle

databases, 145–146memory

avoiding memory leaks, 61–63MEMORY tables, 77

metadata, 45–46DatabaseMetaData.getDriverVersion( ), 597DatabaseMetaDataTool, 45determining database support for scrollable

ResultSets, 212generic search patterns, 57maximum connections limits, 145

MySQL, 146–147Oracle, 145–146

minimizing methods usage, 52methods

BatchUpdateException, 365Connection object

commit, 48controlling commit and rollback updates,

118–119rollback, 48

database metadatageneric search patterns, 57minimizing usage, 52

Date, java.sql.Date.valueOf( ), 315Driver interface, connect( ), 106–108DriverManager class

getConnection( ), 91, 124getDrivers( ), 95–97

java.sql.Blob interface, 232creating BLOBs, 234–235materializing BLOB data, 235–236

java.sql.Clob interfacecreating CLOBs, 261–262materializing CLOB data, 262–265

java.sql.DataTruncation, 375java.sql.SQLWarning, 353PreparedStatement, setXXX( ), 493–495. See also

passing input parameters toPreparedStatement

ResultSetabsolute(int), 215afterLast( ), 210, 215beforeFirst( ), 215cancelRowUpdates( ), 221–222deleteRow( ), 226–229first( ), 215getConcurrency( ), 219–220getType( ), 214insertRow( ), 222–226last( ), 215next( ), 210, 215previous( ), 210, 215refreshRow( ), 229relative(int), 215retrieving Date, Time, and Timestamp,

309scrolling methods, 214–216setting ResultSet type, 176–178updateRow( ), 220–221

ResultSetMetaData, generic search patterns, 57SQLException, 345

printSQLExceptions( ), 351–352Statement object

addBatch(String sql), 403, 408execute( ), 387executeBatch( ), 403, 414–415

■INDEX 625

5203chIDX.qxd 8/12/05 4:03 PM Page 625

Page 655: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

user-defined types (UDTs), 134utilities. See utilities

Microsoft Access URL formats, 94Microsoft Excel

reading/extracting data from, 200–203writing data to, 203–205

Microsoft ODBC Data Source Administrator, 11Microsoft SQL Server

making connections, 94–95URL formats, 94

middle tier (three-tier model), 84months. See DateMyISAM tabls, 77MySQL

BatchUpdateException, 368, 372BLOBs, 233. See also BLOBs

creating tables, 234extracting, 240–241inserting records, 236–237serializing objects, 252–256

checking JDBC installations, 148–150CLOBs, 259

creating tables, 260defining, 260deleting, using servlets, 303–305extracting, 270–271inserting, using servlets, 290–292selecting/displaying CLOBs using servlets,

277–279, 284–285updating, using servlets, 297–300

connecting to MySQL databases, 100–102connection properties

passing using database URLs, 122passing using java.util.Properties, 121–122passing using java.util.Properties and

database URLs, 122–123preventing timeouts using autoReconnect,

130–131core functionality of JDBC drivers, 79–81creating DataSource objects, 156–159

without JNDI, 165–166DataTruncation, 377–378. See also

DataTruncationDate, 310Date/Time normalization, 336–339DATETIME, 310determining whether databases accept

transactions, 115executing batch statements, using Statement,

407–408. See also batch statementsJDBC drivers, 65–66

connecting with, 66–69registering, 66

JDBC URL format, 594–597key JDBC concepts for MySQL databases, 72–73limiting maximum connections, 146–147obtaining JDBC drivers, 34passing input parameters to PreparedStatement

BigDecimal objects, 504–505BinaryStream objects, 511–512BLOB objects, 517–518boolean values, 521–522byte arrays, 529–530

byte data type, 525–526character streams, 533–534CLOBs, 537–538Date, 540–541double data type, 544–545float data type, 544–545InputStream objects, 501–502NULL, 548Object, 550, 553String objects, 560–561Time objects, 564Timestamp objects, 564URL objects, 567

ResultSet. See ResultSetsretrieving automatically generated keys using

Statement, 388–391serializing objects, 252–256SQLException, 348–349SQLState codes, 364Statement, creating tables to store Java data

types, 396–397table types, 77testing JDBC driver installation, 32–33Time, 310Timestamp, 310transaction support, 76–77URL formats, 94viewing system variables, 146

MySQL Connector/J, 65–66URL format, 66

MysqlConnectionFactory class (BCM), 109,112–113

■Nnested sets, 415next( ) method, scrollable ResultSets, 210, 215nodes

deleting, 420–423, 426–427inserting, 416–420

normalization (Date and Time), 335–339Northwind database, 10NULL, passing to PreparedStatement,

545–548MySQL, 548Oracle, 546–547

■OOBJECT (Oracle)

creating, 408using JDBC, 409–410using SQL*Plus, 408–409

deleting from tables, 413–414inserting values into tables

using JDBC, 410–411using SQL*Plus, 410

retrieving values from tables, 411–413objects

BLOBs. See BLOBsCallableStatement, batch updates, 60–61CLOBs. See CLOBsclosing, 23–25Connection. See Connection object;

connections

■INDEX626

5203chIDX.qxd 8/12/05 4:03 PM Page 626

Page 656: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

DataSource. See DataSource objectformatting

Double, 594Integer, 591BigDecimal, 593–594

Map. See type mappingPreparedStatement

batch updates, 59–61caching, 61passing to, 548–553versus Statement, 58

Properties, passing to drivers (Oracle) usingdatabase URLs and Properties object, 123

ResultSet, 175–176, 183. See also ResultSetschecking for SQL warnings, 120–121choosing the right cursor, 58–59

serializing, 246MySQL, 252–256Oracle, 246–252

Statement. See also Statementbatch updates, 59–61checking for SQL warnings, 120versus PreparedStatement, 58

unserializing, 246–248, 252OCI driver (Oracle), 64

connecting with, 65ODBC (Open Database Connectivity), 6–7ODBC bridges, 2, 6ODBC Data Source Administrator, 11ODBC Programmer’s Reference Web site, 6OdbcConnectionFactory class (BCM), 109, 112Open Database Connectivity (ODBC), 6–7Oracle

BatchUpdateException, 369, 373BLOBs, 233. See also BLOBs

creating tables, 233extracting, 241inserting records, 237–240serializing objects, 246–252

checking JDBC installations, 148–151CLOBs, 259. See also CLOBs

creating tables, 260defining, 259–260deleting, using servlets, 300–303empty_clob( ) function, 270extracting, 271inserting, using servlets, 285–290selecting/displaying CLOBs using servlets,

274–277, 280–283updating, using servlets, 293–297

connecting to Oracle databases, 97–100connection pool project, 55connection properties

connecting as SYSDBA and SYSOPER, 147passing properties using DriverManager

getConnection( ) method, 123–124using roles for SYS logon, 123–124

core functionality of JDBC drivers, 81–82creating DataSource objects, 156–159

without JNDI, 165database preparation for custom type mapping,

136–137DataTruncation, 378–379

Date, 310–314Date/Time normalization, 336–339determining whether databases accept

transactions, 115executing batch statements, using Statement,

405–407JDBC drivers, 63–65

driver names, 38KPRB driver, connecting with, 65obtaining, 34OCI driver, 64–65Thin driver, 39, 63–65

JDBC URL format, 594–597key JDBC concepts for Oracle databases,

69–71limiting maximum connections, 145–146OBJECT

creating, using JDBC, 409–410creating, using SQL*Plus, 408–409deleting OBJECTs from Oracle tables,

413–414inserting, using JDBC, 410–411inserting, using SQL*Plus, 410retrieving OBJECT values, 411–413

passing input parameters to PreparedStatementArray objects, 496–498BigDecimal objects, 502–504BinaryStream objects, 506–511BLOB objects, 514–517boolean values, 520–521byte arrays, 528–529byte data type, 524–525character streams, 532–533CLOBs, 535–537Date, 539–540double data type, 543–544float data type, 543–544InputStream objects, 500–501NULL, 546–547Object, 550, 553REF CURSOR objects, 556–557String objects, 559–560Time objects, 563Timestamp objects, 563URL objects, 566

ResultSets. See ResultSetsretrieving automatically generated keys using

Statement, 392–394SEQUENCE object, 392serializing objects, 246–252SQLException, 349SQLState codes, 359–363Statement, creating tables to store Java data

types, 397–398SYSDBA and SYSOPER connections, 147testing JDBC driver installation, 33–34Time, 310–314Timestamp, 310–314transaction support, 77–78URLs

formats, 94, 594–597syntax for connecting to databases, 39

Oracle Application Server, 84

■INDEX 627

5203chIDX.qxd 8/12/05 4:03 PM Page 627

Page 657: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

OracleConnectionFactory class (BCM), 109, 112–113ordinal position, PreparedStatement parameters, 493output

printSQLExceptions( ), 351–352printSQLWarnings( ), 353

■Ppackages, utilities. See utilitiesParameterMetaData, 45passing additional properties to JDBC drivers

MySQL connection propertiesdatabase URLs, 122java.util.Properties, 121–122java.util.Properties and database URLs,

122–123preventing timeouts using autoReconnect,

130–131Oracle connection properties

database URLs and Properties object, 123using roles for SYS logon, 123–124

passing input parameters to PreparedStatement,493–495

Array objects, 495–498Oracle, 496–498

BigDecimal objects, 502–505MySQL, 504–505Oracle, 502–504

BinaryStream objects, 506–512MySQL, 511–512Oracle, 506–511

BLOB objects, 513–518MySQL, 517–518Oracle, 514–517

boolean values, 519–522MySQL, 521–522Oracle, 520–521

byte arrays, 526–530MySQL, 529–530Oracle, 528–529

byte data type, 522–526MySQL, 525–526Oracle, 524–525

character streams, 530–534MySQL, 533–534Oracle, 532–533

CLOBs, 534–538MySQL, 537–538Oracle, 535–537

Date, 538–541MySQL, 540–541Oracle, 539–540

double data type, 542–545MySQL, 544–545Oracle, 543–544

float data type, 542–545MySQL, 544–545Oracle, 543–544

InputStream objects, 498–502MySQL, 501–502Oracle, 500–501

int data type, 522–526MySQL, 525–526Oracle, 524–525

long data type, 522–526MySQL, 525–526Oracle, 524–525

NULL, 545–548MySQL, 548Oracle, 546–547

Object, 548–553MySQL, 550, 553Oracle, 550, 553

ordinal position values, 493REF CURSOR objects, 553–557

Oracle, 556–557setXXX( ) method summary, 493–495short data type, 522–526

MySQL, 525–526Oracle, 524–525

String objects, 558–561MySQL, 560–561Oracle, 559–560

Time objects, 561–564MySQL, 564Oracle, 563

Timestamp objects, 561–564MySQL, 564Oracle, 563

URL objects, 565–567MySQL, 567Oracle, 566

passwordsconnecting with MySQL JDBC drivers, 67–69invoking getDataSource( ) with specific

users/passwords, 157–158invoking getDataSource( ) without specific

users/passwords, 158–159Oracle connection properties, 123

performance improvement, 51–63avoiding generic search patterns, 57avoiding memory leaks, 61–63caching PreparedStatement objects, 61driver selection, 52managing database Connection objects, 53–57

choosing optimal transaction isolationlevels, 56–57

controlling transactions, 55–56setting optimal Connection properties, 53using connection pools, 54–55

minimizing database metadata methods usage,52

retrieving only required data, 58batching multiple update statements, 59–61choosing appropriate cursors, 58–59using Statement object vs.

PreparedStatement object, 58simplifying SQL queries, 52

Performance Management Guide, 51pools of connections, 1, 49–51

application optimization, 54–55pool managers, 49

borrowing Connection objects from, 131–132soft closing Connection, 570

populating databases, 22position(Blob pattern, long stat) method

(Java.sql.Blob interface), 232

■INDEX628

5203chIDX.qxd 8/12/05 4:03 PM Page 628

Page 658: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

position(byte[ ] pattern, long start) method(Java.sql.Blob interface), 232

position(Clob searchstr, long start) method(Java.sql.Clob interface), 258

position(String searchstr, long start) method(Java.sql.Clob interface), 258

precompiling statements, 382preorder tree traversal algorithm, 415PreparedStatement, 27, 45, 382

batch updates, 59–61caching, 61closing, 24, 578–579methods, 493

setXXX( ), 494–495passing input parameters. See passing input

parameters to PreparedStatementversus Statement, 58

previous( ) method, scrollable ResultSets, 210, 215

primary keys, generating random GUIDs, 608, 613

printSQLExceptions( ), 351–352printSQLWarnings( ), 353processing result sets, 23programming (JDBC), 15–25

closing objects, 23Connection, 25PreparedStatement, 24result sets, 23Statement, 24

connecting to existing databases, 17–18creating

databases, 15–16tables, 19–21

dropping databases, 16populating databases, 22processing result sets, 23retrieving table records, 22–23

programscore JDBC functionality test program

MySQL, 79–81Oracle, 81–82

debugging, 75–76running

MySQL databases, 72–73Oracle databases, 70–71

propertiesDataSource objects, 154, 160–161MySQL connection properties

passing using database URLs, 122passing using java.util.Properties,

121–122passing using java.util.Properties and

database URLs, 122–123preventing timeouts using autoReconnect,

130–131Oracle connection properties

connecting as SYSDBA and SYSOPER, 147passing using DriverManager

getConnection( ) method, 123–124passing to drivers (Oracle) using database URLs

and Properties object, 123

■Qqueries. See also retrieving

counting rows using Statement, 401getting rows using Statement, 398–399limiting rows returned from SQL servers,

115–117simplifying SQL queries, 52Statement. See Statement

query plans, 382question mark (?), specifying connection

properties, 121

■Rrandom GUID generation, 608, 613RDBMS (relational database management system),

6reconnecting (autoReconnect parameter,

Connection object), 130–131records, 6

inserting, 22retrieving, 22–23writing, using custom mapping, 138–142, 144

REF CURSOR objects, passing toPreparedStatement, 553–557

registeringdatabases, 11DataSource objects, 161

problems with file-based DataSourceobjects, 163–164

retrieving deployed/registered DataSourceobjects, 164–165

using file-based systems, 162–163JDBC drivers

MySQL, 66Oracle JDBC Thin driver, 63

relational database management system (RDBMS),6

relational databases, 6columns, 6creating DataSource objects, 157–160rows/records, 6tables/views, 6

relative(int) method, scrollable ResultSets, 215releasing resources, 23–25reporting exceptions

closing Connection, 571–575closing PreparedStatement, 576, 579closing ResultSet, 576closing ResultSet, Statement, and Connection

together, 581closing Statement, 578closing Statement and Connection together, 580

resources, releasing, 23–25ResultSets, 27, 169–173

adding objects to listsend of lists, 206head of lists, 207

checking for SQL warnings, 120–121, 355choosing cursors, 58–59closing, 23, 575–576

closing ResultSet, Statement, andConnection together, 580–581

■INDEX 629

5203chIDX.qxd 8/12/05 4:03 PM Page 629

Page 659: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

concurrency, 180getting/setting, 181

creating scrollable ResultSets, 383–385insensitive, 383–384sensitive, 384

creating updatable ResultSets, 385–386DataTruncation, 379debugging/displaying ResultSet objects,

606–608determining whether fetched values are NULL,

191Excel files

reading/extracting data from, 200–203writing data to, 203–205

getting BLOB data from tables, 194–198getting CLOB data from tables, 199getting column names, 191–193getting data from ResultSets, 185–191

knowing column names, position, and type,187–190

not knowing column names, position, andtype, 190–191

getting number of table rows, 193–194getting rows, 183–187

using Statement, 398holdability, 181

checking, 182–183getting, 183setting, 182

JDK definition, 175matching wildcards in SQL statements, 200methods

absolute(int), 215afterLast( ), 210, 215beforeFirst( ), 215cancelRowUpdates( ), 221–222deleteRow( ), 226–229first( ), 215getConcurrency( ), 219–220getType( ), 214insertRow( ), 222–226last( ), 215next( ), 210, 215previous( ), 210, 215refreshRow( ), 229relative(int), 215retrieving Date, Time, and Timestamp, 309scrolling methods, 214–216updateRow( ), 220–221

objects, 175–176, 183preferred collection class for storing, 205–207processing, 23REF CURSOR objects, passing to

PreparedStatement, 553–557relationship to other classes/interfaces, 174retrieving whole rows/records at once, 208row prefetch, setting using Statement, 395–396scrollable ResultSet objects, 209–218

creating, 212–214determining database support for, 212determining whether ResultSets are

scrollable, 214getting cursor position, 216–217

getting number of rows, 217–218scrolling methods, 214–216

types, 175–176database support, 178–180getting types, 178setting type, 176–178

updatable ResultSet objectscanceling updates, 221–222creating, 219deleting rows, 226–229determining database support for, 218–219determining whether ResultSets are

updatable, 219–220inserting rows, 222–226refreshing rows, 229updating rows, 220–221

ResultSetMetaData, generic search patterns, 57retrieving

automatically generated keys using StatementMySQL, 388–391Oracle, 392–394

binary data, using Statement, 402BLOBs, 240–243CLOBs

JFrames, 270–274servlets, MySQL, 277–279, 284–285servlets, Oracle, 274–277, 280–283

Date, 309–310current date, using utilities, 587Oracle Date/Time types, 313–314

deployed/registered DataSource objects, 164–165JDBC driver version number, 597long columns/fields, using utilities, 582–583OBJECT values from Oracle tables, 411–413required data only, 58

batching multiple update statements, 59–61choosing appropriate cursors, 58–59using Statement object vs.

PreparedStatement object, 58rows, using Statement, 398–399SQL table data, 15SQLException

details, 349–350getting all instances using

printSQLExceptions( ), 351–352SQLWarning instances, 353table names, 584–585

table and view names together, 585–586Time, 309–310Timestamp, 309–310, 320trimmed strings, using utilities, 587–588view names, 585

table and view names together, 585–586REVOKE command, 14RmiJdbc, 82roles (Oracle) using for SYS logon, 123–124roll back

controlling updates, 118–119handling rollback( ) when exceptions occur, 44ROLLBACK command, 14rolling back and closing Connection objects,

573–575rolling back transactions, 48

■INDEX630

5203chIDX.qxd 8/12/05 4:03 PM Page 630

Page 660: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

rows, 6getting from tables (result sets), 183–187. See

also ResultSetsgetting number of table rows, 193–194optimal row prefetch values, 53prefetch, setting using Statement, 395–396ResultSets

canceling updates, 221–222deleting, 226–229getting number of rows in scrollable

ResultSets, 217–218inserting rows, 222–226refreshing, 229updating, 220–221

retrieving only required data, 58RowSetMetaData, 45rules, autocommit mode, 134running programs, 12

MySQL databases, 72–73Oracle databases, 70–71

■Sscroll-insensitive result sets, 176scroll-sensitive result sets, 176scrollable ResultSets, 209–218

choosing appropriate cursor, 58–59creating, 212–214, 383–385

insensitive, 383–384sensitive, 384

determining database support for, 212, 384–385determining whether ResultSets are scrollable,

214getting cursor position, 216–217getting number of rows, 217–218scrolling methods, 214–216

searches, generic search patterns, 57SELECT queries

counting rows using Statement, 401getting rows using Statement, 398–399

selectingBLOB data types, 240–243CLOBs

JFrames, 270–274servlets, MySQL, 277–279, 284–285servlets, Oracle, 274–277, 280–283

SELECT command, 14sensitive scrollable ResultSets, 213

creating, 384SEQUENCE object (Oracle), 392serializing objects, 246

MySQL databases, 254–256Oracle databases, 246–252unserializing objects, 246–252

servletsdeleting CLOBs

MySQL, 303–305Oracle, 300–303

inserting CLOBsMySQL, 290–292Oracle, 285–290

selecting/displaying CLOBsMySQL, 277–279, 284–285Oracle, 274–277, 280–283

updating CLOBsMySQL, 297–300Oracle, 293–297

setAsciiStream(long pos) method (Java.sql.Clobinterface), 258

setBinaryStream(long pos) method (Java.sql.Blobinterface), 232

setBytes(long pos, byte[ ] bytes) method(Java.sql.Blob interface), 232

setBytes(long pos, byte[ ] bytes, int offset, int len)method (Java.sql.Blob interface), 232

setCharacterStream(long pos) method(Java.sql.Clob interface), 258

setNextWarning( ), 353setString(long pos, String str) method

(Java.sql.Clob interface), 258setString(long pos, String str, int offset, int len)

method (Java.sql.Clob interface), 259setXXX( ) method (PreparedStatement), 494–495.

See also passing input parameters toPreparedStatement

sharing Connection objects, 50short data type, passing to PreparedStatement,

522–526MySQL, 525–526Oracle, 524–525

SHOW VARIABLES statement (MySQL), 146SimpleDateFormat, 323SimpleText database, 82simplifying SQL queries, 52singleton classes, 109soft closes, 25, 49

Connection, using pool managers, 570sorting restrictions

BLOBs, 234CLOBs, 261

SQL, 13–15commands, 13–14data types. See data typesexecuting SQL statements, 386–387mapping between Java to JDBC SQL types,

39–42queries, simplifying, 52statements, 14wildcards, 200tables, 14–15

SQL Serverlimiting rows returned from, 115–117making connections, 94–95URL formats, 94

SQL*Pluscreating OBJECTs, 408–409inserting OBJECT values into Oracle tables, 410retrieving OBJECT values from Oracle tables,

411–413SQLData interface, 136

implementing, 137–138SQLException, 42–43, 341, 344–345

chaining, 350–351checked vs. unchecked exceptions, 346constructors, 345debugging/displaying SQLException objects,

605

■INDEX 631

5203chIDX.qxd 8/12/05 4:03 PM Page 631

Page 661: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

example usage, 348–349getting all instances using

printSQLExceptions( ), 351–352getting details, 349–350methods, 345relationship to other classes, 346–347

SQLState codes returned bySQLException.getSQLState( ), 358–364

code formats, 358MySQL codes, 364Oracle codes, 359–363

SQLWarning, 42, 341, 352–353checking for, 119–121

using Connection object, 119–120using ResultSet object, 120–121using Statement object, 120

constructors, 352creating/traversing warnings, 355–356debugging/displaying SQLWarning objects,

605–606determining whether warnings have occurred,

353–355getting all instances, 353methods, 353

stack traces, 344starting transactions, 48–49Statement, 14, 27, 381–382

batch updates, 59–61checking for SQL warnings, 120, 354closing, 24, 576–578

closing ResultSet, Statement, andConnection together, 580–581

closing Statement and Connection objectstogether, 579–580

creating ResultSet objects, 209–211scrollable ResultSets, 383–385updatable ResultSets, 385–386

creating Statement objects, 382–383determining whether a SQL warning occurred,

395executing batch statements, 403–405

MySQL, 407–408Oracle, 405–407

executing SQL statements, 386–387PreparedStatement, 382. See also

PreparedStatementpreparing/precompiling statements, 382query plans, 382retrieving automatically generated keys

MySQL, 388–391Oracle, 392–394

setting fetch size, 395–396tables

counting rows, 401creating, 387–388deleting rows, 400dropping, 388getting binary data, 402getting rows, 398–399inserting binary data, 401–402inserting rows, 399

storing Java types, MySQL, 396–397storing Java types, Oracle, 397–398updating rows, 399–400

versus PreparedStatement, 58statements, 14storing long text fields using utilities, 583–584streams

BinaryStream, passing to PreparedStatement,506–512

character streams, passing toPreparedStatement, 530–534

InputStream. See InputStreamstring concatenation

formatting integers, 590formatting strings, 588

StringBufferappend( )

formatting integers, 590–591formatting strings, 589

formatting BigDecimal objects, 593–594Double data types, 592–594

StringsCLOBs. See CLOBsconverting Timestamp to Month-Day-Year

strings, 331–332Dates

converting String dates to java.sql.Dateobjects, 319–320

converting String dates to java.util.Dateobjects, 317

creating yesterday’s date from String dates,317–318

formatting BigDecimal objects, 593Double data types, 591strings, 588–590

getting trimmed strings, using utilities, 587–588

materializing CLOB data, 264–265passing to PreparedStatement, 558–561

MySQL, 560–561Oracle, 559–560

SimpleDateFormat, 323Time

HH:MM:SS format, 324–325MM/DD/YYYY format, 323–324

subnames (JDBC URLs), 38subprotocols (JDBC URLs), 38subtrees, deleting, 427–430sun.jdbc.odbc, 8

using, 10–13sun.jdbc.odbc.ee, 8Sybase URL formats, 94SYS logon (Oracle), using roles for, 123–124SYSDBA and SYSOPER connections (Oracle),

147system variables, viewing (MySQL), 146System.currentTimeMillis( ), 316System.properties( ), loading JDBC drivers

from command lines, 32inside programs, 30–32

■INDEX632

5203chIDX.qxd 8/12/05 4:03 PM Page 632

Page 662: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

■Ttables, 6

binary datagetting, using Statement, 402inserting, using Statement, 401–402

BLOBscolumn restrictions, 234creating, 233–234defining BLOB data types, 233deleting, 243–246getting BLOB data, 194–198inserting records, 236–237, 240restrictions, 234retrieving BLOB data types, 240–243

CLOBscreating, 260defining BLOB data types, 259–260deleting MySQL CLOBs using servlets,

303–305deleting Oracle CLOBs using servlets,

300–303getting CLOB data, 199inserting into MySQL databases using

servlets, 290–292inserting into Oracle databases using

servlets, 285–290inserting records, 265–270restrictions for using, 261selecting/displaying CLOBs in JFrames,

270–274selecting/displaying CLOBs using servlets,

MySQL, 277–279, 284–285selecting/displaying CLOBs using servlets,

Oracle, 274–277, 280–283updating MySQL CLOBs using servlets,

297–300updating Oracle CLOBs using servlets,

293–297counting rows, using Statement, 401creating, 19–21

Date/Time/Timestamp (Oracle), 311storing Java types using Statement, MySQL,

396–397storing Java types using Statement, Oracle,

397–398using Statement, 387–388, 396–398

defining, Date/Time/Timestamp (Oracle), 311deleting rows, using Statement, 400dropping, using Statement, 388getting

BLOB data, 194–198CLOB data, 199rows, using Statement, 398–399table names, using utilities, 584–586

inserting records, 22Oracle Date/Time types, 312

inserting rows, using Statement, 399MySQL table types, 77ResultSet objects, 607–608. See also ResultSetsretrieving records, 22–23. See also ResultSets

only required data, 58Oracle Date/Time types, 313–314

rowsgetting (result sets), 183–187. See also

ResultSetsgetting number of table rows, 193–194

SQL, 14–15updating rows, using Statement, 399–400

testingconnection validity, 125–126, 129JDBC driver installation

MySQL, 32–33Oracle, 33–34

Thin driver (Oracle), 63connecting with, 64–65registering, 63

Thread, loading JDBC drivers, 32three-tier model for JDBC, 84–86Throwable class, 347throwing exceptions, 44Time, 307–308. See also Date

convertingcurrent time to java.sql.Date objects, 322java.util.Date to HH:MM:SS strings, 325java.util.Date to MM/DD/YYYY strings,

323–324creating

HH:MM:SS format strings, 324java.sql.Time objects, 320–321

determining day of week, 322getting current timestamp as a java.util.Time

object, 316mapping, 308MySQL, 310normalization, 335–339Oracle, 310–314passing to PreparedStatement, 561–564

MySQL, 564Oracle, 563

retrieving, 309–310time zones, getting dates for specific zones, 340timeouts, preventing (autoReconnect parameter),

130–131Timestamp, 307–308

convertingjava.sql.Timestamp objects to java.util.Date

objects, 335Timestamp to Month-Day-Year strings,

331–332getting current timestamp

as a java.util.Time object, 316as a java.util.Timestamp object, 316

getting date labels from java.sql.Timestampobjects, 333–335

getting Timestamp objects, 320making java.sql.Timestamp objects for given

Year, Month, Day, Hour, 339–340mapping, 308MySQL, 310Oracle, 310–314passing to PreparedStatement, 561–564

MySQL, 564Oracle, 563

retrieving, 309–310

■INDEX 633

5203chIDX.qxd 8/12/05 4:03 PM Page 633

Page 663: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

TNSNAMES string, 39tracing JDBC operations, 75–76transaction branches, 78transactions, 46–47

ACID (atomicity, consistency, isolation, anddurability), 47

application optimization, optimal transactionisolation levels, 56–57

autocommit mode, 48controlling transactions for application

optimization, 55–56determining whether databases accept

transactions, 113–115distributed

MySQL support, 77Oracle support, 78

explicitmodel 1, 49model 2, 49

isolation levels, 56–57MySQL transaction support, 76–77Oracle transaction support, 77–78rolling back transactions, 48starting/ending, 48–49

traversing warnings, 355–356trees, mapping into SQL using batch updates,

415–423, 427–430deleting existing nodes, 420–423, 426–427deleting existing subtrees, 427–430inserting new nodes, 416–420

trimmed strings, getting using utilities, 587–588trimParameter( ), CLOBs, 290, 297troubleshooting. See also debugging

handling errors/exceptions, 42–44performance improvement. See performance

improvementrunning out of cursors, 61–63

truncate(long len) methodJava.sql.Blob interface, 232Java.sql.Clob interface, 259

try blocks, 343–344two-tier model for JDBC, 83–84type 1 drivers, 35type 2 drivers, 35–36type 3 drivers, 36type 4 drivers, 36–37type mapping, 134–135

creating SQL to Java type map entries, 135–144implementing SQLData, 137–138Java type map support, 135Oracle database preparation, 136–137using a Connection type map, 138writing new records, 138–144

SQLData interface, 136implementing, 137–138

user-defined types (UDTs), 134

■UUDTs (user-defined types), 134. See also type

mappinginserting UDTs into databases, 138–142, 144SQLData interface, 136–138

unchecked vs. checked exceptions, 346

Unicode, materializing CLOB data, 263–264Unix CLASSPATH environmental variable settings,

73unserializing objects, 246–248, 252updatable ResultSet objects

canceling updates, 221–222creating, 219, 385–386deleting rows, 226–229determining database support for, 218–219,

386determining whether ResultSets are updatable,

219–220inserting rows, 222–226refreshing rows, 229updating rows, 220–221

updateRow( ) method, updatable ResultSets,220–221

updating, 14batch updates, 59–61

BatchUpdateException, 43, 341, 364–373JDBC batch update support, 414–415mapping trees into SQL. See mapping trees

into SQL using batch updatesCLOBs, using servlets

MySQL, 297–300Oracle, 293–297

controlling commit and rollback updates,118–119

ResultSets. See updatable ResultSet objectsrows, using Statement, 399–400SQL tables, 15

URLsMySQL drivers

building URLs in JDBC driver format, 594,597

connecting to driver manager usingdatabase URL, 66–68

Connector/J URL format, 66passing additional properties to drivers using

database URLsMySQL, 122–123Oracle, 123

passing to PreparedStatement, 565–567MySQL, 567Oracle, 566

selecting/displaying CLOBs using servletsMySQL, 284–285Oracle CLOBs, 280–283

syntax for connecting to databases, 38–39vendor URL formats, 94

user parameter, connecting with MySQL JDBCdrivers, 67–69

user-defined types (UDTs), 134. See also typemapping

inserting UDTs into databases, 138–142, 144SQLData interface, 136

implementing, 137–138usernames, Oracle connection properties, 123users

invoking getDataSource( ) with specificusers/passwords, 157–158

invoking getDataSource( ) without specificusers/passwords, 158–159

■INDEX634

5203chIDX.qxd 8/12/05 4:03 PM Page 634

Page 664: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

utilities, 569building URLs in JDBC driver format, 594, 597closing Connection objects, 569–575

closing ResultSet, Statement, andConnection together, 580–581

closing Statement and Connection together,579–580

committing and closing, 571–573logging exceptions, 570–574not reporting exceptions, 570–574reporting exceptions, 571–575rolling back and closing, 573–575soft closing using pool managers, 570

closing PreparedStatement objects, 578–579closing ResultSet objects, 575–576

closing ResultSet, Statement, andConnection together, 580–581

closing Statement objects, 576–578closing ResultSet, Statement, and

Connection together, 580–581closing Statement and Connection together,

579–580converting input streams to byte arrays,

586–587DbUtils package, 598–604

core classes/interfaces, 598design goals, 598example usage, 598–604package components, 598Web sites, 598, 602–603

debugging/displaying ResultSet objects,606–608

debugging/displaying SQLException objects,605

debugging/displaying SQLWarning objects,605–606

formatting Double data types, 591–593formatting Double objects, 594formatting integers

int data type, 590–591Integer objects, 591

formatting java.math.BigDecimal objects,593–594

formatting strings, 588–590generating random GUIDs, 608, 613getting current date, 587getting table names, 584–585

getting table and view names together,585–586

getting trimmed strings, 587–588getting view names, 585JDBC drivers

getting driver version, 597loading JDBC drivers, 588

returning long columns/fields, 582–583storing long text fields, 583–584

■Vvariables

CLASSPATH environmental variable, 73viewing MySQL system variables, 146

versions of JDBC, 78–79

viewing. See displaying; retrievingviews, 6

getting view names using utilities, 585–586

■Wwarnings

debugging/displaying SQLWarning objects,605–606

checking for, 119–121using Connection object, 119–120using ResultSet object, 120–121using Statement object, 120, 395

java.sql.DataTruncation, 341, 373–375constructor, 375creating/using DataTruncation objects,

375–379determining whether DataTruncation has

occurred, 374methods, 375using with ResultSet, 379

java.sql.SQLWarning, 42, 341, 352–353constructors, 352creating/traversing warnings, 355–356determining whether warnings have

occurred, 353–355getting all instances, 353methods, 353

Web sitesBEA WebLogic Server, 84FreeTDS, 82IBM WebSphere, 84JDBC, 2

drivers, 32resources, 74

jxDBCon open-source JDBC driver framework,82

Microsoft ODBC Programmer’s Reference, 6MySQL, 34Oracle, 34RmiJdbc, 82SimpleText database, 82Sun, JDBC-ODBC bridge, 13

WebLogic connection pool, 51, 55WebLogic Server, 84WebSphere, 84wildcards

matching in SQL statements, 200specifying connection properties, 121

Windows CLASSPATH environmental variablesettings, 73

wrapping exceptions, 357–358writing

JDBC drivers, 82to Microsoft Excel, 203–205records, using custom mapping, 138–144

■X-Y-ZXML, listing available parameters for creating

connections, 103–106years. See Date

■INDEX 635

5203chIDX.qxd 8/12/05 4:03 PM Page 635

Page 665: Jdbc recipes  a problem-solution approach (problem-solution approach)[2005]

forums.apress.comFOR PROFESSIONALS BY PROFESSIONALS™

JOIN THE APRESS FORUMS AND BE PART OF OUR COMMUNITY. You’ll find discussions that cover topics

of interest to IT professionals, programmers, and enthusiasts just like you. If you post a query to one of our

forums, you can expect that some of the best minds in the business—especially Apress authors, who all write

with The Expert’s Voice™—will chime in to help you. Why not aim to become one of our most valuable partic-

ipants (MVPs) and win cool stuff? Here’s a sampling of what you’ll find:

DATABASES

Data drives everything.

Share information, exchange ideas, and discuss any databaseprogramming or administration issues.

INTERNET TECHNOLOGIES AND NETWORKING

Try living without plumbing (and eventually IPv6).

Talk about networking topics including protocols, design,administration, wireless, wired, storage, backup, certifications,trends, and new technologies.

JAVA

We’ve come a long way from the old Oak tree.

Hang out and discuss Java in whatever flavor you choose:J2SE, J2EE, J2ME, Jakarta, and so on.

MAC OS X

All about the Zen of OS X.

OS X is both the present and the future for Mac apps. Makesuggestions, offer up ideas, or boast about your new hardware.

OPEN SOURCE

Source code is good; understanding (open) source is better.

Discuss open source technologies and related topics such asPHP, MySQL, Linux, Perl, Apache, Python, and more.

PROGRAMMING/BUSINESS

Unfortunately, it is.

Talk about the Apress line of books that cover softwaremethodology, best practices, and how programmers interact withthe “suits.”

WEB DEVELOPMENT/DESIGN

Ugly doesn’t cut it anymore, and CGI is absurd.

Help is in sight for your site. Find design solutions for yourprojects and get ideas for building an interactive Web site.

SECURITY

Lots of bad guys out there—the good guys need help.

Discuss computer and network security issues here. Just don’t letanyone else know the answers!

TECHNOLOGY IN ACTION

Cool things. Fun things.

It’s after hours. It’s time to play. Whether you’re into LEGO®

MINDSTORMS™ or turning an old PC into a DVR, this is wheretechnology turns into fun.

WINDOWS

No defenestration here.

Ask questions about all aspects of Windows programming, gethelp on Microsoft technologies covered in Apress books, orprovide feedback on any Apress Windows book.

HOW TO PARTICIPATE:

Go to the Apress Forums site at http://forums.apress.com/.

Click the New User link.

BOB_Forums_7x9.25.qxd 8/18/03