Why Lombok’s Getter/Setter Naming Breaks MyBatis (and EasyExcel) and How to Fix It

The article analyzes a Lombok @Data‑generated getter/setter naming mismatch that causes MyBatis to store a null enum field, walks through debugging, examines MyBatis’s PropertyNamer source, demonstrates the issue with test code, and then explains a similar @Accessor(chain=true) problem in EasyExcel, offering concrete workarounds for both.

Java Web Project
Java Web Project
Java Web Project
Why Lombok’s Getter/Setter Naming Breaks MyBatis (and EasyExcel) and How to Fix It

Background

In a Java project we use Lombok @Data to generate boilerplate getters, setters, and toString. When inserting an entity with MyBatis we discovered that the enum field nMetaType was always persisted as NULL despite the object containing a value.

Problem Discovery

We created the following entity (simplified):

@Data
public class NMetaVerify {
    private NMetaType nMetaType;
    private Long id;
    // ... other fields
}

Running a MyBatis INSERT showed all columns were saved correctly except nMetaType. Debugging the MyBatis call revealed that the NMetaVerify instance still held a non‑null enum, so the fault lay in the reflection step that MyBatis uses to map getter/setter names to property names.

Root Cause Analysis

Inspecting the generated bytecode we found Lombok produced the methods setNMetaType and getNMetaType – note the capital “N” after the prefix. The JavaBeans convention (and the code generated by IDEs) expects setnMetaType / getnMetaType when the second character of the property name is uppercase.

MyBatis (v3.4.6) determines a property name via

org.apache.ibatis.reflection.property.PropertyNamer.methodToProperty

. The relevant fragment is:

if (name.startsWith("is")) {
    name = name.substring(2);
} else if (name.startsWith("get") || name.startsWith("set")) {
    name = name.substring(3);
}
if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
    name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
return name;

The logic lower‑cases the first character only when the second character is **not** uppercase. Because Lombok’s method has a second uppercase letter, the algorithm returns the original string "NMetaType", which does not match the field name "nMetaType". Consequently MyBatis cannot find a matching setter and writes NULL.

Test Verification

We wrote a JUnit test to print the conversion results:

@Test
public void foundPropertyNamer() {
    String isName = "isName";
    String getName = "getName";
    String getnMetaType = "getnMetaType";
    String getNMetaType = "getNMetaType";
    Stream.of(isName, getName, getnMetaType, getNMetaType)
        .forEach(m -> System.out.println("Method: " + m + " → Property: " + PropertyNamer.methodToProperty(m)));
}

Output:

Method: isName → Property: name
Method: getName → Property: name
Method: getnMetaType → Property: nMetaType
Method: getNMetaType → Property: NMetaType

The last line confirms the mismatch.

Solution for Lombok Issue

Rename the field so that the first two letters are lowercase (e.g., nmetaType) or avoid the capital‑second‑letter pattern.

If the database schema cannot be changed, manually generate the correct getter/setter (e.g., using IDE “Generate Getter/Setter”) and remove Lombok for that property.

@Accessor(chain = true) Problem in EasyExcel

When exporting a new DTO with the Lombok‑style chainable setter annotation @Accessor(chain = true), EasyExcel failed to write the data. The root cause is that EasyExcel relies on CGLIB, which in turn uses java.beans.Introspector to discover property descriptors. The introspector only treats methods whose name starts with set **and** whose return type is void as setters.

Because the generated chainable setter returns the object itself (e.g.,

public UserDto setUserName(String name) { this.userName = name; return this; }

), the introspector discards it, leaving the property unmapped.

Relevant Code Snippet (CGLIB/Introspector)

if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {
    pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);
} else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) {
    // Simple setter
    pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);
}

The comment highlights that only void setters are accepted.

Solution for @Accessor Issue

Remove the @Accessor(chain = true) annotation from DTOs that need to be exported with EasyExcel.

Alternatively, wait for a future EasyExcel release that replaces CGLIB with a library that recognises non‑void chainable setters, or contribute a fix upstream.

Both problems illustrate how code‑generation tools can clash with framework conventions, and the article demonstrates a systematic debugging approach: reproduce the failure, inspect generated bytecode, read the framework source that performs the reflection, and then apply a targeted workaround.

JavaMyBatisAnnotationEasyExcellombokGetterSetter
Java Web Project
Written by

Java Web Project

Focused on Java backend technologies, trending internet tech, and the latest industry developments. The platform serves over 200,000 Java developers, inviting you to learn and exchange ideas together. Check the menu for Java learning resources.

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.