Está en la página 1de 60

iBATIS3

UserGuide

WarningaboutCopyingCodefromthisDocument
No, this is not a legal warning. It is one to help you keep your sanity. Modern word processors do a great job of making text readable and formatted in an aesthetically pleasing way. However, they also tend to completely ruin code examples by inserting special characters, sometimes that look exactly the same as the one you think you want. Quotes and hyphens are a perfect example the quotes and hyphen you see to the left will not work as quotes in an IDE or text editor, at least not the way you intend. So read this document, enjoy it and hopefully it is helpful to you. When it comes to code examples, seek out the examples included with the download (including unit tests etc.), or examples from the website or mailing list.

Helpmakethisdocumentationbetter
If you find this documentation lacking in any way, or missing documentation for a feature, then the best thing to do is learn about it and then write the documentation yourself! We accept public documentation contributions through our wiki at: http://opensource.atlassian.com/confluence/oss/display/IBATIS/Contribute+Documentation Yourethebestauthorofthisdocumentation,peoplelikeyouhavetoreadit!

Contents
WhatisiBATIS? ............................................................................................................................................ 5 GettingStarted ............................................................................................................................................ 5 BuildingSqlSessionFactoryfromXML...................................................................................................... 5 BuildingSqlSessionFactorywithoutXML ................................................................................................. 6 AcquiringaSqlSessionfromSqlSessionFactory ....................................................................................... 6 ExploringMappedSQLStatements ......................................................................................................... 7 ANoteaboutNamespaces .................................................................................................................. 8 ScopeandLifecycle.................................................................................................................................. 9 MapperConfigurationXML ....................................................................................................................... 10 properties .............................................................................................................................................. 10 settings................................................................................................................................................... 11 typeAliases............................................................................................................................................. 12 typeHandlers.......................................................................................................................................... 13 objectFactory ......................................................................................................................................... 14 plugins.................................................................................................................................................... 15 environments......................................................................................................................................... 16 transactionManager .......................................................................................................................... 17 dataSource ......................................................................................................................................... 18 mappers ................................................................................................................................................. 20 SQLMapXMLFiles..................................................................................................................................... 20 select...................................................................................................................................................... 21 insert,update,delete ............................................................................................................................ 22 sql........................................................................................................................................................... 25 Parameters............................................................................................................................................. 25

iBATIS3UserGuide

resultMap............................................................................................................................................... 27 AdvancedResultMapping ................................................................................................................. 29 id,result ............................................................................................................................................. 31 SupportedJDBCTypes ....................................................................................................................... 31 constructor ........................................................................................................................................ 32 association ......................................................................................................................................... 33 collection ........................................................................................................................................... 36 discriminator...................................................................................................................................... 38 cache...................................................................................................................................................... 40 UsingaCustomCache........................................................................................................................ 41 cacheref ................................................................................................................................................ 42 DynamicSQL .............................................................................................................................................. 42 if ............................................................................................................................................................. 43 choose,when,otherwise....................................................................................................................... 43 trim,where,set ..................................................................................................................................... 44 foreach................................................................................................................................................... 46 JavaAPI ...................................................................................................................................................... 47 DirectoryStructure ................................................................................................................................ 47 SqlSessions............................................................................................................................................. 48 SqlSessionFactoryBuilder ................................................................................................................... 48 SqlSessionFactory .............................................................................................................................. 50 SqlSession .......................................................................................................................................... 51 SelectBuilder .............................................................................................................................................. 57

16August2009

iBATIS3UserGuide

WhatisiBATIS?
iBATISisafirstclasspersistenceframeworkwithsupportforcustomSQL,storedproceduresand advancedmappings.iBATISeliminatesalmostalloftheJDBCcodeandmanualsettingofparameters andretrievalofresults.iBATIScanusesimpleXMLorAnnotationsforconfigurationandmapprimitives, MapinterfacesandJavaPOJOs(PlainOldJavaObjects)todatabaserecords.

GettingStarted
EveryiBATISapplicationcentersaroundaninstanceofSqlSessionFactory.ASqlSessionFactoryinstance canbeacquiredbyusingtheSqlSessionFactoryBuilder.SqlSessionFactoryBuildercanbuilda SqlSessionFactoryinstancefromanXMLconfigurationfile,offromacustompreparedinstanceofthe Configurationclass.

BuildingSqlSessionFactoryfromXML
BuildingaSqlSessionFactoryinstancefromanXMLfileisverysimple.Itisrecommendedthatyouusea classpathresourceforthisconfiguration,butyoucoulduseanyReaderinstance,includingonecreated fromaliteralfilepathorafile://URL.iBATISincludesautilityclass,calledResources,thatcontainsa numberofmethodsthatmakeitsimplertoloadresourcesfromtheclasspathandotherlocations.
String resource = "org/apache/ibatis/example/Configuration.xml"; Reader reader = Resources.getResourceAsReader(resource); sqlMapper = new SqlSessionFactoryBuilder().build(reader);

TheconfigurationXMLfilecontainssettingsforthecoreoftheiBATISsystem,includingaDataSourcefor acquiringdatabaseConnectioninstances,aswellasaTransactionManagerfordetermininghow transactionsshouldbescopedandcontrolled.ThefulldetailsoftheXMLconfigurationfilecanbefound laterinthisdocument,buthereisasimpleexample:


<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/apache/ibatis/example/BlogMapper.xml"/> </mappers> </configuration>

16August2009

iBATIS3UserGuide WhilethereisalotmoretotheXMLconfigurationfile,theaboveexamplepointsoutthemostcritical parts.NoticetheXMLheader,requiredtovalidatetheXMLdocument.Thebodyoftheenvironment elementcontainstheenvironmentconfigurationfortransactionmanagementandconnectionpooling. ThemapperselementcontainsalistofmapperstheXMLfilesthatcontaintheSQLcodeandmapping definitions.

BuildingSqlSessionFactorywithoutXML
IfyouprefertodirectlybuildtheconfigurationfromJava,ratherthanXML,orcreateyourown configurationbuilder,iBATISprovidesacompleteConfigurationclassthatprovidesallofthesame configurationoptionsastheXMLfile.
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource(); TransactionFactory transactionFactory = new JdbcTransactionFactory(); Environment environment = new Environment("development", transactionFactory, dataSource); Configuration configuration = new Configuration(environment); configuration.addMapper(BlogMapper.class); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

Noticeinthiscasetheconfigurationisaddingamapperclass.MapperclassesareJavaclassesthat containSQLMappingAnnotationsthatavoidtheneedforXML.However,duetosomelimitationsof JavaAnnotationsandthecomplexityofsomeiBATISmappings,XMLmappingisstillrequiredforthe mostadvancedmappings(e.g.NestedJoinMapping).Forthisreason,iBATISwillautomaticallylookfor andloadapeerXMLfileifitexists(inthiscase,BlogMapper.xmlwouldbeloadedbasedonthe classpathandnameofBlogMapper.class).Moreonthislater.

AcquiringaSqlSessionfromSqlSessionFactory
NowthatyouhaveaSqlSessionFactory,asthenamesuggests,youcanacquireaninstanceof SqlSession.TheSqlSessioncontainsabsolutelyeverymethodneededtoexecuteSQLcommandsagainst thedatabase.YoucanexecutemappedSQLstatementsdirectlyagainsttheSqlSessioninstance.For exmaple:
SqlSession session = sqlMapper.openSession(); try { Blog blog = (Blog) session.select( "org.apache.ibatis.example.BlogMapper.selectBlog", 101); } finally { session.close(); }

Whilethisapproachworks,andisfamiliartousersofpreviousversionsofiBATIS,thereisnowacleaner approach.Usinganinterface(e.g.BlogMapper.class)thatproperlydescribestheparameterandreturn valueforagivenstatement,youcannowexecutecleanerandmoretypesafecode,withouterrorprone stringliteralsandcasting.

16August2009

iBATIS3UserGuide Forexample:
SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlog(101); } finally { session.close(); }

Nowlet'sexplorewhatexactlyisbeingexecutedhere.

ExploringMappedSQLStatements
AtthispointyoumaybewonderingwhatexactlyisbeingexecutedbytheSqlSessionorMapperclass. ThetopicofMappedSQLStatementsisabigone,andthattopicwilllikelydominatethemajorityofthis documentation.Buttogiveyouanideaofwhatexactlyisbeingrun,hereareacoupleofexamples. Ineitheroftheexamplesabove,thestatementscouldhavebeendefinedbyeitherXMLorAnnotations. Let'stakealookatXMLfirst.ThefullsetoffeaturesprovidedbyiBATIScanberealizedbyusingthe XMLbasedmappinglanguagethathasmadeiBATISpopularovertheyears.Ifyou'veusediBATIS before,theconceptwillbefamiliartoyou,buttherehavebeennumerousimprovementstotheXML mappingdocumentsthatwillbecomeclearlater.HereisanexampleofanXMLbasedmapped statementthatwouldsatisfytheaboveSqlSessioncalls.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd"> <mapper namespace="org.apache.ibatis.example.BlogMapper"> <select id="selectBlog" parameterType="int" resultType="Blog"> select * from Blog where id = #{id} </select> </mapper>

Whilethislookslikealotofoverheadforthissimpleexample,itisactuallyverylight.Youcandefineas manymappedstatementsinasinglemapperXMLfileasyoulike,soyougetalotofmilageoutofthe XMLheaderanddoctypedeclaration.Therestofthefileisprettyselfexplanatory.Itdefinesanamefor themappedstatementselectBlog,inthenamespaceorg.apache.ibatis.example.BlogMapper,which wouldallowyoutocallitbyspecifyingthefullyqualifiednameof org.apache.ibatis.example.BlogMapper.selectBlog,aswedidaboveinthefollowingexample:


Blog blog = (Blog) session.select( "org.apache.ibatis.example.BlogMapper.selectBlog", 101);

NoticehowsimilarthisistocallingamethodonafullyqualifiedJavaclass,andthere'sareasonforthat. ThisnamecanbedirectlymappedtoaMapperclassofthesamenameasthenamespace,witha

16August2009

iBATIS3UserGuide methodthatmatchesthename,parameter,andreturntypeasthemappedselectstatement.This allowsyoutoverysimplycallthemethodagainsttheMapperinterfaceasyousawabove,buthereitis againinthefollowingexample:


BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlog(101);

Thesecondapproachhasalotofadvantages.First,itdoesn'tdependonastringliteral,soit'smuch safer.Second,ifyourIDEhascodecompletion,youcanleveragethatwhennavigatingyourmapped SQLstatements.Third,youdon'tneedtocastthereturntype,astheBlogMapperinterfacecanhave clean,typesafereturntypes(andatypesafeparameter).

ANoteaboutNamespaces
Namespaces were optional in previous versions of iBATIS, which was confusing and unhelpful. Namespaces are now required and have a purpose beyond simply isolating statements with longer, fully-qualified names. Namespaces enable the interface bindings as you see here, and even if you dont think youll use them today, you should follow these practices laid out here in case you change your mind. Using the namespace once, and putting it in a proper Java package namespace will clean up your code and improve the usability of iBATIS in the long term. There'sonemoretricktoMapperclasseslikeBlogMapper.Theirmappedstatementsdon'tneedtobe mappedwithXMLatall.InsteadtheycanuseJavaAnnotations.Forexample,theXMLabovecouldbe eliminatedandreplacedwith:
package org.apache.ibatis.example; public interface BlogMapper { @Select("SELECT * FROM blog WHERE id = #{id}") Blog selectBlog(int id); }

Theannotationsarealotcleanerforsimplestatements,however,JavaAnnotationsarebothlimitedand messierformorecomplicatedstatements.Therefore,ifyouhavetodoanythingcomplicated,you're betteroffwithXMLmappedstatements. Itwillbeuptoyouandyourprojectteamtodeterminewhichisrightforyou,andhowimportantitisto youthatyourmappedstatementsbedefinedinaconsistentway.Thatsaid,you'reneverlockedintoa singleapproach.YoucanveryeasilymigrateAnnotationbasedMappedStatementstoXMLandvice versa.

16August2009

iBATIS3UserGuide

ScopeandLifecycle
It'sveryimportanttounderstandthevariousscopesandlifecyclesclasseswe'vediscussedsofar.Using themincorrectlycancausesevereconcurrencyproblems.

SqlSessionFactoryBuilder
Thisclasscanbeinstantiated,usedandthrownaway.Thereisnoneedtokeepitaroundonceyou've createdyourSqlSessionFactory.ThereforethebestscopeforinstancesofSqlSessionFactoryBuilderis methodscope(i.e.alocalmethodvariable).YoucanreusetheSqlSessionFactoryBuildertobuild multipleSqlSessionFactoryinstances,butit'sstillbestnottokeepitaroundtoensurethatalloftheXML parsingresourcesarefreedupformoreimportantthings.

SqlSessionFactory
Oncecreated,theSqlSessionFactoryshouldexistforthedurationofyourapplicationexecution.There shouldbelittleornoreasontoeverdisposeofitorrecreateit.It'sabestpracticetonotrebuildthe SqlSessionFactorymultipletimesinanapplicationrun.Doingsoshouldbeconsideredabadsmell. ThereforethebestscopeofSqlSessionFactoryisapplicationscope.Thiscanbeachievedanumberof ways.ThesimplestistouseaSingletonpatternorStaticSingletonpattern.However,neitherofthose iswidelyacceptedasabestpractice.Instead,youmightprefertoinvestigateadependencyinjection containersuchasGoogleGuiceorSpring.Suchframeworkswillallowyoutocreateprovidersthatwill managethesingletonlifecycleofSqlSessionFactoryforyou.

SqlSession
EachthreadshouldhaveitsowninstanceofSqlSession.InstancesofSqlSessionarenottobeshared andarenotthreadsafe.Thereforethebestscopeisrequestormethodscope.Neverkeepreferences toaSqlSessioninstanceinastaticfieldorevenaninstancefieldofaclass.Neverkeepreferencestoa SqlSessioninanysortofmanagedscope,suchasHttpSessionofoftheServletframework.Ifyou're usingawebframeworkofanysort,considertheSqlSessiontofollowasimilarscopetothatofanHTTP request.Inotherwords,uponrecievinganHTTPrequest,youcanopenaSqlSession,thenupon returningtheresponse,youcancloseit.Closingthesessionisveryimportant.Youshouldalways ensurethatit'sclosedwithinafinallyblock.Thefollowingisthestandardpatternforensuringthat SqlSessionsareclosed:
SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); }

16August2009

iBATIS3UserGuide

Usingthispatternconsistentlythroughoutyourcodewillensurethatalldatabaseresourcesareproperly closed(assumingyoudidnotpassinyourownconnection,whichisanindicationtoiBATISthatyouwish tomanageyourownconnectionresources).

