baomidou/mybatis-plus · error · MybatisPlusException

Unable to retrieve the mapperInterface and sqlSession proper

Error message

Unable to retrieve the mapperInterface and sqlSession properties from %s

What it means

MapperProxyMetadata was constructed from a MetaObject that does not expose 'mapperInterface' and 'sqlSession' getters — i.e. the wrapped object is not (or cannot be reflected as) a MyBatis MapperProxy. This utility extracts the two fields every MapperProxy carries, so anything else is a programming error.

Source

Thrown at mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/MapperProxyMetadata.java:39

/**
 * Mapper代理属性
 *
 * @author nieqiurong
 * @see com.baomidou.mybatisplus.core.override.MybatisMapperProxy
 * @see org.apache.ibatis.binding.MapperProxy
 * @since 3.5.12
 */
@SuppressWarnings("LombokGetterMayBeUsed")
public class MapperProxyMetadata {

    private final SqlSession sqlSession;

    private final Class<?> mapperInterface;

    public MapperProxyMetadata(MetaObject metaObject) {
        if (!metaObject.hasGetter("mapperInterface") || !metaObject.hasGetter("sqlSession")) {
            throw new MybatisPlusException("Unable to retrieve the mapperInterface and sqlSession properties from " + metaObject.getOriginalObject());
        }
        this.mapperInterface = (Class<?>) metaObject.getValue("mapperInterface");
        this.sqlSession = (SqlSession) metaObject.getValue("sqlSession");
    }

    public Class<?> getMapperInterface() {
        return mapperInterface;
    }

    public SqlSession getSqlSession() {
        return sqlSession;
    }

    @Override
    public String toString() {
        return "MapperProxy{" +
            "mapperInterface=" + mapperInterface +
            ", sqlSession=" + sqlSession +

View on GitHub (pinned to bf67d90747)

Solutions

  1. Unwrap to the actual MapperProxy before building metadata: get the InvocationHandler of the JDK proxy first
  2. Check metaObject.hasGetter("mapperInterface") && hasGetter("sqlSession") before constructing, or extract h from Proxy.getInvocationHandler(mapper) and pass that
  3. If you wrapped mappers in another proxy layer, unwrap iteratively until the handler is a MapperProxy

Example fix

// before
Object target = AopProxyUtils.getSingletonTarget(mapper);
new MapperProxyMetadata(SystemMetaObject.forObject(target)); // not a MapperProxy
// after
Object handler = Proxy.getInvocationHandler(mapper);
new MapperProxyMetadata(SystemMetaObject.forObject(handler)); // MapperProxy exposes both getters
Defensive patterns

Strategy: type-guard

Validate before calling

Object h = mapper instanceof Proxy ? Proxy.getInvocationHandler(mapper) : mapper;
if (!(h instanceof MapperProxy)) throw new IllegalArgumentException("Not a MyBatis mapper proxy");

Type guard

static MapperProxyMetadata metadataOf(Object mapper) {
    Object h = mapper;
    while (h instanceof Proxy) h = Proxy.getInvocationHandler(h);
    MetaObject mo = SystemMetaObject.forObject(h);
    if (mo.hasGetter("mapperInterface") && mo.hasGetter("sqlSession")) {
        return new MapperProxyMetadata(mo);
    }
    return null; // caller falls back
}

Try / catch

catch MybatisPlusException and treat the object as non-mapper: log.debug("Not a MapperProxy: {}", obj.getClass()); skip metadata extraction.

Prevention

When it happens

Trigger: Framework/plugin code calling new MapperProxyMetadata(SystemMetaObject.forObject(obj)) where obj is a plain bean, a JDK dynamic proxy whose handler is not MapperProxy, or a CGLIB proxy wrapping the mapper.

Common situations: Custom interceptors or MyBatis-Plus internals (e.g. extending the mapper proxy layer) passing the proxy object itself instead of its invocation handler; wrapping mappers in Spring AOP proxies so the reflective layout differs.

Related errors


AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14). Data as JSON: /api/errors/da747a99971ad3f0. Report an issue: GitHub.