MyBatis Framework: Core Concepts and Getting Started
Core ORM Concepts
Object-Relational Mapping (ORM) is a porgramming technique that converts data between incompatible type systems—specifically between object-oriented programming languages and relational databases. Instead of working with raw SQL queries, developers interact with objects that represent database tables.
Java Persistence API (JPA) is the JavaEE specification defining standard interfaces for ORM implementations. Hibernate serves as a full-featured implementation that abstracts SQL entirely from developers. MyBatis, by contrast, operates as a semi-ORM framework, allowing direct SQL manipulation while providing object mapping capabilities.
Introduction to MyBatis
MyBatis is an open-source data persistence framework that simplifies database operations in Java applications. It fundamentally serves as a JDBC wrapper, eliminating boilerplate code associated with connection management, statement creation, and result mapping.
Originally developed as iBATIS in 2002 under Apache, the project moved to Google Code in 2010 and was rebranded as MyBatis. The version covered here is 3.3.1, though current releases have advanced to 3.4.x.
Key Advantages
- Reduced Boilerplate: Eliminates repetitive JDBC code for managing connections, statements, and result sets
- Gentle Learning Curve: SQL-based approach with intuitive APIs makes it accessible to developers
- Database Portability: Built-in abstractions handle differences between database vendors
- Ecosystem Support: Extensive third-party integrations including pagination plugins, reverse engineering tools, and caching solutions
- Spring Integration: First-class support for seamless integrtaion with Spring applications
Project Setup
Required Dependencies
Core MyBatis Library:
- mybatis-3.3.1.jar
Bytecode Manipulation:
- asm-3.3.jar
- javassist-3.15.0.GA.jar
- cglib-2.2.jar
Logging:
- commons-logging-1.1.1.jar
- log4j-1.2.17.jar
Testing:
- junit-4.11.jar
- hamcrest-core-1.3.jar
Database Driver:
- mysql-connector-java-5.0.8-bin.jar
Building Your First Application
Entity Model
package com.example.demo.domain;
import java.io.Serializable;
import java.sql.Timestamp;
public class Account implements Serializable {
private static final long serialVersionUID = 123456789L;
private Long accountId;
private String accountName;
private String username;
private String pwd;
private Long departmentId;
private Timestamp birthDate;
private Timestamp lastModified;
// Getters and setters
public Long getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public Long getDepartmentId() {
return departmentId;
}
public void setDepartmentId(Long departmentId) {
this.departmentId = departmentId;
}
public Timestamp getBirthDate() {
return birthDate;
}
public void setBirthDate(Timestamp birthDate) {
this.birthDate = birthDate;
}
public Timestamp getLastModified() {
return lastModified;
}
public void setLastModified(Timestamp lastModified) {
this.lastModified = lastModified;
}
@Override
public String toString() {
return "Account [accountId=" + accountId + ", accountName=" + accountName
+ ", username=" + username + ", departmentId=" + departmentId + "]";
}
}
Data Access Interface
package com.example.demo.repository;
import java.util.List;
import com.example.demo.domain.Account;
public interface IAccountRepository {
Account findById(Long id);
List<Account> findAll();
boolean insert(Account account);
boolean delete(Long id);
boolean modify(Account account);
}
Repository Implementation
package com.example.demo.repository.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.example.demo.domain.Account;
import com.example.demo.repository.IAccountRepository;
public class AccountRepository implements IAccountRepository {
private final SqlSessionFactory sessionFactory;
public AccountRepository(SqlSessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public Account findById(Long id) {
SqlSession session = sessionFactory.openSession();
try {
return session.selectOne("account.findById", id);
} finally {
session.close();
}
}
@Override
public List<Account> findAll() {
SqlSession session = sessionFactory.openSession();
try {
return session.selectList("account.findAll");
} finally {
session.close();
}
}
@Override
public boolean insert(Account account) {
SqlSession session = sessionFactory.openSession();
try {
int affected = session.insert("account.insert", account);
session.commit();
return affected > 0;
} catch (Exception e) {
session.rollback();
return false;
} finally {
session.close();
}
}
@Override
public boolean delete(Long id) {
SqlSession session = sessionFactory.openSession();
try {
int affected = session.delete("account.delete", id);
session.commit();
return affected > 0;
} catch (Exception e) {
session.rollback();
return false;
} finally {
session.close();
}
}
@Override
public boolean modify(Account account) {
SqlSession session = sessionFactory.openSession();
try {
int affected = session.update("account.modify", account);
session.commit();
return affected > 0;
} catch (Exception e) {
session.rollback();
return false;
} finally {
session.close();
}
}
}
Configuration Files
Framework Configuration (mybatis-config.xml):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="account-mapper.xml"/>
</mappers>
</configuration>
SQL Mapping (account-mapper.xml):
<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="account">
<select id="findById" parameterType="java.lang.Long"
resultType="com.example.demo.domain.Account">
SELECT ACCOUNT_ID AS accountId,
ACCOUNT_NAME AS accountName,
USERNAME AS username,
BIRTH_DATE AS birthDate,
LAST_MODIFIED AS lastModified
FROM T_ACCOUNT
WHERE ACCOUNT_ID = #{id}
</select>
<select id="findAll" resultType="com.example.demo.domain.Account">
SELECT ACCOUNT_ID AS accountId,
ACCOUNT_NAME AS accountName,
USERNAME AS username,
BIRTH_DATE AS birthDate,
LAST_MODIFIED AS lastModified
FROM T_ACCOUNT
</select>
<insert id="insert" parameterType="com.example.demo.domain.Account">
INSERT INTO T_ACCOUNT(ACCOUNT_NAME, USERNAME, BIRTH_DATE, LAST_MODIFIED)
VALUES(#{accountName}, #{username}, #{birthDate}, #{lastModified})
</insert>
<delete id="delete" parameterType="java.lang.Long">
DELETE FROM T_ACCOUNT WHERE ACCOUNT_ID = #{id}
</delete>
<update id="modify" parameterType="com.example.demo.domain.Account">
UPDATE T_ACCOUNT
SET ACCOUNT_NAME = #{accountName},
USERNAME = #{username},
BIRTH_DATE = #{birthDate}
WHERE ACCOUNT_ID = #{accountId}
</update>
</mapper>
Test Suite
package com.example.demo.test;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.sql.Timestamp;
import com.example.demo.domain.Account;
import com.example.demo.repository.IAccountRepository;
import com.example.demo.repository.impl.AccountRepository;
public class AccountRepositoryTest {
private static final Logger logger = LoggerFactory.getLogger(AccountRepositoryTest.class);
private SqlSessionFactory sqlSessionFactory;
@Before
public void setup() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindById() {
IAccountRepository repository = new AccountRepository(sqlSessionFactory);
Account account = repository.findById(1L);
logger.info("Found account: {}", account);
}
@Test
public void testFindAll() {
IAccountRepository repository = new AccountRepository(sqlSessionFactory);
logger.info("Total accounts: {}", repository.findAll().size());
}
@Test
public void testInsert() {
IAccountRepository repository = new AccountRepository(sqlSessionFactory);
Account newAccount = new Account();
newAccount.setAccountName("New User");
newAccount.setUsername("newuser");
newAccount.setBirthDate(new Timestamp(System.currentTimeMillis()));
boolean result = repository.insert(newAccount);
logger.info("Insert operation: {}", result ? "success" : "failed");
}
@Test
public void testModify() {
IAccountRepository repository = new AccountRepository(sqlSessionFactory);
Account account = new Account();
account.setAccountId(2L);
account.setAccountName("Updated Name");
account.setUsername("updateduser");
boolean result = repository.modify(account);
logger.info("Update operation: {}", result ? "success" : "failed");
}
@Test
public void testDelete() {
IAccountRepository repository = new AccountRepository(sqlSessionFactory);
boolean result = repository.delete(2L);
logger.info("Delete operation: {}", result ? "success" : "failed");
}
}
Execution Workflow
The typical MyBatis workflow follows these steps: load the configuration file, build the SqlSessionFactory, create SqlSession instances, execute mapped statements, and properly release resources. Transaction management requires explicit commits for insert, update, and delete operations to persist changes to the database.