MapperInstances
Mappersareinterfacesthatyoucreatetobindtoyourmappedstatements.Instancesofthemapper interfacesareacquiredfromtheSqlSession.Assuch,technicallythebroadestscopeofanymapper instanceisthesameastheSqlSessionfromwhichtheywererequestsd.However,thebestscopefor mapperinstancesismethodscope.Thatis,theyshouldberequestedwithinthemethodthattheyare used,andthenbediscarded.Theydonotneedtobeclosedexplicitly.Whileit'snotaproblemtokeep themaroundthroughoutarequest,similartotheSqlSession,youmightfindthatmanagingtoomany resourcesatthislevelwillquicklygetoutofhand.Keepitsimple,keepMappersinthemethodscope. Thefollowingexampledemonstratesthispractice.
SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); // do work } finally { session.close(); }

MapperConfigurationXML
TheiBATISXMLconfigurationfilecontainssettingsandpropertiesthathaveadramaticeffectonhow iBATISbehaves.Thehighlevelstructureofthedocumentisasfollows: configuration o properties o settings o typeAliases o typeHandlers o objectFactory o plugins o environments environment transactionManager dataSource o mappers

properties
Theseareexternalizable,substitutablepropertiesthatcanbeconfiguredinatypicalJavaPropertiesfile instance,orpassedinthroughsubelementsofthepropertieselement.Forexample:
<properties resource="org/apache/ibatis/example/config.properties">

16August2009

10

iBATIS3UserGuide
<property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties>

Thepropertiescanthenbeusedthroughouttheconfigurationfilestosubstitutevaluesthatneedtobe dynamicallyconfigured.Forexample:
<dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>

Theusernameandpasswordinthisexamplewillbereplacedbythevaluessetintheproperties elements.Thedriverandurlpropertieswouldbereplacedwithvaluescontainedfromthe config.propertiesfile.Thisprovidesalotofoptionsforconfiguration. PropertiescanalsobepassedintotheSqlSessionBuilder.build()methods.Forexample:


SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, props); // ... or ... SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, props);

Ifapropertyexistsinmorethanoneoftheseplaces,iBATISloadstheminthefollowingorder: Propertiesspecifiedinthebodyofthepropertieselementarereadfirst, Propertiesloadedfromtheclasspathresourceorurlattributesofthepropertieselementare readsecond,andoverrideanyduplicatepropertiesalreadyspecified, Propertiespassedasamethodparameterarereadlast,andoverrideanyduplicateproperties thatmayhavebeenloadedfromthepropertiesbodyandtheresource/urlattributes.

Thus,thehighestprioritypropertiesarethosepassedinasamethodparameter,followedby resource/urlattributesandfinallythepropertiesspecifiedinthebodyofthepropertieselement.

settings
TheseareextremelyimportanttweaksthatmodifythewaythatiBATISbehavesatruntime.The followingtabledescribesthesettings,theirmeaningsandtheirdefaultvalues. Setting cacheEnabled Description Globallyenablesordisablesanycaches configuredinanymapperunderthis configuration. Globallyenablesordisableslazyloading. ValidValues true|false Default true

lazyLoadingEnabled

true|false

true

16August2009

11

iBATIS3UserGuide Whendisabled,allassociationswillbeeagerly loaded. multipleResultSetsEnabled AllowsordisallowsmultipleResultSetstobe returnedfromasinglestatement(compatible driverrequired). useColumnLabel Usesthecolumnlabelinsteadofthecolumn name.Differentdriversbehavedifferentlyin thisrespect.Refertothedriver documentation,ortestoutbothmodesto determinehowyourdriverbehaves. useGeneratedKeys AllowsJDBCsupportforgeneratedkeys.A compatibledriverisrequired.Thissetting forcesgeneratedkeystobeusedifsetto true,assomedriversdenycompatibilitybut stillwork(e.g.Derby). enhancementEnabled EnablessupportforCGLIBandlazyloading withconcretetypes.Normallyonlyinterfaces cansupportlazyloading.CGLIBallows dynamicproxiestobecreatedforanynon finaltype. defaultExecutorType Configuresthedefaultexecutor.SIMPLE executordoesnothingspecial.REUSE executorreusespreparedstatements.BATCH executorreusesstatementsandbatches updates. defaultStatementTimeout Setsthetimeoutthatdetermineshowlong thedriverwillwaitforaresponsefromthe database. Anexampleofthesettingselementfullyconfiguredisasfollows:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="enhancementEnabled" value="false"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25000"/> </settings>

true|false

true

true|false

true

true|false

False

true|false

False

SIMPLE REUSE BATCH

SIMPLE

Anypositive integer

NotSet (null)

typeAliases
AtypealiasissimplyashorternameforaJavatype.It'sonlyrelevanttotheXMLconfigurationand simplyexiststoreduceredundanttypingoffullyqualifiedclassnames.Forexample:
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/>

16August2009

12

iBATIS3UserGuide
<typeAlias alias="Comment" type="domain.blog.Comment"/> <typeAlias alias="Post" type="domain.blog.Post"/> <typeAlias alias="Section" type="domain.blog.Section"/> <typeAlias alias="Tag" type="domain.blog.Tag"/> </typeAliases>

Withthisconfiguration,Blogcannowbeusedanywherethatdomain.blog.Blogcouldbe.

typeHandlers
WheneveriBATISsetsaparameteronaPreparedStatementorretrievesavaluefromaResultSet,a TypeHandlerisusedtoretrievethevalueinameansappropriatetotheJavatype.Thefollowingtable describesthedefaultTypeHandlers. TypeHandler BooleanTypeHandler ByteTypeHandler ShortTypeHandler IntegerTypeHandler LongTypeHandler FloatTypeHandler DoubleTypeHandler BigDecimalTypeHandler StringTypeHandler ClobTypeHandler NStringTypeHandler NClobTypeHandler ByteArrayTypeHandler BlobTypeHandler DateTypeHandler DateOnlyTypeHandler TimeOnlyTypeHandler SqlTimestampTypeHandler SqlDateTypeHadler SqlTimeTypeHandler ObjectTypeHandler EnumTypeHandler Youcanoverridethetypehandlersorcreateyourowntodealwithunsupportedornonstandardtypes. Todoso,simplyimplementingtheTypeHandlerinterface(org.apache.ibatis.type)andmapyournew TypeHandlerclasstoaJavatype,andoptionallyaJDBCtype.Forexample:
// ExampleTypeHandler.java public class ExampleTypeHandler implements TypeHandler { public void setParameter( PreparedStatement ps, int i, Object parameter,JdbcType jdbcType)

JavaTypes Boolean,boolean Byte,byte Short,short Integer,int Long,long Float,float Double,double BigDecimal String String String String byte[] byte[] Date(java.util) Date(java.util) Date(java.util) Timestamp(java.sql) Date(java.sql) Time(java.sql) Any EnumerationType

JDBCTypes AnycompatibleBOOLEAN AnycompatibleNUMERICorBYTE AnycompatibleNUMERICorSHORTINTEGER AnycompatibleNUMERICorINTEGER AnycompatibleNUMERICorLONGINTEGER AnycompatibleNUMERICorFLOAT AnycompatibleNUMERICorDOUBLE AnycompatibleNUMERICorDECIMAL CHAR,VARCHAR CLOB,LONGVARCHAR NVARCHAR,NCHAR NCLOB Anycompatiblebytestreamtype BLOB,LONGVARBINARY TIMESTAMP DATE TIME TIMESTAMP DATE TIME OTHER,orunspecified VARCHARanystringcompatibletype,asthe codeisstored(nottheindex).

16August2009

13

iBATIS3UserGuide
throws SQLException { ps.setString(i, (String) parameter); } public Object getResult( ResultSet rs, String columnName) throws SQLException { return rs.getString(columnName); } public Object getResult( CallableStatement cs, int columnIndex) throws SQLException { return cs.getString(columnIndex); } } // MapperConfig.xml <typeHandlers> <typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.example.ExampleTypeHandler"/> </typeHandlers>

UsingsuchaTypeHandlerwouldoverridetheexistingtypehandlerforJavaStringpropertiesand VARCHARparametersandresults.NotethatiBATISdoesnotintrospectuponthedatabasemetadatato determinethetype,soyoumustspecifythatitsaVARCHARfieldintheparameterandresultmappings tohookinthecorrecttypehandler.ThisisduetothefactthatiBATISisunawareofthedatatypeuntil thestatementisexecuted.

objectFactory
EachtimeiBATIScreatesanewinstanceofaresultobject,itusesanObjectFactoryinstancetodoso. ThedefaultObjectFactorydoeslittlemorethaninstantiatethetargetclasswithadefaultconstructor,or aparameterizedconstructorifparametermappingsexist.Ifyouwanttooverridethedefaultbehaviour oftheObjectFactory,youcancreateyourown.Forexample:
// ExampleObjectFactory.java public class ExampleObjectFactory extends DefaultObjectFactory { public Object create(Class type) { return super.create(type); } public Object create( Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) { return super.create(type, constructorArgTypes, constructorArgs); } public void setProperties(Properties properties) { super.setProperties(properties); } } // MapperConfig.xml <objectFactory type="org.apache.ibatis.example.ExampleObjectFactory"> <property name="someProperty" value="100"/>

16August2009

14

iBATIS3UserGuide
</objectFactory>

TheObjectFactoryinterfaceisverysimple.Itcontainstwocreatemethods,onetodealwiththedefault constructor,andtheothertodealwithparameterizedconstructors.Finally,thesetPropertiesmethod canbeusedtoconfiguretheObjectFactory.PropertiesdefinedwithinthebodyoftheobjectFactory elementwillbepassedtothesetPropertiesmethodafterinitializationofyourObjectFactoryinstance.

plugins
iBATISallowsyoutointerceptcallstoatcertainpointswithintheexecutionofamappedstatement.By default,iBATISallowspluginstointerceptmethodcallsof: Executor (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed) ParameterHandler (getParameterObject,setParameters) ResultSetHandler (handleResultSets,handleOutputParameters) StatementHandler (prepare,parameterize,batch,update,query)

Thedetailsoftheseclassesmethodscanbediscoveredbylookingatthefullmethodsignatureofeach, andthesourcecodewhichisavailablewitheachiBATISrelease.Youshouldunderstandthebehaviour ofthemethodyoureoverriding,assumingyouredoingsomethingmorethanjustmonitoringcalls.If youattempttomodifyoroverridethebehaviourofagivenmethod,yourelikelytobreakthecoreof iBATIS.Thesearelowlevelclassesandmethods,sousepluginswithcaution. Usingpluginsisprettysimplegiventhepowertheyprovide.SimplyimplementtheInterceptor interface,beingsuretospecifythesignaturesyouwanttointercept.


// ExamplePlugin.java @Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})}) public class ExamplePlugin implements Interceptor { public Object intercept(Invocation invocation) throws Throwable { return invocation.proceed(); } public Object plugin(Object target) { return Plugin.wrap(target, this); } public void setProperties(Properties properties) { } }

16August2009

15

iBATIS3UserGuide
// MapperConfig.xml <plugins> <plugin interceptor="org.apache.ibatis.example.ExamplePlugin"> <property name="someProperty" value="100"/> </plugin> </plugins>

ThepluginabovewillinterceptallcallstotheupdatemethodontheExecutorinstance,whichisan internalobjectresponsibleforthelowlevelexecutionofmappedstatements. OverridingtheConfigurationClass InadditiontomodifyingcoreiBATISbehaviourwithplugins,youcanalsooverridetheConfiguration classentirely.Simplyextenditandoverrideanymethodsinside,andpassitintothecalltothe sqlSessionFactoryBuilder.build(myConfig)method.Againthough,thiscouldhaveasevereimpactonthe behaviourofiBATIS,sousecaution.

environments
iBATIScanbeconfiguredwithmultipleenvironments.ThishelpsyoutoapplyyourSQLMapsto multipledatabasesforanynumberofreasons.Forexample,youmighthaveadifferentconfiguration foryourDevelopment,TestandProductionenvironments.Or,youmayhavemultipleproduction databasesthatsharethesameschema,andyoudliketousethesameSQLmapsforboth.Thereare manyusecases. Oneimportantthingtorememberthough:Whileyoucanconfiguremultipleenvironments,youcan onlychooseONEperSqlSessionFactoryinstance. Soifyouwanttoconnecttotwodatabases,youneedtocreatetwoinstancesofSqlSessionFactory,one foreach.Forthreedatabases,youdneedthreeinstances,andsoon.Itsreallyeasytoremember: OneSqlSessionFactoryinstanceperdatabase Tospecifywhichenvironmenttobuild,yousimplypassittotheSqlSessionFactoryBuilderasanoptional parameter.Thetwosignaturesthataccepttheenvironmentare: SqlSessionFactoryfactory=sqlSessionFactoryBuilder.build(reader,environment); SqlSessionFactoryfactory=sqlSessionFactoryBuilder.build(reader,environment,properties); Iftheenvironmentisomitted,thenthedefaultenvironmentisloaded,asfollows: SqlSessionFactoryfactory=sqlSessionFactoryBuilder.build(reader); SqlSessionFactoryfactory=sqlSessionFactoryBuilder.build(reader,properties); Theenvironmentselementdefineshowtheenvironmentisconfigured.
<environments default="development"> <environment id="development">

16August2009

16

iBATIS3UserGuide
<transactionManager type="JDBC"> <property name="" value=""/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>

Noticethekeysectionshere: ThedefaultEnvironmentID(e.g.default=development). TheEnvironmentIDforeachenvironmentdefined(e.g.id=development). TheTransactionManagerconfiguration(e.g.type=JDBC) TheDataSourceconfiguration(e.g.type=POOLED)

ThedefaultenvironmentandtheenvironmentIDsareselfexplanatory.Namethemwhateveryoulike, justmakesurethedefaultmatchesoneofthem.

transactionManager
TherearetwoTransactionManagertypes(i.e.type=[JDBC|MANAGED])thatareincludedwithiBATIS: JDBCThisconfigurationsimplymakesuseoftheJDBCcommitandrollbackfacilitiesdirectly.It reliesontheconnectionretrievedfromthedataSourcetomanagethescopeofthetransaction. MANAGEDThisconfigurationsimplydoesnothing,quiteliterally.Itnevercommits,rollsback orclosesaconnection.Instead,itletsthecontainermanagethefulllifecycleofthetransaction (e.g.SpringoraJEEApplicationServercontext).

NeitheroftheseTransactionManagertypesrequireanyproperties.However,theyarebothType Aliases,soinotherwords,insteadofusingthem,youcouldputyourownfullyqualifiedclassnameor TypeAliasthatreferstoyourownimplementationoftheTransactionFactoryinterface.


public interface TransactionFactory { void setProperties(Properties props); Transaction newTransaction(Connection conn, boolean autoCommit); }

AnypropertiesconfiguredintheXMLwillbepassedtothesetProperties()methodafterinstantiation. YourimplementationwouldalsoneedtocreateaTransactionimplementation,whichisalsoavery simpleinterface:

