Backend Development 10 min read

Improving JPA Development with MyBatis‑Flex: Practical Tips and Code Examples

This article examines the limitations of using JPA for complex queries in SpringBoot projects and demonstrates how MyBatis‑Flex can simplify conditional queries, partial field selection, Kotlin integration, and code generation through clear code examples and practical guidance.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Improving JPA Development with MyBatis‑Flex: Practical Tips and Code Examples

The author introduces a SpringBoot project that initially uses JPA for persistence, appreciating its IDE support and JPQL capabilities, but points out that complex business requirements become cumbersome and unintuitive with JPA.

The article does not criticize JPA or MyBatis‑Plus, nor compare their syntax; it focuses on improving coding productivity and convenience.

Pain Points

JpaRepository (Advantages)

JpaRepository enables CRUD operations by extending the interface and allows method‑name‑derived queries, such as findByCardNo , which automatically generates SQL.

interface AppRealtimeRecordRepository : JpaRepository
{
    fun findByCardNo(cardNo: String?): AppRealtimeRecord?
    @Query("select count(1) from AppRealtimeRecord arr where " +
           "arr.deviceSn = :#{#orderQueryRequest.deviceSn} AND " +
           "arr.createTime BETWEEN COALESCE(:#{#orderQueryRequest.payStartTime}, arr.createTime) AND " +
           "COALESCE(:#{#orderQueryRequest.payEndTime}, arr.createTime)")
    fun summaryQuery(orderQueryRequest: OrderQueryRequest): Long
}

When query conditions become numerous, method names grow excessively long, requiring the use of @Query for custom SQL.

Conditional Queries

JPA makes it difficult to build dynamic queries that depend on the presence of parameters, such as checking whether start and end times are null.

MyBatis‑Flex Solution

MyBatis‑Flex provides a fluent API that resembles writing raw SQL, automatically ignoring null conditions.

QueryWrapper query = QueryWrapper.create()
        .where(EMPLOYEE.LAST_NAME.like(searchWord)) // null condition ignored
        .and(EMPLOYEE.GENDER.eq(1))
        .and(EMPLOYEE.AGE.gt(24));
List
employees = employeeMapper.selectListByQuery(query);

Partial Field Queries

JPA always returns full entity objects, making it cumbersome to fetch only selected columns.

Use List to receive raw column data and map it manually.

Define a custom model class with only the needed fields, e.g., new Model(ParamA, ParamB) .

MyBatis‑Flex Implementation

MyBatis‑Flex allows selecting specific columns directly.

// Query all fields
QueryWrapper queryWrapper = QueryWrapper.create()
        .select()
        .where(ACCOUNT.AGE.eq(18));
Account account = accountMapper.selectOneByQuery(queryWrapper);

// Query specific fields
select(QueryColumn("id"), QueryColumn("name"), QueryColumn("category_id"))

Kotlin Support

The author migrated a Kotlin project from JPA to MyBatis‑Flex with minimal effort, achieving more elegant code.

Pagination Query

fun detailList(fairyDetailParam: FairyDetailParam): List
{
    val paginateWith = paginate
(pageNumber = fairyDetailParam.current,
        pageSize = fairyDetailParam.size,
        init = {
            select(QueryColumn("id"), QueryColumn("name"), QueryColumn("category_id"))
            whereWith {
                FairyDetail::categoryId eq fairyDetailParam.categoryId
            }
        })
    return paginateWith.records
}

The query builder mirrors SQL order: select → where → orderBy .

Update User

// Update user avatar and nickname
update
{
    User::avatarUrl set user.avatarUrl
    whereWith {
        User::openId eq user.openId
    }
}

Code Generation

By adding the annotation processor com.mybatis-flex:mybatis-flex-processor:1.9.3 to Gradle (or the Maven equivalent), entities, mappers, and controllers can be generated automatically from the database schema.

Entity Example

package cn.db101.jcc.entity;

import java.io.Serializable;
import java.util.Date;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.Data;

@Table("t_banner")
@Data
public class Banner {
    @Id(keyType = KeyType.Auto)
    private Integer id;
    private String url;
    private Integer sort;
    private Date createTime;
}

Full‑Automatic Generation

Using MyBatis‑Flex’s code generator, developers can configure data source, package, table prefixes, and column settings to produce entity classes, mappers, and other layers.

public class Codegen {
    public static void main(String[] args) {
        // Configure data source
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/your-database?characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("******");

        // Create global config (style 1)
        GlobalConfig globalConfig = createGlobalConfigUseStyle1();

        // Build generator and generate code
        Generator generator = new Generator(dataSource, globalConfig);
        generator.generate();
    }
    // ... (configuration methods omitted for brevity)
}

Overall Usage

For developers familiar with MyBatis‑Plus, switching to MyBatis‑Flex offers a more intuitive, SQL‑like experience, reduces boilerplate, and improves productivity despite some overlap with existing tools.

backendJavacode generationKotlinSpringBootJPAMyBatis-Flex
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.