iBatis has been my ORM of choice for the last several years. From the Ruby-programmer’s point of view, iBatis is overloaded with XML boiler-plate code, but when it comes to high performance, iBatis is out of competition – explicit mapping of queries in XML configuration files (you have to write all the queries you’re going to use manually) has benefits of transparency and ease of maintenance (at any moment of time you can give your XML with mappings, based on standard SQL, to DBA that should not have a framework-specific knowledge to understand and fine tune your queries).
Since recently I’m tending to use Scala, modern elegant JVM-based language, for the back-end areas that require high performance. Though Scala and Java are 99% backwards compatible (you can call Scala code from Java, and vise verse), iBatis configuration under Scala was quite complicated (and ugly) as of late (until the beta 7 version of Scala 3.0 released, which contains support for non-Java classes: Scala objects, etc.).
Besides the non-Java classes support, iBatis 3.0 has some noticeable surprises (both good and bad) for old iBatis users (I’ve been using iBatis 2.3 in production, before):
- iBatis 3.0 is absolutely incompatible with older pre 3.0 versions of iBatis: package structure, class names, XML schemes and approach, in general, are different;
- It’s now possible to map beans via constructors (not only setter methods) – that’s the coolest feature in the light of Scala’s case classes;
- Mapping can be configured with annotations (an obvious attempt to follow the fashion). At the first glance, not very useful – instead of active record approach (when mapping annotations are added directly to the bean to be mapped), iBatis requires to create one mapping class per domain bean;
In the rest of the article I’m going to give you a small overview that will help to use iBatis 3.0 and Scala together. The example below shows some details of the back-end for IdeaBox project that I mentioned in one of my previous articles.
First of all, you need to create a domain class that will be mapped to query results. For domain classes in Scala you may use case class concept that provides lots of useful features (I’m particularly interested in the field’s immutability and handy default implementation of toString method):
Topic class includes contract that validates constructor arguments, forcing to provide non-empty strings.
As I mentioned before, iBatis 3.0 allows users to call queries by names (old approach), or to use methods of mappers. In the example I’ll use the first approach:
def getTopicById(id:String):Topic
}
class IdeaBoxDaoImpl extends IdeaBoxDao{
val sqlMap = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsReader("ibatis/SqlMapConfig.xml"))
def getTopicById(id:String):Topic = {
val session = sqlMap.openSession()
try{
session.selectOne("com.vasilrem.ideabox.model.TopicMapper.getTopic", id).asInstanceOf[Topic]
}finally {
session.close
}
}
}
Another difference with iBatis 2.3 is that now you have to explicitly open and close sessions (in the previous version, session handling was implicit and user were just calling methods of Sql Map instance).
The rest of classes in the IdeaBox assembly are:
Specification below defines the behavior of the assembly:
object IdeaBoxComponentSpecRunner extends ConsoleRunner(IdeaBoxAssemblySpec)
object IdeaBoxAssemblySpec extends Specification{
"Search for a topic by a valid ID" should{
"return topic instance" in {
IdeaBoxAssembly.getTopicById("1") must notBeNull
}
}
}
Set of XML configuration files required to run the example is similar to the one used in the previous version. You need to define datasource configuration and mapping XMLs:
<properties resource="properties/SqlMapConfig.properties" />
<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="ibatis/IDEA_TOPIC_SqlMap.xml"/>
</mappers>
</configuration>
Finally, you need to provide mapping XML that contains query and rules to map the result set to your domain object:
<resultMap id="topicResult" type="com.vasilrem.ideabox.model.Topic">
<constructor>
<idArg column="id" javaType="String"/>
<arg column="name" javaType="String"/>
</constructor>
</resultMap>
<select id="getTopic" parameterType="String" resultMap="topicResult">
SELECT ID, NAME FROM IDEA_TOPIC WHERE ID = #{value}
</select>
</mapper>
idArg is not a mandatory (you can use simple arg instead to pass the argument to constructor), but it significantly increases performance (idArg is used as identifier when comparing object instances).
Support of non-Java classes and mapping via constructor allows iBatis fans use their favorite ORM with modern JVM-languages. Domain model described with Scala’s case classes is minimalistic, transparent and perfectly compatible with the newer versions of iBatis.







Social comments and analytics for this post…
This post was mentioned on Twitter by remeniuk: Have written a small blog post about using Scala and iBatis3.0 together http://is.gd/99GpE...
Hi,
I just want to mention that with the most recent versions of specs, you can write your specification as:
class IdeaBoxAssemblySpec extends SpecificationWithJUnit {
“Search for a topic by a valid ID” should {
“return topic instance” in {
IdeaBoxAssembly.getTopicById(“1″) must notBeNull
}
}
}
This will be:
– a junit test, runnable with Maven (change the pom.xml to allow for classes ending with “Spec” to be executed, or add “Test” at the end of the class)
– executable on the command line, with: scala -cp run com.vasilrem.ideabox.IdeaBoxAssemblySpec
Eric.
@Eric Thx for the tip! I read about this capability in the Specs docu, but didn’t try it out yet.
Vasil,
Thanks for the helpful post.
I have wondered how to use SqlSession.getMapper and then figured out this:
TopicMapper would be a trait:
trait TopicMapper {
def getTopic(id: String) : Topic
}
Then IdeaBoxDaoImpl.getTopicById could be written:
def getTopicById(id:String):Topic = {
val session = sqlMap.openSession()
val topicMapper = session.getMapper(classOf[TopicMapper])
try{
topicMapper.getTopic(id)
}finally {
session.close
}
}
or
def getTopicById(id:String):Topic = {
val session = sqlMap.openSession()
try{
session.getMapper(classOf[TopicMapper]).getTopic(id)
}finally {
session.close
}
}
Regards.
Hi Eduardo,
Glad you find it helpful!
Your comment is right to the point, and this approach could look even more elegant and trendy, if mapping trait is used with the new annotations (to bind trait’s functions to queries in the code).
Thx+regards