16August2009

17

iBATIS3UserGuide
public interface Transaction { Connection getConnection(); void commit() throws SQLException; void rollback() throws SQLException; void close() throws SQLException; }

Usingthesetwointerfaces,youcancompletelycustomizehowiBATISdealswithTransactions.

dataSource
ThedataSourceelementconfiguresthesourceofJDBCConnectionobjectsusingthestandardJDBC DataSourceinterface. MostiBATISapplicationswillconfigureadataSourceasintheexample.However,itsnot required.Realizethough,thattofacilitateLazyLoading,thisdataSourceisrequired. TherearethreebuildindataSourcetypes(i.e.type=????): UNPOOLEDThisimplementationofDataSourcesimplyopensandclosesaconnectioneachtimeitis requested.Whileitsabitslower,thisisagoodchoiceforsimpleapplicationsthatdonotrequirethe performanceofimmediatelyavailableconnections.Differentdatabasesarealsodifferentinthis performancearea,soforsomeitmaybelessimportanttopoolandthisconfigurationwillbeideal.The UNPOOLEDDataSourceisconfiguredwithonlyfourproperties: driverThisisthefullyqualifiedJavaclassoftheJDBCdriver(NOToftheDataSourceclassif yourdriverincludesone). urlThisistheJDBCURLforyourdatabaseinstance. usernameThedatabaseusernametologinwith. passwordThedatabasepasswordtologinwith.

Optionally,youcanpasspropertiestothedatabasedriveraswell.Todothis,prefixthepropertieswith driver.,forexample: driver.encoding=UTF8

Thiswillpassthepropertyencoding,withthevalueUTF8,toyourdatabasedriverviathe DriverManager.getConnection(url,driverProperties)method. POOLEDThisimplementationofDataSourcepoolsJDBCConnectionobjectstoavoidtheinitial connectionandauthenticationtimerequiredtocreateanewConnectioninstance.Thisisapopular approachforconcurrentwebapplicationstoachievethefastestresponse.

16August2009

18

iBATIS3UserGuide Inadditiontothe(UNPOOLED)propertiesabove,therearemanymorepropertiesthatcanbeusedto configurethePOOLEDdatasource: poolMaximumActiveConnectionsThisisthenumberofactive(i.e.inuse)connectionsthat canexistatanygiventime.Default:10 poolMaximumIdleConnectionsThenumberofidleconnectionsthatcanexistatanygiven time. poolMaximumCheckoutTimeThisistheamountoftimethataConnectioncanbechecked outofthepoolbeforeitwillbeforcefullyreturned.Default:20000ms(i.e.20seconds) poolTimeToWaitThisisalowlevelsettingthatgivesthepoolachancetoprintalogstatus andreattempttheacquisitionofaconnectioninthecasethatitstakingunusuallylong(to avoidfailingsilentlyforeverifthepoolismisconfigured).Default:20000ms(i.e.20seconds) poolPingQueryThePingQueryissenttothedatabasetovalidatethataconnectionisingood workingorderandisreadytoacceptrequests.Thedefaultis"NOPINGQUERYSET",whichwill causemostdatabasedriverstofailwithadecenterrormessage. poolPingEnabledThisenablesordisablesthepingquery.Ifenabled,youmustalsosetthe poolPingQuerypropertywithavalidSQLstatement(preferablyaveryfastone).Default:false.

poolPingConnectionsNotUsedForThisconfigureshowoftenthepoolPingQuerywillbeused. Thiscanbesettomatchthetypicaltimeoutforadatabaseconnection,toavoidunnecessary pings.Default:0(i.e.allconnectionsarepingedeverytimebutonlyifpoolPingEnabledistrue ofcourse).

JNDIThisimplementationofDataSourceisintendedforusewithcontainerssuchasSpringor ApplicationServersthatmayconfiguretheDataSourcecentrallyorexternallyandplaceareferencetoit inaJNDIcontext.ThisDataSourceconfigurationonlyrequirestwoproperties: initial_contextThispropertyisusedfortheContextlookupfromtheInitialContext(i.e. initialContext.lookup(initial_context)).Thispropertyisoptional,andifomitted,thenthe data_sourcepropertywillbelookedupagainsttheInitialContextdirectly. data_sourceThisisthecontextpathwherethereferencetotheinstanceoftheDataSource canbefound.Itwillbelookedupagainstthecontextreturnedbytheinitial_contextlookup,or againsttheInitialContextdirectlyifnoinitial_contextissupplied.

SimilartotheotherDataSourceconfigurations,itspossibletosendpropertiesdirectlytothe InitialContextbyprefixingthosepropertieswithenv.,forexample: env.encoding=UTF8

16August2009

19

iBATIS3UserGuide ThiswouldsendthepropertyencodingwiththevalueofUTF8totheconstructorofthe InitialContextuponinstantiation.

mappers
NowthatthebehaviourofiBATISisconfiguredwiththeaboveconfigurationelements,werereadyto defineourmappedSQLstatements.Butfirst,weneedtotelliBATISwheretofindthem.Javadoesnt reallyprovideanygoodmeansofautodiscoveryinthisregard,sothebestwaytodoitistosimplytell iBATISwheretofindthemappingfiles.Youcanuseclasspathrelativeresourcereferences,orliteral, fullyqualifiedurlreferences(includingfile:///URLs).Forexample:
// Using classpath relative resources <mappers> <mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/> <mapper resource="org/apache/ibatis/builder/BlogMapper.xml"/> <mapper resource="org/apache/ibatis/builder/PostMapper.xml"/> </mappers> // Using url fully qualified paths <mappers> <mapper url="file:///var/sqlmaps/AuthorMapper.xml"/> <mapper url="file:///var/sqlmaps/BlogMapper.xml"/> <mapper url="file:///var/sqlmaps/PostMapper.xml"/> </mappers>

ThesestatementsimplytelliBATISwheretogofromhere.TherestofthedetailsareineachoftheSQL Mappingfiles,andthatsexactlywhatthenextsectionwilldiscuss.

SQLMapXMLFiles
ThetruepowerofiBATISisintheMappedStatements.Thisiswherethemagichappens.Foralloftheir power,theSQLMapXMLfilesarerelativelysimple.Certainlyifyouweretocomparethemtothe equivalentJDBCcode,youwouldimmediatelyseeasavingsof95%ofthecode.iBATISwasbuiltto focusontheSQL,anddoesitsbesttostayoutofyourway. TheSQLMapXMLfileshaveonlyafewfirstclasselements(intheorderthattheyshouldbedefined): cacheConfigurationofthecacheforagivennamespace. cacherefReferencetoacacheconfigurationfromanothernamespace. resultMapThemostcomplicatedandpowerfulelementthatdescribeshowtoloadyour objectsfromthedatabaseresultsets. parameterMapDeprecated!Oldschoolwaytomapparameters.Inlineparametersare preferredandthiselementmayberemovedinthefuture.Notdocumentedhere. sqlAreusablechunkofSQLthatcanbereferencedbyotherstatements.

16August2009

20

iBATIS3UserGuide insertAmappedINSERTstatement. updateAmappedUPDATEstatement. deleteAmappedDELEETEstatement. selectAmappedSELECTstatement.

Thenextsectionswilldescribeeachoftheseelementsindetail,startingwiththestatements themselves.

select
TheselectstatementisoneofthemostpopularelementsthatyoulluseiniBATIS.Puttingdataina databaseisntterriblyvaluableuntilyougetitbackout,somostapplicationsqueryfarmorethanthey modifythedata.Foreveryinsert,updateordelete,thereisprobablymanyselects.Thisisoneofthe foundingprinciplesofiBATIS,andisthereasonsomuchfocusandeffortwasplacedonqueryingand resultmapping.Theselectelementisquitesimpleforsimplecases.Forexample:
<select id=selectPerson parameterType=int resultType=hashmap> SELECT * FROM PERSON WHERE ID = #{id} </select>

ThisstatementiscalledselectPerson,takesaparameteroftypeint(orInteger),andreturnsaHashMap keyedbycolumnnamesmappedtorowvalues. Noticetheparameternotation:


#{id}

ThistellsiBATIStocreateaPreparedStatementparameter.WithJDBC,suchaparameterwouldbe identifiedbya?inSQLpassedtoanewPreparedStatement,somethinglikethis:
// Similar JDBC code, NOT iBATIS String selectPerson = SELECT * FROM PERSON WHERE ID=?; PreparedStatement ps = conn.prepareStatement(selectPerson); ps.setInt(1,id);

Ofcourse,theresalotmorecoderequiredbyJDBCalonetoextracttheresultsandmapthemtoan instanceofanobject,whichiswhatiBATISsavesyoufromhavingtodo.Theresalotmoretoknow aboutparameterandresultmapping.Thosedetailswarranttheirownsection,whichfollowslaterin thissection. Theselectelementhasmoreattributesthatallowyoutoconfigurethedetailsofhoweachstatement shouldbehave.


<select id=selectPerson parameterType=int parameterMap=deprecated resultType=hashmap

16August2009

21

iBATIS3UserGuide
resultMap=personResultMap flushCache=false useCache=true timeout=10000 fetchSize=256 statementType=PREPARED resultSetType=FORWARD_ONLY >

Attribute id parameterType parameterMap resultType

resultMap

flushCache useCache timeout fetchSize statementType

resultSetType

Description Auniqueidentifierinthisnamespacethatcanbeusedtoreferencethisstatement. Thefullyqualifiedclassnameoraliasfortheparameterthatwillbepassedintothis statement. ThisisadeprecatedapproachtoreferencinganexternalparameterMap.Useinline parametermappingsandtheparameterTypeattribute. Thefullyqualifiedclassnameoraliasfortheexpectedtypethatwillbereturned fromthisstatement.Notethatinthecaseofcollections,thisshouldbethetype thatthecollectioncontains,notthetypeofthecollectionitself.UseresultTypeOR resultMap,notboth. AnamedreferencetoanexternalresultMap.Resultmapsarethemostpowerful featureofiBATIS,andwithagoodunderstandingofthem,manydifficultmapping casescanbesolved.UseresultMapORresultType,notboth. Settingthistotruewillcausethecachetobeflushedwheneverthisstatementis called.Default:falseforselectstatements. Settingthistotruewillcausetheresultsofthisstatementtobecached.Default: trueforselectstatements. Thissetsthemaximumtimethedriverwillwaitforthedatabasetoreturnfroma request,beforethrowinganexception.Defaultisunset(driverdependent). Thisisadriverhintthatwillattempttocausethedrivertoreturnresultsinbatches ofrowsnumberinginsizeequaltothissetting.Defaultisunset(driverdependent). AnyoneofSTATEMENT,PREPAREDorCALLABLE.ThiscausesiBATIStouse Statement,PreparedStatementorCallableStatementrespectively.Default: PREPARED. AnyoneofFORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE.Defaultis unset(driverdependent).

insert,update,delete
Thedatamodificationstatementsinsert,updateanddeleteareverysimilarintheirimplementation:
<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" useGeneratedKeys="" timeout="20000">

<update id="insertAuthor"

16August2009

22

iBATIS3UserGuide
parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20000">

<delete id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20000">

Attribute id parameterType

Description Auniqueidentifierinthisnamespacethatcanbeusedtoreferencethisstatement. Thefullyqualifiedclassnameoraliasfortheparameterthatwillbepassedintothis statement. parameterMap ThisisadeprecatedapproachtoreferencinganexternalparameterMap.Use inlineparametermappingsandtheparameterTypeattribute. flushCache Settingthistotruewillcausethecachetobeflushedwheneverthisstatementis called.Default:falseforselectstatements. timeout Thissetsthemaximumtimethedriverwillwaitforthedatabasetoreturnfroma request,beforethrowinganexception.Defaultisunset(driverdependent). statementType AnyoneofSTATEMENT,PREPAREDorCALLABLE.ThiscausesiBATIStouse Statement,PreparedStatementorCallableStatementrespectively.Default: PREPARED. useGeneratedKeys (insertonly)ThistellsiBATIStousetheJDBCgetGeneratedKeysmethodtoretrieve keysgeneratedinternallybythedatabase(e.g.autoincrementfieldsinRDBMSlike MySQLorSQLServer).Default:false keyProperty (insertonly)IdentifiesapropertyintowhichiBATISwillsetthekeyvaluereturned bygetGeneratedKeys,orbyaselectKeychildelementoftheinsertstatement. Default:unset. Thefollowingaresomeexamplesofinsert,updateanddeletestatemens.
<insert id="insertAuthor" parameterType="domain.blog.Author"> insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) </insert> <update id="updateAuthor" parameterType="domain.blog.Author"> update Author set username = #{username}, password = #{password}, email = #{email}, bio = #{bio} where id = #{id} </update> <delete id="deleteAuthor parameterType="int"> delete from Author where id = #{id} </delete>

16August2009 23

iBATIS3UserGuide Asmentioned,insertisalittlebitmorerichinthatithasafewextraattributesandsubelementsthat allowittodealwithkeygenerationinanumberofways. First,ifyourdatabasesupportsautogeneratedkeyfields(e.g.MySQLandSQLServer),thenyoucan simplysetuseGeneratedKeys=trueandsetthekeyPropertytothetargetpropertyandyouredone. Forexample,iftheAuthortableabovehadusedanautogeneratedcolumntypefortheid,the statementwouldbemodifiedasfollows:


<insert id="insertAuthor" parameterType="domain.blog.Author" useGeneratedKeys=true keyProperty=id> insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert>

iBATIShasanotherwaytodealwithkeygenerationfordatabasesthatdontsupportautogenerated columntypes,orperhapsdontyetsupporttheJDBCdriversupportforautogeneratedkeys. Heresasimple(silly)examplethatwouldgeneratearandomID(somethingyoudlikelyneverdo,but thisdemonstratestheflexibilityandhowiBATISreallydoesntmind):


<insert id="insertAuthor" parameterType="domain.blog.Author"> <selectKey keyProperty="id" resultType="int" order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey> insert into Author (id, username, password, email,bio, favourite_section) values (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR} ) </insert>

Intheexampleabove,theselectKeystatementwouldberunfirst,theAuthoridpropertywouldbeset, andthentheinsertstatementwouldbecalled.Thisgivesyouasimilarbehaviourtoanautogenerated keyinyourdatabasewithoutcomplicatingyourJavacode. TheselectKeyelementisdescribedasfollows:


<selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">

Attribute keyProperty resultType order

Description ThetargetpropertywheretheresultoftheselectKeystatementshouldbeset. Thetypeoftheresult.iBATIScanusuallyfigurethisout,butitdoesnthurttoadd ittobesure.iBATISallowsanysimpletypetobeusedasthekey,includingStrings. ThiscanbesettoBEFOREorAFTER.IfsettoBEFORE,thenitwillselectthekey first,setthekeyPropertyandthenexecutetheinsertstatement.IfsettoAFTER,it runstheinsertstatementandthentheselectKeystatementwhichiscommon

