MyBatis / iBATIS
Dependency :
compile ":ibatis:1.3.1"
Summary
Installation
Installation
To install the iBATIS plugin, run this target within your Grails application:$> grails install-plugin ibatis
Description
Impatient? See Quick StartOne of the consequences of this design is that our application logic
must have knowledge of the underlying database schema. The class that
fills this role is called the Gateway. The objects passed to and
from the Gateway are POGOs (or POJOs).
These files are typical for an HSQLDB instance.
The data file The MyBatis plugin uses the standard You should now have a new Grails target 'create-gateway'.Under If you look at the contents of
Run the Grails target
Introduction
The MyBATIS project is a persistence framework developed by Clinton Begin. It's SQL-oriented; that is, developers write SQL statements to perform all of the ORM operations.Prior to 2010, MyBatis was hosted at Apache and was known as iBATIS. You may still notice this legacy in the documentation and code (including the name of this plugin).
Why would I use MyBatis?
The question arises: why would we want to use another persistence framework when Grails already includes GORM? GORM is, arguably, the most productive ORM implementation available. The underlying Hibernate framework is extremely flexible and can be fit to any 'corner case' persistence issue.GORM and MyBatis are complementary approaches. Generally, prefer GORM over MyBatis. However, there are a few situations where you may find MyBatis to express the concepts more clearly. Here are a few examples:- Working with stored procedures, such as in a Transaction Script architecture
- Projects where database developers need to perform fine-grained tuning of SQL
- Porting of legacy applications to Grails
Where can I find out more about MyBatis?
A great source of information is the MyBatis User Guide.Table Data Gateway architecture
This plugin implements the Table Data Gateway pattern, as described in the excellent Patterns of Enterprise Application Architecture book by Martin Fowler. To excerpt:Mixing SQL in application logic can cause several problems. Many developers aren't comfortable with SQL, and many who are comfortable may not write it well. Database administrators need to be able to find SQL easily so they can figure out how to tune and evolve the database.A Table Data Gateway holds all the SQL for accessing a single table or view: selects, inserts, updates, and deletes. Other code calls its methods for all interaction with the database.
Quick Start
Set up the DataSource and Test data
To get us started quickly, we'll use a preconfigured HSQLDB instance. Download the test files here and extract into your main project folder. You'll now have these files:testDb.properties testDb.script accounts.csv
testDb.script defines the test table
we'll use for this example.
| table ACCOUNT | |||
|---|---|---|---|
| ID | ACCOUNT_HOLDER | ACCOUNT_TYPE | INCEPTION_DATE |
| primary key | varchar(100) | varchar(10), one of 'savings' or 'checking' | date |
accounts.csv contains our test data0,Matthew Bellamy,checking,2009-01-01 1,Matthew Bellamy,savings,2009-01-05 2,Christopher Wolstenholme,checking,2009-04-22 3,Dominic Howard,savings,2009-10-15
DataSource.groovy file to define the
connections, just like GORM. Changing our application to use our test database is just a matter
of modifying grails-app/conf/DataSource.groovy to set the proper connection properties:
environments {
…
test {
dataSource {
dbCreate = "update"
url = "jdbc:hsqldb:file:testDb;shutdown=true"
}
}
…
}Create our POGO
Now we'll define a Groovy object to store our table data. Create the filesrc/groovy/com/example/AccountInfo.groovy :
package com.exampleenum AccountType { checking, savings }class AccountInfo { Long id String accountHolder AccountType accountType Date inceptionDate }
Install the iBATIS plugin
Within your Grails application, install the MyBatis plugin using the commandgrails install-plugin ibatis
Create a gateway class
Thecreate-gateway target will generate our gateway class, iBATIS mapping file,
and integration test.grails create-gateway com.example.Account
grails-app/gateways , you should see the following files:
user ~ $ ls grails-app/gateways/com/example AccountGateway.groovy account.xml
AccoutGateway.groovy , you'll see only a class definition.
We won't define our methods here. Rather, we'll add operations to the associated
mapping file.Add operations to the mapping file
Our iBATIS mapping file,account.xml , will define all of our database operations.
Each operation defined here gets automatically exposed as a method on our gateway class.This quick start is a bit contrived, given that all of the operations we will define can more easily be expressed in GORM. See the Introduction for tips about when you might want to use iBATIS over GORM.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.account"> <select id="getAccountByID" resultMap="accountResultMap"> select id, account_holder, account_type, inception_date from account where id = #{value,jdbcType=NUMERIC} </select> <select id="getAccountsOfType" parameterType="string" resultMap="accountResultMap"> select id, account_holder, account_type, inception_date from account where account_type = #{value,jdbcType=VARCHAR} order by account_holder </select> <select id="getAccountsOpenedSince" parameterType="date" resultMap="accountResultMap"> select id, account_holder, account_type, inception_date from account where inception_date > #{value,jdbcType=DATE} order by inception_date </select> <update id="updateAccount" parameterType="com.example.AccountInfo"> update account set account_holder = #{accountHolder}, account_type = #{accountType,jdbcType=VARCHAR}, inception_date = #{inceptionDate} where id = #{id} </update> <resultMap id="accountResultMap" type="com.example.AccountInfo"> <result column="account_holder" property="accountHolder"/> <result column="account_type" property="accountType"/> <result column="inception_date" property="inceptionDate"/> </resultMap> </mapper>
The above syntax might seem slightly off to experienced iBATIS 2.x users. This is the new iBATIS 3.x format; you'll find it similar (but not totally compatible) with the 2.x syntax.
Integration Test
We'll put it all together in the integration test. Thecreate-gateway Grails target created an integration test, test/integration/com/example/AccountGatewayTests.groovy :package com.exampleimport grails.test.*/* In these tests, the property 'gateway' is provided by the superclass */ class AccountGatewayTests extends GatewayIntegrationTest { /* Our simplest case: call the 'getAccountByID' operation to retrieve a single value */ void testAccountById() { def chris = gateway.getAccountByID(2) // from our test data, "2,Christopher Wolstenholme,checking,2009-04-22" assert chris?.accountHolder == 'Christopher Wolstenholme' assert chris?.accountType == AccountType.checking assert !gateway.getAccountByID(-5) } /* Multiple result values: since we included the plural form 'Accounts' in our operation name, we expect multiple rows */ void testAccountsOfType() { def checkingAccounts = gateway.getAccountsOfType(AccountType.checking) assert checkingAccounts?.size() == 2 } /* Note that in the operation we had to escape the '>' sign */ void testAccountsOpenedSince() { def referenceDate = Calendar.getInstance(TimeZone.getTimeZone('GMT')) referenceDate.clear() referenceDate.set(2009, Calendar.MARCH, 1) def newAccounts = gateway.getAccountsOpenedSince(referenceDate.time) assert newAccounts?.size() == 2 newAccounts.each { assert referenceDate.time.before(it?.inceptionDate) } } /* We can also update */ void testUpdate() { def dominic = gateway.getAccountByID(3) assert dominic?.accountType == AccountType.savings dominic.accountType = AccountType.checking gateway.updateAccount(dominic) assert gateway.getAccountByID(3).accountType == AccountType.checking } }
test-app and look at the generated file target/test-reports/html/index.html .
You should see success for all our test cases.