What Is MyBatis? A Guide to How It Differs from JPA and How to Set It Up with Spring Boot
Quite a few people probably know the name “MyBatis” but aren’t really sure what it actually is. Searching for it mostly turns up articles about how to implement Mappers or write dynamic SQL, and there’s surprisingly little content explaining its basic position in the ecosystem.
This article walks through what MyBatis actually is, how it differs from JPA/Hibernate, and how to set up a minimal configuration with Spring Boot. By the end, you should be able to judge whether MyBatis is a good fit for your own project.
What Is MyBatis?
MyBatis is a type of O/R mapper where you write the SQL yourself. It’s easiest to think of it as a library that takes over the work of executing SQL statements and mapping the results to Java objects.
It originated as a project called iBatis under the Apache Software Foundation, and was later renamed to MyBatis when it moved to its current development structure. You don’t need to know the details of the renaming to use it, but if you ever come across the word “iBatis” somewhere, you can assume it’s from the same lineage.
The key point is that developers write the actual SQL for table operations themselves. This is the biggest difference from JPA.
The Philosophical Difference from JPA/Hibernate
With JPA (including implementations like Hibernate), once you define the mapping between entity classes and tables, the framework generally generates the SQL automatically.
// With Spring Data JPA, findById works without writing any SQL
public interface UserRepository extends JpaRepository<User, Long> {
}
This findById isn’t part of the JPA specification itself — it’s a query derivation feature provided by Spring Data JPA, and its return value is Optional<User>. Even when writing JPQL or using EntityManager directly, the fact remains that you’re retrieving data using an object-oriented query language rather than SQL.
MyBatis, on the other hand, has you write the SQL statements themselves explicitly, either in XML or via annotations. The framework’s job is limited to mapping the execution results to Java objects.
// With MyBatis, you write the SQL yourself
@Mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(Long id);
}
The return type also differs from JPA — this returns User directly (not wrapped in Optional). This difference isn’t just a matter of syntax; it reflects a difference in philosophy. JPA aims for “a world where you don’t need to think about SQL,” while MyBatis aims for “a world for people who want full control over SQL.” Neither is inherently superior — it’s a choice about what you want automated and what you want to handle manually.
When MyBatis Is a Good Fit — and When It Isn’t
The practical decision criteria are actually fairly simple.
If you frequently write complex aggregate SQL that spans multiple tables, or you’re working with a legacy database schema that’s insufficiently normalized or uses non-standard column naming that doesn’t map cleanly to entities, MyBatis tends to let you write things more naturally. Since you can write SQL directly, it’s also easier to tune with execution plans in mind.
Conversely, if your application centers on simple CRUD operations where the table structure and entities map roughly 1:1 — like membership management or a to-do app — and you might switch database engines in the future, or you simply want to reduce the effort of writing SQL, JPA is the better fit.
Note that you don’t have to commit to one or the other for an entire project — in practice, it’s common to see a mix, such as using JPA for reads and MyBatis only for complex aggregations. If you want to dig deeper into the decision criteria, see our MyBatis vs. JPA comparison article.
Setting Up a Minimal Configuration with mybatis-spring-boot-starter
Theory alone can be hard to grasp, so let’s actually get something running. Spring Boot has an official starter available, letting you get started with minimal configuration. The versions below reflect the time of writing, so be sure to check the latest versions when you set this up.
First, add the dependency. The 3.x series of mybatis-spring-boot-starter supports Spring Boot 3.x.
dependencies {
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4'
runtimeOnly 'com.mysql:mysql-connector-j'
}
Database connection settings go in application.properties, just like any standard Spring Boot app — no MyBatis-specific connection configuration is needed.
Note that the plaintext password below is for local testing only — in production, inject it from environment variables or a secrets management service such as Secrets Manager.
spring.datasource.url=jdbc:mysql://localhost:3306/sampledb
spring.datasource.username=root
spring.datasource.password=secret
From there, all you need is a single Mapper interface, and you’re ready to go.
@Mapper
public interface UserMapper {
@Select("SELECT id, name FROM users WHERE id = #{id}")
User findById(Long id);
}
You can omit @Param when there’s only a single argument, but for methods that take two or more parameters, @Param is required to explicitly name the parameters referenced by #{}. We’ll cover the detailed syntax in the Mapper implementation guide mentioned below.
This Mapper can be injected into a service class just like any other Bean.
@Service
public class UserService {
private final UserMapper userMapper;
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
public User getUser(Long id) {
return userMapper.findById(id);
}
}
When using mybatis-spring-boot-starter, interfaces annotated with @Mapper are automatically detected by Spring Boot’s auto-configuration, so you generally don’t need to add @MapperScan. Just think of @MapperScan as something you only need when you want to explicitly specify the scan scope — for example, when spreading Mappers across multiple packages.
What to Read Next
By this point, you should have a good grasp of where MyBatis stands and how it differs from JPA. Depending on your goals, here are some good next steps:
- To dig deeper into writing Mappers and dynamic SQL, see the MyBatis implementation guide
- To understand the practical criteria for choosing between MyBatis and JPA, see the MyBatis vs. JPA comparison article
- For pagination when working with large datasets, see the MyBatis pagination article
Summary
MyBatis is an O/R mapper for people who want to write their own SQL, while JPA is an O/R mapper for people who want SQL automated. Once you understand this difference in philosophy, the choice mostly comes down to “do you want to write complex SQL or not.”
Start by adding mybatis-spring-boot-starter and getting a single Mapper up and running. Once you actually try it out, you’ll quickly get a feel for what it’s like to have direct control over your SQL.