{"record":{"id":"e626abf0fc4b88e8","repo":"mybatis/mybatis-3","slug":"cannot-lazy-load-property-this-property","errorCode":null,"errorMessage":"Cannot lazy load property [\" + this.property + \"] of deserialized object [\" + userObject.getClass() + \"] because configuration does not contain statement [\" + this.mappedStatement + \"]\"","messagePattern":"Cannot lazy load property \\[\" \\+ this\\.property \\+ \"\\] of deserialized object \\[\" \\+ userObject\\.getClass\\(\\) \\+ \"\\] because configuration does not contain statement \\[\" \\+ this\\.mappedStatement \\+ \"\\]\"","errorType":"exception","errorClass":"ExecutorException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/executor/loader/ResultLoaderMap.java","lineNumber":202,"sourceCode":"      }\n      if (this.resultLoader == null) {\n        throw new IllegalArgumentException(\"resultLoader is null\");\n      }\n\n      this.load(null);\n    }\n\n    public void load(final Object userObject) throws SQLException {\n      if (this.metaResultObject == null || this.resultLoader == null) {\n        if (this.mappedParameter == null) {\n          throw new ExecutorException(\"Property [\" + this.property + \"] cannot be loaded because \"\n              + \"required parameter of mapped statement [\" + this.mappedStatement + \"] is not serializable.\");\n        }\n\n        final Configuration config = this.getConfiguration();\n        final MappedStatement ms = config.getMappedStatement(this.mappedStatement);\n        if (ms == null) {\n          throw new ExecutorException(\n              \"Cannot lazy load property [\" + this.property + \"] of deserialized object [\" + userObject.getClass()\n                  + \"] because configuration does not contain statement [\" + this.mappedStatement + \"]\");\n        }\n\n        this.metaResultObject = config.newMetaObject(userObject);\n        this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,\n            metaResultObject.getSetterType(this.property), null, null);\n      }\n\n      /*\n       * We are using a new executor because we may be (and likely are) on a new thread and executors aren't thread\n       * safe. (Is this sufficient?) A better approach would be making executors thread safe.\n       */\n      if (this.serializationCheck == null) {\n        final ResultLoader old = this.resultLoader;\n        this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,\n            old.parameterObject, old.targetType, old.cacheKey, old.boundSql);\n      }","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/executor/loader/ResultLoaderMap.java#L184-L220","documentation":"Thrown when a lazily-loaded property of a Java-serialized (deserialized) domain object is accessed, and the rebuilt Configuration no longer contains the MappedStatement that was saved in the LoadPair at serialization time. Lazy-loading proxies survive serialization by storing only the statement id plus a serializable parameter, then re-resolving the statement through the configurationFactory. If the Configuration returned by that factory does not have a statement registered under the saved id, this ExecutorException is thrown.","triggerScenarios":"ResultLoaderMap.LoadPair.load(userObject) is invoked after deserialization (metaResultObject/resultLoader are null), mappedParameter is not null, configurationFactory returns a Configuration, and config.getMappedStatement(this.mappedStatement) returns null because the statement id stored at serialize time is absent from that Configuration.","commonSituations":"Objects with unresolved lazy properties are serialized (HTTP session replication, caches, messaging) and read back in a JVM whose SqlSessionFactory was built from a different or smaller mybatis-config/mapper set; renamed or removed statement ids between application versions; a configurationFactory pointing at a different SqlSessionFactory than the one that produced the object.","solutions":["Make sure the configurationFactory class returns a Configuration built from exactly the same mapper XML/annotations (same statement ids) that produced the serialized object","Verify the statement id named in the message exists in the current SqlSessionFactory: sqlSessionFactory.getConfiguration().getMappedStatementNames()","Force lazy properties to load before serializing (touch the getters, or call the proxy's fully-loaded state) so no LoadPair has to rebuild after deserialization","If the statement was renamed, keep the old id as an alias or migrate serialized objects before upgrading"],"exampleFix":"// before: session replicated with lazy objects, factory returns a config missing 'com.foo.Bar.selectItems'\npublic class MyConfigurationFactory {\n  public static Configuration getConfiguration() {\n    return otherFactory.getConfiguration(); // different mapper set\n  }\n}\n// after: factory returns the Configuration that owns the mapped statements\npublic class MyConfigurationFactory {\n  public static Configuration getConfiguration() {\n    return AppSqlSessionFactoryBean.getSqlSessionFactory().getConfiguration();\n  }\n}","handlingStrategy":"validation","validationCode":"// Before touching a deserialized lazy object, verify the factory's config still knows the statement\nConfiguration cfg = MyConfigFactory.getConfiguration();\nif (!cfg.getMappedStatementNames().contains(expectedStatementId)) {\n  throw new IllegalStateException(\"Statement gone: \" + expectedStatementId + \" - force reload before serializing\");\n}","typeGuard":null,"tryCatchPattern":"try { order.getItems(); } catch (ExecutorException e) { if (e.getMessage() != null && e.getMessage().contains(\"configuration does not contain statement\")) { // re-fetch the entity fresh from DB instead of using the deserialized copy } else { throw e; } }","preventionTips":["Always pair lazyLoadingEnabled with a configurationFactory backed by the same SqlSessionFactory","Touch lazy getters before writing objects into sessions/caches","Regression-test statement id renames against serialized-object consumers"],"tags":["mybatis","lazy-loading","serialization","deserialization","configuration"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}