16August2009

24

iBATIS3UserGuide withdatabaseslikeOraclethatmayhaveembeddedsequencecallsinsideofinsert statements. Sameasabove,iBATISsupportsSTATEMENT,PREPAREDandCALLABLEstatement typesthatmaptoStatement,PreparedStatementandCallableStatement respectively.

statementType

sql
ThiselementcanbeusedtodefineareusablefragmentofSQLcodethatcanbeincludedinother statements.Forexample:
<sql id=userColumns> id,username,password </sql>

TheSQLfragmentcanthenbeincludedinanotherstatement,forexample:
<select id=selectUsers parameterType=int resultType=hashmap> select <include refid=userColumns/> from some_table where id = #{id} </select>

Parameters
Inallofthepaststatements,youveseenexamplesofsimpleparameters.Parametersareverypowerful elementsiniBATIS.Forsimplesituations,probably90%ofthecases,theresnotmuchtoothem,for example:
<select id=selectUsers parameterType=int resultType=User> select id, username, password from users where id = #{id} </select>

Theexampleabovedemonstratesaverysimplenamedparametermapping.TheparameterTypeisset toint,sothereforetheparametercouldbenamedanything.Primitiveorsimplydatatypessuchas IntegerandStringhavenorelevantproperties,andthuswillreplacethefullvalueoftheparameter entirely.However,ifyoupassinacomplexobject,thenthebehaviourisalittledifferent.Forexample:


<insert id=insertUser parameterType=User > insert into users (id, username, password) values (#{id}, #{username}, #{password}) </insert>

IfaparameterobjectoftypeUserwaspassedintothatstatement,theid,usernameandpassword propertywouldbelookedupandtheirvaluespassedtoaPreparedStatementparameter. Thatsniceandsimpleforpassingparametersintostatements.Buttherearealotofotherfeaturesof parametermaps. First,likeotherpartsofiBATIS,parameterscanspecifyamorespecificdatatype.

16August2009

25

iBATIS3UserGuide

#{property,javaType=int,jdbcType=NUMERIC}

LiketherestofiBATIS,thejavaTypecanalmostalwaysbedeterminedfromtheparameterobject,unless thatobjectisaHashMap.ThenthejavaTypeshouldbespecifiedtoensurethecorrectTypeHandleris used. Note:TheJDBCTypeisrequiredbyJDBCforallnullablecolumns,ifnullispassedasavalue.Youcan investigatethisyourselfbyreadingtheJavaDocsforthePreparedStatement.setNull()method. Tofurthercustomizetypehandling,youcanalsospecifyaspecificTypeHandlerclass(oralias),for example:


#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}

Soalreadyitseemstobegettingverbose,butthetruthisthatyoullrarelysetanyofthese. FornumerictypestheresalsoanumericScalefordetermininghowmanydecimalplacesarerelevant.
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}

Finally,themodeattributeallowsyoutospecifyIN,OUTorINOUTparameters.IfaparameterisOUTor INOUT,theactualvalueoftheparameterobjectpropertywillbechanged,justasyouwouldexpectif youwerecallingforanoutputparameter.Ifthemode=OUT(orINOUT)andthejdbcType=CURSOR(i.e. OracleREFCURSOR),youmustspecifyaresultMaptomaptheResultSettothetypeoftheparameter


#{department, mode=OUT, jdbcType=CURSOR, javaType=Department, resultMap=departmentResultMap}

Despiteallofthesepowerfuloptions,mostofthetimeyoullsimplyspecifythepropertyname,and iBATISwillfigureouttherest.Atmost,youllspecifythejdbcTypefornullablecolumns.
#{firstName} #{middleInitial,jdbcType=VARCHAR} #{lastName}

StringSubstitution Bydefault,usingthe#{}syntaxwillcauseiBATIStogeneratePreparedStatementpropertiesandsetthe valuessafelyagainstthePreparedStatementparameters(e.g.?).Whilethisissafer,fasterandalmost alwayspreferred,sometimesyoujustwanttodirectlyinjectastringunmodifiedintotheSQLStatement. Forexample,forORDERBY,youmightusesomethinglikethis:


ORDER BY ${columnName}

HereiBATISwontmodifyorescapethestring.

16August2009

26

iBATIS3UserGuide

IMPORTANT:Itsnotsafetoacceptinputfromauserandsupplyittoastatementunmodifiedinthis way.ThisleadstopotentialSQLInjectionattacksandthereforeyoushouldeitherdisallowuserinputin thesefields,oralwaysperformyourownescapesandchecks.

resultMap
TheresultMapelementisthemostimportantandpowerfulelementiniBATIS.Itswhatallowsyoutodo awaywith90%ofthecodethatJDBCrequirestoretrievedatafromResultSets,andinsomecasesallows youtodothingsthatJDBCdoesnotevensupport.Infact,towritetheequivalentcodeforsomething likeajoinmappingforacomplexstatementcouldprobablyspanthousandsoflinesofcode.Thedesign oftheResultMapsissuchthatsimplestatementsdontrequireexplicitresultmappingsatall,andmore complexstatementsrequirenomorethanisabsolutelynecessarytodescribetherelationships. YouvealreadyseenexamplesofsimplemappedstatementsthatdonthaveanexplicitresultMap.For example:
<select id=selectUsers parameterType=int resultType=hashmap> select id, username, hashedPassword from some_table where id = #{id} </sql>

SuchastatementsimplyresultsinallcolumnsbeingautomaticallymappedtothekeysofaHashMap,as specifiedbytheresultTypeattribute.Whileusefulinmanycases,aHashMapdoesntmakeaverygood domainmodel.ItsmorelikelythatyourapplicationwilluseJavaBeansorPOJOs(PlainOldJavaObjects) forthedomainmodel.iBATISsupportsboth.ConsiderthefollowingJavaBean:


package com.someapp.model; public class User { private int id; private String username; private String hashedPassword; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getHashedPassword() { return hashedPassword; } public void setHashedPassword(String hashedPassword) { this.hashedPassword = hashedPassword; } }

BasedontheJavaBeansspecification,theaboveclasshas3properties:id,username,and hashedPassword.Thesematchupexactlywiththecolumnnamesintheselectstatement. 16August2009 27

iBATIS3UserGuide SuchaJavaBeancouldbemappedtoaResultSetjustaseasilyastheHashMap.
<select id=selectUsers parameterType=int resultType=com.someapp.model.User> select id, username, hashedPassword from some_table where id = #{id} </sql>

AndrememberthatTypeAliasesareyourfriend.Usethemsothatyoudonthavetokeeptypingthe fullyqualifiedpathofyourclassout.Forexample:
<!-- In Config XML file --> <typeAlias type=com.someapp.model.User alias=User/> <!-- In SQL Mapping XML file --> <select id=selectUsers parameterType=int resultType=User> select id, username, hashedPassword from some_table where id = #{id} </sql>

InthesecasesiBATISisautomaticallycreatingaResultMapbehindthescenestomapthecolumnstothe JavaBeanpropertiesbasedonname.Ifthecolumnnamesdidnotmatchexactly,youcouldemploy selectclausealiases(astandardSQLfeature)onthecolumnnamestomakethelabelsmatch.For example:


<select id=selectUsers parameterType=int resultType=User> select user_id as id, user_name as userName, hashed_password as hashedPassword from some_table where id = #{id} </sql>

ThegreatthingaboutResultMapsisthatyouvealreadylearnedalotaboutthem,butyouhaventeven seenoneyet!Thesesimplecasesdontrequireanymorethanyouveseenhere.Justforexamplesake, letsseewhatthislastexamplewouldlooklikeasanexternalresultMap,asthatisanotherwaytosolve columnnamemismatches.


<resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <result property="username" column="username"/> <result property="password" column="password"/> </resultMap>

AndthestatementthatreferencesitusestheresultMapattributetodoso(noticeweremovedthe resultTypeattribute).Forexample:
<select id=selectUsers parameterType=int resultMap=userResultMap>

16August2009

28

iBATIS3UserGuide
select user_id, user_name, hashed_password from some_table where id = #{id} </sql>

Nowifonlytheworldwerealwaysthatsimple.

AdvancedResultMapping
iBATISwascreatedwithoneideainmind:Databasesarentalwayswhatyouwantorneedthemtobe. Whilewedloveeverydatabasetobeperfect3rdnormalformorBCNF,theyarent.Anditwouldbe greatifitwaspossibletohaveasingledatabasemapperfectlytoalloftheapplicationsthatuseit,its not.ResultMapsaretheanswerthatiBATISprovidestothisproblem. Forexample,howwouldwemapthisstatement?
<!-- Very Complex Statement --> <select id="selectBlogDetails" parameterType="int" resultMap="detailedBlogResultMap"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id} </select>

YoudprobablywanttomapittoanintelligentobjectmodelconsistingofaBlogthatwaswrittenbyan Author,andhasmanyPosts,eachofwhichmayhavezeroormanyCommentsandTags.Thefollowing isacompleteexampleofacomplexResultMap(assumeAuthor,Blog,Post,CommentsandTagsareall typealiases).Havealookatit,butdontworry,weregoingtogothrougheachstep.Whileitmaylook dauntingatfirst,itsactuallyverysimple.

16August2009

29

iBATIS3UserGuide
<!-- Very Complex Result Map --> <resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="id" javaType="int"/> </constructor> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javaType=" Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> <result property="favouriteSection" column="author_favourite_section"/> </association> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" column="post_author_id" javaType="Author"/> <collection property="comments" column="post_id" ofType=" Comment"> <id property="id" column="comment_id"/> </collection> <collection property="tags" column="post_id" ofType=" Tag" > <id property="id" column="tag_id"/> </collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> </collection> </resultMap>

TheresultMapelementhasanumberofsubelementsandastructureworthyofsomediscussion.The followingisaconceptualviewoftheresultMapelement. resultMap constructorusedforinjectingresultsintotheconstructorofaclassuponinstantiation o idArgIDargument;flaggingresultsasIDwillhelpimproveoverallperformance o arganormalresultinjectedintotheconstructor idanIDresult;flaggingresultsasIDwillhelpimproveoverallperformance resultanormalresultinjectedintoafieldorJavaBeanproperty associationacomplextypeassociation;manyresultswillrollupintothistype o nestedresultmappingsassociationsareresultMapsthemselves,orcanreferto one collectionacollectionofcomplextypes o nestedresultmappingscollectionsareresultMapsthemselves,orcanrefertoone discriminatorusesaresultvaluetodeterminewhichresultMaptouse o caseacaseisaresultmapbasedonsomevalue nestedresultmappingsacaseisalsoaresultmapitself,andthuscan containmanyofthesesameelements,oritcanrefertoanexternal resultMap. BestPractice:AlwaysbuildResultMapsincrementally.Unittestsreallyhelpouthere.Ifyou trytobuildagiganticresultMapliketheoneaboveallatonce,itslikelyyoullgetitwrongandit willbehardtoworkwith.Startsimple,andevolveitastepatatime.Andunittest!The downsidetousingframeworksisthattheyaresometimesabitofablackbox(opensourceor

16August2009

30

iBATIS3UserGuide not).Yourbestbettoensurethatyoureachievingthebehaviourthatyouintend,istowrite unittests.Italsohelpstohavethemwhensubmittingbugs. Thenextsectionswillwalkthrougheachoftheelementsinmoredetail.

id,result
Thesearethemostbasicofresultmappings.Bothid,andresultmapasinglecolumnvaluetoasingle propertyorfieldofasimpledatatype(String,int,double,Date,etc.). Theonlydifferencebetweenthetwoisthatidwillflagtheresultasanidentifierpropertytobeused whencomparingobjectinstances.Thishelpstoimprovegeneralperformance,butespecially performanceofcachingandnestedresultmapping(i.e.joinmapping). Eachhasanumberofattributes: Attribute Description property Thefieldorpropertytomapthecolumnresultto.IfamatchingJavaBeansproperty existsforthegivenname,thenthatwillbeused.Otherwise,iBATISwilllookforafield ofthegivenname.Inbothcasesyoucanusecomplexpropertynavigationusingthe usualdotnotation.Forexample,youcanmaptosomethingsimplelike:username, ortosomethingmorecomplicatedlike:address.street.number. column Thecolumnnamefromthedatabase,orthealiasedcolumnlabel.Thisisthesame stringthatwouldnormallybepassedtoresultSet.getString(columnName). javaType AfullyqualifiedJavaclassname,oratypealias(seethetableaboveforthelistofbuilt intypealiases).iBATIScanusuallyfigureoutthetypeifyouremappingtoaJavaBean. However,ifyouaremappingtoaHashMap,thenyoushouldspecifythejavaType explicitlytoensurethedesiredbehaviour. jdbcType TheJDBCTypefromthelistofsupportedtypesthatfollowsthistable.TheJDBCtypeis onlyrequiredfornullablecolumnsuponinsert,updateordelete.ThisisaJDBC requirement,notaniBATISone.SoevenifyouwerecodingJDBCdirectly,youdneed tospecifythistypebutonlyfornullablevalues. typeHandler Wediscusseddefaulttypehandlerspreviouslyinthisdocumentation.Usingthis propertyyoucanoverridethedefaulttypehandleronamappingbymappingbasis. ThevalueiseitherafullyqualifiedclassnameofaTypeHandlerimplementation,ora typealias.
<id property="id" column="post_id"/> <result property="subject" column="post_subject"/>

SupportedJDBCTypes
Forfuturereference,iBATISsupportsthefollowingJDBCTypesviatheincludedJdbcTypeenumeration.
BIT TINYINT SMALLINT INTEGER BIGINT FLOAT REAL DOUBLE NUMERIC DECIMAL CHAR VARCHAR LONGVARCHAR DATE TIME TIMESTAMP BINARY VARBINARY LONGVARBINARY NULL OTHER BLOB CLOB BOOLEAN CURSOR UNDEFINED NVARCHAR NCHAR NCLOB

16August2009

31

iBATIS3UserGuide

constructor
<constructor> <idArg column="id" javaType="int"/> <arg column=username javaType=String/> </constructor>

WhilepropertieswillworkformostDataTransferObject(DTO)typeclasses,andlikelymostofyour domainmodel,theremaybesomecaseswhereyouwanttouseimmutableclasses.Oftentablesthat containreferenceorlookupdatathatrarelyorneverchangesissuitedtoimmutableclasses. Constructorinjectionallowsyoutosetvaluesonaclassuponinstantiation,withoutexposingpublic methods.iBATISalsosupportsprivatepropertiesandprivateJavaBeanspropertiestoachievethis,but somepeoplepreferConstructorinjection.Theconstructorelementenablesthis. Considerthefollowingconstructor:


