MyBatis
MyBatis is a SQL-centric lightweight persistence framework that provides complete control over SQL statements. It supports both XML mapping files and annotation-based approaches, focusing on complex queries and high performance, with particular strength in enterprise legacy system integration for Java applications.
GitHub Overview
mybatis/mybatis-3
MyBatis SQL mapper framework for Java
Topics
Star History
Library
MyBatis
Overview
MyBatis is a SQL-centric lightweight persistence framework that provides complete control over SQL statements. It supports both XML mapping files and annotation-based approaches, focusing on complex queries and high performance, with particular strength in enterprise legacy system integration for Java applications.
Details
MyBatis adopts a different approach from traditional ORMs by being SQL-centric. It allows developers to write SQL directly, providing complete control over database interactions and making it easy to integrate with complex queries and stored procedures. Dynamic SQL generation capabilities enable flexible query construction based on conditions.
Key Features
- SQL-Centric Design: Complete control through raw SQL writing
- XML and Annotations: Support for both mapping approaches
- Dynamic SQL: Flexible query generation based on conditions
- Spring Boot Integration: Seamless integration and transaction management
- High Performance: Optimized query execution and caching capabilities
Pros and Cons
Pros
- Complete SQL control enables efficient implementation of complex queries
- Easy integration with existing databases and stored procedures
- Low learning curve, suitable for developers familiar with SQL
- Lightweight with fast query execution and caching features
- Excellent Spring Boot integration for enterprise applications
Cons
- Direct SQL writing can lead to database-dependent code
- Managing large numbers of XML files can become cumbersome
- Limited automatic schema management capabilities
- No dynamic type generation, limiting compile-time type checking
Reference Pages
Code Examples
Basic Setup
<!-- Maven Dependencies -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: user
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.model
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
Basic Operations (CRUD, XML Mapping)
// User.java
public class User {
private int id;
private String name;
private String email;
// getters and setters
}
<!-- UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectUser" resultType="User">
SELECT id, name, email FROM users WHERE id = #{id}
</select>
<insert id="insertUser">
INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert>
<update id="updateUser">
UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
</update>
<delete id="deleteUser">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
// UserMapper.java
@Mapper
public interface UserMapper {
User selectUser(int id);
void insertUser(User user);
void updateUser(User user);
void deleteUser(int id);
}
Annotation-Based Approach
@Mapper
public interface UserMapper {
@Select("SELECT id, name, email FROM users WHERE id = #{id}")
User selectUser(int id);
@Insert("INSERT INTO users (name, email) VALUES (#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUser(User user);
@Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}")
void updateUser(User user);
@Delete("DELETE FROM users WHERE id = #{id}")
void deleteUser(int id);
@Select("SELECT * FROM users WHERE name LIKE CONCAT('%', #{name}, '%')")
List<User> findUsersByName(String name);
}
Dynamic SQL
<select id="findUsers" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="email != null">
AND email = #{email}
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</select>
<update id="updateUserSelective">
UPDATE users
<set>
<if test="name != null">name = #{name},</if>
<if test="email != null">email = #{email},</if>
<if test="status != null">status = #{status}</if>
</set>
WHERE id = #{id}
</update>
<select id="findUsersByIds" resultType="User">
SELECT * FROM users
WHERE id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</select>
Spring Boot Integration
@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Service
@Transactional
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(int id) {
return userMapper.selectUser(id);
}
public void createUser(User user) {
userMapper.insertUser(user);
}
@Transactional(rollbackFor = Exception.class)
public void transferUserData(User fromUser, User toUser) {
userMapper.updateUser(fromUser);
userMapper.updateUser(toUser);
// Automatic rollback if exception occurs
}
}
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User getUser(@PathVariable int id) {
return userService.getUserById(id);
}
@PostMapping
public void createUser(@RequestBody User user) {
userService.createUser(user);
}
}
Advanced Features (Custom Type Handlers, Plugins)
// Custom Type Handler
@Component
public class JsonTypeHandler implements TypeHandler<Map<String, Object>> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void setParameter(PreparedStatement ps, int i, Map<String, Object> parameter,
JdbcType jdbcType) throws SQLException {
try {
ps.setString(i, objectMapper.writeValueAsString(parameter));
} catch (JsonProcessingException e) {
throw new SQLException(e);
}
}
@Override
public Map<String, Object> getResult(ResultSet rs, String columnName) throws SQLException {
try {
String json = rs.getString(columnName);
return json == null ? null : objectMapper.readValue(json, Map.class);
} catch (JsonProcessingException e) {
throw new SQLException(e);
}
}
}
// MyBatis Configuration
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer mybatisConfigurer() {
return configuration -> {
// Register custom type handler
configuration.getTypeHandlerRegistry()
.register(Map.class, JsonTypeHandler.class);
// Cache configuration
configuration.setCacheEnabled(true);
configuration.setLazyLoadingEnabled(true);
configuration.setAggressiveLazyLoading(false);
};
}
}
// Complex mapping with result maps
<resultMap id="userWithProfileMap" type="User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<result property="profile" column="profile_json"
typeHandler="com.example.handler.JsonTypeHandler"/>
<association property="department" javaType="Department">
<id property="id" column="dept_id"/>
<result property="name" column="dept_name"/>
</association>
<collection property="roles" ofType="Role">
<id property="id" column="role_id"/>
<result property="name" column="role_name"/>
</collection>
</resultMap>
<select id="selectUserWithProfile" resultMap="userWithProfileMap">
SELECT u.id as user_id, u.name as user_name, u.profile_json,
d.id as dept_id, d.name as dept_name,
r.id as role_id, r.name as role_name
FROM users u
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN user_roles ur ON u.id = ur.user_id
LEFT JOIN roles r ON ur.role_id = r.id
WHERE u.id = #{id}
</select>