260 47 3MB
English Pages 431 Year 2005
Hibernate in Action
Hibernate in Action CH RI ST I AN BAU ER GAVI N KI N G
M ANNING Green wich ( 74° w. lon g.)
For on lin e in formation and orderin g of th is an d oth er Manning books, please visit www.man nin g.com. Th e publisher offers discoun ts on this book when ordered in quan tity. For more in formation , please con tact: Special Sales Departmen t Mann in g Publication s Co. 209 Bruce Park Aven ue Green wich , CT 06830
Fax: ( 203) 661-9018 email: man n in [email protected]
©2005 by Man nin g Publication s Co. All righ ts reserved.
No part of th is publication may be reproduced, stored in a retrieval system, or transmitted, in an y form or by mean s electron ic, mech an ical, ph otocopying, or otherwise, without prior written permission of th e publish er.
Many of th e design ation s used by manufacturers an d sellers to distinguish their products are claimed as trademarks. Wh ere th ose design ations appear in the book, and Manning Publication s was aware of a trademark claim, th e designations have been printed in initial caps or all caps.
Recogn izin g th e importan ce of preservin g wh at h as been written, it is Manning’s policy to have the books th ey publish prin ted on acid-free paper, and we exert our best efforts to that end.
.
Mann in g Publication s Co. 209 Bruce Park Aven ue Green wich , CT 06830
Copyeditor: Tiffany Taylor Typesetter: Dottie Marsico Cover design er: Leslie Haimes
ISBN 1932394-15-X Printed in th e Un ited States of America 1 2 3 4 5 6 7 8 9 10 – VHG – 07 06 05 04
contents foreword xi preface xiii acknowledgments xv about this book xvi about Hibernate3 and EJB 3 xx author online xxi about the title and cover xxii
1
Understanding object/ relational persistence 1.1
1
Wh at is persisten ce? 3 Relational databases 3 Understanding SQL 4 Using SQL in Java 5 Persistence in object-oriented applications 5 ■
■
■
1.2
Th e paradigm mismatch
7
T he problem of granularity 9 T he problem of subtypes 10 T he problem of identity 11 Problems relating to associations T he problem of object graph navigation 14 T he cost of the mismatch 15 ■
■
■
1.3
Persisten ce layers an d altern atives 16 Layered architecture 17 Hand-coding a persistence layer with SQL/ JDBC 18 Using serialization 19 Considering EJB entity beans 20 Object-oriented database systems 21 Other options 22 ■
■
■
■
1.4
Object/ relational mapping What is ORM? 23 Why ORM? 26
1.5
■
Summary 29 v
22
Generic ORM problems
25
13
vi
CONTENTS
2
Introducing and integrating Hibernate 2.1
“Hello World” with Hibern ate
2.2
Un derstan din g the arch itecture
30
31 36
T he core interfaces 38 Callback interfaces T ypes 40 Extension interfaces 41 ■
40
■
2.3
Basic configuration
41
Creating a SessionFactory 42 Configuration in non-managed environments 45 Configuration in managed environments 48 ■
■
2.4
Advan ced con figuration settin gs 51 Using XML-based configuration SessionFactory 53 Logging Extensions (JMX) 55 ■
2.5
3
■ ■
JNDI-bound Java Management
Summary 58
Mapping persistent classes 3.1
51 54
59
Th e CaveatEmptor application
60
Analyzing the business domain 61 T he CaveatEmptor domain model 61
3.2
Implemen tin g th e domain model
64
Addressing leakage of concerns 64 T ransparent and automated persistence 65 Writing POJOs 67 Implementing POJO associations 69 Adding logic to accessor methods 73 ■
■
■
3.3
Defin in g th e mappin g metadata
75
Metadata in XML 75 Basic property and class mappings 78 Attribute-oriented programming 84 Manipulating metadata at runtime 86 ■
■
3.4
Un derstan din g object iden tity 87 Identity versus equality 87 Database identity with Hibernate 88 Choosing primary keys 90 ■
■
3.5
Fine-grained object models 92 Entity and value types
3.6
93
■
Mapping class in h eritan ce
Using components
93
97
T able per concrete class 97 T able per class hierarchy 99 T able per subclass 101 Choosing a strategy 104 ■
■
CONTENTS
3.7
Introducing associations 105 Managed associations? 106 Multiplicity 106 T he simplest possible association 107 Making the association bidirectional 108 A parent/ child relationship 111 ■
■
■
3.8
4
Summary 112
Working with persistent objects 4.1
Th e persisten ce lifecycle
114 115
T ransient objects 116 Persistent objects 117 Detached objects 118 T he scope of object identity 119 Outside the identity scope 121 Implementing equals() and hashCode() 122 ■
■
■
■
■
4.2
Th e persisten ce man ager
126
Making an object persistent 126 Updating the persistent state of a detached instance 127 Retrieving a persistent object 129 Updating a persistent object 129 Making a persistent object transient 129 Making a detached object transient 130 ■
■
■
■
4.3
Usin g tran sitive persistence in Hibern ate
131
Persistence by reachability 131 Cascading persistence with Hibernate 133 Managing auction categories 134 Distinguishing between transient and detached instances 138 ■
■
4.4
Retrieving objects 139 Retrieving objects by identifier 140 Introducing HQL 141 Query by criteria 142 Query by example 143 Fetching strategies 143 Selecting a fetching strategy in mappings 146 T uning object retrieval 151 ■
■
■
■
4.5
5
Summary 152
Transactions, concurrency, and caching
154
5.1
Tran saction s, con curren cy, an d cach ing
154
5.2
Un derstan din g database tran saction s 156 JDBC and JT A transactions 157 T he Hibernate T ransaction API 158 Flushing the Session 160 Understanding isolation levels 161 Choosing an isolation level 163 Setting an isolation level 165 Using pessimistic locking 165 ■
■
■
■
■
■
5.3
Workin g with application tran saction s 168 Using managed versioning 169 Granularity of a Session 172 Other ways to implement optimistic locking 174 ■
■
vii
viii
CONTENTS
5.4
Cachin g th eory and practice
175
Caching strategies and scopes 176 T he Hibernate cache architecture 179 Caching in practice 185 ■
■
5.5
6
Summary 194
Advanced mapping concepts 6.1
195
Un derstan din g the Hibern ate type system Built-in mapping types
198
6.2
Mappin g collection s of value types 211
6.3
Mapping en tity association s 220
Sets, bags, lists, and maps One-to-one associations
6.4
196
Using mapping types
■
200
211
220
■
Many-to-many associations
225
Mapping polymorph ic association s 234 Polymorphic many-to-one associations 234 Polymorphic collections 236 Polymorphic associations and table-perconcrete-class 237 ■
■
6.5
7
Summary 239
Retrieving objects efficiently 7.1
241
Executing queries 243 T he query interfaces 243 Binding parameters Using named queries 249
245
■
7.2
Basic queries for objects 250 T he simplest query 250 Using aliases 251 Polymorphic queries 251 Restriction 252 Comparison operators 253 String matching 255 Logical operators 256 Ordering query results 257 ■
■
■
■
■
7.3
■
Join in g association s 258 Hibernate join options 259 Fetching associations 260 Using aliases with joins 262 Using implicit joins 265 T heta-style joins 267 Comparing identifiers 268 ■
■
■
7.4
Writin g report queries 269 Projection 270 Using aggregation Restricting groups with having 274 with report queries 275 ■
272 Grouping 273 Improving performance ■
■
CONTENTS
7.5
Advanced query tech niques 276 Dynamic queries 276 Collection filters 279 Subqueries 281 Native SQL queries 283 ■
■
7.6
Optimizin g object retrieval
286
Solving the n+1 selects problem 286 Using iterate() queries 289 Caching queries 290 ■
■
7.7
8
Summary 292
Writing Hibernate applications 8.1
294
Design in g layered application s 295 Using Hibernate in a servlet engine 296 Using Hibernate in an EJB container 311
8.2
Implemen tin g application tran saction s 320 Approving a new auction 321 Doing it the hard way 322 Using detached persistent objects 324 Using a long session 325 Choosing an approach to application transactions 329 ■
■
8.3 8.4
9
Han dling special kin ds of data
330
Legacy schemas and composite keys
330
Audit logging 340
Summary 347
Using the toolset 9.1
■
348
Developmen t processes 349 T op down 350 Bottom up 350 Middle out (metadata oriented) 350 Meet in the middle 350 Roundtripping 351 ■
■
■
9.2
Automatic schema generation
351
Preparing the mapping metadata 352 Creating the schema 355 Updating the schema 357 ■
■
9.3
Generating POJO code Adding meta-attributes Configuring hbm2java
9.4
358 358 362
■ ■
Generating finders 360 Running hbm2java 363
Existin g schemas and Middlegen
364
Starting Middlegen 364 Restricting tables and relationships 366 Customizing the metadata generation Generating hbm2java and XDoclet metadata 370 ■
■
368
ix
x
CONTENTS
9.5
XDoclet
372
Setting value type attributes 372 Mapping entity associations 374 Running XDoclet 375 ■
■
9.6
Summary 376
appendix A: SQL fundamentals
378
appendix B: ORM implementation strategies B.1
Properties or fields? 383
B.2
Dirty-checkin g strategies 384
appendix C: Back in the real world
388
C.1
Th e stran ge copy 389
C.2
Th e more th e better
C.3
We don’t need primary keys 390
C.4
Time isn’t linear
C.5
Dyn amically un safe
C.6
To synchronize or not? 392
C.7
Really fat client
C.8
Resuming Hibernate
references 395 index 397
390
391 391
393 394
382
foreword Relational databases are indisputably at the core of th e modern en terprise. While modern programming languages, including Java TM, provide an in tuitive, object-orien ted view of ap p lication -level bu sin ess en tities, th e en terp rise d ata un derlyin g th ese en tities is h eavily relational in nature. Further, the main strength of th e relation al m od el—over earlier n avigation al m od els as well as over later O O DB models—is th at by design it is in trin sically agn ostic to th e programmatic manipulation and application-level view of the data that it serves up. Man y attempts h ave been made to bridge relational and object-oriented techn ologies, or to replace one with th e oth er, but the gap between th e two is on e of the hard facts of enterprise computing today. It is this challenge—to provide a bridge between relational data and Java TM objects—that Hibernate takes on through its object/ relational mapping ( ORM) approach. Hibernate meets this challenge in a very pragmatic, direct, and realistic way. As Christian Bauer and Gavin King demonstrate in this book, the effective use of ORM tech n ology in all but th e simplest of enterprise environments requires understan din g an d con figurin g h ow th e mediation between relation al data an d objects is per formed. This demands that the developer be aware and knowledgeable both of the application and its data requirements, and of the SQL query language, relational storage structures, and th e poten tial for optimization th at relational technology offers. Not on ly does Hibern ate provide a full-fun ction solution th at meets th ese requirements head on, it is also a flexible an d con figurable arch itecture. Hibernate’s developers designed it with modularity, pluggability, extensibility, and user customization in min d. As a result, in th e few years sin ce its in itial release,
xi
xii
FOREWORD
Hibernate has rapidly become one of the leading ORM technologies for enterprise developers—an d deservedly so. Th is book provides a compreh en sive overview of Hibern ate. It covers h ow to use its type mappin g capabilities an d facilities for modelin g association s an d in h eritan ce; h ow to retrieve objects efficiently using the Hibernate query language; h ow to con figure Hibern ate for use in both managed and unmanaged environments; and how to use its tools. In addition, throughout the book the authors provide insight into the underlying issues of ORM an d in to th e design ch oices beh in d Hibern ate. Th ese in sigh ts give the reader a deep understanding of th e effective use of ORM as an en terprise tech n ology. Hibernate in Action is th e defin itive guide to usin g Hibernate and to object/ relation al mappin g in en terprise computin g today. LINDA DEMICHIEL Lead Arch itect, En terprise JavaBean s Sun Microsystems
preface Just because it is possible to push twigs along the ground with one’s nose does not necessarily mean that that is the best way to collect firewood. —An th on y Berglas
Today, many software developers work with Enterprise Information Systems ( EIS) . This kind of application creates, man ages, an d stores structured in formation an d shares this information between many users in multiple physical locations. Th e storage of EIS data in volves massive usage of SQL-based database man agemen t systems. Every compan y we’ve met durin g our careers uses at least on e SQL database; most are completely dependent on relational database technology at the core of their business. In th e past five years, broad adoption of th e Java programmin g lan guage h as brough t about th e ascen dan cy of th e object-orien ted paradigm for software development. Developers are now sold on the benefits of object orientation. However, the vast majority of businesses are also tied to long-term investments in expensive relation al database systems. Not on ly are particular ven dor products en tren ch ed, but existing legacy data must be made available to ( and via) the shiny new objectoriented web applications. However, the tabular representation of data in a relation al system is fun damen tally different than the networks of objects used in object-oriented Java applications. This difference has led to the so-called object/ relational paradigm mismatch . Traditionally, the importance and cost of th is mismatch h ave been un derestimated, and tools for solving the mismatch have been insufficient. Meanwhile, Java developers blame relation al tech n ology for the mismatch; data professionals blame object tech n ology. xiii
xiv
PREFACE
Object/ relational mapping ( ORM) is the name given to automated solutions to the mismatch problem. For developers weary of tedious data access code, th e good news is that ORM h as come of age. Application s built with ORM middleware can be expected to be cheaper, more per formant, less vendor-specific, and more able to cope with ch an ges to th e in tern al object or un derlyin g SQL sch ema. Th e aston ish ing thing is that these benefits are now available to Java developers for free. Gavin King began developing Hibernate in late 2001 wh en h e foun d th at th e popular persisten ce solution at th e time—CMP En tity Bean s—didn ’t scale to n on trivial applications with complex data models. Hibernate began life as an independent, noncommercial open source project. The Hibernate team ( includin g th e auth ors) h as learn ed O RM th e h ard way— th at is, by listen in g to user requests an d implemen tin g wh at was n eeded to satisfy th ose requests. Th e result, Hibernate, is a practical solution, emphasizing developer productivity and technical leadersh ip. Hibern ate h as been used by ten s of thousands of users and in many th ousan ds of production application s. When th e deman ds on th eir time became overwhelming, the Hibernate team con cluded th at th e future success of the project ( and Gavin’s continued sanity) deman ded profession al developers dedicated full-time to Hibern ate. Hibern ate joined jboss.org in late 2003 and now has a commercial aspect; you can purch ase commercial support and training from JBoss Inc. But commercial training shouldn’t be th e on ly way to learn about Hibernate. It’s obvious that many, perhaps even most, Java projects benefit from the use of an ORM solution like Hibern ate—alth ough th is wasn ’t obvious a couple of years ago! As O RM tech n ology becomes in creasin gly main stream, product documen tation such as Hibernate’s free user manual is n o lon ger sufficien t. We realized th at the Hibern ate commun ity an d n ew Hibernate users needed a full-length book, n ot on ly to learn about developin g software with Hibernate, but also to understand and appreciate the object/ relation al mismatch an d th e motivation s beh in d Hibernate’s design. The book you’re h oldin g was an en ormous effort that occupied most of our spare time for more than a year. It was also the source of many heated disputes an d learn in g experien ces. We h ope th is book is an excellen t guide to Hibern ate ( or, “th e Hibern ate bible,” as on e of our reviewers put it) an d also th e first comprehensive documentation of the object/ relational mismatch and ORM in general. We hope you find it helpful and enjoy working with Hibernate.
acknowledgments Writin g ( in fact, creatin g) a book wouldn ’t be possible with out h elp. We’d first like to thank the Hibernate community for keeping us on our toes; without your requests for th e book, we probably would h ave given up early on . A book is on ly as good as its reviewers, an d we had the best. J. B. Rainsberger, Matt Scarpin o, Ara Abrah amian , Mark Eagle, Glen Smith, Patrick Peak, Max Rydahl Andersen, Peter Eisentraut, Matt Raible, an d Mich ael A. Koziarski. Th an ks for your en dless h ours of readin g our h alf-fin ish ed an d raw man uscript. We’d like to than k Emman uel Bern ard for h is tech n ical review and Nick Heudecker for his help with th e first ch apters. Our team at Man n in g was in valuable. Clay An dres got this project started, Jackie Carter stayed with us in good and bad times and taught us how to write. Marjan Bace provided the necessary confidence that kept us going. Tiffany Taylor and Liz Welch found all the many mistakes we made in grammar an d style. Mary Piergies organized the production of th is book. Man y th an ks for your h ard work. An y oth ers at Man n in g wh om we’ve forgotten : You made it possible.
xv
about this book We introduce the object/ relational paradigm mismatch in th is book an d give you a h igh -level overview of curren t solution s for th is time-con sumin g problem. You’ll learn h ow to use Hibern ate as a persisten ce layer with a rich ly typed domain object model in a sin gle, con tin uing example application. This persistence layer implementation covers all entity association, class in h eritan ce, an d special type mapping strategies. We teach you h ow to tun e th e Hibern ate object query an d transaction system for the best per formance in highly concurren t multiuser application s. Th e flexible Hibernate dual-layer caching system is also an important topic in this book. We discuss Hibern ate in tegration in differen t scen arios an d also sh ow you typical arch itectural problems in two- and three-tiered Java database application s. If you h ave to work with an existing SQL database, you’ll also be interested in Hibernate’s legacy database in tegration features an d th e Hibern ate developmen t toolset.
Roadmap Chapter 1 defin es object persistence. We discuss why a relational database with a SQL inter face is the system for persistent data in today’s application s, an d wh y hand-coded Java persistence layers with JDBC and SQL code are time-consuming and error-prone. After looking at altern ative solution s for th is problem, we in troduce object/ relational mapping and talk about th e advan tages an d down sides of th is approach . Chapter 2 gives an architectural overview of Hibernate and shows you the most important application-programming inter faces. We demonstrate Hibernate
xvi
ABOUT THIS BOOK
xvii
configuration in managed ( and non-managed) J2EE and J2SE environments after lookin g at a simple “Hello World” application . Chapter 3 introduces the example application and all kinds of entity and relationship mappings to a database schema, in cludin g un i- an d bidirection al association s, class in h eritan ce, an d com p osition . You ’ll learn h ow to write H ibern ate mapping files and how to design persistent classes. Ch apter 4 teach es you th e H ibernate in ter faces for read an d save operation s; we also sh ow you h ow tran sitive persisten ce ( persisten ce by reach ability) works in Hibern ate. Th is ch apter is focused on loading and storing objects in the most efficient way. Ch apter 5 discusses con curren t data access, with database an d lon g-run n in g application tran saction s. We in troduce th e con cepts of lockin g an d version in g of data. We also cover cach in g in gen eral an d th e H ibern ate cach in g system, wh ich are closely related to con curren t data access. Ch apter 6 completes your un derstan din g of H ibern ate mappin g tech n iques with more advan ced mappin g con cepts, such as custom user types, collection s of values, an d mappin gs for on e-to-on e an d man y-to-man y association s. We briefly discuss Hibernate’s fully polymorphic behavior as well. Chapter 7 introduces the Hibernate Query Language ( HQL) and other objectretrieval meth ods such as th e query by criteria ( QBC) API, which is a typesafe way to express an object query. We show you how to translate complex search dialogs in your application to a query by example ( QBE) query. You’ll get th e full power of Hibern ate queries by combin in g th ese th ree features; we also sh ow you h ow to use direct SQL calls for th e special cases an d h ow to best optimize query per forman ce. Chapter 8 describes some basic practices of Hibern ate application arch itecture. This includes handling the SessionFactory, th e popular ThreadLocal Session pattern, and encapsulation of the persistence layer functionality in data access objects ( DAO ) an d J2EE commands. We show you how to design long-running application tran saction s an d h ow to use th e in n ovative detached object support in Hibernate. We also talk about audit loggin g an d legacy database schemas. Ch apter 9 in troduces several different development scenarios and tools that may be used in each case. We sh ow you th e common tech n ical pitfalls with each approach an d discuss th e Hibern ate toolset ( hbm2ddl, hbm2java) and the integration with popular open source tools such as XDoclet and Middlegen.
xviii
ABOUT THIS BOOK
Who should read this book? Readers of th is book sh ould h ave basic knowledge of object-oriented software developmen t an d sh ould h ave used th is kn owledge in practice. To un derstan d th e application examples, you should be familiar with th e Java programmin g lan guage an d th e Un ified Modelin g Lan guage. Our primary target audien ce con sists of Java developers wh o work with SQLbased database systems. We’ll sh ow you h ow to substan tially in crease your productivity by leveragin g ORM. If you’re a database developer, the book could be part of your in troduction to object-orien ted software developmen t. If you’re a database administrator, you’ll be in terested in h ow ORM affects performance and how you can tune the per formance of the SQL database man agement system and persistence layer to ach ieve per forman ce targets. Sin ce data access is the bottleneck in most Java application s, th is book pays close atten tion to per formance issues. Many DBAs are understandably nervous about entrusting performance to tool-generated SQL code; we seek to allay th ose fears an d also to h igh ligh t cases wh ere application s sh ould not use tool-managed data access. You may be relieved to discover that we don’t claim that ORM is th e best solution to every problem.
Code conventions and downloads This book provides copious examples, wh ich in clude all th e Hibern ate application artifacts: Java code, Hibern ate con figuration files, an d XML mappin g metadata files. Source code in listings or in text is in a fixed-width font like this to separate it from ordinary text. Additionally, Java method names, component parameters, object properties, and XML elements and attributes in text are also presented using fixed-width font. Java, HTML, and XML can all be verbose. In many cases, the original source code ( available online) has been reformatted; we’ve added line breaks and reworked indentation to accommodate the available page space in th e book. In rare cases, even th is was n ot en ough , an d listin gs in clude line-continuation markers. Addition ally, commen ts in th e source code h ave been removed from th e listin gs.
ABOUT THIS BOOK
xix
Code an n otation s accompan y man y of th e source code listin gs, h igh ligh tin g important concepts. In some cases, numbered bullets lin k to explan ation s th at follow th e listin g. Hibernate is an open source project released un der th e Lesser GNU Public License. Directions for downloading Hibernate, in source or binary form, are available from the Hibernate web site: www.h ibern ate.org/ . Th e source code for all CaveatEmptor examples in th is book is available from http:/ / caveatemptor.hibernate.org/ . Th e CaveatEmptor example application code is available on th is web site in differen t flavors: for example, for servlet an d for EJB deployment, with or without a presen tation layer. However, on ly th e stan dalone persistence layer source package is th e recommen ded compan ion to th is book.
About the authors Ch ristian Bauer is a member of th e Hibern ate developer team an d is also respon sible for the Hibernate web site and documentation. Christian is interested in relational database systems and sound data man agemen t in Java application s. He works as a developer and consultant for JBoss Inc. and lives in Frankfurt, Germany. Gavin King is the founder of the Hibernate project and lead developer. He is an en th usiastic proponen t of agile developmen t an d open source software. Gavin is helpin g in tegrate ORM tech n ology in to th e J2EE stan dard as a member of th e EJB 3 Expert Group. He is a developer an d con sultan t for JBoss In c., based in Melbourne, Australia.
about Hibernate3 and EJB 3 Th e world doesn ’t stop turn in g wh en you fin ish writin g a book, an d gettin g th e book in to production takes more time th an you could believe. Therefore, some of the information in any technical book becomes quickly outdated, especially wh en new standards and product versions are already on the horizon. Hibern ate3, an evolution ary n ew version of Hibernate, was in the early stages of plann in g an d design wh ile th is book was bein g written . By th e time th e book hits the shelves, there may be an alpha release available. However, the information in th is book is valid for Hibern ate3; in fact, we con sider it to be an essen tial reference even for the new version. We discuss fun damen tal con cepts th at will be foun d in Hibern ate3 an d in most ORM solution s. Furth ermore, Hibern ate3 will be mostly backward compatible with Hibern ate 2.1. New features will be added, of course, but you won ’t h ave problems pickin g th em up after readin g th is book. In spired by th e success of Hibern ate, th e EJB 3 Expert Group used several key concepts and APIs from Hibern ate in its redesign of en tity bean s. At the time of writin g, on ly an early draft of th e n ew EJB specification was available; hen ce we don ’t discuss it in this book. However, after reading Hibernate in Action , you’ll kn ow all th e fun damen tals th at will let you quickly un derstan d en tity bean s in EJB 3. For more up-to-date in formation, see th e Hibern ate road map: www.h ibernate.org/ About/ RoadMap.
xx
author online Purchase of Hibernate in Action includes free access to a private web forum where you can make comments about the book, ask technical questions, and receive help from the author and from other users. To access the forum and subscribe to it, point your web browser to www.manning.com/bauer. This page provides information on how to get on the forum once you are registered, what kind of help is available, and the rules of conduct on the forum. It also provides links to the source code for the examples in the book, errata, and other downloads. Manning’s commitment to our readers is to provide a venue where a meaningful dialog between individual readers and between readers and the authors can take place. It is not a commitment to any specific amount of participation on the part of the authors, whose contribution to the AO remains voluntary (and unpaid). We suggest you try asking the authors some challenging questions lest their interest stray!
xxi
about the title and cover By combin in g in troduction s, overviews, an d h ow-to examples, Man n in g’s In Action books are design ed to h elp learn in g an d rememberin g. Accordin g to research in cognitive science, the things people remember are things they discover during self-motivated exploration. Alth ough n o on e at Man n in g is a cogn itive scien tist, we are con vin ced th at for learning to become permanent it must pass through stages of exploration, play, an d, in terestin gly, re-tellin g of wh at is bein g learn ed. People un derstan d an d remember n ew th in gs, wh ich is to say th ey master them, only after actively exploring them. Humans learn in action . An essen tial part of an In Action guide is th at it is example-driven . It en courages th e reader to try th in gs out, to play with n ew code, an d explore n ew ideas. There is another, more mundane, reason for the title of this book: our readers are busy. Th ey use books to do a job or solve a problem. Th ey n eed books th at allow th em to jump in an d jump out easily an d learn just what they want, just when th ey wan t it. Th ey n eed books th at aid th em in action . The books in this series are design ed for such readers.
About the cover illustration Th e figure on th e cover of Hibernate in Action is a peasan t woman from a village in Switzerlan d, “Paysan n e de Sch watzen bourg en Suisse.” The illustration is taken from a French travel book, Encyclopedie des Voyages by J. G. St. Saveur, publish ed in 1796. Travel for pleasure was a relatively new phenomenon at the time and travel guides such as th is on e were popular, introducing both the tourist as well as the armchair traveler, to th e in h abitan ts of oth er region s of Fran ce an d abroad. xxii
ABOUT THE TITLE AND COVER
xxiii
Th e diversity of th e drawin gs in th e Encyclopedie des Voyages speaks vividly of th e un iquen ess an d in dividuality of th e world’s town s an d provin ces just 200 years ago. This was a time when the dress codes of two region s separated by a few dozen miles iden tified people un iquely as belonging to one or the other. The travel guide brin gs to life a sen se of isolation an d distance of that period and of every other historic period except our own hyperkinetic present. Dress codes have changed since then and th e diversity by region , so rich at th e time, has faded away. It is now often h ard to tell th e in h abitan t of on e con tin en t from another. Perhaps, trying to view it optimistically, we h ave traded a cultural an d visual diversity for a more varied personal life. Or a more varied and interesting intellectual an d tech n ical life. We at Man n in g celebrate th e in ven tiven ess, the initiative, and the fun of the computer busin ess with book covers based on th e rich diversity of region al life two centuries ago brought back to life by the pictures from this travel book.
Understanding object/ relational persistence
This chapter covers ■
Obje c t pe rs is te nc e with S QL databas e s
■
The o bje c t/ re latio nal paradigm mis matc h
■
Pe rs is te nc e laye rs in o bje c t-o rie nte d applic atio ns
■
Obje c t/ re latio nal mapping bas ic s
1
2
CHAPTER 1
Understanding object/ relational persistence
The approach to managing persistent data has been a key design decision in every software project we’ve worked on. Given that persistent data isn’t a new or unusual requiremen t for Java application s, you’d expect to be able to make a simple ch oice amon g similar, well-establish ed persisten ce solution s. Th in k of web application frameworks ( Jakarta Struts versus WebWork) , GUI compon en t frameworks ( Swin g versus SWT ) , or template en gin es ( JSP versus Velocity) . Each of th e competin g solutions has advantages and disadvantages, but th ey at least sh are th e same scope an d overall approach . Un fortun ately, this isn ’t yet th e case with persisten ce tech n ologies, wh ere we see some wildly differin g solution s to th e same problem. For several years, persistence has been a hot topic of debate in the Java commun ity. Man y developers don ’t even agree on th e scope of th e problem. Is “persistence” a problem that is already solved by relational technology and extensions such as stored procedures, or is it a more pervasive problem that must be addressed by special Java component models such as EJB en tity bean s? Sh ould we h an d-code even th e most primitive CRUD ( create, read, update, delete) operations in SQL and JDBC, or sh ould th is work be automated? How do we ach ieve portability if every database man agemen t system h as its own SQL dialect? Sh ould we abandon SQL completely and adopt a new database tech n ology, such as object database systems? Debate con tin ues, but recen tly a solution called object/ relational mapping ( ORM) has met with increasing acceptance. Hibern ate is an open source ORM implemen tation . Hibernate is an ambitious project that aims to be a complete solution to the problem of man agin g persisten t data in Java. It mediates th e application ’s in teraction with a relational database, leaving the developer free to concentrate on the busin ess problem at h an d. Hibernate is an non-intrusive solution. By this we mean you aren’t required to follow many Hibern ate-specific rules an d design pattern s when writin g your busin ess logic and persistent classes; thus, Hibernate integrates smoothly with most new and existing applications and doesn’t require disruptive changes to the rest of the application. Th is book is about Hibern ate. We’ll cover basic an d advan ced features an d describe some recommen ded ways to develop n ew application s usin g Hibern ate. Often, th ese recommen dation s won ’t be specific to Hibern ate—sometimes th ey will be our ideas about th e best ways to do things when working with persistent data, explain ed in th e con text of Hibern ate. Before we can get started with Hibern ate, h owever, you n eed to understan d th e core problems of object persistence and object/ relational mapping. This ch apter explain s wh y tools like Hibern ate are needed.
What is persistence?
3
First, we defin e persisten t data management in the context of object-oriented application s an d discuss th e relation sh ip of SQL, JDBC, and Java, the underlying technologies and standards that Hibernate is built on . We th en discuss th e socalled object/ relational paradigm mismatch an d th e gen eric problems we en coun ter in object-oriented software development with relational databases. As this list of problems grows, it becomes apparent that we n eed tools an d pattern s to min imize th e time we h ave to spen d on th e persisten ce-related code of our application s. After we look at altern ative tools an d persisten ce mech an isms, you’ll see th at ORM is th e best available solution for man y scen arios. Our discussion of the advantages and drawbacks of ORM gives you th e full backgroun d to make th e best decision wh en picking a persistence solution for your own project. The best way to learn isn’t necessarily lin ear. We un derstan d th at you probably want to try Hibernate right away. If this is how you’d like to proceed, skip to chapter 2, section 2.1, “Getting started,” wh ere we jump in an d start codin g a ( small) Hibernate application. You’ll be able to un derstan d ch apter 2 with out reading this chapter, but we also recommend that you return here at some point as you circle th rough th e book. Th at way, you’ll be prepared and have all the backgroun d con cepts you n eed for th e rest of th e material.
1.1 What is persistence? Almost all application s require persisten t data. Persisten ce is on e of th e fun damental con cepts in application developmen t. If an in formation system didn ’t preser ve data en tered by users wh en th e h ost mach in e was powered off, th e system would be of little practical use. Wh en we talk about persistence in Java, we’re normally talkin g about storin g data in a relational database using SQL. We start by takin g a brief look at th e tech n ology an d h ow we use it with Java. Armed with th at in formation , we th en con tin ue our discussion of persisten ce an d h ow it’s implemen ted in object-orien ted application s. 1 .1 .1 Relational databases
You, like most oth er developers, have probably worked with a relational database. In fact, most of us use a relation al database every day. Relation al tech n ology is a known quan tity. Th is alone is sufficien t reason for man y organ ization s to ch oose it. But to say on ly th is is to pay less respect th an is due. Relation al databases are so en tren ch ed n ot by acciden t but because th ey’re an in credibly flexible an d robust approach to data man agemen t.
4
CHAPTER 1
Understanding object/ relational persistence
A relation al database man agemen t system isn ’t specific to Java, and a relational database isn ’t specific to a particular application . Relation al tech n ology provides a way of sharing data among different application s or amon g differen t tech n ologies that form part of the same application ( th e tran saction al en gin e an d th e reportin g engine, for example) . Relational technology is a common denominator of many disparate systems and technology platforms. Hen ce, the relation al data model is often the common enterprise-wide represen tation of busin ess en tities. Relational database management systems have SQL-based application programming interfaces; h en ce we call today’s relation al database products SQL database management systems or, when we’re talkin g about particular systems, SQL databases. 1 .1 .2 Understanding SQL
To use H ibern ate effectively, a solid un derstan din g of th e relation al model an d SQL is a prerequisite. You’ll n eed to use your kn owledge of SQL to tun e th e performance of your Hibernate application. H ibernate will automate many repetitive codin g tasks, but your kn owledge of persisten ce tech n ology must exten d beyon d Hibernate itself if you want take advan tage of th e full power of modern SQL databases. Remember that the underlying goal is robust, efficient management of persistent data. Let’s review some of the SQL terms used in this book. You use SQL as a data definition language ( DDL) to create a database schema with CREATE and ALTER statements. After creating tables ( and indexes, sequences, and so on) , you use SQL as a data manipulation language ( DML) . With DML, you execute SQL operation s th at man ipulate an d retrieve data. Th e man ipulation operation s in clude insertion , update, and deletion . You retrieve data by executing queries with restriction , projection, and join operation s ( in cludin g the Cartesian product) . For efficien t reportin g, you use SQL to group, order, an d aggregate data in arbitrary ways. You can even n est SQL statements inside each other; this technique is called subselecting. You have probably used SQL for man y years an d are familiar with th e basic operation s an d statemen ts written in th is lan guage. Still, we kn ow from our own experien ce th at SQL is sometimes hard to remember and that some terms vary in usage. To understand th is book, we h ave to use th e same terms an d concepts; so, we advise you to read appen dix A if an y of th e terms we’ve mentioned are new or unclear. SQL kn owledge is man datory for soun d Java database application developmen t. If you n eed more material, get a copy of th e excellen t book SQL T uning by Dan Tow [ Tow 2003] . Also read An Introduction to Database Systems [ Date 2004] for the theory, concepts, an d ideals of ( relation al) database systems. Although the relational
What is persistence?
5
database is on e part of ORM, th e oth er part, of course, con sists of th e objects in your Java application that need to be persisted to the database using SQL. 1 .1 .3 Using SQL in Java
Wh en you work with an SQL database in a Java application , th e Java code issues SQL statemen ts to th e database via th e Java DataBase Connectivity ( JDBC) API . Th e SQL itself might have been written by han d and embedded in th e Java code, or it migh t h ave been gen erated on th e fly by Java code. You use th e JDBC API to bin d arguments to query parameters, initiate execution of the query, scroll th rough th e quer y result table, retrieve values from th e result set, an d so on . Th ese are lowlevel data access tasks; as application developers, we’re more in terested in th e busin ess problem th at requires th is data access. It isn ’t clear th at we sh ould be concerning ourselves with such tedious, mechanical details. What we’d really like to be able to do is write code that saves and retrieves complex objects—th e in stan ces of our classes—to and from the database, relieving us of this low-level drudgery. Since the data access tasks are often so tedious, we h ave to ask: Are th e relation al data model an d ( especially) SQL th e righ t ch oices for persisten ce in objectoriented applications? We answer this question immediately: Yes! Th ere are man y reasons why SQL databases dominate the computing industry. Relational database management systems are the only proven data man agemen t tech n ology an d are almost always a requirement in an y Java project. However, for th e last 15 years, developers h ave spoken of a paradigm mismatch. This mismatch explains why so much effort is expen ded on persisten ce-related concerns in every enterprise project. Th e paradigms referred to are object modeling and relational modeling, or perhaps object-orien ted programmin g an d SQL. Let’s begin our exploration of the mismatch problem by askin g wh at persistence means in the context of object-oriented application development. First we’ll widen the simplistic definition of persistence stated at the begin n in g of th is section to a broader, more mature understanding of what is involved in maintaining and using persistent data. 1 .1 .4 Persistence in object-oriented applications
In an object-orien ted application , persisten ce allows an object to outlive th e process th at created it. Th e state of th e object may be stored to disk an d an object with the same state re-created at some poin t in th e future. Th is application isn ’t limited to sin gle objects—en tire graph s of in tercon n ected objects may be made persistent and later re-created in a new process. Most objects
6
CHAPTER 1
Understanding object/ relational persistence
aren’t persisten t; a transient object h as a limited lifetime th at is boun ded by th e life of the process th at in stan tiated it. Almost all Java applications contain a mix of persistent and transient objects; hen ce we n eed a subsystem that manages our persistent data. Modern relation al databases provide a structured representation of persistent data, enabling sorting, searching, and aggregation of data. Database management systems are responsible for managing concurren cy an d data in tegrity; th ey’re respon sible for sh arin g data between multiple users and multiple applications. A database management system also provides data-level security. When we discuss persistence in this book, we’re thinking of all these things: ■
Storage, organ ization , an d retrieval of structured data
■
Concurrency and data integrity
■
Data sharing
In particular, we’re th in kin g of th ese p roblems in th e con text of an object-oriented application that uses a domain model. An application with a domain model doesn ’t work directly with th e tabular representation of th e busin ess en tities; th e application h as its own , object-orien ted model of the business entities. If the database has ITEM an d BID tables, th e Java application defines Item an d Bid classes. Then, in stead of directly workin g with th e rows an d column s of an SQL result set, the busin ess logic in teracts with th is object-oriented domain model and its runtime realization as a graph of interconnected objects. The business logic is never executed in the database ( as an SQL stored procedure) , it’s implemen ted in Java. Th is allows busin ess logic to make use of soph isticated object-orien ted con cepts such as in h eritan ce an d polymorph ism. For example, we could use wellknown design pattern s such as Strategy, Mediator, an d Composite [ GOF 1995] , all of which depend on polymorphic method calls. Now a caveat: Not all Java application s are design ed th is way, n or sh ould th ey be. Simple application s migh t be much better off with out a domain model. SQL an d th e JDBC API are perfectly serviceable for dealin g with pure tabular data, an d the n ew JDBC RowSet ( Sun JCP, JSR 114) makes CRUD operations even easier. Working with a tabular representation of persistent data is straightforward and well understood. However, in th e case of applications with nontrivial business logic, the domain model helps to improve code reuse and maintainability significantly. We focus on applications with a domain model in th is book, sin ce Hibern ate an d ORM in general are most relevant to this kind of application.
The paradigm mismatch
7
If we con sider SQL and relational databases again, we finally observe the mismatch between the two paradigms. SQL operation s such as projection an d join always result in a tabular representation of th e resultin g data. Th is is quite differen t th an th e graph of in tercon n ected objects used to execute the business logic in a Java application! These are fundamentally different models, not just different ways of visualizing the same model. With this realization, we can begin to see the problems—some well understood and some less well understood—that must be solved by an application th at combin es both data represen tation s: an object-oriented domain model and a persistent relational model. Let’s take a closer look.
1.2 The paradigm mismatch Th e p arad igm m ism atch can be broken down into several parts, which we’ll exam1..* BillingDetails User in e on e at a time. Let’s start our exploration with a simple example th at is problem Figure 1 .1 A simple UM L class diagram of the user and billing details entities free. Then, as we build on it, you’ll begin to see th e mismatch appear. Suppose you h ave to design an d implement an online e-commerce application. In th is application , you’d n eed a class to represen t in formation about a user of th e system, and another class to represen t in formation about th e user’s billin g details, as shown in figure 1.1. Looking at this diagram, you see that a User h as man y BillingDetails. You can navigate the relationship between the classes in both directions. To begin with, the classes represen tin g th ese en tities migh t be extremely simple: public class User { private String userName; private String name; private String address; private Set billingDetails; // accessor methods (get/set pairs), business methods, etc. ... } public class BillingDetails { private String accountNumber; private String accountName; private String accountType; private User user;
8
CHAPTER 1
Understanding object/ relational persistence //methods, get/set pairs... ... }
Note th at we’re on ly in terested in th e state of th e en tities with regard to persisten ce, so we’ve omitted th e implemen tation of property accessors an d busin ess meth ods ( such as getUserName() or billAuction()) . It’s quite easy to come up with a good SQL schema design for this case: create table USER ( USERNAME VARCHAR(15) NOT NULL PRIMARY KEY, NAME VARCHAR(50) NOT NULL, ADDRESS VARCHAR(100) ) create table BILLING_DETAILS ( ACCOUNT_NUMBER VARCHAR(10) NOT NULL PRIMARY Key, ACCOUNT_NAME VARCHAR(50) NOT NULL, ACCOUNT_TYPE VARCHAR(2) NOT NULL, USERNAME VARCHAR(15) FOREIGN KEY REFERENCES USER )
Th e r elatio n sh ip between th e two en tities is r ep r esen ted as th e for eign key, USERNAME, in BILLING_DETAILS. For th is simple object model, th e object/ relation al mismatch is barely in eviden ce; it’s straigh tfor ward to write JDBC code to in sert,
update, and delete information about user and billing details. Now, let’s see what happens when we consider something a little more realistic. The paradigm mismatch will be visible wh en we add more en tities an d en tity relation sh ips to our application . Th e most glarin gly obvious problem with our current implementation is that we’ve modeled an address as a simple String value. In most systems, it’s n ecessary to store street, city, state, coun try, an d ZIP code information separately. Of course, we could add these properties directly to the User class, but since it’s highly likely th at oth er classes in th e system will also carry address in formation , it makes more sense to create a separate Address class. The updated object model is shown in figure 1.2. Should we also add an ADDRESS table? Not n ecessarily. It’s common to keep address information in the USER table, in in dividual column s. Th is design is likely to perform better, sin ce we don ’t require a table join to retrieve th e user an d address in a sin gle query. Th e n icest solution migh t even be to create a user-defin ed Address Figure 1 .2
User
The User has an Address.
1..*
BillingDetails
The paradigm mismatch
9
SQL data type to represen t addresses an d to use a sin gle column of th at n ew type in th e USER table instead of several new columns.
Basically, we h ave th e ch oice of addin g either several columns or a single column ( of a n ew SQL data type) . This is clearly a problem of granularity. 1 .2 .1 The problem of granularity
Gran ularity refers to th e relative size of th e objects you’re workin g with . Wh en we’re talkin g abou t Java objects an d d atabase tables, th e gran u larity p roblem mean s persistin g objects th at can h ave various kin ds of gran ularity to tables an d columns that are inherently limited in granularity. Let’s return to our example. Adding a new data type to store Address Java objects in a single column to our database catalog soun ds like th e best approach . After all, a new Address type ( class) in Java an d a n ew ADDRESS SQL data type sh ould guarantee interoperability. However, you’ll fin d various problems if you ch eck th e support for user-defin ed column types ( UDT ) in today’s SQL database managemen t systems. UDT support is on e of a n umber of so-called object-relational extensions to traditional SQL. Un fortun ately, UDT support is a somewhat obscure feature of most SQL database management systems and certainly isn’t portable between different systems. Th e SQL stan dard supports user-defin ed data types, but very poorly. For th is reason and ( whatever) other reasons, use of UDT s isn ’t common practice in th e industry at this time—and it’s unlikely that you’ll encounter a legacy schema that makes exten sive use of UDT s. We th erefore can ’t store objects of our n ew Address class in a single new column of an equivalent user-defined SQL data type. Our solution for this problem has several column s, of ven dor-defin ed SQL types ( such as boolean , n umeric, an d strin g data types) . Considering the granularity of our tables again, the USER table is usually defined as follows: create table USER ( USERNAME VARCHAR(15) NOT NULL PRIMARY KEY, NAME VARCHAR(50) NOT NULL, ADDRESS_STREET VARCHAR(50), ADDRESS_CITY VARCHAR(15), ADDRESS_STATE VARCHAR(15), ADDRESS_ZIPCODE VARCHAR(5), ADDRESS_COUNTRY VARCHAR(15) )
This leads to the following observation: Classes in our domain object model come in a range of different levels of granularity—from coarse-grain ed en tity classes like
10
CHAPTER 1
Understanding object/ relational persistence
User, to fin er grain ed classes like Address, righ t down to simple String-valued properties such as zipcode.
In con trast, just two levels of gran ularity are visible at the level of the database: tables such as USER, along with scalar columns such as ADDRESS_ZIPCODE. Th is obviously isn’t as flexible as our Java type system. Many simple persistence mechanisms fail to recognize this mismatch and so end up forcing the less flexible representation upon the object model. We’ve seen countless User classes with properties named zipcode! It turn s out th at th e gran ularity problem isn’t especially difficult to solve. Indeed, we probably wouldn’t even list it, were it not for the fact that it’s visible in so many existin g systems. We describe the solution to this problem in chapter 3, section 3.5, “Fin e-grain ed object models.” A much more difficult and interesting problem arises when we consider domain object models that use inheritance, a feature of object-oriented design we might use to bill the users of our e-commerce application in new and interesting ways. 1 .2 .2 The problem of subtypes
In Java, we implemen t in h eritan ce usin g super- an d subclasses. To illustrate wh y th is can presen t a mismatch problem, let’s con tin ue to build our example. Let’s ad d to ou r e-com m erce ap p lication so th at we n ow can accep t n ot on ly ban k accoun t billin g, but also credit an d debit cards. We th erefore h ave several meth od s to bill a u ser accou n t. Th e m ost n atu ral way to reflect th is ch an ge in ou r object model is to use in h eritan ce for th e BillingDetails class. We migh t h ave an abstract BillingDetails superclass alon g with several con crete subclasses: CreditCard, DirectDebit, Cheque, an d so on . Each of th ese subclasses will define slightly different data ( an d completely differen t fun ction ality that acts upon that data) . The UML class diagram in figure 1.3 illustrates th is object model. We notice immediately that SQL provides no direct support for inheritance. We can ’t declare th at a CREDIT_CARD_DETAILS table is a subtype of BILLING_DETAILS by writin g, say, CREATE TABLE CREDIT_CARD_DETAILS EXTENDS BILLING_DETAILS (...).
Figure 1 .3 Using inheritance for different billing strategies
The paradigm mismatch
11
In chapter 3, section 3.6, “Mapping class in heritan ce,” we discuss h ow object/ relational mapping solutions such as Hibernate solve the problem of persisting a class h ierarch y to a database table or tables. This problem is now quite well understood in the community, an d most solutions support approximately th e same fun ction ality. But we aren ’t quite fin ish ed with in h eritan ce—as soon as we in troduce inheritance into the object model, we have the possibility of polymorphism. The User class h as an association to th e BillingDetails superclass. This is a polymorphic association . At run time, a User object migh t be associated with an in stan ce of an y of th e subclasses of BillingDetails. Similarly, we’d like to be able to write queries that refer to the BillingDetails class and have the query return instances of its subclasses. Th is feature is called polymorphic queries. Sin ce SQL databases don ’t provide a n otion of inheritance, it’s hardly surprising th at th ey also lack an obvious way to represen t a polymorph ic association . A stan dard foreign key constraint refers to exactly one table; it isn’t straightforward to define a foreign key that refers to multiple tables. We might explain this by saying that Java ( and other object-oriented languages) is less strictly typed th an SQL. Fortun ately, two of th e in h eritan ce mappin g solution s we sh ow in ch apter 3 are design ed to accommodate th e representation of polymorphic associations and efficient execution of polymorph ic queries. So, th e mismatch of subtypes is on e in which the inheritance structure in your Java model must be persisted in an SQL database th at doesn ’t offer an in h eritan ce strategy. Th e n ext aspect of th e mismatch problem is th e issue of object identity. You probably n oticed th at we defin ed USERNAME as th e primary key of our USER table. Was th at a good ch oice? Not really, as you’ll see n ext. 1 .2 .3 The problem of identity
Alth ough th e problem of object identity might not be obvious at first, we’ll encounter it often in ou r growin g an d exp an d in g exam p le e-com m erce system . Th is problem can be seen when we con sider two objects ( for example, two Users) an d ch eck if th ey’re iden tical. Th ere are th ree ways to tackle th is problem, two in th e Java world an d on e in our SQL database. As expected, th ey work togeth er on ly with some help. Java objects define two different notions of sameness: ■
Object identity ( roughly equivalent to memory location, checked with a==b)
■
Equality as determin ed by the implementation of the equals() meth od ( also called equality by value)
12
CHAPTER 1
Understanding object/ relational persistence
On the other hand, the identity of a database row is expressed as th e primary key valu e. As you ’ll see in section 3.4, “Un d er stan d in g object id en tity,” n eith er equals() n or == is n aturally equivalen t to th e primary key value. It’s common for several ( n on iden tical) objects to simultan eously represen t th e same row of th e d atabase. Fu rth erm ore, som e su btle d ifficulties are in volved in im p lem en tin g equals() correctly for a persistent class. Let’s discuss another problem related to database identity with an example. In our table definition for USER, we’ve used USERNAME as a primary key. Un fortun ately, this decision makes it difficult to change a usern ame: We’d n eed to update n ot on ly the USERNAME column in USER, but also th e foreign key column in BILLING_DETAILS. So, later in the book, we’ll recommend that you use surrogate keys wh erever possible. A surrogate key is a primary key column with n o mean in g to th e user. For example, we migh t ch an ge our table defin ition s to look like th is: create table USER ( USER_ID BIGINT NOT NULL PRIMARY KEY, USERNAME VARCHAR(15) NOT NULL UNIQUE, NAME VARCHAR(50) NOT NULL, ... ) create table BILLING_DETAILS ( BILLING_DETAILS_ID BIGINT NOT NULL PRIMARY KEY, ACCOUNT_NUMBER VARCHAR(10) NOT NULL UNIQUE, ACCOUNT_NAME VARCHAR(50) NOT NULL, ACCOUNT_TYPE VARCHAR(2) NOT NULL, USER_ID BIGINT FOREIGN KEY REFERENCES USER )
Th e USER_ID an d BILLING_DETAILS_ID column s con tain system-gen erated values. These columns were introduced purely for th e ben efit of th e relation al data model. How ( if at all) sh ould th ey be represen ted in th e object model? We’ll discuss th is question in section 3.4 an d fin d a solution with object/ relational mapping. In the context of persistence, identity is closely related to h ow th e system h an dles cach in g an d tran saction s. Differen t persisten ce solution s h ave ch osen various strategies, and this has been an area of con fusion . We cover all th ese in terestin g topics—and show how they’re related—in chapter 5. The skeleton e-commerce application we’ve designed and implemented has served our purpose well. We’ve identified the mismatch problems with mapping granularity, subtypes, and object identity. We’re almost ready to move on to other parts of th e application . But first, we n eed to discuss th e importan t con cept of associations—th at is, h ow th e relation sh ips between our classes are mapped and handled. Is th e foreign key in the database all we n eed?
The paradigm mismatch
13
1 .2 .4 Problems relating to associations
In our object model, association s represen t th e relation sh ips between en tities. You remember th at th e User, Address, an d BillingDetails classes are all associated. Un like Address, BillingDetails stan ds on its own . BillingDetails objects are stored in their own table. Association mapping and the management of entity associations are central concepts of any object persistence solution. Object-oriented languages represent associations using object references and collections of object references. In the relation al world, an association is represen ted as a foreign key column , with copies of key values in several tables. Th ere are subtle differences between the two representations. Object referen ces are in h eren tly direction al; th e association is from on e object to th e oth er. If an association between objects should be navigable in both directions, you must define the association twice, on ce in each of th e associated classes. You’ve already seen th is in our object model classes: public class User { private Set billingDetails; ... } public class BillingDetails { private User user; ... }
O n th e oth er h and, foreign key associations aren ’t by n ature direction al. In fact, navigation h as n o mean in g for a relation al data model, because you can create arbitrary data association s with table joins an d projection . Actually, it isn’t possible to determine the multiplicity of a unidirectional association by lookin g on ly at th e Java classes. Java association s may h ave many-to-many multiplicity. For example, our object model might have looked like this: public class User { private Set billingDetails; ... } public class BillingDetails { private Set users; ... }
Table associations on th e oth er hand, are always one-to-many or one-to-one. You can see th e multiplicity immediately by looking at the foreign key definition. The following is a on e-to-man y association ( or, if read in th at direction , a man y-to-on e) :
14
CHAPTER 1
Understanding object/ relational persistence USER_ID BIGINT FOREIGN KEY REFERENCES USER
These are one-to-one associations: USER_ID BIGINT UNIQUE FOREIGN KEY REFERENCES USER BILLING_DETAILS_ID BIGINT PRIMARY KEY FOREIGN KEY REFERENCES USER
If you wish to represen t a man y-to-many association in a relation al database, you must in troduce a n ew table, called a link table. Th is table doesn ’t appear an ywh ere in th e object model. For our example, if we con sider th e relation sh ip between a u ser an d th e u ser ’s billin g in form ation to be m an y-to-m an y, th e lin k table is defined as follows: CREATE TABLE USER_BILLING_DETAILS ( USER_ID BIGINT FOREIGN KEY REFERENCES USER, BILLING_DETAILS_ID BIGINT FOREIGN KEY REFERENCES BILLING_DETAILS PRIMARY KEY (USER_ID, BILLING_DETAILS_ID) )
We’ll discuss association mappin gs in great detail in ch apters 3 an d 6. So far, the issues we’ve considered are main ly structural. We can see th em by considering a purely static view of the system. Perhaps the most difficult problem in object persistence is a dynamic. It con cern s association s, an d we’ve already hinted at it when we drew a distinction between object graph navigation and table joins in section 1.1.4, “Persistence in object-orien ted application s.” Let’s explore th is significant mismatch problem in more depth. 1 .2 .5 The problem of object graph navigation
Th ere is a fun damen tal differen ce in th e way you access objects in Java an d in a relational database. In Java, wh en you access th e billin g in formation of a user, you call aUser.getBillingDetails().getAccountNumber(). Th is is th e most n atural way to access object-orien ted data an d is often described as walking the object graph. You n avigate from on e object to another, following associations between instances. Unfortunately, this isn’t an efficient way to retrieve data from an SQL database. The single most important thing to do to improve performance of data access code is to minimize the number of requests to the database. The most obvious way to do this is to minimize the number of SQL queries. ( Oth er ways in clude usin g stored procedures or the JDBC batch API.) Therefore, efficient access to relational data using SQL usually requires th e use of joins between the tables of interest. Th e n umber of tables in cluded in th e join determines the depth of the object graph you can navigate. For example, if we need to retrieve a User and aren’t interested in the user’s BillingDetails, we use th is simple query:
The paradigm mismatch
15
select * from USER u where u.USER_ID = 123
O n th e oth er h an d, if we n eed to retrieve th e same User an d th en subsequen tly visit each of the associated BillingDetails in stan ces, we use a differen t query: select * from USER u left outer join BILLING_DETAILS bd on bd.USER_ID = u.USER_ID where u.USER_ID = 123
As you can see, we n eed to kn ow wh at p ortion of th e object grap h we p lan to access when we retrieve the initial User, before we start navigating the object graph! On th e oth er h an d, an y object persistence solution provides functionality for fetch in g th e data of associated objects on ly wh en th e object is first accessed. However, th is piecemeal style of data access is fun damen tally in efficien t in th e con text of a relation al database, because it requires execution of one select statement for each node of the object graph. This is the dreaded n+1 selects problem. Th is mismatch in th e way we access objects in Java and in a relational database is perh aps th e sin gle most common source of performan ce problems in Java applications. Yet, although we’ve been blessed with innumerable books and magazine articles advisin g us to use StringBuffer for strin g con caten ation , it seems impossible to find any advice about strategies for avoiding the n+1 selects problem. Fortunately, Hibernate provides sophisticated features for efficiently fetching graphs of objects from th e database, tran sparen tly to th e application accessin g th e graph . We discuss these features in chapters 4 and 7. We n ow h ave a quite elaborate list of object/ relation al mismatch problems, and it will be costly to find solutions, as you might know from experience. This cost is often underestimated, and we th ink th is is a major reason for man y failed software projects. 1 .2 .6 The cost of the mismatch
Th e overall solution for th e list of mismatch problems can require a sign ifican t outlay of time an d effort. In our experien ce, th e main purpose of up to 30 percen t of th e Java application code written is to h an dle th e tedious SQL/ JDBC an d the manual bridging of the object/ relational paradigm mismatch. Despite all this effort, th e en d result still doesn ’t feel quite righ t. We’ve seen projects n early sin k due to the complexity and inflexibility of their database abstraction layers. On e of th e major costs is in th e area of modeling. The relational and object models must both en compass th e same busin ess entities. But an object-oriented purist will model these entities in a very differen t way th an an experien ced relation al data
16
CHAPTER 1
Understanding object/ relational persistence
modeler. The usual solution to this problem is to bend and twist the object model un til it match es th e un derlying relational technology. This can be done successfully, but on ly at th e cost of losin g some of th e advan tages of object orientation. Keep in min d th at relation al modelin g is un derpin n ed by relation al th eory. Object orien tation h as n o such rigorous math ematical defin ition or body of th eoretical work. So, we can’t look to mathematics to explain how we should bridge the gap between the two paradigms—there is no elegant transformation waiting to be discovered. ( Doing away with Java and SQL an d startin g from scratch isn’t considered elegant.) Th e domain modelin g mismatch problem isn’t the only source of the inflexibility and lost productivity that lead to h igh er costs. A furth er cause is th e JDBC API itself. JDBC an d SQL provide a statement- ( th at is, comman d-) orien ted approach to movin g data to an d from an SQL database. A structural relation sh ip must be specified at least th ree times ( Insert, Update, Select) , addin g to th e time required for design and implementation. The unique dialect for every SQL database doesn ’t improve th e situation . Recen tly, it h as been fash ion able to regard architectural or pattern-based models as a partial solution to th e mismatch problem. Hence, we have the entity bean compon en t model, th e data access object ( DAO ) pattern , an d oth er practices to implement data access. These approaches leave most or all of th e problems listed earlier to the application developer. To round out your understanding of object persisten ce, we n eed to discuss application architecture and the role of a persistence layer in typical application design .
1.3 Persistence layers and alternatives In a medium- or large-sized application, it usually makes sense to organize classes by con cern . Persisten ce is on e con cern . O th er con cern s are presen tation , workflow, and business logic. There are also the so-called “cross-cutting” concerns, which may be implemen ted gen erically—by framework code, for example. Typical crosscuttin g con cern s in clude loggin g, auth orization , an d tran saction demarcation . A typical object-oriented architecture comprises layers th at represen t th e concerns. It’s normal, and certainly best practice, to group all classes an d compon en ts respon sible for persisten ce in to a separate persistence layer in a layered system architecture. In this section, we first look at the layers of this type of architecture and why we use them. After th at, we focus on th e layer we’re most in terested in —th e persisten ce layer—an d some of the ways it can be implemented.
Persistence layers and alternatives
17
1 .3 .1 Layered architecture
A layered architecture defin es in ter faces between code th at implemen ts the various con cern s, allowin g a ch an ge to th e way on e con cern is implemen ted with out sign ifican t disruption to code in th e oth er layers. Layering also determines the kinds of in terlayer depen den cies that occur. Th e rules are as follows: ■
Layers communicate top to bottom. A layer is dependent only on the layer directly below it.
■
Each layer is un aware of an y oth er layers except for th e layer just below it.
Differen t application s group con cerns differen tly, so th ey defin e differen t layers. A typical, proven , h igh -level application arch itecture uses th ree layers, on e each for presen tation , busin ess logic, an d persisten ce, as sh own in figure 1.4. Let’s take a closer look at the layers and elements in the diagram: ■
Presentation layer—The user inter face logic is topmost. Code responsible for the presen tation an d con trol of page an d screen n avigation forms th e presentation layer.
■
Business layer—Th e exact form of th e n ext layer varies widely between applications. It’s generally agreed, however, that this business layer is responsible for implemen tin g an y busin ess rules or system requiremen ts th at would be un derstood by users as part of th e problem domain . In some systems, th is layer h as its own in tern al represen tation of the business domain entities. In oth ers, it reuses th e model defin ed by th e persisten ce layer. We revisit th is issue in chapter 3. Presentation Layer
Business Layer
Utility and Helper Classes
Persistence Layer
Database
Figure 1.4 A persistence layer is the basis in a layered architecture.
18
CHAPTER 1
Understanding object/ relational persistence
■
Persistence layer—The persistence layer is a group of classes an d compon en ts respon sible for data storage to, an d retrieval from, on e or more data stores. Th is layer n ecessarily in clu d es a m od el of th e bu sin ess d om ain en tities ( even if it’s only a metadata model) .
■
Database—The database exists outside the Java application . It’s th e actual, persistent representation of the system state. If an SQL database is used, th e database in cludes th e relation al sch ema an d possibly stored procedures.
■
Helper/ utility classes—Every application has a set of infrastructural helper or utility classes th at are used in ever y layer of th e application ( for example, Exception classes for error h an dlin g) . Th ese in frastructural elemen ts don ’t form a layer, sin ce th ey don ’t obey th e rules for in terlayer depen den cy in a layered arch itecture.
Let’s n ow take a brief look at th e various ways th e persistence layer can be implemented by Java applications. Don’t worry—we’ll get to O RM an d Hibernate soon . There is much to be learned by looking at other approaches. 1 .3 .2 Hand-coding a persistence layer with SQL/ JDBC
Th e most common approach to Java persisten ce is for application programmers to work directly with SQL an d JDBC. After all, developers are familiar with relation al database man agemen t systems, un derstan d SQL , an d kn ow h ow to work with tables an d foreign keys. Moreover, th ey can always use th e well-kn own an d widely used DAO design pattern to hide complex JDBC code and nonportable SQL from the business logic. The DAO pattern is a good one—so good that we recommend its use even with ORM ( see ch apter 8) . However, th e work involved in manually coding persistence for each domain class is con siderable, particularly wh en multiple SQL dialects are supported. Th is work usually en ds up consuming a large portion of the development effort. Furthermore, when requirements change, a hand-coded solution always requires more atten tion an d main ten an ce effort. So why not implement a simple ORM framework to fit th e specific requiremen ts of your project? The result of such an effort could even be reused in future projects. Many developers have taken this approach; numerous homegrown object/ relational persistence layers are in production systems today. However, we don’t recommend this approach. Excellent solutions already exist, not only the ( mostly expen sive) tools sold by commercial ven dors but also open source projects with free licenses. We’re certain you’ll be able to find a solution that meets your
Persistence layers and alternatives
19
requiremen ts, both busin ess an d tech n ical. It’s likely that such a solution will do a great deal more, and do it better, than a solution you could build in a limited time. Developmen t of a reason ably full-featured ORM may take many developers months. For example, Hibernate is 43,000 lines of code ( some of which is much more difficult than typical application code) , alon g with 12,000 lin es of un it test code. Th is migh t be more th an your application . A great man y details can easily be overlooked—as both th e auth ors kn ow from experien ce! Even if an existin g tool doesn ’t fully implemen t two or th ree of your more exotic requirements, it’s still probably n ot worth creatin g your own . An y ORM will handle the tedious common cases—the ones that really kill productivity. It’s okay th at you migh t n eed to h an dcode certain special cases; few applications are composed primarily of special cases. Don ’t fall for th e “Not In ven ted Here” syn drome an d start your own object/ relational mapping effort just to avoid the learn in g curve associated with th ird-party software. Even if you decide th at all th is ORM stuff is crazy, an d you wan t to work as close to th e SQL database as possible, oth er persisten ce frameworks exist th at don’t implemen t full ORM. For example, th e iBATIS database layer is an open source persistence layer that handles some of the more tedious JDBC code while lettin g developers h an dcraft the SQL. 1 .3 .3 Using serialization
Java h as a built-in persisten ce mech an ism: Serialization provides th e ability to write a graph of objects ( th e state of th e application ) to a byte-stream, wh ich may th en be p ersisted to a file or d atabase. Ser ialization is also u sed by Java’s Rem ote Method In vocation ( RMI) to achieve pass-by value seman tics for complex objects. An oth er usage of serialization is to replicate application state across n odes in a cluster of machines. Why not use serialization for the persistence layer? Unfortunately, a serialized graph of interconnected objects can only be accessed as a whole; it’s impossible to retrieve any data from the stream without deserializin g th e en tire stream. Th us, th e resulting byte-stream must be considered unsuitable for arbitrary search or aggregation. It isn’t even possible to access or update a single object or subgraph indepen den tly. Loadin g an d overwritin g an en tire object graph in each tran saction is no option for systems designed to support h igh con curren cy. Clearly, given current technology, serialization is inadequate as a persistence mechanism for high concurrency web and en terprise application s. It h as a particular n ich e as a suitable persisten ce mech an ism for desktop application s.
20
CHAPTER 1
Understanding object/ relational persistence
1 .3 .4 Considering EJB entity beans
In recen t years, En terprise JavaBean s ( EJBs) h ave been a recommen ded way of persistin g data. If you’ve been workin g in th e field of Java en terprise application s, you’ve probably worked with EJBs an d en tity bean s in particular. If you h aven ’t, don ’t worry—en tity bean s are rapidly declin in g in popularity. ( Man y of th e developer con cern s will be addressed in the new EJB 3.0 specification , h owever.) En tity bean s ( in th e curren t EJB 2.1 specification) are interesting because, in contrast to the other solutions mentioned here, they were created entirely by committee. Th e oth er solution s ( th e DAO pattern , serialization , an d ORM) were distilled from many years of experience; th ey represen t approach es th at h ave stood the test of time. Un surprisin gly, perh aps, EJB 2.1 en tity bean s h ave been a disaster in practice. Design flaws in th e EJB specification preven t bean-managed persistence ( BMP) entity beans from performing efficien tly. A margin ally more acceptable solution is container-managed persistence ( CMP) , at least sin ce some glarin g deficien cies of th e EJB 1.1 specification were rectified. Neverth eless, CMP doesn’t represent a solution to the object/ relational mismatch . Here are six reason s wh y: ■
CMP bean s are defin ed in on e-to-on e correspon den ce to th e tables of th e
relation al m od el. Th u s, th ey’re too coarse grain ed ; th ey m ay n ot take fu ll advan tage of Java’s rich typin g. In a sen se, CMP forces your domain model into first n ormal form. ■
On th e other h an d, CMP bean s are also too fine grained to realize th e stated goal of EJB: th e d efin ition of reu sable software com p on en ts. A reu sable compon en t sh ould be a very coarse-grain ed object, with an extern al in terface that is stable in the face of small ch an ges to th e database sch ema. ( Yes, we really did just claim that CMP en tity bean s are both too fin e grain ed an d too coarse grain ed!)
■
Alth ough EJBs may take advan tage of implemen tation in h eritance, en tity bean s don ’t support polymorph ic associations and queries, one of the definin g features of “true” ORM.
■
Entity beans, despite the stated goal of the EJB specification , aren ’t portable in practice. Capabilities of CMP en gin es var y widely between ven dors, an d the mappin g metadata is h igh ly ven dor-specific. Some projects h ave ch osen H ibern ate for th e sim p le reason th at H ibern ate ap p lication s are m u ch more portable between application servers.
Persistence layers and alternatives
21
■
Entity beans aren’t serializable. We find that we must define additional data transfer objects ( DTO s, also called value objects) wh en we n eed to tran sport data to a remote clien t tier. Th e use of fin e-grain ed meth od calls from th e client to a remote entity bean instance is not scalable; DTO s provide a way of batch in g remote data access. Th e DTO pattern results in the growth of parallel class h ierarch ies, wh ere each en tity of th e d om ain m od el is rep resen ted as both an en tity bean an d a DTO .
■
EJB is an intrusive model; it man dates an un n atural Java style an d makes
reuse of code outside a specific container extremely difficult. Th is is a h uge barrier to u n it test driven developmen t ( TDD ) . It even cau ses p roblem s in applications that require batch processin g or oth er offlin e fun ction s. We won ’t spen d more time discussin g th e pros an d con s of EJB 2.1 en tity bean s. After lookin g at th eir persisten ce capabilities, we’ve come to th e con clusion th at th ey aren ’t suitable for a full object mappin g. We’ll see what the new EJB 3.0 specification can im p r ove. Let’s tu rn to an oth er object p er sisten ce solu tion th at deserves some attention. 1 .3 .5 Object-oriented database systems
Sin ce we work with objects in Java, it would be ideal if th ere were a way to store th ose objects in a database with out h avin g to ben d an d twist th e object model at all. In th e mid-1990s, n ew object-orien ted database systems gain ed atten tion . An object-oriented database management system ( OODBMS) is more like an exten sion to th e application en viron men t th an an extern al data store. An OODBMS usually features a multitiered implemen tation , with th e backen d data store, object cach e, an d clien t application coupled tigh tly togeth er an d in teractin g via a proprietary n etwork protocol. Object-orien ted database developmen t begin s with th e top-down defin ition of h ost lan guage bin din gs th at add persisten ce capabilities to the programming language. Hence, object databases offer seamless in tegration in to th e object-orien ted application en viron men t. Th is is differen t from th e model used by today’s relational databases, wh ere in teraction with th e database occurs via an in termediate language ( SQL) . Analogously to ANSI SQL, th e stan dard query in terface for relation al databases, there is a stan dard for object database products. The Object Data Management Group ( ODMG) specification defin es an API, a query language, a metadata language, and host language bindings for C++, SmallTalk, and Java. Most object-
22
CHAPTER 1
Understanding object/ relational persistence
oriented database systems provide some level of support for th e ODMG stan dard, but to th e best of our kn owledge, there is n o complete implemen tation . Furthermore, a number of years after its release, an d even in version 3.0, th e specification feels immature and lacks a number of useful features, especially in a Javabased en viron men t. Th e ODMG is also n o lon ger active. More recen tly, th e Java Data Objects ( JDO ) specification ( published in April 2002) opened up new possibilities. JDO was driven by members of th e object-oriented database community and is now being adopted by object-oriented database products as the primary API, often in addition to th e existin g ODMG support. It remains to be seen if this new effort will see object-orien ted databases pen etrate beyon d CAD/ CAM ( computeraided design/ modeling) , scientific computin g, an d oth er n ich e markets. We won’t bother looking too closely into wh y object-orien ted database tech n ology hasn’t been more popular—we’ll simply observe that object databases haven’t been widely adopted an d th at it doesn ’t appear likely th at th ey will be in th e n ear future. We’re con fiden t th at th e overwh elmin g majority of developers will h ave far more opportunity to work with relational tech n ology, given th e curren t political realities ( predefined deployment environments) . 1 .3 .6 Other options
O f course, th ere are oth er kin ds of persisten ce layers. XML persisten ce is a variation on th e serialization th eme; th is approach addresses some of th e limitation s of byte-stream serialization by allowin g tools to access th e data structure easily ( but is itself subject to an object/ h ierarch ical im pedan ce mism atch ) . Furth ermore, th ere is n o addition al ben efit from th e XML, because it’s just an other text file format. You can use stored procedures ( even write th em in Java usin g SQLJ) an d m ove th e p r oblem in to th e d atabase tier. We’r e su re th ere are p len ty of oth er examples, but n on e of th em are likely to become popular in th e immediate future. Political constraints ( long-term investments in SQL databases) an d th e requirement for access to valuable legacy data call for a different approach. ORM may be the most practical solution to our problems.
1.4 Object/ relational mapping Now th at we’ve looked at th e altern ative tech n iques for object persisten ce, it’s time to in troduce th e solution we feel is th e best, and the one we use with Hibern ate: O RM. Despite its lon g h istor y ( th e first research papers were publish ed in th e late 1980s) , th e term s for O RM u sed by d evelop ers var y. Som e call it object
Object/ relational mapping
23
relational mapping, oth ers prefer th e simple object mapping. We exclusively use th e term object/ relational mapping an d its acron ym, O RM. Th e slash stresses th e mismatch problem that occurs wh en the two worlds collide. In th is section , we first look at wh at ORM is. Th en we en umerate th e problems th at a good O RM solution needs to solve. Finally, we discuss th e gen eral ben efits that ORM provides and why we recommen d th is solution . 1 .4 .1 What is ORM ?
In a nutshell, object/ relational mapping is th e automated ( an d tran sparen t) persisten ce of objects in a Java ap p lication to th e tables in a relation al d atabase, using metadata that describes the mapping between th e objects an d th e database. O RM, in essen ce, works by ( reversibly) tran sformin g data from on e represen tation to another. This implies certain performance penalties. However, if ORM is implemented as middleware, th ere are man y opportun ities for optimization th at wouldn ’t exist for a hand-coded persistence layer. A further overh ead ( at developmen t time) is th e provision and management of metadata that governs the transformation. But again, the cost is less than equivalent costs in volved in main tain in g a h an d-coded solution . An d even ODMG-compliant object databases require significant classlevel metadata. FAQ
Isn’t ORM a Visio plugin? Th e acron ym ORM can also mean object role modeling, an d th is term was in ven ted before object/ relational mappin g became relevan t. It describes a meth od for in formation an alysis, used in database modelin g, an d is primarily supported by Microsoft Visio, a graph ical modelin g tool. Database specialists use it as a replacemen t or as an addition to th e more popular entity-relationship modeling. However, if you talk to Java developers about ORM, it’s usually in th e con text of object/ relation al mappin g.
An ORM solution con sists of th e followin g four pieces: ■
An API for per forming basic CRUD operation s on objects of persisten t classes
■
A language or API for specifyin g queries th at refer to classes and properties of classes
■
A facility for specifying mapping metadata
■
A technique for the ORM implementation to interact with transactional objects to per form dirty ch eckin g, lazy association fetching, and other optimization fun ction s
24
CHAPTER 1
Understanding object/ relational persistence
We’re using th e term O RM to in clude an y persisten ce layer wh ere SQL is autogenerated from a metadata-based description. We aren ’t in cludin g persisten ce layers wh ere th e object/ relation al mappin g problem is solved man ually by developers h an d-codin g SQL an d usin g JDBC. With O RM, th e application in teracts with th e O RM API s an d th e domain model classes an d is abstracted from th e un derlyin g SQ L / JDBC . Dep en din g on th e featu res or th e p articu lar im p lem en tation , th e ORM runtime may also take on responsibility for issues such as optimistic lockin g and caching, relieving the application of these concerns entirely. Let’s look at the various ways ORM can be implemen ted. Mark Fussel [ Fussel 1997] , a researcher in the field of ORM, defined the following four levels of ORM quality. Pure relational
Th e wh ole application , in cludin g th e user in ter face, is design ed aroun d th e relational model and SQL-based relational operations. This approach , despite its deficien cies for large systems, can be an excellen t solution for simple application s wh ere a low level of code reuse is tolerable. Direct SQL can be fine-tuned in every aspect, but the drawbacks, such as lack of portability an d main tain ability, are significant, especially in the long run. Application s in th is category often make h eavy use of stored procedures, sh iftin g some of th e work out of th e busin ess layer an d in to th e database. Light object mapping
En tities are rep resen ted as classes th at are m ap p ed m an u ally to th e relation al tables. H an d -co d ed SQ L / JDBC is h id d en fr om th e b u sin ess logic u sin g wellkn own design pattern s. Th is approach is extremely widespread an d is successful for ap p lication s with a sm all n u m ber of en tities, or ap p lication s with gen eric, metadata-driven data models. Stored procedures migh t h ave a place in th is kin d of application . M edium object mapping
Th e application is design ed aroun d an object model. SQL is gen erated at build time using a code generation tool, or at runtime by framework code. Associations between objects are supported by the persistence mechanism, and queries may be specified using an object-oriented expression lan guage. Objects are cach ed by th e persisten ce layer. A great man y O RM products an d h omegrown persisten ce layers support at least this level of functionality. It’s well suited to medium-sized application s with som e com p lex tran saction s, p articu larly wh en p or tability between
Object/ relational mapping
25
differen t database products is im portan t. Th ese ap p lication s u su ally d on ’t u se stored procedures. Full object mapping
Full object mappin g supports soph isticated object modelin g: composition , in h eritan ce, polymorph ism , an d “persisten ce by reach ability.” Th e persisten ce layer implemen ts tran sparen t persisten ce; persisten t classes do not in h erit an y special base class or h ave to implemen t a special in ter face. Efficien t fetch in g strategies ( lazy an d eager fetch in g) an d cach in g strategies are implemented transparently to the application . Th is level of fun ction ality can hardly be achieved by a homegrown persistence layer—it’s equivalent to months or years of developmen t time. A n umber of commercial an d open source Java O RM tools h ave ach ieved th is level of quality. This level meets the definition of ORM we’re usin g in th is book. Let’s look at the problems we expect to be solved by a tool th at ach ieves full object mappin g. 1 .4 .2 Generic ORM problems
Th e following list of issues, which we’ll call the O/ R mapping problems, are th e fun damental problems solved by a full object/ relational mapping tool in a Java en viron m en t. Particu lar O RM tools m ay p rovid e extra fun ction ality ( for exam p le, aggressive caching) , but this is a reasonably exhaustive list of the conceptual issues th at are specific to object/ relation al mappin g: 1
What do persistent classes look like? Are they fine-grained JavaBeans? Or are th ey in stan ces of some ( coarser gran ularity) compon en t model like EJB? How transparent is the persistence tool? Do we have to adopt a programming model and conventions for classes of the business domain?
2
How is mapping metadata defined? Since the object/ relational transformation is govern ed en tirely by metadata, th e format an d defin ition of th is metadata is a centrally important issue. Should an ORM tool provide a GUI to man ipulate th e metadata graph ically? O r are th ere better approach es to metadata defin ition ?
3
How should we map class inheritance hierarchies? Th ere are several stan dard strategies. Wh at abou t p olym orp h ic association s, abstract classes, an d inter faces?
4
How do object identity and equality relate to database (primary key) iden tity? H ow d o we m ap in stan ces of p articu lar classes to p articu lar table rows?
26
CHAPTER 1
Understanding object/ relational persistence
5
How does the persistence logic interact at runtime with the objects of the business domain ? Th is is a p roblem of gen eric p rogram m in g, an d th ere are a n u m ber of solu tion s in clu d in g sou rce gen eration , ru n tim e reflection , runtime bytecode generation, and buildtime bytecode enhancement. The solution to th is problem migh t affect your build process ( but, preferably, shouldn’t otherwise affect you as a user) .
6
What is the lifecyle of a persistent object? Does the lifecycle of some objects depen d upon th e lifecycle of oth er associated objects? H ow do we tran slate th e lifecyle of an object to th e lifecycle of a database row?
7
What facilities are provided for sorting, searching, and aggregating? Th e application could do some of th ese th in gs in memor y. But efficien t use o f r e latio n al te ch n o lo gy r e q u ir e s th at th is wo r k so m e tim e s b e p e r formed by th e database.
8
How do we efficiently retrieve data with associations? Efficient access to relation al data is usually accomplish ed via table join s. O bject-orien ted applications usually access data by navigatin g an object graph . Two data access pattern s sh ould be avoided when possible: the n+1 selects problem, an d its complemen t, th e Cartesian product problem ( fetch in g too much data in a sin gle select) .
In ad d ition , two issu es are com m on to an y d ata-access tech n ology. Th ey also impose fun damen tal con strain ts on the design and architecture of an ORM: ■
Tran saction s an d con curren cy
■
Cache management ( and concurrency)
As you can see, a full object-mappin g tool n eeds to address quite a lon g list of issu es. We d iscu ss th e way H ibern ate m an ages th ese p roblem s an d d ata-access issues in chapters 3, 4, and 5, and we broaden th e subject later in th e book. By now, you sh ould be startin g to see th e value of ORM. In the next section, we look at some of the other benefits you gain when you use an ORM solution . 1 .4 .3 Why ORM ?
An O RM implemen tation is a complex beast—less complex th an an application server, but more complex th an a web application framework like Struts or Tapestry. Wh y sh ould we in troduce an oth er n ew complex in frastructural elemen t in to our system? Will it be worth it?
Object/ relational mapping
27
It will take us most of th is book to provide a complete answer to those questions. For th e impatien t, th is section provides a quick summary of the most compelling benefits. But first, let’s quickly dispose of a non-benefit. A supposed advan tage of ORM is th at it “sh ields” developers from “messy” SQL. This view holds that object-oriented developers can ’t be expected to un derstan d SQL or relation al databases well an d th at th ey fin d SQL somehow offensive. On the con trary, we believe th at Java developers must have a sufficient level of familiarity with—and appreciation of—relational modeling and SQL in order to work with ORM. ORM is an advan ced tech n ique to be used by developers wh o h ave already done it the hard way. To use Hibernate effectively, you must be able to view an d in terpret th e SQL statemen ts it issues an d un derstan d th e implication s for performan ce. Let’s look at some of the benefits of O RM and Hibernate. Productivity
Persisten ce-related code can be perh aps th e most tedious code in a Java application . Hibern ate elimin ates much of th e grun t work ( more th an you’d expect) an d lets you con cen trate on th e busin ess problem. No matter wh ich application development strategy you prefer—top-down, starting with a domain model; or bottomup, startin g with an existin g database sch ema—Hibern ate used togeth er with th e appropriate tools will significantly reduce developmen t time. M aintainability
Fewer lines of code ( LOC) makes the system more understandable since it emphasizes busin ess logic rath er th an plumbin g. Most importan t, a system with less code is easier to refactor. Automated object/ relational persistence substantially reduces LO C. O f course, coun tin g lin es of code is a debatable way of measurin g application complexity. However, there are other reasons that a Hibernate application is more maintainable. In systems with hand-coded persistence, an inevitable tension exists between th e relation al represen tation an d th e object model implemen tin g th e domain . Changes to on e almost always in volve ch an ges to the other. And often the design of one representation is compromised to accommodate th e existen ce of th e oth er. ( What almost always happens in practice is that the object model of the domain is compromised.) ORM provides a buffer between the two models, allowing more elegant use of object orientation on the Java side, and insulating each model from minor changes to the other.
28
CHAPTER 1
Understanding object/ relational persistence
Performance
A common claim is that hand-coded persisten ce can always be at least as fast, an d can often be faster, than automated persisten ce. Th is is true in th e same sen se th at it’s true th at assembly code can always be at least as fast as Java code, or a h an dwr itten p ar ser can always be at least as fast as a p arser gen er ated by YACC or ANTLR—in oth er words, it’s beside th e poin t. Th e un spoken implication of th e claim is that hand-coded persistence will per form at least as well in an actual application . But th is implication will be true on ly if th e effort required to implemen t at-least-as-fast h an d-coded persisten ce is similar to th e amoun t of effort in volved in utilizin g an automated solution . Th e really in terestin g question is, wh at h appen s wh en we con sider time an d budget con strain ts? Given a persisten ce task, man y optimizations are possible. Some ( such as query h in ts) are much easier to achieve with h an d-coded SQL/ JDBC. Most optimization s, h owever, are much easier to achieve with automated ORM. In a project with time constraints, hand-coded persisten ce usually allows you to make some optimization s, some of the time. Hibernate allows many more optimization s to be used all the time. Furthermore, automated persistence improves developer productivity so much that you can spen d more time h an d-optimizin g the few remaining bottlenecks. Finally, the people who implemented your ORM software probably had much more time to in vestigate performan ce optimization s th an you h ave. Did you know, for instance, that pooling PreparedStatement in stan ces results in a sign ificant performance increase for the DB2 JDBC driver but breaks th e In terBase JDBC driver? Did you realize th at updatin g on ly th e ch an ged column s of a table can be sign ifican tly faster for some databases but potentially slower for others? In your h an dcrafted solution , h ow easy is it to experimen t with th e impact of th ese various strategies? Vendor independence
An O RM abstracts your application away from th e un derlyin g SQL database an d SQL dialect. If th e tool supports a n umber of differen t databases ( most do) , th en th is con fers a certain level of portability on your application. You shouldn’t necessarily expect write on ce/ run an ywh ere, sin ce th e capabilities of databases differ an d ach ievin g full portability would require sacrificin g some of th e stren gth of th e more power ful platforms. Neverth eless, it’s usually much easier to develop a crossplatform application usin g O RM. Even if you don ’t require cross-platform operation, an ORM can still h elp mitigate some of th e risks associated with ven dor lock-
Summary
29
in . In addition , database in depen den ce h elps in developmen t scen arios wh ere developers use a ligh tweigh t local database but deploy for production on a different database.
1.5 Summary In th is ch apter, we’ve discussed th e con cept of object persisten ce an d th e importan ce of O RM as an implemen tation tech n ique. O bject persisten ce mean s th at in dividual objects can outlive th e application process; they can be saved to a data store an d be re-created at a later poin t in time. Th e object/ relation al mismatch comes in to play wh en th e data store is an SQL-based relation al database man agemen t system. For in stan ce, a graph of objects can ’t simply be saved to a database table; it m u st be d isassem bled an d p ersisted to colu m n s of p ortable SQ L d ata types. A good solution for th is problem is O RM, wh ich is especially h elpful if we consider richly typed Java domain models. A domain model represents the business en tities used in a Java application . In a layered system arch itecture, th e domain model is used to execute business logic in the business layer ( in Java, not in the database) . Th is busin ess layer commun icates with the persistence layer beneath in order to load and store the persistent objects of the domain model. O RM is the middleware in the persistence layer that manages the persisten ce. ORM isn ’t a silver bullet for all persisten ce tasks; its job is to relieve the developer of 95 percen t of object persisten ce work, such as writin g complex SQL statements with many table joins and copying values from JDBC result sets to objects or graphs of objects. A full-featured ORM middleware might provide database portability, certain optimization techniques like caching, an d oth er viable fun ction s th at aren ’t easy to hand-code in a limited time with SQL an d JDBC. It’s likely th at a better solution th an ORM will exist some day. We ( and many others) may have to rethink everything we know about SQL, persisten ce API stan dards, and application in tegration . Th e evolution of today’s systems into true relational database systems with seamless object-oriented integration remains pure speculation. But we can’t wait, and there is no sign th at an y of th ese issues will improve soon ( a multibillion -dollar in dustry isn ’t very agile) . ORM is th e best solution curren tly available, an d it’s a timesaver for developers facing the object/ relational mismatch every day.
Introducing and integrating Hibernate
This chapter covers ■
Hibe rnate in ac tio n with “ He llo Wo rld”
■
The Hibe rnate c o re pro gramming inte rfac e s
■
Inte gratio n with manage d and no n-manage d e nviro nme nts
■
Advanc e d c o nfiguratio n o ptio ns
30
“Hello World” with Hibernate
31
It’s good to un derstan d th e n eed for object/ relation al mappin g in Java application s, but you’re probably eager to see Hibern ate in action . We’ll start by sh owin g you a simple example that demonstrates some of its power. As you’re probably aware, it’s tradition al for a programmin g book to start with a “Hello World” example. In this ch apter, we follow th at tradition by in troducin g Hibernate with a relatively simple “Hello World” program. However, simply printing a message to a console window won’t be en ough to really demon strate Hibern ate. Instead, our program will store n ewly created objects in the database, update them, an d perform queries to retrieve them from the database. This chapter will form the basis for the subsequen t ch apters. In addition to th e can on ical “Hello World” example, we in troduce th e core Hibern ate APIs an d explain h ow to con figure Hibern ate in various runtime environments, such as J2EE application servers and stand-alone applications.
2.1 “Hello World” with Hibernate Hibern ate applications defin e persistent classes th at are “mapped” to database tables. O ur “H ello World” example con sists of on e class an d on e mappin g file. Let’s see wh at a simple persisten t class looks like, h ow th e mappin g is specified, an d some of th e th in gs we can do with in stan ces of th e persisten t class usin g Hibern ate. Th e objective of our sample application is to store messages in a database an d to retrieve them for display. The application has a simple persistent class, Message, which represents these printable messages. Our Message class is sh own in listin g 2.1. Listing 2 .1
Message.java: A simple persistent class
package hello; Identifier public class Message { attribute private Long id; private String text; private Message nextMessage; private Message() {} public Message(String text) { this.text = text; } public Long getId() { return id; } private void setId(Long id) { this.id = id; } public String getText() { return text;
Message text Reference to another Message
32
CHAPTER 2
Introducing and integrating Hibernate } public void setText(String text) { this.text = text; } public Message getNextMessage() { return nextMessage; } public void setNextMessage(Message nextMessage) { this.nextMessage = nextMessage; } }
Our Message class has three attributes: the identifier attribute, the text of the message, and a reference to another Message. The identifier attribute allows the application to access th e d atabase id en tity—th e p rim ar y key valu e—of a p ersisten t object. If two in stan ces of Message h ave th e same iden tifier value, th ey represen t th e same row in th e database. We’ve ch osen Long for th e type of our iden tifier attribute, but th is isn ’t a requiremen t. Hibern ate allows virtually an yth in g for th e identifier type, as you’ll see later. You may have noticed that all attributes of the Message class h ave JavaBean -style property accessor methods. The class also has a constructor with no parameters. The persisten t classes we use in our examples will almost always look someth in g like this. In stan ces of th e Message class may be managed ( made persisten t) by Hibern ate, but they don ’t have to be. Since the Message object doesn ’t implemen t an y Hibernate-specific classes or interfaces, we can use it like an y oth er Java class: Message message = new Message("Hello World"); System.out.println( message.getText() );
Th is code fragmen t does exactly wh at we’ve come to expect from “H ello World” applications: It prints "Hello World" to the console. It might look like we’re trying to be cu te h ere; in fact, we’re d em on stratin g an im portan t feature th at distin gu ish es H iber n ate from som e oth er p ersisten ce solu tion s, su ch as EJB en tity bean s. Our persisten t class can be used in any execution context at all—no special con tain er is n eeded. Of course, you came h ere to see Hibern ate itself, so let’s save a n ew Message to th e database: Session session = getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); Message message = new Message("Hello World"); session.save(message);
“Hello World” with Hibernate
33
tx.commit(); session.close();
Th is code calls th e H ibern ate Session an d Transaction in ter faces. ( We’ll get to that getSessionFactory() call soon .) It results in th e execution of someth in g similar to th e followin g SQL: insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID) values (1, 'Hello World', null)
Hold on —th e MESSAGE_ID column is bein g in itialized to a stran ge value. We didn ’t set th e id property of message an ywh ere, so we would expect it to be null, righ t? Actually, th e id property is special: It’s an identifier property—it h olds a gen erated u n iqu e valu e. ( We’ll d iscu ss h ow th e valu e is gen er ated later.) Th e valu e is assign ed to th e Message instance by Hibernate when save() is called. For th is example, we assume th at the MESSAGES table already exists. In ch apter 9, we’ll sh ow you h ow to use Hibern ate to automatically create th e tables your application needs, using just the information in the mappin g files. ( Th ere’s some more SQL you won ’t n eed to write by h an d!) Of course, we want our “Hello World” program to prin t th e message to th e con sole. Now that we have a message in the database, we’re ready to demon strate th is. The n ext example retrieves all messages from the database, in alphabetical order, and prints them: Session newSession = getSessionFactory().openSession(); Transaction newTransaction = newSession.beginTransaction(); List messages = newSession.find("from Message as m order by m.text asc"); System.out.println( messages.size() + " message(s) found:" ); for ( Iterator iter = messages.iterator(); iter.hasNext(); ) { Message message = (Message) iter.next(); System.out.println( message.getText() ); } newTransaction.commit(); newSession.close();
Th e literal strin g "from Message as m order by m.text asc" is a H ibern ate query, expressed in Hibernate’s own object-oriented Hibernate Query Language ( H QL) . Th is query is in tern ally tran slated in to th e followin g SQL wh en find() is called: select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID from MESSAGES m order by m.MESSAGE_TEXT asc
The code fragmen t prin ts 1 message(s) found: Hello World
34
CHAPTER 2
Introducing and integrating Hibernate
If yo u ’ve n ever u sed an O RM tool like H ib er n ate befor e, you wer e p r obably expectin g to see th e SQL statemen ts somewh ere in th e code or metadata. Th ey aren ’t there. All SQL is gen erated at run time ( actually at startup, for all reusable SQL statemen ts) . To allow this magic to occur, Hibern ate n eeds more in formation about h ow th e Message class should be made persistent. This information is usually provided in an XML mapping document. The mapping document defines, among other things, how properties of th e Message class map to column s of th e MESSAGES table. Let’s look at the mapping document in listing 2.2. Listing 2 .2
A simple Hibernate XM L mapping
Note that Hibernate 2.0
The mappin g documen t tells Hibern ate th at th e Message class is to be persisted to th e MESSAGES tab le , th at th e id e n tifie r p r o p e r ty m ap s to a co lu m n n am e d MESSAGE_ID, th at th e text property maps to a column n amed MESSAGE_TEXT, an d that the property n amed nextMessage is an association with many-to-one multiplicity th at maps to a column n amed NEXT_MESSAGE_ID. ( Don ’t worr y about th e oth er details for now.) As you can see, the XML documen t isn ’t difficult to un derstan d. You can easily write and maintain it by hand. In chapter 3, we discuss a way of gen eratin g th e
“Hello World” with Hibernate
35
XML file from comments embedded in the source code. Wh ich ever meth od you choose, Hibern ate h as en ough in formation to completely generate all the SQL
statements that would be needed to insert, update, delete, an d retrieve in stan ces of th e Message class. You no longer need to write these SQL statemen ts by h an d. NOTE
Man y Java developers h ave complain ed of th e “metadata h ell” th at accompan ies J2EE developmen t. Some h ave suggested a movemen t away from XML metadata, back to plain Java code. Although we applaud this suggestion for some problems, ORM represen ts a case wh ere text-based metadata really is n ecessary. Hibern ate h as sen sible defaults th at min imize typin g an d a mature documen t type defin ition th at can be used for auto-completion or validation in editors. You can even automatically gen erate metadata with various tools.
Now, let’s ch an ge our first message an d, wh ile we’re at it, create a n ew message associated with the first, as shown in listing 2.3. Listing 2 .3
Updating a message
Session session = getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); // 1 is the generated id of the first message Message message = (Message) session.load( Message.class, new Long(1) ); message.setText("Greetings Earthling"); Message nextMessage = new Message("Take me to your leader (please)"); message.setNextMessage( nextMessage ); tx.commit(); session.close();
This code calls three SQL statements inside the same transaction: select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID from MESSAGES m where m.MESSAGE_ID = 1 insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID) values (2, 'Take me to your leader (please)', null) update MESSAGES set MESSAGE_TEXT = 'Greetings Earthling', NEXT_MESSAGE_ID = 2 where MESSAGE_ID = 1
Notice h ow H ibern ate detected th e modification to th e text an d nextMessage properties of th e first message an d au tomatically updated th e database. We’ve taken advan tage of a Hibern ate feature called automatic dirty checking: Th is feature
36
CHAPTER 2
Introducing and integrating Hibernate
saves us th e effort of explicitly askin g H ibern ate to update th e database wh en we modify th e state of an object in side a tran saction . Similarly, you can see th at th e new message was made persistent when a referen ce was created from th e first message. Th is feature is called cascading save: It saves us th e effort of explicitly makin g the new object persistent by calling save(), as long as it’s reachable by an alreadypersisten t in stan ce. Also n otice th at th e orderin g of th e SQL statemen ts isn ’t th e same as th e order in wh ich we set property values. Hibern ate uses a soph isticated algorithm to determine an efficient ordering that avoids database foreign key constrain t violation s bu t is still su fficien tly p red ictable to th e u ser. Th is featu re is called transactional write-behind. If we run “Hello World” again , it prin ts 2 message(s) found: Greetings Earthling Take me to your leader (please)
This is as far as we’ll take the “Hello World” application . Now th at we fin ally h ave som e cod e u n d er ou r belt, we’ll take a step back an d p r esen t an over view of Hibernate’s main APIs.
2.2 Understanding the architecture Th e programmin g in ter faces are th e first th in g you h ave to learn about H ibernate in order to use it in the persistence layer of your application. A major objective of API d esign is to keep th e in ter faces between softwar e com p on en ts as n arrow as possible. In practice, h owever, O RM API s aren ’t especially small. Don ’t worry, th ough ; you don ’t h ave to un derstan d all th e Hibern ate in ter faces at on ce. Figure 2.1 illustrates th e roles of th e most importan t H ibernate in ter faces in th e busin ess an d persisten ce layers. We sh ow th e busin ess layer above th e persisten ce layer, sin ce th e busin ess layer acts as a clien t of th e persisten ce layer in a traditio n ally layer ed ap p licatio n . No te th at so m e sim p le ap p licatio n s m igh t n o t clean ly separate business logic from persisten ce logic; that’s okay—it merely simplifies th e diagram. Th e Hibern ate in terfaces sh own in figure 2.1 may be approximately classified as follows: ■
Inter faces called by applications to per form basic CRUD and querying operation s. Th ese in ter faces are th e main poin t of depen den cy of application busin ess/ con trol logic on H ibern ate. Th ey in clude Session, Transaction, and Query.
Understanding the architecture
37
■
Inter faces called by application infrastructure code to con figure Hibern ate, most importantly the Configuration class.
■
Callback in ter faces th at allow th e application to react to events occurring inside Hibernate, such as Interceptor, Lifecycle, and Validatable.
■
Inter faces that allow extension of Hibernate’s power ful mappin g fun ction ality, su ch as UserType , CompositeUserType , an d IdentifierGenerator. Th ese in ter faces are im plem en ted by application in frastructure code ( if necessary) .
H ibern ate makes use of existin g Java API s, in cludin g JDBC) , Java Transaction API ( JTA, an d Java Namin g an d Directory In ter face ( JNDI) . JDBC provides a rudimen tary level of abstraction of fun ction ality common to relational databases, allowing almost an y database with a JDBC driver to be supported by H ibern ate. JNDI an d JTA allow Hibernate to be integrated with J2EE application servers. In this section, we don’t cover the detailed semantics of Hibernate API meth ods, just th e role of each of th e primary in terfaces. You can fin d most of th ese in terfaces in th e package net.sf.hibernate. Let’s take a brief look at each interface in turn.
Figure 2 .1
High-level overview of the HIbernate API in a layered architecture
38
CHAPTER 2
Introducing and integrating Hibernate
2 .2 .1 The core interfaces
Th e five cor e in ter faces ar e u sed in ju st abou t ever y H iber n ate ap p lication . Usin g th ese in ter faces, you can store an d retrieve persisten t objects an d con trol transactions. Session interface
The Session inter face is the primary inter face used by Hibernate applications. An in stan ce of Session is ligh tweigh t an d is in expen sive to create an d destroy. Th is is importan t because your application will n eed to create an d destroy session s all th e time, perh aps on every request. Hibern ate session s are not threadsafe and should by design be used by only one thread at a time. The Hibern ate n otion of a session is something between connection and transaction . It may be easier to th in k of a session as a cach e or collection of loaded objects relating to a single unit of work. Hibernate can detect ch an ges to th e objects in th is unit of work. We sometimes call th e Session a persistence manager because it’s also the interface for persisten ce-related operation s such as storin g an d retrievin g objects. Note th at a Hibern ate session h as n oth in g to do with th e web-tier HttpSession. Wh en we use th e word session in this book, we mean the Hibernate session. We sometimes use user session to refer to th e HttpSession object. We describe the Session interface in detail in ch apter 4, section 4.2, “The persistence manager.” SessionFactory interface
Th e application obtain s Session in stan ces from a SessionFactory. Compared to the Session inter face, this object is much less exciting. The SessionFactory is certainly not lightweight! It’s intended to be shared among many application threads. There is typically a single SessionFactory for th e whole application—created durin g application in itialization , for example. However, if your application accesses multiple databases usin g Hibern ate, you’ll n eed a SessionFactory for each database. The SessionFactory caches generated SQL statements and other mapping metadata that Hibernate uses at runtime. It also holds cached data that has been read in one unit of work and may be reused in a future un it of work ( on ly if class and collection mappings specify that this second-level cache is desirable) .
Understanding the architecture
39
Configuration interface
Th e Configuration object is u sed to con figu re an d bootstrap H ibern ate. Th e application uses a Configuration instance to specify the location of mapping documen ts an d Hibern ate-specific properties and then create the SessionFactory. Even though the Configuration interface plays a relatively small part in the total scope of a Hibern ate application , it’s th e first object you’ll meet wh en you begin using Hibernate. Section 2.3 covers th e problem of con figurin g Hibern ate in some detail. Transaction interface
The Transaction in ter face is an option al API. Hibernate applications may choose n ot to use th is in ter face, in stead man agin g tran saction s in th eir own in frastructure code. A Transaction abstracts application code from th e un derlyin g tran saction implemen tation —wh ich migh t be a JDBC tran saction , a JTA UserTransaction, or even a Common O bject Request Broker Arch itecture ( CO RBA) tran saction — allowin g th e application to con trol tran saction boun daries via a con sisten t API . Th is h elps to keep H ibern ate application s portable between differen t kin ds of execution environments and containers. We use the Hibernate Transaction API th rough out th is book. Tran saction s an d the Transaction in terface are explain ed in ch apter 5. Query and Criteria interfaces
Th e Query in ter face allows you to per form queries again st th e database an d control h ow th e query is executed. Queries are written in H QL or in th e n ative SQL dialect of your database. A Query in stan ce is used to bin d query parameters, limit the number of results returned by the query, an d fin ally to execute th e query. The Criteria in terface is very similar; it allows you to create an d execute objectoriented criteria queries. To help make application code less verbose, Hibernate provides some shortcut methods on the Session interface that let you invoke a query in one line of code. We won ’t use th ese sh ortcuts in th e book; in stead, we’ll always use th e Query in terface. A Query instance is lightweight and can’t be used outside th e Session that created it. We describe th e features of th e Query in terface in ch apter 7.
40
CHAPTER 2
Introducing and integrating Hibernate
2 .2 .2 Callback interfaces
Callback inter faces allow the application to receive a n otification wh en someth in g in terestin g h appen s to an object—for example, wh en an object is loaded, saved, or deleted. H ibern ate applications don ’t n eed to implemen t th ese callbacks, but they’re useful for implemen tin g certain kin ds of gen eric fun ction ality, such as creating audit records. The Lifecycle an d Validatable in terfaces allow a persisten t object to react to events relatin g to its own persistence lifecycle. The persistence lifecycle is encompassed by an object’s CRUD operations. The Hibernate team was heavily influen ced by oth er ORM solution s th at h ave similar callback in terfaces. Later, th ey realized that having the persistent classes implement Hibernate-specific interfaces probably isn ’t a good idea, because doing so pollutes our persistent classes with nonportable code. Since these approaches are no longer favored, we don’t discuss th em in th is book. The Interceptor interface was introduced to allow the application to process callbacks without forcing the persistent classes to implemen t Hibern ate-specific APIs. Implementations of the Interceptor interface are passed to the persistent instances as parameters. We’ll discuss an example in ch apter 8. 2 .2 .3 Types
A fu n d am en tal an d ver y p ower fu l elem en t of th e arch itectu re is H ibern ate’s notion of a Type. A Hibern ate Type object maps a Java type to a database column type ( actually, th e type may span multiple column s) . All persisten t properties of persisten t classes, in cludin g association s, h ave a correspon din g H ibern ate type. This design makes Hibernate extremely flexible an d exten sible. There is a rich range of built-in types, coverin g all Java primitives an d man y JDK classes, including types for java.util.Currency, java.util.Calendar, byte[], and java.io.Serializable. Even better, Hibernate supports user-defin ed custom types. The interfaces UserType and CompositeUserType are provided to allow you to add your own types. You can use this feature to allow commonly used application classes such as Address, Name, or MonetaryAmount to be h an dled con veniently and elegantly. Custom types are considered a central feature of Hibernate, and you’re encouraged to put them to new and creative uses! We explain Hibernate types and user-defined types in chapter 6, section 6.1, “Un derstan din g th e Hibern ate type system.”
Basic configuration
41
2 .2 .4 Extension interfaces
Much of the functionality that Hibernate provides is con figurable, allowin g you to choose between certain built-in strategies. When the built-in strategies are insufficien t, H ibern ate will usually let you plug in your own custom implemen tation by implemen tin g an in ter face. Exten sion poin ts in clude: ■
Primary key gen eration ( IdentifierGenerator in ter face)
■
SQL dialect support ( Dialect abstract class)
■
Cach in g strategies ( Cache an d CacheProvider in ter faces)
■
JDBC con n ection man agemen t ( ConnectionProvider in ter face)
■
Tran saction man agemen t ( TransactionFactory, Transaction, and TransactionManagerLookup in ter faces)
■
ORM strategies ( ClassPersister inter face hierarchy)
■
Property access strategies ( PropertyAccessor in ter face)
■
Proxy creation ( ProxyFactory inter face)
Hibern ate sh ips with at least on e implementation of each of the listed inter faces, so you don ’t usually n eed to start from scratch if you wish to exten d th e built-in fun ction ality. Th e source code is available for you to use as an example for your own implemen tation . By n ow you can see th at before we can start writing any code that uses Hibernate, we must an swer th is question: How do we get a Session to work with?
2.3 Basic configuration We’ve looked at an example application an d examin ed H ibern ate’s core in terfaces. To use H ibern ate in an application , you n eed to kn ow h ow to con figure it. H ibern ate can be con figured to run in almost an y Java application an d developmen t en viron men t. Gen erally, H ibern ate is used in two- and th ree-tiered clien t/ server applications, with Hibernate deployed only on the server. The client application is u su ally a web browser, bu t Swin g an d SWT clien t ap p lication s aren ’t u n com m on . Alth ou gh we con cen trate on m u ltitiered web ap p lication s in th is book, our explan ation s apply equally to oth er arch itectures, such as comman dlin e ap p lication s. It’s im p ortan t to u n d erstan d th e d ifferen ce in con figu rin g Hibern ate for man aged an d n on -man aged en viron men ts: ■
Managed environment— Pools resources such as database con n ection s an d allows transaction boundaries and security to be specified declaratively ( that
42
CHAPTER 2
Introducing and integrating Hibernate
is, in metadata) . A J2EE application server such as JBoss, BEA WebLogic, or IBM WebSphere implements the standard ( J2EE-specific) managed environmen t for Java. ■
Non-managed environment— Provides basic concurrency management via th read p oolin g. A ser vlet con tain er like Jetty or Tom cat p rovid es a n on managed server environment for Java web application s. A stan d-alon e desktop or com m an d-lin e ap p lication is also con sidered n on -m an aged. Non man aged en viron men ts don ’t provide automatic tran saction or resource management or security infrastructure. The application itself manages database con n ection s an d demarcates tran saction boun daries.
Hibernate attempts to abstract the environment in which it’s deployed. In the case of a non-managed environment, Hibernate handles transactions and JDBC connections ( or delegates to application code that h an dles th ese con cern s) . In man aged en viron m en ts, H ibern ate in tegrates with con tain er-m an aged tran saction s an d datasources. Hibernate can be configured for deployment in both environments. In both man aged an d n on -man aged en viron men ts, th e first th in g you must do is start Hibernate. In practice, doing so is very easy: You have to create a SessionFactory from a Configuration. 2 .3 .1 Creating a SessionFactory
In order to create a SessionFactory, you first create a single instance of Configuration durin g application in itialization an d use it to set th e location of th e mapp in g files. O n ce con figu red , th e Configuration in stan ce is u sed to create th e SessionFactory. After the SessionFactory is created, you can discard the Configuration class. Th e followin g code starts Hibern ate: Configuration cfg = new Configuration(); cfg.addResource("hello/Message.hbm.xml"); cfg.setProperties( System.getProperties() ); SessionFactory sessions = cfg.buildSessionFactory();
Th e location of th e mappin g file, Message.hbm.xml, is relative to th e root of th e application classpath . For example, if th e classpath is th e curren t director y, th e Message.hbm.xml file must be in th e hello directory. XML mappin g files must be placed in the classpath . In th is example, we also use th e system properties of th e virtual machine to set all other configuration options ( which might have been set before by application code or as startup option s) .
Basic configuration
M ETHOD CHAINING
43
Method chaining is a programmin g style supported by man y H ibern ate in terfaces. This style is more popular in Smalltalk th an in Java an d is con sidered by some people to be less readable an d more difficult to debug than th e more accepted Java style. However, it’s very con ven ien t in most cases. Most Java developers declare setter or adder meth ods to be of type void, mean in g they return n o value. In Smalltalk, wh ich h as no void type, setter or adder meth ods usually return th e receivin g object. Th is would allow us to rewrite th e previous code example as follows: SessionFactory sessions = new Configuration() .addResource("hello/Message.hbm.xml") .setProperties( System.getProperties() ) .buildSessionFactory();
Notice th at we didn ’t n eed to declare a local variable for th e Configuration. We use th is style in some code examples; but if you don ’t like it, you don ’t n eed to use it yourself. If you do use th is codin g style, it’s better to write each meth od in vocation on a differen t lin e. O th erwise, it migh t be difficult to step th rough th e code in your debugger.
By convention, Hibernate XML mapping files are named with the .hbm.xml extension. An other conven tion is to h ave on e mappin g file per class, rath er th an h ave all your mappin gs listed in on e file ( wh ich is possible but con sidered bad style) . O ur “H ello World” exam ple h ad on ly on e p ersisten t class, bu t let’s assu m e we have multiple persistent classes, with an XML mapping file for each. Where should we put these mapping files? The Hibern ate documen tation recommen ds th at th e mappin g file for each persistent class be placed in the same directory as that class. For instance, the mapping file for the Message class would be placed in th e hello directory in a file n amed Message.hbm.xml. If we h ad an oth er persisten t class, it would be defin ed in its own mapping file. We suggest that you follow this practice. The monolithic metadata files en couraged by some frameworks, such as the struts-config.xml foun d in Struts, are a major con tributor to “metadata hell.” You load multiple mapping files by calling addResource() as often as you have to. Alternatively, if you follow the conven tion just described, you can use th e meth od addClass(), passin g a persisten t class as th e parameter: SessionFactory sessions = new Configuration() .addClass(org.hibernate.auction.model.Item.class) .addClass(org.hibernate.auction.model.Category.class) .addClass(org.hibernate.auction.model.Bid.class) .setProperties( System.getProperties() ) .buildSessionFactory();
44
CHAPTER 2
Introducing and integrating Hibernate
The addClass() meth od assumes th at th e n ame of th e mappin g file en ds with th e .hbm.xml exten sion an d is deployed alon g with the mapped class file. We’ve demonstrated the creation of a single SessionFactory, wh ich is all th at most application s n eed. If an oth er SessionFactory is needed—if there are multiple databases, for example—you repeat the process. Each SessionFactory is then available for one database and ready to produce Sessions to work with th at particular database and a set of class mappings. Of course, there is more to con figurin g Hibern ate th an just poin tin g to mapping documents. You also need to specify how database connections are to be obtained, along with various other settings that affect the behavior of Hibernate at runtime. The multitude of configuration properties may appear overwhelming ( a complete list appears in the Hibernate documentation) , but don’t worry; most defin e reason able default values, an d on ly a h an dful are common ly required. To specify configuration options, you may use any of the following techniques: ■
Pass an instance of java.util.Properties to Configuration.setProperties().
■
Set system properties using java -Dproperty=value.
■
Place a file called hibernate.properties in the classpath.
■
Include elemen ts in hibernate.cfg.xml in the classpath.
Th e first an d secon d option s are rarely used except for quick testin g an d prototypes, but most application s n eed a fixed con figuration file. Both th e hibernate. properties and the hibernate.cfg.xml files provide the same function: to configure Hibern ate. Wh ich file you ch oose to use depen ds on your syn tax preferen ce. It’s even possible to mix both option s an d have different settings for development an d deploymen t, as you’ll see later in th is ch apter. A rarely used alternative option is to allow the application to provide a JDBC Connection wh en it open s a Hibern ate Session from th e SessionFactory ( for example, by calling sessions.openSession(myConnection)) . Using this option means that you don’t have to specify any database connection properties. We don’t recommend this approach for new applications that can be configured to use the environment’s database connection in frastructure ( for example, a JDBC connection pool or an application server datasource) . Of all the configuration options, database connection settings are the most important. They differ in managed and non-managed environments, so we deal with the two cases separately. Let’s start with non-managed.
Basic configuration
45
2 .3 .2 Configuration in non-managed environments
In a n on -man aged en viron men t, such as a ser vlet con tain er, th e application is responsible for obtainin g JDBC con n ection s. Hibern ate is part of th e application , so it’s respon sible for gettin g th ese con n ections. You tell Hibernate how to get ( or create n ew) JDBC con n ection s. Gen erally, it isn ’t advisable to create a con n ection each time you want to interact with the database. Instead, Java applications should use a pool of JDBC connections. There are three reasons for using a pool: ■
Acquirin g a n ew con n ection is expen sive.
■
Main tain in g man y idle con n ection s is expen sive.
■
Creating prepared statements is also expen sive for some drivers.
Figure 2.2 sh ows th e role of a JDBC con n ection pool in a web application run time en viron men t. Sin ce th is n on -man aged en viron men t doesn ’t implemen t con n ection poolin g, th e application must implemen t its own poolin g algorith m or rely upon a th ird-party library such as th e open source C3P0 con n ection pool. With out Hibern ate, th e application code usually calls th e con n ection pool to obtain JDBC connections and execute SQL statemen ts. With Hibern ate, th e picture ch an ges: It acts as a client of the JDBC connection pool, as shown in figure 2.3. The application code uses th e Hibern ate Session an d Query APIs for persistence operations and only has to manage database transactions, ideally usin g th e Hibern ate Transaction API. Using a connection pool
H ibern ate defin es a plugin arch itecture th at allows in tegration with an y con n ection pool. H owever, support for C3P0 is built in , so we’ll use th at. H ibern ate will set up th e con figuration pool for you with th e given properties. An example of a hibernate.properties file usin g C3P0 is sh own in listin g 2.4. Non-Managed Environment Application
JSP Servlet main()
User-managed JDBC connections
Connection Pool
Database
Figure 2 .2
JDBC connection pooling in a non-managed environment
46
CHAPTER 2
Introducing and integrating Hibernate
Non-Managed Environment Hibernate Application
Session
JSP Servlet main()
Figure 2 .3
Transaction
Connection Pool
Query
Database
Hibernate with a connection pool in a non-managed environment
Listing 2 .4
Using hibernate.properties for C3P0 connection pool settings
hibernate.connection.driver_class = org.postgresql.Driver hibernate.connection.url = jdbc:postgresql://localhost/auctiondb hibernate.connection.username = auctionuser hibernate.connection.password = secret hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect hibernate.c3p0.min_size=5 hibernate.c3p0.max_size=20 hibernate.c3p0.timeout=300 hibernate.c3p0.max_statements=50 hibernate.c3p0.idle_test_period=3000
This code’s lines specify the following information, beginning with the first line: ■
Th e n ame of th e Java class implemen tin g th e JDBC Driver ( the driver JAR file must be placed in th e application ’s classpath ) .
■
A JDBC URL th at specifies th e h ost an d database n ame for JDBC connection s.
■
Th e database user n ame.
■
Th e database password for th e specified user.
■
A Dialect for th e database. Despite th e ANSI standardization effort, SQL is implemen ted differen tly by various databases ven dors. So, you must specify a Dialect. H ibern ate in clu d es bu ilt-in su p p ort for all p op u lar SQ L d atabases, and new dialects may be defined easily.
■
Th e min imum n umber of JDBC con n ection s th at C3P0 will keep ready.
Basic configuration
■
Th e maximum n umber of con n ection s in th e pool. An exception will be thrown at run time if th is n umber is exh austed.
■
The timeout period ( in this case, 5 min utes or 300 secon ds) after wh ich an idle con n ection will be removed from the pool.
■
The maximum number of prepared statements that will be cached. Caching of prepared statemen ts is essential for best per forman ce with Hibern ate.
■
Th e idle time in secon ds before a connection is automatically validated.
Specifyin g properties of th e form hibernate.c3p0.* selects C3P0 as H ibern ate’s connection pool ( you don’t need an y oth er switch to en able C3P0 support) . C3P0 has even more features than we’ve shown in the previous example, so we refer you to th e H ibern ate API documen tation . Th e Javadoc for th e class net.sf.hibernate.cfg.Environment d o cu m en ts ever y H ib er n ate co n figu r atio n p r o p er ty, in cludin g all C3P0-related settin gs an d settin gs for oth er th ird-party con n ection pools directly supported by Hibernate. Th e oth er supported con n ection pools are Apache DBCP and Proxool. You should try each pool in your own en viron ment before deciding between th em. Th e Hibern ate commun ity ten ds to prefer C3P0 an d Proxool. Hibernate also ships with a default connection pooling mechanism. This connection pool is only suitable for testing an d experimen tin g with Hibern ate: You should not use this built-in pool in production systems. It isn’t designed to scale to an en viron men t with man y con curren t requests, an d it lacks the fault toleran ce features foun d in specialized con n ection pools. Starting Hibernate
How do you start Hibernate with these properties? You declared th e properties in a file named hibernate.properties, so you n eed on ly place th is file in th e application classpath . It will be automatically detected an d read wh en H ibern ate is first initialized wh en you create a Configuration object. Let’s summarize the configuration steps you’ve learned so far ( this is a good time to down load an d in stall Hibern ate, if you’d like to con tin ue in a n on managed environment) : 1
Download and unpack the JDBC driver for your database, which is usually available from the database vendor web site. Place the JAR files in the application classpath; do the same with hibernate2.jar.
2
Add Hibern ate’s depen den cies to th e classpath ; th ey’re distributed alon g with Hibernate in the lib/ directory. See also the text file lib/README.txt for a list of required an d option al libraries.
47
48
CHAPTER 2
Introducing and integrating Hibernate
3
Choose a JDBC connection pool supported by Hibernate and configure it with a properties file. Don’t forget to specify th e SQL dialect.
4
Let the Configuration know about these properties by placing them in a hibernate.properties file in the classpath.
5
Create an in stan ce of Configuration in your application and load the XML mappin g files usin g eith er addResource() or addClass(). Build a SessionFactory from th e Configuration by callin g buildSessionFactory().
Un fortun ately, you don ’t h ave an y mappin g files yet. If you like, you can run th e “H ello World” example or skip th e rest of th is ch apter an d start learn in g about persisten t classes an d mappin gs in ch apter 3. Or, if you want to know more about using Hibernate in a managed en viron men t, read on . 2 .3 .3 Configuration in managed environments
A man aged en viron men t h an dles certain cross-cuttin g con cern s, such as application security ( auth orization an d auth en tication ) , con n ection poolin g, an d tran saction man agemen t. J2EE application servers are typical man aged en viron men ts. Alth ough application servers are gen erally design ed to support EJBs, you can still take advan tage of th e oth er man aged services provided, even if you don’t use EJB entity beans. Hibern ate is often used with session or message-driven EJBs, as shown in figure 2.4. EJBs call th e same Hibern ate APIs as servlets, JSPs, or stan d-alon e application s: Session, Transaction, and Query. The Hibern ate-related code is fully portable between non-managed and managed en viron men ts. Hibern ate h an dles th e different connection and transaction strategies tran sparen tly. Application Server Hibernate Application
EJB EJB EJB
Session
Transaction Manager
Transaction Query
Resource Manager
Database Figure 2 .4
Hibernate in a managed environment with an application server
Basic configuration
49
An application server exposes a con n ection pool as a JNDI-boun d datasource, an in stan ce of javax.jdbc.Datasource. You n eed to tell Hibern ate wh ere to fin d th e datasource in JNDI, by supplying a fully qualified JNDI n ame. An example Hibernate con figuration file for th is scen ario is sh own in listin g 2.5. Listing 2 .5
Sample hibernate.properties for a container-provided datasource
hibernate.connection.datasource = java:/comp/env/jdbc/AuctionDB hibernate.transaction.factory_class = \ net.sf.hibernate.transaction.JTATransactionFactory hibernate.transaction.manager_lookup_class = \ net.sf.hibernate.transaction.JBossTransactionManagerLookup hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect
Th is file first gives th e JNDI n am e of th e d atasou rce. Th e d atasou rce m u st be con figu red in th e J2EE en terp rise ap p lication d ep loym en t d escrip tor; th is is a ven d or-sp ecific settin g. Next, you en able H ibern ate in tegration with JTA. Now Hibernate needs to locate th e application server’s TransactionManager in order to in tegrate fully with th e con tain er tran saction s. No stan dard approach is defin ed by th e J2EE specification , but Hibern ate in cludes support for all popular application servers. Fin ally, of course, th e Hibern ate SQL dialect is required. Now that you’ve configured everything correctly, usin g Hibern ate in a man aged en viron men t isn ’t much differen t th an usin g it in a n on -man aged en viron men t: Just create a Configuration with mappin gs an d build a SessionFactory. However, some of th e tran saction en viron men t–related settin gs deserve some extra con sideration . Java already has a standard transaction API, JTA, which is used to control transaction s in a man aged en viron men t with J2EE. This is called container-managed transactions ( CMT ) . If a JTA transaction manager is present, JDBC con n ections are enlisted with this manager and under its full con trol. Th is isn ’t th e case in a n on managed environment, where an application ( or th e pool) man ages th e JDBC conn ection s an d JDBC tran saction s directly. Therefore, managed and non-managed en vironments can use different transaction meth ods. Sin ce Hibern ate n eeds to be portable across th ese en viron men ts, it defines an API for controlling transactions. The Hibernate Transaction in terface abstracts th e un derlyin g JTA or JDBC tran saction ( or, poten tially, even a CO RBA transaction) . This underlying transaction strategy is set with th e property hibernate.connection.factory_class, an d it can take on e of th e followin g two values:
50
CHAPTER 2
Introducing and integrating Hibernate
■
net.sf.hibernate.transaction.JDBCTransactionFactory delegates to direct JDBC tran saction s. Th is strategy sh ould be used with a con n ection pool in a
non-managed environment and is the default if n o strategy is specified. ■
net.sf.hibernate.transaction.JTATransactionFactory delegates to JTA. This is the correct strategy for CMT, wh ere con n ection s are en listed with JTA. Note th at if a JTA tran saction is alread y in p rogress wh en beginTransaction() is called, subsequen t work takes place in the context of that transaction ( oth erwise a new JTA tran saction is started) .
For a more detailed in troduction to H ibern ate’s Transaction API an d th e effects on your specific application scen ario, see ch apter 5, section 5.1, “Tran saction s.” Just remember the two steps that are necessary if you work with a J2EE application server: Set the factory class for the Hibernate Transaction API to JTA as described earlier, an d declare th e tran saction man ager lookup specific to your application server. The lookup strategy is required only if you use th e secon d-level cach in g system in Hibern ate, but it doesn ’t h urt to set it even with out usin g th e cach e. HIBERNATE WITH TOM CAT
Tomcat isn ’t a full application server; it’s just a servlet con tain er, albeit a servlet con tain er with some features usually found on ly in application servers. O n e of th ese features may be used with Hibern ate: th e Tomcat con n ection pool. Tomcat uses th e DBCP con n ection pool in tern ally but exposes it as a JNDI datasource, just like a real application server. To con figure th e Tomcat datasource, you’ll n eed to edit server.xml accordin g to in struction s in th e Tomcat JNDI/ JDBC documen tation . You can con figure Hibern ate to use th is datasource by settin g hibernate.connection.datasource. Keep in min d th at Tomcat doesn ’t sh ip with a tran saction man ager, so th is situation is still more like a n on -man aged en viron men t as described earlier.
You sh ould n ow h ave a run n in g Hibern ate system, whether you use a simple servlet con tain er or an application server. Create an d compile a persisten t class ( th e in itial Message, for exam p le) , cop y H ibern ate an d its requ ired libraries to th e classpath togeth er with a hibernate.properties file, an d build a SessionFactory. The n ext section covers advan ced Hibern ate con figuration option s. Some of th em are recommen ded, such as loggin g executed SQL statements for debuggin g or using th e con ven ien t XML configuration file instead of plain properties. However, you may safely skip th is section an d come back later once you have read more about persistent classes in chapter 3.
51
Advanced configuration settings
2.4 Advanced configuration settings Wh en you fin ally h ave a Hibern ate application run n in g, it’s well worth gettin g to know all the Hibernate configuration parameters. Th ese parameters let you optimize the runtime behavior of Hibern ate, especially by tun in g th e JDBC in teraction ( for example, usin g JDBC batch updates) . We won’t bore you with these details n ow; th e best source of in formation about con figuration option s is th e Hibernate referen ce documen tation . In th e previous section , we sh owed you th e option s you’ll n eed to get started. However, there is one parameter that we must emphasize at this point. You’ll need it continually whenever you develop software with Hibern ate. Settin g th e property hibernate.show_sql to th e value true en ables loggin g of all gen erated SQL to th e con sole. You’ll use it for troublesh ootin g, performan ce tun in g, an d just to see what’s going on. It pays to be aware of what your ORM layer is doin g—th at’s why ORM doesn ’t hide SQL from developers. So far, we’ve assumed th at you specify configuration parameters using a hibernate.properties file or an in stan ce of java.util.Properties programmatically. There is a third option you’ll probably like: using an XML con figuration file. 2 .4 .1 Using XM L-based configuration
You can use an XML con figuration file ( as demon strated in listin g 2.6) to fully con figure a SessionFactory. Un like hibernate.properties, wh ich con tain s on ly con figuration parameters, th e hibernate.cfg.xml file may also specify th e location of mappin g documen ts. Man y users prefer to cen tralize th e con figuration of Hibern ate in th is way in stead of addin g parameters to th e Configuration in application code. Listing 2 .6
Sample hibernate.cfg.xml configuration file
?xml version='1.0'encoding='utf-8'?> Document type
Name
attribute true
Property java:/comp/env/jdbc/AuctionDB specifications
net.sf.hibernate.dialect.PostgreSQLDialect
B
C
D
52
CHAPTER 2
Introducing and integrating Hibernate
net.sf.hibernate.transaction.JBossTransactionManagerLookup
Mapping
document specifications
d
E
B
The document type declaration is used by th e XML parser to validate this document again st th e Hibern ate con figuration DTD.
C
Th e option al name attribute is equivalent to the property hibernate.session_ factory_name and used for JNDI bin din g of th e SessionFactory, discussed in th e next section.
D
Hibernate properties may be specified without the hibernate prefix. Property n ames an d values are oth erwise iden tical to programmatic con figuration properties.
E
Mapping documen ts may be specified as application resources or even as hardcoded filen ames. Th e files used h ere are from our on lin e auction application , which we’ll introduce in chapter 3. Now you can initialize Hibernate using SessionFactory sessions = new Configuration() .configure().buildSessionFactory();
Wait—how did Hibernate know where the configuration file was located? When configure() was called, Hibernate searched for a file named hibernate.cfg.xml in th e classpath . If you wish to use a differen t filen ame or h ave Hibernate look in a subdirectory, you must pass a path to the configure() meth od: SessionFactory sessions = new Configuration() .configure("/hibernate-config/auction.cfg.xml") .buildSessionFactory();
Using an XML configuration file is certain ly more comfortable th an a properties file or even programmatic property configuration. The fact that you can have the class mappin g files extern alized from th e application ’s source ( even if it would be on ly in a startup h elper class) is a major ben efit of th is approach . You can , for e xam p le , u se d iffe r e n t se ts o f m ap p in g file s ( an d d iffe r e n t co n figu r atio n option s) , depen din g on your database an d en viron men t ( developmen t or production) , and switch them programatically.
Advanced configuration settings
53
If you have both hibernate.properties and hibernate.cfg.xml in the classpath, th e settin gs of th e XML con figuration file will override th e settin gs used in th e properties. This is useful if you keep some base settin gs in properties an d override them for each deploymen t with an XML configuration file. You may have noticed that the SessionFactory was also given a name in th e XML configuration file. Hibernate uses this name to automatically bind the SessionFactory to JNDI after creation . 2 .4 .2 JNDI-bound SessionFactory
In most H ibern ate application s, th e SessionFactory should be in stan tiated on ce durin g application in itialization . Th e sin gle in stan ce sh ould th en be used by all code in a particular process, and any Sessions sh ould be created usin g th is sin gle SessionFactory. A frequen tly asked question is wh ere th is factory must be placed and how it can be accessed without much hassle. In a J2EE en viron men t, a SessionFactory boun d to JNDI is easily shared between differen t th reads an d between various Hibern ate-aware compon en ts. Or course, JNDI isn ’t th e on ly way th at application compon en ts migh t obtain a SessionFactory. Th ere are man y possible implementations of this Registry pattern, including use of th e ServletContext or a static final variable in a sin gleton. A particularly elegan t approach is to use an application scope IoC ( In version of Con trol) framework component. However, JNDI is a popular approach ( an d is exposed as a JMX service, as you'll see later) . We discuss some of th e altern atives in ch apter 8, section 8.1, “Design in g layered applications.” NOTE
Th e Java Namin g an d Directory In terface ( JNDI) API allows objects to be stored to an d retrieved from a h ierarch ical structure ( directory tree) . JNDI implemen ts th e Registry pattern . In frastructural objects ( tran saction con texts, datasources) , con figuration settings ( en viron ment settin gs, user registries) , an d even application objects ( EJB referen ces, object factories) may all be boun d to JNDI.
Th e SessionFactory will automatically bin d itself to JNDI if th e property hibernate.session_factory_name is set to th e n ame of th e directory n ode. If your run time en viron men t doesn ’t provide a default JNDI con text ( or if th e default JNDI implementation doesn’t support instances of Referenceable) , you n eed to specify a JNDI in itial con text u sin g th e p r op er ties hibernate.jndi.url an d hibernate.jndi.class.
54
CHAPTER 2
Introducing and integrating Hibernate
Here is an example Hibern ate con figuration th at bin ds th e SessionFactory to the name hibernate/HibernateFactory usin g Sun ’s ( free) file system–based JNDI implemen tation , fscontext.jar: hibernate.connection.datasource = java:/comp/env/jdbc/AuctionDB hibernate.transaction.factory_class = \ net.sf.hibernate.transaction.JTATransactionFactory hibernate.transaction.manager_lookup_class = \ net.sf.hibernate.transaction.JBossTransactionManagerLookup hibernate.dialect = net.sf.hibernate.dialect.PostgreSQLDialect hibernate.session_factory_name = hibernate/HibernateFactory hibernate.jndi.class = com.sun.jndi.fscontext.RefFSContextFactory hibernate.jndi.url = file:/auction/jndi
Of course, you can also use th e XML-based configuration for this task. This example also isn ’t realistic, sin ce most application ser vers th at provide a con n ection pool th rough JNDI also h ave a JNDI implemen tation with a writable default con text. JBoss certain ly has, so you can skip th e last two properties and just specify a n ame for th e SessionFactory. All you h ave to do n ow is call Configuration.configure().buildSessionFactory() once to initialize the binding. NOTE
Tomcat comes bun dled with a read-on ly JNDI con text, wh ich isn ’t writable from application -level code after th e startup of th e servlet con tain er. Hibern ate can ’t bin d to th is con text; you h ave to eith er use a full con text implemen tation ( like th e Sun FS con text) or disable JNDI bin din g of th e SessionFactory by omittin g th e session_factory_name property in th e con figuration .
Let’s look at some oth er very importan t con figuration settin gs th at log Hibern ate operations. 2 .4 .3 Logging
H ibern ate ( an d m an y oth er O RM im p lem en tation s) execu tes SQ L statem en ts asynchronously. An INSERT statemen t isn ’t usually executed wh en th e application calls Session.save(); an UPDATE isn ’t im m ediately issued wh en th e application calls Item.addBid(). In stead, th e SQL statemen ts are usually issued at th e en d of a transaction. This behavior is called write-behind, as we mentioned earlier. This fact is evidence that tracing and debugging ORM code is sometimes n on trivial. In th eory, it’s possible for th e application to treat Hibernate as a black box and ignore this behavior. Certainly the Hibernate application can’t detect this asyn ch ron icity ( at least, n ot with out resortin g to direct JDBC calls) . However, wh en you find yourself troubleshooting a difficult problem, you n eed to be able to see exactly what’s going on inside Hibernate. Since Hibernate is open source, you can
Advanced configuration settings
55
easily step into the Hibernate code. Occasionally, doing so helps a great deal! But, especially in the face of asynchronous beh avior, debuggin g Hibern ate can quickly get you lost. You can use loggin g to get a view of Hibern ate’s in tern als. We’ve already mentioned the hibernate.show_sql con figuration parameter, wh ich is usually th e first port of call when troubleshooting. Sometimes the SQL alon e is in sufficien t; in th at case, you must dig a little deeper. Hibernate logs all interesting events using Apache commons-logging, a thin abstraction layer that directs output to either Apache log4j ( if you put log4j.jar in your classpath ) or JDK1.4 logging ( if you’re running under JDK1.4 or above and log4j isn ’t presen t) . We recommen d log4j, sin ce it’s more mature, more popular, and under more active developmen t. To see any output from log4j, you’ll n eed a file n amed log4j.properties in your classpath ( righ t n ext to hibernate.properties or hibernate.cfg.xml) . Th is example directs all log messages to th e con sole: ### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} ➾ %5p %c{1}:%L - %m%n ### root logger option ### log4j.rootLogger=warn, stdout ### Hibernate logging options ### log4j.logger.net.sf.hibernate=info ### log JDBC bind parameters ### log4j.logger.net.sf.hibernate.type=info ### log PreparedStatement cache activity ### log4j.logger.net.sf.hibernate.ps.PreparedStatementCache=info
With th is con figuration , you won ’t see man y log messages at run time. Replacin g in fo with debug for th e log4j.logger.net.sf.hibernate categor y will reveal th e in n er workin gs of H ibern ate. Make sure you don ’t do th is in a production en vironmen t—writin g th e log will be much slower than the actual database access. Fin ally, you have th e hibernate.properties, hibernate.cfg.xml, an d log4j.properties configuration files. There is another way to configure Hibernate, if your application server supports the Java Man agemen t Exten sion s. 2 .4 .4 Java M anagement Extensions (JM X)
The Java world is full of specification s, standards, and, of course, implementations of th ese. A relatively n ew but importan t stan dard is in its first version : th e Java
56
CHAPTER 2
Introducing and integrating Hibernate
Management Extension s ( JM X ) . JMX is about th e man agemen t of systems components or, better, of system services. Where does Hibernate fit into this new picture? Hibernate, when deployed in an application server, makes use of other services like managed transactions and pooled database tran saction s. But wh y n ot make Hibernate a managed service itself, wh ich oth ers can depen d on an d use? Th is is possible with th e Hibern ate JMX in tegration , makin g Hibern ate a man aged JMX compon en t. The JMX specification defin es th e followin g compon en ts: ■
T he JMX MBean —A reusable component ( usually infrastructural) that exposes an in ter face for management ( administration)
■
T he JMX container— Mediates generic access ( local or remote) to th e MBean
■
T he (usually generic) JMX client— May be used to administer any MBean via the JMX container
An application server with support for JMX ( such as JBoss) acts as a JMX con tain er an d allows an MBean to be con figured an d in itialized as part of th e application ser ver startup process. It’s possible to mon itor an d admin ister th e MBean usin g the application server’s admin istration con sole ( which acts as th e JMX client) . An MBean may be packaged as a JMX service, wh ich is n ot on ly portable between application servers with JMX support but also deployable to a running system ( a hot deploy) . Hibernate may be packaged and administered as a JMX MBean. The Hibernate JMX service allows Hibern ate to be in itialized at application server startup and controlled ( con figured) via a JMX clien t. However, JMX compon en ts aren ’t automatically integrated with container-managed tran saction s. So, th e con figuration options in listing 2.7 ( a JBoss service deploymen t descriptor) look similar to th e usual Hibern ate settin gs in a managed environment. Listing 2 .7
Hibernate jboss-service.xml JM X deployment descriptor
jboss.jca:service=RARDeployer jboss.jca:service=LocalTxCM,name=DataSource
auction/Item.hbm.xml, auction/Bid.hbm.xml
Advanced configuration settings
57
java:/hibernate/HibernateFactory
java:/comp/env/jdbc/AuctionDB
net.sf.hibernate.dialect.PostgreSQLDialect
net.sf.hibernate.transaction.JTATransactionFactory
net.sf.hibernate.transaction.JBossTransactionManagerLookup
java:/UserTransaction
Th e HibernateService depen ds on two oth er JMX services: service=RARDeployer an d service=LocalTxCM,name=DataSource, both in th e jboss.jca service domain name. The Hibernate MBean may be found in the package net.sf.hibernate.jmx. Unfortunately, lifecycle management meth ods like startin g an d stoppin g th e JMX service aren ’t part of th e JMX 1.0 specification. The methods start() and stop() of th e HibernateService are th erefore specific to th e JBoss application server. NOTE
If you’re interested in th e advan ced usage of JMX, JBoss is a good open source startin g poin t: All services ( even th e EJB con tain er) in JBoss are implemen ted as MBean s an d can be man aged via a supplied con sole in terface.
We recommen d th at you try to con figure Hibernate programmatically ( using th e Configuration object) before you try to run Hibern ate as a JMX service. However,
some features ( like h ot-redeploymen t of Hibern ate application s) may be possible on ly with JMX, on ce th ey become available in H ibern ate. Righ t n ow, th e biggest advan tage of Hibern ate with JMX is th e automatic startup; it means you n o lon ger h ave to create a Configuration an d build a SessionFactory in your application co d e , b u t ca n sim p ly a cce ss t h e SessionFactory t h r o u gh JN DI o n ce t h e HibernateService has been deployed and started.
58
CHAPTER 2
Introducing and integrating Hibernate
2.5 Summary In th is ch apter, we took a h igh -level look at H ibern ate an d its arch itecture after run n in g a simple “H ello World” example. You also saw h ow to con figure H ibern ate in various en viron men ts an d with various techniques, even including JMX. The Configuration and SessionFactory in terfaces are th e en try poin ts to Hibernate for applications running in both man aged an d n on -man aged en viron ments. Hibern ate provides addition al APIs, such as th e Transaction in terface, to bridge th e differen ces between en viron men ts an d allow you to keep your persistence code portable. Hibern ate can be in tegrated in to almost every Java en viron men t, be it a servlet, an applet, or a fully managed three-tiered client/ server application. The most importan t elemen ts of a Hibern ate con figuration are the database resources ( connection configuration) , the transaction strategies, an d, of course, th e XML-based mapping metadata. Hibern ate’s con figuration in terfaces h ave been design ed to cover as man y usage scenarios as possible while still being easy to understand. Usually, a single file named hibernate.cfg.xml an d on e lin e of code are en ough to get Hibern ate up and running. None of this is much use without some persisten t classes an d th eir XML mapping documen ts. Th e n ext ch apter is dedicated to writin g an d mappin g persisten t classes. You’ll soon be able to store an d retrieve persistent objects in a real application with a n on trivial object/ relation al mappin g.
Mapping persistent classes
This chapter covers ■
POJO bas ic s fo r ric h do main mo de ls
■
Mapping POJOs with Hibe rnate me tadata
■
Mapping c las s inhe ritanc e and fine -graine d mo de ls
■
An intro duc tio n to c las s as s o c iatio n mappings
59
60
CHAPTER 3
Mapping persistent classes
Th e “H ello World ” exam p le in ch ap ter 2 in trod u ced you to H ibern ate; h owever, it isn ’t ver y useful for un derstan din g th e requiremen ts of real-world application s with com p lex d ata m od els. For th e rest of th e book, we’ll u se a m u ch more soph isticated example application —an on lin e auction system—to demon strate Hibernate. In th is ch apter, we start our discussion of the application by introducing a programming model for persistent classes. Designing an d implementing the persistent classes is a multistep process th at we’ll examin e in detail. First, you’ll learn h ow to iden tify th e business entities of a problem domain . We create a conceptual model of these entities and their attributes, called a domain model. We implement this domain model in Java by creating a persistent class for each en tity. ( We’ll spen d some time exploring exactly what these Java classes should look like.) We then define mapping metadata to tell Hibernate how these classes and their properties relate to database tables and columns. This involves writing or generating XML documen ts th at are even tually deployed along with the compiled Java classes an d used by Hibern ate at run time. Th is discussion of mappin g metadata is the core of this chapter, along with the in-depth exploration of the mapping techniques for fine-grained classes, object identity, inheritance, and associations. This chapter therefore provides the begin n in gs of a solution to th e first four gen eric problems of ORM listed in section 1.4.2, “Gen eric ORM problems.” We’ll start by in troducin g th e example application .
3.1 The CaveatEmptor application The CaveatEmptor on lin e auction application demon strates ORM techniques and Hibernate fun ction ality; you can down load the source code for the entire working ap plication from th e web site h ttp:/ / caveatemptor.h ibern ate.org. Th e app lication will have a web-based user inter face and run inside a servlet engine like Tomcat. We won ’t pay much atten tion to th e user in ter face; we’ll con centrate on th e data access code. In ch apter 8, we discuss the chan ges th at would be necessary if we were to per form all business logic and data access from a separate busin ess-tier implemented as EJB session bean s. But, let’s start at the beginning. In order to understand the design issues involved in O RM, let’s pretend the CaveatEmptor application doesn ’t yet exist, an d th at we’re buildin g it from scratch . Our first task would be analysis.
The CaveatEmptor application
61
3 .1 .1 Analyzing the business domain
A softwar e d evelop m en t effor t begin s with an alysis of th e p r o blem d om ain ( assumin g th at n o legacy code or legacy database already exist) . At th is stage, you, with th e h elp of problem domain experts, iden tify th e main entities th at are relevan t to th e software system. En tities are usually n otion s un derstood by users of th e system: Payment, Customer, Order, Item, Bid, an d so forth . Some entities might be abstractions of less concrete things the user thinks about ( for example, PricingAlgorithm) , but even these would usually be understandable to th e user. All th ese en tities are found in the conceptual view of the business, which we sometimes call a business model. Developers of object-orien ted software analyze the business model and create an object model, still at the conceptual level ( n o Java code) .This object model may be as simple as a men tal image existin g on ly in the min d of th e developer, or it may be as elaborate as a UML class diagram ( as in figure 3.1) created by a CASE ( Computer-Aided Software En gin eerin g) tool like Argo UML or TogetherJ. This simple model contains entities that you’re bound to find in any typical auction system: Category, Item, an d User. Th e en tities an d th eir relation sh ips ( an d perhaps their attributes) are all represented by this model of the problem domain. We call th is kin d of model—an object-orien ted model of entities from the problem domain , en compassin g on ly th ose en tities th at are of in terest to th e user—a domain model. It’s an abstract view of th e real world. We refer to th is model wh en we implemen t our persisten t Java classes. Let’s examin e th e outcome of our an alysis of th e problem domain of th e CaveatEmptor application. 3 .1 .2 The CaveatEmptor domain model
Th e CaveatEmptor site auction s man y differen t kin ds of items, from electron ic equipment to airline tickets. Auctions proceed according to the “English auction” model: Users continue to place bids on an item un til th e bid period for th at item expires, an d th e h igh est bidder win s. In an y store, goods are categorized by type an d grouped with similar goods in to section s an d on to sh elves. Clearly, our auction catalog requires some kind of hierarchy of item categories. A buyer may browse th ese categories or arbitrarily search by category and item attributes. Lists of items appear in the category browser and Category Figure 3 .1
0..*
Item
0..*
sells
A class diagram of a typical online auction object model
User
62
CHAPTER 3
Mapping persistent classes
search result screens. Selecting an item from a list will take th e buyer to an item detail view. An auction con sists of a sequen ce of bids. One particular bid is the winning bid. User details include name, login, address, email address, and billing information. A web of trust is an essen tial feature of an on lin e auction site. Th e web of trust allows users to build a reputation for trustworthiness ( or untrustworthiness) . Buyers may create commen ts about sellers ( an d vice versa) , an d th e commen ts are visible to all other users. A high -level overview of our domain model is sh own in figure 3.2. Let’s briefly discuss some interesting features of this model. Each item may be auction ed on ly on ce, so we don’t need to make Item distin ct from the Auction entities. Instead, we have a sin gle auction item en tity n amed Item. Th us, Bid is associated directly with Item. Users can write Comments about oth er users on ly in th e con text of an auction ; h en ce th e association between Item and Comment. The Address information of a User is modeled as a separate class, even th ough th e User may have only one Address. We do allow th e user to h ave multiple BillingDetails. The various billing strategies are represented as subclasses of an abstract class ( allowin g future exten sion ) . A Category migh t be n ested in side an oth er Category. This is expressed by a recursive association , from th e Category en tity to itself. Note th at a sin gle Category may have multiple child categories but at most one parent category. Each Item belongs to at least one Category. Th e en tities in a domain model sh ould encapsulate state an d beh avior. For example, the User entity sh ould defin e th e n ame an d address of a customer an d the logic required to calculate the shippin g costs for items ( to th is particular customer) . Our domain model is a rich object model, with complex association s, interactions, and inheritance relationships. An interesting and detailed discussion of object-orien ted techn iques for workin g with domain models can be found in Patterns of Enterprise Application Architecture [ Fowler 2003] or in Domain-Driven Design [ Evan s 2004] . However, in this book, we won’t have much to say about business rules or about the behavior of our domain model. Th is is certain ly n ot because we con sider th is an unimportan t con cern ; rath er, th is con cern is mostly orth ogon al to th e problem of persistence. It’s the state of our en tities th at is persisten t. So, we concentrate our discussion on how to best represent state in our domain model, n ot on h ow to represent behavior. For example, in this book, we aren ’t in terested in h ow tax for sold items is calculated or how the system might approve a n ew user accoun t. We’re
Figure 3 .2
Persistent classes of the CaveatEmptor object model and their relationships
The CaveatEmptor application
63
64
CHAPTER 3
Mapping persistent classes
more interested in how the relationship between users and the items they sell is represen ted an d made persisten t. FAQ
Can you use ORM without a domain model? We stress th at object persisten ce with full ORM is most suitable for application s based on a rich domain model. If your application doesn ’t implemen t complex busin ess rules or complex in teraction s between en tities ( or if you h ave few en tities) , you may n ot n eed a domain model. Man y simple an d some n ot-so-simple problems are perfectly suited to table-orien ted solution s, wh ere th e application is design ed aroun d th e database data model in stead of aroun d an object-orien ted domain model, often with logic executed in th e database ( stored procedures) . H owever, the more complex an d expressive your domain model, th e more you will ben efit from usin g H ibern ate; it sh in es wh en dealin g with th e full complexity of object/ relation al persisten ce.
Now th at we h ave a domain model, our n ext step is to implemen t it in Java. Let’s look at some of th e th in gs we n eed to con sider.
3.2 Implementing the domain model Several issues typically must be addressed wh en you implemen t a domain model in Java. For in stan ce, h ow do you separate th e busin ess con cern s from th e crosscutting concerns ( such as transactions and even persisten ce) ? Wh at kin d of persisten ce is n eeded: Do you n eed automated or transparent persistence? Do you have to use a specific programmin g model to ach ieve th is? In th is section , we examin e these types of issues and how to address th em in a typical Hibern ate application . Let’s start with an issue th at an y implemen tation must deal with : th e separation of concerns. The domain model implementation is usually a central, organizing compon en t; it’s reused h eavily whenever you implement new application fun ction ality. For th is reason , you sh ould be prepared to go to some lengths to ensure that concerns other than business aspects don ’t leak in to th e domain model implementation. 3 .2 .1 Addressing leakage of concerns
Th e domain model implemen tation is such an importan t piece of code th at it sh ould n ’t depen d on oth er Java API s. For example, code in th e domain model shouldn’t per form JNDI lookups or call the database via the JDBC API. This allows you to reuse the domain model implemen tation virtually an ywh ere. Most importan tly, it makes it easy to unit test th e domain model ( in JUn it, for example) outside of any application server or oth er man aged en viron men t.
Implementing the domain model
65
We say th at th e domain model sh ould be “con cern ed” on ly with modelin g th e business domain. However, there are other concerns, such as persistence, transaction management, and authorization. You shouldn ’t put code th at addresses th ese cross-cutting concerns in th e classes th at implemen t th e domain model. Wh en th ese concerns start to appear in the domain model classes, we call th is an example of leakage of concerns. The EJB stan dard tries to solve th e problem of leaky con cern s. In deed, if we implemented our domain model using en tity bean s, th e con tain er would take care of some con cern s for us ( or at least extern alize th ose con cern s to th e deploymen t descriptor) . Th e EJB container prevents leakage of certain cross-cutting concerns using interception . An EJB is a managed component, always executed inside the EJB container. Th e con tain er in tercepts calls to your beans and executes its own functionality. For example, it migh t pass con trol to th e CMP engine, which takes care of persisten ce. Th is approach allows th e con tain er to implemen t th e predefin ed cross-cutting concerns—security, concurren cy, persisten ce, tran saction s, an d remoteness—in a generic way. Unfortunately, the EJB specification imposes man y rules an d restriction s on h ow you must implemen t a domain model. This in itself is a kind of leakage of concern s—in th is case, th e con cern s of th e contain er implemen tor h ave leaked! Hibernate isn’t an application server, and it doesn’t try to implement all the cross-cutting concerns mentioned in the EJB specification . Hibern ate is a solution for just on e of these con cern s: persisten ce. If you require declarative security and transaction management, you should still access your domain model via a session bean , takin g advan tage of th e EJB container’s implementation of these con cern s. Hibern ate is commonly used together with the well-known session façade J2EE pattern . Much discussion h as gon e in to th e topic of persisten ce, an d both Hibern ate an d EJB entity beans take care of that concern . However, Hibern ate offers someth in g that entity beans don’t: transparent persistence. 3 .2 .2 Transparent and automated persistence
Your application ser ver’s CMP en gin e implemen ts automated persistence. It takes care of th e tedious details of JDBC ResultSet an d PreparedStatement h an dlin g. So d oes H ibern ate; in d eed , H ibern ate is a great d eal m ore sop h isticated in th is respect. But Hibernate does this in a way that is transparent to your domain model. We use transparent to mean a complete separation of concerns between the persistent classes of the domain model an d th e persisten ce logic itself, wh ere th e persisten t classes are un aware of—an d have no dependency to—the persistence mechanism.
66
CHAPTER 3
Mapping persistent classes
Our Item class, for example, will n ot h ave an y code-level dependency to any Hibernate API. Furthermore: ■
Hibernate doesn’t require that any special superclasses or inter faces be in h erited or implemen ted by persisten t classes. Nor are an y special classes used to implemen t properties or association s. Th us, tran sparen t persisten ce improves code readability, as you’ll soon see.
■
Persisten t classes may be reused outside th e con text of persisten ce, in un it tests or in th e u ser in ter face ( UI ) tier, for exam p le. Testability is a basic requiremen t for application s with rich domain models.
■
In a system with tran sparen t persisten ce, objects aren’t aware of the underlyin g data store; th ey n eed n ot even be aware that they are being persisted or retrieved. Persisten ce con cern s are extern alized to a gen eric persistence manager inter face —in the case of Hibernate, the Session an d Query inter faces.
Transparent persistence fosters a degree of portability; without special inter faces, th e persisten t classes are decoupled from an y particular persisten ce solution . Our busin ess logic is fully reusable in an y oth er application con text. We could easily change to another transparent persistence mechanism. By th is defin ition of tran sparen t persisten ce, you see th at certain n on -automated persistence layers are transparent ( for example, the DAO pattern ) because th ey decouple th e persisten ce-related code with abstract programmin g in terfaces. On ly plain Java classes without dependencies are exposed to th e busin ess logic. Con versely, some automated persisten ce layers ( in cludin g en tity bean s an d some ORM solution s) are n on -tran sparen t, because th ey require special interfaces or intrusive programming models. We regard transparency as required. In fact, tran sparen t persisten ce sh ould be one of the primary goals of any ORM solution. However, n o automated persisten ce solution is completely transparent: Every automated persisten ce layer, in cludin g Hibern ate, imposes some requirements on the persistent classes. For example, Hibern ate requires th at collection -valued properties be typed to an interface such as java.util.Set or java.util.List an d n ot to an actual implemen tation such as java.util.HashSet ( th is is a good practice an yway) . ( We discuss th e reason s for th is requiremen t in appen dix B, “ORM implementation strategies.”) You now know why the persistence mechanism should have minimal impact on h ow you implemen t a domain model an d th at tran sparen t an d automated persistence are required. EJB isn’t transparent, so what kind of programming model sh ould you use? Do you n eed a special programming model at all? In theory, no;
Implementing the domain model
67
in practice, you sh ould adopt a disciplined, consistent programming model that is well accepted by the Java community. Let’s discuss th is programmin g model an d see h ow it works with Hibern ate. 3 .2 .3 Writing POJOs
Developers h ave foun d en tity bean s to be tedious, unnatural, and unproductive. As a reaction again st en tity bean s, man y developers started talkin g about Plain Old Java Objects ( PO JO s) , a back-to-basics approach th at essen tially revives JavaBean s, a component model for UI developmen t, an d reapplies it to the business layer. ( Most developers are now using the terms POJO an d JavaBean almost synonymously.) 1 Hibernate works best with a domain model implemented as PO JO s. The few requirements that Hibernate imposes on your domain model are also best practices for th e PO JO programmin g model. So, most POJO s are Hibernate-compatible without any changes. The programming model we’ll introduce is a non-intrusive mix of JavaBean specification details, PO JO best practices, and Hibernate requirements. A PO JO declares business methods, wh ich defin e beh avior, an d properties, which represent state. Some properties represen t association s to oth er PO JO s. Listing 3.1 shows a simple PO JO class; it’s an implementation of the User en tity of our domain model. Listing 3 .1
POJO implementation of the User class
public class User implements Serializable {
B
Implementation of Serializable
private String username; private Address address; public User() {}
C Class constructor
public String getUsername() { return username; } public void setUsername(String username) { this.username = username; }
d Accessor methods
public Address getAddress() { return address; }
1
POJO is sometimes also written as Plain Ordinary Java Objects; this term was coined in 2002 by Martin Fowler, Rebecca Parson s, an d Josh Macken zie.
68
CHAPTER 3
Mapping persistent classes public void setAddress(Address address) { this.address = address; }
d
public MonetaryAmount calcShippingCosts(Address fromLocation) { ... } Business method
E
}
B
Hibernate doesn’t require that persistent classes implement Serializable. However, when objects are stored in an HttpSession or passed by value usin g RMI, serialization is n ecessary. ( Th is is very likely to h appen in a Hibern ate application .)
C
Unlike the JavaBeans specification, which requires no specific constructor, Hibern ate requires a con structor with n o arguments for every persistent class. Hibernate instan tiates persisten t classes usin g Constructor.newInstance(), a feature of the Java reflection API. Th e con structor may be n on -public, but it sh ould be at least package-visible if runtime-generated proxies will be used for per forman ce optimization ( see ch apter 4) .
D
Th e properties of th e PO JO implement the attributes of our business entities—for example, th e usern ame of User. Properties are usually implemented as instance variables, together with property accessor methods: a meth od for retrievin g th e value of th e in stan ce variable an d a meth od for changing its value. These methods are kn own as th e getter and setter, respectively. Our example POJO declares getter an d setter meth ods for th e private username instance variable and also for address. The JavaBean specification defines the guidelin es for n amin g th ese meth ods. Th e guidelin es allow gen eric tools like Hibernate to easily discover and manipulate the property value. A getter method name begins with get, followed by th e n ame of th e property ( th e first letter in uppercase) ; a setter meth od n ame begin s with set. Getter methods for Boolean properties may begin with is in stead of get. Hibern ate doesn ’t require th at accessor meth ods be declared public; it can easily use private accessors for property management. Some getter and setter meth ods do someth in g more soph isticated th an simple instance variables access ( validation, for example) . Trivial accessor methods are common , h owever.
E
This PO JO also defines a business method that calculates th e cost of sh ippin g an item to a particular user ( we left out th e implemen tation of th is meth od) .
Implementing the domain model
69
Now th at you un derstan d th e value of usin g PO JO persisten t classes as th e programmin g model, let’s see h ow you h an dle th e association s between th ose classes. 3 .2 .4 Implementing POJO associations
You u se p rop erties to exp ress association s between PO JO classes, and you use accessor meth ods to n avigate th e object 0..* name : String graph at run time. Let’s con sider th e association s defin ed by t h e Category cla ss. T h e fir st a sso cia t io n is sh o wn in figure 3.3. Figure 3 .3 Diagram of As with all our diagrams, we left out th e association the Category class related attributes ( parentCategory an d childCategories) with an association because th ey would clutter the illustration. These attributes an d th e meth ods th at man ipulate th eir values are called scaffolding code. Let’s implement the scaffolding code for the one-to-many self-association of Category: Category
public class Category implements Serializable { private String name; private Category parentCategory; private Set childCategories = new HashSet(); public Category() { } ... }
To allow bidirection al n avigation of th e association , we require two attributes. The parentCategory attribute implements the single-valued end of th e association an d is declared to be of type Category. The many-valued end, implemen ted by th e childCategories attribute, must be of collection type. We choose a Set, since duplicates are disallowed, and initialize the instance variable to a new instance of HashSet. Hibern ate requires in terfaces for collection -typed attributes. You must use java.util.Set rather than HashSet, for example. At run time, Hibern ate wraps th e HashSet in stan ce with an in stan ce of on e of Hibernate’s own classes. ( This special class isn ’t visible to th e application code) . It is good practice to program to collection in terfaces, rath er th an con crete implementations, so this restriction shouldn’t bother you. We now have some private instance variables but no public interface to allow access from business code or property management by Hibernate. Let’s add some accessor methods to the Category class: public String getName() { return name; }
70
CHAPTER 3
Mapping persistent classes public void setName(String name) { this.name = name; } public Set getChildCategories() { return childCategories; } public void setChildCategories(Set childCategories) { this.childCategories = childCategories; } public Category getParentCategory() { return parentCategory; } public void setParentCategory(Category parentCategory) { this.parentCategory = parentCategory; }
Again , th ese accessor meth ods n eed to be declared public on ly if th ey’re part of th e extern al in ter face of th e p ersisten t class, th e p u blic in ter face u sed by th e application logic. Th e basic procedure for addin g a ch ild Category to a paren t Category looks like this: Category aParent = new Category(); Category aChild = new Category(); aChild.setParentCategory(aParent); aParent.getChildCategories().add(aChild);
Wh en ever an association is created between a paren t Category an d a ch ild Category, two action s are required: ■
The parentCategory of the child must be set, effectively breaking the association between the child and its old parent ( there can be only one parent for an y ch ild) .
■
Th e ch ild must be added to th e childCategories collection of the new parent Category.
M ANAGED RELATIONSHIPS IN HIBERNATE
Hibern ate doesn ’t “man age” persisten t association s. If you wan t to man ipulate an association , you must write exactly th e same code you would write with out Hibern ate. If an association is bidirection al, both sides of th e relation sh ip must be con sidered. Programmin g models like EJB en tity bean s muddle th is beh avior by in troducin g container-managed relationships. Th e con tain er automatically ch an ges th e oth er side of a relation sh ip if on e side is modified by th e application . Th is is on e of th e reason s wh y code th at uses en tity bean s can ’t be reused outside th e con tain er.
Implementing the domain model
71
If you ever h ave problems un derstan din g th e beh avior of association s in H ibern ate, just ask yourself, “Wh at would I do without H ibern ate?” H ibern ate doesn ’t change the usual Java semantics. It’s a good idea to add a con ven ien ce meth od to th e Category class th at groups these operations, allowing reuse and helping ensure correctness: public void addChildCategory(Category childCategory) { if (childCategory == null) throw new IllegalArgumentException("Null child category!"); if (childCategory.getParentCategory() != null) childCategory.getParentCategory().getChildCategories() .remove(childCategory); childCategory.setParentCategory(this); childCategories.add(childCategory); }
The addChildCategory() method not only reduces the lines of code when dealing with Category objects, but also enforces th e cardin ality of th e association . Errors that arise from leavin g out on e of th e two required action s are avoided. This kind of grouping of operations sh ould always be provided for associations, if possible. Because we would like th e addChildCategory() to be th e on ly extern ally visible mutator meth od for th e ch ild categories, we make th e setChildCategories() meth od private. Hibern ate doesn ’t care if property accessor methods are private or public, so we can focus on good API design. A different kind of relationship exists between Category and the Item: a bidirectional many-to-many association ( see figure 3.4) . In the case of a many-to-many association, both sides are implemented with collection -valued attributes. Let’s add th e n ew attributes an d meth ods to access th e Item class to our Category class, as shown in listing 3.2.
Figure 3 .4
Category and the associated Item
72
CHAPTER 3
Mapping persistent classes
Listing 3 .2
Category to Item scaffolding code
public class Category { ... private Set items = new HashSet(); ... public Set getItems() { return items; } public void setItems(Set items) { this.items = items; } }
Th e code for th e Item class ( th e oth er en d of th e man y-to-man y association ) is similar to th e code for th e Category class. We add th e collection attribute, th e stan dard accessor meth ods, an d a meth od th at simplifies relation sh ip man agement ( you can also add th is to th e Category class, see listin g 3.3) . Listing 3 .3
Item to Category scaffolding code
public class Item { private String name; private String description; ... private Set categories = new HashSet(); ... public Set getCategories() { return categories; } private void setCategories(Set categories) { this.categories = categories; } public void addCategory(Category category) { if (category == null) throw new IllegalArgumentException("Null category"); category.getItems().add(this); categories.add(category); } }
Implementing the domain model
73
Th e addCategory() of th e Item meth od is similar to th e addChildCategory con ven ien ce meth od of th e Category class. It’s used by a clien t to man ipulate th e relation sh ip between Item an d a Category. For th e sake of readability, we won ’t sh ow convenience methods in future code samples and assume you’ll add them according to your own taste. Convenience methods for association handling is however not the only way to improve a domain model implemen tation . You can also add logic to your accessor meth ods. 3 .2 .5 Adding logic to accessor methods
O n e of th e reason s we like to use JavaBean s-style accessor meth ods is th at th ey provide en capsulation : Th e h idden in tern al implemen tation of a property can be ch an ged with out an y ch an ges to th e public in ter face. Th is allows you to abstract th e in tern al data structure of a class—the in stan ce variables—from th e design of the database. For example, if your database stores a name of the user as a single NAME column , but your User class has firstname an d lastname properties, you can add the followin g persisten t name property to your class: public class User { private String firstname; private String lastname; ... public String getName() { return firstname + ' ' + lastname; } public void setName(String name) { StringTokenizer t = new StringTokenizer(name); firstname = t.nextToken(); lastname = t.nextToken(); ) ... }
Later, you’ll see th at a H ibern ate custom type is probably a better way to h an dle many of these kinds of situations. However, it helps to have several options. Accessor meth ods can also perform validation. For instance, in the following example, the setFirstName() meth od verifies th at th e n ame is capitalized: public class User { private String firstname; ...
74
CHAPTER 3
Mapping persistent classes public String getFirstname() { return firstname; } public void setFirstname(String firstname) throws InvalidNameException { if ( !StringUtil.isCapitalizedName(firstname) ) throw new InvalidNameException(firstname); this.firstname = firstname; ) ... }
H owever, H ibern ate will later use our accessor meth ods to populate th e state of an object wh en loadin g th e object from th e database. Sometimes we would prefer that this validation not occur wh en Hibern ate is in itializin g a n ewly loaded object. In th at case, it migh t make sense to tell H ibern ate to directly access th e instance variables ( we m ap th e p rop erty with access="field" in H ibern ate m etad ata) , forcin g H ibern ate to bypass th e setter meth od an d access th e in stan ce variable directly. Another issue to consider is dirty checking. Hibernate automatically detects object state ch an ges in order to syn ch ron ize th e updated state with th e database. It’s usually completely safe to return a differen t object from th e getter meth od to th e object passed by H ibern ate to th e setter. H ibern ate will compare th e objects by value—n ot by object iden tity—to determin e if th e property’s persisten t state n eeds to be updated. For example, th e followin g getter meth od won ’t result in un n ecessary SQL UPDATEs: public String getFirstname() { return new String(firstname); }
H owever, th ere is on e ver y im p ortan t excep tion . Collection s are com p ared by identity! For a property mapped as a persisten t collection , you sh ould return exactly th e same collection instance from the getter method as Hibernate passed to the setter method. If you don’t, Hibernate will update the database, even if no update is necessary, every time the session synchronizes state held in memory with the database. This kind of code should almost always be avoided in accessor meth ods: public void setNames(List namesList) { names = (String[]) namesList.toArray(); } public List getNames() { return Arrays.asList(names); }
Defining the mapping metadata
75
You can see th at H ibern ate doesn ’t un n ecessarily restrict th e JavaBean s ( PO JO ) programming model. You’re free to implement whatever logic you n eed in accessor meth ods ( as lon g as you keep th e same collection instance in both getter and setter) . If absolutely n ecessar y, you can tell H ibern ate to use a differen t access strategy to read an d set th e state of a property ( for example, direct in stan ce field access) , as you’ll see later. Th is kin d of tran sparen cy guaran tees an in depen den t an d reusable domain model implemen tation . Now that we’ve implemented some persistent classes of our domain model, we n eed to defin e th e ORM.
3.3 Defining the mapping metadata O RM tools require a metadata format for th e application to specify th e mappin g
between classes an d tables, properties an d column s, association s an d foreign keys, Java types an d SQL types. Th is in formation is called th e object/ relational mapping metadata . It defin es th e tran sformation between th e differen t data type systems and relationship representations. It’s our job as developers to defin e an d main tain th is metadata. We discuss various approaches in this section. 3 .3 .1 M etadata in XM L
An y O RM solution sh ould provide a h uman -readable, easily h an d-editable mappin g format, n ot on ly a GUI mappin g tool. Curren tly, th e most popular object/ relational metadata format is XML. Mappin g documen ts written in an d with XML are ligh tweigh t, are h uman readable, are easily man ipulated by version -con trol systems an d text editors, an d may be customized at deploymen t time ( or even at runtime, with programmatic XML gen eration ) . But is XML-based metadata really th e best approach ? A certain backlash again st the overuse of XML can be seen in the Java community. Every framework and application server seems to require its own XML descriptors. In our view, th ere are th ree main reason s for th is backlash : ■
Man y existin g metadata formats weren ’t designed to be readable and easy to edit by h an d. In particular, a major cause of pain is th e lack of sen sible defaults for attribute an d elemen t values, requirin g sign ifican tly more typin g th an sh ould be n ecessary.
■
Metadata-based solutions were often used in appropriately. Metadata is n ot, by nature, more flexible or maintainable than plain Java code.
76
CHAPTER 3
Mapping persistent classes
■
Good XML editors, especially in IDEs, aren’t as common as good Java cod in g en viron m en ts. Worst, an d m ost easily fixable, a d ocu m en t typ e declaration ( DTD ) often isn ’t provided, preven tin g auto-completion an d validation . An oth er problem are DTDs th at are too gen eric, wh ere ever y declaration is wrapped in a generic “extension” of “meta” element.
Th ere is n o gettin g aroun d th e n eed for text-based metadata in O RM. H owever, Hibern ate was design ed with full awaren ess of th e typical metadata problems. Th e metadata format is extremely readable an d defin es useful default values. Wh en attribu te valu es are m issin g, H ibern ate u ses reflection on th e m apped class to h elp determin e th e defaults. H ibern ate comes with a documen ted an d complete DTD. Fin ally, IDE support for XML h as improved lately, an d modern IDEs provide dynamic XML validation an d even an auto-complete feature. If th at’s n ot en ough for you, in ch apter 9 we demon strate some tools th at may be used to gen erate Hibernate XML mappin gs. Let’s look at th e way you can use XML metadata in Hibernate. We created the Category class in th e previous section ; n ow we n eed to map it to th e CATEGORY table in th e database. To do th at, we use th e XML mappin g documen t in listin g 3.4. Listing 3 .4
Hibernate XM L mapping of the Category class
B
C Mapping
D
declaration Category class mapped
E Identifier
F Name property mapped
to NAME column
Defining the mapping metadata
77
B
Th e Hibern ate mappin g DTD should be declared in every mapping file; it’s required for syn tactic validation of th e XML.
C
Mappings are declared inside a element. You can include as many class mappings as you like, along with certain other special declarations that we’ll mention later in the book.
D
Th e class Category ( in th e package org.hibernate.auction.model) is mapped to the table CATEGORY. Every row in this table represents one instance of type Category.
E
We h aven ’t discussed th e con cept of object identity, so you may be surprised by th is mappin g elemen t. Th is complex topic is covered in section 3.4. To un derstan d th is mappin g, it’s sufficien t to kn ow th at every record in th e CATEGORY table will h ave a primary key value th at match es th e object iden tity of the instance in memory. Th e mapping element is used to define the details of object identity.
F
Th e property n ame of type String is mapped to a database column NAME. Note that the type declared in the mapping is a built-in Hibernate type ( string) , n ot the type of the Java property or the SQL column type. Th in k about th is as th e “mappin g data type.” We take a closer look at th ese types in ch apter 6, section 6.1, “Un derstan din g th e Hibern ate type system.” We’ve intentionally left the association mappin gs out of th is example. Association mappings are more complex, so we’ll return to th em in section 3.7. TRY IT
Starting Hibernate with your first persistent class—After you’ve written th e POJO code for the Category an d saved its Hibern ate mappin g to an XML file, you can start up Hibern ate with th is mappin g an d try some operation s. H owever, the POJO code for Category sh own earlier wasn ’t complete: You h ave to add an addition al property n amed id of type java.lang.Long an d its accessor meth ods to en able H ibern ate iden tity man agemen t, as discussed later in th is ch apter. Creatin g th e database sch ema with its tables for such a simple class should be n o problem for you. O bserve th e log of your application to ch eck for a successful startup an d creation of a n ew SessionFactory from th e Configuration sh own in ch apter 2. If you can ’t wait an y lon ger, ch eck out th e save(), load(), and delete() meth ods of th e Session you can obtain from th e SessionFactory. Make sure you correctly deal with tran saction s; th e easiest way is to get a n ew Transaction object with Session.beginTransaction() and commit it with its commit() meth od after you’ve made your calls. See th e code in section 2.1, “H ello World with H ibern ate,” if you’d like to copy some example code for your first test.
78
CHAPTER 3
Mapping persistent classes
Alth ough it’s possible to declare mappin gs for multiple classes in on e mappin g file by u sin g m u ltip le elem en ts, th e recom m en d ed p ractice ( an d th e practice expected by some Hibern ate tools) is to use on e mappin g file per persisten t class. Th e con ven tion is to give th e file th e same n ame as th e mapped class, appendin g an hbm suffix: for example, Category.hbm.xml. Let’s discuss basic class and property mappin gs in Hibern ate. Keep in min d th at we still n eed to come back later in th is chapter to the problem of mapping association s between persisten t classes. 3 .3 .2 Basic property and class mappings
A typical Hibernate property mapping defin es a JavaBean s property n ame, a database column n ame, an d th e n ame of a H ibern ate type. It maps a JavaBean style property to a table column . Th e basic declaration provides man y variation s an d optional settin gs. It’s often possible to omit th e type n ame. So, if description is a property of ( Java) type java.lang.String, H ibern ate will use th e H ibern ate type string by default ( we discuss th e Hibern ate type system in ch apter 6) . Hibern ate uses reflection to determ in e th e Java type of th e property. Th us, th e followin g mappings are equivalent:
You can even omit th e column n ame if it’s the same as the property name, ignorin g case. ( Th is is on e of th e sen sible defaults we mentioned earlier.) For some cases you migh t n eed to use a element instead of the column attribute. The elemen t provides more flexibility; it h as more option al attributes and may appear more than once. Th e followin g two property mappin gs are equivalent:
Th e elemen t ( an d especially th e elemen t) also defin es certain attributes th at apply main ly to automatic database sch ema gen eration . If you aren’t using the hbm2ddl tool ( see section 9.2, “Automatic sch ema gen eration ”) to generate the database schema, you can safely omit th ese. However, it’s still preferable to in clude at least th e not-null attribute, since Hibernate will then be able to report illegal n ull property values with out goin g to th e database:
Defining the mapping metadata
79
Detection of illegal n ull values is main ly useful for providing sen sible exception s at developmen t time. It isn ’t in ten ded for true data validation , wh ich is outside the scope of Hibernate. Some properties don ’t map to a column at all. In particular, a derived property takes its value from an SQL expression . Using derived properties
Th e valu e of a d erived p rop erty is calcu lated at ru n tim e by evalu ation of an expression . You defin e th e expression usin g th e formula attribute. For example, we migh t map a totalIncludingTax property with out h avin g a sin gle column with th e total price in th e database:
Th e given SQL form ula is evaluated ever y tim e th e en tity is retrieved from th e d atabase. Th e p rop erty d oesn ’t h ave a column attribu te ( or su b-elem en t) an d never appears in an SQL INSERT or UPDATE, on ly in SELECTs. Formulas may refer to column s of th e database table, call SQL fun ction s, an d in clude SQL subselects. This example, mapping a derived property of item, uses a correlated subselect to calculate th e average amount of all bids for an item:
Notice that unqualified column names refer to table columns of the class to which th e derived property belon gs. As we mentioned earlier, Hibernate doesn ’t require property accessor methods on PO JO classes, if you defin e a n ew property access strategy. Property access strategies
Th e access attribute allows you to specify h ow H ibern ate sh ould access property values of th e PO JO . Th e default strategy, property, uses th e property accessors ( get/ set meth od pair) . Th e field strategy uses reflection to access th e in stan ce variable directly. Th e followin g “property” mapping doesn’t require a get/ set pair:
Access to properties via accessor methods is con sidered best practice by th e Hibernate commun ity. It provides an extra level of abstraction between the Java domain model an d th e data model, beyon d wh at is already provided by H ibern ate. Properties are more flexible; for example, property defin ition s may be overridden by persistent subclasses. If neither accessor meth ods n or direct instance variable access is appropriate, you can defin e your own customized property access strategy by implemen tin g th e in terface net.sf.hibernate.property.PropertyAccessor an d n ame it in th e access attribute. Controlling insertion and updates
For properties th at map to column s, you can con trol wh eth er th ey appear in th e INSERT statemen t by usin g th e insert attribute an d wh eth er th ey appear in th e UPDATE statement by using the update attribute.
Th e followin g property n ever h as its state written to th e database:
Th e property name of th e JavaBean is th erefore immutable an d can be read from the database but n ot modified in an y way. If th e complete class is immutable, set the immutable="false" in the class mapping In addition , th e dynamic-insert attribute tells Hibern ate wh eth er to in clude unmodified property values in an SQL INSERT , an d th e dynamic-update attribute tells Hibernate whether to include unmodified properties in the SQL UPDATE:
...
Th ese are both class-level settin gs. En ablin g eith er of th ese settin gs will cau se H ibern ate to gen erate some SQL at run time, in stead of usin g th e SQL cach ed at startu p tim e. Th e p er form an ce cost is u su ally sm all. Fu rth erm ore, leavin g ou t colu m n s in an in sert ( an d esp ecially in an u p d ate) can occasion ally im p rove per formance if your tables define many columns.
Defining the mapping metadata
81
Using quoted SQL identifiers
By default, H ibern ate d oesn ’t quote table an d column n ames in th e gen erated SQL. Th is makes th e SQL sligh tly more readable an d also allows us to take advan tage of th e fact th at m ost SQ L d atabases are case in sen sitive wh en com p arin g u n qu oted id en tifiers. From tim e to tim e, esp ecially in legacy d atabases, you ’ll en coun ter iden tifiers with stran ge ch aracters or wh itespace, or you may wish to force case-sensitivity. If you quote a table or column name with backticks in the mapping document, Hibernate will always quote this identifier in the generated SQL. The following property declaration forces Hibernate to generate SQL with the quoted column name "Item Description". Hibernate will also know that Microsoft SQL Server needs the variation [Item Description] and that MySQL requires `Item Description`.
Th ere is n o way, apart from quotin g all table an d column n ames in backticks, to force Hibernate to use quoted identifiers everywhere. Naming conventions
You’ll often en coun ter organ ization s with strict con ven tion s for database table and column n ames. Hibern ate provides a feature that allows you to enforce namin g stan dards automatically. Suppose th at all table n ames in CaveatEmptor should follow the pattern CE_