Getting started with mybatis

Using straight up jdbc data access is tedious, error prone and difficult to map database structures to object oriented types. In this tutorial we will explore how to get started with a persistence framework named mybatis.

Detailed Video Notes

MyBatis, formally known as ibatis, is a data mapper framework that has made it easier to work with relational databases and java object. It was built around giving developers the control to manage sql statements and is typically the choice for organizations that have segregated database and data modeler teams with stringent code reviews. In this tutorial we take an introductory look at mybatis.

Overview

[0:22]

Before diving into code it is important to understand high level components of mybatis. SqlSessionFactory is an entity that knows about environment, data sources, transaction management and mappers. Mappers, just as they sound, are in charge of mapping sql to and from java objects. They hide the JDBC data access code you might of traditionally written. As mybatis evolved it has given developers configuration options using xml, java and annotations.

Project set up

[0:48]

We will generate a project through the command line using maven's quick start archetype and import the project into eclipse. Next adding mybatis, junit and derby as pom dependencies will bring in the necessary files into our class path. If you aren't familiar with derby, derby is an embedded database written in java that mybatis will be configured to connect to.

mvn archetype:generate -DgroupId=com.levelup -DartifactId=018-introduction-to-mybatis-ibatis -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.2.8</version>
    </dependency>

    <dependency>
        <groupId>org.apache.derby</groupId>
        <artifactId>derby</artifactId>
        <version>10.11.1.1</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Creating POJO

[1:8]

Let's create an Agency POJO object that has two fields, ID is an Integer and name is a String which will be returned from a mybatis mapper.

public class Agency {

    private Integer id;
    private String name;

    //..
}

Creating configuration files

[1:18]

Next creating a mybatis-config.xml file that will contain properties such as a database provider, mybatis mappers and many more which will be used to configure mybatis.

<?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="POOLED">
                <property name="driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
                <property name="url"
                    value="jdbc:derby:myDB;create=true;user=me;password=me" />
                <property name="username" value="me" />
                <property name="password" value="me" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="agency-mapper.xml" />
    </mappers>
</configuration>

You can see there is a referenced agency-mapper.xml file that contains information on how to execute sql queries and how to map an agency object from specified sql statements. Examining the file contents the mapper xml element namespaces is now required and allows the ability to isolate statements and bind to interfaces.

Breaking down the select statement, the select tag allows us to execute a sql select statement to retrieve data from a database. The id is a unique identifier within this namespace and will be reference from java code. The resultType is a string that represents a class name or alias for the type of object mybatis will return.

This is where the magic happens. Mybatis will use the column names generated from the ResultSet and map them to the java object based on the java bean specification. In other words, mybatis knows what class it needs to create and for each column it will dynamically call the setter method on the object, in our example setId and setName. In the instance your column name doesn't match object attribute you can alias the column. There are two additional update statements that will be used to seed the local data store.

<?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.levelup.Agency">

    <select id="selectAgency" resultType="com.levelup.domain.Agency">
        select ID, NAME from AGENCY where
        id = #{id}
    </select>

    <update id="createTable">
        CREATE TABLE AGENCY (ID INT PRIMARY KEY, NAME VARCHAR(50))
    </update>
    
    <update id="seedAgency">
        INSERT INTO AGENCY VALUES (10,'American family insurance')
    </update>

</mapper>

Building SqlSessionFactory

[2:34]

Now that we have the configuration xml files defined we can writing some java code. Let's break down the unit test class. The set up method, which will run before any of the test cases by using the @Before, will read in the mybatis-config.xml file and build a SqlSessionFactory class. Remember the SqlSessionFactory class is the brain and knows what statements are available and what database to execute against. Following the build and calling seedData() method will populate the database with some dummy data.

private SqlSession session;

@Before
public void setup() throws IOException, SQLException {

    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
            .build(inputStream);
    
    session = sqlSessionFactory.openSession();

    seedData();
    
}

private void seedData() {

    int createTable = session.update("com.levelup.Agency.createTable");

    int seedAgency = session.update("com.levelup.Agency.seedAgency");
    
    System.out.println(createTable);
    System.out.println(seedAgency);
}

Executing sql statement

[3:36]

Next calling a SqlSession.selectOne will pass the namespace plus the id while the selectOne() method will return a single Agency object. In this case "com.levelup.Agency" is the namespace and "selectAgency" is the select statement which will be executed. Under the covers mybatis has internal code that breaks apart the mapping statement but at high level think of it as it translates into a PreparedStatement setting a parameter of 10. Once this code executes we should see the Agency object that we wrote a query for.

@Test
public void test() {

    Agency agency = session
            .selectOne("com.levelup.Agency.selectAgency", 10);

    System.out.println(agency);
}
Agency [id=10, name=American family insurance]

Finally, calling the tearDown() will close the session.

@After
public void tearDown() {
    session.close();
}

There is a lot more to mybatis but this should have given you an introductory look at the high level components. As you decided on a ORM or move forward with integrating into your environment be sure to look into connection pooling and how mybatis integrates with frameworks such as spring. Thanks for joining in today's level up, have a great day!