public class User { // public User(int id, String username) { // } // }

Inordertoinjecttheresultsintotheconstructor,iBATISneedstoidentifytheconstructorbythetypeof itsparameters.Javahasnowaytointrospect(orreflect)onparameternames.Sowhencreatinga constructorelement,ensurethattheargumentsareinorder,andthatthedatatypesarespecified.


<constructor> <idArg column="id" javaType="int"/> <arg column=username javaType=String/> </constructor>

Therestoftheattributesandrulesarethesameasfortheregularidandresultelements. Attribute Description column Thecolumnnamefromthedatabase,orthealiasedcolumnlabel.Thisisthesame stringthatwouldnormallybepassedtoresultSet.getString(columnName). javaType AfullyqualifiedJavaclassname,oratypealias(seethetableaboveforthelistofbuilt intypealiases).iBATIScanusuallyfigureoutthetypeifyouremappingtoaJavaBean. However,ifyouaremappingtoaHashMap,thenyoushouldspecifythejavaType explicitlytoensurethedesiredbehaviour. jdbcType TheJDBCTypefromthelistofsupportedtypesthatfollowsthistable.TheJDBCtypeis onlyrequiredfornullablecolumnsuponinsert,updateordelete.ThisisaJDBC requirement,notaniBATISone.SoevenifyouwerecodingJDBCdirectly,youdneed tospecifythistypebutonlyfornullablevalues. typeHandler Wediscusseddefaulttypehandlerspreviouslyinthisdocumentation.Usingthis propertyyoucanoverridethedefaulttypehandleronamappingbymappingbasis. ThevalueiseitherafullyqualifiedclassnameofaTypeHandlerimplementation,ora typealias.

16August2009

32

iBATIS3UserGuide

association
<association property="author" column="blog_author_id" javaType=" Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> </association>

Theassociationelementdealswithahasonetyperelationship.Forexample,inourexample,aBlog hasoneAuthor.Anassociationmappingworksmostlylikeanyotherresult.Youspecifythetarget property,thecolumntoretrievethevaluefrom,thejavaTypeoftheproperty(whichiBATIScanfigure outmostofthetime),thejdbcTypeifnecessaryandatypeHandlerifyouwanttooverridetheretrieval oftheresultvalues. WheretheassociationdiffersisthatyouneedtotelliBATIShowtoloadtheassociation.iBATIScando sointwodifferentways: NestedSelect:ByexecutinganothermappedSQLstatementthatreturnsthecomplextype desired. NestedResults:Byusingnestedresultmappingstodealwithrepeatingsubsetsofjoined results. First,letsexaminethepropertiesoftheelement.Asyoullsee,itdiffersfromanormalresultmapping onlybytheselectandresultMapattributes. Attribute Description property Thefieldorpropertytomapthecolumnresultto.IfamatchingJavaBeansproperty existsforthegivenname,thenthatwillbeused.Otherwise,iBATISwilllookforafield ofthegivenname.Inbothcasesyoucanusecomplexpropertynavigationusingthe usualdotnotation.Forexample,youcanmaptosomethingsimplelike:username, ortosomethingmorecomplicatedlike:address.street.number. column Thecolumnnamefromthedatabase,orthealiasedcolumnlabel.Thisisthesame stringthatwouldnormallybepassedtoresultSet.getString(columnName). Note:Todealwithcompositekeys,youcanspecifymultiplecolumnnamestopass tothenestedselectstatementbyusingthesyntax column={prop1=col1,prop2=col2}.Thiswillcauseprop1andprop2tobesetagainst theparameterobjectforthetargetnestedselectstatement. javaType AfullyqualifiedJavaclassname,oratypealias(seethetableaboveforthelistofbuilt intypealiases).iBATIScanusuallyfigureoutthetypeifyouremappingtoaJavaBean. However,ifyouaremappingtoaHashMap,thenyoushouldspecifythejavaType explicitlytoensurethedesiredbehaviour. jdbcType TheJDBCTypefromthelistofsupportedtypesthatfollowsthistable.TheJDBCtypeis onlyrequiredfornullablecolumnsuponinsert,updateordelete.ThisisaJDBC requirement,notaniBATISone.SoevenifyouwerecodingJDBCdirectly,youdneed tospecifythistypebutonlyfornullablevalues. typeHandler Wediscusseddefaulttypehandlerspreviouslyinthisdocumentation.Usingthis propertyyoucanoverridethedefaulttypehandleronamappingbymappingbasis. ThevalueiseitherafullyqualifiedclassnameofaTypeHandlerimplementation,ora

16August2009

33

iBATIS3UserGuide typealias. NestedSelectforAssociation select

TheIDofanothermappedstatementthatwillloadthecomplextyperequiredbythis propertymapping.Thevaluesretrievedfromcolumnsspecifiedinthecolumn attributewillbepassedtothetargetselectstatementasparameters.Adetailed examplefollowsthistable. Note:Todealwithcompositekeys,youcanspecifymultiplecolumnnamestopass tothenestedselectstatementbyusingthesyntax column={prop1=col1,prop2=col2}.Thiswillcauseprop1andprop2tobesetagainst theparameterobjectforthetargetnestedselectstatement.

Forexample:
<resultMap id=blogResult type=Blog> <association property="author" column="blog_author_id" javaType="Author" select=selectAuthor/> </resultMap> <select id=selectBlog parameterType=int resultMap=blogResult> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id=selectAuthor parameterType=int resultType="Author"> SELECT * FROM AUTHOR WHERE ID = #{id} </select>

Thatsit.Wehavetwoselectstatements:onetoloadtheBlog,theothertoloadtheAuthor,andthe BlogsresultMapdescribesthattheselectAuthorstatementshouldbeusedtoloaditsauthor property. Allotherpropertieswillbeloadedautomaticallyassumingtheircolumnandpropertynamesmatch. Whilethisapproachissimple,itwillnotperformwellforlargedatasetsorlists.Thisproblemisknown astheN+1SelectsProblem.Inanutshell,theN+1selectsproblemiscausedlikethis: YouexecuteasingleSQLstatementtoretrievealistofrecords(the+1). Foreachrecordreturned,youexecuteaselectstatementtoloaddetailsforeach(theN). ThisproblemcouldresultinhundredsorthousandsofSQLstatementstobeexecuted.Thisisnot alwaysdesirable. TheupsideisthatiBATIScanlazyloadsuchqueries,thusyoumightbesparedthecostofthese statementsallatonce.However,ifyouloadsuchalistandthenimmediatelyiteratethroughitto accessthenesteddata,youwillinvokeallofthelazyloads,andthusperformancecouldbeverybad. Andso,thereisanotherway. NestedResultsforAssociation

16August2009

34

iBATIS3UserGuide resultMap ThisistheIDofaResultMapthatcanmapthenestedresultsofthisassociationintoan appropriateobjectgraph.Thisisanalternativetousingacalltoanotherselect statement.ItallowsyoutojoinmultipletablestogetherintoasingleResultSet.Sucha ResultSetwillcontainduplicated,repeatinggroupsofdatathatneedstobe decomposedandmappedproperlytoanestedobjectgraph.Tofacilitatethis,iBATIS letsyouchainresultmapstogether,todealwiththenestedresults.Anexamplewill befareasiertofollow,andonefollowsthistable.

Youvealreadyseenaverycomplicatedexampleofnestedassociationsabove.Thefollowingisafar simplerexampletodemonstratehowthisworks.Insteadofexecutingaseparatestatement,welljoin theBlogandAuthortablestogether,likeso:


<select id="selectBlog" parameterType="int" resultMap="blogResult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio from Blog B left outer join Author A on B.author_id = A.id where B.id = #{id} </select>

Noticethejoin,aswellasthecaretakentoensurethatallresultsarealiasedwithauniqueandclear name.Thismakesmappingfareasier.Nowwecanmaptheresults:
<resultMap id="blogResult" type="Blog"> <id property=blog_id column="id" /> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javaType="Author" resultMap=authorResult/> </resultMap>

<resultMap id="authorResult" type="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </resultMap>

IntheexampleaboveyoucanseeattheBlogsauthorassociationdelegatestotheauthorResult resultMaptoloadtheAuthorinstance. VeryImportant:idelementsplayaveryimportantroleinNestedResultmapping.Youshouldalways specifyoneormorepropertiesthatcanbeusedtouniquelyidentifytheresults.ThetruthisthatiBATIS willstillworkifyouleaveitout,butatasevereperformancecost.Chooseasfewpropertiesaspossible thatcanuniquelyidentifytheresult.Theprimarykeyisanobviouschoice(evenifcomposite). Now,theaboveexampleusedanexternalresultMapelementtomaptheassociation.Thismakesthe AuthorresultMapreusable.However,ifyouhavenoneedtoreuseit,orifyousimplyprefertoco

16August2009

35

iBATIS3UserGuide locateyourresultmappingsintoasingledescriptiveresultMap,youcannesttheassociationresult mappings.Heresthesameexampleusingthisapproach:


<resultMap id="blogResult" type="Blog"> <id property=blog_id column="id" /> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </association> </resultMap>

Youveseenabovehowtodealwithahasonetypeassociation.Butwhatabouthasmany?Thats thesubjectofthenextsection.

collection

<collection property="posts" ofType="domain.blog.Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection>

Thecollectionelementworksalmostidenticallytotheassociation.Infact,itssosimilar,todocument thesimilaritieswouldberedundant.Soletsfocusonthedifferences. Tocontinuewithourexampleabove,aBlogonlyhadoneAuthor.ButaBloghasmanyPosts.Onthe blogclass,thiswouldberepresentedbysomethinglike:


private List<Post> posts;

TomapasetofnestedresultstoaListlikethis,weusethecollectionelement.Justliketheassociation element,wecanuseanestedselect,ornestedresultsfromajoin. NestedSelectforCollection First,letslookatusinganestedselecttoloadthePostsfortheBlog.


<resultMap id=blogResult type=Blog> <collection property="posts" javaType=ArrayList column="blog_id" ofType="Post" select=selectPostsForBlog/> </resultMap> <select id=selectBlog parameterType=int resultMap=blogResult> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id=selectPostsForBlog parameterType=int resultType="Author"> SELECT * FROM POST WHERE BLOG_ID = #{id} </select>

16August2009

36

iBATIS3UserGuide

Thereareanumberthingsyoullnoticeimmediately,butforthemostpartitlooksverysimilartothe associationelementwelearnedaboutabove.First,youllnoticethatwereusingthecollectionelement. ThenyoullnoticethattheresanewofTypeattribute.Thisattributeisnecessarytodistinguish betweentheJavaBean(orfield)propertytypeandthetypethatthecollectioncontains.Soyoucould readthefollowingmappinglikethis:


<collection property="posts" javaType=ArrayList column="blog_id" ofType="Post" select=selectPostsForBlog/>

Readas:AcollectionofpostsinanArrayListoftypePost.

ThejavaTypeattributeisreallyunnecessary,asiBATISwillfigurethisoutforyouinmostcases.Soyou canoftenshortenthisdowntosimply:
<collection property="posts" column="blog_id" ofType="Post" select=selectPostsForBlog/>

NestedResultsforCollection Bythispoint,youcanprobablyguesshownestedresultsforacollectionwillwork,becauseitsexactly thesameasanassociation,butwiththesameadditionoftheofTypeattributeapplied. First,letslookattheSQL:


<select id="selectBlog" parameterType="int" resultMap="blogResult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, P.id as post_id, P.subject as post_subject, P.body as post_body, from Blog B left outer join Post P on B.id = P.blog_id where B.id = #{id} </select>

Again,wevejoinedtheBlogandPosttables,andhavetakencaretoensurequalityresultcolumnlabels forsimplemapping.NowmappingaBlogwithitscollectionofPostmappingsisassimpleas:
<resultMap id="blogResult" type="Blog"> <id property=id column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection> </resultMap>

Again,remembertheimportanceoftheidelementshere,orreadtheassociationsectionaboveifyou haventalready. Also,ifyoupreferthelongerformthatallowsformorereusabilityofyourresultmaps,youcanusethe followingalternativemapping: 16August2009 37

iBATIS3UserGuide
<resultMap id="blogResult" type="Blog"> <id property=id column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post" resultMap=blogPostResult/> </resultMap> <resultMap id="blogPostResult" type="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </resultMap>

Note:Theresnolimittothedepth,breadthorcombinationsoftheassociationsandcollectionsthat youmap.Youshouldkeepperformanceinmindwhenmappingthem.Unittestingandperformance testingofyourapplicationgoesalongwaytowarddiscoveringthebestapproachforyourapplication. ThenicethingisthatiBATISletsyouchangeyourmindlater,withverylittle(ifany)impacttoyourcode. Advancedassociationandcollectionmappingisadeepsubject.Documentationcanonlygetyousofar. Withalittlepractice,itwillallbecomeclearveryquickly.

discriminator
<discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator>

Sometimesasingledatabasequerymightreturnresultsetsofmanydifferent(buthopefullysomewhat related)datatypes.Thediscriminatorelementwasdesignedtodealwiththissituation,andothers, includingclassinheritancehierarchies.Thediscriminatorisprettysimpletounderstand,asitbehaves muchlikeaswitchstatementinJava. AdiscriminatordefinitionspecifiescolumnandjavaTypeattributes.ThecolumniswhereiBATISwilllook forthevaluetocompare.ThejavaTypeisrequiredtoensuretheproperkindofequalitytestis performed(althoughStringwouldprobablyworkforalmostanysituation).Forexample:


<resultMap id="vehicleResult" type="Vehicle"> <id property=id column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultMap="carResult"/> <case value="2" resultMap="truckResult"/> <case value="3" resultMap="vanResult"/> <case value="4" resultMap="suvResult"/> </discriminator> </resultMap>

Inthisexample,iBATISwouldretrieveeachrecordfromtheresultsetandcompareitsvehicletype value.Ifitmatchesanyofthediscriminatorcases,thenitwillusetheresultMapspecifiedbythecase. Thisisdoneexclusively,soinotherwords,therestoftheresultMapisignored(unlessitisextended,

16August2009

38

iBATIS3UserGuide whichwetalkaboutinasecond).Ifnoneofthecasesmatch,theniBATISsimplyusestheresultMapas definedoutsideofthediscriminatorblock.So,ifthecarResultwasdeclaredasfollows:


<resultMap id="carResult" type="Car"> <result property=doorCount column="door_count" /> </resultMap>

ThenONLYthedoorCountpropertywouldbeloaded.Thisisdonetoallowcompletelyindependent groupsofdiscriminatorcases,evenonesthathavenorelationshiptotheparentresultMap.Inthiscase wedoofcourseknowthattheresarelationshipbetweencarsandvehicles,asaCarisaVehicle. Therefore,wewanttherestofthepropertiesloadedtoo.OnesimplechangetotheresultMapand weresettogo.


<resultMap id="carResult" type="Car" extends=vehicleResult> <result property=doorCount column="door_count" /> </resultMap>

NowallofthepropertiesfromboththevehicleResultandcarResultwillbeloaded. Onceagainthough,somemayfindthisexternaldefinitionofmapssomewhattedious.Therefore theresanalternativesyntaxforthosethatpreferamoreconcisemappingstyle.Forexample:


<resultMap id="vehicleResult" type="Vehicle"> <id property=id column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultType="carResult"> <result property=doorCount column="door_count" /> </case> <case value="2" resultType="truckResult"> <result property=boxSize column="box_size" /> <result property=extendedCab column="extended_cab" /> </case> <case value="3" resultType="vanResult"> <result property=powerSlidingDoor column="power_sliding_door" /> </case> <case value="4" resultType="suvResult"> <result property=allWheelDrive column="all_wheel_drive" /> </case> </discriminator> </resultMap>

RememberthattheseareallResultMaps,andifyoudontspecifyanyresultsatall,theniBATISwill automaticallymatchupcolumnsandpropertiesforyou.Somostoftheseexamplesaremoreverbose thantheyreallyneedtobe.Thatsaid,mostdatabasesarekindofcomplexanditsunlikelythatwellbe abletodependonthatforallcases.

16August2009

39

iBATIS3UserGuide

cache
iBATIShasincludesapowerfulquerycachingfeaturewhichisveryconfigurableandcustomizable.Alot ofchangeshavebeenmadeintheiBATIS3cacheimplementationtomakeitbothmorepowerfuland fareasiertoconfigure. Bydefault,thereisnocachingenabled,exceptforlocalsessioncaching,whichimprovesperformance andisrequiredtoresolvecirculardependencies.Toenableasecondlevelofcaching,yousimplyneed toaddonelinetoyourSQLMappingfile:
<cache/>

Literallythatsit.Theeffectofthisonesimplestatementisasfollows: Allresultsfromselectstatementsinthemappedstatementfilewillbecached. Allinsert,updateanddeletestatementsinthemappedstatementfilewillflushthecache. ThecachewilluseaLeastRecentlyUsed(LRU)algorithmforeviction. Thecachewillnotflushonanysortoftimebasedschedule(i.e.noFlushInterval). Thecachewillstore1024referencestolistsorobjects(whateverthequerymethodreturns). Thecachewillbetreatedasaread/writecache,meaningobjectsretrievedarenotsharedand canbesafelymodifiedbythecaller,withoutinterferingwithotherpotentialmodificationsby othercallersorthreads.

Allofthesepropertiesaremodifiablethroughtheattributesofthecacheelement.Forexample:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

ThismoreadvancedconfigurationcreatesaFIFOcachethatflushesonceevery60seconds,storesupto 512referencestoresultobjectsorlists,andobjectsreturnedareconsideredreadonly,thusmodifying themcouldcauseconflictsbetweencallersindifferentthreads. Theavailableevictionpoliciesavailableare: LRULeastRecentlyUsed:Removesobjectsthathaventbeenusedforthelongstperiodof time. FIFOFirstInFirstOut:Removesobjectsintheorderthattheyenteredthecache.

16August2009

40

iBATIS3UserGuide SOFTSoftReference:Removesobjectsbasedonthegarbagecollectorstateandtherulesof SoftReferences. WEAKWeakReference:Moreaggressivelyremovesobjectsbasedonthegarbagecollector stateandrulesofWeakReferences. ThedefaultisLRU. TheflushIntervalcanbesettoanypositiveintegerandshouldrepresentareasonableamountoftime specifiedinmilliseconds.Thedefaultisnotset,thusnoflushintervalisusedandthecacheisonly flushedbycallstostatements. Thesizecanbesettoanypositiveinteger,keepinmindthesizeoftheobjectsyourcachingandthe availablememoryresourcesofyourenvironment.Thedefaultis1024. ThereadOnlyattributecanbesettotrueorfalse.Areadonlycachewillreturnthesameinstanceof thecachedobjecttoallcallers.Thussuchobjectsshouldnotbemodified.Thisoffersasignificant performanceadvantagethough.Areadwritecachewillreturnacopy(viaserialization)ofthecached object.Thisisslower,butsafer,andthusthedefaultisfalse.

UsingaCustomCache
Inadditiontocustomizingthecacheintheseways,youcanalsocompletelyoverridethecachebehavior byimplementingyourowncache,orcreatinganadaptertoother3rdpartycachingsolutions.
<cache type=com.domain.something.MyCustomCache/>

Thisexampledemonstrateshowtouseacustomcacheimplementation.Theclassspecifiedinthetype attributemustimplementtheorg.apache.ibatis.cache.Cacheinterface.Thisinterfaceisoneofthemore complexintheiBATISframework,butsimplegivenwhatitdoes.


public interface Cache { String getId(); int getSize(); void putObject(Object key, Object value); Object getObject(Object key); boolean hasKey(Object key); Object removeObject(Object key); void clear(); ReadWriteLock getReadWriteLock(); }

Toconfigureyourcache,simplyaddpublicJavaBeanspropertiestoyourCacheimplementation,and passpropertiesviathecacheElement,forexample,thefollowingwouldcallamethodcalled setCacheFile(Stringfile)onyourCacheimplementation:


<cache type=com.domain.something.MyCustomCache>

16August2009

41

iBATIS3UserGuide
<property name=cacheFile value=/tmp/my-custom-cache.tmp/> </cache>

YoucanuseJavaBeanspropertiesofallsimpletypes,iBATISwilldotheconversion. Itsimportanttorememberthatacacheconfigurationandthecacheinstanceareboundtothe namespaceoftheSQLMapfile.Thus,allstatementsinthesamenamespaceasthecacheareboundby it.Statementscanmodifyhowtheyinteractwiththecache,orexcludethemselvescompletelybyusing twosimpleattributesonastatementbystatementbasis.Bydefault,statementsareconfiguredlike this:


<select <insert <update <delete ... ... ... ... flushCache=false useCache=true/> flushCache=true/> flushCache=true/> flushCache=true/>

Sincethatsthedefault,youobviouslyshouldneverexplicitlyconfigureastatementthatway.Instead, onlysettheflushCacheanduseCacheattributesifyouwanttochangethedefaultbehavior.For example,insomecasesyoumaywanttoexcludetheresultsofaparticularselectstatementfromthe cache,oryoumightwantaselectstatementtoflushthecache.Similarly,youmayhavesomeupdate statementsthatdontneedtoflushthecacheuponexecution.

cacheref
Recallfromtheprevioussectionthatonlythecacheforthisparticularnamespacewillbeusedor flushedforstatementswithinthesamenamespace.Theremaycomeatimewhenyouwanttoshare thesamecacheconfigurationandinstancebetweennamespaces.Insuchcasesyoucanreference anothercachebyusingthecacherefelement.
<cache-ref namespace=com.someone.application.data.SomeMapper/>

DynamicSQL
OneofthemostpowerfulfeaturesofiBATIShasalwaysbeenitsDynamicSQLcapabilities.Ifyouhave anyexperiencewithJDBCoranysimilarframework,youunderstandhowpainfulitistoconditionally concatenatestringsofSQLtogether,makingsurenottoforgetspacesortoomitacommaattheendof alistofcolumns.DynamicSQLcanbedownrightpainfultodealwith. WhileworkingwithDynamicSQLwillneverbeaparty,iBATIScertainlyimprovesthesituationwitha powerfulDynamicSQLlanguagethatcanbeusedwithinanymappedSQLstatement. TheDynamicSQLelementsshouldbefamiliartoanyonewhohasusedJSTLoranysimilarXMLbased textprocessors.InpreviousversionsofiBATIS,therewerealotofelementstoknowandunderstand. iBATIS3greatlyimprovesuponthis,andnowtherearelessthanhalfofthoseelementstoworkwith. iBATISemployspowerfulOGNLbasedexpressionstoeliminatemostoftheotherelements.

16August2009

42

iBATIS3UserGuide if choose(when,otherwise) trim(where,set) foreach

if
ThemostcommonthingtodoindynamicSQLisconditionallyincludeapartofawhereclause.For example:
<select id=findActiveBlogWithTitleLike parameterType=Blog resultType=Blog> SELECT * FROM BLOG WHERE state = ACTIVE <if test=title != null> AND title like #{title} </if> </select>

Thisstatementwouldprovideanoptionaltextsearchtypeoffunctionality.Ifyoupassedinnotitle,then allactiveBlogswouldbereturned.Butifyoudopassinatitle,itwilllookforatitlelikethat(forthe keeneyed,yesinthiscaseyourparametervaluewouldneedtoincludeanymaskingorwildcard characters). Whatifwewantedtooptionallysearchbytitleandauthor?First,Idchangethenameofthestatement tomakemoresense.Thensimplyaddanothercondition.


<select id=findActiveBlogLike parameterType=Blog resultType=Blog> SELECT * FROM BLOG WHERE state = ACTIVE <if test=title != null> AND title like #{title} </if> <if test=author != null && author.name != null> AND title like #{author.name} </if> </select>

choose,when,otherwise
Sometimeswedontwantalloftheconditionalstoapply,insteadwewanttochooseonlyonecase amongmanyoptions.SimilartoaswitchstatementinJava,iBATISoffersachooseelement. Letsusetheexampleabove,butnowletssearchonlyontitleifoneisprovided,thenonlybyauthorif oneisprovided.Ifneitherisprovided,letsonlyreturnfeaturedblogs(perhapsastrategicallylist selectedbyadministrators,insteadofreturningahugemeaninglesslistofrandomblogs).
<select id=findActiveBlogLike parameterType=Blog resultType=Blog> SELECT * FROM BLOG WHERE state = ACTIVE

16August2009

43

iBATIS3UserGuide
<choose> <when test=title != null> AND title like #{title} </when> <when test=author != null && author.name != null> AND title like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </select>

trim,where,set
ThepreviousexampleshavebeenconvenientlydancingaroundanotoriousdynamicSQLchallenge. Considerwhatwouldhappenifwereturntoourifexample,butthistimewemakeACTIVE=1a dynamicconditionaswell.
<select id=findActiveBlogLike parameterType=Blog resultType=Blog> SELECT * FROM BLOG WHERE <if test=state != null> state = #{state} </if> <if test=title != null> AND title like #{title} </if> <if test=author != null && author.name != null> AND title like #{author.name} </if> </select>

Whathappensifnoneoftheconditionsaremet?YouwouldendupwithSQLthatlookedlikethis:
SELECT * FROM BLOG WHERE

Thiswouldfail.Whatifonlythesecondconditionwasmet?YouwouldendupwithSQLthatlookedlike this:
SELECT * FROM BLOG WHERE AND title like someTitle

Thiswouldalsofail.Thisproblemisnoteasilysolvedwithconditionals,andifyouveeverhadtowrite it,thenyoulikelyneverwanttodosoagain. iBATIShasasimpleanswerthatwilllikelyworkin90%ofthecases.Andincaseswhereitdoesnt,you cancustomizeitsothatitdoes.Withonesimplechange,everythingworksfine:


<select id=findActiveBlogLike parameterType=Blog resultType=Blog>

16August2009

44

iBATIS3UserGuide
SELECT * FROM BLOG <where> <if test=state != null> state = #{state} </if> <if test=title != null> AND title like #{title} </if> <if test=author != null && author.name != null> AND title like #{author.name} </if> </where> </select>

ThewhereelementknowstoonlyinsertWHEREifthereisanycontentreturnedbythecontaining tags.Furthermore,ifthatcontentbeginswithANDorOR,itknowstostripitoff. Ifthewhereelementdoesnotbehaveexactlyasyoulike,youcancustomizeitbydefiningyourowntrim element.Forexample,thetrimequivalenttothewhereelementis:


<trim prefix="WHERE" prefixOverrides="AND |OR "> </trim>

Theoverridesattributetakesapipedelimitedlistoftexttooverride,wherewhitespaceisrelevant.The resultistheremovalofanythingspecifiedintheoverridesattribute,andtheinsertionofanythinginthe withattribute. Thereisasimilarsolutionfordynamicupdatestatementscalledset.Thesetelementcanbeusedto dynamicallyincludecolumnstoupdate,andleaveoutothers.Forexample:


<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author"> update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update>

Here,thesetelementwilldynamicallyprependtheSETkeyword,andalsoeliminateanyextraneous commasthatmighttrailthevalueassignmentsaftertheconditionsareapplied. Ifyourecuriousaboutwhattheequivalenttrimelementwouldlooklike,hereitis:


<trim prefix="SET" suffixOverrides=","> </trim>

16August2009

45

iBATIS3UserGuide Noticethatinthiscasewereoverridingasuffix,whilewerestillappendingaprefix.

foreach
AnothercommonnecessityfordynamicSQListheneedtoiterateoveracollection,oftentobuildanIN condition.Forexample:
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>

Theforeachelementisverypowerful,andallowsyoutospecifyacollection,declareitemandindex variablesthatcanbeusedinsidethebodyoftheelement.Italsoallowsyoutospecifyopeningand closingstrings,andaddaseparatortoplaceinbetweeniterations.Theelementissmartinthatitwont accidentallyappendextraseparators. Note:YoucanpassaListinstanceoranArraytoiBATISasaparameterobject.Whenyoudo, iBATISwillautomaticallywrapitinaMap,andkeyitbyname.Listinstanceswillbekeyedtothe namelistandarrayinstanceswillbekeyedtothenamearray. ThiswrapsupthediscussionregardingtheXMLconfigurationfileandXMLmappingfiles.Thenext sectionwilldiscusstheJavaAPIindetail,sothatyoucangetthemostoutofthemappingsthatyouve created.

16August2009

46

iBATIS3UserGuide

JavaAPI
NowthatyouknowhowtoconfigureiBATISandcreatemappings,yourereadyforthegoodstuff.The iBATISJavaAPIiswhereyougettoreaptherewardsofyourefforts.Asyoullsee,comparedtoJDBC, iBATISgreatlysimplifiesyourcodeandkeepsitclean,easytounderstandandmaintain.iBATIS3has introducedanumberofsignificantimprovementstomakeworkingwithSQLMapsevenbetter.

DirectoryStructure
BeforewediveintotheJavaAPIitself,itsimportanttounderstandthebestpracticessurrounding directorystructures.iBATISisveryflexible,andyoucandoalmostanythingwithyourfiles.Butaswith anyframework,theresapreferredway. Letslookatatypicalapplicationdirectorystructure: /my_application /bin /devlib

/lib
/src /org/myapp/ /action

iBATIS *.jar files go here.

/data /SqlMapConfig.xml /BlogMapper.java /BlogMapper.xml


/model /service /view

iBATIS artifacts go here, including, Mapper Classes, XML Configuration, XML Mapping Files.

/properties
/test /org/myapp/ /action /data /model /service /view /properties /web /WEB-INF /web.xml

Properties included in your XML Configuration go here.

Remember, these are preferences, not requirements, but others will thank you for using a common directory structure.

Therestoftheexamplesinthissectionwillassumeyourefollowingthisdirectorystructure. 16August2009 47

iBATIS3UserGuide

SqlSessions
TheprimaryJavainterfaceforworkingwithiBATISistheSqlSession.Throughthisinterfaceyoucan executecommands,getmappersandmanagetransactions.WelltalkmoreaboutSqlSessionitself shortly,butfirstwehavetolearnhowtoacquireaninstanceofSqlSession.SqlSessionsarecreatedby aSqlSessionFactoryinstance.TheSqlSessionFactorycontainsmethodsforcreatinginstancesof SqlSessionsalldifferentways.TheSqlSessionFactoryitselfiscreatedbytheSqlSessionFactoryBuilder thatcancreatetheSqlSessonFactoryfromXML,AnnotationsorhandcodedJavaconfiguration.

SqlSessionFactoryBuilder
TheSqlSessionFactoryBuilderhasfivebuild()methods,eachwhichallowsyoutobuildaSqlSessionfrom adifferentsource.
SqlSessionFactory SqlSessionFactory SqlSessionFactory SqlSessionFactory SqlSessionFactory build(Reader reader) build(Reader reader, String environment) build(Reader reader, Properties properties) build(Reader reader, String env, Properties props) build(Configuration config)

Thefirstfourmethodsarethemostcommon,astheytakeaReaderinstancethatreferstoanXML document,ormorespecifically,theSqlMapConfig.xmlfilediscussedabove.Theoptionalparametersare environmentandproperties.Environmentdetermineswhichenvironmenttoload,includingthe datasourceandtransactionmanager.Forexample:


<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <dataSource type="POOLED"> </environment> <environment id="production"> <transactionManager type="EXTERNAL"> <dataSource type="JNDI"> </environment> </environments>

Ifyoucallabuildmethodthattakestheenvironmentparameter,theniBATISwillusetheconfiguration forthatenvironment.Ofcourse,ifyouspecifyaninvalidenvironment,youwillreceiveanerror.Ifyou calloneofthebuildmethodsthatdoesnottaketheenvironmentparameter,thenthedefault environmentisuses(whichisspecifiedasdefault=developmentintheexampleabove). Ifyoucallamethodthattakesapropertiesinstance,theniBATISwillloadthosepropertiesandmake themavailabletoyourconfiguration.Thosepropertiescanbeusedinplaceofmostvaluesinthe configurationusingthesyntax:${propName}

16August2009

48

iBATIS3UserGuide

RecallthatpropertiescanalsobereferencedfromtheSqlMapConfig.xmlfile,orspecifieddirectlywithin it.Thereforeitsimportanttounderstandthepriority.Wementioneditearlierinthisdocument,but hereitisagainforeasyreference: Ifapropertyexistsinmorethanoneoftheseplaces,iBATISloadstheminthefollowingorder: Propertiesspecifiedinthebodyofthepropertieselementarereadfirst, Propertiesloadedfromtheclasspathresourceorurlattributesoftheproperties elementarereadsecond,andoverrideanyduplicatepropertiesalreadyspecified, Propertiespassedasamethodparameterarereadlast,andoverrideanyduplicate propertiesthatmayhavebeenloadedfromthepropertiesbodyandtheresource/url attributes.

Thus,thehighestprioritypropertiesarethosepassedinasamethodparameter,followedby resource/urlattributesandfinallythepropertiesspecifiedinthebodyoftheproperties element. Sotosummarize,thefirstfourmethodsarelargelythesame,butwithoverridestoallowyouto optionallyspecifytheenvironmentand/orproperties.Hereisanexampleofbuildinga SqlSessionFactoryfromanSqlMapConfig.xmlfile.


String resource = "org/apache/ibatis/builder/MapperConfig.xml"; Reader reader = Resources.getResourceAsReader(resource); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(reader);

NoticethatweremakinguseoftheResourcesutilityclass,whichlivesintheorg.apache.ibatis.io package.TheResourcesclass,asitsnameimplies,helpsyouloadresourcesfromtheclasspath, filesystemorevenawebURL.AquicklookattheclasssourcecodeorinspectionthroughyourIDEwill revealitsfairlyobvioussetofusefulmethods.Heresaquicklist:


URL getResourceURL(String resource) URL getResourceURL(ClassLoader loader, String resource) InputStream getResourceAsStream(String resource) InputStream getResourceAsStream(ClassLoader loader, String resource) Properties getResourceAsProperties(String resource) Properties getResourceAsProperties(ClassLoader loader, String resource) Reader getResourceAsReader(String resource) Reader getResourceAsReader(ClassLoader loader, String resource) File getResourceAsFile(String resource) File getResourceAsFile(ClassLoader loader, String resource) InputStream getUrlAsStream(String urlString) Reader getUrlAsReader(String urlString) Properties getUrlAsProperties(String urlString) Class classForName(String className)

16August2009 49

iBATIS3UserGuide

ThefinalbuildmethodtakesaninstanceofConfiguration.TheConfigurationclasscontainseverything youcouldpossiblyneedtoknowaboutaSqlSessionFactoryinstance.TheConfigurationclassisuseful forintrospectingontheconfiguration,includingfindingandmanipulatingSQLmaps(notrecommended oncetheapplicationisacceptingrequests).Theconfigurationclasshaseveryconfigurationswitchthat youvelearnedaboutalready,onlyexposedasaJavaAPI.Heresasimpleexampleofhowtomanuallya Configurationinstanceandpassittothebuild()methodtocreateaSqlSessionFactory.


DataSource dataSource = BaseDataTest.createBlogDataSource(); TransactionFactory transactionFactory = new JdbcTransactionFactory(); Environment environment = new Environment("development", transactionFactory, dataSource); Configuration configuration = new Configuration(environment); configuration.setLazyLoadingEnabled(true); configuration.setEnhancementEnabled(true); configuration.getTypeAliasRegistry().registerAlias(Blog.class); configuration.getTypeAliasRegistry().registerAlias(Post.class); configuration.getTypeAliasRegistry().registerAlias(Author.class); configuration.addMapper(BoundBlogMapper.class); configuration.addMapper(BoundAuthorMapper.class); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(configuration);

NowyouhaveaSqlSessionFactory,thatcanbeusedtocreateSqlSessioninstances.

SqlSessionFactory
SqlSessionFactoryhassixmethodsthatareusedtocreateSqlSessionInstances.Ingeneral,thedecisions youllbemakingwhenselectingoneofthesemethodsare: Transaction:Doyouwanttouseatransactionscopeforthesession,oruseautocommit (usuallymeansnotransactionwithmostdatabasesand/orJDBCdrivers)? Connection:DoyouwantiBATIStoacquireaConnectionfromtheconfiguredDataSourcefor you,ordoyouwanttoprovideyourown? Execution:DoyouwantiBATIStoreusePreparedStatementsand/orbatchupdates(including insertsanddeletes)?

ThesetofoverloadedopenSession()methodsignaturesallowyoutochooseanycombinationofthese optionsthatmakessense.
SqlSession openSession() SqlSession openSession(boolean autoCommit) SqlSession openSession(Connection connection) SqlSession openSession(ExecutorType execType) SqlSession openSession(ExecutorType execType, boolean autoCommit) SqlSession openSession(ExecutorType execType, Connection connection) Configuration getConfiguration();

16August2009

50

iBATIS3UserGuide ThedefaultopenSession()methodthattakesnoparameterswillcreateaSqlSessionwiththefollowing characteristics: Atransactionscopewillbestarted(i.e.NOTautocommit) AConnectionobjectwillbeacquiredfromtheDataSourceinstanceconfiguredbytheactive environment. NoPreparedStatementswillbereused,andnoupdateswillbebatched.

Mostofthemethodsareprettyselfexplanatory.Toenableautocommit,passavalueoftruetothe optionalautoCommitparameter.Toprovideyourownconnection,passaninstanceofConnectionto theconnectionparameter.NotethattheresnooverridetosetboththeConnectionandautoCommit, becauseiBATISwillusewhateversettingtheprovidedconnectionobjectiscurrentlyusing. TheoneparameterthatmightbenewtoyouisExecutorType.Thisenumerationdefines3values: ExecutorType.SIMPLE Thistypeofexecutordoesnothingspecial.ItcreatesanewPreparedStatementforeach executionofastatement. ExecutorType.REUSE ThistypeofexecutorwillreusePreparedStatements. ExecutorType.BATCH ThisexecutorwillbatchallupdatestatementsanddemarcatethemasnecessaryifSELECTsare executedbetweenthem,toensureaneasytounderstandbehavior. Note:TheresonemoremethodontheSqlSessionFactorythatwedidntmention,andthatis getConfiguration().ThismethodwillreturnaninstanceofConfigurationthatyoucanusetointrospect upontheiBATISconfigurationatruntime. Note:IfyouveusedapreviousversionofiBATIS,youllrecallthatsessions,transactionsandbatches wereallsomethingseparate.Thisisnolongerthecase.Allthreeareneatlycontainedwithinthescope ofasesson.Youneednotdealwithtransactionsorbatchesseparatelytogetthefullbenefitofthem.

SqlSession
Asmentionedabove,theSqlSessioninstanceisthemostpowerfulclassiniBATIS.Itiswhereyoullfind allofthemethodstoexecutestatements,commitorrollbacktransactionsandacquiremapper instances. ThreareovertwentymethodsontheSqlSessionclass,soletsbreakthemupintomoredigestible groupings.

StatementExecutionMethods
16August2009 51

iBATIS3UserGuide

ThesemethodsareusedtoexecuteSELECT,INSERT,UPDATEandDELETEstatementsthataredefinedin yourSQLMappingXMLfiles.Theyareprettyselfexplanatory,eachtakestheIDofthestatementand theParameterObject,whichcanbeaprimitive(autoboxed,orwrapper),aJavaBean,aPOJOoraMap.


Object selectOne(String statement, Object parameter) List selectList(String statement, Object parameter) int insert(String statement, Object parameter) int update(String statement, Object parameter) int delete(String statement, Object parameter)

ThedifferencebetweenselectOneandselectListisonlyinthatselectOnemustreturnexactlyone object.Ifanymorethanone,ornone(null)isreturned,anexceptionwillbethrown.Ifyoudontknow howmanyobjectsareexpected,useselectList.Ifyouwanttocheckfortheexistenceofanoject,youre betteroffreturningacount(0or1).Becausenotallstatementsrequireaparameter,thesemethods areoverloadedwithversionsthatdonotrequiretheparameterobject.


Object selectOne(String statement) List selectList(String statement) int insert(String statement) int update(String statement) int delete(String statement)

Finally,therearethreeadvancedversionsoftheselectmethodsthatallowyoutorestricttherangeof rowstoreturn,orprovidecustomresulthandlinglogic,usuallyforverylargedatasets.
List selectList (String statement, Object parameter, int offset, int limit) void select (String statement, Object parameter, ResultHandler handler) void select (String statement, Object parameter, int offset, int limit, ResultHandler handler)

TheoffsetparametercausesiBATIStoskipthenumberofrecordsspecified.Differentdriversareableto achievedifferentlevelsofefficiencyinthisregard.Forthebestperformance,useresultsettypesof SCROLL_SENSITIVEorSCROLL_INSENSITIVE(inotherwords:notFORWARD_ONLY). TheResultHandlerparameterallowsyoutohandleeachrowhoweveryoulike.YoucanaddittoaList, createaMap,Set,orthroweachresultawayandinsteadkeeponlyrolleduptotalsofcalculations.You candoprettymuchanythingwiththeResultHandler,anditswhatiBATISusesinternallyitselftobuild resultsetlists. Theinterfaceisverysimple.


package org.apache.ibatis.executor.result; public interface ResultHandler { void handleResult(ResultContext context); }

16August2009

52

iBATIS3UserGuide TheResultContextparametergivesyouaccesstotheresultobjectitself,acountofthenumberofresult objectscreated,andaBooleanstop()methodthatyoucanusetostopiBATISfromloadinganymore results.

TransactionControlMethods
Therearefourmethodsforcontrollingthescopeofatransaction.Ofcourse,thesehavenoeffectif youvechosentouseautocommitorifyoureusinganexternaltransactionmanager.However,if youreusingtheJDBCtransactionmanager,managedbytheConnectioninstance,thenthefour methodsthatwillcomeinhandyare:
void void void void commit() commit(boolean force) rollback() rollback(boolean force)

BydefaultiBATISdoesnotactuallycommitunlessitdetectsthatthedatabasehasbeenchangedbya calltoinsert,updateordelete.Ifyouvesomehowmadechangeswithoutcallingthesemethods,then youcanpasstrueintothecommitandrollbackmethodstoguaranteethatitwillbecommitted(note, youstillcantforceasessioninautocommitmode,oronethatisusinganexternaltransaction manager.Mostofthetimeyouwonthavetocallrollback(),asiBATISwilldothatforyouifyoudont callcommit.However,ifyouneedmorefinegrainedcontroloverasessionwheremultiplecommitsand rollbacksarepossible,youhavetherollbackoptiontheretomakethatpossible.

EnsuringthatSqlSessionisClosed
void close()

Themostimportantthingyoumustensureisthatyoucloseanysessionsthatyouopen.Thebestway toensurethisistousethefollowingunitofworkpattern:
SqlSession session = sqlSessionFactory.openSession(); try { // following 3 lines pseudocod for doing some work session.insert(); session.update(); session.delete(); session.commit(); } finally { session.close(); }

Note:JustlikeSqlSessionFactory,youcangettheinstanceofConfigurationthattheSqlSessionis usingbycallingthegetConfiguration()method.
Configuration getConfiguration()

16August2009

53

iBATIS3UserGuide

UsingMappers
<T> T getMapper(Class<T> type)

Whilethevariousinsert,update,deleteandselectmethodsabovearepowerful,theyarealsovery verbose,nottypesafeandnotashelpfultoyourIDEorunittestsastheycouldbe.Wevealreadyseen anexampleofusingMappersintheGettingStartedsectionabove. Therefore,amorecommonwaytoexecutemappedstatementsistouseMapperclasses.Amapper classissimplyaninterfacewithmethoddefinitionsthatmatchupagainsttheSqlSessionmethods.The followingexampleclassdemonstratessomemethodsignaturesandhowtheymaptotheSqlSession.


public class AuthorMapper { // (Author) selectOne(selectAuthor,5); Author selectAuthor(int id); // (List<Author>) selectList(selectAuthors) List<Author> selectAuthors(); // insert(insertAuthor, author) void insertAuthor(Author author); // updateAuthor(updateAuhor, author) void updateAuthor(Author author); // delete(deleteAuthor,5) void deleteAuthor(int id); }

Inanutshell,eachMappermethodsignatureshouldmatchthatoftheSqlSessionmethodthatits associatedto,butwithouttheStringparameterID.Instead,themethodnamemustmatchthemapped statementID. Inaddition,thereturntypemustmatchthatoftheexpectedresulttype.Alloftheusualtypesare supported,including:Primitives,Maps,POJOsandJavaBeans. Mapperclassesdonotneedtoimplementanyinterfaceorextendanyclass.Aslongasthemethod signaturecanbeusedtouniquelyidentifyacorrespondingmappedstatement.

MapperAnnotations
Sincetheverybeginning,iBATIShasbeenanXMLdrivenframework.TheconfigurationisXMLbased, andtheMappedStatementsaredefinedinXML.WithiBATIS3,therearenewoptionsavailable.iBATIS 3buildsontopofacomprehensiveandpowerfulJavabasedConfigurationAPI.ThisConfigurationAPIis thefoundationfortheXMLbasediBATISconfiguration,aswellasthenewAnnotationbased configuration.Annotationsofferasimplewaytoimplementsimplemappedstatementswithout introducingalotofoverhead. Note:JavaAnnotationsareunfortunatelylimitedintheirexpressivenessandflexibility.Despitealot oftimespentininvestigation,designandtrials,themostpowerfuliBATISmappingssimplycannotbe builtwithAnnotationswithoutgettingridiculousthatis.C#Attributes(forexample)donotsuffer fromtheselimitations,andthusiBATIS.NETwillenjoyamuchricheralternativetoXML.Thatsaid,the JavaAnnotationbasedconfigurationisnotwithoutitsbenefits.

16August2009

54

iBATIS3UserGuide TheAnnotationsareasfollows: Annotation @CacheNamespace Target Class

@CacheNamespaceRef

Class

@ConstructorArgs

Method

@Arg

Method

@TypeDiscriminator

Method

@Case

Method

@Results

Method

@Result

Method

@One

Method

XMLEquivalent Description <cache> Configuresthecacheforthegivennamespace (i.e.class).Attributes:implementation, eviction,flushInterval,sizeandreadWrite. <cacheRef> Referencesthecacheofanothernamespaceto use.Attributes:value,whichshouldbethe stringvalueofanamespace(i.e.afullyqualified classname). <constructor> Collectsagroupofresultstobepassedtoa resultobjectconstructor.Attributes:value, whichisanarrayofArgs. <arg> Asingleconstructorargumentthatispartofa <idArg> ConstructorArgscollection.Attributes:id, column,javaType,jdbcType,typeHandler.The idattributeisabooleanvaluethatidentifiesthe propertytobeusedforcomparisons,similarto the<idArg>XMLelement. <discriminator> Agroupofvaluecasesthatcanbeusedto determinetheresultmappingtoperform. Attributes:column,javaType,jdbcType, typeHandler,cases.Thecasesattributeisan arrayofCases. <case> Asinglecaseofavalueanditscorresponding mappings.Attributes:value,type,results.The resultsattributeisanarrayofResults,thusthis CaseAnnotationissimilartoanactual ResultMap,specifiedbytheResultsannotation below. <resultMap> AlistofResultmappingsthatcontaindetailsof howaparticularresultcolumnismappedtoa propertyorfield.Attributes:value,whichisan arrayofResultannotations. <result> Asingleresultmappingbetweenacolumnand <id> apropertyorfield.Attributes:id,column, property,javaType,jdbcType,typeHandler, one,many.Theidattributeisabooleanvalue thatindicatesthatthepropertyshouldbeused forcomparisons(similarto<id>intheXML mappings).Theoneattributeisforsingle associations,similarto<association>,andthe manyattributeisforcollections,similarto <collection>.Theyarenamedastheyareto avoidclassnamingconflicts. <association> Amappingtoasinglepropertyvalueofa complextype.Attributes:select,whichisthe

16August2009

55

iBATIS3UserGuide

fullyqualifiednameofamappedstatement(i.e. mappermethod)thatcanloadaninstanceof theappropriatetype.Note:Youwillnoticethat joinmappingisnotsupportedviathe AnnotationsAPI.Thisisduetothelimitationin JavaAnnotationsthatdoesnotallowfor circularreferences. Amappingtoacollectionpropertyofacomplex types.Attributes:select,whichisthefully qualifiednameofamappedstatement(i.e. mappermethod)thatcanloadacollectionof instancesoftheappropriatetypes.Note:You willnoticethatjoinmappingisnotsupported viatheAnnotationsAPI.Thisisduetothe limitationinJavaAnnotationsthatdoesnot allowforcircularreferences. Thisannotationprovidesaccesstothewide rangeofswitchesandconfigurationoptions thatarenormallypresentonthemapped statementasattributes.Ratherthan complicateeachstatementannotation,the Optionsannotationprovidesaconsistentand clearwaytoaccessthese.Attributes: useCache=true,flushCache=false, resultSetType=FORWARD_ONLY, statementType=PREPARED,fetchSize=1, timeout=1,useGeneratedKeys=false, keyProperty=id.Itsimportanttounderstand thatwithJavaAnnotations,thereisnowayto specifynullasavalue.Therefore,onceyou engagetheOptionsannotation,yourstatement issubjecttoallofthedefaultvalues.Pay attentiontowhatthedefaultvaluesareto avoidunexpectedbehavior. Eachoftheseannotationsrepresentstheactual SQLthatistobeexecuted.Theyeachtakean arrayofstrings(orasinglestringwilldo).Ifan arrayofstringsispassed,theyareconcatenated withasinglespacebetweeneachtoseparate them.Thishelpsavoidthemissingspace problemwhenbuildingSQLinJavacode. However,yourealsowelcometoconcatenate togetherasinglestringifyoulike.Attributes: value,whichisthearrayofStringstoformthe singleSQLstatement. ThesealternativeSQLannotationsallowyouto specifyaclassnameandamethodthatwill returntheSQLtorunatexecutiontime.Upon

@Many

Method <collection>

@Options

Method Attributesof mapped statements.

@Insert @Update @Delete @Select

Method <insert> <update> <delete> <select>

@InsertProvider @UpdateProvider @DeleteProvider 16August2009

Method <insert> <update> <delete> 56

iBATIS3UserGuide @SelectProvider <select> Allowsfor creationof dynamicSQL.

executingthemappedstatement,iBATISwill instantiatetheclass,andexecutethemethod, asspecifiedbytheprovider.Themethodcan optionallyaccepttheparameterobjectasits soleparameter,butmustonlyspecifythat parameter,ornoparameters.Attributes:type, method.Thetypeattributeisthefullyqualified nameofaclass.Themethodisthenameofthe methodonthatclass.Note:Followingthis sectionisadiscussionabouttheSelectBuilder class,whichcanhelpbuilddynamicSQLina cleaner,easiertoreadway.

SelectBuilder
OneofthenastiestthingsaJavadeveloperwilleverhavetodoisembedSQLinJavacode.Usuallythis isdonebecausetheSQLhastobedynamicallygeneratedotherwiseyoucouldexternalizeitinafileor astoredproc.Asyouvealreadyseen,iBATIShasapowerfulanswerfordynamicSQLgenerationinits XMLmappingfeatures.However,sometimesitbecomesnecessarytobuildaSQLstatementstring insideofJavacode.Inthatcase,iBATIShasonemorefeaturetohelpyouout,beforereducingyourself tothetypicalmessofplussigns,quotes,newlines,formattingproblemsandnestedconditionalstodeal withextracommasorANDconjunctionsIndeed,dynamicallygeneratingSQLcodeinJavacanbeareal nightmare. iBATIS3introducesasomewhatdifferentconcepttodealwiththeproblem.Wecouldhavejustcreated aninstanceofaclassthatletsyoucallmethodsagainstittobuildaSQLstatementonestepatatime. ButthenourSQLendsuplookingmorelikeJavaandlesslikeSQL.Instead,weretryingsomethinga littledifferent.TheendresultisaboutasclosetoaDomainSpecificLanguagethatJavawilleverachieve initscurrentform TheSecretsofSelectBuilder TheSelectBuilderclassisnotmagical,nordoesitdoanyofusanygoodifyoudontknowhowitworks. Sorightoffthebat,letslookatwhatitdoes.SelectBuilderusesacombinationofStaticImportsanda ThreadLocalvariabletoenableacleansyntaxthatcanbeeasilyinterlacedwithconditionalsandtakes careofalloftheSQLformattingforyou.Itallowsustocreatemethodslikethis:
public String selectBlogsSql() { BEGIN(); // Clears ThreadLocal variable SELECT("*"); FROM("BLOG"); return SQL(); }

Thatsaprettysimpleexamplethatyoumightjustchoosetobuildstatically.Soheresamore complicatedexample:

16August2009

57

iBATIS3UserGuide

private String selectPersonSql() { BEGIN(); // Clears ThreadLocal variable SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME"); SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON"); FROM("PERSON P"); FROM("ACCOUNT A"); INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID"); INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID"); WHERE("P.ID = A.ID"); WHERE("P.FIRST_NAME like ?"); OR(); WHERE("P.LAST_NAME like ?"); GROUP_BY("P.ID"); HAVING("P.LAST_NAME like ?"); OR(); HAVING("P.FIRST_NAME like ?"); ORDER_BY("P.ID"); ORDER_BY("P.FULL_NAME"); return SQL(); }

BuildingtheaboveSQLwouldbeabitofatrialinStringconcatenation.Forexample:
"SELECT P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME, " "P.LAST_NAME,P.CREATED_ON, P.UPDATED_ON " + "FROM PERSON P, ACCOUNT A " + "INNER JOIN DEPARTMENT D on D.ID = P.DEPARTMENT_ID " + "INNER JOIN COMPANY C on D.COMPANY_ID = C.ID " + "WHERE (P.ID = A.ID AND P.FIRST_NAME like ?) " + "OR (P.LAST_NAME like ?) " + "GROUP BY P.ID " + "HAVING (P.LAST_NAME like ?) " + "OR (P.FIRST_NAME like ?) " + "ORDER BY P.ID, P.FULL_NAME";

Ifyoupreferthatsyntax,thenyourestillwelcometouseit.Itisquiteerrorpronethough.Noticethe carefuladditionofaspaceattheendofeachline.Nowevenifyoudopreferthatsyntax,thenext exampleisinarguablyfarsimplerthanJavaStringconcatenation:


private String selectPersonLike(Person p){ BEGIN(); // Clears ThreadLocal variable SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME"); FROM("PERSON P"); if (p.id != null) { WHERE("P.ID like #{id}"); } if (p.firstName != null) { WHERE("P.FIRST_NAME like #{firstName}"); } if (p.lastName != null) { WHERE("P.LAST_NAME like #{lastName}"); } ORDER_BY("P.LAST_NAME"); return SQL();

16August2009

58

iBATIS3UserGuide
}

Whatissospecialaboutthatexample?Well,ifyoulookclosely,itdoesnthavetoworryabout accidentallyduplicatingANDkeywords,orchoosingbetweenWHEREandANDorneither!The statementabovewillgenerateaquerybyexampleforallPERSONrecords,oneswithIDlikethe parameter,orthefirstNameliketheparameter,orthelastNameliketheparameterorany combinationofthethree.TheSelectBuildertakescareofunderstandingwhereWHENneedstogo, whereanANDshouldbeusedandalloftheStringconcatenation.Bestofall,itdoesitalmost regardlessofwhichorderyoucallthesemethodsin(theresonlyoneexceptionwiththeOR()method). Thetwomethodsthatmaycatchyoureyeare:BEGIN()andSQL().Inanutshell,everySelectBuilder methodshouldstartwithacalltoBEGIN()andendwithacalltoSQL().Ofcourseyoucanextract methodsinthemiddletobreakupyourlogic,butthescopeoftheSQLgenerationshouldalwaysbegin withBEGIN()andendwithSQL().TheBEGIN()methodclearstheThreadLocalvariable,tomakesureyou dontaccidentallycarryanystateforward,andtheSQL()methodassemblesyourSQLstatementbased onthecallsyoumadesincethelastcalltoBEGIN().NotethatBEGIN()hasasynonymcalledRESET(), whichdoesexactlythesamethingbutreadsbetterincertaincontexts. TousetheSelectBuilderasintheexamplesabove,yousimplyneedtoimportitstaticallyasfollows:
import static org.apache.ibatis.jdbc.SelectBuilder.*;

Oncethisisimported,theclassyoureworkingwithinwillhavealloftheSelectBuildermethods availabletoit.Thecompletesetofmethodsisasfollows: Method BEGIN()/RESET() Description ThesemethodscleartheThreadLocalstateoftheSelectBuilderclass,and prepareitforanewstatementtobebuilt.BEGIN()readsbestwhen startinganewstatement.RESET()readsbestwhenclearingastatement inthemiddleofexecutionforsomereason(perhapsifthelogicdemands acompletelydifferentstatementundersomeconditions). StartsorappendstoaSELECTclause.Canbecalledmorethanonce,and parameterswillbeappendedtotheSELECTclause.Theparametersare usuallyacommaseparatedlistofcolumnsandaliases,butcanbe anythingacceptabletothedriver. StartsorappendstoaFROMclause.Canbecalledmorethanonce,and parameterswillbeappendedtotheFROMclause.Parametersareusually atablenameandanalias,oranythingacceptabletothedriver. AddsanewJOINclauseoftheappropriatetype,dependingonthe methodcalled.Theparametercanincludeastandardjoinconsistingof thecolumnsandtheconditionstojoinon.

SELECT(String)

FROM(String)

JOIN(String) INNER_JOIN(String) LEFT_OUTER_JOIN(String) RIGHT_OUTER_JOIN(String) WHERE(String) AppendsanewWHEREclausecondition,concatenatedbyAND.Canbe calledmultipletimes,whichcausesittoconcatenatethenewconditions eachtimewithAND.UseOR()tosplitwithanOR. OR() SplitsthecurrentWHEREclauseconditionswithanOR.Canbecalled 16August2009 59

iBATIS3UserGuide morethanonce,butcallingmorethanonceinarowwillgenerateerratic SQL. SplitsthecurrentWHEREclauseconditionswithanAND.Canbecalled morethanonce,butcallingmorethanonceinarowwillgenerateerratic SQL.BecauseWHEREandHAVINGbothautomaticallyconcatenatewith AND,thisisaveryuncommonmethodtouseandisonlyreallyincluded forcompleteness. AppendsanewGROUPBYclauseelements,concatenatedbyacomma. Canbecalledmultipletimes,whichcausesittoconcatenatethenew conditionseachtimewithacomma. AppendsanewHAVINGclausecondition,concatenatedbyAND.Canbe calledmultipletimes,whichcausesittoconcatenatethenewconditions eachtimewithAND.UseOR()tosplitwithanOR. AppendsanewORDERBYclauseelements,concatenatedbyacomma. Canbecalledmultipletimes,whichcausesittoconcatenatethenew conditionseachtimewithacomma. ThisreturnsthegeneratedSQL()andresetstheSelectBuilderstate(asif BEGIN()orRESET()werecalled).Thus,thismethodcanonlybecalled ONCE!

AND()

GROUP_BY(String)

HAVING(String)

ORDER_BY(String)

SQL()

16August2009

60

También podría gustarte