Abator is a code generator for iBATIS. The website says "abator" means "one who abates a nuisance". "Abate" means to become less intense.
Abator gathers information from a database schema and generates the following types of files.
Detailed documentation on Abator can be found here.
To install Abator for use with Java, download from here and unzip it. See the link "Abator 1.0.0 w/Binaries and Source". This will result in a directory name "abator-1".
The doc directory contains HTML documentation with index.html as the starting point.
The abator-1 directory contains abator.jar.
Our example database is named "music". It has two tables. The "artists" table has the columns "id" and "name". The "recordings" table has the columns "id", "name", "year" and "artist_id". The id columns are the primary keys of their tables. The recordings table artist_id column is a foreign key reference to the artists table id column. SQL for creating the database is in the following file named "createTables.sql".
Note that SQL is sometimes not portable between databases. In this example, were using the type "text" which is supported by MySQL, but not by all relational databases. Also, the way that primary keys are configured to "auto increment" varies across databases.
drop database if exists music; create database music; use music; drop table if exists artists; create table artists ( id int not null auto_increment, name text, primary key (id) ); drop table if exists recordings; create table recordings ( id int not null auto_increment, name text, year int, artist_id int, primary key (id) );
When using MySQL, this can be run with a command like the following.
mysql -uroot < createTables.sql
The first step in using Abator is to create an XML configuration file that specifies the following information.
Here's an example of an Abator configuration file named AbatorConfig.xml. This uses XML entities to provide a single place to modify certain values and avoid repeating them in the XML. For an explanation of the generatorSet attribute of the abatorContext element see here.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE abatorConfiguration PUBLIC "-//Apache Software Foundation//DTD Abator for iBATIS Configuration 1.0//EN" "http://ibatis.apache.org/dtd/abator-config_1_0.dtd" [ <!ENTITY connector "mysql-connector-java-5.0.4"> <!ENTITY dbName "music"> <!ENTITY mysqlDir "/Users/Mark/Documents/Programming/Databases/MySQL"> <!ENTITY mysqlJAR "&mysqlDir;/&connector;/&connector;-bin.jar"> <!ENTITY packagePrefix "com.ociweb.music"> <!ENTITY target "gen"> ]> <abatorConfiguration> <abatorContext id="MySQLTables" generatorSet="Java5"> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/&dbName;" userId="root" password=""> <classPathEntry location="&mysqlJAR;" /> </jdbcConnection> <javaModelGenerator targetPackage="&packagePrefix;.model" targetProject="⌖"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <sqlMapGenerator targetPackage="sqlmaps" targetProject="⌖"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <daoGenerator type="SPRING" targetPackage="&packagePrefix;.dao" targetProject="⌖"> <property name="enableSubPackages" value="true" /> </daoGenerator> <table schema="&dbName;" tableName="artists" domainObjectName="Artist"> <generatedKey column="id" sqlStatement="MySQL" identity="true"/> <!-- The next line is needed because Abator doesn't understand the MySQL type TEXT. --> <columnOverride column="name" jdbcType="VARCHAR" /> </table> <table schema="&dbName;" tableName="recordings" domainObjectName="Recording"> <generatedKey column="id" sqlStatement="MySQL" identity="true"/> <!-- The next line is needed because Abator doesn't understand the MySQL type TEXT. --> <columnOverride column="name" jdbcType="VARCHAR" /> </table> </abatorContext> </abatorConfiguration>
The following libraries are required to run the Abator tool and run the resulting code.
Abator can be run from Ant. Here's our Ant build.properties file.
programming.dir=/Users/Mark/Documents/Programming java.dir=${programming.dir}/Languages/Java abator.dir=${java.dir}/iBATIS/abator-1 abator.jar=${abator.dir}/abator.jar ibatis.dir=${java.dir}/iBATIS/ibatis-2 ibatis.jar=${ibatis.dir}/lib/ibatis-2.3.0.677.jar junit.dir=${java.dir}/JUnit/junit4.1 junit.jar=${junit.dir}/junit-4.1.jar log4j.dir=${java.dir}/Log4J/logging-log4j-1.2.14 log4j.jar=${log4j.dir}/dist/lib/log4j-1.2.14.jar mysql.dir=${programming.dir}/Databases/MySQL spring.dir=${java.dir}/Spring/spring-framework-2.0.2 jakarta.commons.dbcp.jar=${spring.dir}/lib/jakarta-commons/commons-dbcp.jar jakarta.commons.logging.jar=${spring.dir}/lib/jakarta-commons/commons-logging.jar jakarta.commons.pool.jar=${spring.dir}/lib/jakarta-commons/commons-pool.jar spring.jar=${spring.dir}/dist/spring.jar connector=mysql-connector-java-5.0.4 mysql.driver.jar=${mysql.dir}/${connector}/${connector}-bin.jar test=*
Here's our Ant build.xml file that includes an "abator" target and many other targets that will be discussed later.
<project name="AbatorDemo" default="test"> <property file="build.properties"/> <path id="classpath"> <pathelement location="build/classes"/> <!-- The next line is for generated SqlMap XML files. --> <pathelement location="gen/sqlmaps"/> <pathelement location="${abator.jar}"/> <pathelement location="${ibatis.jar}"/> <pathelement location="${jakarta.commons.dbcp.jar}"/> <pathelement location="${jakarta.commons.logging.jar}"/> <pathelement location="${jakarta.commons.pool.jar}"/> <pathelement location="${junit.jar}"/> <pathelement location="${log4j.jar}"/> <pathelement location="${mysql.driver.jar}"/> <pathelement location="${spring.jar}"/> </path> <target name="abator" depends="prepare" description="generates files for using iBATIS and Spring DAO"> <!-- Set the overwrite attribute to true to overwrite existing files with the same name or false to save newly generated files with a unique name (dot number at end) --> <taskdef name="abator" classpath="${abator.jar}" classname="org.apache.ibatis.abator.ant.AbatorAntTask"/> <abator overwrite="false" configfile="AbatorConfig.xml" verbose="false"/> </target> <target name="clean" depends="clean.test" description="deletes all generated files"> <delete dir="build"/> <delete dir="gen"/> </target> <target name="clean.test" description="deletes all generated files"> <delete dir="test"/> </target> <target name="compile" depends="abator" description="compiles source files"> <javac srcdir="gen" destdir="build/classes" classpathref="classpath" deprecation="on" debug="on"/> <javac srcdir="src/java" destdir="build/classes" classpathref="classpath" deprecation="on" debug="on"/> </target> <target name="load" depends="compile" description="deletes all records from the database and loads new ones"> <java classname="com.ociweb.music.LoadDB" classpathref="classpath" fork="true"/> </target> <target name="prepare"> <mkdir dir="build/classes"/> <mkdir dir="gen"/> <copy todir="build/classes"> <fileset dir="."> <include name="Item.xml"/> <include name="Store.xml"/> <include name="SqlMapConfig.xml"/> <include name="log4j.properties"/> </fileset> </copy> </target> <target name="run" depends="compile" description="runs the client application"> <java classname="com.ociweb.demo.Client" classpathref="classpath" fork="true"/> </target> <target name="test" depends="clean.test, compile" description="runs all JUnit tests"> <mkdir dir="test"/> <junit fork="yes" printsummary="yes"> <classpath refid="classpath"/> <batchtest todir="test"> <fileset dir="src/java" includes="**/${test}Test.java"/> </batchtest> <formatter type="xml"/> </junit> <junitreport toDir="test"> <fileset dir="test"/> <report format="frames" todir="test"/> </junitreport> <exec os="Windows" executable="cmd.exe"> <arg line="/c start test/index.html"/> </exec> <exec os="Mac OS X" executable="open"> <arg line="-a /Applications/Safari.app test/index.html"/> </exec> </target> </project>
Now we need to put some data in the database.
The following Java application demonstrates how to do this.
The methods getArtist
and addRecording
are used to simplify the process.
package com.ociweb.music; import com.ociweb.music.dao.*; import com.ociweb.music.model.*; import java.util.*; public class LoadDB { private ArtistDAO artistDAO; private RecordingDAO recordingDAO; private Map<String, Artist> artists = new HashMap<String, Artist>(); public static void main(String[] args) { new LoadDB(); } private LoadDB() { DAOHelper helper = new DAOHelper(); artistDAO = helper.getArtistDAO(); recordingDAO = helper.getRecordingDAO(); // Delete all records from the artists table. artistDAO.deleteByExample(new ArtistExample()); // Delete all records from the recordings table. recordingDAO.deleteByExample(new RecordingExample()); String name = "Deathcab For Cutie"; addRecording(name, "We Have the Facts and We're Voting Yes", 2000); addRecording(name, "The Photo Album", 2001); addRecording(name, "You Can Play These Songs With Chords", 2002); addRecording(name, "Transatlanticism", 2003); addRecording(name, "Plans", 2005); name = "Regina Spektor"; addRecording(name, "Begin To Hope", 2006); addRecording(name, "Soviet Kitch", 2003); } private Artist getArtist(String name) { Artist artist = artists.get(name); if (artist == null) { artist = new Artist(); artist.setName(name); int id = artistDAO.insert(artist); artists.put(name, artist); } return artist; } private void addRecording(String artistName, String name, int year) { Artist artist = getArtist(artistName); Recording recording = new Recording(); recording.setArtistId(artist.getId()); recording.setName(name); recording.setYear(year); int id = recordingDAO.insert(recording); } }
We're ready to write queries now. The following application demonstrates a few queries.
package com.ociweb.music; import com.ociweb.music.dao.*; import com.ociweb.music.model.*; import java.util.*; public class Client { private ArtistDAO artistDAO; private RecordingDAO recordingDAO; public static void main(String[] args) { new Client(); } private Client() { DAOHelper helper = new DAOHelper(); artistDAO = helper.getArtistDAO(); recordingDAO = helper.getRecordingDAO(); // Output the names of all the Artists in ascending order. System.out.println("All artists"); List<Artist> artists = artistDAO.selectByExample(new ArtistExample()); sortArtists(artists); Artist artist = null; for (Artist a : artists) { System.out.println(" " + a.getName()); artist = a; // retain a reference to the last Artist } // Get the id of the last Artist. int id = artist.getId(); // Output the name of the artists with a given id. System.out.println("\nArtist with id=" + id); artist = artistDAO.selectByPrimaryKey(id); System.out.println(" " + artist.getName()); // Output the names of all recordings from 2003 in ascending order. System.out.println("\n2003 Recordings"); RecordingExample recordingExample = new RecordingExample(); RecordingExample.Criteria recordingCriteria = recordingExample.createCriteria(); recordingCriteria.andYearEqualTo(2003); List<Recording> recordings = recordingDAO.selectByExample(recordingExample); sortRecordings(recordings); for (Recording r : recordings) { artist = artistDAO.selectByPrimaryKey(r.getArtistId()); System.out.println(" " + r.getName() + " by " + artist.getName()); } } private void sortArtists(List<Artist> list) { Comparator<Artist> artistComparator = new ArtistComparator(); Collections.sort(list, artistComparator); } private void sortRecordings(List<Recording> list) { Comparator<Recording> recordingComparator = new RecordingComparator(); Collections.sort(list, recordingComparator); } static class ArtistComparator implements Comparator<Artist> { public int compare(Artist artist1, Artist artist2) { return artist1.getName().compareTo(artist2.getName()); } } static class RecordingComparator implements Comparator<Recording> { public int compare(Recording recording1, Recording recording2) { return recording1.getName().compareTo(recording2.getName()); } } }
The output from this code follows.
All artists Deathcab For Cutie Regina Spektor Artist with id=2 Regina Spektor 2003 Recordings Soviet Kitch by Regina Spektor Transatlanticism by Deathcab For Cutie
See the instructions for configuring iBATIS logging here.
The "selectByExample" methods in the generated DAO classes can only perform queries that examine the columns of the single table associated with the DAO. To perform a query that requires a join, mapped statements can be added to the generated SqlMap.xml files as described in the iBATIS notes here.
Copyright © 2007 Object Computing, Inc. All rights reserved.