Tuesday, March 27, 2012

Refactoring to Java Enums

There are only two books (so far) I've ever read three times in full: Dune by Frank Herbert and The Pragmatic Progammer by Dave Thomas and Andy Hunt. From these you could rightly infer what I consider the pinnacle of SF and programming/software engineering literature. (Cryptonomicon will certainly join this list at some point.)

There is a much larger number of books I've read twice in full, so I'll spare you (and myself) the tedium of listing them here.

Then there are those "reference style" books where you have read parts of them dozens of times and the whole thing through at least once, maybe twice. Two that fall into that category for me are Josh Bloch's Effective Java and Martin Fowler's (et al.) Refactoring.

In fact, I recently just re-read Effective Java cover to cover and decided to read most of Refactoring again. The former, with the 2nd edition, has been updated to use many of the latest features of Java 5 and 6. The latter has not.


/* ---[ Java 5 enums: powerful, underutilized ]--- */

One of the themes in Effective Java (and there are many) is that Java enums are powerful and underutilized. For example, in Item 30 Bloch says:

Java's enum types are full-fledged classes, far more powerful than their counterparts in these other languages [C,C++,C#], where enums are essentially int values....

In addition to rectifying the deficiencies of int enums, enum types let you add arbitrary methods and fields and implement arbitrary interfaces. They provide high-quality implementations of all the Object methods, [and] they implement Comparable and Serializable...

So why would you want to add methods or fields to an enum type? For starters, you might want to associate data with its constants.... You can augment an enum type with any method that seems appropriate. An enum type can start life as a simple collection of enum constants and evolve over time into a full-featured abstraction.

While reading Chapter 1 of Refactoring, I felt the urge to update the example Fowler works through - not only to replace Vectors and Enumerations with Lists and Iterators, but in particular to explore the power of the Java enum.


/* ---[ Refactoring, Ch. 1, in brief ]--- */

So today I focus on the section of Ch. 1 where Fowler is extracting a switch statement into the State pattern and replacing a conditional with polymorphic classes.

