MyBatis-Plus 3.5.15: Spring Boot 4.0 & Jackson 3.0 Support Explained

This article details MyBatis-Plus 3.5.15's new support for Spring Boot 4.0 and Jackson 3.0, providing step-by-step migration guidance including starter dependency changes, JSON field handling with Jackson3TypeHandler, and common upgrade pitfalls like starter mismatches and factoryBeanObjectType errors.

java1234
java1234
java1234
MyBatis-Plus 3.5.15: Spring Boot 4.0 & Jackson 3.0 Support Explained

What MyBatis-Plus Does

MyBatis-Plus adds a layer of common capabilities on top of MyBatis without changing its core behavior. It eliminates repetitive boilerplate: single-table CRUD via BaseMapper, type-safe conditional queries with LambdaQueryWrapper (e.g., User::getUsername), automatic pagination SQL, and a code generator that produces Entity, Mapper, and Service classes from table schemas.

Key Changes in 3.5.15

The release focuses on two major compatibilities:

Spring Boot 4.0 support — Spring Boot 4 changes starter Maven coordinates. The old mybatis-plus-spring-boot3-starter does not work with Boot 4 because auto-configuration classes differ. Version 3.5.15 introduces mybatis-plus-spring-boot4-starter.

Jackson 3 support — Jackson 3 moved its core package from com.fasterxml.jackson to tools.jackson. The existing JacksonTypeHandler still targets Jackson 2. A new Jackson3TypeHandler is provided for projects already on Jackson 3.

Minor fixes include metadata adjustments in the code generator, a fix for Enjoy template XML generation, and CrudRepository batch operations now close connections more promptly when not in a transaction.

Integrating with Spring Boot 4

Starter Version Mapping

Starter artifacts are bound to specific Spring Boot major versions; mixing them causes auto-configuration failures.

Spring Boot 2.x → mybatis-plus-boot-starter Spring Boot 3.x → mybatis-plus-spring-boot3-starter Spring Boot 4.x →

mybatis-plus-spring-boot4-starter

Maven Dependency

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>4.0.0</version>
</parent>
<dependencies>
  <!-- Note: this is boot4, not boot3 -->
  <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot4-starter</artifactId>
    <version>3.5.15</version>
  </dependency>
  <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
  </dependency>
</dependencies>

DataSource Configuration

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/db_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: auto

Entity, Mapper, and Query Example

The following maps to table t_user without Lombok.

/**
 * User entity, maps to table t_user
 */
@TableName("t_user")
public class User {
    /** Primary key */
    @TableId(type = IdType.AUTO)
    private Long id;

    /** Username */
    private String username;

    /** Password, plaintext for demo */
    private String password;

    /** Gender, default male */
    private String gender;

    /** Creation time */
    private LocalDateTime createTime;

    // getters and setters omitted for brevity
}
/**
 * User table Mapper
 */
public interface UserMapper extends BaseMapper<User> {
}
/**
 * Query user by username
 */
@Service
public class UserService {
    private final UserMapper userMapper;

    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    /**
     * Use method reference for condition, avoids typos in column names
     */
    public User findByUsername(String username) {
        return userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getUsername, username)
                .last("LIMIT 1"));
    }
}

The only required change from a Boot 3 project is replacing the starter dependency with mybatis-plus-spring-boot4-starter; @MapperScan and the main application class remain unchanged.

JSON Fields with Jackson 3

Many tables store extension data in a JSON column (e.g., user city and tags) to avoid extra tables. Previously JacksonTypeHandler handled Jackson 2; now Jackson3TypeHandler handles Jackson 3. It serializes objects to JSON strings on write and deserializes on read.

Extension Info Class

/**
 * User extension info, stored as JSON in t_user.extra_info
 */
public class ExtraInfo {
    /** City */
    private String city;

    /** Tag list */
    private List<String> tags;

    // getters and setters omitted
}

Attach to Entity Field

A critical detail: @TableName must include autoResultMap = true. Without it, the JSON column bypasses the type handler, resulting in null or raw string values.

/**
 * User entity with JSON extension field
 */
@TableName(value = "t_user", autoResultMap = true)
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;

    private String username;

    /**
     * Read/write extra_info column using Jackson 3
     */
    @TableField(typeHandler = Jackson3TypeHandler.class)
    private ExtraInfo extraInfo;

    // getters and setters omitted
}

Insert Example

/**
 * Save user with JSON extension info
 */
public void saveUser() {
    ExtraInfo extraInfo = new ExtraInfo();
    extraInfo.setCity("Shanghai");
    extraInfo.setTags(Arrays.asList("vip", "spring-boot4"));

    User user = new User();
    user.setUsername("admin");
    user.setPassword("123456");
    user.setGender("Male");
    user.setExtraInfo(extraInfo);
    userMapper.insert(user);
}

The extra_info column then contains:

{"city":"Shanghai","tags":["vip","spring-boot4"]}

Reuse Spring's ObjectMapper

If the project already configures a custom Jackson 3 ObjectMapper (date formats, ignore nulls, etc.), inject it into the handler at startup to avoid a second default instance:

/**
 * Reuse Spring container's ObjectMapper
 */
@Configuration
public class Jackson3Config {
    public Jackson3Config(ObjectMapper objectMapper) {
        Jackson3TypeHandler.setObjectMapper(objectMapper);
    }
}

Package name caution: Jackson 3's ObjectMapper resides in tools.jackson.databind. Accidentally importing com.fasterxml.jackson.databind.ObjectMapper (Jackson 2) may compile but cause runtime type-handler mismatches.

Common Upgrade Pitfalls

1. Wrong Starter

Most frequent issue: using mybatis-plus-spring-boot3-starter in a Boot 4 project. The dependency resolves but auto-configuration fails, often with obscure errors not directly pointing to MyBatis-Plus.

2. JSON Handler and Jackson Version Mismatch

If the entity declares Jackson3TypeHandler, the classpath must contain Jackson 3. Legacy projects on Jackson 2 should keep using JacksonTypeHandler; no need to upgrade solely for the new handler name.

3. factoryBeanObjectType Error on Startup

Some users encountered:

Invalid value type for attribute 'factoryBeanObjectType': java.lang.String

Root cause: transitive mybatis-spring version too old for Boot 4. Fix by explicitly declaring org.mybatis:mybatis-spring:4.0.0 in the POM. Version 3.5.16 already upgrades this dependency in the Boot 4 starter, so a minor version bump is often simpler than manual exclusion.

4. Code Generator Changes

Metadata building was adjusted and Enjoy template XML generation bugs were fixed. After upgrading, regenerate a table's artifacts and compare with existing Mapper XML to catch discrepancies.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaORMMyBatis-PlusDatabase MigrationTypeHandlerUpgrade GuideJackson 3.0Spring Boot 4.0
java1234
Written by

java1234

Former senior programmer at a Fortune Global 500 company, dedicated to sharing Java expertise. Visit Feng's site: Java Knowledge Sharing, www.java1234.com

0 followers
Reader feedback

How this landed with the community

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.