Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Integrating MyBatis with SSM: Core Concepts and Configuration

Tech May 15 1

MyBatis is a lightweight persistence framework that supports custom SQL, stored procedures, and advanced mappings. It eliminates most of the boilerplate JDBC code, such as parameter setting and result set handling. With minimal XML configuration or annotations, MyBatis maps plain Java objects (POJOs) to database records, enabling seamless interaction between Java applications and relational databases.

Comparison of Persistence Frameworks

  • JDBC: SQL embedded directly in Java code leads to tight coupling, making maintenance difficult and increasing the risk of hard-coded errors. Frequent SQL changes require constant code updates, reducing development efficiency.

  • Hibernate / JPA: Offer high-level abstraction and rapid development but struggle with complex queries. Generated SQL is often inflexible for optimization. Full-object mapping becomes cumbersome when partial data retrieval is needed. Excessive reflection impacts performance.

  • MyBatis: Strikes a balance—lightweight, performant, and flexible. Separates SQL logic from Java code, allowing developers to focus on business logic while keeping SQL clean and manageable. While slightly slower than Hibernate in development speed, it offers full control over SQL execution.

Development Efficiency: Hibernate > MyBatis > JDBC

Runtime Performance: JDBC > MyBatis > Hibernate

Key Design Principle

In MyBatis, SQL is no longer writtten inside Java classes. Instead, it's defined externally in XML files or via annnotations, improving readability and maintainability. The Mapper interface replaces traditional DAO patterns—only an interface is required, without a concrete implementation. Actual SQL is placed in corresponding XML mapping files.

Example Setup

Employee Entity Class:

package com.ergou.pojo;

public class Employee {
    private Integer empId;
    private String empName;
    private Double empSalary;

    // Getters and setters
    public Integer getEmpId() { return empId; }
    public void setEmpId(Integer empId) { this.empId = empId; }
    public String getEmpName() { return empName; }
    public void setEmpName(String empName) { this.empName = empName; }
    public Double getEmpSalary() { return empSalary; }
    public void setEmpSalary(Double empSalary) { this.empSalary = empSalary; }
}

Mapper Interface:

package com.ergou.mapper;

import com.ergou.pojo.Employee;

public interface EmployeeMapper {
    Employee queryById(Integer id);
    int deleteById(Integer id);
}

XML Mapping File (EmployeeMapper.xml):

<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="com.ergou.mapper.EmployeeMapper">
    <select id="queryById" resultType="com.ergou.pojo.Employee">
        SELECT emp_id AS empId, emp_name AS empName, emp_salary AS empSalary
        FROM t_emp
        WHERE emp_id = #{empId}
    </select>
    <delete id="deleteById">
        DELETE FROM t_emp WHERE emp_id = #{id}
    </delete>
</mapper>

Main Configuration (mybatis-config.xml):

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example" />
                <property name="username" value="liergou" />
                <property name="password" value="liergou070509" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/EmployeeMapper.xml" />
    </mappers>
</configuration>

Test Case:

@Test
public void testQuery() throws IOException {
    try (InputStream configStream = Resources.getResourceAsStream("mybatis-config.xml")) {
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(configStream);
        try (SqlSession session = factory.openSession()) {
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
            Employee employee = mapper.queryById(1);
            System.out.println(employee);
            session.commit();
        }
    }
}

Evolution from iBATIS

MyBatis evolved from iBATIS by introducing stronger type safety and cleaner abstractions. In iBATIS, you could directly use sqlSession.selectOne() without defining interfaces. However, this approach limited flexibility—methods like selectOne only accepted one parameter.

For example, in iBATIS:

<mapper namespace="StudentMapper">
    <select id="query" resultType="com.ergou.pojo.Student">
        SELECT * FROM student WHERE sid = #{id}
    </select>
</mapper>

And the test:

Student student = sqlSession.selectOne("StudentMapper.query", 1);

This limitation was resolved in MyBatis through the use of interface-based proxies, enabling method signatures with multiple parameters.

Parameter Binding

#{} vs ${}

  • #{} acts as a placeholder, safely binding values via prepared statements. Ideal for all user inputs to prevent SQL injection.
  • ${} performs string substitution, useful for dynamic table names, column names, or keywords.

Best practice: Prefer #{} unless dynamic SQL requires ${}.

Example using @Param annotation:

@Select("SELECT * FROM user WHERE ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

Input Handling

Simple Types

When passing a single primitive or String, the placeholder name doesn’t matter—it’s treated as a single value.

<delete id="deleteById">
    DELETE FROM t_emp WHERE emp_id = #{id}
</delete>

Single POJO

Use property names matching the object’s fields.

<insert id="insertEmployee">
    INSERT INTO t_emp(emp_name, emp_salary)
    VALUES(#{empName}, #{empSalary})
</insert>

Multiple Simple Parameters

Use @Param to assign meaningful keys:

List<Employee> queryByNameAndSalary(@Param("name") String name, @Param("salary") Double salary);

Or rely on default naming (arg0, param1, etc.) if no annotation is used.

Map Parameters

Access map keys directly in SQL:

<insert id="insertEmployeeMap">
    INSERT INTO t_emp(emp_name, emp_salary)
    VALUES(#{name}, #{salary})
</insert>

Output Handling

Primitive Return Types

For DML operations (INSERT, UPDATE, DELETE), return type is automatically inferred as int or long (number of affected rows).

<delete id="deleteById">
    DELETE FROM t_emp WHERE emp_id = #{id}
</delete>

Query Results

Specify resultType for queries. Valid values include:

  • Full class name
  • Built-in aliases (e.g., string, integer, date, map, list)

Custom aliases can be registered:

<typeAliases>
    <typeAlias alias="Employee" type="com.ergou.pojo.Employee" />
    <package name="com.ergou.pojo" />
</typeAliases>

Auto-Camel Case Mapping

Enable automatic conversion from underscore to camel case:

<settings>
    <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

This allows emp_id to map to empId automatically.

ResultMap for Complex Mappings

When field-to-property mapping isn't automatic, define a resultMap:

<resultMap id="employeeResultMap" type="com.ergou.pojo.Employee">
    <id column="emp_id" property="empId" />
    <result column="emp_name" property="empName" />
    <result column="emp_salary" property="empSalary" />
</resultMap>

<select id="queryById" resultMap="employeeResultMap">
    SELECT emp_id, emp_name, emp_salary FROM t_emp WHERE emp_id = #{id}
</select>

Primary Key Handling

Auto-Icnremented Keys

Use useGeneratedKeys="true" and keyProperty to capture generated IDs:

<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">
    INSERT INTO t_emp(emp_name, emp_salary)
    VALUES(#{empName}, #{empSalary})
</insert>

Non-Auto-Increment Keys

Use <selectKey> to generate primary keys before insert:

<insert id="insertEmployee">
    <selectKey keyProperty="empId" resultType="Integer" order="BEFORE">
        SELECT NEXTVAL('emp_seq')
    </selectKey>
    INSERT INTO t_emp(emp_id, emp_name, emp_salary)
    VALUES(#{empId}, #{empName}, #{empSalary})
</insert>

Column-Property Mismatch

When database column names differ from Java properties:

  • Use aliases in SQL: SELECT emp_id AS empId
  • Enable camel case mapping globally
  • Define explicit resultMap for full control

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

SBUS Signal Analysis and Communication Implementation Using STM32 with Fus Remote Controller

Overview In a recent project, I utilized the SBUS protocol with the Fus remote controller to control a vehicle's basic operations, including movement, lights, and mode switching. This article is aimed...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.