You may want to peruse that chapter again to refresh yourself on the context (if you don't own the book and you are a programmer, you should obtain one post-haste), but I will review the part that is relevant to the refactoring of Fowler's code that I did.

Fowler starts with a Movie class that uses the old int constants style to doing enums to distinguish between three types of Movies that can be rented at a video rental store (and thus the example is showing its age in more ways than one).

package refactor;

public class Movie {
  public static final int REGULAR = 0;
  public static final int NEW_RELEASE = 1;
  public static final int CHILDRENS = 2;
  private String _title;
  private int _priceCode;

  public Movie(String title, int priceCode) {
    _title = title;
    _priceCode = priceCode;
  }

  public int getPriceCode() {
    return _priceCode;
  }

  public void setPriceCode(int arg) {
    _priceCode = arg;
  }

  public String getTitle (){
    return _title;
  };
}

At the starting point of the refactoring exercise, there are three classes: Customer, Rental and Movie. A Customer can have multiple Rentals and a Rental (in this case) is allowed to have only a single Movie. Only Customer has any business logic, the other classes are just value objects.

Fowler proceeds to refactor functionality out of Customer, first into Rental and then into Movie. In particular, since the Movie class has the "int enum" of REGULAR, CHILDRENS and NEW_RELEASE, he refactors Movie to provide two pieces of functionality that were originally in the Customer class:

  • getCharge: calculates the charge for renting the movie, which varies by Movie enum type
  • getFrequentRenterPoints: calculates the rental agency's bonus "frequent renter" points, which also varies by Movie enum type

So far he has basically been changing the location of a switch statement based on the int constants in Movie.

  switch (rental.getMovie().getPriceCode()) {
  case Movie.REGULAR:
    thisAmount += 2;
    if (rental.getDaysRented() > 2)
      thisAmount += (rental.getDaysRented() - 2) * 1.5;
    break;
  case Movie.NEW_RELEASE:
    thisAmount += rental.getDaysRented() * 3;
    break;
  case Movie.CHILDRENS:
    thisAmount += 1.5;
    if (rental.getDaysRented() > 3)
      thisAmount += (rental.getDaysRented() - 3) * 1.5;
    break;
  }    

After he has the switch statement in the right class, now he works to replace the conditional with polymorphism. He decides that the right way to do it is not to make subclasses of Movie (e.g., RegularMovie, ChildrensMovie, etc.), because a specific movie can change its type while the application is running (e.g., from "New Release" to "Regular"). Thus, he uses the Replace Type Code with State/Strategy refactoring pattern.

In the end, he creates an abstract Price state class that is used by Movie to implement the state-dependent methods getCharge and getFrequentRenterPoints. Here is the UML diagram:

State Pattern Using Price class

Here is the code for the abstract Price class and its concrete subclasses:

// in Price.java file
public abstract class Price {
  public abstract int getPriceCode();
  public abstract double getCharge(int daysRented);
  public int getFrequentRenterPoints(int daysRented) {
    return 1;
  }
}

// in ChildrensPrice.java file
public class ChildrensPrice extends Price {

  @Override
  public int getPriceCode() {
    return Movie.CHILDRENS;
  }

  @Override
  public double getCharge(int daysRented) {
    double result = 1.5;
    if (daysRented > 3)
      result += (daysRented - 3) * 1.5;
    return result;
  }

}

// in NewReleasePrice.java file
public class NewReleasePrice extends Price {

  @Override
  public int getPriceCode() {
    return Movie.NEW_RELEASE;
  }

  @Override
  public double getCharge(int daysRented) {
    return daysRented * 3;
  }

  @Override
  public int getFrequentRenterPoints(int daysRented) {
    if (daysRented > 1)
      return 2;
    else
      return 1;
  }
}

// in RegularPrice.java file
public class RegularPrice extends Price {

  @Override
  public int getPriceCode() {
    return Movie.REGULAR;
  }

  @Override
  public double getCharge(int daysRented) {
    double result = 2;
    if (daysRented > 2)
      result += (daysRented - 2) * 1.5;
    return result;
  }
}

Note that Price is an abstract class, rather than an interface. Because the getFrequentRenterPoints method is shared between two of the subclasses, it has been pulled up into the abstract base class. When I refactor this code further, you'll see that we can duplicate this behavior with an enum as well.

In the end, Fowler has achieved a clean design that adheres to the open/closed principle. It is open for extension by adding new subclasses to Price and it is closed for modification in that one never needs to modify the Price class in order to add a new type or state.


/* ---[ Refactoring to use Java 5 enums ]--- */

It is easy to see how to use a Java 5 enum for the original code (before Fowler's refactoring). It would just be a matter of replacing this:

public class Movie {
  public static final int REGULAR = 0;
  public static final int NEW_RELEASE = 1;
  public static final int CHILDRENS = 2;

  ...
}

with this:

public class Movie {

  public enum Price {
    REGULAR, NEW_RELEASE, CHILDRENS;
  }
  ...
} 

... and then refactoring the dependent classes to refer to Movie.Price.REGULAR, etc.

And that is how most people, from what I've seen and read, use Java enums. But as Bloch said earlier, Java enums are much more powerful than that. They are type-safe full-fleged Java immutable (final) classes where each enum entry (REGULAR, NEW_RELEASE, etc.) is a singleton for that entry. They can have constructors, implement arbitrary methods and implement interfaces. In fact, you can even put abstract methods on the "base" enum to require the concrete enum singleton entries to implement a method (as we'll see in my example below).

Here is the end product of my refactoring starting from Fowler's end product:

public enum Price {
  REGULAR {
    @Override
    public double getCharge(int daysRented) {
      double result = 2;
      if (daysRented > 2)
        result += (daysRented - 2) * 1.5;
      return result;
    }
  }, 
  CHILDRENS {
    @Override
    public double getCharge(int daysRented) {
      double result = 1.5;
      if (daysRented > 3)
        result += (daysRented - 3) * 1.5;
      return result;
    }
  }, 
  NEW_RELEASE{
    @Override
    public double getCharge(int daysRented) {
      return daysRented * 3;
    }

    @Override
    public int getFrequentRenterPoints(int daysRented) {
      if (daysRented > 1) return 2;
      else        return 1;
    }
  };

  public abstract double getCharge(int daysRented);

  /**
   * Default implementation of getFrequentRenterPoints for all 
   * types in the enum. May be overridden by specific enum types
   * if they give fewer or higher numbers of bonus points.
   * 
   * @param daysRented number of days the movie was rented
   * @return number of bonus points
   */
  public int getFrequentRenterPoints(int daysRented) {
    return 1;
  }
}

If you haven't used the advanced features of enums, this may look surprising.

First, notice that you can declare methods in the "main body" of the enum, and that they can even be abstract. In this case, I have created the getCharge and getFrequentRenterPoints methods. This means that all specific (singleton) entries of the enum have these methods. In the case of the abstract method, the compiler will require you to implementat that method in each entry body.

Which brings me to the second point - enum entries can have bodies that are specific to that entry and not shared with the other entries. Methods outside the entries are common to all entries and methods inside an entry are specific to the entry.

The zone of "inside an enum entry" is demarcated by a matching pair of curly braces after the declaration of the entry's name (e.g., REGULAR). This is just like the notation for a class body.

You create specific data fields and methods inside the enum entry body. In this case these entries don't have any state to retain (their name is the representation of the state needed), so I only have methods, not fields.

If one did need fields, how you would populate them with user/client-provided data? You would define a constructor, which would go in the section outside the entries (but inside the enum class body of course). Effective Java has a nice example of when you might need to have enum constructors.


/* ---[ Using the Price enum ]--- */

If you scroll back up and review the UML diagram, you'll see that the Movie class has both getCharge and getFrequentRenterPoints methods. In my refactoring the Movie class does the same thing. The only difference is that Price is an enum, not a regular Java class.

public class Movie {
  private String _title;
  private Price priceCode;

  public Movie(String title, Price priceCode) {
    _title = title;
    this.priceCode = priceCode;
  }

  public Price getPriceCode() {
    return priceCode;
  }

  public String getTitle() {
    return _title;
  }

  double getCharge(int daysRented) {
    return priceCode.getCharge(daysRented);
  }

  public int getFrequentRenterPoints(int daysRented) {
    return priceCode.getFrequentRenterPoints(daysRented);
  } 
}

Users of the Movie class would then need references the standalone Price enum when creating a Movie:

Movie m1 = new Movie("Grease", Price.REGULAR);
Movie m2 = new Movie("Cars", Price.CHILDRENS);


/* ---[ Analysis ]--- */

So is this better? When are enums more appropriate?

First of all, Java 5 enums are always more appropriate than using the "int enum" pattern. Their primary purpose is to replace that pattern. They bring type-safety to those constants, they bring a toString() method that prints out their name, they implement Comparable and Serializable and allow for the addition of arbitrary behavior.

Java 5 enums are appropriate when you need singleton behavior from each entry - only one copy of the REGULAR, CHILDREN and NEW_RELEASE enum objects will ever be created. In fact, they are such perfect singleton implementations, both in terms of proper initialization and thread-safety, that Josh Bloch recommends them as the best way to implement the Singleton pattern in Java now. If you want it to be a true singleton, then you only create one "entry", which you might call INSTANCE, like so:

public enum MySingleton {
  INSTANCE {
    // put instance fields and methods here
  };
}

Creating a singleton with guaranteed creation thread-safety and no known ways to create two (such as by Serialization attacks) has never been so easy.

Of course, as singletons, enums inherit some well-known pitfalls of the Singleton pattern, including the fact that they are difficult to test, as you can't dependency inject a mock or stub version of them. With a good dependency injection framework, like Spring, singletons are frequently not needed any more.

In any case, an enum with extra behavior is appropriate when you need behavior without state or where the state of each enum entry would the be same for any objects using them. In the case of my refactored version, the only state the Price enum needs is to know its type (which is what the enum is for) and do some calculations based on that state. There is only a need for one of them in the entire app, no matter how many Movie objects I need to create, so using a set of singleton immutable enums works. And the behavior of those enums does not leverage any external dependencies or resources - they just do some simple arithmetic and return an answer, so I don't need to mock or stub them for testing.

So finally, what about the open/closed principle? Well, with enums in my refactored version, one could argue that they do violate this principle. It isn't open for extension, since enums are final and cannot be subclassed. And it isn't closed for modification, as new entries of the enum cannot be created without editing the Price enum java source code file directly.

True critique. Enums do not support the open/closed principle and thus should only be used in situations where you have (and preferably own) the source code and can modify it yourself or when you actively want to prevent anyone from creating any other types. It is closed/closed intentionally.

But is my refactoring really worse than Fowler's on this criterion? Actually no, because even though his Price class follows the open/closed principle, the Movie class does not - it still expects its clients to indirectly reference the Price class via its int constants. To add a new int constant one would have to edit the Movie java file source code. So Fowler's overall refactored design doesn't follow the open/closed principle either.

If we truly wanted to follow that principle, we would have to refactor to some third design. I'll leave that as an exercise for any reader that might be interested in trying that out.


/* ---[ Antipattern: double bad ]--- */

A final side note: Fowler's example code has a smell in it that he didn't correct: using double to handle monetary values. A refactoring should also be done to use long, BigDecimal or a self-constructed Money class instead.

And that's the joy of refactoring - with new language features evolving and the list of code smells you are aware of growing, you will frequently be able to go back to old code and find a way to improve it.

Sunday, March 11, 2012

MyBatis: A User Guide Companion


/*---[ Rationale ]---*/

One of the frustrations I have about reading guides on how to learn a new software technology is that they tend to focus on showing a snippet of code that they want to talk about rather than all the code in context. I have seen others comment that this seems to be a trend in many programming books and online guides these days and was not the case in days when Kernighan and Ritchie wrote the seminal The C Programming Language.

The MyBatis 3 User Guide is a case in point. It is easy for someone new to it to quickly get confused because they don't understand the context of the snippets. Simple complete working examples that you can try on the command line or in Eclipse would solve this problem. The authors also sometimes refer to getting the example source code, but finding that is not trivial. I ended up pulling their source code from their SVN repo and that has a couple of different test apps mixed together (the blog and jpetstore are two).

They do provide the JPetStore full code "sample", but it is large and intertwined with Stripes and Spring, so not a place for a newbie to start.

So, since I really like MyBatis and want to promote people learning this, here is my contribution to present the simplest possible MyBatis setup that will allow you to read the MyBatis3 documentation and have working code to allow you to try out its examples.

There are a number of good tutorials for MyBatis (exhibit A and exhibit B) on the web, but they either typically jump right into a full fledged example that can be overwhelming or are a full stack example including servlets and app servers, etc. when you just want to understand the basics and try it out isolation. That's the gap I'm trying to fill in this tutorial companion.


/*---[ "User Guide Companion" Overview ]---*/

This is a companion to, not a substitute for, the MyBatis 3 User Guide, so make sure you download that and the mybatis code bundle from the MyBatis website.

This tutorial companion comes in two parts. Since I'm a big believer in "provide an example of the simplest thing that works" - that's what part 1 is: a bare bones, but fully working, setup of a MyBatis-based system. Part two is the set up you'll need for the Blog example that the MyBatis 3 User Guide largely uses. By having this foundation, you can tweak and test a working system as you read through the User Guide.

Here I assume you know how to put jar files on your CLASSPATH either from the command line or in an IDE like Eclipse.

Download the latest MyBatis bundle from the MyBatis website here. Put mybatis-3.x.x.jar in your CLASSPATH (Java Build Path > Libraries in Eclipse). You will need to also download the JDBC jar for the database you are using.

I provide .sql files for creating tables and initial data sets for both PostgreSQL and MySQL. You can get all the code I reference here from my GitHub repo.



Tutorial Companion Part One: MyBatis101 - The Simplest Thing That Could Work

I call this first application "MyBatis101" and I've created a directory with that name. In that directory, I have created 7 files, including an Ant build.xml file, so in the end it looks like this:

MyBatis101$ tree
.
|-- build.xml
|-- MyBatis101.sql
|-- src
    |--MyBatis101-config.xml
    |-- mybatis101
        |-- Main.java
        |-- Mapper.java
        |-- MyBatis101-mapper.xml 
        |-- User.java


/*---[ First: Set up a Database ]---*/

First let's set up a very simple database with one table, having two columns and two rows of data.

As I said above, I will show this in both PostgreSQL and MySQL. I don't describe how to install and set up those databases. If you need to start there, here are links to good documentation:

In my setup I am using PostgreSQL 9.1 and MySQL 5.1.

I also show these using a Linux command line, but it should work the same on Mac and Windows. I list all the files one by one below, but to avoid copy and paste, be sure to pull them from my from my GitHub repo:

git clone git@github.com:midpeter444/MyBatis-UserGuide-Companion-Code.git


/*---[ PostgreSQL ]---*/

Create the MyBatis101 database and check that it is there:

$ createdb MyBatis101
$ psql --list
                                   List of databases
    Name    |    Owner    | Encoding | Collation  |   Ctype     
------------+-------------+----------+------------+-------------
 depot      | midpeter444 | UTF8     | en_US.utf8 | en_US.utf8 
 MyBatis101 | midpeter444 | UTF8     | en_US.utf8 | en_US.utf8 
 postgres   | postgres    | UTF8     | en_US.utf8 | en_US.utf8 
 template0  | postgres    | UTF8     | en_US.utf8 | en_US.utf8 

 template1  | postgres    | UTF8     | en_US.utf8 | en_US.utf8 

(5 rows)


Create the file MyBatis101.sql:

drop table if exists users;

create table users (
  id integer,
  name varchar(20)
);

insert into users (id, name) values(1, 'User1');
insert into users (id, name) values(2, 'User2');


Create the tables and load test data into the MyBatis101 database and check that it is there:

$ psql MyBatis101 < MyBatis101.sql
$ psql MyBatis101
psql (9.1.3)
Type "help" for help.

MyBatis101=> \d
          List of relations
 Schema | Name  | Type  |    Owner    
--------+-------+-------+-------------
 public | users | table | midpeter444
(1 row)

MyBatis101=> select * from users;
 id | name  
----+-------
  1 | User1
  2 | User2
(2 rows)


/*---[ MySQL ]---*/

Create the MyBatis101 database and check that it is there:

$ mysql -p
mysql> create database MyBatis101;
Query OK, 1 row affected (0.03 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| MyBatis101         |
| mysql              |
+--------------------+
3 rows in set (0.03 sec)


Create the file MyBatis101.sql: (Same file as above)


Create the tables and load test data into the MyBatis101 database and check that it is there:

$ mysql -p MyBatis101 < MyBatis101.sql
$ mysql -p MyBatis101

mysql> show tables;
+----------------------+
| Tables_in_MyBatis101 |
+----------------------+
| users                |
+----------------------+
1 row in set (0.00 sec)

mysql> desc users;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

Now that the databases is set up with a table and a little sample table, now we can try out MyBatis.


/*---[ The MyBatis Set Up Files ]---*/

Create the MyBatis101-config.xml in the src directory:

Note: Use the correct driver and url according to which database you are using.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="UNPOOLED">
        <property name="driver" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql:MyBatis101" />
        <!--  <property name="driver" value="com.mysql.jdbc.Driver" />   -->
        <!--  <property name="url" value="jdbc:mysql://localhost:3306/MyBatis101" /> -->        
        <property name="username" value="" />
        <property name="password" value="" />
      </dataSource>
    </environment>
  </environments>

  <mappers>
    <mapper resource="MyBatis101-mapper.xml" />
  </mappers>

</configuration>


Create MyBatis101-mapper.xml in the src/mybatis101 directory:

<?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="mybatis101.Mapper">

    <select id="getUser" parameterType="int" resultType="mybatis101.User">
        select * from users where id = #{id}
    </select>

</mapper>


Create src/mybatis101/Mapper.java:

package mybatis101;

public interface Mapper {
  User getUser(Integer id);
}


Create src/mybatis101/User.java:

package mybatis101;

public class User {

  private Integer id;
  private String name;

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}


Create src/mybatis101/Main.java:

package mybatis101;

import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {

  private static SqlSessionFactory sessionFactory;

  public static void initSessionFactory() throws Exception {
    Reader rdr = Resources.getResourceAsReader("MyBatis101-config.xml");
    sessionFactory = new SqlSessionFactoryBuilder().build(rdr);
    rdr.close();
  }

  // uses the old iBATIS style of lookup
  public static void lookUpUserOldWay() throws Exception {
    SqlSession session = sessionFactory.openSession();
    try {
      User user = (User) session.selectOne(
          "mybatis101.Mapper.getUser", Integer.valueOf(1));
      System.out.println(user.getName());  // should print out "User1"

    } finally {
      session.close();
    }
  }

  // uses the new MyBatis style of lookup
  public static void lookUpUser() throws Exception {
    SqlSession session = sessionFactory.openSession();

    try {
      Mapper mapper = session.getMapper(Mapper.class);
      User user = mapper.getUser(2);
      System.out.println(user.getName());  // should print out "User2"

    } finally {
      session.close();        
    }
  }

  public static void main(String[] args) throws Exception {
    initSessionFactory();
    lookUpUserOldWay();
    lookUpUser();
  }
}


Create build.xml ant script (not required if you are doing this in Eclipse) and run it:

<project name="MyBatis101" default="run" basedir=".">
  <!-- Template based on: http://sourceforge.net/apps/mediawiki/import-ant/index.php?title=Snippets -->
  <description>Build script for simple MyBatis101 app</description>

  <!-- load environment variables as properties -->
  <property environment="env"/>

  <!-- default folder location properties -->
  <property name="src.dir" value="src"/>
  <property name="build.dir" value="bin"/>
  <!-- TODO: EDIT THESE -->
  <property name="lib.dir" value="/home/midpeter444/java/lib"/> 
  <property name="jdbc.jar" value="postgresql.jar"/>
  <!-- <property name="jdbc.jar" value="mysql-connector-java.jar"/> -->

  <!-- project classpath -->

  <path id="project.classpath">
    <!-- compiled classes -->
    <pathelement location="${build.dir}" />
    <!-- libraries -->
    <fileset dir="${lib.dir}">
      <include name="${jdbc.jar}" />  
      <include name="mybatis.jar" />    <!-- TODO: EDIT THIS -->
    </fileset>
  </path>

  <!-- basic -->

  <target name="init">
    <mkdir dir="${build.dir}"/>
  </target>

  <!-- compile -->

  <target name="prepare-resources" depends="init">
    <copy todir="${build.dir}" overwrite="true">
      <fileset dir="${src.dir}" includes="**/*.xml" />
    </copy>
  </target>

  <target name="compile" depends="init,prepare-resources"
          description="Compile java classes">
    <javac
        srcdir="${src.dir}"
        destdir="${build.dir}"
        includeantruntime="false"> <!-- to overcome misfeature in An t1.8 -->
      <classpath refid="project.classpath" />
    </javac>
  </target>


  <!-- run on console -->

  <property name="run.main-class" value="mybatis101.Main"/>
  <property name="run.args" value=""/>

  <target name="run" depends="compile"
          description="Run MyBatis101 program">
    <java classname="${run.main-class}" fork="true">
      <arg line="${run.args}" />
      <classpath>
        <path refid="project.classpath" />
      </classpath>
    </java>
  </target>
</project>
$ ant run
Buildfile: /home/midpeter444/databases/postgresql/mybatis-learn/MyBatis101/build.xml

init:

prepare-resources:
     [copy] Copying 2 files to /home/midpeter444/databases/postgresql/mybatis-learn/MyBatis101/bin

compile:

run:
     [java] User1
     [java] User2

BUILD SUCCESSFUL
Total time: 2 seconds

I'm not going to explain much about this code. Read it along with the User Guide or the MyBatis Getting Started tutorial and it should start to make sense pretty quickly.




Tutorial Companion Part Two: Blog App in the MyBatis3 User Guide

The MyBatis101 code was intended just to get your feet wet with MyBatis. In this section, we briefly peruse the companion code as part of the "blog" application that is mostly referenced in the MyBatis3 User Guide. Note: the references to the blog database structure and codebase are not consistent in the MyBatis3 User Guide, so I've done the best I can at finding something close to most examples.

This code is intended as starter code - it is a fully working example, with a Main.java that exercises a couple of MyBatis query mappings and one insert mapping. I have also provided one JUnit 4 test that only tests one of the query mappings.

This code along with the User Guide could be used as a sort of poor man's koan - a series of exercises to be filled out with more functionality.

The routine will be the same as above, we just have more of it. Again, pull all the files from my GitHub account.

I have created a directory called "blog" and the code base structure I provide looks like this:

blog$ tree
.
|-- blogdb-ddl-mysql.sql
|-- blogdb-ddl-postgres.sql
|-- blogdb-dml.sql
|-- build.xml
|-- src
    |-- main
        |-- java
            |-- org
                |-- mybatis
                    |-- example
                        |-- Author.java
                        |-- AuthorMapper.xml
                        |-- Blog.java
                        |-- BlogMapper.java
                        |-- BlogMapper.xml
                        |-- config.properties
                        |-- Configuration.xml
                        |-- Main.java
    |-- test
        |-- java
            |-- org
                |-- mybatis
                    |-- example
                        |-- BlogMapperTests.java

We start by creating the database and tables for the Blog and Author classes they discuss in the MyBatis3 User Guide.


/*---[ PostgreSQL ]---*/

Create the database:

$ createdb blogdb


Create the DDL for the blog and author tables and save it in a file called "blogdb-ddl-postgres.sql":

DROP TABLE    IF EXISTS blog;
DROP TABLE    IF EXISTS author; 
DROP SEQUENCE IF EXISTS blogdb_blog_seq;
DROP SEQUENCE IF EXISTS blogdb_author_seq;
CREATE SEQUENCE blogdb_blog_seq;
CREATE SEQUENCE blogdb_author_seq;

CREATE TABLE author (
  id               integer PRIMARY KEY DEFAULT nextval('blogdb_author_seq') NOT NULL,
  username         varchar(255) NOT NULL CHECK (username <> ''),
  hashed_password  varchar(255) NOT NULL CHECK (hashed_password <> ''),
  email            varchar(100) NOT NULL CHECK (email <> ''),
  bio              text
);

CREATE TABLE blog (
  id          integer PRIMARY KEY DEFAULT nextval('blogdb_blog_seq') NOT NULL,
  title       varchar(255) NOT NULL CHECK (title <> ''),
  author_id   integer NOT NULL references author(id)
);


Create some fake data to load into the tables and saved it in a file called "blogdb-dml.sql":

INSERT into author (username, hashed_password, email, bio)
VALUES('aaron1', 'aaron1', 'aaron@pobox.com', 'Aaron is "The Dude".');

INSERT into author (username, hashed_password, email)
VALUES('barb2', 'barb2', 'barb@pobox.com');

INSERT into author (username, hashed_password, email, bio)
VALUES('carol3', 'carol3', 'carol@pobox.com', 'Carol is an avid atom-smasher and street luger.');

INSERT into blog (title, author_id)
VALUES('Why I am "The Dude"', (select id from author where username='aaron1'));

INSERT into blog (title, author_id)
VALUES('A Day in the Life of "The Dude"', (select id from author where username='aaron1'));

INSERT into blog (title, author_id)
VALUES('Sanity is my strong suit', (select id from author where username='barb2'));

INSERT into blog (title, author_id)
VALUES('I are smart?', (select id from author where username='carol3'));

INSERT into blog (title, author_id)
VALUES('The Large-Hadron Collider will not create a black hole that ends the universe', (select id from author where username='carol3'));


Run the DDL and DML scripts against the database:

$ psql blogdb < blogdb-ddl-postgres.sql
$ psql blogdb < blogdb-dml.sql


/*---[ MySQL ]---*/

Create the database:

$ mysql -p
mysql> create database blogdb;
Query OK, 1 row affected (0.03 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| MyBatis101         |
| blogdb             |
| mysql              |
+--------------------+
4 rows in set (0.03 sec)


Create the DDL for the blog and author tables and save it in a file called "blogdb-ddl-mysql.sql":

DROP TABLE    IF EXISTS blog;
DROP TABLE    IF EXISTS author; 

CREATE TABLE author (
  id               integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
  username         varchar(255) NOT NULL CHECK (username <> ''),
  hashed_password  varchar(255) NOT NULL CHECK (hashed_password <> ''),
  email            varchar(100) NOT NULL CHECK (email <> ''),
  bio              text
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;

CREATE TABLE blog (
  id          integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
  title       varchar(255) NOT NULL CHECK (title <> ''),
  author_id   integer NOT NULL,
  FOREIGN KEY (author_id) REFERENCES author(id)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;


Create some fake data to load into the tables and save it in a file called "blogdb-dml.sql": (Same file as in the PostgreSQL section.)


Run the DDL and DML scripts against the database:

$ mysql -p blogdb < blogdb-ddl-mysql.sql
$ mysql -p blogdb < blogdb-dml.sql


/*---[ Work Through The User Guide ]---*/

Next, start by looking at Main.java in src/main/java/org/mybatis/example. Follow each of its steps to see how the MyBatis system works. This example includes a complex mapping - what MyBatis calls a resultMap. On p. 29 of the User Guide they go into some detail about it, as it is one of the most important features of MyBatis.

From this code base you should be able to work through the entire User Guide, trying out new features as you go. Make sure to write tests for everything you do. I've intentionally made the JUnit test very bare bones so that is the place you can learn by doing.

After doing this, I recommend trying out one of the MyBatis tutorials on the web, such as either of those that I already mentioned above:

Good luck and feedback is welcome.


[Update 28-May-2012]: I've recently published a set of koans to learn MyBatis, so consider trying those out as well. My May 2012 blog entry describes these and how to get started.

Friday, March 2, 2012

Technical Podcasts I Listen To

The internet is amazing. If information yearns to be free (and plentiful), then information is in its heyday. I've seen some predictions that formal institutions of learning, such as universities, will need to adapt or die. A motivated person could literally get the equivalent of a computer science degree from buying some key books and using information freely available on the web, including webinars, videocasts, podcasts, tutorial sites and stackoverflow. I spend a lot of time at InfoQ, for instance, watching the excellent lineup of software engineering video lectures they make available. There is Google Code University to sink your time into. And MIT and Stanford are leading the way with free online courses of high quality (so I hear - I haven't taken one yet ...).

In any case, besides this paean to the internet, what I mean to list here are the current computer science/software engineering podcasts I currently listen to or have heard about and intend to get to. Like the video casts on InfoQ and (to a lesser degree) Google Tech Talks on youtube, there are more than I can keep up with.

Here's my list with my subjective rating from 1 to 10 (best). My rating is based on how useful it is to me as a software engineer first and second as a software/electronic device user. And I'm biased towards Linux, the JVM, JavaScript and Ruby. My interest in all things Windows and .NET is pretty low.

ShowSubjective Rating: 1-10 (best)
Software Engineering Radio10
Ruby Rogues9
Think Relevance Podcast9
Mostly lazy (Clojure)8
Teach Me to Code8
JavaScript Jabber8
Basement Coders (Java heavy)7
Adam Glover Podcasts - IBM developerWorks7
Security NowBumped to 7.
Been really getting into these lately and some excellent stuff on how the internet works
FLOSS Weekly6
Pragmatic Programmer's Podcast6
A Minute with Brendan (Eich)6
Herding Code (Windows centric)5
(their episode on git was excellent)
Java Spotlight4
The JavaScript Show4
Java Posse4
All About Android3
Linux Outlaws2
Computer Science Podcast??? - haven't listened yet
Curly Brace Cast??? - haven't listened yet
Hacker Media??? - haven't listened yet
Deductive Developers??? - haven't listened yet
Linux Trivia Podcast??? - haven't listened yet
Paul dot com (security related)??? - haven't listened yet
YayQuery??? - haven't listened yet
ArsTechnica PodcastBrand new: ??? - haven't listened yet

I'll update this as I get time to listen to more or find others that I like. I welcome any additions you know about.