Repository: baomidou/mybatis-plus Branch: 3.0 Commit: 856acc1b0f28 Files: 1128 Total size: 3.6 MB Directory structure: gitextract_7cwa3873/ ├── .editorconfig ├── .gitee/ │ ├── ISSUE_TEMPLATE.zh-CN.md │ └── PULL_REQUEST_TEMPLATE.zh-CN.md ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ └── feature_request.yml │ ├── dependabot.yml │ └── workflows/ │ ├── gradle.yml │ └── publish.yml ├── CHANGELOG.md ├── LICENSE ├── MPCodeStyle.xml ├── README-zh.md ├── README.md ├── build.gradle ├── changelog-temp.md ├── gradle.properties ├── libs/ │ └── jdbcDriver-18.jar ├── license.txt ├── mybatis-plus/ │ ├── build.gradle │ └── src/ │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ ├── BaseDbTest.java │ │ ├── DbTypeTest.java │ │ ├── MybatisTest.java │ │ ├── OgnlTest.java │ │ ├── PageTest.java │ │ ├── autoresultmap/ │ │ │ ├── AutoResultMapTest.java │ │ │ ├── Entity.java │ │ │ └── EntityMapper.java │ │ ├── batch/ │ │ │ ├── BatchTest.java │ │ │ ├── Entity.java │ │ │ └── EntityMapper.java │ │ ├── cache/ │ │ │ ├── page/ │ │ │ │ ├── PageCache.java │ │ │ │ ├── PageCacheMapper.java │ │ │ │ └── PageCacheTest.java │ │ │ └── xml/ │ │ │ ├── XmlCache.java │ │ │ ├── XmlCacheMapper.java │ │ │ └── XmlCacheTest.java │ │ ├── chainwrapper/ │ │ │ ├── ChainWrapperTest.java │ │ │ ├── ChainWrappersTest.java │ │ │ ├── Entity.java │ │ │ └── EntityMapper.java │ │ ├── enums/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ ├── EnumInt.java │ │ │ ├── EnumOrdinal.java │ │ │ ├── EnumStr.java │ │ │ └── EnumTest.java │ │ ├── fill/ │ │ │ ├── FillEntity.java │ │ │ ├── FillMapper.java │ │ │ └── FillTest.java │ │ ├── gaussdb/ │ │ │ ├── Demo.java │ │ │ ├── DemoMapper.java │ │ │ └── GaussdbTest.java │ │ ├── h2/ │ │ │ ├── ActiveRecordTest.java │ │ │ ├── BaseTest.java │ │ │ ├── CustomFillTest.java │ │ │ ├── FillPerformanceTest.java │ │ │ ├── H2Delete1Eq1Test.java │ │ │ ├── H2KeyGeneratorTest.java │ │ │ ├── H2LogicDeleteTest.java │ │ │ ├── H2MetaObjectHandler.java │ │ │ ├── H2StudentMapperTest.java │ │ │ ├── H2UserMapperTest.java │ │ │ ├── H2UserStrategyTest.java │ │ │ ├── H2UserTest.java │ │ │ ├── H2userNameJsonTypeHandler.java │ │ │ ├── LastSqlTest.java │ │ │ ├── MybatisMapperRegistryTest.java │ │ │ ├── SqlRunnerTest.java │ │ │ ├── TestXmlConfig.java │ │ │ ├── cache/ │ │ │ │ ├── CacheConfig.java │ │ │ │ ├── CacheTest.java │ │ │ │ ├── CustomCache.java │ │ │ │ ├── CustomPage.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── CacheMapper.java │ │ │ │ ├── model/ │ │ │ │ │ └── CacheModel.java │ │ │ │ └── service/ │ │ │ │ ├── ICacheService.java │ │ │ │ └── impl/ │ │ │ │ └── CacheServiceImpl.java │ │ │ ├── config/ │ │ │ │ ├── DBConfig.java │ │ │ │ ├── MybatisPlusConfig.java │ │ │ │ ├── MybatisPlusConfigLogicDelete.java │ │ │ │ └── MybatisXmlConfig.java │ │ │ ├── customfill/ │ │ │ │ ├── CustomFillConfig.java │ │ │ │ ├── annotation/ │ │ │ │ │ └── InsertUpdateFill.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── TestModelMapper.java │ │ │ │ └── model/ │ │ │ │ └── TestModel.java │ │ │ ├── entity/ │ │ │ │ ├── H2Addr.java │ │ │ │ ├── H2Student.java │ │ │ │ ├── H2User.java │ │ │ │ ├── H2UserLogicDelete.java │ │ │ │ ├── H2UserStrategy.java │ │ │ │ ├── SuSuperEntity.java │ │ │ │ ├── SuSuperEntityCamel.java │ │ │ │ ├── SuperEntity.java │ │ │ │ └── SuperEntityCamel.java │ │ │ ├── enums/ │ │ │ │ ├── AgeEnum.java │ │ │ │ ├── GenderEnum.java │ │ │ │ └── GradeEnum.java │ │ │ ├── fillperformance/ │ │ │ │ ├── FillPerformanceConfig.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── PerformanceModelMapper.java │ │ │ │ ├── model/ │ │ │ │ │ └── PerformanceModel.java │ │ │ │ └── service/ │ │ │ │ ├── IPerformanceModelService.java │ │ │ │ └── impl/ │ │ │ │ └── PerformanceModelServiceImpl.java │ │ │ ├── idgenerator/ │ │ │ │ ├── CustomIdGenerator.java │ │ │ │ ├── IdGeneratorConfig.java │ │ │ │ ├── IdentifierGeneratorTest.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── BigDecimalIdGeneratorMapper.java │ │ │ │ │ ├── BigIntegerIdGeneratorMapper.java │ │ │ │ │ ├── IntegerIdGeneratorMapper.java │ │ │ │ │ ├── IntegerStringIdGeneratorMapper.java │ │ │ │ │ ├── LongIdGeneratorMapper.java │ │ │ │ │ ├── LongStringIdGeneratorMapper.java │ │ │ │ │ └── StringIdGeneratorMapper.java │ │ │ │ └── model/ │ │ │ │ ├── BigDecimalIdGeneratorModel.java │ │ │ │ ├── BigIntegerIdGeneratorModel.java │ │ │ │ ├── IntegerIdGeneratorModel.java │ │ │ │ ├── IntegerStringIdGeneratorModel.java │ │ │ │ ├── LongIdGeneratorModel.java │ │ │ │ ├── LongStringIdGeneratorModel.java │ │ │ │ └── StringIdGeneratorModel.java │ │ │ ├── issues/ │ │ │ │ ├── aop/ │ │ │ │ │ ├── AopConfig1.java │ │ │ │ │ ├── AopConfig2.java │ │ │ │ │ ├── AppConfig.java │ │ │ │ │ ├── MultiAopTest.java │ │ │ │ │ ├── NoAopTest.java │ │ │ │ │ ├── SingleAopTest.java │ │ │ │ │ ├── entity/ │ │ │ │ │ │ └── Demo.java │ │ │ │ │ ├── mapper/ │ │ │ │ │ │ └── DemoMapper.java │ │ │ │ │ └── service/ │ │ │ │ │ ├── IDemoService.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── DemoServiceImpl.java │ │ │ │ ├── genericid/ │ │ │ │ │ ├── GenericIdTest.java │ │ │ │ │ ├── MybatisPlusConfig.java │ │ │ │ │ ├── entity/ │ │ │ │ │ │ ├── LongEntity.java │ │ │ │ │ │ ├── StringEntity.java │ │ │ │ │ │ └── SuperEntity.java │ │ │ │ │ └── mapper/ │ │ │ │ │ ├── LongEntityMapper.java │ │ │ │ │ └── StringEntityMapper.java │ │ │ │ └── repositoryscan/ │ │ │ │ ├── AppConfig.java │ │ │ │ ├── AppConfigWithMapperScan.java │ │ │ │ ├── RepositoryDefaultScanTest.java │ │ │ │ ├── RepositoryMapperScanTest.java │ │ │ │ ├── entity/ │ │ │ │ │ └── Demo.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── DemoRepositoryMapper.java │ │ │ │ └── service/ │ │ │ │ ├── IDemoRepositoryService.java │ │ │ │ └── impl/ │ │ │ │ └── DemoRepositoryServiceImpl.java │ │ │ ├── keygenerator/ │ │ │ │ ├── KeyGeneratorConfig.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── ExtendKeyGeneratorMapper.java │ │ │ │ │ ├── IntegerKeyGeneratorMapper.java │ │ │ │ │ ├── KeyGeneratorMapper.java │ │ │ │ │ ├── LongKeyGeneratorMapper.java │ │ │ │ │ └── StringKeyGeneratorMapper.java │ │ │ │ └── model/ │ │ │ │ ├── BaseMode.java │ │ │ │ ├── ExtendKeyGeneratorModel.java │ │ │ │ ├── IntegerKeyGeneratorModel.java │ │ │ │ ├── KeyGeneratorModel.java │ │ │ │ ├── LongKeyGeneratorModel.java │ │ │ │ └── StringKeyGeneratorModel.java │ │ │ ├── mapper/ │ │ │ │ ├── H2StudentMapper.java │ │ │ │ ├── H2UserLogicDeleteMapper.java │ │ │ │ ├── H2UserMapper.java │ │ │ │ ├── H2UserStrategyMapper.java │ │ │ │ └── SuperMapper.java │ │ │ ├── service/ │ │ │ │ ├── IH2StudentService.java │ │ │ │ ├── IH2UserService.java │ │ │ │ └── impl/ │ │ │ │ ├── H2StudentServiceImpl.java │ │ │ │ └── H2UserServiceImpl.java │ │ │ └── tenant/ │ │ │ ├── CustomCacheExecutor.java │ │ │ ├── TenantConfig.java │ │ │ ├── TenantTest.java │ │ │ ├── mapper/ │ │ │ │ └── StudentMapper.java │ │ │ ├── model/ │ │ │ │ └── Student.java │ │ │ └── service/ │ │ │ ├── IStudentService.java │ │ │ └── impl/ │ │ │ └── StudentServiceImpl.java │ │ ├── json/ │ │ │ ├── FastJson2Entity.java │ │ │ ├── FastJson2EntityMapper.java │ │ │ ├── FastJsonEntity.java │ │ │ ├── FastJsonEntityMapper.java │ │ │ ├── Fastjson2Test.java │ │ │ ├── FastjsonTest.java │ │ │ ├── GsonEntity.java │ │ │ ├── GsonEntityMapper.java │ │ │ ├── GsonTest.java │ │ │ ├── JackJsonEntity.java │ │ │ ├── JackJsonEntityMapper.java │ │ │ └── JacksonTest.java │ │ ├── logicdel/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ └── LogicDelTest.java │ │ ├── multisqlsessionfactory/ │ │ │ ├── AppConfig.java │ │ │ ├── MultiSqlSessionFactoryTest.java │ │ │ ├── a/ │ │ │ │ ├── entity/ │ │ │ │ │ └── AEntity.java │ │ │ │ ├── mapper/ │ │ │ │ │ └── AEntityMapper.java │ │ │ │ └── service/ │ │ │ │ ├── AEntityService.java │ │ │ │ └── impl/ │ │ │ │ └── AEntityServiceImpl.java │ │ │ └── b/ │ │ │ ├── entity/ │ │ │ │ └── BEntity.java │ │ │ ├── mapper/ │ │ │ │ └── BEntityMapper.java │ │ │ └── service/ │ │ │ ├── BEntityService.java │ │ │ └── impl/ │ │ │ └── BEntityServiceImpl.java │ │ ├── non/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ └── NonTest.java │ │ ├── optimisticlocker/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ └── OptimisticLockerTest.java │ │ ├── orderby/ │ │ │ ├── OrderByEntity.java │ │ │ ├── OrderByMapper.java │ │ │ └── OrderByMapperTest.java │ │ ├── phoenix/ │ │ │ ├── PhoenixBaseMapper.java │ │ │ ├── PhoenixTest.java │ │ │ ├── config/ │ │ │ │ ├── DBConfig.java │ │ │ │ └── MybatisPlusConfig.java │ │ │ ├── entity/ │ │ │ │ └── PhoenixTestInfo.java │ │ │ └── mapper/ │ │ │ └── PhoenixTestInfoMapper.java │ │ ├── pom/ │ │ │ └── GeneratePomTest.java │ │ ├── postgresql/ │ │ │ ├── PostgresqlTest.java │ │ │ ├── config/ │ │ │ │ └── PostgresqlConfig.java │ │ │ ├── entity/ │ │ │ │ └── Pgtable.java │ │ │ ├── mapper/ │ │ │ │ └── PgtableMappper.java │ │ │ └── service/ │ │ │ ├── IPgtableService.java │ │ │ └── impl/ │ │ │ └── PgtableServiceImpl.java │ │ ├── puginsome/ │ │ │ ├── A.java │ │ │ ├── AMapper.java │ │ │ ├── B.java │ │ │ ├── BMapper.java │ │ │ └── PluginSomeTest.java │ │ ├── record/ │ │ │ ├── RecordEntity.java │ │ │ ├── RecordEntityMapper.java │ │ │ └── RecordEntityTest.java │ │ ├── reflection/ │ │ │ └── ExampleObjectFactory.java │ │ ├── replaceplaceholder/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ ├── EntitySubMapper.java │ │ │ └── ReplacePlaceholderTest.java │ │ ├── resultmap/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ └── ResultMapTest.java │ │ ├── rewrite/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ └── RewriteTest.java │ │ ├── scheam/ │ │ │ ├── SchemaEntity.java │ │ │ ├── SchemaEntityMapper.java │ │ │ └── SchemaEntityTest.java │ │ ├── sqlrunner/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ └── SqlRunnerTest.java │ │ ├── strategy/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ └── StrategyTest.java │ │ ├── tenant/ │ │ │ ├── Entity.java │ │ │ ├── EntityMapper.java │ │ │ └── TenantTest.java │ │ ├── toolkit/ │ │ │ ├── DbTest.java │ │ │ ├── JdbcUtilsTest.java │ │ │ ├── SimpleQueryTest.java │ │ │ ├── SqlHelperTest.java │ │ │ └── StringUtilsTest.java │ │ ├── uuid/ │ │ │ ├── DeleteByIdDto.java │ │ │ ├── UUIDEntity.java │ │ │ ├── UUIDEntityMapper.java │ │ │ ├── UUIDEntityTest.java │ │ │ ├── UUIDLogicEntity.java │ │ │ ├── UUIDLogicEntityMapper.java │ │ │ ├── UUIDLogicEntityTest.java │ │ │ └── UUIDTypeHandler.java │ │ └── version/ │ │ ├── Entity.java │ │ ├── EntityMapper.java │ │ └── VersionTest.java │ ├── kotlin/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ └── h2/ │ │ ├── KtTestConfig.kt │ │ ├── MetaObjectHandlerTest.kt │ │ ├── MyMetaObjectHandler.kt │ │ └── kotlin/ │ │ ├── KtH2UserTest.kt │ │ ├── entity/ │ │ │ └── KtH2User.kt │ │ ├── mapper/ │ │ │ └── KtUserMapper.kt │ │ └── service/ │ │ ├── KtH2UserService.kt │ │ └── impl/ │ │ └── KtH2UserServiceImpl.kt │ └── resources/ │ ├── cache/ │ │ └── init.ddl.sql │ ├── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ ├── cache/ │ │ │ ├── package-info.java │ │ │ └── xml/ │ │ │ └── XmlCacheMapper.xml │ │ ├── enums/ │ │ │ └── EntityMapper.xml │ │ ├── fill/ │ │ │ └── FillMapper.xml │ │ ├── h2/ │ │ │ └── mapper/ │ │ │ └── H2StudentMapper.xml │ │ ├── puginsome/ │ │ │ ├── AMapper.xml │ │ │ └── BMapper.xml │ │ ├── resultmap/ │ │ │ └── EntityMapper.xml │ │ └── rewrite/ │ │ └── EntityMapper.xml │ ├── customfilltest/ │ │ └── init.ddl.sql │ ├── fillperformance/ │ │ └── init.ddl.sql │ ├── h2/ │ │ ├── spring-cache-h2.xml │ │ ├── spring-custom-fill-test-h2.xml │ │ ├── spring-fill-performance-h2.xml │ │ ├── spring-id-generator-h2.xml │ │ ├── spring-keygenerator-h2.xml │ │ ├── spring-logic-delete-h2.xml │ │ ├── spring-sharding-h2.xml │ │ ├── spring-tenant-h2.xml │ │ ├── spring-test-h2.xml │ │ ├── spring-test-xml-h2.xml │ │ ├── student.ddl.sql │ │ ├── student.insert.sql │ │ ├── user.ddl.sql │ │ └── user.insert.sql │ ├── hbase-site.xml │ ├── idgenerator/ │ │ └── init.ddl.sql │ ├── issues/ │ │ └── genericid/ │ │ ├── spring.xml │ │ └── sql/ │ │ └── init.ddl.sql │ ├── keygenerator/ │ │ └── init.ddl.sql │ ├── logback.xml │ ├── mybatis-config-object-factory.xml │ ├── mybatis-config.xml │ ├── phoenix/ │ │ ├── spring-test-phoenix.xml │ │ └── test_info.ddl.sql │ ├── postgresql/ │ │ └── spring-test-postgresql.xml │ └── tenant/ │ └── init.ddl.sql ├── mybatis-plus-annotation/ │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── baomidou/ │ └── mybatisplus/ │ └── annotation/ │ ├── DbType.java │ ├── EnumValue.java │ ├── FieldFill.java │ ├── FieldStrategy.java │ ├── IEnum.java │ ├── IdType.java │ ├── InterceptorIgnore.java │ ├── KeySequence.java │ ├── OrderBy.java │ ├── SqlCondition.java │ ├── TableField.java │ ├── TableId.java │ ├── TableLogic.java │ ├── TableName.java │ ├── Version.java │ └── package-info.java ├── mybatis-plus-bom/ │ └── build.gradle ├── mybatis-plus-core/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── core/ │ │ │ ├── InjectorResolver.java │ │ │ ├── MybatisConfiguration.java │ │ │ ├── MybatisMapperAnnotationBuilder.java │ │ │ ├── MybatisMapperBuilderAssistant.java │ │ │ ├── MybatisMapperRegistry.java │ │ │ ├── MybatisMethodResolver.java │ │ │ ├── MybatisParameterHandler.java │ │ │ ├── MybatisPlusVersion.java │ │ │ ├── MybatisSqlSessionFactoryBuilder.java │ │ │ ├── MybatisXMLConfigBuilder.java │ │ │ ├── MybatisXMLLanguageDriver.java │ │ │ ├── MybatisXMLMapperBuilder.java │ │ │ ├── MybatisXMLScriptBuilder.java │ │ │ ├── assist/ │ │ │ │ ├── AbstractSqlRunner.java │ │ │ │ ├── ISqlRunner.java │ │ │ │ └── package-info.java │ │ │ ├── batch/ │ │ │ │ ├── BatchMethod.java │ │ │ │ ├── BatchSqlSession.java │ │ │ │ ├── MybatisBatch.java │ │ │ │ └── ParameterConvert.java │ │ │ ├── conditions/ │ │ │ │ ├── AbstractLambdaWrapper.java │ │ │ │ ├── AbstractWrapper.java │ │ │ │ ├── ISqlSegment.java │ │ │ │ ├── SharedString.java │ │ │ │ ├── Wrapper.java │ │ │ │ ├── interfaces/ │ │ │ │ │ ├── Compare.java │ │ │ │ │ ├── Func.java │ │ │ │ │ ├── Join.java │ │ │ │ │ ├── Nested.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── package-info.java │ │ │ │ ├── query/ │ │ │ │ │ ├── LambdaQueryWrapper.java │ │ │ │ │ ├── Query.java │ │ │ │ │ ├── QueryWrapper.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── segments/ │ │ │ │ │ ├── AbstractISegmentList.java │ │ │ │ │ ├── ColumnSegment.java │ │ │ │ │ ├── GroupBySegmentList.java │ │ │ │ │ ├── HavingSegmentList.java │ │ │ │ │ ├── MatchSegment.java │ │ │ │ │ ├── MergeSegments.java │ │ │ │ │ ├── NormalSegmentList.java │ │ │ │ │ ├── OrderBySegmentList.java │ │ │ │ │ └── package-info.java │ │ │ │ └── update/ │ │ │ │ ├── LambdaUpdateWrapper.java │ │ │ │ ├── Update.java │ │ │ │ ├── UpdateWrapper.java │ │ │ │ └── package-info.java │ │ │ ├── config/ │ │ │ │ ├── GlobalConfig.java │ │ │ │ └── package-info.java │ │ │ ├── enums/ │ │ │ │ ├── SqlKeyword.java │ │ │ │ ├── SqlLike.java │ │ │ │ ├── SqlMethod.java │ │ │ │ ├── WrapperKeyword.java │ │ │ │ └── package-info.java │ │ │ ├── exceptions/ │ │ │ │ ├── MybatisPlusException.java │ │ │ │ └── package-info.java │ │ │ ├── handlers/ │ │ │ │ ├── AnnotationHandler.java │ │ │ │ ├── CompositeEnumTypeHandler.java │ │ │ │ ├── IJsonTypeHandler.java │ │ │ │ ├── MetaObjectHandler.java │ │ │ │ ├── MybatisEnumTypeHandler.java │ │ │ │ ├── PostInitTableInfoHandler.java │ │ │ │ ├── StrictFill.java │ │ │ │ └── package-info.java │ │ │ ├── incrementer/ │ │ │ │ ├── DefaultIdentifierGenerator.java │ │ │ │ ├── IKeyGenerator.java │ │ │ │ ├── IdentifierGenerator.java │ │ │ │ ├── ImadcnIdentifierGenerator.java │ │ │ │ └── package-info.java │ │ │ ├── injector/ │ │ │ │ ├── AbstractMethod.java │ │ │ │ ├── AbstractSqlInjector.java │ │ │ │ ├── DefaultSqlInjector.java │ │ │ │ ├── ISqlInjector.java │ │ │ │ ├── SqlRunnerInjector.java │ │ │ │ ├── methods/ │ │ │ │ │ ├── Delete.java │ │ │ │ │ ├── DeleteBatchByIds.java │ │ │ │ │ ├── DeleteById.java │ │ │ │ │ ├── DeleteByIds.java │ │ │ │ │ ├── DeleteByMap.java │ │ │ │ │ ├── Insert.java │ │ │ │ │ ├── SelectBatchByIds.java │ │ │ │ │ ├── SelectById.java │ │ │ │ │ ├── SelectByIds.java │ │ │ │ │ ├── SelectByMap.java │ │ │ │ │ ├── SelectCount.java │ │ │ │ │ ├── SelectList.java │ │ │ │ │ ├── SelectMaps.java │ │ │ │ │ ├── SelectMapsPage.java │ │ │ │ │ ├── SelectObjs.java │ │ │ │ │ ├── SelectOne.java │ │ │ │ │ ├── SelectPage.java │ │ │ │ │ ├── Update.java │ │ │ │ │ ├── UpdateById.java │ │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ ├── mapper/ │ │ │ │ ├── BaseMapper.java │ │ │ │ ├── Mapper.java │ │ │ │ └── package-info.java │ │ │ ├── metadata/ │ │ │ │ ├── IPage.java │ │ │ │ ├── MapperProxyMetadata.java │ │ │ │ ├── OrderFieldInfo.java │ │ │ │ ├── OrderItem.java │ │ │ │ ├── TableFieldInfo.java │ │ │ │ ├── TableInfo.java │ │ │ │ ├── TableInfoHelper.java │ │ │ │ └── package-info.java │ │ │ ├── override/ │ │ │ │ ├── MybatisMapperMethod.java │ │ │ │ ├── MybatisMapperProxy.java │ │ │ │ ├── MybatisMapperProxyFactory.java │ │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ ├── plugins/ │ │ │ │ ├── IgnoreStrategy.java │ │ │ │ └── InterceptorIgnoreHelper.java │ │ │ ├── spi/ │ │ │ │ ├── CompatibleHelper.java │ │ │ │ └── CompatibleSet.java │ │ │ └── toolkit/ │ │ │ ├── AES.java │ │ │ ├── AnnotationUtils.java │ │ │ ├── AopUtils.java │ │ │ ├── ArrayUtils.java │ │ │ ├── Assert.java │ │ │ ├── BeanUtils.java │ │ │ ├── ClassUtils.java │ │ │ ├── CollectionUtils.java │ │ │ ├── Constants.java │ │ │ ├── EncryptUtils.java │ │ │ ├── ExceptionUtils.java │ │ │ ├── GlobalConfigUtils.java │ │ │ ├── IdWorker.java │ │ │ ├── LambdaUtils.java │ │ │ ├── MybatisBatchUtils.java │ │ │ ├── MybatisUtils.java │ │ │ ├── NetUtils.java │ │ │ ├── ObjectUtils.java │ │ │ ├── ParameterUtils.java │ │ │ ├── PluginUtils.java │ │ │ ├── ReflectionKit.java │ │ │ ├── Sequence.java │ │ │ ├── SerializationUtils.java │ │ │ ├── SetAccessibleAction.java │ │ │ ├── StringPool.java │ │ │ ├── StringUtils.java │ │ │ ├── SystemClock.java │ │ │ ├── TableNameParser.java │ │ │ ├── Wrappers.java │ │ │ ├── package-info.java │ │ │ ├── reflect/ │ │ │ │ ├── GenericTypeUtils.java │ │ │ │ ├── IGenericTypeResolver.java │ │ │ │ ├── SpringReflectionHelper.java │ │ │ │ └── TypeParameterResolver.java │ │ │ ├── sql/ │ │ │ │ ├── SqlInjectionUtils.java │ │ │ │ ├── SqlScriptUtils.java │ │ │ │ ├── SqlUtils.java │ │ │ │ ├── StringEscape.java │ │ │ │ └── package-info.java │ │ │ └── support/ │ │ │ ├── BiIntFunction.java │ │ │ ├── ColumnCache.java │ │ │ ├── IdeaProxyLambdaMeta.java │ │ │ ├── LambdaMeta.java │ │ │ ├── ReflectLambdaMeta.java │ │ │ ├── SFunction.java │ │ │ ├── SerializedLambda.java │ │ │ └── ShadowLambdaMeta.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring-devtools.properties │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ ├── code/ │ │ │ ├── Overwrite.java │ │ │ ├── OverwriteFile.java │ │ │ ├── OverwriteRunner.java │ │ │ └── sub/ │ │ │ └── MapperMethod.java │ │ ├── core/ │ │ │ ├── MethodTest.java │ │ │ ├── MybatisMapperAnnotationBuilderTest.java │ │ │ ├── MybatisXMLConfigBuilderTest.java │ │ │ ├── MybatisXMLLanguageDriverTest.java │ │ │ ├── MybatisXMLScriptBuilderTest.java │ │ │ ├── conditions/ │ │ │ │ ├── AbstractWrapperTest.java │ │ │ │ ├── BaseWrapperTest.java │ │ │ │ ├── LambdaQueryWrapperTest.java │ │ │ │ ├── QueryWrapperTest.java │ │ │ │ ├── UpdateWrapperIncrDecrTest.java │ │ │ │ └── UpdateWrapperTest.java │ │ │ ├── handlers/ │ │ │ │ └── MetaObjectHandlerTest.java │ │ │ ├── incrementer/ │ │ │ │ └── ImadcnIdentifierGeneratorTest.java │ │ │ ├── metadata/ │ │ │ │ └── TableInfoTest.java │ │ │ ├── plugins/ │ │ │ │ └── InterceptorIgnoreHelperTest.java │ │ │ └── toolkit/ │ │ │ ├── MybatisUtilsTest.java │ │ │ ├── SequenceTest.java │ │ │ ├── reflect/ │ │ │ │ └── TypeParameterResolverTest.java │ │ │ ├── sql/ │ │ │ │ ├── SqlInjectionUtilsTest.java │ │ │ │ ├── SqlScriptUtilsTest.java │ │ │ │ └── SqlUtilsTest.java │ │ │ └── support/ │ │ │ └── IdeaProxyLambdaMetaTest.java │ │ └── test/ │ │ ├── EncryptTest.java │ │ ├── MybatisConfigurationTest.java │ │ ├── MybatisMapperAnnotationBuilderTest.java │ │ ├── MybatisParameterHandlerTest.java │ │ ├── Role.java │ │ ├── User.java │ │ ├── entity/ │ │ │ ├── AEntity.java │ │ │ └── BEntity.java │ │ ├── mapper/ │ │ │ ├── AMapper.java │ │ │ └── BMapper.java │ │ ├── metadata/ │ │ │ └── TableInfoHelperTest.java │ │ ├── override/ │ │ │ └── MybatisMapperMethodTest.java │ │ ├── pom/ │ │ │ ├── GeneratePomTest.java │ │ │ └── ReflectionKitTest.java │ │ └── toolkit/ │ │ ├── AESTest.java │ │ ├── AnnotationUtilsTest.java │ │ ├── BeanUtilsTest.java │ │ ├── ClassUtilsTest.java │ │ ├── CollectionUtilsTest.java │ │ ├── LambdaUtilsTest.java │ │ ├── ParameterUtilsTest.java │ │ ├── PluginUtilsTest.java │ │ ├── ReflectionKitTest.java │ │ ├── StringUtilsTest.java │ │ └── TableNameParserTest.java │ └── resources/ │ ├── MybatisXMLConfigBuilderTest.xml │ ├── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ └── mapper/ │ │ ├── AMapper.xml │ │ └── BMapper.xml │ └── mybatis-config-empty.xml ├── mybatis-plus-extension/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── extension/ │ │ │ ├── MybatisMapWrapperFactory.java │ │ │ ├── activerecord/ │ │ │ │ ├── AbstractModel.java │ │ │ │ └── package-info.java │ │ │ ├── conditions/ │ │ │ │ ├── AbstractChainWrapper.java │ │ │ │ ├── ChainWrapper.java │ │ │ │ ├── query/ │ │ │ │ │ ├── ChainQuery.java │ │ │ │ │ ├── LambdaQueryChainWrapper.java │ │ │ │ │ └── QueryChainWrapper.java │ │ │ │ └── update/ │ │ │ │ ├── ChainUpdate.java │ │ │ │ ├── LambdaUpdateChainWrapper.java │ │ │ │ └── UpdateChainWrapper.java │ │ │ ├── ddl/ │ │ │ │ ├── DdlHelper.java │ │ │ │ ├── DdlScript.java │ │ │ │ ├── DdlScriptErrorHandler.java │ │ │ │ ├── IDdl.java │ │ │ │ └── history/ │ │ │ │ ├── IDdlGenerator.java │ │ │ │ ├── MysqlDdlGenerator.java │ │ │ │ ├── OracleDdlGenerator.java │ │ │ │ ├── PostgreDdlGenerator.java │ │ │ │ └── SQLiteDdlGenerator.java │ │ │ ├── handlers/ │ │ │ │ ├── AbstractJsonTypeHandler.java │ │ │ │ ├── Fastjson2TypeHandler.java │ │ │ │ ├── FastjsonTypeHandler.java │ │ │ │ ├── GsonTypeHandler.java │ │ │ │ ├── Jackson3TypeHandler.java │ │ │ │ ├── JacksonTypeHandler.java │ │ │ │ ├── MybatisMapWrapper.java │ │ │ │ └── package-info.java │ │ │ ├── incrementer/ │ │ │ │ ├── DB2KeyGenerator.java │ │ │ │ ├── DmKeyGenerator.java │ │ │ │ ├── FirebirdKeyGenerator.java │ │ │ │ ├── H2KeyGenerator.java │ │ │ │ ├── KingbaseKeyGenerator.java │ │ │ │ ├── LealoneKeyGenerator.java │ │ │ │ ├── OracleKeyGenerator.java │ │ │ │ ├── PostgreKeyGenerator.java │ │ │ │ ├── SapHanaKeyGenerator.java │ │ │ │ └── package-info.java │ │ │ ├── injector/ │ │ │ │ ├── methods/ │ │ │ │ │ ├── AlwaysUpdateSomeColumnById.java │ │ │ │ │ ├── InsertBatchSomeColumn.java │ │ │ │ │ ├── LogicDeleteBatchByIds.java │ │ │ │ │ ├── LogicDeleteByIdWithFill.java │ │ │ │ │ ├── Upsert.java │ │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ ├── p6spy/ │ │ │ │ ├── MybatisPlusLogFactory.java │ │ │ │ ├── MybatisPlusLoggingEventListener.java │ │ │ │ ├── P6SpyLogger.java │ │ │ │ └── StdoutLogger.java │ │ │ ├── package-info.java │ │ │ ├── plugins/ │ │ │ │ ├── MybatisPlusInterceptor.java │ │ │ │ ├── handler/ │ │ │ │ │ └── TableNameHandler.java │ │ │ │ ├── inner/ │ │ │ │ │ ├── DynamicTableNameInnerInterceptor.java │ │ │ │ │ ├── InnerInterceptor.java │ │ │ │ │ ├── OptimisticLockerInnerInterceptor.java │ │ │ │ │ └── ReplacePlaceholderInnerInterceptor.java │ │ │ │ ├── package-info.java │ │ │ │ └── pagination/ │ │ │ │ ├── DialectFactory.java │ │ │ │ ├── DialectModel.java │ │ │ │ ├── Page.java │ │ │ │ ├── PageDTO.java │ │ │ │ ├── dialects/ │ │ │ │ │ ├── DB2Dialect.java │ │ │ │ │ ├── GBase8sDialect.java │ │ │ │ │ ├── GaussDBDialect.java │ │ │ │ │ ├── Hive2Dialect.java │ │ │ │ │ ├── IDialect.java │ │ │ │ │ ├── InformixDialect.java │ │ │ │ │ ├── MySqlDialect.java │ │ │ │ │ ├── Oracle12cDialect.java │ │ │ │ │ ├── OracleDialect.java │ │ │ │ │ ├── PostgreDialect.java │ │ │ │ │ ├── SQLServer2005Dialect.java │ │ │ │ │ ├── SQLServerDialect.java │ │ │ │ │ ├── SybaseDialect.java │ │ │ │ │ ├── TrinoDialect.java │ │ │ │ │ ├── XCloudDialect.java │ │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ ├── repository/ │ │ │ │ ├── AbstractRepository.java │ │ │ │ └── IRepository.java │ │ │ ├── scripting/ │ │ │ │ ├── MybatisFreeMarkerLanguageDriver.java │ │ │ │ ├── MybatisThymeleafLanguageDriver.java │ │ │ │ └── MybatisVelocityLanguageDriver.java │ │ │ ├── spi/ │ │ │ │ ├── CompatibleHelper.java │ │ │ │ └── CompatibleSet.java │ │ │ └── toolkit/ │ │ │ ├── ChainWrappers.java │ │ │ ├── Db.java │ │ │ ├── JdbcUtils.java │ │ │ ├── PropertyMapper.java │ │ │ ├── SimpleQuery.java │ │ │ ├── SqlHelper.java │ │ │ ├── SqlParserUtils.java │ │ │ └── package-info.java │ │ └── kotlin/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── extension/ │ │ └── kotlin/ │ │ ├── AbstractKtWrapper.kt │ │ ├── KtQueryChainWrapper.kt │ │ ├── KtQueryWrapper.kt │ │ ├── KtUpdateChainWrapper.kt │ │ └── KtUpdateWrapper.kt │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ ├── DdlHelperTest.java │ │ ├── DdlScriptTest.java │ │ ├── SqlParserUtilsTest.java │ │ ├── extension/ │ │ │ ├── parsers/ │ │ │ │ └── DynamicTableNameParserTest.java │ │ │ └── plugins/ │ │ │ └── pagination/ │ │ │ └── dialects/ │ │ │ └── IDialectTest.java │ │ ├── handlers/ │ │ │ ├── BaseTypeHandlerTest.java │ │ │ ├── FastJson2TypeHandlerTest.java │ │ │ ├── FastJsonTypeHandlerTest.java │ │ │ ├── GsonTypeHandlerTest.java │ │ │ ├── Jackson3TypeHandlerTest.java │ │ │ ├── JacksonTypeHandlerTest.java │ │ │ ├── MybatisEnumTypeHandlerTest.java │ │ │ └── MybatisMapWrapperTest.java │ │ ├── model/ │ │ │ └── UserBean.java │ │ ├── plugins/ │ │ │ └── pagination/ │ │ │ ├── DialectFactoryTest.java │ │ │ └── SQLServer2005DialectTest.java │ │ └── pom/ │ │ └── GeneratePomTest.java │ └── resources/ │ ├── ddl/ │ │ └── test.sql │ └── logback.xml ├── mybatis-plus-generator/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── generator/ │ │ │ ├── AutoGenerator.java │ │ │ ├── DefaultTableAnnotationHandler.java │ │ │ ├── DefaultTableFieldAnnotationHandler.java │ │ │ ├── FastAutoGenerator.java │ │ │ ├── IFill.java │ │ │ ├── IGenerateMapperMethodHandler.java │ │ │ ├── ITableAnnotationHandler.java │ │ │ ├── ITableFieldAnnotationHandler.java │ │ │ ├── ITemplate.java │ │ │ ├── config/ │ │ │ │ ├── ConstVal.java │ │ │ │ ├── DataSourceConfig.java │ │ │ │ ├── GlobalConfig.java │ │ │ │ ├── IConfigBuilder.java │ │ │ │ ├── IDbQuery.java │ │ │ │ ├── IKeyWordsHandler.java │ │ │ │ ├── INameConvert.java │ │ │ │ ├── IOutputFile.java │ │ │ │ ├── ITypeConvert.java │ │ │ │ ├── InjectionConfig.java │ │ │ │ ├── OutputFile.java │ │ │ │ ├── PackageConfig.java │ │ │ │ ├── StrategyConfig.java │ │ │ │ ├── TemplateConfig.java │ │ │ │ ├── TemplateLoadWay.java │ │ │ │ ├── TemplateType.java │ │ │ │ ├── builder/ │ │ │ │ │ ├── BaseBuilder.java │ │ │ │ │ ├── ConfigBuilder.java │ │ │ │ │ ├── Controller.java │ │ │ │ │ ├── CustomFile.java │ │ │ │ │ ├── Entity.java │ │ │ │ │ ├── GeneratorBuilder.java │ │ │ │ │ ├── Mapper.java │ │ │ │ │ ├── PathInfoHandler.java │ │ │ │ │ ├── Service.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── converts/ │ │ │ │ │ ├── ClickHouseTypeConvert.java │ │ │ │ │ ├── DB2TypeConvert.java │ │ │ │ │ ├── DmTypeConvert.java │ │ │ │ │ ├── FirebirdTypeConvert.java │ │ │ │ │ ├── GaussDBSqlTypeConvert.java │ │ │ │ │ ├── KingbaseESTypeConvert.java │ │ │ │ │ ├── MySqlTypeConvert.java │ │ │ │ │ ├── OracleTypeConvert.java │ │ │ │ │ ├── OscarTypeConvert.java │ │ │ │ │ ├── PostgreSqlTypeConvert.java │ │ │ │ │ ├── SqlServerTypeConvert.java │ │ │ │ │ ├── SqliteTypeConvert.java │ │ │ │ │ ├── TypeConverts.java │ │ │ │ │ ├── package-info.java │ │ │ │ │ └── select/ │ │ │ │ │ ├── Branch.java │ │ │ │ │ ├── BranchBuilder.java │ │ │ │ │ └── Selector.java │ │ │ │ ├── package-info.java │ │ │ │ ├── po/ │ │ │ │ │ ├── LikeTable.java │ │ │ │ │ ├── TableField.java │ │ │ │ │ ├── TableInfo.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── querys/ │ │ │ │ │ ├── AbstractDbQuery.java │ │ │ │ │ ├── ClickHouseQuery.java │ │ │ │ │ ├── DB2Query.java │ │ │ │ │ ├── DMQuery.java │ │ │ │ │ ├── DbQueryDecorator.java │ │ │ │ │ ├── DbQueryRegistry.java │ │ │ │ │ ├── FirebirdQuery.java │ │ │ │ │ ├── GaussDBSqlQuery.java │ │ │ │ │ ├── GaussQuery.java │ │ │ │ │ ├── GbaseQuery.java │ │ │ │ │ ├── H2Query.java │ │ │ │ │ ├── KingbaseESQuery.java │ │ │ │ │ ├── MariadbQuery.java │ │ │ │ │ ├── MySqlQuery.java │ │ │ │ │ ├── OracleQuery.java │ │ │ │ │ ├── OscarQuery.java │ │ │ │ │ ├── PostgreSqlQuery.java │ │ │ │ │ ├── SqlServerQuery.java │ │ │ │ │ ├── SqliteQuery.java │ │ │ │ │ ├── SybaseQuery.java │ │ │ │ │ ├── XuguQuery.java │ │ │ │ │ ├── ZenithQuery.java │ │ │ │ │ └── package-info.java │ │ │ │ └── rules/ │ │ │ │ ├── DateType.java │ │ │ │ ├── DbColumnType.java │ │ │ │ ├── IColumnType.java │ │ │ │ ├── NamingStrategy.java │ │ │ │ └── package-info.java │ │ │ ├── engine/ │ │ │ │ ├── AbstractTemplateEngine.java │ │ │ │ ├── BeetlTemplateEngine.java │ │ │ │ ├── EnjoyTemplateEngine.java │ │ │ │ ├── FreemarkerTemplateEngine.java │ │ │ │ └── VelocityTemplateEngine.java │ │ │ ├── fill/ │ │ │ │ ├── Column.java │ │ │ │ └── Property.java │ │ │ ├── function/ │ │ │ │ └── ConverterFileName.java │ │ │ ├── index/ │ │ │ │ ├── AbstractMapperMethodHandler.java │ │ │ │ ├── DefaultGenerateMapperLambdaMethodHandler.java │ │ │ │ └── DefaultGenerateMapperMethodHandler.java │ │ │ ├── jdbc/ │ │ │ │ └── DatabaseMetaDataWrapper.java │ │ │ ├── keywords/ │ │ │ │ ├── BaseKeyWordsHandler.java │ │ │ │ ├── H2KeyWordsHandler.java │ │ │ │ ├── MySqlKeyWordsHandler.java │ │ │ │ ├── PostgreSqlKeyWordsHandler.java │ │ │ │ └── package-info.java │ │ │ ├── model/ │ │ │ │ ├── AnnotationAttributes.java │ │ │ │ ├── ClassAnnotationAttributes.java │ │ │ │ └── MapperMethod.java │ │ │ ├── package-info.java │ │ │ ├── query/ │ │ │ │ ├── AbstractDatabaseQuery.java │ │ │ │ ├── DefaultQuery.java │ │ │ │ ├── IDatabaseQuery.java │ │ │ │ └── SQLQuery.java │ │ │ ├── type/ │ │ │ │ ├── ITypeConvertHandler.java │ │ │ │ └── TypeRegistry.java │ │ │ └── util/ │ │ │ ├── ClassUtils.java │ │ │ ├── FileUtils.java │ │ │ ├── KotlinTypeUtils.java │ │ │ └── RuntimeUtils.java │ │ └── resources/ │ │ └── templates/ │ │ ├── controller.java.btl │ │ ├── controller.java.ej │ │ ├── controller.java.ftl │ │ ├── controller.java.vm │ │ ├── entity.java.btl │ │ ├── entity.java.ej │ │ ├── entity.java.ftl │ │ ├── entity.java.vm │ │ ├── entity.kt.btl │ │ ├── entity.kt.ej │ │ ├── entity.kt.ftl │ │ ├── entity.kt.vm │ │ ├── mapper.java.btl │ │ ├── mapper.java.ej │ │ ├── mapper.java.ftl │ │ ├── mapper.java.vm │ │ ├── mapper.xml.btl │ │ ├── mapper.xml.ej │ │ ├── mapper.xml.ftl │ │ ├── mapper.xml.vm │ │ ├── service.java.btl │ │ ├── service.java.ej │ │ ├── service.java.ftl │ │ ├── service.java.vm │ │ ├── serviceImpl.java.btl │ │ ├── serviceImpl.java.ej │ │ ├── serviceImpl.java.ftl │ │ └── serviceImpl.java.vm │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ ├── demo/ │ │ │ ├── beetl/ │ │ │ │ ├── controller/ │ │ │ │ │ └── SimpleController.java │ │ │ │ ├── entity/ │ │ │ │ │ └── Simple.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── SimpleMapper.java │ │ │ │ │ └── xml/ │ │ │ │ │ └── SimpleMapper.xml │ │ │ │ └── service/ │ │ │ │ ├── ISimpleService.java │ │ │ │ └── impl/ │ │ │ │ └── SimpleServiceImpl.java │ │ │ ├── enjoy/ │ │ │ │ ├── controller/ │ │ │ │ │ └── SimpleController.java │ │ │ │ ├── entity/ │ │ │ │ │ └── Simple.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── SimpleMapper.java │ │ │ │ │ └── xml/ │ │ │ │ │ └── SimpleMapper.xml │ │ │ │ └── service/ │ │ │ │ ├── ISimpleService.java │ │ │ │ └── impl/ │ │ │ │ └── SimpleServiceImpl.java │ │ │ ├── freemarker/ │ │ │ │ ├── controller/ │ │ │ │ │ └── SimpleController.java │ │ │ │ ├── entity/ │ │ │ │ │ └── Simple.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── SimpleMapper.java │ │ │ │ │ └── xml/ │ │ │ │ │ └── SimpleMapper.xml │ │ │ │ └── service/ │ │ │ │ ├── ISimpleService.java │ │ │ │ └── impl/ │ │ │ │ └── SimpleServiceImpl.java │ │ │ └── velocity/ │ │ │ ├── controller/ │ │ │ │ └── SimpleController.java │ │ │ ├── entity/ │ │ │ │ └── Simple.java │ │ │ ├── mapper/ │ │ │ │ ├── SimpleMapper.java │ │ │ │ └── xml/ │ │ │ │ └── SimpleMapper.xml │ │ │ └── service/ │ │ │ ├── ISimpleService.java │ │ │ └── impl/ │ │ │ └── SimpleServiceImpl.java │ │ └── mybatisplus/ │ │ └── generator/ │ │ ├── config/ │ │ │ ├── DataSourceConfigTest.java │ │ │ ├── DbQueryRegistryTest.java │ │ │ ├── GlobalConfigTest.java │ │ │ ├── InjectionConfigTest.java │ │ │ ├── PackageConfigTest.java │ │ │ ├── StrategyConfigTest.java │ │ │ ├── TemplateConfigTest.java │ │ │ ├── builder/ │ │ │ │ └── ConfigBuilderTest.java │ │ │ ├── converts/ │ │ │ │ ├── GaussDBSqlTypeConvertTest.java │ │ │ │ ├── SqlServerTypeConvertTest.java │ │ │ │ ├── TypeConvertsTest.java │ │ │ │ └── select/ │ │ │ │ └── SelectorTest.java │ │ │ ├── po/ │ │ │ │ ├── TableFieldTest.java │ │ │ │ └── TableInfoTest.java │ │ │ └── rules/ │ │ │ └── NamingStrategyTest.java │ │ ├── engine/ │ │ │ └── TemplateEngineTest.java │ │ ├── entity/ │ │ │ ├── BaseEntity.java │ │ │ └── SuperEntity.java │ │ ├── index/ │ │ │ └── MapperMethodHandlerTest.java │ │ ├── jdbc/ │ │ │ └── DatabaseMetaDataWrapperTest.java │ │ ├── keywords/ │ │ │ ├── H2KeyWordsHandlerTest.java │ │ │ ├── MySqlKeyWordsHandlerTest.java │ │ │ └── PostgreSqlKeyWordsHandlerTest.java │ │ ├── model/ │ │ │ └── ClassAnnotationAttributesTest.java │ │ └── samples/ │ │ ├── BaseGeneratorTest.java │ │ ├── DMGeneratorTest.java │ │ ├── FastAutoGeneratorTest.java │ │ ├── GaussDBGeneratorTest.java │ │ ├── H2CodeGeneratorTest.java │ │ ├── MySQLGeneratorTest.java │ │ ├── OracleGeneratorTest.java │ │ └── PostgreSQLGeneratorTest.java │ ├── kotlin/ │ │ └── com/ │ │ └── baomidou/ │ │ ├── beetl/ │ │ │ ├── controller/ │ │ │ │ └── SimpleController.kt │ │ │ ├── entity/ │ │ │ │ └── Simple.kt │ │ │ ├── mapper/ │ │ │ │ ├── SimpleMapper.kt │ │ │ │ └── xml/ │ │ │ │ └── SimpleMapper.xml │ │ │ └── service/ │ │ │ ├── ISimpleService.kt │ │ │ └── impl/ │ │ │ └── SimpleServiceImpl.kt │ │ ├── enjoy/ │ │ │ ├── controller/ │ │ │ │ └── SimpleController.kt │ │ │ ├── entity/ │ │ │ │ └── Simple.kt │ │ │ ├── mapper/ │ │ │ │ ├── SimpleMapper.kt │ │ │ │ └── xml/ │ │ │ │ └── SimpleMapper.xml │ │ │ └── service/ │ │ │ ├── ISimpleService.kt │ │ │ └── impl/ │ │ │ └── SimpleServiceImpl.kt │ │ ├── freemarker/ │ │ │ ├── controller/ │ │ │ │ └── SimpleController.kt │ │ │ ├── entity/ │ │ │ │ └── Simple.kt │ │ │ ├── mapper/ │ │ │ │ ├── SimpleMapper.kt │ │ │ │ └── xml/ │ │ │ │ └── SimpleMapper.xml │ │ │ └── service/ │ │ │ ├── ISimpleService.kt │ │ │ └── impl/ │ │ │ └── SimpleServiceImpl.kt │ │ └── velocity/ │ │ ├── controller/ │ │ │ └── SimpleController.kt │ │ ├── entity/ │ │ │ └── Simple.kt │ │ ├── mapper/ │ │ │ ├── SimpleMapper.kt │ │ │ └── xml/ │ │ │ └── SimpleMapper.xml │ │ └── service/ │ │ ├── ISimpleService.kt │ │ └── impl/ │ │ └── SimpleServiceImpl.kt │ └── resources/ │ ├── sql/ │ │ └── init.sql │ └── templates/ │ ├── dto.java.ftl │ ├── dto.java.vm │ ├── entity1.java.ftl │ ├── entity1.java.vm │ ├── test.vm │ ├── vo.java.ftl │ └── vo.java.vm ├── mybatis-plus-jsqlparser-support/ │ ├── build.gradle │ ├── mybatis-plus-jsqlparser/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── extension/ │ │ │ ├── DynamicTableNameHandler.java │ │ │ ├── parser/ │ │ │ │ ├── JsqlParserFunction.java │ │ │ │ ├── JsqlParserGlobal.java │ │ │ │ ├── JsqlParserSupport.java │ │ │ │ └── cache/ │ │ │ │ ├── AbstractCaffeineJsqlParseCache.java │ │ │ │ ├── FstFactory.java │ │ │ │ ├── FstSerialCaffeineJsqlParseCache.java │ │ │ │ ├── FuryFactory.java │ │ │ │ ├── FurySerialCaffeineJsqlParseCache.java │ │ │ │ ├── JdkSerialCaffeineJsqlParseCache.java │ │ │ │ └── JsqlParseCache.java │ │ │ └── plugins/ │ │ │ ├── handler/ │ │ │ │ ├── DataPermissionHandler.java │ │ │ │ ├── MultiDataPermissionHandler.java │ │ │ │ └── TenantLineHandler.java │ │ │ └── inner/ │ │ │ ├── BaseMultiTableInnerInterceptor.java │ │ │ ├── BlockAttackInnerInterceptor.java │ │ │ ├── DataChangeRecorderInnerInterceptor.java │ │ │ ├── DataPermissionInterceptor.java │ │ │ ├── DynamicTableNameJsqlParserInnerInterceptor.java │ │ │ ├── IllegalSQLInnerInterceptor.java │ │ │ ├── PaginationInnerInterceptor.java │ │ │ └── TenantLineInnerInterceptor.java │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ ├── JSqlParserTest.java │ │ ├── extension/ │ │ │ ├── parser/ │ │ │ │ ├── JsqlParserSimpleSerialTest.java │ │ │ │ └── cache/ │ │ │ │ └── FstFactoryTest.java │ │ │ └── plugins/ │ │ │ ├── MybatisPlusInterceptorTest.java │ │ │ └── inner/ │ │ │ ├── BlockAttackInnerInterceptorTest.java │ │ │ ├── DataChangeRecorderInnerInterceptorTest.java │ │ │ ├── DataPermissionInterceptorTest.java │ │ │ ├── DynamicTableNameInnerInterceptorTest.java │ │ │ ├── DynamicTableNameJsqlParserInnerInterceptorTest.java │ │ │ ├── IllegalSQLInnerInterceptorTest.java │ │ │ ├── MultiDataPermissionInterceptorTest.java │ │ │ ├── PaginationInnerInterceptorTest.java │ │ │ └── TenantLineInnerInterceptorTest.java │ │ └── pagination/ │ │ └── SelectBodyToPlainSelectTest.java │ ├── mybatis-plus-jsqlparser-4.9/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── extension/ │ │ │ ├── DynamicTableNameHandler.java │ │ │ ├── parser/ │ │ │ │ ├── JsqlParserFunction.java │ │ │ │ ├── JsqlParserGlobal.java │ │ │ │ ├── JsqlParserSupport.java │ │ │ │ └── cache/ │ │ │ │ ├── AbstractCaffeineJsqlParseCache.java │ │ │ │ ├── FstFactory.java │ │ │ │ ├── FstSerialCaffeineJsqlParseCache.java │ │ │ │ ├── JdkSerialCaffeineJsqlParseCache.java │ │ │ │ └── JsqlParseCache.java │ │ │ └── plugins/ │ │ │ ├── handler/ │ │ │ │ ├── DataPermissionHandler.java │ │ │ │ ├── MultiDataPermissionHandler.java │ │ │ │ └── TenantLineHandler.java │ │ │ └── inner/ │ │ │ ├── BaseMultiTableInnerInterceptor.java │ │ │ ├── BlockAttackInnerInterceptor.java │ │ │ ├── DataChangeRecorderInnerInterceptor.java │ │ │ ├── DataPermissionInterceptor.java │ │ │ ├── DynamicTableNameJsqlParserInnerInterceptor.java │ │ │ ├── IllegalSQLInnerInterceptor.java │ │ │ ├── PaginationInnerInterceptor.java │ │ │ └── TenantLineInnerInterceptor.java │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ ├── JSqlParserTest.java │ │ ├── extension/ │ │ │ ├── parser/ │ │ │ │ ├── JsqlParserSimpleSerialTest.java │ │ │ │ └── cache/ │ │ │ │ └── FstFactoryTest.java │ │ │ └── plugins/ │ │ │ ├── MybatisPlusInterceptorTest.java │ │ │ └── inner/ │ │ │ ├── BlockAttackInnerInterceptorTest.java │ │ │ ├── DataChangeRecorderInnerInterceptorTest.java │ │ │ ├── DataPermissionInterceptorTest.java │ │ │ ├── DynamicTableNameInnerInterceptorTest.java │ │ │ ├── DynamicTableNameJsqlParserInnerInterceptorTest.java │ │ │ ├── IllegalSQLInnerInterceptorTest.java │ │ │ ├── MultiDataPermissionInterceptorTest.java │ │ │ ├── PaginationInnerInterceptorTest.java │ │ │ └── TenantLineInnerInterceptorTest.java │ │ └── plugins/ │ │ └── pagination/ │ │ └── SelectBodyToPlainSelectTest.java │ ├── mybatis-plus-jsqlparser-5.0/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── extension/ │ │ │ ├── DynamicTableNameHandler.java │ │ │ ├── parser/ │ │ │ │ ├── JsqlParserFunction.java │ │ │ │ ├── JsqlParserGlobal.java │ │ │ │ ├── JsqlParserSupport.java │ │ │ │ └── cache/ │ │ │ │ ├── AbstractCaffeineJsqlParseCache.java │ │ │ │ ├── FstFactory.java │ │ │ │ ├── FstSerialCaffeineJsqlParseCache.java │ │ │ │ ├── JdkSerialCaffeineJsqlParseCache.java │ │ │ │ └── JsqlParseCache.java │ │ │ └── plugins/ │ │ │ ├── handler/ │ │ │ │ ├── DataPermissionHandler.java │ │ │ │ ├── MultiDataPermissionHandler.java │ │ │ │ └── TenantLineHandler.java │ │ │ └── inner/ │ │ │ ├── BaseMultiTableInnerInterceptor.java │ │ │ ├── BlockAttackInnerInterceptor.java │ │ │ ├── DataChangeRecorderInnerInterceptor.java │ │ │ ├── DataPermissionInterceptor.java │ │ │ ├── DynamicTableNameJsqlParserInnerInterceptor.java │ │ │ ├── IllegalSQLInnerInterceptor.java │ │ │ ├── PaginationInnerInterceptor.java │ │ │ └── TenantLineInnerInterceptor.java │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ ├── JSqlParserTest.java │ │ ├── extension/ │ │ │ ├── parser/ │ │ │ │ ├── JsqlParserSimpleSerialTest.java │ │ │ │ └── cache/ │ │ │ │ └── FstFactoryTest.java │ │ │ └── plugins/ │ │ │ ├── MybatisPlusInterceptorTest.java │ │ │ └── inner/ │ │ │ ├── BlockAttackInnerInterceptorTest.java │ │ │ ├── DataChangeRecorderInnerInterceptorTest.java │ │ │ ├── DataPermissionInterceptorTest.java │ │ │ ├── DynamicTableNameInnerInterceptorTest.java │ │ │ ├── DynamicTableNameJsqlParserInnerInterceptorTest.java │ │ │ ├── IllegalSQLInnerInterceptorTest.java │ │ │ ├── MultiDataPermissionInterceptorTest.java │ │ │ ├── PaginationInnerInterceptorTest.java │ │ │ └── TenantLineInnerInterceptorTest.java │ │ └── pagination/ │ │ └── SelectBodyToPlainSelectTest.java │ └── mybatis-plus-jsqlparser-common/ │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── baomidou/ │ └── mybatisplus/ │ └── jsqlparser/ │ ├── JsqlParserThreadPool.java │ └── enums/ │ └── ExpressionAppendMode.java ├── mybatis-plus-spring/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── extension/ │ │ │ ├── activerecord/ │ │ │ │ └── Model.java │ │ │ ├── ddl/ │ │ │ │ └── SimpleDdl.java │ │ │ ├── repository/ │ │ │ │ └── CrudRepository.java │ │ │ ├── service/ │ │ │ │ ├── IService.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── ServiceImpl.java │ │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ ├── spi/ │ │ │ │ └── SpringCompatibleSet.java │ │ │ ├── spring/ │ │ │ │ ├── MybatisPlusApplicationContextAware.java │ │ │ │ └── MybatisSqlSessionFactoryBean.java │ │ │ └── toolkit/ │ │ │ └── SqlRunner.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ ├── com.baomidou.mybatisplus.core.spi.CompatibleSet │ │ └── com.baomidou.mybatisplus.extension.spi.CompatibleSet │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ └── service/ │ │ └── ServiceTest.java │ └── kotlin/ │ └── com/ │ └── baomidou/ │ └── mybatisplus/ │ └── test/ │ ├── instance.kt │ └── kotlin/ │ ├── BaseDbTest.kt │ ├── ChainWrappersTest.kt │ ├── DbTest.kt │ ├── FixIssue1986.kt │ ├── User.kt │ ├── UserMapper.kt │ └── WrapperTest.kt ├── settings.gradle └── spring-boot-starter/ ├── build.gradle ├── mybatis-plus-boot-starter/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── autoconfigure/ │ │ │ └── MybatisPlusAutoConfiguration.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── additional-spring-configuration-metadata.json │ │ ├── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── spring.factories │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ ├── autoconfigure/ │ │ │ └── ApplicationTestStartSuccess.java │ │ └── test/ │ │ ├── MetadataTest.java │ │ ├── MybatisPlusPropertiesTest.java │ │ ├── entity/ │ │ │ └── Test.java │ │ ├── mapper/ │ │ │ └── TestMapper.java │ │ └── pom/ │ │ └── GeneratePomTest.java │ └── resources/ │ ├── application.yml │ └── mapper/ │ ├── modulea/ │ │ └── testa.xml │ └── moduleb/ │ └── testb.xml ├── mybatis-plus-boot-starter-test/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ └── resources/ │ │ └── META-INF/ │ │ ├── spring/ │ │ │ └── com.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus.imports │ │ └── spring.factories │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ └── autoconfigure/ │ │ ├── MybatisPlusSampleTest.java │ │ ├── MybatisPlusTestApplication.java │ │ ├── Sample.java │ │ └── SampleMapper.java │ └── resources/ │ ├── application.yml │ └── schema.sql ├── mybatis-plus-spring-boot-autoconfigure/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── autoconfigure/ │ │ ├── ConfigurationCustomizer.java │ │ ├── DdlApplicationRunner.java │ │ ├── DdlAutoConfiguration.java │ │ ├── IdentifierGeneratorAutoConfiguration.java │ │ ├── MybatisDependsOnDatabaseInitializationDetector.java │ │ ├── MybatisPlusInnerInterceptorAutoConfiguration.java │ │ ├── MybatisPlusLanguageDriverAutoConfiguration.java │ │ ├── MybatisPlusProperties.java │ │ ├── MybatisPlusPropertiesCustomizer.java │ │ ├── SafetyEncryptProcessor.java │ │ ├── SpringBootVFS.java │ │ ├── SqlSessionFactoryBeanCustomizer.java │ │ └── package-info.java │ └── test/ │ └── java/ │ └── com/ │ └── baomidou/ │ └── mybatisplus/ │ └── test/ │ ├── MetadataTest.java │ └── MybatisPlusPropertiesTest.java ├── mybatis-plus-spring-boot-test-autoconfigure/ │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── baomidou/ │ └── mybatisplus/ │ └── test/ │ └── autoconfigure/ │ ├── AutoConfigureMybatisPlus.java │ ├── MybatisPlusTest.java │ ├── MybatisPlusTestContextBootstrapper.java │ └── MybatisPlusTypeExcludeFilter.java ├── mybatis-plus-spring-boot3-starter/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── autoconfigure/ │ │ │ └── MybatisPlusAutoConfiguration.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── additional-spring-configuration-metadata.json │ │ ├── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── spring.factories │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ ├── autoconfigure/ │ │ │ └── ApplicationTestStartSuccess.java │ │ └── test/ │ │ ├── MetadataTest.java │ │ ├── MybatisPlusPropertiesTest.java │ │ ├── entity/ │ │ │ └── Test.java │ │ ├── mapper/ │ │ │ └── TestMapper.java │ │ └── pom/ │ │ └── GeneratePomTest.java │ └── resources/ │ ├── application.yml │ └── mapper/ │ ├── modulea/ │ │ └── testa.xml │ └── moduleb/ │ └── testb.xml ├── mybatis-plus-spring-boot3-starter-test/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ └── resources/ │ │ └── META-INF/ │ │ ├── spring/ │ │ │ └── com.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus.imports │ │ └── spring.factories │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ └── test/ │ │ └── autoconfigure/ │ │ ├── MybatisPlusSampleTest.java │ │ ├── MybatisPlusTestApplication.java │ │ ├── Sample.java │ │ └── SampleMapper.java │ └── resources/ │ ├── application.yml │ └── schema.sql ├── mybatis-plus-spring-boot4-starter/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── baomidou/ │ │ │ └── mybatisplus/ │ │ │ └── autoconfigure/ │ │ │ └── MybatisPlusAutoConfiguration.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── additional-spring-configuration-metadata.json │ │ ├── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── spring.factories │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── baomidou/ │ │ └── mybatisplus/ │ │ ├── autoconfigure/ │ │ │ └── ApplicationTestStartSuccess.java │ │ └── test/ │ │ ├── MetadataTest.java │ │ ├── MybatisPlusPropertiesTest.java │ │ ├── entity/ │ │ │ └── Test.java │ │ ├── mapper/ │ │ │ └── TestMapper.java │ │ └── pom/ │ │ └── GeneratePomTest.java │ └── resources/ │ ├── application.yml │ └── mapper/ │ ├── modulea/ │ │ └── testa.xml │ └── moduleb/ │ └── testb.xml └── mybatis-plus-spring-boot4-starter-test/ ├── build.gradle └── src/ ├── main/ │ └── resources/ │ └── META-INF/ │ ├── spring/ │ │ └── com.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus.imports │ └── spring.factories └── test/ ├── java/ │ └── com/ │ └── baomidou/ │ └── mybatisplus/ │ └── test/ │ └── autoconfigure/ │ ├── MybatisPlusSampleTest.java │ ├── MybatisPlusTestApplication.java │ ├── Sample.java │ └── SampleMapper.java └── resources/ ├── application.yml └── schema.sql ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true end_of_line = lf ================================================ FILE: .gitee/ISSUE_TEMPLATE.zh-CN.md ================================================ ### 当前使用版本(必填,否则不予处理) ### 该问题是如何引起的?(确定最新版也有问题再提!!!) ### 重现步骤(如果有就写完整) ### 报错信息 ================================================ FILE: .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md ================================================ ### 该Pull Request关联的Issue ### 修改描述 ### 测试用例 ### 修复效果的截屏 ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: 错误报告 description: File a bug report. title: "[错误报告]: 描述" labels: [""] body: - type: markdown attributes: value: | 请确认以下信息: 1. 请按此模板提交issues, 不按模板提交的问题将直接关闭 2. 如果你使用的版本号不是最新版, 那么你的 issue 大概率将会被直接关闭 3. 如果你的问题与该仓库无关或者可以直接在以往 issue 中找到, 那么你的 issue 大概率将会被直接关闭 4. 提交问题务必描述清楚、附上日志, 描述不清导致无法理解和分析的问题 大概率会被直接关闭 - type: checkboxes id: confirm attributes: label: 确认 description: 在提交 issue 之前, 请确认你已经阅读并确认以下内容, 如果 不全部勾选 或 勾选与事实不符 那么你的 issue 大概率将会被直接关闭 options: - label: 我使用的版本是[最新版](https://central.sonatype.com/search?q=g:com.baomidou%20%20a:mybatis-plus&smo=true), 并且使用插件确认过项目里无依赖版本冲突 required: true - label: 我已经在 [issue](https://github.com/baomidou/mybatis-plus/issues) 中搜索过, 确认问题没有被提出过 required: true - label: 我已经修改标题, 将标题中的 **描述** 替换为遇到的问题(不得删除 **描述** 前面的部分) required: true - type: input id: version attributes: label: 当前程序版本 description: 遇到问题时程序所在的版本号 validations: required: true - type: textarea id: what-happened attributes: label: 问题描述 description: 请详细描述你碰到的问题 placeholder: "问题描述" validations: required: true - type: textarea id: logs attributes: label: 详细堆栈日志 description: 问题出现时,程序错误堆栈日志。 render: bash ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: 官网 url: https://baomidou.com/ about: document. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: 功能改进 description: Feature Request title: "[功能改进]: 描述" labels: [""] body: - type: markdown attributes: value: | 请说明你希望添加的功能。 - type: checkboxes id: confirm attributes: label: 确认 description: 在提交 issue 之前,请确认你已经阅读并确认以下内容 options: - label: 我的版本是最新版本, 我的版本号与 [version](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.baomidou%22%20AND%20a%3A%22mybatis-plus%22) 相同, 并且项目里无依赖冲突 required: true - label: 我已经在 [issue](https://github.com/baomidou/mybatis-plus/issues) 中搜索过, 确认问题没有被提出过 required: true - label: 我已经修改标题, 将标题中的 **描述** 替换为你的想法(不得删除 **描述** 前面的部分) required: true - type: textarea id: feature-request attributes: label: 功能改进 description: 请详细描述需要改进或者添加的功能。 placeholder: "功能改进" validations: required: true - type: textarea id: references attributes: label: 参考资料 description: 可以列举一些参考资料,但是不要引用同类但商业化软件的任何内容。 placeholder: "参考资料" ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gradle" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "daily" ================================================ FILE: .github/workflows/gradle.yml ================================================ # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle name: Java CI with Gradle on: push: branches: [ "3.0" ] pull_request: branches: [ "3.0" ] jobs: build: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md - name: Setup Gradle uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - name: Build with Gradle Wrapper run: ./gradlew build # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. # # - name: Setup Gradle # uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 # with: # gradle-version: '8.5' # # - name: Build with Gradle 8.5 # run: gradle build dependency-submission: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md - name: Generate and submit dependency graph uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 ================================================ FILE: .github/workflows/publish.yml ================================================ name: Publish package to the Maven Central Repository on: push: tags: - v* jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build without tests run: ./gradlew build -x test - name: Decode run: | echo "${{secrets.SIGNING_SECRET_KEY_RING_FILE}}" > ~/.gradle/secring.gpg.b64 base64 -d ~/.gradle/secring.gpg.b64 > ~/.gradle/secring.gpg - name: Publish run: ./gradlew publish -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -Psigning.secretKeyRingFile=$(echo ~/.gradle/secring.gpg) env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} ================================================ FILE: CHANGELOG.md ================================================ # CHANGELOG ## [v3.5.16] 2026.1.11 - fix: 修复`Jackson3TypeHandler`自定义`ObjectMapper`无效 - fix: 处理代码生成器`PackageConfig`指定模块为空时拼接错误 - feat: 升级`SpringBoot3`至3.5.9 - feat: 升级`JUnit`单元测试 - feat: 升级`fastjson`至2.0.60 - feat: 升级`jackson`至2.20.1 - feat: 升级`gson`至2.13.2 - feat: 升级`postgresql`至42.7.8 - feat: 升级`h2database`至2.4.240 - feat: 升级`mysql-connector-j`至9.5.0 - feat: 升级`sqlite-jdbc`至3.51.1.0 - feat: 升级`jaybird`至5.0.10.java8 - feat: 升级`mybatis-spring`4.0.0 ## [v3.5.15] 2025.11.30 - fix: 修复`Enjoy`模板生成xml错误 - feat: 调整代码生成器元数据构建 - feat: 优化`CrudRepository`批量执行前判断非事务中关闭连接 - feat: 支持`SpringBoot`4.0.0 - feat: 支持`Jackson`3.0 ## [v3.5.14] 2025.08.29 - feat: 增加`bom`对`mybatis-plus-spring-boot4-starter`与`mybatis-plus-spring-boot4-starter-test`管理 ## [v3.5.13] 2025.08.29 - fix: 修复在`Spring`中使用`@PostConstruct`调用`Db`方法查询出现警告日志 - fix: 修复`Db`使用`count`返回`null`导致的空指针异常 - fix: 修复`BaseMapper`在非`Spring`项目中报错 - feat: 升级`Jsqlparser`5.2 - feat: `OrderItem`增加`withExpression`根据表达式排序(注意:不支持序列化方式使用,自行控制`sql`注入) - feat: `OracleDdlGenerator`支持指定`schema`模式运行 - feat: 适配华为云`GaussDb`数据库 - feat: 雪花生成器初始化错误增加降级处理 - feat: 新增`spring-boot4`支持 - feat: 升级`gradle`8.13 - opt: `SqlUtils`中`replaceSqlPlaceholder`方法增加缓存处理 - opt:优化`DDL`执行记录表检查是否存在 - opt: 容器环境下`workerId`获取优化 ## [v3.5.12] 2025.04.27 - fix: 修复批量操作异步执行首次可能的出现`NoSuchElementException`错误 - fix: 修复默认`SQL`解析线程池在`JVM`退出关闭导致的任务拒绝 - fix: 修复`entity.java.btl`生成`toString`方法样式错误 - fix: 修复`entity.java.ftl`模板类注释与导包缺少换行 - opt: 重构`SqlRunner`执行`SQL`语句 (动态传参,不再根据参数值生成执行`SQL`) - opt: 增强`SqlRunner`执行(支持单参数使用`Map`({key}),`List`({index}),`JavaBean`({property})获取值) - opt: 改进`MybatisUtils`对自`SqlSessionFactory`的提取(支持自定义`SqlSessionTemplate`子类) - opt: 自动识别数据库支持`TDengine`数据库`websocket`连接 - opt: 支持`Db`工具类对多数据源的支持 - opt: 优化`MapperProxy`属性访问 - opt: `CompatibleSet`接口增加`getBean`与`getProxyTargetObject`方法 - opt: `CompatibleSet`与`CompatibleHelper`调整至`com.baomidou.mybatisplus.core.spi`包之中 - opt: 支持手动指定`CompatibleSet`实现 - opt: 代码生成器处理驱动返回索引信息`null` - opt: 代码生成器处理`PRIMARY_KEY_`为开头的主键索引情况 - opt: 去除`entity.kt.btl`模板`@Override`注解 - opt: 解决`serviceImpl.java.ej`生成格式不统一 - opt: 去除`mapper.java.ftl`多余的换行生成 - opt: 去除`entity.kt.vm`,`entity.kt.ej`,`entity.kt.btl`导包结束分隔符 - opt: 去除`controller.java.ej`,`controller.java.vm`多余的换行 - opt: 去除`entity.kt.btl`生成属性多余的空格 - opt: 统一`entity.java.btl`,`entity.java.ej`,`entity.java.ftl`,`entity.java.vm` 生成的`toString`方法样式 ## [v3.5.11] 2025.03.23 - fix: 修复代码生成器链式模型非`lombok`下生成了`@Accessors`注解 - fix: 修复主键使用`UUID`执行批量删除错 - fix: 修复`Kotlin`使用`select(predicate)`方法错误 - fix: 修复`AbstractCaffeineJsqlParseCache`异步产生的错误 - fix: 修复动态SQL解析包含SQL注释(--或#)导致的合并错误 (动态脚本语句不再处理换行,如果需要去除换行请自行处理) - fix: 修复`DataChangeRecorderInnerInterceptor`数据比较出现强转异常 - fix: 修复`IllegalSQLInnerInterceptor`拦截插件获取`catalog`与`schema`错误 - fix: 修复动态表解析`create table if not exists` 获取表名错误 - fix: 修复动态表解析`create [type] index` 获取表名错误 - feat: 新增`DynamicTableNameJsqlParserInnerInterceptor` 基于`JsqlParser`动态表处理 - feat: 支持`DdlScript`自定义脚本运行器参数 - feat: 支持`DdlHelper`自定义脚本运行器参数 - feat: 支持`DdlApplicationRunner`参数配置(脚本错误处理,自定义`ScriptRunner`,多处理器执行异常是否中断) - feat: 支持`BaseMultiTableInnerInterceptor`指定追加条件模式 (默认条件追加至末尾,仅作用于`select`,`delete`,`update`) - feat: 支持生成器`Entity`指定`serialVersionUID`添加`@Serial`注解 - feat: 支持生成器`Entity`注解(字段,类注解)自定义处理 - feat: 支持生成器`Entity`导包自定义处理 - feat: 支持`崖山`数据库 - feat: 支持`Hive2`分页 - feat: 升级`Gradle`至8.10 - feat: 支持`DdlHelper`执行自定义异常处理 - opt: 调整`DynamicTableNameInnerInterceptor`表处理逻辑并保证`hook`运行 - opt: 调整`DdlScript`类方法实现(分离DDL版本记录,优化执行方法) - opt: 调整`DbType#GAUSS`数据库名为`gauss` - opt: 调整`JsqlParserGlobal`解析线程池指定 - opt: 移除过时的`FieldStrategy.IGNORED` - opt: 移除过时的`GlobalConfig.DbConfig#selectStrategy` - opt: 移除过时的`MybatisSqlSessionFactoryBean#typeEnumsPackage` - opt: 优化`DdlHelper`资源加载(不再依赖`Spring`或者其他实现) - opt: 去除`DdlHelper`中`getScriptRunner`方法指定的字符集编码 - doc: 修正`DdlHelper`中注释错误 由于`jsqlParser`5.0版本与5.1版本升级不兼容性不是很大,计划后期移除`mybatis-plus-jsqlparser-5.0`支持模块。 多版本支持相对来说比较麻烦,后期只维护`mybatis-plus-jsqlparser-4.9` 与 `mybatis-plus-jsqlparser`(保持最新版跟进,直到再提升jdk) ## [v3.5.10.1] 2025.01.13 - fix: 修复动态节点处理错误 ## [v3.5.10] 2025.01.12 - fix: 修复字段有`TableField`注解但未指定`value`值下全局`columnFormat`未生效问题 - fix: 修复enjoy模板生成kotlin代码报错 - fix: 修复enjoy模板生成字符串代码报错 - fix: 修复springdoc生成注解未转义双引号 - fix: 修复数据变动插件更新无主键报错 - fix: 修复多表解析processJoins解析表出现越界 - feat: TableName注解新增`properties`属性 - feat: 支持@InterceptorIgnore注解在default方法上 - feat: 适配jsqlparser5.1版本(5.0兼容版本请使用`mybatis-plus-jsqlparser-5.0`) - feat: 提供`InterceptorIgnoreHelper.execute`模板执行方法处理插件跳过策略(防止手动使用handle方法出现未清理线程资源造成的错误) - feat: 代码生成器全局package配置属性支持自定义模板信息获取 - feat: 代码生成器新增表索引信息获取 - feat: 代码生成器提供`Mapper.Builder.generateMapperMethodHandler`处理器基于索引生成索引方法 - feat: 代码生成器Entity支持自定义Class注解和字段注解生成 - feat: 代码生成器Entity支持lombok模式指定生成类注解 - feat: 代码生成器Entity支持ToString`(Entity.Builder.toString(boolean))`方法控制生成 (默认生成,lombok模式下将会生成@ToString,低版本下lombok不生成,属于不兼容改动) - feat: 代码生成器Entity支持字段文档注释(`Entity.Builder.fieldUseJavaDoc(boolean)`)控制生成 (默认生成,低版本下,使用swagger或springdoc不会生成字段文档注释,属于不兼容改动) - feat: 重写动态语句生成(生成执行SQL将不再包含\n换行符) - feat: 安全加密处理器密钥获取支持环境变量与系统属性传入 - feat: 升级mybatis至3.5.19 - feat: 升级springboot至3.4.1 - feat: 升级kotlin至2.1.0 - 实用性低,检查语法不完善,计划移除IllegalSQLInnerInterceptor插件 - 功能缺陷较多,计划移除DataChangeRecorderInnerInterceptor插件 ## [v3.5.9] 2024.10.23 - opt: 优化代码生成器支持可视化配置生成能力 - opt: 解耦扩展包不再强制依赖 spring 开发框架 - opt: 拆分jsqlparser支持模块,提供mybatis-plus-jsqlparser(支持最新jsqlparser)与mybatis-plus-jsqlparser-4.9模块, 默认不携带,升级后需要自行引入. - feat: 重构 service 模块抽象为 CrudRepository 不再建议使用 IService 避免业务层数据混乱 - feat: 新增 solon 启动插件支持 - feat: 升级SpringBoot3.3.4 - feat: 升级velocity2.4 ## [v3.5.8] 2024.09.18 - fix: 解决optimizeJoinOfCountSql反序列化不支持问题 - fix: 解决Db工具类批量操作使用rewriteBatchedStatements=true返回值不准确 - fix: 修复逻辑删除填充与乐观锁冲突 - fix: 修复IllegalSQLInnerInterceptor分析嵌套count语句错误 - fix: 升级jsqlParser5.0 解决 for update 语句错误 - fix: 修复处自增自减负数情况导致jsqlParser解析优化错误 - fix: 修复removeMapper缓存清理不完全 - fix: 修复SqlServerQuery查询表注释乱码 - opt: 完善函数注入校验逻辑 - opt: Page属性访问调整为private,重写toString方法 - opt: 主键生成策略(uuid)不支持的类型打印警告日志 - opt: MybatisPlusException转化为PersistenceException子类 - feat: 增加deleteByIds空集合处理 - feat: 重命名selectBatchIds方法为selectByIds - feat: 支持tableName与schema属性占位符配 - feat: 代码生成器增加对虚拟列的属性获取 - feat: chain wrapper to lambda chain wrapper #6314 - feat: 代码生成器增加手动指定数据库驱动兼容不能自动注册的驱动实现 - feat: 升级kotlin2.0.0 - feat: 升级SpringBoot3.3.2 - feat: 升级fastjson2.0.52 - feat: 升级mybatis-spring3.0.4 - feat: 升级spring-cloud-commons4.1.4 - feat: 部分支持依赖升级更新 - feat: 支持GoldenDB数据库 - feat: 支持Duckdb数据库 - feat: 支持Derby数据库 - feat: 支持Vastbase数据库 ## [v3.5.7] 2024.06.10 - fix: 修复动态表名处理 update ignore 错误 - fix: 修复SQLServer2005分页处理空格错误 - fix: 修复多租户查询出现问题 - fix: 修正非通用泛型情况下序列化json减少强转 - fix: 修复代码生成器禁用模板失效 - fix: 修复分页count优化distinct搭配orderBy处理错误 - fix: 修复达梦数据库生成代码错误 - fix: 修复租户插件特殊exists语句会失效 - fix: 修复sqlite数据库ddl_history错误导致无法创建表 - fix: 修复DataChangeRecorderInnerInterceptor在Insert时配置忽略无效 - fix: 修复代码生成器处理不标准的JdbcType导致空指针错误 - feat: BaseMapper新增批量操作与InsertOrUpdate方法 - feat: BaseMapper新增批量操作方法返回值List - feat: BaseMapper方法逻辑删除默认支持填充 - feat: 调整Service层逻辑删除填充逻辑处理 - feat: 重构批量删除参数填充处理逻辑. - feat: 自增自减处理BigDecimal - feat: 新增雪花ID配置(支持手动分配workerId与datacenterId或指定网卡信息自动获取方式) - feat: 重构ServiceImpl泛型参数提取 - feat: 修改AES密钥随机性生成 - feat: UpdateWrapper增加checkSqlInjection方法 - feat: 调整DDL脚本自动装配逻辑(当无实现时或无mybatis-plus-extension模块时不注入DDL运行bean) - feat: 注入方法deleteBatchIds重命名deleteByIds - feat: SpringBoot升级至2.7.18和3.2.6 - feat: 升级kotlin至1.9.24 - feat: 升级lombok至1.18.32 ## [v3.5.6] 2024.04.08 - fix: 修复通用Service多层代理引发的错误 - fix: 修复Json类型处理器反序列化泛型丢失原始类型 - fix: 修复填充器处理器基本类型数组出现强制错误 - fix: 修复上版本移除掉Page方法保留至PageDto类之中 - fix: 修复IllegalSQLInnerInterceptor未处理Parenthesis - fix: 修复IllegalSQLInnerInterceptor表名或字段名包裹导致无法获取索引信息和索引字段校验问题 - fix: 修复KtUpdateChainWrapper调用setSql的时候params没有展开 - fix: 修复useGeneratedShortKey配置失效 - fix: 修复DataChangeRecorderInnerInterceptor一系列问题 - feat: 去除sqlFirst与sqlComment转义(如有需要转义操作,请手动调用转义后传入) - feat: ServiceImpl修改为抽象类,防止错误直接实例化 - feat: 重构代码生成器TemplateConfig配置,模板禁用与路径配置更改至对应具体实现之上 - feat: 支持组合注解 - feat: 新增 LambdaUpdateWrapper 字段自增 setIncrBy 自减 setDecrBy 方法 - feat: 获取注入方法时传递org.apache.ibatis.session.Configuration - feat: 新增自增主键兼容配置开关(mybatis-plus.global-config.db-config.insert-ignore-auto-increment-column 默认false,开启INSERT语句无视主键字段生成) - feat: 新增参数填充器跳过方式(基于MappedStatement#id) - feat: 新增SQLite的DDL自动维护功 - feat: 新增eqSql方法 - feat: 新增SQL解析线程池 - feat: 增加雪花ID生成器初始化日志打印(默认超过5秒打印警告日志) - feat: 升级mybatis至3.5.16 - feat: 升级spring-cloud-commons - feat: 升级jsqlparser至4.9 - test: Github增加CI - doc: 增加update(Wrapper)相关api无法自动填充注释 ## [v3.5.5] 2023.12.24 - fix: 修复配置databaseId失效 - fix: 修复自增主键忽略注入错误忽略非自增主键注入问题 - fix: 修复ChainWrapper模式下GroupBy生成多的逗 - fix: 修复selectOne缓存问题 - fix: 修复数据权限多表支持在某些场景下失效问题 - fix: 修复生成器mysql类型转换器point转换错误 - fix: 修复kotlin下无法使用父类属性操作 - fix: 修复自动注入DdlApplicationRunner返回null导致的高版本springboot启动错误 - fix: 修复生成器代码提示的RuntimeUtils安全性漏洞问题 - feat: 新增fastjson2支持 - feat: 升级gradle-wrapper至8.4 - feat: 升级kotlin-gradle-plugin至1.9.21 - feat: 升级mybatis至3.5.15 - feat: 升级lombok至1.18.30 - feat: 升级spring-boot3至3.2.0 - feat: 升级spring-boot2版本mybatis-spring至2.1.2 - feat: 升级spring-boot3版本mybatis-spring至3.0.3 - feat: 移除通用service中saveOrUpdate的事务 - feat: 支持Trino,Presto,GBase8s-pg,SUNDB数据库 ## [v3.5.4.1] 2023.11.4 - fix: 修复Aop增强Mapper层导致的转换错误. ## [v3.5.4] 2023.10.22 - fix: 修复Insert无字段时执行SQL报错. - fix: 修复高版本JDK下lambda无法执行IDEA调试. - fix: 修复LambdaQuery中select,groupBy,orderBy,orderByAsc,orderByDesc提示的警告,新增对应doXxx方法支持重写(不兼容改动,api方法做了final处理). - fix: 修复inject-sql-session-on-mapper-scan无配置提示. - fix: 修复@OrderBy搭配@TableId排序字段错误(不兼容改动,com.baomidou.mybatisplus.core.metadata.TableInfo.orderByFields调整了类型). - fix: 修复Service中根据主键逻辑删除时类型不匹配导致的错误. - fix: 修复分页插件Count与自定义ResultHandler冲突. - fix: 修复字段填充处理器可能会出现重入问题 - feat: 新增自增主键字段是否允许插入控制,可使用方法注入覆盖Insert(boolean ignoreAutoIncrementColumn)或Insert(String name, boolean ignoreAutoIncrementColumn)控制自增主键是否支持写入行为. - feat: ActiveRecord模式下deleteById(逻辑删除)方法支持自动填充功能. - feat: 内置泛型提取,支持非Spring体系项目使用. - feat: BaseMapper新增update(wrapper)更新方法. - feat: BaseMapper新增流式查询方法对大数据查询支持. - feat: 代码生成器元数据信息公开tableName与columnName字段访问. - feat: 新增mybatis-plus-spring-boot3-starter与mybatis-plus-spring-boot3-starter-test支持SpringBoot3. - feat: 支持插件缺省注入,当无MybatisPlusInterceptor注入时,支持com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor自动注入. - feat: 升级源码Jdk开发版本至Java21. - feat: 升级gradle-wrapper至8.4-rc-1. - feat: 升级kotlin-gradle-plugin至1.9.20-Beta. - feat: 升级SpringBoot2.x版本至2.7.15. - feat: 升级lombok至1.18.30. - opt: mybatis-plus-extension中mybatis-spring依赖修改为可选依赖(不兼容改动,如果项目在非spring或非springBoot下使用到了请手动添加依赖). - opt: spring-boot-starter减少无用的配置提示(不兼容改动,调整了com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties.configuration类型). - opt: 字段填充处理器提取去除固定参数提取,支持更宽松的mapper方法参数提取填充处理, - opt: 去除com.baomidou.mybatisplus.core.toolkit.ReflectionKit.setAccessible方法调用,防止高版本Jdk移除 - opt: 调整selectOne方法(配合流式处理,最多提取两行数据,日志不再打印总记录数). - opt: 优化selectObjs方法返回值,减少类型强制转换. - opt: 通用Service支持多SqlSessionFactory注入. - opt: 优化TableInfo.newInstance创建实例方法. - opt: 去除多余的@SuppressWarnings("serial") ## [v3.5.3.2] 2023.08.08 - feat: 升级mybatis至3.5.13,mybatis-spring至2.1.1 - feat: jsqlparser提供统一解析类,可配置解析函数,并加入缓存选项 - feat: 增加Sequence初始化debug日志 - feat: 参数填充器支持多参数填充 - feat: BaseMapper新增selectMaps(page, wrapper)与selectList(page, wrapper)方法 - feat: 乐观锁字段支持 java.time.Instant - feat: `wrapper#apply`支持配置`mapping`比如`column={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}` - feat: 调整 QueryWrapper 需要主动开启检查 SQL 注入过滤(移除掉wrapper的orderby的sql过滤功能) - feat: 新增星瑞格数据库支持 - feat: `updateWrapper#setSql`方法支持`动态入参`参考`wrapper#apply`方法 - feat: 自动 SQL 维护 DDL 支持 SQL 执行存储过程 - perf: `ktWrapper`加强泛型限制 - fix: 修复在选择springdoc文档注释时entity描述异常 - fix: 在主键的`IdType`为`AUTO`的情况下,`Table#getAllInsertSqlColumnMaybeIf("xx.")`所生成sql错误问题 - fix: 租户插件支持`update set subSelect`的情况 - fix: 修复高版本Jdk提示非法反射警告(Illegal reflective access by com.baomidou.mybatisplus.core.toolkit.SetAccessibleAction) - fix: 修复高版本Jdk插件动态代理反射错误 (Unable to make field protected java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.h accessible) - fix: 修复路径替换将原有的“.”替换成了文件分隔符“/” - fix: 修复Beetl模板引擎无法生成注释 - fix: 修复Types.DOUBLE类型无法映射 - fix: 修复转换父类公共字段报错 - fix: 修复生成器无法通过cfg.取值 - fix: 修复单元测试下MockBean时事务回滚失败 - fix: 修复Warpper类nonEmptyOfWhere方法命名不规范,导致Ognl未正确缓存带来的执行开销 - fix: ClickHouseQuery类的tableComment()方法返回表注释字段为comment - fix: 修复在选择springdoc文档注释时entity描述异常问题 - fix: Table\#getAllInsertSqlColumnMaybeIf("xx.")下的sql生成错误问题 - fix: Db类增加根据实体不为空的字段条件查询方法重载 - fix: 生成器对于Kotlin的Entity文件的superEntityClass的错误 - fix: 修复springdoc freemarker模式下 表注释取值取不到 - opt: 增强参数填充处理器,防止因参数名称与填充名称一致类型不匹配导致转换错误 - opt: 优化方法注入,去除SelectPage,SelectMapsPage,SelectByMap,DeleteByMap注入 - opt: 减少MappedStatement堆内存占用 - opt: 解决PluginUtils重复获取元数据带来的性能消耗 - opt: 注入方法去除多余的换行符 - opt: 去除SqlRunner持有的sqlSessionFactory变量 - opt: 解决Sequence初始化多次问题(自定义情况下可不创建默认主键生成器) - opt: 优化 SqlHelper#getMapper 返回泛型 - opt: 去除SqlRunner持有的sqlSessionFactory变量 - docs: 修正DdlHelper注释错误 ## [v3.5.3.1] 2022.12.29 - bug:生成模块pg和dm语句模式名增加 - feat: 优化 ChainWrapper#getEntityClass - fix: 修复在 IService.lambdaQuery().one() 使用场景在数据库无数据时报错问题 - est 以及租户插件解析sql遇到多表必须给表起别名 ## [v3.5.3] 2022.12.28 - 多租户插件:多表join表名必需起别名,否则追加的过滤条件不带前缀 - InterceptorIgnore 不能过滤 selectKey 的问题 - 分页新增`informix数据库`支持 - 分页新增`优炫数据库`支持 - 分页新增`TDengine数据库`支持 - 分页新增`亚马逊redshift数据库`支持 - 支持spring-boot 2.7以上版本 - 雪花id新增反解时间戳方法`Sequence#parseIdTimestamp` - BaseMapper.selectCount生成语句加入中`AS total` - 修复IllegalSQLInnerInterceptor类ClassCastException异常,并优化日志 - 移除注解`OrderBy`的过时属性`isDesc` - 移除`TableInfo`过时方法 - 加入`JoinTableInfoInitHandler`类参与`TableInfo`初始化 - 修复StringUtils.sqlInjectionReplaceBlank方法过滤sql不全,可能会导致sql注入的情况 - 增加IService.lambdaQuery(entity)支持,写法更便捷 - 新增数据变更记录(数据审计)插件`DataChangeRecorderInnerInterceptor` - 新增查询条件方法 notLikeLeft 和 notLikeRight - 数据权限多表解析部分处理优化 - 允许子类重写 orderBy 基础方法 gitee issues/I61F51 - 新增Db类,调整 SimpleQuery 类 - 新增脚本自动维护功能 - 新增支持手动拦截器忽略策略,例如 `InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());` - 支持 PG 数据字段大写 ID 自增 fixed issues/I4T0YJ - 代码生成器重构完成,合并回 MP 核心代码库 - 代码生成器增加是否生成service接口的开关 ## [v3.5.2] 2022.06.01 - 升级 mybatis 3.5.10 - 升级 jsqlparser 4.4 - 添加 vertical 数据库分页支持 - 添加对Gbase 8s 数据库支持 - 添加对 行云 数据库分页的支持 - 添加对 Firebird 数据库分页的支持 - 修复参数填充判断错误,标记替换字段常量 - DbType 清理以及 IDialect 实现类的清理 - 新增SqlHelper.execute,通过entityClass获取BaseMapper - 枚举处理优化,不再需要'typeEnumsPackage'这个配置 - fix 租户id获取的执行顺序 - 新增Firebird数据库的KeyGenerator - 新增达梦Dm数据库的KeyGenerator - Merge pull request #4343 from LK820/fix-IdType.java - Merge pull request #4495 from nieqiurong/fix-parameter - Merge pull request #4314 from tomalloc/3.0 ## [v3.5.1] 2022.01.25 - 新增 impala 数据库支~~持 - 缓存动态获取数据库类型~~ - 新增可控分配 id 方法 fixed github pull/4231 - 延迟枚举扫描注册 - 乐观锁插件支持根据wrapper填充 github pull/3664 - H2KeyGenerator 语法修改 - SimpleQuery 优化及Bug修改 - fixed gitee issues/I4P9EN - SybaseDialect 关键词替换优化 ## [v3.5.0] 2022.01.01 - 升级 mybatis 3.5.9 - 升级 jsqlparser 4.3 - 新增移除 Mapper 相关缓存,支持 GroovyClassLoader 动态注入 Mapper - 添加动态表名的钩子函数 https://github.com/baomidou/mybatis-plus/pull/3965 - 注入类 DefaultSqlInjector 优化调整 - 反射类 ReflectionKit 优化 field -> field 改为 Function.identity() - baseMapper 新增方法 exist 方法 - 解决 sysbase 小写 from 导致 index 取不到正确的索引值问题 - 新增通过 entityClass 获取 Mapper 方法 `BaseMapper mapper = SqlHelper.getMapper(Entity.class);` - 注入方法 byId 注入优化 - 多租户 right join bug https://gitee.com/baomidou/mybatis-plus/issues/I4FP6E https://github.com/baomidou/mybatis-plus/pull/4035 - 自定义注入方法名优化 https://github.com/baomidou/mybatis-plus/pull/4159 - 新增 sap hana 内存数据库 - 新增 SimpleQuery 工具栏查询 - SQL 注入验证工具类 代码修改写法 - 整理字符串常量的使用 - upgrade license-gradle-plugin version - 自定义注入方法名优化 (不兼容) - 重载columnsToString方法允许子类调整 - 修复 et 判断逻辑 fixed gitee issues/I4L4XV - 逻辑删除 byId 支持转换为实体删除填充 ## [v3.4.3.4] 2021.09.22 - order by wrapper 存在条件不排序问题处理 - 解决引入 cloud InetUtils 类编译错误 - 升级 sql 解析依赖 jsqlparser 版本 4.2 - fix: 修复 JDK16 中增加模块化校验后,导致 lambda 序列化失败问题 - fix: java 17 的支持 #I4A7I5 - bug: fix left join 条件构造会多一个的问题 - fix: 当逻辑删除字段默认值为null时,阻止全表更新插件失效 - 分页 count(*) as total - 允许注入自定义事务工厂 TransactionFactory ## [v3.4.3.3] 2021.09.05 - 移除不在实用类 ISqlParserFilter AbstractJsqlParser 需要使用从旧版本复制 - 移除全局配置workerId,datacenterId参数,推荐直接初始化identifierGenerator - count 方法 Integer 修改为 Long 涉及升级成本【注意】,对于涉及缺陷调整给您造成困扰表示抱歉 - 修复主键 @0rderby 注解 bug - 修复 String 主键删除失败 - 主键类型增加 BigDecimal BigInteger 支持 - 隔离 spring 框架强依赖,非 spring 框架用 mp 注入 GenericTypeUtils.setGenericTypeResolver ## [v3.4.3.2] 2021.08.21 - 增加 goldilocks 数据库 csiidb 数据库 的支持 - 增加对南大通用GBase 8s数据库的支持(GBASEDBT),区别于原有定义(GBASE) - 优化 selectOne 查询方式,精简 SQL 注入 - PropertyMapper.whenNotBlack to whenNotBlank - BaseMapper新增deleteById(T entity)方法 - jsqlparser 版本 4.0 升级 4.1 - TableInfo新增原生Reflector反射操作. - 解决 lambda 构造器在 JDK16 中无法运行的问题 - wrapper clear 将sqlSegment重置为空串 缓存标志重置为true - 注入器调整无主键不注入ById方法 - 自动构建 resultMap 处理主键获取真正的字段名 - Wrapper optimized: 优化警告 - Wrapper 新增 gtSql geSql ltSql leSql 方法 - 新增对CUBRID数据库的支持 - fix github pull/3557 乐观锁新增版本号 null 自定义异常,租户插入忽略逻辑允许自定义 - fix github issues/2931 解决结果集大于 Integer 异常问题 - fix github issues/3652 k8s 网络获取失败问题 - fix gitee issues/I3Z2RG 优化 Order By SQL 注入识别率 - fix gitee issues/3826 优化动态表名处理器 - fix gitee issues/I3UQH5 修复注解@OrderBy,使用limit 异常 - fix github issues/3768 mysql 批量自增 bug - 修复自动构建resultMap时主键字段映射错误&OrderBySegmentList懒加载执行 - 源代码升级相关测试依赖,构建环境 gradle 升级为 7.1 新增更多测试用例 ## [v3.4.3.1] 2021.06.15 - 支持多重继承获取泛型 - 应要求 pageDto 修改为 PageDTO - 分页排序优化 - TableField 新增 ResultMapping#property 注解支持 - fixed github pull/3550 优化排序 - fix #I3T0LA - 开放KtUpdateChainWrapper、KtQueryChainWrapper的继承 - 新增 exists 方法判断 count 存在 - 优化数据方言获取方式减少对象创建 - feat GlobalConfig增加whereStrategy属性和适配selectStrategy的getWhereStrategy()方法 - 扩展 p6spy 优化 - fix github#3390 SqlRunner.selectPage()方法未释放连接克隆 - 优化 JDK 默认不推荐泛型数组 - perf: 替换为 JVM 中本身的方法 - 当用户指定ID时,不用自动生成,不指定时自增 - Github Merge pull request #3549 #3555 #3565 #3571 #3587 #3591 #3592 #3595 #3599 #3605 #3606 - 提供处理Map多key取值工具方法 - 调整 page 注解泛型 E 为 P 方便阅读 - Pattern定义为静态常量,优化正则匹配速度 - Fix 主键添加@OrderBy无效 - 去除addMappedStatement日志打印 - NoKeyGenerator Jdbc3KeyGenerator shared instance ## [v3.4.3] 2021.05.21 - 增加瀚高数据库支持 - 增加注解 Order By 支持默认排序 - Wrapper exists notExists orderBy groupBy 支持参数绑定 - Wrapper 支持 setParamAlias 其它优化 - 优化 KeyGenerator 支持多实现多数据源注入 - 增强 ServiceImpl 泛型推断,解决多继承与代理问题 - 新增 PageDto 用于微服务对象传输序列化 - 新增 Page 提供静态 of 构造方式 - 增加代理 MethodHandleProxies 对 lambda 调试支持 - 调整 ActiveRecord 日志对象初始化 - 调整 ActiveRecord 模式 Model 类开发 pkVal 方法外部可用 - 删除标记过时代码 - 优化枚举值获取方式 - 分页 count 安全处理 - Sequence 方法支持重写支持 - 升级 Mybatis 3.5.7 - 修复自动配置 lazy-initialization 无属性提示 - 修复 mysql on duplicate key update 字段名判断为表名问题 - 修复 lambda 条件 npe 异常 - 重构 lambda 信息提取方法 - 获取 lambda 信息不在序列化 - 合并 gitee pulls/ 141 - fixed github issues/3208 3016 - fixed github issues/3482 数据权限处理器支持 union all - 调整事务未启用打印提示信息 - 单元测试优化相关依赖升级 ## [v3.4.2] 2021.01.15 - fix: 移除 BlockAttackInnerInterceptor 内引用的 commons 的 utils - feat: PaginationInnerInterceptor 添加 optimizeJoin 属性控制是否在count时对sql的join进行优化 - feat: 可通过Resources.setDefaultClassLoader设置默认类加载器. - feat: InterceptorIgnore 注解新增 others 属性 - feat: IService 增加 kotlin 链式调用支持(ktQuery() 和 ktUpdate()) - style: jsqlparser up to 4.0 - style: 移除 com.baomidou.mybatisplus.extension.injector.methods.additional 包下的过时类 - style: generator 模块另开仓库 [generator](https://github.com/baomidou/generator) ## [v3.4.1] 2020.11.10 - fix: 新多租户插件完善子查询,支持 比较符号,in,EXISTS,NOT EXISTS - feat: 公开 AbstractWrapper.getEntityClass - feat: 新增 FakeTenantLineInnerInterceptor 对 TenantSqlParser 进行过度 - feat: 分页count识别 `left join (subSelect)` 优化 - feat: 所有 count 从 count(1) 变更为 count(*) - style: mybatis up to 3.5.6 ## [v3.4.0] 2020.8.23 - fix: @TableName.autoResultMap=true 情况下, 内置的 selectBody 将不会进行 as ,使用了的需要注意!!! - feat: 新增 mybatis-plus-boot-starter-test 模块 - fix: MetaObjectHandler 重载错误(解决办法是参数位置对调),填充值在泛型上支持字段类型的子类 - feat: mybatis up to 3.5.5, mybatis-spring up to 2.0.5 - feat: jsqlparser up to 3.2 - feat: 新增 MybatisParameterHandler, 废弃 MybatisDefaultParameterHandler - feat: 分页插件加入 GBase,ClickHouse,oscar,OceanBase 数据库连接自动识别的支持 - feat: Wrapper 新增api not(boolean condition, Consumer consumer) - feat: 新增 MybatisPlusInterceptor 解决 多租户和分页 插件一级和二级缓存不正确问题 - feat: 新分页插件优化 size<0 时继续拼接 orderBy - feat: 新增 IdentifierGenerator 一个实现类 ImadcnIdentifierGenerator - fix: chainWrapper#func 强转异常 - fix(mybatis-plus-generator.main): 重构生成器数据库类型转换器,修复部分支条,提交选择器测试 - fix: 修复复杂情况中动态表名替换产生的问题:正则由空白检测转为单词边界检测 - refactor: 重构动态表名解析器,去除正则替换程序,改为按表名位置进行替换 - refactor: 将表名解析重构为访问者模式,现在不会对原有 SQL 做改动 ## [v3.3.2] 2020.5.26 - 分页参数提取,单元测试用例修复 - 达梦数据库代码生成器表过滤支持 - 微软数据库代码生成器表过滤支持 - 修复代码生成器属性字段规则错误 - SelectById 支持自定义方法名 - 修复分页插件获取数据库类型问题 - Json转换器空值处理 - bugfix(mybatis-plus-generator):SQL类型返回错误问题 - 调整未知方言异常,自动识别url转换小写匹配. - fix: 初始化 TableInfo 中遇到多个字段有 @TableId 注解时未能抛出异常的问题 - SuperController有Class参数的set方法 - 增加方法StrategyConfig.setSuperServiceImplClass(java.lang.Class). - 代码生成器命名策略调整. - 扩展分页缓存key值计算. - 去除方法推测,直接访问属性字段. - 修正枚举处理器类型不匹配比较. - 修改表前缀匹配方式 - 修改在Mybatis全局配置文件中设置分页插件参数不生效问题 - 修复PR未指定解析器的时候引发空指针 - 增加分页插件limit参数配置 - 修复指定superEntityClass重复生成父类字段问题 - 无主键的情况无需导入IdType与TableId包 - 调整生成BaseResultMap格式 - 支持lombok模式下选择是否进行链式set生成 - 修复解析器for update错误 - 过滤PG约束列(只留下主键约束) - 增加生成器禁用模板生成 - fix(kotlin): 修复动态表名 BUG,最大努力替换表名 - 修复PG约束生成重复属性字段问题 - fix(kotlin): 将 LambdaUtils 中缓存的 key 改为 String - 代码生成器增加数据库关键字处理接口 - fix github/issues/2454 支持注解可继承 - 新增 AES 加密数据库用户名密码 - 优化方法入参泛型,支持更多类型 - 修复代码生成器开启移除is前缀生成实体缺少包导入 - fixed github issues/2470 ## [v3.3.1] 2020.1.17 - 新增`TableName`注解属性`excludeProperty`支持排除字段 - 新增ServiceImpl#entityClass属性,减少泛型提取 - 新增phoenix支持 - 新增支持hbase的选装件`Upsert` - 新增生成器策略配置enableSqlFilter属性来控制是否启用SQL过滤表支持 - 新增批量执行方法,方便用户自定义批量执行操作 - `Wrapper`支持`clear`清空 - `Wrapper`子类新增`func`方法,主要为了支持在`if else`情况下使用`Wrapper`的不同method不会导致断链(链式调用不能一链到底) - `BaseMapper`部分入参为`Wrapper`的select方法支持`wrapper.first`来设置RDS的hint - `KtUpdateWrapper#set`支持value为null - 支持泛型主键支持 - 优化分页拦截器数据类型与方言实现类配置 - 二级缓存复用count查询缓存 - `IService`部分method调整为default方法 - 二级缓存兼容json序列化情况(主要出现默认缓存count出现long反序列化回来为int的情况) - 处理批量操作嵌套事物问题(二级缓存更新问题) - 修复启用乐观锁下updateById时自动填充不生效的问题 - 修复自动填充接口的default方法(`setFieldValByName`和`getFieldValByName`)某些情况下会发生异常的问题 - 修复`KtWrapper`嵌套函数问题 - 修复Freemarker生成Kotlin类的常量错误 - 修复StringUtils#guessGetterName错误 - 修复SerializationUtils资源未释放问题 ## [v3.3.0] 2019.12.06 - BaseMapper 接口两个 page 方法优化 - IService 以及 ServiceImpl 对应 page 方法优化,个别返回 collection 的方法修改为返回 list - 逻辑删除字段的两个表示已删除和未删除的定义支持字符串 `"null"` - 修复批量操作未清空缓存 - 批量操作异常转换为DataAccessException - mybatis up 3.5.3, mybatis-spring up 2.0.3, jsqlparser up 3.1 - mapper 选装件包调整, chainWrapper 包调整 - 新增 ChainWrappers 工具类 - 新增 IdentifierGenerator 接口,支持自定义Id生成 - 代码生成工具废弃正则表名匹配,新增likeTable与notLikeTable - 分页插件支持自定义处理页数限制与溢出总页数处理 - 修复SqlExplainInterceptor导致的Oracle序列自增两次 - 分页二级缓存支持 - 扩展p6spy日志打印 - DbConfig加入新属性propertyFormat,TableFieldInfo移除属性related - 优化序列生成器,过时KeySequence的clazz属性 - 修复Ognl表达式关键字导致的null值判断失效 - 修复更新填充开关失效 - 优化填充逻辑 - ISqlRunner支持selectPage - 支持全局逻辑删除字段 - BaseMapper的方法可自定义 - 添加【虚谷】【Oracle12c】【Kingbase】数据库支持 - 解决数据库字段与实体字段名称不同时出现`null as xxx`的情况 - 过时ID_WORKER_STR,自动识别主键类型 - 配置开启注解,TableName也强制生成 ## [v3.2.0] 2019.08.26 - 代码生成器添加达梦数据库支持 - 修复多主键查询表字段SQL的Bug - 新增 updateWrapper 尝试更新,否继续执行saveOrUpdate(T)方法 - 代码生成器 pg 增加 numeric instant 类型支持 - 修复InjectionConfig不存在时无法生成代码的问题 - fix: #1386(github) 逻辑删除字段为Date类型并且非删除数据日期为null - 升级依赖 mybatis 版本为 3.5.2 - 升级依赖 jsqlparser 版本为 2.1 - 应 EasyScheduler 计划提交 Apache 孵化请求移除 996NPL 协议限制 - 调整 SQL 移除 SET 部分 Github/1460 - 移除 SqlMethod 枚举 UPDATE_ALL_COLUMN_BY_ID 属性,推荐使用 AlwaysUpdateSomeColumnById 套 - fix: #1412(github) github:mybatis-plus-generator can't support oracle - fix: github 1380 - 移除全局配置的 dbType 和 columnLike - 移除 fieldStrategy, 使用上个版本新增的三个替代 - 移除 PerformanceInterceptor 相关, 建议使用 p6spy - 移除 el 拆分为 jdbcType typeHandler 等具体属性 - 升级 gradle-5.5.1,lombok-1.18.4 - 当selectStatement.getSelectBody()的类型为SetOperationList - 移除 GlobalConfig#sqlParserCache 属性,移除 LogicSqlInjector, OrderItem 新增2个快捷生成的method, page 新增一个入参是 List 的 addOrder method - Nested 接口个别入参是 `Function func` 的method,入参更改为 `Consumer consumer`,不影响规范的使用 - fixed gitee/I10XWC 允许根据 TableField 信息判断自定义类型 - Merge pull request #1445 from kana112233/3.0 - 支持过滤父类属性功能 - 添加批量异常捕获测试 - 多租户ID 值表达式,支持多个 ID 条件查询 - 扩展新增 json 类型处理器 jackson fastjson 两种实现 ## [v3.1.2] 2019.06.26 - EnumTypeHandler 更名为 MybatisEnumTypeHandler,移除 EnumAnnotationTypeHandler - 新增自动构建 resultMap 功能,去除转义符 - 注解增加变量控制是否自动生成resultmap - 修改分页缓存Key值错误 - TableField.el 属性标记过时 - 取消 MybatisMapWrapperFactory 的自动注册 - starter 增加默认xml路径扫描 - 新增 MybatisPlusPropertiesCustomizer 及配置使用 - ConfigurationCustomizer 内部方法入参更新为 MybatisConfiguration - 原有 fieldStrategy 标记过时,新增 3 种 fieldStrategy 进行区分 - 获取注入方法时传递当前mapperClass - 增加sqlite代码自动生成测试代码及测试用的数据库文件 - JsqlParserCountOptimize 对 left join 的 sql 优化 count 更精确 - fix(AbstractWrapper.java): 修复 lambda 表达式在 order、groupBy 只有条件一个时引起的类型推断错误 - apply plugin: 'kotlin' - refactor(order): 修复排序字段优先级问题(#IX1QO) - 启动就缓存 lambdacache - Merge pull request #1213 from sandynz/feature/sqlComment 支持SQL注释 - 去除 wrapper 的一些变量,wrapper 内部 string 传递优化 - fix: #1160(github) 分页组件orderBy: 同时存在group by 和order by,且IPage 参数中存在排序属性时,拼接 - Merge pull request #1253 from ShammgodYoung/patch-1 代码生成器输入表名忽略大小写 - 新增渲染对象 MAP 信息预处理注入 - 修改 dts rabbitAdmin bean 判断方式 - Merge pull request #1255 from ShammgodYoung/patch-2 对serialVersionUID属性进行缩进 - JsqlParserCountOptimize 加入 boolean 字段,判断是否优化 join - Merge pull request #1256 from baomidou/master Master - freemarker entity 模板缩进调整 - 增加jdbcType,typeHandler属性, 合并el属性 ## [v3.1.1] 2019.04.25 - 新增 996icu license 协议 - 新增 mybatis-plus-dts 分布式事务 rabbit 可靠消息机制 - 新增 DynamicTableNameParser 解析器、支持动态表名 - 优化 getOne 日志打印 - sql 优化跳过存储过程 - 优化分页查询(count为0不继续查询) - 修复分页一级缓存无法继续翻页问题 - MybatisMapWrapperFactory 自动注入 - 支持纯注解下使用 IPage 的子类作为返回值 - 逻辑删除不再需要 LogicInject - GlobalConfig 加入 enableSqlRunner 属性控制是否注入 SqlRunner ,默认 false - SqlParser注解不再需要全局设置参数才会缓存,以及支持注解在 mapper 上 - GlobalConfig 的 sqlParserCache 设置为过时 - mybatis 升级到 3.5.1 , mybatis-spring 升级到 2.0.1 , jsqlparser 降级到 1.2 - ISqlInjector 接口 移除 injectSqlRunner 方法 - SqlFormatter 类设置为过时 - 解决自动注入的 method 的 SqlCommandType 在逻辑删除下混乱问题 - 新增 AlwaysUpdateSomeColumnById 选装件 - SFunction 继承 Function - DbConfig 的 columnLike 和 dbType 属性设置为过时 - DbConfig 新增 schema 和 columnFormat 属性 - TableField 注解增加 keepGlobalFormat 属性 - TableName 注解增加 schema 和 keepGlobalPrefix 属性 - fixed bug tmp文件格式错乱 github #1048 - 处理表/字段名称抽象 INameConvert 接口策略 github #1038 - DB2支持动态 schema 配置 github #1035 - 把字段缓存的key从className替换成了.class, 如果使用dev-tools会导致:MybatisPlusException: Your property named "xxxx" cannot find the corresponding database column name!(解决方案:去掉dev-tools) ## [v3.1.0] 2019.02.24 - 升级 `mybatis` 到 `3.5.0` 版本 - 升级 `mybatis-spring` 到 `2.0.0` 版本 - 升级 `jsqlparser` 到 `1.4` 版本 - 新增 p6spy 日志打印支持 - 变更 `IService` 的 `getOne(Wrapper queryWrapper)` 方法如果获取到多条数据将会抛出 `TooManyResultsException` 异常 - 修复 自定义分页功能不支持注解 `@select` 问题 - 修复 生成器的配置 kotlin 模式下 swagger 模式无效问题 - 修复 生成器 is 开头字段无法自动注解问题 - 修复 生成器 Serializable Active 模式继承父类包自动导入异常问题 - 修复 生成器 支持公共字段自动读取父类 class 属性问题 - 修复 枚举(注解方式)转换器在存储过程中转换失败 - 修复 beetl 模板逻辑删除注解错误问题 - 修复 通过 `mybatis-config.xml` 方式构建的 `Configuration` 的 `mapUnderscoreToCamelCase` 默认值非 `true` 的问题 - 修复 sql解析器动态代理引发的bug - 修复 `mapper` 使用纯注解下可能触发的重试机制在个别情况下启动报错的问题 - 优化 支持指定 `defaultEnumTypeHandler` 来进行通用枚举处理 - 优化 从 hibernate copy 最新代码到 SqlFormatter - 移除 `wrapper` 的 `in` 以及 `notIn` 方法内部对入参 `coll` 及 `动态数组` 的非empty判断(**注意: 如果以前有直接使用以上的方法的入参可能为 empty 的现在会产出如下sql: `in ()` 或 `not in ()` 导致报错**) - 移除 `wrapper` 的 `notInOrThrow` 和 `inOrThrow` 方法(**使用新版的 `in` 以及 `notIn` 效果一样,异常则为sql异常**) - 移除 `IService` 的 `query` 链式调用的 `delete` 操作 - 移除 xml 热加载相关配置项,只保留`MybatisMapperRefresh`该类并打上过时标志 - 日常优化 ## [v3.0.7.1] 2019.01.02 - 修复 lambdaWrapper 的获取不到主键缓存的问题 - 优化 `IService` 新增的 `update` 链式调用支持 `remove` 操作 - 过时 `IService` 新增的 `query` 链式调用的 `delete` 打上过时标识 - 日常优化 ## [v3.0.7] 2019.01.01 - 优化 generator 的 postgresSql 数据库支持生成 java8 时间类型 - 优化 generator 的 sqlServer 数据库支持生成 java8 时间类型 - 优化 LambdaWrapper 反射获取字段信息支持首字母大写的字段 - 优化 仅 LambdaWrapper 的 select 优化(支持字段对不上数据库时自动 as) - 优化 重复扫描 `BaseMapper` 子类时,`TableInfo` 缓存的 `Configuration` 只保留最后一个 - 优化 `MergeSegments` 获取 `getSqlSegment` 方式 - 优化 SQL 自动注入器的初始化 modelClass 过程,提高初始化速度 - 优化 `BaseMapper` 的 `update` 方法的第一个入参支持为 `null` - 新增 `IService` 增加4个链式调用方法 - 新增 代码生成器增加 `beetl` 模板 - 新增 `IdWorker` 增加毫秒时间 ID 可用于订单 ID - 新增 wrapper 新增 `inOrThrow` 方法,入参为 empty 则抛出 `MybatisPlusExcuption` 异常 - 新增 `MetaObjectHandler` 新提供几个能根据注解才插入值的 `default` 方法 - 新增 kotlin 下 lambda 的支持,`KtQueryWrapper` 和 `KtUpdateWrapper`类 - 新增 简化MP自定义SQL使用方法,现在可以使用 `自定义sql` + ${ew.customSqlSegment} 方式 - 新增 提供新的 `InsertBatchSomeColumn` 选装件 - 修复 Page` 的 `setTotal(Long total)` -> `setTotal(long total)` - 修复 `Page` 的 `setSearchCount` 为 `public` - 修复 `TenantSqlParser` 如果 where 条件的开头是一个 `orExpression`,直接在左边用and拼接租户信息会造成逻辑不符合预期的问题 - 修复 wrapper 的 `lambda` 方法会向下传递 sqlSelect - 修复 `ServiceImpl` 个别 batch 操作 `flushStatements` 问题 - 修复 selectObjs 泛型错误问题 - 移除 `InsertBatchAllColumn` 选装件 - 移除 `ServiceImpl` 的 batch 操作之外的事务注解 - 移除 `Model` 的事务注解 - 移除 `AbstractSqlInjector` 的 `isInjectSqlRunner` 方法(SqlRunner初始化较早,目前isInjectSqlRunner无法控制) - 移除 `MybatisSessionFactoryBuilder` - 移除 对 `mybatis-plus-generator` 包的依赖,自己按需引入 - 还原 xml 热加载,打上过时标识 - 升级 jsqlparser 依赖到 1.3 - 日常优化 ## [v3.0.6] 2018.11.18 - 修复entity中2个以上条件并且拼接ODER BY 或 GROUP BY 产生的 WHERE X1 =? AND X2 - refactor(SerializedLambda.java):重构方法增加反序列化安全性,优化命名 - 基础Mapper优化支持自定义父类Mapper构造自己需要的注入方法 - 使用代替 - 部分优化: 直到抛出异常时才进行字符串 format - 优化 IdWorker 生成UUID使用并发性能 - feat: 动态分页模型、优化分页方言重新修正db2分页语句 - Assert 支持 i18n 多语言错误提示 - 支持 total 控制是否 count sql 新增 isSearchCount 方法 - feat: move spring dependency from core module to extension - fix: Junit.assertTrue - 强制使用自定义ParameterHandler,去除byId类型限制. - 新增选装件的 InsertBatch 通用方法,以及相应测试,以及代码和性能的优化 - IPage 新增功能,泛型转换 - 自动填充判断填充值是否为空,为空时跳过填充逻辑 - batchsize 阈值设 30 修改为 1000 提升效率 - 修复在极端情况下saveOrUpdate执行错误 - 移除 MybatisSqlSessionTemplate - 移除 xml 热加载 - 其他优化 ## [v3.0.5] 2018.10.11 - 移除 ApiAssert 改为 Assert - 移除 ApiResult 改为 R - SQL 注入器优化 - 移除 excludeColumns 方法 - 修复 last 方法的 condition 入参不生效的问题 - 修复去除1=1 BUG - 移除对 spring-devtools 的支持 - 修复实体属性都为null时Sql拼接出错问题 - 缓存Class反射信息,提升效率 - 继承Model类的实体中,现在无需重写pkVal()方法 - 解决在设置了config-location的情况下报mpe的bug,以及优化初始化逻辑 - 修复存在 mapper.xml 情况下逻辑删除失效 - 调整 关于ServiceImpl中的事务问题 gitee issue/IN8T8 - 修复 DB2分页方言 github issues/526 ## [v3.0.4] 2018.09.28 - 修正全局配置 FieldStrategy 为非默认值 - 修正批量事务异常问题 - Api 层 R 类自动处理逻辑失败 - 修改h2脚本初始化加载,去除测试用例注入. - 新增注释其它 ## [v3.0.3] 2018.09.17 - 新增筛选查询字段方法 - fixed orderBy多入参的bug - 新增 LogicDeleteByIdWithFill 组件 - fixed github issues/476 issues/473 - fixed github issues/360 gitee issues/IMIHN IM6GM - 改进 allEq入参的value改用泛型 - fixed saveOrUpdateBatch使用BatchExecutor - fixed 修正getOne获取多条数据为抛出异常 - 修正service 的getOne 方法 - 修正service 的个别方法为default方法 - 修复了page在set了desc下,sql有bug的问题 - 去除不再需要的方法 - 解决 generator 的 optional 的俩 jar 问题 - 重载 select(Predicate predicate) - 其他优化 ## [v3.0.2] 2018.09.11 - 新增 Wrapper 条件辅助类 - 新增 banner 属性控制是否打印 - 修复 gitee #IMMF4:批量插入(AR)事务无效 - fix: entity 无主键,生成 ew 的 where 条件的 bug - 处理SqlRunner的sqlSession获取与释放 - 去除全局缓存sqlSession,增加Model,通用service层sqlSession释放 - ext: 抽象原生枚举处理类注册,方便扩展 - 优化扩展性其他 ## [v3.0.1] 2018.08.31 - 修复代码生成器设置表前缀异常 - 新增 EnumValue 注解方式扫描通用枚举处理 - 修复逻辑删除混用失败 - DB2 方言改进何鹏举优化 - 新增测试用例及其他 ## [v3.0-RELEASE] 2018.08.28 代号:超级棒棒糖 🍭 - 乐观锁 update(et,ew)方法 et带上 version 注解字段回写 - 优化改进优化代码生成器 - 包扫描为空时不抛出异常(枚举,别名) - 去除 SqlSession - 修改 issue 模板,完善注释 - 优化初始化过程,添加逻辑删除注解次数检测 - SQL检查允许跳过检查 - 支持达梦数据库 - 修改 code 为数值型严谨限制简化 api 层命名及初始值规则 - 初始化 SQL 解析移至 SqlInjector - 其他代码优化 ## [v3.0-RC3] 2018.08.19 代号:超级棒棒糖 🍭 RC3 - 支持 TableField select 属性 false 排除默认注入大字段查询 - 解决 page 反序列化 pages 属性报错 - 合并2.x dataSource被代理处理 - 去除DbConfig.columnUnderline属性 - 过滤掉selectObjs查询结果集为空的情况 - baseMapper 的 insert 和 update 返回值不再使用包装类 - fixed Gitee issues/IM3NW - 优化代码完善注释等 ## [v3.0-RC2] 2018.08.10 代号:超级棒棒糖 🍭 RC2 - 生成器加回 MODULE_NAME 开放配置 config - 修复setting - defaultEnumTypeHandler属性配置无效 - 兼容 Spring boot 1.x 启动. - 日常优化 , 测试用例 , 优化抛出异常的过程 - 新增 Gitee Github issue,pull_request模板 - 移除数据库关键字转义, 只支持注解模式转义 - 优化掉抛异常为使用 assert 或者 exceptionUtils - 设置下划线转驼峰到 configuration 优化 ColumnUnderline - 解决 page 序列化 asc desc 多态序列化异常 - 默认的 dbType 改为 other, 如果用户没有配置才会自动获取 dbType - 优化,ColumnUnderline与MapUnderscoreToCamelCase意义相同 - fixed ILY8C 生成器指定 IdType 场景导入包 - 补充注释新增大量测试用例 ## [v3.0-RC1] 2018.08.01 代号:超级棒棒糖 🍭 RC1 - 优化工具类部分代码,并修复一个在多线程环境下可能会引发死锁的BUG - 新增断言类,顺便修改几处地方的判断抛异常为使用断言 - 去掉多余的 "implements Serializable" - 魔法值都改为全局常量模式 - 咩咩说了 MP 3.0 分页已经飘飘欲仙了,不在需要迁就使用 PageHelper 模式 - issue #384 QueryWrapper 支持排除指定字段模式 - 全新 banner,全新感觉 - 再优化一下抛异常的过程 - 修改 class 实例化对象的方式,现在可以实例化私有 class - 支持无配置可启动使用 Gitee issues/ILJQA - 释放sqlSession,待优化 ActiveRecord单元测试 - 解决只调用 last 产生的 sql 会出的问题 - 修复Lambda首位属性为基类属性时错误. - 增加泛型限制,格式化下代码. - 优化一下 AbstractWrapper 使用的 ISqlSegment - 其他 ## [v3.0-RC] 2018.07.23 代号:超级棒棒糖 🍭 RC - 优化 page 当 size 小于 0 自动调整为 list 模式 - 新增 攻击 SQL 阻断解析器 - 优化解析核心方法名,新增 querywrapper lambda 转换参数测试 - 调整通用 service 层方法命名为阿里规范 ( 小白鼠,对不起,请唾弃我们吧!然后修改下您的项目。) - 代码生成器允许正则表达式匹配表名 - 乐观锁 回写更新后的version到实体 - Github #385:查询动态表名能利用Wrapper - 修复 Gitee issues/ILEYD - Page 的序列化接口挪到 IPage 接口 - 解决了 gamma 不能自动赋值 ID - 代码改个常量引用优化 ## [v3.0-gamma] 2018.07.15 代号:超级棒棒糖 🍭 伽玛 - IPage 新增 listMode 集合模式 - fixd gitee issues/IL7W4 - fixed gitee issues/IL7W4 - 优化生成器包导入 - 解决 Page ascs,descs 异常 - 逻辑删除无法 set where entity 一个参数并存逻辑 - 合并 PR 修改typeAliasesPackage扫描多维度 - 完善 3.0 测试用例 - 代码性能优化及其他 ## [v3.0-beta] 2018.07.07 代号:超级棒棒糖 🍭 贝塔 - 新增字段 LIKE 查询注入全局配置,默认 true 开启 - 修改 dbtype 的 oracle db2 修改 CONCAT 方式 - 修正无论 update 的入参 updateWrapper 如何变化,逻辑删除下依然存在限制条件 - 注释加上告警,完善注释 - 修复 github issues/377 378 389 - 解决逻辑删除同时存在非逻辑删除逻辑 - 逻辑删除支持 delete set 其他字段,update 排除逻辑删除字段 - 支持 typeAliasesPackage 多项每项都有通配符 com.a.b.*.po, com.c.*.po - 修复 gitee issues/IKJ48 IL0B2 - 其他完善 ## [v3.0-alpha] 2018.07.01 代号:超级棒棒糖 🍭 - 升级 JDK 8 + 优化性能 Wrapper 支持 lambda 语法 - 模块化 MP 合理的分配各个包结构 - 重构注入方法,支持任意方法精简注入模式 - 全局配置下划线转换消灭注入 AS 语句 - 改造 Wrapper 更改为 QueryWrapper UpdateWrapper - 重构 分页插件 消灭固定分页模型,支持 Mapper 直接返回 IPage 接口 - 新增 Rest Api 通过 Controller 层 - 实体 String 类型字段默认使用 LIKE 查询 SelectOne 默认 LIMIT 1 - 辅助支持 selectMaps 新增 bean map 互转工具类 - 增加 db2 支持 starter 改为 Spring boot 2+ 支持 - 重构生成器提供自定义 DB 多种模板引擎支持 - 相关 BUG 修复 ## [v2.1.9] 2018.01.28 代号:怀念(纪念 2017 baomidou 组织小伙伴 MP 共同成长之路,奔向 2018 旺旺旺) - page 分页新增控制是否优化 Count Sql 设置 ``` // 不进行 count sql 优化 page.setOptimizeCountSql(false); ``` - 注入定义填充,支持sql注入器,主键生成器. - fixed github issues/231 - fixed github issues/234 - 修改逻辑删除 selectByIds coll 问题 - fixed gitee issues/IHF7N - fixed gitee issues/IHH83 - 兼容配置方式,优先使用自定义注入. - 其他优化 ## [v2.1.9-SNAPSHOT] 2018.01.16 - 调整 Gradle 依赖模式 - IdType 可选 ID_WORKER_STR `字符串类型` IdWorker.getIdStr() 字符串类型 - TableField 注解新增属性 `update` 预处理 set 字段自定义注入 fixed gitee IHART ``` 例如:@TableField(.. , update="%s+1") 其中 %s 会填充为字段 输出 SQL 为:update 表 set 字段=字段+1 where ... ``` ``` 例如:@TableField(.. , update="now()") 使用数据库时间 输出 SQL 为:update 表 set 字段=now() where ... ``` - TableField 注解新增属性 `condition` 预处理 WHERE 实体条件自定义运算规则 ``` @TableField(condition = SqlCondition.LIKE) private String name; 输出 SQL 为:select 表 where name LIKE CONCAT('%',值,'%') ``` - 添加 spring-boot-starter 模块内置 `jdbc mp 包不需要单独引入` 更舒服的使用 boot - 添加对 SQL Server 视图生成的支持 - 允许字段策略独立设置,默认为 naming 策略 ``` strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略 strategy.setColumnNaming(NamingStrategy.underline_to_camel);// 允许字段策略独立设置,默认为 naming 策略 ``` - 代码生成器抽象 AbstractTemplateEngine 模板引擎抽象类,可自定义模板引擎,新增内置 freemarker 可选 ``` // 选择 freemarker 引擎 mpg.setTemplateEngine(new FreemarkerTemplateEngine()); ``` - 相关 SQL 解析如多租户可通过 `@SqlParser(filter=true)` 排除 SQL 解析 ``` # 开启 SQL 解析缓存注解生效 mybatis-plus: global-config: sql-parser-cache: true ``` - 解决xml加载顺序问题,可随意引入其他 xml sql 片段 - 修复 author 带123的bug - fix #IGQGE:Wrapper为空,但是page.getCondition()不为空的情况,Condition无法传递问题 - fix #IH6ED:Pagination dubbo 排序等属性序列化不支持 - 判断Wrapper是否为空,使用==,避免被equals方法重载的影响 - 避免注入自定义基类 - 剥离 sql 单独提出至 SqlUtils - 统一缩进编码风格 - 优化生成代码执行性能 github issues/219 - 优化 sql 解析过程 - fixed gitee issues/IHCQB - springboot-configuration-processor 修改 compileOnly为optional - 其他 ## [v2.1.8] 2018.01.02 代号:囍 - 修复代码生成器>字段前缀导致的bug - 使用类全名替代手写的全名 - build修改 - 脚本警告,忽略目录 - 其他优化 ## [v2.1.8-SNAPSHOT] 2017.12.28 代号:翻车鱼(秋秋赐名) - 返回Map自动下划线转驼峰 - kotlin entity 静态常量支持 - 优化 pagination 构造模式 - Merge pull request #201 - fix: selectByMap @alexqdjay - 增加sqlRuner测试用例,修复selectObjs只获取一个字段的bug - 新增 BlobTypeHandler - 去掉参数map的初始大小配置 - 增加.editorconfig,模板空格问题修复. - Hikaricp连接池无法打印sql - 全局中去掉了路径,mapperLocations不可缺少了. - k 神 全部覆盖测试用例 ## [v2.1.7] 2017.12.11 代号:清风徐来 , 该版本号存在 bug 请改为 2.1.8-SNAPSHOT + - 枚举处理:基本类型,Number类型,String类型 - IGDRW:源码注释错误,容易给人误导 注释错误问题 - 炮灰 PR !42:添加分页构造方法重载 添加分页构造方法重载 - 代码生成 > oracle > 解决超出最大游标的问题 - fixed gitee IGNL9 - k 神 一大波 testcase 来袭 - 使用transient关键字去除Page中部分字段参与序列化 - 去除无效日志 - fix #IGI3H:selectBatchIds 参数改为Collection类型 - bugfix for logic delete sql injector - 添加多个排序字段支持 - fixed github #185:2.0.2版本 自增主键 批量插入问题 pr - 其他优化` ## [v2.1.6] 2017.11.22 代号:小秋秋之吻 - 模块拆分为 support core generate 代码生成分离可选择依赖 - 解决 gitee issue IFX30 拆分 mybatis-plus-support 包支持 - 解决 gitee issue IGAPX 通用枚举 bigdecimal 类型映射 - druid补充,填充字段修改 - 修复 kotlin 代码生成部分逻辑 Bug - 合并 gitee pr 40 updateAllColumn****等方法排除fill = FieldFill.INSERT注释的字段 感谢 Elsif - 构造模式设置 kotlin 修改 - Sql 工具类反射实例优化 - 其他优化 ## [v2.1.5] 2017.11.11 代号:离神 - 通用枚举 spring boot 兼容调整 - PostgreSQL 支持关键词非关键词转换问题 - Cat73 PR 稍微调整下自动生成的代码 - 支持 kotlin 代码生成 - bugfix for metaObj handler set val which not included in ... - alibaba 规范调整 - 其他 ## [v2.1.3 - 2.1.4] 2017.10.15 - 新增通用枚举处理器,参考 spring boot demno - 优化 SQL 解析器 - 新增 schema 租户解析器待完善 - 其他优化 ## [v2.1.2] 2017.09.17 代号: X - 修复代码生成器 Bug - fixed gitee issues/IF2DY - 修改 page 可链式操作 - 去掉转义 oracle - fixed github issues/119 - fixed gitee issues/IF2OI ## [v2.1.1] 2017.09.12 代号:小锅盖 - 修改分页超过总记录数自动设置第一页 bug @wujing 感谢 pr - fixed IEID6 - 升级 mybatis 3.4.5 - 升级生成器模板引擎 veloctiy 2.0 - 升级 jsqlparser 1.1 - 新增 SQL 解析链可动态扩展自定义 SQL 解析 - 新增 多租户 SQL 解析逻辑,具体查看 spring boot 演示 demo - jasonlong10 PR 性能分析拦截器 支持OraclePreparedStatementWrapper的情况打印 SQL - fixed github issues/145 - fixed gitee issue/IF1OF - add sqlSelect("distinct test_type") test case - 添加填充生成器遗漏 TableField 导入类 - fixed github issues/MYSQL表名含有保留字代码生成时报错 #124:字段全为 大写 下划线命名支持 - fixed github issues/134 - PostgreSQL 代码生成支持指定 schema 表字段按照默认排序 - 其他优化调整 ## [v2.1.0] 2017.08.01 代号:小秋秋 ####主体功能 - 批量sqlSession没有关闭问题修复 - 处理sql格式化报错问题,添加填充信息 - #91:关于insertBatch在大数据量的时候优化 github - 新增 uuid 主键测试用例 - 修复BUG自动填充会覆盖之前的值 - 升级pom依赖,spring-test作用域test - 更改sqlServer驱动,去掉乐观锁不需要的string类型测试 - #86:关于plus的底层映射设计问题 github issue - SqlHelper处理Wrapper为空,但是page.getCondition()不为空的情况 - Merge pull request !33:生成实体增加字段排序 from 老千/master - 解决使用代理对象导致无法获取实例缓存信息 - 解决布尔类型is开头生成sql错误问题 - DBType设置错误 - fix #351:DB2Dialect返回NULL - fix #356:自动代码生成的Boolean类型的get方法不对 - fix #353:代码生成@TableLogic问题 - 新增 PostgreSqlInjector 自动注入器,处理字段大小写敏感,自动双引号转义。 - 仓库地址与用户信息使用自定义传入. - fix #357:代码生成@TableLogic引入包Bug - Sequence 新增 mac 判断,分页 pageHelper 模式新增 freeTotal() 方法 - #95:分页插件俩个建议 Github, selectItems contains #{} ${}, - 添加 Wrapper#setSqlSelect(String... columns) 方法,方便通过自动生成的实体... - fixed github 116 issue - fixed osgit IE436 IDVPZ IDTZH ####代码生成 - 修改实体生成模板 - 修复自动填充代码生成错误 - 新增 postgresql schemaname 生成器支持 - 调整序列化导入问题 - 其他 ## [v2.1-gamma] 2017.06.29 ####主体功能 - 修正之前sqlserver自动获取类型错误问题 - 修复用户无法自定义分页数据库方言问题 ####代码生成 - 完善了自动填充代码生成 - 修复postgresql生成重复字段问题 ####上个版本(2.0.9)升级导致的问题 - 修复实体主键不在第一位无法读取的问题 - 修复在自定义insert操作时报`Insert not found et`异常,见#331 - 修复Sql生成错误问题(普通注入Group,Having,Order) - 修复逻辑删除生成Sql顺序错误 - 感谢各路小伙伴及时反馈的问题,上个版本给大家带来的问题深感抱歉 ###Mybatis-Plus-Boot-Start [1.0.4] ####主体变动 - 去除Mybatis-plus直接依赖 - 去除SpringBoot jdbc-starter直接依赖 ## [v2.0.9] 2017.06.26 代号:K 神 ###Mybaits-Plus ####主体功能 - 修正乐观锁和逻辑删除冲突问题 - 处理在生成注入SQL时之前没有考虑到存在且打开下划线配置情况 - 修复EntityWrapper继承关系问题 - Wrapper添加条件判断 - 性能分析插件支持记录日志提示 - Wrapper重写了toString方式,解决之前Debug时显示为null给用户造成错觉 - 处理Sequence非毫秒内并发偶数居多问题 - 忽略策略优化处理更改了注解的属性 - 注入Sql的方式优化,去除之前XML注入方式 - 处理逻辑删除出现2个Where的问题 - 添加其他数据库序列的实现方式,并开放出接口给用户自行扩展 - 乐观锁优化调整 - 优化Wrapper中Where AND OR 去除之前基于反射方式实现,提高代码运行效率 - 处理不添加mybatis-config.xml主键无法填充问题 - MybatisPlus添加支持gradle构建方式 - Wrapper 添加 `and()` `or()` 方法 - 优化GlobalConfiguration,抽离出GlobalConfigUtils减少耦合 - 修复Sqlserver2008与SqlServer2005分页问题 - 新增自动识别数据库,减少用户显式配置 - 优化分页插件减少用户显示配置属性 - 自动填充字段问题解决 - 新增PageHelper,获取当前线程来管理分页(之前老用户最好不要使用,改方式只用户适用MybatisPageHelper用户习惯) - 大幅度的添加测试用例(感谢K神支持) - 代码的其他优化 - 添加了JSqlparser的依赖以后不用手动去添加该Jar包 ####代码生成 - 支持逻辑删除方式生成 - 支持乐观锁方式生成 - 修复生成器不能识别sqlServer的自增主键代码生成器不能识别SqlServer自增主键的问题 - 支持Lombok方式生成 - 支持构建模式方式生成 - 添加Clob和Blob类型转换 - 修复Oracle的Number类型字段转换错误问题 ###Mybatis-Plus-Boot-Start [1.0.2] 代号:清风 ####主体功能 - 处理AR模式devtool替换数据源失效问题 - 添加逻辑删除支持 - 添加序列支持 ## [v2.0.8] 2017.05.15 - Wrapper添加设置对象sqlSelect - 兼容无注解情况 - 乐观锁去除默认short实现,优化绑定注册器在扫描阶段绑定. 测试改为h2环境. - 优化热加载,去除mapper路径配置. - 减少刷新Mapper配置 - 修复tableFiled value 为空情况,开启下划线命名 - sequence 升级提示 - 开放表信息、预留子类重写 - 修改Idwork测试 - 支持 devtools - fixed 259 支持 xml resultMap 公共字段生成 - fixed pulls 28 支持属性重载 ## [v2.0.6 2.0.7] 2017.04.20 - 新增 逻辑删除 - 新增 Oracle Sequence - 新增 jdk1.8 时间类型 - 完善支持乐观锁 - 完善字段填充器,支持更新填充 - 升级 mybatis 依赖为 3.4.4 - 代码调整优化,支持 wrapper limit 等逻辑 - 修复 Id 策略 auto bug ,生成器 bug 其他 ## [v2.0.5] 2017.03.25 - 修复分页连接池没有关闭的bug - issues fixed 217 - IMetaObjectHandler当主键类型是AUTO或者INPUT的时候不起效的bug - 修复 like 占位符问题 - 生成代码的时候如果目录不存在则新建 ## [v2.0.3 - v2.0.4] 2017.03.22 - 优化Wrapper代码结构 - 优化原有数据库连接获取 - 解决Page初始化问题(之前只能通过构造方法生效,现在可以通过setget也可以生效) - 支持乐观锁插件 - 改造Wrapper让JDBC底层来处理参数,更好的与PreparedStatement结合 - 修复相关错误日志提示级别 - Wrapper开放isWhere方法,现在可以自定义是否拼接"WHERE" - JDK版本向下兼容,之前相关代码用到了1.7新特性,当前版本解除 - sqlserver生成bug修复以及代码优化 - 优化MybatisPlus,SqlSession获取 - 解决未配置切点的情况下获取的sqlSession提交不属于当前事务的问题以及多个sqlSession造成的事务问题 - 增强执行sql类,sqlRunner - Model添加序列化ID,避免以后在修改Model后没有设置序列号ID时序列号ID可以会变动的情况 - 添加重写默认BaseMapper测试用例 - 感谢各路小伙伴提问的好的建议以及贡献代码,就不一一点名了 ## [v2.0.2] 2017.02.13 - 修复全局配置不起作用 2.0.1 逻辑 - 去除byId强制配置类型 - Wrapper Page 等程序优化 - 优化AR模式自动关闭数据库连接(之前需要手动设置事务) - 优化代码生成器,下划线名称注解不处理驼峰,支持自定义更多的模板例如 jsp html 等 - 新增 service 层测试 - sql日志记录整合至性能分析插件. - 处理多数据源分页插件支持多重数据库 ## [v2.0.1] 2017.01.15 - 解决EntityWrapper对布尔类型构造sql语句错误 - 全局配置初始化日志提示调整 - Mybatis依赖升级至3.4.2,Mybatis-Spring依赖升级至1.3.1 - Service中补充方法(selectObjs,selectMaps) - 解决selectCount数据库返回null报错问题 - 支持PostgreSql代码生成 - 拓展支持外部提供转义字符以及关键字列表 - 开放数据库表无主键依然注入MP的CRUD(无主键不能使用MP的xxById方法) - 解决EntityWrapper拼接SQL时,首次调用OR方法不起作用的问题 - sqlServer代码生成(基于2008版本) - 解决生成代码时未导入BigDecimal问题. - 释放自动读取数据库时的数据库连接 - 优化全局校验机制(机制为EMPTY增加忽略Date类型) - 优化注入,避免扫描到BaseMapper - 优化注入,去除多余注入方法 - SQLlikeType改名为SqlLike - 解决热加载关联查询错误问题 - SqlQuery改名为SqlRunner - 优化完善代码生成器 - 修复代码生成器未导入@tableName - 全局配置需要手动添加MP的默认注入类,更改为自动注入简化配置 - Wrapper增加ne方法 - 修复Mybatis动态参数无法生成totalCount问题 - 代码结构优化,生成器模板优化 - 解决issus[138,140,142,148,151,152,153,156,157],具体请查看里程碑[mybatis-plus 2.0.1 计划](https://gitee.com/baomidou/mybatis-plus/milestones/2)中所有issus ## [v2.0.0] 2016.12.11 - 支持全局大写命名策略 - 自动分页Count语句优化 - 优化现有全局配置策略 - 优化全局验证策略 - 优化代码生成器(之前硬编码,现使用模板形式) - 优化注入通用方法ByMap逻辑 - 添加自动选择数据库类型 - 改善SqlExplainInterceptor(自行判断MySQL版本不支持该拦截器则直接放行(版本过低小于5.6.3)) - 修复部分特殊字符字符多次转义的问题 - 优化现有EntityWrapper添加Wrapper父类以及Condition链式查询 - Wrapper类使LIKE方法兼容多种数据库 - 优化日志使用原生Mybatis自带的日志输出提示信息 - 修复使用缓存导致使用分页无法计算Count值 - 修复PerformanceInterceptor替换`?`导致打印SQL不准确问题,并添加格式化SQL选项 - 添加多种数据库支持,请查看DBType - 添加字符串类型字段非空校验策略(字符串类型自动判断非空以及非空字符串) - Wrapper添加类似QBC查询(eq、gt、lt等等) - 支持AR模式(需继承Model) - 合并所有Selective通用方法(例如:去除之前的insert方法并把之前的insetSelective改名为insert) - 解决sql剥离器会去除`--`的情况 - 支持MySQL关键词,自动转义 - 精简底层Service、Mapper继承结构 - 不喜欢在XML中写SQL的福音,新增执行SQL方式,具体请查看SqlQuery - 优化代码结构 - 解决issus[95,96,98,100,103,104,108,114,119,121,123,124,125,126,127,128,131,133,134,135],具体请查看里程碑[mybatis-plus 2.0 计划](https://gitee.com/baomidou/mybatis-plus/milestones/1)中所有issus ## [v1.4.9] 2016.10.28 - ServiceImpl去除@Transactional注解、去除Slf4j依赖 - 解决使用EntityWrapper查询时,参数为特殊字符时,存在sql注入问题 - 调整Mybatis驼峰配置顺序 MybatisPlus > Mybatis - 优化分页插件并修复分页溢出设置不起作用问题 - 去除DBKeywordsProcessor,添加MySQL自动转义关键词 - 代码生成器新增支持TEXT、TIME、TIMESTAMP类型生成 - 新增批量插入方法 - 代码生成器新增Controller层代码生成 - 调整EntityWrapper类部分List入参为Collection - 代码生成器优化支持 resultMap ## [v1.4.8] 2016.10.12 - insertOrUpdate增加主键空字符串判断 - 支持Mybatis原生驼峰配置 mapUnderscoreToCamelCase 开关设置 - 支持 TableField FieldStrategy 注解全局配置 - SelectOne、SelectCount方法支持EntityWrapper方式 - oracle 代码生成器支持 Integer Long Dobule 类型区分 - 修复INPUT主键策略InsertOrUpdate方法Bug - EntityWrapper IN 添加可变数组支持 - 基础Mapper、Servcie通用方法PK参数类型更改至Serializable - 当selectOne结果集不唯一时,添加警告提示(需开启日志warn模式) - baseService添加logger,子类直接调用logger不用重新定义(需slf4j依赖) ## [v1.4.7] 2016.09.27 - 主键注解 I 改为 PK 方便理解,去掉 mapper 注解 - 性能分析插件,特殊处理 $ 符内容 - 添加自动提交事务说明,新增事务测试 - 支持 resultMap 实体结果集映射 - 增加#TableField(el = "")表达式,当该Field为对象时, 可使用#{对象.属性}来映射到数据表、及测试 - 新增 typeHanler 级联查询支持 - 新增验证字段策略枚举类 - 代码生成器支持实体构建者模型设置 - 代码生成器新增实体常量生成支持 - CRUD 新增 insertOrUpdate 方法 - 解决MessageFormat.format格式化数字类型sql错误 - EntityWrapper添加 EXISTS、IN、BETWEEN AND(感谢D.Yang提出)方法支持 - 支持 mysql5.7+ json enum 类型,代码生成 - 支持无XML依然注入CRUD方法 - 修改Mybatis原生配置文件加载顺序 ## [v1.4.6] 2016.09.05 - 新增无 @TableId 注解跳过注入SQL - 支持非表映射对象插入不执行填充 - xxxByMap 支持 null 查询 ## [v1.4.5] 2016.08.28 - 新增 XML 修改自动热加载功能 - 添加自动处理EntityWrapper方法中的MessageFormat Params类型为字符串的参数 - 新增表公共字段自动填充功能 ## [v1.4.4] 2016.08.25 - entitywrapper所有条件类方法支持传入null参数,该条件不会附件到SQL语句中 - TSQLPlus更名为TSqlPlus与整体命名保持一致。 - 修复mysql关键字bug----将关键字映射转换加上``符号,增加xml文件生成时可自定义文件后缀名 - 关闭资源前增加非空判断,避免错误sql引起的空指针错误,增加选择 current>pages 判断 - TSQL 相关类实现序列化支持 dubbo - 增加 mybatis 自动热加载插件 - 支持数据库 order key 等关键词转义 curd 操作 ## [v1.4.3] 2016.08.23 - 优化 Sequence 兼容无法获取 mac 情况 - 兼容用户设置 ID 空字符串,自动填充 - 纯大写命名,转为小写属性 - 修改EntityWrapper符合T-SQL语法标准的条件进行方法封装定义 - 升级 1.4.3 测试传递依赖 ## [v1.4.0] 2016.08.17 - 增加自定义 select 结果集,优化 page 分页 - 未考虑 函数,去掉 field 优化 - 新增 delete update 全表操作禁止执行拦截器 ## [v1.3.9] 2016.08.09 - 修复 bug - 解决插入 map 异常 - 插入 map 不处理,原样返回 - 优化 IdWorker 生成器 - 支持自定义 LanguageDriver - 支持代码生成自定义类名 - 升级 mybatis 3.4.1 依赖 ## [v1.3.6] 2016.07.28 - 支持全局表字段下划线命名设置 - 增加自定义 注入 sql 方法 - 优化分页总记录数为0不执行列表查询逻辑 - 自动生成 xml 基础字段增加 AS 处理 - 支持字段子查询 ## [v1.3.5] 2016.07.24 - 升级 1.3.5 支持全局表字段下划线命名设置 - 添加发现设置多个主键注解抛出异常 - 添加无主键主键启动异常 - 去掉重置 getDefaultScriptingLanuageInstance - 修改歧义重载方法 ## [v1.3.3] 2016.07.15 - 处理 SimpleDateFormat 非现场安全问题 - 修改 oracle 分页 bug 修复 - oracle TIMESTAMP 生成支持 bug 修复 ## [v1.3.2] 2016.07.12 - service 暴露 sqlSegment 的方法调用 - 新增 sql 执行性能分析 plugins - 新增 deleteByMap , selectByMap ## [v1.3.0] 2016.07.07 - 支持 like 比较等查询 sqlSegment 实现 - 支持 typeAliasesPackage 通配符扫描, 无 count 分页查询 - mybatis mapper 方法调用执行原理测试 - 添加 IOC 演示用例 ## [v1.2.17] 2016.06.15 - 优化 代码生成器 感谢 yanghu pull request - 调整 sql 加载顺序 xmlSql > curdSql - 支持 CURD 二级缓存 - 增加缓存测试,及特殊字符测试 ## [v1.2.15] 2016.04.27 - 新增 支持oracle 自动代码生成,测试 功能 - 新增 UUID 策略 - 演示demo 点击 spring-wind - 新增支持单表 count 查询 ## [v1.2.12] 2016.04.22 - 添加 service 层支持泛型 id 支持,自动生成代码优化 - 升级 mybatis 为 3.4.0 ,mybatis-spring 为 1.3.0 ## [v1.2.11] 2016.04.18 - 新增批量更新,支持 oracle 批量操作 - 去掉,移植至 spring-wind 的文档 - 支持 jdk1.5 修改 param 描述 - 添加数据库类型 ## [v1.2.9] 2016.04.10 - EntityWrapper 新增无 order by 构造方法 - MailHelper 重载 sendMail 方法 - 新增 String 主键ID 支持 CommonMapper - 原来方法 selectList 分离为 selectList , selectPage 两个方法 - 优化代码生成器,添加文档说明、其他 ## [v1.2.8] 2016.04.02 - 优化生成代码处理大写字段,支持自动生成 entity mapper service 文件 - 优化分页 index 超出逻辑,新增 5 个 CRUD 操作方法 - 开放模板引擎 getHtmltext 方法 - 优化邮件发送配置添加说明文档 - 添加文档说明、其他 ## [v1.2.6] 2016.03.29 - 优化代码 service 层封装,抽离 list 、 page 方法 - 优化分页 count sql 语句 - 改进 mail 工具类 - 完善 framework 对 spring 框架的支持 - 添加文档说明、其他 ## [v1.2.5] 2016.03.25 - 独立支持id泛型的 baseMapper - 更完善的自动生成工具 - 支持实体封装排序 - 分页插件完善 - 抽离 service 主键泛型支持 ## [v1.2.2] 2016.03.14 - 注解 ID 区分 AUTO 数据库自增,ID_WORKER 自动填充自定义自增ID , INPUT 手动输入 。 - 优化代码及自动生成器功能。 - 其他 ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright (c) 2011-${year}, baomidou (jobob@qq.com). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: MPCodeStyle.xml ================================================ ================================================ FILE: README-zh.md ================================================

Mybatis-Plus-Logo

为简化开发工作、提高生产率而生

code style code style code style maven code style

[企业版 Mybatis-Mate 高级特性](https://gitee.com/baomidou/mybatis-mate-examples) # 简介 | Intro Mybatis 增强工具包 - 只做增强不做改变,简化`CRUD`操作 添加 `微信 wx153666` 备注进 mp 群 > 不允许非法项目使用,后果自负 # 特别用户

aizuda-Logo mall4j-Logo crmeb-Logo

# 依赖引用 - Latest Version: [![Maven Central](https://img.shields.io/maven-central/v/com.baomidou/mybatis-plus.svg)](https://search.maven.org/search?q=g:com.baomidou%20a:mybatis-*) - Maven: - SpringBoot2 ```xml com.baomidou mybatis-plus-boot-starter Latest Version ``` - SpringBoot3 ```xml com.baomidou mybatis-plus-spring-boot3-starter Latest Version ``` - SpringBoot4 `^3.5.13` ```xml com.baomidou mybatis-plus-spring-boot4-starter Latest Version ``` - `^3.5.9` 你可能需要额外的引用 - jdk11+ ```xml com.baomidou mybatis-plus-jsqlparser Latest Version ``` - jdk8 ```xml com.baomidou mybatis-plus-jsqlparser-4.9 Latest Version ``` - Gradle - SpringBoot2 ```groovy compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: 'Latest Version' ``` - SpringBoot3 ```groovy compile group: 'com.baomidou', name: 'mybatis-plus-spring-boot3-starter', version: 'Latest Version' ``` # 优点 | Advantages - **无侵入**:Mybatis-Plus 在 Mybatis 的基础上进行扩展,只做增强不做改变,引入 Mybatis-Plus 不会对您现有的 Mybatis 构架产生任何影响,而且 MP 支持所有 Mybatis 原生的特性 - **依赖少**:仅仅依赖 Mybatis 以及 Mybatis-Spring - **损耗小**:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作 - **通用CRUD操作**:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 - **多种主键策略**:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题 - **支持ActiveRecord**:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作 - **支持代码生成**:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(P.S. 比 Mybatis 官方的 Generator 更加强大!) - **支持自定义全局通用操作**:支持全局通用方法注入( Write once, use anywhere ) - **内置分页插件**:基于Mybatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于写基本List查询 - **内置性能分析插件**:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询 - **内置全局拦截插件**:提供全表 delete 、 update 操作智能分析阻断,预防误操作 ## 相关链接 | Links - [文档](https://baomidou.com) - [代码生成](https://github.com/baomidou/generator) - [功能示例](https://github.com/baomidou/mybatis-plus-samples) - [展示](https://github.com/baomidou/awesome-mybatis-plus) - [企业版 Mybatis-Mate 高级特性](https://github.com/baomidou/mybatis-mate-examples) # 其他开源项目 | Other Project - [基于Cookie的SSO中间件 Kisso](https://gitee.com/baomidou/kisso) - [Java快速开发框架 SpringWind](https://gitee.com/juapk/SpringWind) - [基于Hibernate扩展 Hibernate-Plus](https://gitee.com/baomidou/hibernate-plus) - [基于 pac4j-jwt 的快速集成的 web 安全组件 shaun](https://gitee.com/baomidou/shaun) # 王者荣耀 ![MPTrophy](./images/c9bf2ba5_12260.jpeg "mybatis-plus.jpg") ![GitCode](./images/2025-05-21_163231_398.jpg "2025-05-21_163231_398.jpg") # 期望 | Futures > 欢迎提出更好的意见,帮助完善 Mybatis-Plus # 版权 | License [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) ![捐赠 mybatis-plus](./images/211207_0acab44e_12260.png "支持一下mybatis-plus") # 关注我 | About Me * Github: https://github.com/baomidou/mybatis-plus * Gitee: https://gitee.com/baomidou/mybatis-plus * GitCode: https://gitcode.com/baomidou/mybatis-plus ![程序员日记](./images/181933_46d5b802_12260.png "程序员日记") ================================================ FILE: README.md ================================================

Mybatis-Plus-Logo

Born To Simplify Development

code style code style code style maven

[企业版 Mybatis-Mate 高级特性](https://gitee.com/baomidou/mybatis-mate-examples) 添加 `微信 wx153666` 备注进 mp 群 > 不允许非法项目使用,后果自负 # Special user

aizuda-Logo mall4j-Logo

## What is MyBatis-Plus? MyBatis-Plus is a powerful and enhanced toolkit of MyBatis for simplifying development. It provides efficient, and out-of-the-box features (such as code generation, conditional query builders, pagination plugins...), effectively saving development time ## Links - [Documentation](https://baomidou.com) - [Code Generator](https://github.com/baomidou/generator) - [Samples](https://github.com/baomidou/mybatis-plus-samples) - [Showcase](https://github.com/baomidou/awesome-mybatis-plus) - [企业版 Mybatis-Mate 高级特性](https://gitee.com/baomidou/mybatis-mate-examples) ## Features - Fully compatible with MyBatis - Auto configuration on startup - Out-of-the-box interfaces for operate database - Powerful and flexible where condition wrapper - Multiple strategy to generate primary key - Lambda-style API - Almighty and highly customizable code generator - Automatic paging operation - SQL Inject defense - Support active record - Support pluggable custom interface - Build-in many useful extensions ## Getting started - Add MyBatis-Plus dependency - Latest Version: [![Maven Central](https://img.shields.io/maven-central/v/com.baomidou/mybatis-plus.svg)](https://search.maven.org/search?q=g:com.baomidou%20a:mybatis-*) - Maven: - SpringBoot2 ```xml com.baomidou mybatis-plus-boot-starter Latest Version ``` - SpringBoot3 ```xml com.baomidou mybatis-plus-spring-boot3-starter Latest Version ``` - SpringBoot4 `^3.5.13` ```xml com.baomidou mybatis-plus-spring-boot4-starter Latest Version ``` - `^3.5.9` may need additional citations - jdk11+ ```xml com.baomidou mybatis-plus-jsqlparser Latest Version ``` - jdk8 ```xml com.baomidou mybatis-plus-jsqlparser-4.9 Latest Version ``` - Gradle - SpringBoot2 ```groovy compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: 'Latest Version' ``` - SpringBoot3 ```groovy compile group: 'com.baomidou', name: 'mybatis-plus-spring-boot3-starter', version: 'Latest Version' ``` - Modify mapper file extends BaseMapper interface ```java public interface UserMapper extends BaseMapper { } ``` - Use it ``` java List userList = userMapper.selectList( new QueryWrapper() .lambda() .ge(User::getAge, 18) ); ``` MyBatis-Plus will execute the following SQL ```sql SELECT * FROM user WHERE age >= 18 ``` > This showcase is just a small part of MyBatis-Plus features. If you want to learn more, please refer to > the [documentation](https://baomidou.com). ## License MyBatis-Plus is under the Apache 2.0 license. See the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) file for details. ================================================ FILE: build.gradle ================================================ import java.time.LocalDateTime allprojects { group APP_GROUP version APP_VERSION } ext { configuration = [ javaVersion = JavaVersion.VERSION_21 ] libraries = [ mybatisVersion = '3.5.19', mybatisSpringVersion = '2.1.2', mybatisSpringBootStarterVersion = '2.3.2', springVersion = '5.3.39', springBootVersion = '2.7.18', springBoot3Version = '3.5.9', springBoot4Version = '4.0.1', springCloudVersion = '3.1.8', junitVersion = '5.14.1', ] lib = [ "kotlin-reflect" : "org.jetbrains.kotlin:kotlin-reflect:2.1.0", "kotlin-stdlib-jdk8" : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.0", "mybatis" : "org.mybatis:mybatis:${mybatisVersion}", "mybatis-spring" : "org.mybatis:mybatis-spring:${mybatisSpringVersion}", "mybatis-thymeleaf" : "org.mybatis.scripting:mybatis-thymeleaf:1.0.4", "mybatis-freemarker" : "org.mybatis.scripting:mybatis-freemarker:1.3.0", "mybatis-velocity" : "org.mybatis.scripting:mybatis-velocity:2.1.2", "spring-context-support" : "org.springframework:spring-context-support:${springVersion}", "spring-jdbc" : "org.springframework:spring-jdbc:${springVersion}", "spring-tx" : "org.springframework:spring-tx:${springVersion}", "spring-web" : "org.springframework:spring-web:${springVersion}", "spring-aop" : "org.springframework:spring-aop:${springVersion}", "cglib" : "cglib:cglib:3.3.0", "imadcn" : "com.imadcn.framework:idworker:1.6.0", "spring-cloud-commons" : "org.springframework.cloud:spring-cloud-commons:${springCloudVersion}", "aspectjweaver" : "org.aspectj:aspectjweaver:1.9.22.1", "slf4j-api" : "org.slf4j:slf4j-api:1.7.36", //copy "mybatis-spring-boot-starter": "org.mybatis.spring.boot:mybatis-spring-boot-starter:${mybatisSpringBootStarterVersion}", //test "spring-test" : "org.springframework:spring-test:${springVersion}", "assertj-core" : "org.assertj:assertj-core:3.27.6", "junit-jupiter" : "org.junit.jupiter:junit-jupiter:${junitVersion}", "fastjson" : "com.alibaba:fastjson:2.0.60", "jackson" : "com.fasterxml.jackson.core:jackson-databind:2.20.1", "jackson3" : "tools.jackson.core:jackson-databind:3.0.3", "gson" : "com.google.code.gson:gson:2.13.2", "lagarto" : "org.jodd:jodd-lagarto:6.0.6", //datasource "p6spy" : "p6spy:p6spy:3.9.1", "sqlserver" : "com.microsoft.sqlserver:sqljdbc4:4.0", "postgresql" : "org.postgresql:postgresql:42.7.8", "oracle" : "com.oracle.database.jdbc:ojdbc8:23.7.0.25.01", "dm" : fileTree(dir: 'libs', includes: ["jdbcDriver-18.jar"]), "h2" : "com.h2database:h2:2.4.240", "mysql" : "com.mysql:mysql-connector-j:9.5.0", "sqlite" : "org.xerial:sqlite-jdbc:3.51.1.0", "firebird" : "org.firebirdsql.jdbc:jaybird:5.0.10.java8", "gaussdb" : "com.huaweicloud.gaussdb:gaussdbjdbc:506.0.0.b058", //cache "mybatis-caffeine" : "org.mybatis.caches:mybatis-caffeine:1.2.0", //code generator "velocity" : "org.apache.velocity:velocity-engine-core:2.4.1", "freemarker" : "org.freemarker:freemarker:2.3.33", "beetl" : "com.ibeetl:beetl:3.17.0.RELEASE", "swagger-annotations" : "io.swagger:swagger-annotations:1.6.14", "enjoy" : "com.jfinal:enjoy:5.2.2", "logback-classic" : "ch.qos.logback:logback-classic:1.5.15", ] } description = "Mybatis 增强工具包 - 只做增强不做改变,简化CRUD操作" subprojects { apply plugin: 'java-library' apply plugin: 'signing' apply plugin: 'maven-publish' apply plugin: 'tech.yanand.maven-central-publish' apply plugin: "io.freefair.lombok" sourceCompatibility = "${javaVersion}" targetCompatibility = "${javaVersion}" lombok { version = "1.18.32" } compileJava { options.release = 8 } repositories { mavenLocal() maven { url "https://maven.aliyun.com/repository/public" } maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } mavenCentral() } dependencies { testImplementation "${lib["assertj-core"]}" testImplementation "${lib["junit-jupiter"]}" testImplementation "org.mockito:mockito-junit-jupiter:5.21.0" testImplementation "${lib["lagarto"]}" testImplementation("org.junit.platform:junit-platform-engine:6.0.1") testImplementation("org.junit.jupiter:junit-jupiter-engine:6.0.1") testImplementation("org.junit.platform:junit-platform-launcher:6.0.1") } tasks.withType(JavaCompile) { options.encoding = 'UTF-8' options.warnings = false options.deprecation = true options.compilerArgs += ["-parameters"] } tasks.withType(GenerateModuleMetadata) { enabled = false } jar { into("META-INF/") { from rootProject.file("LICENSE") } into("META-INF/maven/$project.group/$project.name") { from { generatePomFileForMavenJavaPublication } rename ".*", "pom.xml" } afterEvaluate { manifest { attributes 'Implementation-Title': archiveBaseName attributes 'Implementation-Version': archiveVersion attributes 'Built-Gradle': gradle.gradleVersion attributes 'Bundle-DocURL': 'https://baomidou.com/' attributes 'Build-OS': System.getProperty("os.name") attributes 'Built-By': System.getProperty("user.name") attributes 'Build-Jdk': System.getProperty("java.version") attributes 'Build-Timestamp': LocalDateTime.now().format("yyyy-MM-dd HH:mm:ss") attributes 'Automatic-Module-Name': "${project.group}.${project.name.replaceAll("-", ".")}" } } } //noinspection GroovyAssignabilityCheck task sourcesJar(type: Jar) { archiveClassifier = 'sources' from sourceSets.main.allSource } javadoc { afterEvaluate { configure(options) { encoding "UTF-8" charSet 'UTF-8' author true version true failOnError false links "http://docs.oracle.com/javase/8/docs/api" } } } test { dependsOn("cleanTest", "generatePomFileForMavenJavaPublication") useJUnitPlatform() exclude("**/phoenix/**") exclude("**/postgresql/**") exclude("**/gaussdb/**") // exclude("**/generator/**") } task cleanBuildDir(type: Delete) { delete "${projectDir}/out" } tasks.clean.dependsOn(cleanBuildDir) task javadocJar(type: Jar) { archiveClassifier = 'javadoc' from javadoc } tasks.whenTaskAdded { task -> if (task.name.contains('signMavenJavaPublication')) { task.enabled = new File(project.property('signing.secretKeyRingFile') as String).isFile() } } publishing { repositories { maven { name = "Local" url = layout.buildDirectory.dir('repos/bundles') } maven { def userName = System.getProperty("un") def passWord = System.getProperty("ps") name = "snapshots" url = "https://central.sonatype.com/repository/maven-snapshots/" credentials { username userName password passWord } } } // use example : ./gradlew clean build publishMavenJavaPublicationToLocalRepository publishToMavenCentralPortal -DauthToken='xxxxxx' -x test mavenCentral { repoDir = layout.buildDirectory.dir('repos/bundles') // Base64 encoded of "username:password" authToken = System.getProperty("authToken") // 默认自动发布 AUTOMATIC publishingType = 'USER_MANAGED' } publications { mavenJava(MavenPublication) { from components.java artifact sourcesJar artifact javadocJar pom { name = 'mybatis-plus' packaging 'jar' description = 'An enhanced toolkit of Mybatis to simplify development.' url = 'https://github.com/baomidou/mybatis-plus' scm { connection = 'scm:git@github.com:Codearte/gradle-nexus-staging-plugin.git' developerConnection = 'scm:git@github.com:Codearte/gradle-nexus-staging-plugin.git' url = 'https://github.com/baomidou/mybatis-plus' } licenses { license { name = 'The Apache License, Version 2.0' url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' } } developers { developer { id = 'baomidou' name = 'hubin' email = 'jobob@qq.com' } } withXml { def root = asNode() root.dependencies.'*'.findAll { def d = it d.scope.text() == 'runtime' && project.configurations.findByName("implementation").allDependencies.find { dep -> dep.name == it.artifactId.text() }.each() { d.scope*.value = 'compile' d.appendNode('optional', true) } } } } } } signing { sign publishing.publications.mavenJava } } } ================================================ FILE: changelog-temp.md ================================================ - feat: 支持配置加密在环境变量中使用 ================================================ FILE: gradle.properties ================================================ APP_VERSION=3.5.17-SNAPSHOT APP_GROUP=com.baomidou signing.keyId=1FD337F9 signing.password=243194995 signing.secretKeyRingFile=/home/nieqiurong/signing.gpg #signing.secretKeyRingFile=/Users/ming/Documents/signing.gpg ================================================ FILE: license.txt ================================================ Copyright (c) 2011-2025, baomidou (jobob@qq.com). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: mybatis-plus/build.gradle ================================================ apply plugin: 'kotlin' compileKotlin{ kotlinOptions.jvmTarget = "1.8" } compileTestKotlin { kotlinOptions { freeCompilerArgs = ['-Xjvm-default=all'] } } configurations { testImplementation.exclude module: 'mybatis-plus-jsqlparser-4.9' } dependencies { api project(":mybatis-plus-core") api project(":mybatis-plus-annotation") api project(":mybatis-plus-spring") api "${lib.mybatis}" implementation "${lib."mybatis-spring"}" implementation "${lib."kotlin-stdlib-jdk8"}" implementation project(":mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser-4.9") testImplementation "${lib.'spring-web'}" testImplementation "${lib.'spring-test'}" testImplementation "${lib.'spring-aop'}" testImplementation "${lib.'aspectjweaver'}" testImplementation "${lib."jackson"}" testImplementation "${lib."fastjson"}" testImplementation "${lib."gson"}" testImplementation "${lib.h2}" testImplementation "${lib.sqlserver}" testImplementation "${lib.postgresql}" testImplementation "${lib.oracle}" testImplementation "${lib.mysql}" testImplementation "${lib.'spring-context-support'}" testImplementation "${lib.'spring-jdbc'}" testImplementation "${lib.'slf4j-api'}" testImplementation "${lib.'logback-classic'}" testImplementation "${lib.cglib}" testImplementation "${lib.postgresql}" testImplementation "${lib.gaussdb}" testImplementation project(":mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser") // testCompile ('org.apache.phoenix:phoenix-core:5.0.0-HBase-2.0') } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/BaseDbTest.java ================================================ package com.baomidou.mybatisplus.test; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder; import com.baomidou.mybatisplus.core.MybatisXMLMapperBuilder; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.logging.slf4j.Slf4jImpl; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.transaction.managed.ManagedTransactionFactory; import org.apache.ibatis.type.TypeReference; import org.h2.Driver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.List; import java.util.function.Consumer; /** * @author miemie * @since 2020-06-23 */ public abstract class BaseDbTest extends TypeReference { protected SqlSessionFactory sqlSessionFactory; protected Class mapper; protected JdbcTemplate jdbcTemplate; @SuppressWarnings("unchecked") public BaseDbTest() { DataSource ds = dataSource(); List tableSql = tableSql(); String tableDataSql = tableDataSql(); String mapperXml = mapperXml(); GlobalConfig globalConfig = globalConfig(); List interceptors = interceptors(); Consumer consumer = consumer(); mapper = (Class) getRawType(); jdbcTemplate = new JdbcTemplate(ds); if (CollectionUtils.isNotEmpty(tableSql)) { for (String sql : tableSql) { if (StringUtils.isNotBlank(sql)) { jdbcTemplate.execute(sql); } } } if (StringUtils.isNotBlank(tableDataSql)) { jdbcTemplate.execute(tableDataSql); } MybatisSqlSessionFactoryBuilder builder = new MybatisSqlSessionFactoryBuilder(); Environment environment = new Environment("test", new ManagedTransactionFactory(), ds); MybatisConfiguration configuration = new MybatisConfiguration(environment); if (consumer != null) { consumer.accept(configuration); } GlobalConfigUtils.setGlobalConfig(configuration, globalConfig); configuration.setLogImpl(Slf4jImpl.class); if (StringUtils.isNotBlank(mapperXml)) { try { InputStream inputStream = Resources.getResourceAsStream(mapperXml); MybatisXMLMapperBuilder xmlMapperBuilder = new MybatisXMLMapperBuilder(inputStream, configuration, mapperXml, configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (IOException e) { throw ExceptionUtils.mpe(e); } } configuration.addMapper(mapper); otherMapper().forEach(configuration::addMapper); if (CollectionUtils.isNotEmpty(interceptors)) { interceptors.forEach(configuration::addInterceptor); } sqlSessionFactory = builder.build(configuration); } private DataSource dataSource() { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new Driver()); dataSource.setUrl("jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } protected SqlSession sqlSession(ExecutorType type) { return sqlSessionFactory.openSession(type); } protected void doTest(Consumer consumer) { try (SqlSession sqlSession = sqlSession(null)) { doTest(sqlSession, consumer); } } protected void doTestAutoCommit(Consumer consumer) { try (SqlSession sqlSession = sqlSession(null)) { doTestAutoCommit(sqlSession, consumer); } } protected void doTest(SqlSession sqlSession, Consumer consumer) { doMapper(sqlSession, false, consumer); } protected void doTestAutoCommit(SqlSession sqlSession, Consumer consumer) { doMapper(sqlSession, true, consumer); } protected void doMapper(SqlSession sqlSession, boolean commit, Consumer consumer) { T t = sqlSession.getMapper(mapper); consumer.accept(t); if (commit) { sqlSession.commit(); } else { sqlSession.rollback(); } } protected List tableSql() { return null; } protected String tableDataSql() { return null; } protected String mapperXml() { return null; } protected List interceptors() { return null; } protected GlobalConfig globalConfig() { return GlobalConfigUtils.defaults(); } protected Consumer consumer() { return null; } protected List> otherMapper() { return Collections.emptyList(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/DbTypeTest.java ================================================ package com.baomidou.mybatisplus.test; import com.baomidou.mybatisplus.annotation.DbType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * @author nieqiuqiu */ class DbTypeTest { @Test void testGetDbType() { Assertions.assertEquals(DbType.MYSQL, DbType.getDbType("mysql")); Assertions.assertEquals(DbType.MYSQL, DbType.getDbType("Mysql")); Assertions.assertEquals(DbType.OTHER, DbType.getDbType("other")); Assertions.assertEquals(DbType.OTHER, DbType.getDbType("unknown")); } @Test void testGaussDb(){ Assertions.assertEquals(DbType.GAUSS, DbType.getDbType("gauss")); Assertions.assertEquals(DbType.GAUSS, DbType.getDbType("Gauss")); Assertions.assertEquals(DbType.GAUSS_DB, DbType.getDbType("gaussdb")); Assertions.assertEquals(DbType.GAUSS_DB, DbType.getDbType("GaussDB")); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/MybatisTest.java ================================================ package com.baomidou.mybatisplus.test; import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler; import com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils; import com.baomidou.mybatisplus.test.h2.entity.H2User; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.io.Resources; import org.apache.ibatis.jdbc.ScriptRunner; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.TypeHandlerRegistry; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import javax.sql.DataSource; import java.io.IOException; import java.io.Reader; import java.sql.Connection; import java.sql.SQLException; import java.util.List; /** * 原生Mybatis测试 * * @author nieqiurong 2019/2/27. */ @ExtendWith(MockitoExtension.class) class MybatisTest { private static SqlSessionFactory sqlSessionFactory; @BeforeAll public static void init() throws IOException, SQLException { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(reader); DataSource dataSource = sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(); Configuration configuration = sqlSessionFactory.getConfiguration(); TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); /* * 如果是将defaultEnumTypeHandler设置成MP的处理器, * 请自行注册处理非MP枚举处理类的原生枚举类型 */ typeHandlerRegistry.register(AgeEnum.class, MybatisEnumTypeHandler.class); //这里我举起了个栗子 Connection connection = dataSource.getConnection(); ScriptRunner scriptRunner = new ScriptRunner(connection); scriptRunner.runScript(Resources.getResourceAsReader("h2/user.ddl.sql")); } @Test void test() { try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) { H2UserMapper mapper = sqlSession.getMapper(H2UserMapper.class); Assertions.assertEquals(1, mapper.myInsertWithNameVersion("test", 2)); Assertions.assertEquals(1, mapper.insert(new H2User("test"))); Assertions.assertEquals(2, mapper.selectCount(new QueryWrapper().lambda().eq(H2User::getName, "test"))); Assertions.assertEquals(2, mapper.delete(new QueryWrapper().lambda().eq(H2User::getName, "test"))); H2User h2User = new H2User(66L, "66666", AgeEnum.THREE, 666); Assertions.assertEquals(1, mapper.insert(h2User)); h2User.setName("7777777777"); H2User user = mapper.selectById(66L); Assertions.assertNotNull(user); Assertions.assertEquals(AgeEnum.THREE, user.getAge()); Assertions.assertNotNull(user.getTestType()); Assertions.assertEquals(1, mapper.updateById(new H2User(66L, "777777"))); Assertions.assertEquals(1, mapper.deleteById(66L)); Assertions.assertNull(mapper.selectById(66L)); } } @Test void testBatchAutoCommitFalse() { var userList = List.of(new H2User(2000L, "测试"), new H2User(2001L, "测试")); MybatisBatchUtils.execute(sqlSessionFactory, userList, H2UserMapper.class.getName() + ".insert"); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); for (H2User u : userList) { Assertions.assertNotNull(mapper.selectById(u.getTestId())); } } } @Test void testBatchAutoCommitFalseOnException1() { Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory, List.of(new H2User(1000L, "测试"), new H2User(1000L, "测试")), H2UserMapper.class.getName() + ".insert")); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); Assertions.assertNull(mapper.selectById(1000L)); } Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory, List.of(new H2User(1001L, "测试"), new H2User(1001L, "测试")), H2UserMapper.class.getName() + ".insert", 1)); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); Assertions.assertNotNull(mapper.selectById(1001L)); } } @Test void testBatchAutoCommitFalseOnException2() { List userList = List.of(new H2User(1010L, "测试"), new H2User(1011L, "测试")); Assertions.assertThrowsExactly(RuntimeException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory, userList, H2UserMapper.class.getName() + ".insert", parameter -> { if (parameter.getTestId() == 1011L) { throw new RuntimeException("出异常了"); } return parameter; })); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); Assertions.assertNull(mapper.selectById(1010L)); } } @Test void testBatchAutoCommitTrue() { var userList = List.of(new H2User(3000L, "测试"), new H2User(3001L, "测试")); MybatisBatchUtils.execute(sqlSessionFactory, userList, true, H2UserMapper.class.getName() + ".insert"); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); for (H2User u : userList) { Assertions.assertNotNull(mapper.selectById(u.getTestId())); } } } @Test void testBatchAutoCommitTrueOnException1() { Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory, List.of(new H2User(4000L, "测试"), new H2User(4000L, "测试")), true, H2UserMapper.class.getName() + ".insert")); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); Assertions.assertNotNull(mapper.selectById(4000L)); } Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory, List.of(new H2User(4020L, "测试"), new H2User(4020L, "测试"), 1), true, H2UserMapper.class.getName() + ".insert")); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); Assertions.assertNotNull(mapper.selectById(4020L)); } } @Test void testBatchAutoCommitTrueOnException2() { Assertions.assertThrowsExactly(RuntimeException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory, List.of(new H2User(4010L, "测试"), new H2User(4011L, "测试")), true, H2UserMapper.class.getName() + ".insert", parameter -> { if (parameter.getTestId() == 4011L) { throw new RuntimeException("出异常了"); } return parameter; })); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); Assertions.assertNull(mapper.selectById(4010L)); Assertions.assertNull(mapper.selectById(4011L)); } Assertions.assertThrowsExactly(RuntimeException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory, List.of(new H2User(4015L, "测试"), new H2User(4016L, "测试")), true, H2UserMapper.class.getName() + ".insert", parameter -> { if (parameter.getTestId() == 4016L) { throw new RuntimeException("出异常了"); } return parameter; },1)); try (var sqlSession = sqlSessionFactory.openSession()) { var mapper = sqlSession.getMapper(H2UserMapper.class); Assertions.assertNotNull(mapper.selectById(4015L)); Assertions.assertNull(mapper.selectById(4016L)); } } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/OgnlTest.java ================================================ package com.baomidou.mybatisplus.test; import lombok.Data; import org.apache.ibatis.scripting.xmltags.OgnlCache; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; /** * @author nieqiuqiu */ class OgnlTest { @Data private static class Bean { private String name; private Map properties; } /** * size keys keySet values isEmpty 这五个key值需要注意一下. * * @see org.apache.ibatis.ognl.MapPropertyAccessor#getProperty */ @Test void test() { Map propertiesMap = new HashMap<>(); propertiesMap.put("color", "yellow"); propertiesMap.put("size", "xxxL"); Assertions.assertEquals("yellow", OgnlCache.getValue("color", propertiesMap)); Assertions.assertEquals(2, OgnlCache.getValue("size", propertiesMap)); Assertions.assertFalse((Boolean) OgnlCache.getValue("isEmpty", propertiesMap)); Assertions.assertNull(OgnlCache.getValue("['isEmpty']", propertiesMap)); Assertions.assertEquals("xxxL", OgnlCache.getValue("['size']", propertiesMap)); Assertions.assertEquals("yellow", OgnlCache.getValue("['color']", propertiesMap)); Bean bean = new Bean(); bean.setName("靓仔"); bean.setProperties(propertiesMap); Assertions.assertEquals("靓仔", OgnlCache.getValue("name", bean)); Assertions.assertEquals("靓仔", OgnlCache.getValue("['name']", bean)); Assertions.assertEquals(2, OgnlCache.getValue("properties.size", bean)); Assertions.assertEquals("xxxL", OgnlCache.getValue("properties['size']", bean)); Assertions.assertEquals("yellow", OgnlCache.getValue("properties.color", bean)); Assertions.assertEquals("yellow", OgnlCache.getValue("properties['color']", bean)); Assertions.assertFalse((Boolean) OgnlCache.getValue("properties.isEmpty", bean)); Assertions.assertNull(OgnlCache.getValue("properties['isEmpty']", bean)); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/PageTest.java ================================================ package com.baomidou.mybatisplus.test; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONB; import com.alibaba.fastjson2.JSONReader; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import org.apache.ibatis.reflection.property.PropertyCopier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; import org.springframework.beans.BeanUtils; import org.springframework.cglib.beans.BeanCopier; import java.util.Collections; import java.util.List; /** * @author nieqiurong 2020/3/20. */ class PageTest { private final ObjectMapper objectMapper = new ObjectMapper(); private final Gson gson = new Gson(); @Test @EnabledOnJre(JRE.JAVA_8) void testCopy() { Page page1 = new Page<>(2, 10, 100, false); page1.setOptimizeCountSql(false); page1.setOrders(Collections.singletonList(OrderItem.asc("test"))); Page page2 = new Page<>(); PropertyCopier.copyBeanProperties(Page.class, page1, page2); Assertions.assertEquals(page1.getCurrent(), page2.getCurrent()); Assertions.assertEquals(page1.getTotal(), page2.getTotal()); Assertions.assertEquals(page1.getSize(), page2.getSize()); Assertions.assertEquals(page1.optimizeCountSql(), page2.optimizeCountSql()); Assertions.assertEquals(page1.searchCount(), page2.searchCount()); Assertions.assertEquals(page1.orders().size(), page2.orders().size()); Page page3 = new Page<>(); BeanUtils.copyProperties(page1, page3); Assertions.assertEquals(page1.getCurrent(), page3.getCurrent()); Assertions.assertEquals(page1.getTotal(), page3.getTotal()); Assertions.assertEquals(page1.getSize(), page3.getSize()); Assertions.assertEquals(page1.optimizeCountSql(), page3.optimizeCountSql()); Assertions.assertEquals(page1.searchCount(), page3.searchCount()); Assertions.assertEquals(page1.orders().size(), page3.orders().size()); Page page4 = new Page<>(); BeanCopier.create(page1.getClass(), page4.getClass(), false).copy(page1, page4, null); //链式的set方法会导致属性BeanCopier的拷贝方法失败 https://github.com/cglib/cglib/issues/108. Assertions.assertTrue(page4.optimizeCountSql()); Assertions.assertTrue(page4.searchCount()); Assertions.assertEquals(1, page4.getCurrent()); Assertions.assertEquals(1, page4.orders().size()); Assertions.assertEquals(10, page4.getSize()); } @Test void testPageToJson() throws JsonProcessingException { var page = new Page<>(1, 10, 2000); page.setOrders(List.of(OrderItem.asc("a"))); //page无法序列化排序等其他属性 {"records":[],"total":2000,"size":10,"current":1,"pages":200} assertPage(page); } @Test void testPageDtoToJson() throws JsonProcessingException { var page = new PageDTO<>(1, 10, 100); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setOptimizeCountSql(false); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setSearchCount(false); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setOptimizeJoinOfCountSql(false); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setRecords(List.of("1", "2", "3")); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setRecords(List.of("1", "2", "3")); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setMaxLimit(1000L); page.setRecords(List.of("1", "2", "3")); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setRecords(List.of("1", "2", "3")); page.setCountId("123"); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setOrders(OrderItem.descs("a","b")); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setOrders(OrderItem.ascs("a","b")); assertPageDto(page); page = new PageDTO<>(1, 10, 100); page.setRecords(List.of("1", "2", "3")); page.setOrders(OrderItem.ascs("a","b")); assertPageDto(page); } private void assertPage(Page source) throws JsonProcessingException { toConvert(source, Page.class).forEach(target -> { Assertions.assertEquals(source.getCurrent(), target.getCurrent()); Assertions.assertEquals(source.getTotal(), target.getTotal()); Assertions.assertEquals(source.getSize(), target.getSize()); Assertions.assertEquals(source.countId(), target.countId()); Assertions.assertEquals(source.getRecords().size(), target.getRecords().size()); Assertions.assertEquals(source.getPages(), target.getPages()); }); } private > List toConvert(T source, Class tClass) throws JsonProcessingException { return List.of( objectMapper.readValue(objectMapper.writeValueAsString(source), tClass), gson.fromJson(gson.toJson(source), tClass), JSON.parseObject(JSON.toJSONString(source), tClass), // dubbo 反序列化下出现问题 https://github.com/alibaba/fastjson2/issues/2734 JSONB.parseObject(JSONB.toBytes(source), tClass, JSONReader.Feature.FieldBased ), com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson.JSON.toJSONString(source), tClass) ); } private void assertPageDto(PageDTO source) throws JsonProcessingException { toConvert(source, PageDTO.class).forEach(target -> { Assertions.assertEquals(source.toString(), target.toString()); Assertions.assertEquals(source.getCurrent(), target.getCurrent()); Assertions.assertEquals(source.getTotal(), target.getTotal()); Assertions.assertEquals(source.getSize(), target.getSize()); Assertions.assertEquals(source.countId(), target.getCountId()); Assertions.assertEquals(source.countId(), target.countId()); Assertions.assertEquals(source.searchCount(), target.isSearchCount()); Assertions.assertEquals(source.searchCount(), target.searchCount()); Assertions.assertEquals(source.optimizeCountSql(), target.isOptimizeCountSql()); Assertions.assertEquals(source.optimizeCountSql(), target.optimizeCountSql()); Assertions.assertEquals(source.optimizeJoinOfCountSql(), target.optimizeJoinOfCountSql()); Assertions.assertEquals(source.optimizeJoinOfCountSql(), target.isOptimizeJoinOfCountSql()); Assertions.assertEquals(source.getRecords().size(), target.getRecords().size()); Assertions.assertEquals(source.maxLimit(), target.getMaxLimit()); Assertions.assertEquals(source.maxLimit(), target.maxLimit()); Assertions.assertEquals(source.getOrders().size(), target.getOrders().size()); Assertions.assertEquals(source.getOrders().size(), target.orders().size()); Assertions.assertEquals(source.getPages(), target.getPages()); }); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/autoresultmap/AutoResultMapTest.java ================================================ package com.baomidou.mybatisplus.test.autoresultmap; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ public class AutoResultMapTest extends BaseDbTest { @Test void test() { doTestAutoCommit(m -> m.insert(new Entity().setName("老王").setGg(new Entity.Gg("老王")))); doTest(m -> { Entity entity = m.selectOne(null); assertThat(entity).as("插入正常").isNotNull(); assertThat(entity.getName()).as("名称不一致正常").isNotNull(); assertThat(entity.getGg()).as("typeHandler正常").isNotNull(); assertThat(entity.getGg().getName()).as("是老王").isEqualTo("老王"); }); doTest(m -> { Entity entity = new Entity().setName("老王"); m.selectOne(Wrappers.lambdaQuery(entity).ne(Entity::getId, 1)); }); } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (\n" + "id BIGINT(20) NOT NULL,\n" + "x_name VARCHAR(20) NOT NULL,\n" + "gg VARCHAR(255) NULL DEFAULT NULL,\n" + "PRIMARY KEY (id)" + ")"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/autoresultmap/Entity.java ================================================ package com.baomidou.mybatisplus.test.autoresultmap; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data @Accessors(chain = true) @TableName(autoResultMap = true) public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; @TableField("x_name") private String name; @TableField(typeHandler = GsonTypeHandler.class) private Gg gg; @Data @NoArgsConstructor @AllArgsConstructor public static class Gg { private String name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/autoresultmap/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.autoresultmap; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/batch/BatchTest.java ================================================ package com.baomidou.mybatisplus.test.batch; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.executor.BatchExecutor; import org.apache.ibatis.session.ExecutorType; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2021-01-06 */ class BatchTest extends BaseDbTest { @Test void save() { doTestAutoCommit(sqlSession(ExecutorType.BATCH), i -> { int i1 = i.insert(new Entity("老王")); assertThat(i1).isEqualTo(BatchExecutor.BATCH_UPDATE_RETURN_VALUE); int i2 = i.insert(new Entity("老李")); assertThat(i2).isEqualTo(BatchExecutor.BATCH_UPDATE_RETURN_VALUE); }); doTest(i -> { assertThat(i.selectCount(null)).isEqualTo(2); }); } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/batch/Entity.java ================================================ package com.baomidou.mybatisplus.test.batch; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data @NoArgsConstructor public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; private String name; public Entity(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/batch/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.batch; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/page/PageCache.java ================================================ package com.baomidou.mybatisplus.test.cache.page; import lombok.Data; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data public class PageCache implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/page/PageCacheMapper.java ================================================ package com.baomidou.mybatisplus.test.cache.page; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.CacheNamespace; import org.apache.ibatis.annotations.ResultType; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.session.ResultHandler; /** * @author miemie * @since 2020-06-23 */ @CacheNamespace public interface PageCacheMapper extends BaseMapper { @Select("") Page otherPage(Page page, PageCacheTest.Tj tj); @ResultType(PageCache.class) @Select("") void otherPageHandler(Page page, PageCacheTest.Tj tj, ResultHandler handler); @Select("") Long otherCount(PageCacheTest.Tj tj); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/page/PageCacheTest.java ================================================ package com.baomidou.mybatisplus.test.cache.page; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.test.BaseDbTest; import lombok.Data; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.executor.result.DefaultResultHandler; import org.apache.ibatis.plugin.Interceptor; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ class PageCacheTest extends BaseDbTest { @Test void page() { Cache cache = sqlSessionFactory.getConfiguration().getCache(PageCacheMapper.class.getName()); assertThat(cache).as("使用 @CacheNamespace 指定了使用缓存").isNotNull(); final long total = 5; final long size = 3; doTestAutoCommit(m -> { Page page = new Page<>(1, size); IPage result = m.selectPage(page, null); assertThat(page).as("对象是同一个").isEqualTo(result); assertThat(result.getTotal()).isEqualTo(total); assertThat(result.getRecords().size()).isEqualTo(size); }); assertThat(cache.getSize()).as("一条count缓存一条分页缓存").isEqualTo(2); doTestAutoCommit(m -> { Page page = new Page<>(1, size); IPage result = m.selectPage(page, null); assertThat(page).isEqualTo(result); assertThat(result.getTotal()).isEqualTo(total); assertThat(result.getRecords().size()).isEqualTo(size); }); assertThat(cache.getSize()).as("因为命中缓存了所以还是2条").isEqualTo(2); doTestAutoCommit(m -> { Page page = new Page<>(1, size); page.addOrder(OrderItem.asc("id")); IPage result = m.selectPage(page, null); assertThat(page).isEqualTo(result); assertThat(result.getTotal()).isEqualTo(total); assertThat(result.getRecords().size()).isEqualTo(size); }); assertThat(cache.getSize()).as("条件不一样了,缓存变为3条").isEqualTo(3); doTestAutoCommit(m -> m.insert(new PageCache())); assertThat(cache.getSize()).as("update 操作清除了所有缓存").isEqualTo(0); doTestAutoCommit(m -> { Page page = new Page<>(1, size); IPage result = m.selectPage(page, null); assertThat(page).isEqualTo(result); assertThat(result.getTotal()).isEqualTo(total + 1); assertThat(result.getRecords().size()).isEqualTo(size); }); assertThat(cache.getSize()).as("一条count缓存一条分页缓存").isEqualTo(2); doTest(i -> { Page page = new Page<>(1, size); page.setCountId("otherCount"); i.otherPage(page, new Tj()); }); } @Test void handlerTest() { doTest(i -> { Page page = new Page<>(1, 2); i.otherPageHandler(page, new Tj(), new DefaultResultHandler()); System.out.println(page); }); } @Override protected List interceptors() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return Collections.singletonList(interceptor); } @Override protected String tableDataSql() { return "insert into page_cache(id,name) values(1,'1'),(2,'2'),(3,'3'),(4,'4'),(5,'5');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists page_cache", "CREATE TABLE IF NOT EXISTS page_cache (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } @Data public static class Tj { private boolean name = true; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/xml/XmlCache.java ================================================ package com.baomidou.mybatisplus.test.cache.xml; import lombok.Data; import java.io.Serializable; /** * @author miemie * @since 2022-03-07 */ @Data public class XmlCache implements Serializable { private static final long serialVersionUID = 907016853109330217L; private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/xml/XmlCacheMapper.java ================================================ package com.baomidou.mybatisplus.test.cache.xml; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.CacheNamespaceRef; /** * @author miemie * @since 2020-06-23 */ @CacheNamespaceRef(XmlCacheMapper.class) public interface XmlCacheMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/xml/XmlCacheTest.java ================================================ package com.baomidou.mybatisplus.test.cache.xml; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.cache.Cache; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ class XmlCacheTest extends BaseDbTest { @Test void page() { Cache cache = sqlSessionFactory.getConfiguration().getCache(XmlCacheMapper.class.getName()); assertThat(cache).as("使用 xml-cache 指定了使用缓存").isNotNull(); final long total = 5; doTestAutoCommit(m -> { List result = m.selectList(null); assertThat(result).isNotNull(); assertThat(result.size()).isEqualTo(total); }); assertThat(cache.getSize()).as("一条缓存").isEqualTo(1); } @Override protected String tableDataSql() { return "insert into xml_cache(id,name) values(1,'1'),(2,'2'),(3,'3'),(4,'4'),(5,'5');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists xml_cache", "CREATE TABLE IF NOT EXISTS xml_cache (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/chainwrapper/ChainWrapperTest.java ================================================ package com.baomidou.mybatisplus.test.chainwrapper; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; /** * @author miemie * @since 2020-06-23 */ public class ChainWrapperTest extends BaseDbTest { @Test void test() { final String id = "id"; Entity entity = new Entity(); doTest(i -> i.queryChain() .func(j -> j.isNotNull(id)) .func(entity.getId() != null, j -> j.eq("id", entity.getId()))// 不会npe,也不会加入sql .and(j -> j.isNotNull(id)) .or(j -> j.isNotNull(id)) .nested(j -> j.isNotNull(id)) .not(j -> j.isNull(id)) .list()); doTest(i -> i.queryChain().groupBy("id").list()); doTest(i -> i.queryChain().groupBy(List.of("id")).list()); doTest(i -> i.queryChain().groupBy(List.of("id", "name")).list()); } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1,'1'),(2,'2');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/chainwrapper/ChainWrappersTest.java ================================================ package com.baomidou.mybatisplus.test.chainwrapper; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers; import com.baomidou.mybatisplus.test.BaseDbTest; /** * @author VampireAchao * @since 2022-04-18 */ public class ChainWrappersTest extends BaseDbTest { @Test void test() { final String id = "id"; Entity entity = new Entity(); Assertions.assertAll(() -> ChainWrappers.queryChain(entity.getClass()) .func(j -> j.isNotNull(id)) .func(entity.getId() != null, j -> j.eq("id", entity.getId()))// 不会npe,也不会加入sql .and(j -> j.isNotNull(id)) .or(j -> j.isNotNull(id)) .nested(j -> j.isNotNull(id)) .not(j -> j.isNull(id)) .list()); } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1,'1'),(2,'2');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/chainwrapper/Entity.java ================================================ package com.baomidou.mybatisplus.test.chainwrapper; import lombok.Data; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/chainwrapper/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.chainwrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper; import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { default QueryChainWrapper queryChain() { return ChainWrappers.queryChain(this); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/Entity.java ================================================ package com.baomidou.mybatisplus.test.enums; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data @Accessors(chain = true) @TableName(autoResultMap = true) public class Entity implements Serializable { private static final long serialVersionUID = 5094767605376915138L; private Long id; private EnumStr enumStr; private EnumInt enumInt; /** * 使用 EnumOrdinalTypeHandler */ private EnumOrdinal enumOrdinal; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.enums; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { Entity findById(Long id); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EnumInt.java ================================================ package com.baomidou.mybatisplus.test.enums; import com.baomidou.mybatisplus.annotation.EnumValue; import lombok.AllArgsConstructor; import lombok.Getter; /** * @author miemie * @since 2020-06-23 */ @Getter @AllArgsConstructor public enum EnumInt { ONE(1), TWO(2); @EnumValue private final int value; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EnumOrdinal.java ================================================ package com.baomidou.mybatisplus.test.enums; import lombok.Getter; /** * @author miemie * @since 2020-07-21 */ @Getter public enum EnumOrdinal { ONE, TWO, } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EnumStr.java ================================================ package com.baomidou.mybatisplus.test.enums; import com.baomidou.mybatisplus.annotation.EnumValue; import lombok.AllArgsConstructor; import lombok.Getter; /** * @author miemie * @since 2020-06-23 */ @Getter @AllArgsConstructor public enum EnumStr { ONE("1"), TWO("2"); @EnumValue private final String value; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EnumTest.java ================================================ package com.baomidou.mybatisplus.test.enums; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ class EnumTest extends BaseDbTest { @Test void test() { Long id = 1L; doTestAutoCommit(m -> { Entity entity = new Entity().setId(id).setEnumInt(EnumInt.ONE).setEnumStr(EnumStr.TWO).setEnumOrdinal(EnumOrdinal.TWO); int insert = m.insert(entity); assertThat(insert).as("插入成功").isEqualTo(1); }); doTestAutoCommit(m -> { Entity entity = m.selectById(id); assertThat(entity).as("查出刚刚插入的数据").isNotNull(); assertThat(entity.getEnumInt()).as("枚举正确").isEqualTo(EnumInt.ONE); assertThat(entity.getEnumStr()).as("枚举正确").isEqualTo(EnumStr.TWO); assertThat(entity.getEnumOrdinal()).as("枚举正确").isEqualTo(EnumOrdinal.TWO); entity.setEnumOrdinal(EnumOrdinal.ONE); m.updateById(entity); }); doTest(m -> { Entity entity = m.findById(id); assertThat(entity).as("查出刚刚插入的数据").isNotNull(); assertThat(entity.getEnumInt()).as("枚举正确").isEqualTo(EnumInt.ONE); assertThat(entity.getEnumStr()).as("枚举正确").isEqualTo(EnumStr.TWO); assertThat(entity.getEnumOrdinal()).as("枚举正确").isEqualTo(EnumOrdinal.ONE); }); } @Override protected Consumer consumer() { return i -> i.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (\n" + "id BIGINT(20) NOT NULL,\n" + "enum_int integer NULL DEFAULT NULL,\n" + "enum_str VARCHAR(30) NULL DEFAULT NULL,\n" + "enum_ordinal integer NULL DEFAULT NULL,\n" + "PRIMARY KEY (id)" + ")"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/fill/FillEntity.java ================================================ package com.baomidou.mybatisplus.test.fill; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * @author nieqiurong 2023年9月29日 */ @Data @TableName(value = "t_fill") public class FillEntity { private Long id; @TableField(fill = FieldFill.INSERT_UPDATE) private String name; private transient int seq; public FillEntity() { } public FillEntity(Long id) { this.id = id; } public FillEntity(Long id, String name) { this.id = id; this.name = name; } void addSeq(){ this.seq ++; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/fill/FillMapper.java ================================================ package com.baomidou.mybatisplus.test.fill; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @author nieqiurong 2023年9月29日 */ public interface FillMapper extends BaseMapper { void insertBatch1(List entityList); void insertBatch2(@Param("mpList") List entityList); void insertBatch3(@Param("mybatisList") List entityList); void insertBatch4(@Param("list") List entityList, String a, String b, String c); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/fill/FillTest.java ================================================ package com.baomidou.mybatisplus.test.fill; import com.baomidou.mybatisplus.core.batch.MybatisBatch; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.reflection.MetaObject; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author nieqiurong 2023年9月29日 */ public class FillTest extends BaseDbTest { @Test void testInsert() { doTest(mapper -> { var entity = new FillEntity(); mapper.insert(entity); assertEntity(entity, "insertAdmin", 1); }); } @Test void testInsertBatch1() { doTest(mapper -> { var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity())); mapper.insertBatch1(entityList); for (FillEntity entity : entityList) { assertEntity(entity, "insertAdmin", 1); } }); } @Test void testInsertBatch2() { doTest(mapper -> { var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity())); mapper.insertBatch2(entityList); for (FillEntity entity : entityList) { assertEntity(entity, "insertAdmin", 1); } }); } @Test void testInsertBatch3() { doTest(mapper -> { var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity())); mapper.insertBatch3(entityList); for (FillEntity entity : entityList) { assertEntity(entity, "insertAdmin", 1); } }); } @Test void testInsertBatch4() { doTest(mapper -> { var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity())); mapper.insertBatch4(entityList, "", "", ""); for (FillEntity entity : entityList) { assertEntity(entity, "insertAdmin", 1); } }); } @Test void testBatch() { var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity())); var method = new MybatisBatch.Method(FillMapper.class); MybatisBatchUtils.execute(sqlSessionFactory, entityList, method.insert()); for (FillEntity entity : entityList) { assertEntity(entity, "insertAdmin", 1); } } void assertEntity(FillEntity entity, String name, int seq) { Assertions.assertEquals(entity.getName(), name); Assertions.assertEquals(entity.getSeq(), seq); } @Override protected GlobalConfig globalConfig() { GlobalConfig globalConfig = super.globalConfig(); globalConfig.setMetaObjectHandler(new MetaObjectHandler() { @Override public void insertFill(MetaObject metaObject) { var entity = (FillEntity) metaObject.getOriginalObject(); entity.setName("insertAdmin"); entity.addSeq(); } @Override public void updateFill(MetaObject metaObject) { var entity = (FillEntity) metaObject.getOriginalObject(); entity.setName("updateAdmin"); entity.addSeq(); } }); return globalConfig; } @Override protected List tableSql() { return Arrays.asList("drop table if exists t_fill", "CREATE TABLE IF NOT EXISTS t_fill (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/gaussdb/Demo.java ================================================ package com.baomidou.mybatisplus.test.gaussdb; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import lombok.ToString; import java.time.LocalDate; import java.time.LocalDateTime; /** * @author nieqiurong * @since 3.5.13 */ @Data @ToString public class Demo { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private LocalDate birthday; private LocalDateTime createTime; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/gaussdb/DemoMapper.java ================================================ package com.baomidou.mybatisplus.test.gaussdb; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; /** * @author nieqiurong * @since 3.5.13 */ @Mapper public interface DemoMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/gaussdb/GaussdbTest.java ================================================ package com.baomidou.mybatisplus.test.gaussdb; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.huawei.gaussdb.jdbc.Driver; import lombok.AllArgsConstructor; import lombok.Data; import org.apache.ibatis.datasource.pooled.PooledDataSource; import org.apache.ibatis.jdbc.SqlRunner; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; /** * @author nieqiurong * @since 3.5.13 */ public class GaussdbTest { private static final Logger LOGGER = LoggerFactory.getLogger(GaussdbTest.class); private static final String CREATE_TABLE = """ DROP TABLE IF EXISTS "demo"; CREATE TABLE "demo" ( "id" bigserial, "name" varchar(255), "age" int4, "birthday" date, "create_time" timestamp, PRIMARY KEY ("id") ) ; COMMENT ON COLUMN "demo"."id" IS '主键'; COMMENT ON COLUMN "demo"."name" IS '姓名'; COMMENT ON COLUMN "demo"."age" IS '年龄'; COMMENT ON COLUMN "demo"."birthday" IS '生日'; COMMENT ON COLUMN "demo"."create_time" IS '创建时间'; """; private static SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.addMapper(DemoMapper.class); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); sqlSessionFactory.setConfiguration(configuration); return sqlSessionFactory.getObject(); } private static Stream dataSource() { String host = "127.0.0.1"; int port = 8000; String user = "root"; String dbPassword = "123456"; return Stream.of( new DbConfig("mysql", new PooledDataSource(Driver.class.getName(), "jdbc:gaussdb://" + host + ":" + port + "/test_mysql", user, dbPassword)), new DbConfig("oracle", new PooledDataSource(Driver.class.getName(), "jdbc:gaussdb://" + host + ":" + port + "/test_oracle", user, dbPassword)) ); } @Data @AllArgsConstructor private static class DbConfig { private final String name; private final DataSource dataSource; @Override public String toString() { return name; } } @ParameterizedTest @MethodSource("dataSource") void test(DbConfig dbConfig) throws Exception { DataSource dataSource = dbConfig.getDataSource(); new SqlRunner(dataSource.getConnection()).run(CREATE_TABLE); try (SqlSession sqlSession = sqlSessionFactory(dataSource).openSession(true)) { DemoMapper demoMapper = sqlSession.getMapper(DemoMapper.class); LOGGER.info("delete all data:{}", demoMapper.delete(Wrappers.emptyWrapper())); Demo demo = new Demo(); demo.setAge(12); demo.setName("12岁的用户"); demo.setBirthday(LocalDate.of(2013, 1, 1)); demo.setCreateTime(LocalDateTime.now()); LOGGER.info("insert data:{}", demoMapper.insert(demo)); Demo selectDemo = demoMapper.selectById(demo.getId()); Assertions.assertNotNull(selectDemo); LOGGER.info("select data:{}", selectDemo); Demo updateDemo = new Demo(); updateDemo.setAge(23); updateDemo.setId(selectDemo.getId()); Assertions.assertEquals(1, demoMapper.updateById(updateDemo)); LOGGER.info("delete by id:{}", demoMapper.deleteById(demo)); List demoList = new ArrayList<>(); for (int i = 0; i < 50; i++) { demo = new Demo(); demo.setAge(1); demo.setName("demo" + (i + 1)); demo.setBirthday(LocalDate.of(2020, 12, 1)); demo.setCreateTime(LocalDateTime.now()); demoList.add(demo); } LOGGER.info("insert data on batch:{}", demoMapper.insert(demoList)); Long count = demoMapper.selectCount(Wrappers.emptyWrapper()); LOGGER.info("select count:{}", count); Assertions.assertEquals(50L, count); Page page = demoMapper.selectPage(new Page<>(1, 10), Wrappers.emptyWrapper()); Assertions.assertEquals(50, page.getTotal()); Assertions.assertEquals(10, page.getSize()); for (Demo record : page.getRecords()) { LOGGER.info("record:{}", record); } LOGGER.info("next page ."); page = demoMapper.selectPage(new Page<>(2, 10), Wrappers.emptyWrapper()); Assertions.assertEquals(50, page.getTotal()); Assertions.assertEquals(10, page.getSize()); for (Demo record : page.getRecords()) { LOGGER.info("record:{}", record); } } } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/ActiveRecordTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.test.h2.entity.H2Student; import com.baomidou.mybatisplus.test.h2.service.IH2StudentService; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * ActiveRecord 测试 * * @author nieqiurong 2018/7/27. */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"}) class ActiveRecordTest { private static final Logger LOGGER = LoggerFactory.getLogger(ActiveRecordTest.class); @Autowired private IH2StudentService h2StudentService; @Test @Transactional @Order(1) void testInsert() { H2Student student = new H2Student(3L, "测试学生", 2); assertThat(student.insert()).isTrue(); System.out.println(student.getId()); assertThat(student.getId()).isEqualTo(3); student.setId(null); assertThat(student.insert()).isTrue(); System.out.println(student.getId()); } @Test @Order(2) void testUpdate() { H2Student student = new H2Student(1L, "Tom长大了", 2); Assertions.assertTrue(student.updateById()); student.setName("不听话的学生"); Assertions.assertTrue(student.update(new QueryWrapper().gt("id", 10))); } @Test @Order(3) void testSelect() { H2Student student = new H2Student(); student.setId(1L); Assertions.assertNotNull(student.selectById()); Assertions.assertNotNull(student.selectById(1L)); } @Test @Order(4) void testSelectList() { H2Student student = new H2Student(); List students = student.selectList(new QueryWrapper<>(student)); students.forEach($this -> LOGGER.info("用户信息:{}", $this)); Assertions.assertTrue(students.size() > 1); } @Test @Order(5) void testSelectPage() { IPage page = new Page<>(1, 10); H2Student student = new H2Student(); page = student.selectPage(page, new QueryWrapper<>(student)); List records = page.getRecords(); LOGGER.info("总数:{}", page.getTotal()); records.forEach($this -> LOGGER.info("用户信息:{}", $this)); Assertions.assertTrue(page.getTotal() > 1); } @Test @Order(6) void testSelectCount() { H2Student student = new H2Student(); long count = new H2Student().selectCount(new QueryWrapper<>(student)); LOGGER.info("count:{}", count); Assertions.assertTrue(count > 1); } @Test @Order(7) void testInsertOrUpdate() { H2Student student = new H2Student(2L, "Jerry也长大了", 2); Assertions.assertTrue(student.insertOrUpdate()); student.setId(null); Assertions.assertTrue(student.insertOrUpdate()); } @Test @Order(8) void testSelectAll() { H2Student student = new H2Student(); List students = student.selectAll(); Assertions.assertNotNull(students); students.forEach($this -> LOGGER.info("用户信息:{}", $this)); } @Test @Order(9) void testSelectOne() { H2Student student = new H2Student(); Assertions.assertNotNull(student.selectOne(new QueryWrapper<>())); } @Test @Order(10) void testTransactional() { try { h2StudentService.testTransactional(); } catch (MybatisPlusException e) { List students = new H2Student().selectList(new QueryWrapper().lambda().like(H2Student::getName, "tx")); Assertions.assertTrue(CollectionUtils.isEmpty(students)); } } @Test @Order(11) void testDelete() { H2Student student = new H2Student(); student.setId(2L); Assertions.assertTrue(student.deleteById()); Assertions.assertTrue(student.deleteById(12L)); Assertions.assertTrue(student.delete(new QueryWrapper().gt("id", 10))); } @Test @Order(12) void sqlCommentTest() { String name = "name1", nameNew = "name1New"; H2Student student = new H2Student().setName(name).setAge(2); student.delete(new QueryWrapper().comment("deleteAllStu")); Assertions.assertTrue(student.insert()); boolean updated = new H2Student().setName(nameNew).update(new QueryWrapper().comment("updateStuName1").lambda() .eq(H2Student::getName, name) ); Assertions.assertTrue(updated); H2Student h2Student = student.selectOne( new QueryWrapper().lambda().comment("getStuByUniqueName") .eq(H2Student::getName, nameNew) ); Assertions.assertNotNull(h2Student); LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda().ge(H2Student::getAge, 1); long userCount = student.selectCount(queryWrapper.comment("getStuCount")); Assertions.assertEquals(1, userCount); List h2StudentList = student.selectList(queryWrapper.comment("getStuList")); Assertions.assertEquals(1, h2StudentList.size()); IPage h2StudentIPage = student.selectPage(new Page<>(1, 10), queryWrapper.comment("getStuPage")); Assertions.assertEquals(1, h2StudentIPage.getRecords().size()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/BaseTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.test.h2.entity.H2User; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.annotation.DirtiesContext; import java.util.List; @DirtiesContext public class BaseTest { @Autowired protected JdbcTemplate jdbcTemplate; protected static final String NQQ = "聂秋秋"; protected void log(Object object) { System.out.println(object); } protected List queryByName(String name) { String sql = "select TEST_ID, NAME, AGE,LAST_UPDATED_DT from h2user "; if (name != null) { sql += "where name='" + name + "'"; } return jdbcTemplate.query(sql, (rs, rowNum) -> { H2User u = new H2User(); u.setTestId(rs.getLong("TEST_ID")); u.setName(rs.getString("NAME")); u.setAge(AgeEnum.parseValue(rs.getInt("AGE"))); u.setLastUpdatedDt(rs.getDate("LAST_UPDATED_DT")); return u; }); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/CustomFillTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.test.h2.customfill.mapper.TestModelMapper; import com.baomidou.mybatisplus.test.h2.customfill.model.TestModel; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-custom-fill-test-h2.xml"}) public class CustomFillTest { @Autowired private TestModelMapper testModelMapper; @BeforeAll public static void before(){ TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), TestModel.class); } @Test public void testInsert() { TestModel testModel = new TestModel(); testModelMapper.insert(testModel); Assertions.assertNotNull(testModel.getA()); Assertions.assertNotNull(testModel.getB()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/FillPerformanceTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.test.h2.fillperformance.model.PerformanceModel; import com.baomidou.mybatisplus.test.h2.fillperformance.service.IPerformanceModelService; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.ArrayList; import java.util.List; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-fill-performance-h2.xml"}) class FillPerformanceTest { @Autowired private IPerformanceModelService performanceModelService; @Test void test(){ List list = new ArrayList<>(); for (int i = 0; i < 5000; i++) { list.add(new PerformanceModel()); } System.out.println("-------------------------"); long start = System.currentTimeMillis(); performanceModelService.saveBatch(list); long end = System.currentTimeMillis(); System.out.println(end - start); System.out.println("-------------------------"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2Delete1Eq1Test.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.test.h2.entity.H2Student; import com.baomidou.mybatisplus.test.h2.entity.H2User; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper; import com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.List; /** * Mybatis Plus H2 Junit Test * * @author hubin * @since 2018-06-05 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"}) class H2Delete1Eq1Test extends BaseTest { @Autowired protected H2UserMapper logicDeleteMapper; @Autowired protected H2StudentMapper defaultMapper; @Test @Order(1) void crudTest() { for (int i = 0; i < 10; i++) { logicDeleteMapper.insert(new H2User("mp" + i, AgeEnum.ONE)); } log(logicDeleteMapper.selectList(new QueryWrapper().orderByAsc("`desc`"))); log(logicDeleteMapper.selectOne(new QueryWrapper().last("limit 1"))); H2User h2User = new H2User(); h2User.setDesc("1"); h2User.setName("2"); log(logicDeleteMapper.selectList(new QueryWrapper<>(h2User).orderByAsc("name"))); for (long i = 30; i < 50L; i++) { defaultMapper.insert(new H2Student(i, "Tom长大了", 1)); } log(logicDeleteMapper.selectList(new QueryWrapper<>(h2User).eq("name", "2").orderByAsc("name"))); log(defaultMapper.selectList(new QueryWrapper().orderByAsc("id"))); log(defaultMapper.selectOne(new QueryWrapper().last("limit 1"))); H2Student h2Student = new H2Student(); h2Student.setId(1L); h2Student.setAge(2); log(defaultMapper.selectList(new QueryWrapper<>(h2Student).orderByAsc("id"))); } @Test @Order(Integer.MAX_VALUE) void delete() { logicDeleteMapper.delete(new QueryWrapper<>()); defaultMapper.delete(new QueryWrapper<>()); Assertions.assertEquals(0, logicDeleteMapper.selectCount(new QueryWrapper<>())); List userList = queryByName(null); System.out.println(userList.size()); Assertions.assertNotEquals(0, userList.size()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2KeyGeneratorTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.test.h2.keygenerator.mapper.*; import com.baomidou.mybatisplus.test.h2.keygenerator.model.*; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-keygenerator-h2.xml"}) class H2KeyGeneratorTest { @Autowired private KeyGeneratorMapper keyGeneratorMapper; @Autowired private LongKeyGeneratorMapper longKeyGeneratorMapper; @Autowired private StringKeyGeneratorMapper stringKeyGeneratorMapper; @Autowired private ExtendKeyGeneratorMapper extendKeyGeneratorMapper; @Autowired private IntegerKeyGeneratorMapper integerKeyGeneratorMapper; @Test void test() { KeyGeneratorModel keyGeneratorModel = new KeyGeneratorModel(); keyGeneratorModel.setName("我举起了咩咩"); keyGeneratorMapper.insert(keyGeneratorModel); Assertions.assertNotNull(keyGeneratorModel.getUid()); Assertions.assertEquals(1L, keyGeneratorModel.getUid()); LongKeyGeneratorModel longKeyGeneratorModel = new LongKeyGeneratorModel(); longKeyGeneratorModel.setName("我举起了个栗子"); longKeyGeneratorMapper.insert(longKeyGeneratorModel); Assertions.assertNotNull(longKeyGeneratorModel.getId()); Assertions.assertEquals(2L, longKeyGeneratorModel.getId()); StringKeyGeneratorModel stringKeyGeneratorModel = new StringKeyGeneratorModel(); stringKeyGeneratorModel.setName("我举起了个锤子"); stringKeyGeneratorMapper.insert(stringKeyGeneratorModel); Assertions.assertNotNull(stringKeyGeneratorModel.getId()); Assertions.assertEquals("3", stringKeyGeneratorModel.getId()); ExtendKeyGeneratorModel extendKeyGeneratorModel = new ExtendKeyGeneratorModel(); extendKeyGeneratorModel.setName("我举起了句号"); extendKeyGeneratorMapper.insert(extendKeyGeneratorModel); Assertions.assertNotNull(extendKeyGeneratorModel.getUid()); Assertions.assertEquals(4L, extendKeyGeneratorModel.getUid()); //这个受限数据库,如果返回是long的话,那就救不了. IntegerKeyGeneratorModel integerKeyGeneratorModel = new IntegerKeyGeneratorModel(); integerKeyGeneratorModel.setName("我举起了K神"); integerKeyGeneratorMapper.insert(integerKeyGeneratorModel); Assertions.assertNotNull(integerKeyGeneratorModel.getUid()); Assertions.assertEquals(5, integerKeyGeneratorModel.getUid()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2LogicDeleteTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.test.h2.entity.H2User; import com.baomidou.mybatisplus.test.h2.entity.H2UserLogicDelete; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import com.baomidou.mybatisplus.test.h2.mapper.H2UserLogicDeleteMapper; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.List; /** * Mybatis Plus H2 Junit Test * * @author hubin * @since 2018-06-05 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-logic-delete-h2.xml"}) class H2LogicDeleteTest extends BaseTest { @Autowired protected H2UserLogicDeleteMapper logicDeleteMapper; @Test @Order(1) void crudTest() { String name = "LogicDelete4Date"; H2UserLogicDelete user = new H2UserLogicDelete(); user.setAge(AgeEnum.ONE); user.setName(name); logicDeleteMapper.insert(user); Assertions.assertNotNull(user.getTestId(), "id should not be null"); Assertions.assertNull(user.getLastUpdatedDt()); logicDeleteMapper.deleteById(user.getTestId()); List userList = queryByName(name); Assertions.assertTrue(userList!=null && !userList.isEmpty()); Assertions.assertNotNull(userList.get(0).getLastUpdatedDt(),"lastUpdateDt should not be null after logic delete"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2MetaObjectHandler.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import java.sql.Timestamp; import java.util.Date; /** * 测试,自定义元对象字段填充控制器,实现公共字段自动写入 * * @author hubin * @since 2017-06-25 */ public class H2MetaObjectHandler implements MetaObjectHandler { /** * 测试 user 表 name 字段为空自动填充 */ @Override public void insertFill(MetaObject metaObject) { // System.out.println("*************************"); // System.out.println("insert fill"); // System.out.println("*************************"); // 测试下划线 Object testType = this.getFieldValByName("testType", metaObject); // System.out.println("testType=" + testType); if (testType == null) { //测试实体没有的字段,配置在公共填充,不应该set到实体里面 this.strictInsertFill(metaObject, "testType1", Integer.class, 3); this.strictInsertFill(metaObject, "testType", Integer.class, 3); } } @Override public void updateFill(MetaObject metaObject) { // System.out.println("*************************"); // System.out.println("update fill"); // System.out.println("*************************"); //测试实体没有的字段,配置在公共填充,不应该set到实体里面 this.strictUpdateFill(metaObject, "lastUpdatedDt1", Date.class, new Timestamp(System.currentTimeMillis())); this.strictUpdateFill(metaObject, "lastUpdatedDt", Date.class, new Timestamp(System.currentTimeMillis())); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2StudentMapperTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.test.h2.entity.H2Student; import com.baomidou.mybatisplus.test.h2.enums.GenderEnum; import com.baomidou.mybatisplus.test.h2.enums.GradeEnum; import com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Arrays; import java.util.List; import java.util.Objects; /** * Mybatis Plus H2 Junit Test * * @author hubin * @since 2018-06-05 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"}) class H2StudentMapperTest extends BaseTest { @Autowired protected H2StudentMapper studentMapper; @Test @Order(1) void crudTest() { // H2Student stu = new H2Student(); // stu.setGrade(GradeEnum.HIGH); // studentMapper.update(stu, null); H2Student student = new H2Student(); Long id = 10086L; student.setId(id); student.setAge(188); student.setGender(GenderEnum.MALE); student.setGrade(GradeEnum.PRIMARY); studentMapper.insert(student); List list = studentMapper.selectList(new QueryWrapper<>()); for (H2Student s : list) { System.out.println(s.getGrade()); if (Objects.equals(s.getId(), id)) { Assert.notNull(s.getGrade(), "id=" + id + " should have grade"); Assert.notNull(s.getGender(), "id=" + id + " should have gender"); } } H2Student updateStu = new H2Student(); updateStu.setId(15L); updateStu.setGrade(GradeEnum.HIGH); updateStu.setGender(GenderEnum.FEMALE); Assert.isTrue(studentMapper.updateById(updateStu) == 1, "should update success"); updateStu = studentMapper.selectById(15L); Assert.notNull(updateStu.getGrade(), "grade should updated"); Assert.notNull(updateStu.getGender(), "gender should updated"); } @Test @Order(Integer.MAX_VALUE) void pageCountZeroTest() { IPage page = studentMapper.selectPage(new Page<>(), Wrappers.query().eq("name", "无")); if (null != page) { System.out.println("total: " + page.getTotal()); } } /** * group 或者 order 测试 */ @Test void groupByOrderBy() { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery().groupBy(H2Student::getAge); LambdaQueryWrapper wrapper2 = Wrappers.lambdaQuery().orderByAsc(H2Student::getAge); System.out.println(wrapper.getSqlSegment()); Assertions.assertEquals(" GROUP BY age", wrapper.getSqlSegment()); Assertions.assertEquals(" ORDER BY age ASC", wrapper2.getSqlSegment()); } @Test void testDeleteByIdWithEntity() { H2Student h2Student = new H2Student(111L, "测试根据实体删除", 12); studentMapper.insert(h2Student); Assertions.assertEquals(1, studentMapper.deleteById(h2Student)); } @Test void testIn() { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery().in(H2Student::getName, Arrays.asList("a", "b")); studentMapper.selectList(wrapper); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserMapperTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.batch.MybatisBatch; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; import com.baomidou.mybatisplus.test.h2.entity.H2User; import com.baomidou.mybatisplus.test.h2.entity.SuperEntity; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.IntStream; import static java.util.stream.Collectors.toList; /** * Mybatis Plus H2 Junit Test * * @author hubin * @since 2018-06-05 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"}) class H2UserMapperTest extends BaseTest { @Autowired protected H2UserMapper userMapper; @Autowired private SqlSessionFactory sqlSessionFactory; @Autowired private TransactionTemplate transactionTemplate; @Test void testMapperSaveBatch() { var list = List.of(new H2User("秋秋1"), new H2User("秋秋2")); List batchResults = userMapper.insert(list); Assertions.assertTrue(SqlHelper.retBool(batchResults)); Assertions.assertEquals(2, batchResults.getFirst().getUpdateCounts().length); } @Test void testMapperUpdateBatch() { var list = List.of(new H2User("秋秋1"), new H2User("秋秋2")); userMapper.insert(list); for (H2User h2User : list) { h2User.setName("test" + 1); } List batchResults = userMapper.updateById(list); Assertions.assertTrue(SqlHelper.retBool(batchResults)); Assertions.assertEquals(2, batchResults.getFirst().getUpdateCounts().length); } @Test void testBatchTransaction() { List h2UserList = Arrays.asList(new H2User(1000036L, "测试12323232"), new H2User(10000367L, "测试3323232")); try { transactionTemplate.execute((TransactionCallback>) status -> { MybatisBatch.Method mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class); // 执行批量插入 MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.insert()); throw new RuntimeException("出错了"); }); } catch (Exception exception) { for (H2User h2User : h2UserList) { Assertions.assertNull(userMapper.selectById(h2User.getTestId())); } } transactionTemplate.execute(status -> { MybatisBatch.Method mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class); // 执行批量插入 return MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.insert()); }); for (H2User h2User : h2UserList) { Assertions.assertNotNull(userMapper.selectById(h2User.getTestId())); } } @Test void testInsertBatch() { int batchSize = 1000; List h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User("test" + i)); } MybatisBatch.Method mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class); // 执行批量插入 List batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.insert()); Assertions.assertTrue(SqlHelper.retBool(batchResults)); int[] updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); List ids = Arrays.asList(120000L, 120001L); MybatisBatch.Method method = new MybatisBatch.Method<>(H2UserMapper.class); Assertions.assertTrue(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, ids, method.insert(H2User::ofId)))); } @Test void testInsertBatchByCustomMethod() { int batchSize = 1000; List batchResults; int[] updateCounts; List h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User("myInsertWithoutParam" + i)); } MybatisBatch.Method method = new MybatisBatch.Method<>(H2UserMapper.class); // 执行批量插入 batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.get("myInsertWithoutParam")); Assertions.assertTrue(SqlHelper.retBool(batchResults)); updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User("myInsertWithParam" + i)); } // 执行批量插入 batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.get("myInsertWithParam", parameter -> Map.of("user1", parameter))); Assertions.assertTrue(SqlHelper.retBool(batchResults)); updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User("myInsertWithParam" + i)); } batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.get("myInsertWithParam", parameter -> Map.of("user1", parameter))); Assertions.assertTrue(SqlHelper.retBool(batchResults)); updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); } @Test void testDeleteByIds() { List batchResults; int batchSize = 1000; List ids = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { ids.add((long) 7120000 + i); } List userList = new ArrayList<>(); MybatisBatch.Method method = new MybatisBatch.Method<>(H2UserMapper.class); // 转换成实体进行逻辑删除 batchResults = MybatisBatchUtils.execute(sqlSessionFactory, ids, method.deleteById(id -> { H2User h2User = H2User.ofId(id); userList.add(h2User); return h2User; })); Assertions.assertFalse(SqlHelper.retBool(batchResults)); int[] updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); for (H2User h2User : userList) { Assertions.assertNotNull(h2User.getLastUpdatedDt()); } // 不能走填充 batchResults = MybatisBatchUtils.execute(sqlSessionFactory, ids, method.deleteById()); Assertions.assertFalse(SqlHelper.retBool(batchResults)); updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); } @Test void testUpdateBatch() { int batchSize = 1000; List h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User(Long.valueOf(30000 + i), "test" + i)); } MybatisBatch.Method mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class); // 执行批量更新 List batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.updateById()); Assertions.assertFalse(SqlHelper.retBool(batchResults)); int[] updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); List ids = Arrays.asList(120000L, 120001L); MybatisBatch.Method method = new MybatisBatch.Method<>(H2UserMapper.class); Assertions.assertFalse(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, ids, method.update(id -> Wrappers.lambdaUpdate().set(H2User::getName, "updateTest").eq(H2User::getTestId, id))))); Assertions.assertFalse(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, ids, method.update(id -> new H2User().setName("updateTest2"), id -> Wrappers.lambdaUpdate().eq(H2User::getTestId, id))))); Assertions.assertFalse(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.update(user -> Wrappers.update().set("name", "updateTest3").eq("test_id", user.getTestId()))))); Assertions.assertFalse(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.update(user -> new H2User("updateTests4"), p -> Wrappers.update().eq("test_id", p.getTestId()))))); } @Test void testSaveOrUpdateBatch1() { int batchSize = 10; List h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User(Long.valueOf(40000 + i), "test" + i)); } MybatisBatch.Method mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class); List batchResults = MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, h2UserList, mapperMethod.insert(), ((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null), mapperMethod.updateById()); // 没有使用共享的sqlSession,由于都是新增返回还是一个批次 Assertions.assertTrue(SqlHelper.retBool(batchResults)); int[] updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); } @Test void testSaveOrUpdateBatchMapper1() { int batchSize = 10; List h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User(Long.valueOf(140000 + i), "test" + i)); } List batchResults = userMapper.insertOrUpdate(h2UserList); Assertions.assertTrue(SqlHelper.retBool(batchResults)); Assertions.assertEquals(batchSize, batchResults.size()); // 使用共享的sqlSession,等于每次都是刷新了,批次总结果集就等于数据大小了 for (BatchResult batchResult : batchResults) { Assertions.assertEquals(1, batchResult.getUpdateCounts().length); Assertions.assertEquals(1, batchResult.getUpdateCounts()[0]); } } @Test void testSaveOrUpdateBatchMapper2() { int batchSize = 10; List h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User(Long.valueOf(40000 + i), "test" + i)); } List batchResults = userMapper.insertOrUpdate(h2UserList,((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null)); Assertions.assertTrue(SqlHelper.retBool(batchResults)); // 没有使用共享的sqlSession,由于都是新增返回还是一个批次 int[] updateCounts = batchResults.getFirst().getUpdateCounts(); Assertions.assertEquals(batchSize, updateCounts.length); } @Test void testSaveOrUpdateBatchMapper3() { var id = IdWorker.getId(); var h2UserList = List.of(new H2User(id, "testSaveOrUpdateBatchMapper3"), new H2User(id, "testSaveOrUpdateBatchMapper3-1")); // 由于没有共享一个sqlSession,第二条记录selectById的时候第一个sqlSession的数据还没提交,会执行插入导致主键冲突. Assertions.assertThrowsExactly(PersistenceException.class, () -> userMapper.insertOrUpdate(h2UserList, ((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null))); } @Test void testSaveOrUpdateBatchMapper4() { var id = IdWorker.getId(); var h2UserList = List.of(new H2User(id, "testSaveOrUpdateBatchMapper4"), new H2User(id, "testSaveOrUpdateBatchMapper4-1")); var mapperMethod = new MybatisBatch.Method(H2UserMapper.class); // 共享一个sqlSession,每次selectById都会刷新一下,第二条记录为update. var batchResults = userMapper.insertOrUpdate(h2UserList, ((sqlSession, h2User) -> sqlSession.selectList(mapperMethod.get("selectById").getStatementId(), h2User.getTestId()).isEmpty())); Assertions.assertTrue(SqlHelper.retBool(batchResults)); Assertions.assertEquals(h2UserList.size(), batchResults.stream().flatMapToInt(r -> IntStream.of(r.getUpdateCounts())).count()); Assertions.assertEquals("testSaveOrUpdateBatchMapper4-1", userMapper.selectById(id).getName()); } @Test void testRemoveByIds() { Assertions.assertEquals(userMapper.deleteByIds(List.of(666666661, "2")), userMapper.deleteByIds(List.of(666666661, "2"), false)); H2User h2User = new H2User("testRemoveByIds"); Assertions.assertEquals(1, userMapper.insert(h2User)); Assertions.assertEquals(1, userMapper.deleteByIds(List.of(h2User))); Assertions.assertNotNull(userMapper.getById(h2User.getTestId()).getLastUpdatedDt()); h2User = new H2User("testRemoveByIds"); Assertions.assertEquals(1, userMapper.insert(h2User)); Assertions.assertEquals(1, userMapper.deleteByIds(List.of(h2User), false)); Assertions.assertNull(userMapper.getById(h2User.getTestId()).getLastUpdatedDt()); } @Test void testSaveOrUpdateBatch2() { int batchSize = 10; List h2UserList = new ArrayList<>(); for (int i = 0; i < batchSize; i++) { h2UserList.add(new H2User(Long.valueOf(50000 + i), "test" + i)); } MybatisBatch.Method mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class); List batchResults = MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, h2UserList, mapperMethod.insert(), ((sqlSession, h2User) -> sqlSession.selectList(H2UserMapper.class.getName() + ".selectById", h2User.getTestId()).isEmpty()), mapperMethod.updateById()); // 使用共享的sqlSession,等于每次都是刷新了,批次总结果集就等于数据大小了 Assertions.assertEquals(batchSize, batchResults.size()); for (BatchResult batchResult : batchResults) { Assertions.assertEquals(1, batchResult.getUpdateCounts().length); Assertions.assertEquals(1, batchResult.getUpdateCounts()[0]); } } @Test void testSaveOrUpdateBatch3() { var id = IdWorker.getId(); var h2UserList = List.of(new H2User(id, "testSaveOrUpdateBatch3"), new H2User(id, "testSaveOrUpdateBatch3-1")); var mapperMethod = new MybatisBatch.Method(H2UserMapper.class); // 由于没有共享一个sqlSession,第二条记录selectById的时候第一个sqlSession的数据还没提交,会执行插入导致主键冲突. Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, h2UserList, mapperMethod.insert(), ((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null), mapperMethod.updateById())); } @Test void testSaveOrUpdateBatch4() { var id = IdWorker.getId(); var h2UserList = List.of(new H2User(id, "testSaveOrUpdateBatch4"), new H2User(id, "testSaveOrUpdateBatch4-1")); var mapperMethod = new MybatisBatch.Method(H2UserMapper.class); // 共享一个sqlSession,每次selectById都会刷新一下,第二条记录为update. var batchResults = MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, h2UserList, mapperMethod.insert(), ((sqlSession, h2User) -> sqlSession.selectList(mapperMethod.get("selectById").getStatementId(), h2User.getTestId()).isEmpty()), mapperMethod.updateById()); var updateCounts = batchResults.getFirst().getUpdateCounts(); for (int updateCount : updateCounts) { Assertions.assertEquals(1, updateCount); } Assertions.assertEquals("testSaveOrUpdateBatch4-1", userMapper.selectById(id).getName()); } @Test @Order(1) void crudTest() { H2User h2User = new H2User(); h2User.setName(NQQ); h2User.setAge(AgeEnum.ONE); h2User.setDeleted(0); h2User.setDesc("这是一个不错的小伙子"); h2User.setTestType(1); Assertions.assertEquals(1, userMapper.insert(h2User)); log(h2User.getTestId()); // 新增一条自定义 ID = 1 的测试删除数据 h2User.setTestId(1L); h2User.setName("测试"); userMapper.insert(h2User); for (int i = 0; i < 10; i++) { userMapper.insert(new H2User("mp" + i, AgeEnum.ONE)); } Assertions.assertEquals(1, userMapper.deleteById(1L)); Map map = new HashMap<>(); map.put("name", "mp0"); map.put("age", AgeEnum.ONE); // 根据 map 查询 h2User = userMapper.selectByMap(map).getFirst(); Assertions.assertSame(AgeEnum.ONE, h2User.getAge()); // 根据 map 删除 Assertions.assertEquals(1, userMapper.deleteByMap(map)); // 查询列表 LambdaQueryWrapper wrapper = new QueryWrapper().lambda().like(H2User::getName, "mp"); log(wrapper.getSqlSegment()); List h2UserList = userMapper.selectList(wrapper); Assertions.assertTrue(CollectionUtils.isNotEmpty(h2UserList)); // 查询总数 long count = userMapper.selectCount(wrapper.clone()); Assertions.assertTrue(count > 1); // 批量删除 Assertions.assertEquals(count, userMapper.deleteByIds(h2UserList.stream().map(SuperEntity::getTestId).collect(toList()))); // 更新 h2User = new H2User(); h2User.setAge(AgeEnum.TWO); h2User.setDesc("测试置空"); Assertions.assertEquals(1, userMapper.update(h2User, new QueryWrapper().eq("name", NQQ))); log(userMapper.selectOne(new QueryWrapper<>(new H2User().setName(NQQ).setAge(AgeEnum.TWO)))); h2User.setAge(AgeEnum.THREE); h2User.setDesc(null); Assertions.assertTrue(userMapper.update(h2User, new UpdateWrapper().lambda() .set(H2User::getDesc, "") .eq(H2User::getName, "Jerry")) > 0); log(userMapper.selectOne(new QueryWrapper<>(new H2User().setName(NQQ).setAge(AgeEnum.THREE)))); Assertions.assertEquals(1, userMapper.insert(h2User)); // 根据主键更新 age = 18 h2User.setAge(AgeEnum.TWO); Assertions.assertEquals(1, userMapper.updateById(h2User)); long testId = h2User.getTestId(); // https://github.com/baomidou/mybatis-plus/issues/299 Assertions.assertEquals(1, userMapper.updateById(new H2User() {{ setTestId(testId); setAge(AgeEnum.TWO); }})); // 查询一条记录 Assertions.assertNotNull(userMapper.selectOne(new QueryWrapper<>(new H2User().setName("Joe").setTestType(1)))); log(h2User.toString()); // 分页查询 IPage h2UserPage = userMapper.selectPage(new Page<>(1, 10), null); if (null != h2UserPage) { System.out.println(h2UserPage.getTotal()); System.out.println(h2UserPage.getSize()); } Assertions.assertNotNull(userMapper.selectPage(new Page<>(1, 10), new QueryWrapper().orderByAsc("name"))); // 查询结果集,测试 lambda 对象后 QueryWrapper 是否参数继续传递 QueryWrapper qw = new QueryWrapper<>(); qw.lambda().eq(H2User::getName, NQQ); List> mapList = userMapper.selectMaps(qw); if (CollectionUtils.isNotEmpty(mapList)) { for (Map m : mapList) { System.out.println(m); } } Assertions.assertTrue(CollectionUtils.isNotEmpty(userMapper.selectMaps(new QueryWrapper<>(new H2User().setTestType(3))))); // 测试自定义注入方法 h2User.setDesc(""); h2User.setTestDate(new Date()); Assertions.assertTrue(userMapper.alwaysUpdateSomeColumnById(h2User) > 0); Assertions.assertEquals("", userMapper.selectById(h2User.getTestId()).getDesc()); } @Test void testCall() { Assertions.assertEquals("1", userMapper.testCall()); } @Test @Order(Integer.MAX_VALUE) void delete() { userMapper.delete(new QueryWrapper<>(new H2User().setAge(AgeEnum.TWO)) .eq("name", "Tony")); } @Test @Order(Integer.MAX_VALUE) void sqlCommentTest() { userMapper.delete(new QueryWrapper().comment("deleteAllUsers")); String name = "name1", nameNew = "name1New"; int insertCount = userMapper.insert(new H2User().setName(name).setAge(AgeEnum.ONE)); Assertions.assertEquals(1, insertCount); int updateCount = userMapper.update(new H2User(), new UpdateWrapper().comment("updateUserName1").lambda() .set(H2User::getName, nameNew) .eq(H2User::getName, name) ); Assertions.assertEquals(1, updateCount); H2User h2User = userMapper.selectOne( new QueryWrapper().lambda().comment("getUserByUniqueName") .eq(H2User::getName, nameNew) ); Assertions.assertNotNull(h2User); LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda().ge(H2User::getAge, 1); long userCount = userMapper.selectCount(queryWrapper.comment("getUserCount")); Assertions.assertEquals(1, userCount); List h2UserList = userMapper.selectList(queryWrapper.comment("getUserList")); Assertions.assertEquals(1, h2UserList.size()); IPage h2UserIPage = userMapper.selectPage(new Page<>(1, 10), queryWrapper.comment("getUserPage")); Assertions.assertEquals(1, h2UserIPage.getRecords().size()); List> selectMaps = userMapper.selectMaps(queryWrapper.comment("getUserMaps")); Assertions.assertEquals(1, selectMaps.size()); IPage> selectMapsPage = userMapper.selectMapsPage(new Page<>(1, 10), queryWrapper.comment("getUserMapsPage")); Assertions.assertEquals(1, selectMapsPage.getRecords().size()); List selectObjs = userMapper.selectObjs(queryWrapper.comment("getUserObjs")); Assertions.assertEquals(1, selectObjs.size()); } @Test void test() { Page page = new Page<>(); userMapper.testPage1(new H2User(), page); userMapper.testPage2(page, new H2User()); } @Test void testCountLong() { Long count = userMapper.selectCountLong(); System.out.println(count); } @Test void testUpdateByWrapper() { var h2User = new H2User(); userMapper.insert(h2User); var wrapper = Wrappers.lambdaUpdate().set(H2User::getName, "testUpdateByWrapper").eq(H2User::getTestId, h2User.getTestId()); Assertions.assertEquals(1, userMapper.update(wrapper)); Assertions.assertEquals("testUpdateByWrapper", userMapper.selectById(h2User.getTestId()).getName()); } @Test void testSaveOrUpdate() { var h2User = new H2User(); userMapper.insertOrUpdate(h2User); Assertions.assertNotNull(h2User.getTestId()); Assertions.assertNull(h2User.getLastUpdatedDt()); h2User.setName("test"); userMapper.insertOrUpdate(h2User); Assertions.assertNotNull(h2User.getLastUpdatedDt()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserStrategyTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.test.h2.entity.H2UserStrategy; import com.baomidou.mybatisplus.test.h2.mapper.H2UserStrategyMapper; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; /** * Mybatis Plus H2 Junit Test * * @author Caratacus * @since 2017/4/1 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"}) class H2UserStrategyTest extends BaseTest { @Autowired protected H2UserStrategyMapper userStrategyMapper; @Test @Order(1) void testNewlyAdded3Strategy() { H2UserStrategy insertUser = new H2UserStrategy(); insertUser.setName("userStrategy").setDesc("updateStrategy=IGNORE").setVersion(1); int row = userStrategyMapper.insert(insertUser); Long id = insertUser.getTestId(); Assertions.assertEquals(1, row); Assertions.assertEquals(3, userStrategyMapper.selectById(id).getTestType(), "autofilled with 3"); QueryWrapper wrapper = new QueryWrapper<>(new H2UserStrategy().setDesc("updateStrategy=IGNORE").setTestType(3)); Assertions.assertEquals(0, userStrategyMapper.selectCount(wrapper), "name is whereStrategy=IGNORE, so should have where name=null which cause count=0"); H2UserStrategy updateUser = new H2UserStrategy().setName("update"); updateUser.setTestId(id); Assertions.assertEquals(1, userStrategyMapper.updateById(updateUser)); H2UserStrategy selectUser = userStrategyMapper.selectById(id); Assertions.assertEquals("update", selectUser.getName()); Assertions.assertNull(selectUser.getDesc(), "desc is updateStrategy=IGNORE, so should have set desc=null when updateById"); Assertions.assertNull(selectUser.getTestType(), "handle: strategy=IGNORED, should be set test_type=null when updateById "); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.util.*; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.exceptions.TooManyResultsException; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.DataChangeRecorderInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.test.h2.entity.H2User; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper; import com.baomidou.mybatisplus.test.h2.service.IH2UserService; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.select.Select; /** * Mybatis Plus H2 Junit Test * * @author Caratacus * @since 2017/4/1 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"}) class H2UserTest extends BaseTest { @Autowired protected IH2UserService userService; @Autowired SqlSessionFactory sqlSessionFactory; @Autowired private H2StudentMapper h2StudentMapper; public void initBatchLimitation(int limitation) { if (sqlSessionFactory instanceof DefaultSqlSessionFactory) { Configuration configuration = sqlSessionFactory.getConfiguration(); for (Interceptor interceptor : configuration.getInterceptors()) { if (interceptor instanceof MybatisPlusInterceptor) { List innerInterceptors = ((MybatisPlusInterceptor) interceptor).getInterceptors(); for (InnerInterceptor innerInterceptor : innerInterceptors) { if (innerInterceptor instanceof DataChangeRecorderInnerInterceptor) { ((DataChangeRecorderInnerInterceptor) innerInterceptor).setBatchUpdateLimit(limitation).openBatchUpdateLimitation(); } } } } } } @Test @Order(1) void testInsertMy() { String name = "自定义insert"; int version = 1; int row = userService.myInsert(name, version); Assertions.assertEquals(1, row); } @Test @Order(2) void testInsertObjectWithParam() { String name = "自定义insert带Param注解"; int version = 1; int row = userService.myInsertWithParam(name, version); Assertions.assertEquals(1, row); } @Test @Order(3) void testInsertObjectWithoutParam() { String name = "自定义insert带Param注解"; int version = 1; int row = userService.myInsertWithoutParam(name, version); Assertions.assertEquals(1, row); } @Test @Order(6) void testSelectLambdaById() { H2User h2User = userService.getOne(Wrappers.lambdaQuery().eq(H2User::getTestId, 101)); Assertions.assertNotNull(h2User); } @Test @Order(7) void testLambdaTypeHandler() { // 演示 json 格式 Wrapper TypeHandler 查询 H2User h2User = userService.getOne(Wrappers.lambdaQuery() .apply("name={0,typeHandler=" + H2userNameJsonTypeHandler.class.getCanonicalName() + "}", "{\"id\":101,\"name\":\"Tomcat\"}")); Assertions.assertNotNull(h2User); } @Test @Order(10) void testEntityWrapperSelectSql() { QueryWrapper ew = new QueryWrapper<>(); ew.select("test_id, name, age"); List list = userService.list(ew); for (H2User u : list) { Assertions.assertNotNull(u.getTestId()); Assertions.assertNotNull(u.getName()); Assertions.assertNull(u.getPrice()); } } @Test @Order(10) void testQueryWithParamInSelectStatement() { Map param = new HashMap<>(); String nameParam = "selectStmtParam"; param.put("nameParam", nameParam); param.put("ageFrom", 1); param.put("ageTo", 100); List list = userService.queryWithParamInSelectStatememt(param); Assertions.assertNotNull(list); for (H2User u : list) { Assertions.assertEquals(nameParam, u.getName()); Assertions.assertNotNull(u.getTestId()); } } @Test @Order(10) void testSelectCountWithParamInSelectItems() { Map param = new HashMap<>(); String nameParam = "selectStmtParam"; param.put("nameParam", nameParam); param.put("ageFrom", 1); param.put("ageTo", 100); int count = userService.selectCountWithParamInSelectItems(param); Assertions.assertNotEquals(0, count); } @Test @Order(15) void testUpdateByIdWithOptLock() { Long id = 991L; H2User user = new H2User(); user.setTestId(id); user.setName("991"); user.setAge(AgeEnum.ONE); user.setPrice(BigDecimal.TEN); user.setDesc("asdf"); user.setTestType(1); user.setVersion(1); final LocalDateTime dateTime = LocalDateTime.of(2024, 3, 29, 10, 0, 0); user.setCreatedDt(dateTime); userService.save(user); H2User userDB = userService.getById(id); Assertions.assertEquals(1, userDB.getVersion().intValue()); Assertions.assertEquals(0, userDB.getCreatedDt().compareTo(dateTime)); userDB.setName("992"); userDB.setCreatedDt(dateTime); System.out.println("==============================================="); userService.updateById(userDB); Assertions.assertEquals(2, userDB.getVersion().intValue(), "updated version value should be updated to entity"); userDB = userService.getById(id); Assertions.assertEquals(2, userDB.getVersion().intValue()); Assertions.assertEquals("992", userDB.getName()); userDB.setCreatedDt(LocalDateTime.now()); userService.updateById(userDB); System.out.println("==============================================="); userService.lambdaUpdate().set(H2User::getAge, AgeEnum.THREE).eq(H2User::getTestId, id).update(); } @Test @Order(16) void testUpdateByEwWithOptLock() { H2User userInsert = new H2User(); userInsert.setName("optLockerTest"); userInsert.setAge(AgeEnum.THREE); userInsert.setPrice(BigDecimal.TEN); userInsert.setDesc("asdf"); userInsert.setTestType(1); userInsert.setVersion(99); userService.save(userInsert); QueryWrapper ew = new QueryWrapper<>(); ew.ge("age", AgeEnum.TWO.getValue()); Long id99 = null; for (H2User u : userService.list(ew)) { System.out.println(u.getName() + "," + u.getAge() + "," + u.getVersion()); if (u.getVersion() != null && u.getVersion() == 99) { id99 = u.getTestId(); } } userService.update(new H2User().setPrice(BigDecimal.TEN).setVersion(99), ew); System.out.println("============after update"); ew = new QueryWrapper<>(); ew.ge("age", AgeEnum.TWO.getValue()); for (H2User u : userService.list(ew)) { System.out.println(u.getName() + "," + u.getAge() + "," + u.getVersion()); if (u.getTestId().equals(id99)) { Assertions.assertEquals(100, u.getVersion().intValue(), "optLocker should update version+=1"); } } } @Test @Order(17) void testOptLocker4WrapperIsNull() { H2User userInsert = new H2User(); userInsert.setName("optLockerTest"); userInsert.setAge(AgeEnum.THREE); userInsert.setPrice(BigDecimal.TEN); userInsert.setDesc("asdf"); userInsert.setTestType(1); userInsert.setVersion(99); userService.save(userInsert); QueryWrapper ew = new QueryWrapper<>(); ew.ge("age", AgeEnum.TWO.getValue()); Long id99 = null; Map idPriceMap = new HashMap<>(); for (H2User u : userService.list(ew)) { System.out.println(u.getName() + "," + u.getAge() + "," + u.getVersion()); idPriceMap.put(u.getTestId(), u.getPrice()); if (u.getVersion() != null && u.getVersion() == 99) { id99 = u.getTestId(); } } userService.update(new H2User().setPrice(BigDecimal.TEN).setVersion(99), null); System.out.println("============after update"); ew = new QueryWrapper<>(); ew.ge("age", AgeEnum.TWO.getValue()); for (H2User u : userService.list(ew)) { System.out.println(u.getName() + "," + u.getAge() + "," + u.getVersion()); if (u.getTestId().equals(id99)) { Assertions.assertEquals(100, u.getVersion().intValue(), "optLocker should update version+=1"); } else { Assertions.assertEquals(idPriceMap.get(u.getTestId()), u.getPrice(), "other records should not be updated"); } } userService.update(new H2User().setPrice(BigDecimal.ZERO), null); for (H2User u : userService.list(new QueryWrapper<>())) { System.out.println(u.getName() + "," + u.getAge() + "," + u.getVersion()); Assertions.assertEquals(u.getPrice().setScale(2, RoundingMode.HALF_UP).intValue(), BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP).intValue(), "all records should be updated"); } try { initBatchLimitation(3); userService.update(new H2User().setPrice(BigDecimal.ZERO), null); Assertions.fail("SHOULD NOT REACH HERE"); } catch (Exception e) { Assertions.assertTrue(checkIsDataUpdateLimitationException(e)); } } private boolean checkIsDataUpdateLimitationException(Throwable e) { if (e instanceof DataChangeRecorderInnerInterceptor.DataUpdateLimitationException) { return true; } if (e.getCause() == null) { return false; } return checkIsDataUpdateLimitationException(e.getCause()); } @Test @Order(18) void testBatchTransactional() { try { userService.testBatchTransactional(); } catch (MybatisPlusException e) { List list = userService.list(new QueryWrapper().like("name", "batch")); Assertions.assertTrue(CollectionUtils.isEmpty(list)); } } @Test @Order(19) void testSimpleTransactional() { try { userService.testSimpleTransactional(); } catch (MybatisPlusException e) { List list = userService.list(new QueryWrapper().like("name", "simple")); Assertions.assertTrue(CollectionUtils.isEmpty(list)); } } @Test @Order(20) void testSaveOrUpdateBatchTransactional() { try { userService.testSaveOrUpdateBatchTransactional(); } catch (MybatisPlusException e) { List list = userService.list(new QueryWrapper().like("name", "savOrUpdate")); Assertions.assertTrue(CollectionUtils.isEmpty(list)); } } @Test @Order(21) void testSaveBatch() { Assertions.assertTrue(userService.saveBatch(List.of(new H2User("saveBatch0")))); Assertions.assertTrue(userService.saveBatch(List.of(new H2User("saveBatch1"), new H2User("saveBatch2"), new H2User("saveBatch3"), new H2User("saveBatch4")))); Assertions.assertEquals(5, userService.count(new QueryWrapper().like("name", "saveBatch"))); Assertions.assertTrue(userService.saveBatch(List.of(new H2User("saveBatch5"), new H2User("saveBatch6"), new H2User("saveBatch7"), new H2User("saveBatch8")), 2)); Assertions.assertEquals(9, userService.count(new QueryWrapper().like("name", "saveBatch"))); } @Test @Order(22) void testUpdateBatch() { Assertions.assertTrue(userService.updateBatchById(Arrays.asList(new H2User(1010L, "batch1010"), new H2User(1011L, "batch1011"), new H2User(1010L, "batch1010"), new H2User(1012L, "batch1012")))); Assertions.assertEquals("batch1010", userService.getById(1010L).getName()); Assertions.assertEquals("batch1011", userService.getById(1011L).getName()); Assertions.assertEquals("batch1012", userService.getById(1012L).getName()); Assertions.assertTrue(userService.updateBatchById(Arrays.asList(new H2User(1010L, "batch1010A"), new H2User(1011L, "batch1011A"), new H2User(1010L, "batch1010"), new H2User(1012L, "batch1012")), 1)); Assertions.assertEquals("batch1010", userService.getById(1010L).getName()); Assertions.assertEquals("batch1011A", userService.getById(1011L).getName()); Assertions.assertEquals("batch1012", userService.getById(1012L).getName()); } @Test @Order(23) void testSaveOrUpdateBatch() { Assertions.assertTrue(userService.saveOrUpdateBatch(Arrays.asList(new H2User(1010L, "batch1010"), new H2User("batch1011"), new H2User(1010L, "batch1010"), new H2User("batch1015")))); Assertions.assertEquals("batch1010", userService.getById(1010L).getName()); Assertions.assertEquals(1, userService.count(new QueryWrapper().eq("name", "batch1011"))); Assertions.assertEquals(1, userService.count(new QueryWrapper().eq("name", "batch1015"))); Assertions.assertTrue(userService.saveOrUpdateBatch(Arrays.asList(new H2User(1010L, "batch1010A"), new H2User("batch1011AB"), new H2User(1010L, "batch1010"), new H2User("batch1016")), 1)); Assertions.assertEquals("batch1010", userService.getById(1010L).getName()); Assertions.assertEquals(1, userService.count(new QueryWrapper().eq("name", "batch1011AB"))); Assertions.assertEquals(1, userService.count(new QueryWrapper().eq("name", "batch1016"))); } @Test @Order(24) void testSimpleAndBatch() { Assertions.assertTrue(userService.save(new H2User("testSimpleAndBatch1", 0))); Assertions.assertEquals(1, userService.count(new QueryWrapper().eq("name", "testSimpleAndBatch1"))); Assertions.assertTrue(userService.saveOrUpdateBatch(Arrays.asList(new H2User("testSimpleAndBatch2"), new H2User("testSimpleAndBatch3"), new H2User("testSimpleAndBatch4")), 1)); Assertions.assertEquals(4, userService.count(new QueryWrapper().like("name", "testSimpleAndBatch"))); } @Test @Order(25) void testSimpleAndBatchTransactional() { try { userService.testSimpleAndBatchTransactional(); } catch (MybatisPlusException e) { List list = userService.list(new QueryWrapper().like("name", "simpleAndBatchTx")); Assertions.assertTrue(CollectionUtils.isEmpty(list)); } } @Test @Order(26) void testServiceImplInnerLambdaQuery() { H2User tomcat = userService.lambdaQuery().eq(H2User::getName, "Tomcat").one(); Assertions.assertNotNull(tomcat); Assertions.assertNotEquals(0L, userService.lambdaQuery().like(H2User::getName, "a").count().longValue()); List users = userService.lambdaQuery().like(H2User::getName, "T") .ne(H2User::getAge, AgeEnum.TWO) .ge(H2User::getVersion, 1) .isNull(H2User::getPrice) .list(); Assertions.assertTrue(users.isEmpty()); } @Test @Order(27) void testServiceChainQuery() { H2User tomcat = userService.query().eq("name", "Tomcat").one(); Assertions.assertNotNull(tomcat, "tomcat should not be null"); userService.query().nested(i -> i.eq("name", "Tomcat")).list(); userService.lambdaUpdate().set(H2User::getName, "Tom").eq(H2User::getName, "Tomcat").update(); } @Test @Order(28) void testSaveBatchException() { try { userService.saveBatch(Arrays.asList( new H2User(1L, "tom"), new H2User(1L, "andy") )); } catch (Exception e) { Assertions.assertInstanceOf(DataAccessException.class, e); } } @Test @Order(29) @Transactional void testClearSqlSessionCache() { H2User h2User; h2User = userService.getById(996102919L); assert h2User == null; userService.saveBatch(Collections.singletonList(new H2User(996102919L, "靓仔"))); h2User = userService.getById(996102919L); Assertions.assertNotNull(h2User); } @Test @Order(30) void testSaveBatchNoTransactional1() { userService.testSaveBatchNoTransactional1(); Assertions.assertEquals(3, userService.count(new QueryWrapper().like("name", "testSaveBatchNoTransactional1"))); } @Test @Order(30) void testSaveBatchNoTransactional2() { try { userService.testSaveBatchNoTransactional2(); } catch (Exception e) { Assertions.assertEquals(3, userService.count(new QueryWrapper().like("name", "testSaveBatchNoTransactional2"))); } } @Test @Order(31) void testSpaceCharacter() { Assertions.assertFalse(StringUtils.isNotBlank(" ")); Assertions.assertTrue(StringUtils.checkValNotNull(" ")); H2User h2User = new H2User(); h2User.setName(" "); Assertions.assertTrue(CollectionUtils.isEmpty(userService.list(new QueryWrapper<>(h2User) .gt("age", 1).lt("age", 5)))); } @Test @Order(32) void testSqlInjectionByCustomSqlSegment() { // Preparing: select * from h2user WHERE (name LIKE ?) // Parameters: %y%%(String) List h2Users = userService.testCustomSqlSegment(new QueryWrapper().like("name", "y%")); Assertions.assertEquals(2, h2Users.size()); } @Test void myQueryWithGroupByOrderBy() { userService.mySelectMaps().forEach(System.out::println); } @Test void notParser() throws Exception { final String targetSql1 = "SELECT * FROM user WHERE id NOT LIKE ?"; final Select select = (Select) CCJSqlParserUtil.parse(targetSql1); Assertions.assertEquals(targetSql1, select.toString()); final String targetSql2 = "SELECT * FROM user WHERE id NOT IN (?)"; final Select select2 = (Select) CCJSqlParserUtil.parse(targetSql2); Assertions.assertEquals(targetSql2, select2.toString()); final String targetSql3 = "SELECT * FROM user WHERE id IS NOT NULL"; final Select select3 = (Select) CCJSqlParserUtil.parse(targetSql3); Assertions.assertEquals(targetSql3, select3.toString()); } /** * CTO 说批量插入性能不行,让我们来分析一下问题在哪里 */ @Test void batchInsertPerformanceTest() { List users = mockUser(10_000, 99989); userService.saveBatch(users); // 卧槽,速度挺快的 } /** * 模拟一群人 * * @param size 这群人的数量 * @param cardinal 这群人 id 的起始值 * @return 返回模拟的一群人 */ private List mockUser(int size, long cardinal) { return new AbstractList<>() { @Override public H2User get(int index) { long id = cardinal + index + 1; H2User h2User = new H2User(id, Long.toHexString(id)); h2User.setVersion(0); h2User.setPrice(BigDecimal.ZERO); return h2User; } @Override public int size() { return size; } }; } @Test void testSimpleWrapperClear() { userService.save(new H2User("逗号", AgeEnum.TWO)); QueryWrapper queryWrapper = new QueryWrapper().eq("name", "咩咩"); Assertions.assertEquals(0, userService.count(queryWrapper)); queryWrapper.clear(); queryWrapper.eq("name", "逗号"); Assertions.assertEquals(1, userService.count(queryWrapper)); UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.set("name", "逗号二号"); Assertions.assertFalse(userService.update(updateWrapper.eq("name", "逗号一号"))); updateWrapper.clear(); updateWrapper.set("name", "逗号一号"); Assertions.assertTrue(userService.update(updateWrapper.eq("name", "逗号"))); } @Test void testLambdaWrapperClear() { userService.save(new H2User("小红", AgeEnum.TWO)); LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper().lambda().eq(H2User::getName, "小宝"); lambdaQueryWrapper.orderByDesc(H2User::getName); Assertions.assertEquals(0, userService.count(lambdaQueryWrapper)); lambdaQueryWrapper.clear(); lambdaQueryWrapper.eq(H2User::getName, "小红"); Assertions.assertEquals(1, userService.count(lambdaQueryWrapper)); LambdaUpdateWrapper lambdaUpdateWrapper = new UpdateWrapper().lambda().set(H2User::getName, "小红二号"); Assertions.assertFalse(userService.update(lambdaUpdateWrapper.eq(H2User::getName, "小红一号"))); lambdaUpdateWrapper.clear(); lambdaUpdateWrapper.set(H2User::getName, "小红一号"); Assertions.assertTrue(userService.update(lambdaUpdateWrapper.eq(H2User::getName, "小红"))); } @Test void testLogicDelWithFill() { H2User h2User = new H2User("逻辑删除(根据ID)不填充", AgeEnum.TWO); userService.save(h2User); userService.removeById(h2User.getTestId()); Assertions.assertNull(h2User.getLastUpdatedDt()); h2User = new H2User("测试逻辑(根据实体)删除填充", AgeEnum.TWO); userService.save(h2User); userService.removeById(h2User); Assertions.assertNotNull(h2User.getLastUpdatedDt()); } /** * 观察 {@link com.baomidou.mybatisplus.core.toolkit.LambdaUtils#extract(SFunction)} */ @RepeatedTest(1000) void testLambdaCache() { lambdaCache(); } private void lambdaCache() { Wrappers.lambdaQuery() .eq(H2User::getAge, 2) .eq(H2User::getName, 2) .eq(H2User::getPrice, 2) .getTargetSql(); } @Test void testRemove() { //不报错即可,无需关注返回值 H2User h2User = new H2User(12L, "test"); // userService.removeById((short) 100); // userService.removeById(100.00); // userService.removeById((float) 100); // userService.removeById(100); userService.removeById(100000L); // userService.removeById(new BigDecimal("100")); // userService.removeById("100000"); userService.removeById(h2User); userService.removeByIds(Arrays.asList(10000L, h2User)); userService.removeByIds(Arrays.asList(10000L, h2User), false); h2User = new H2User("test"); H2UserMapper h2UserMapper = (H2UserMapper) userService.getBaseMapper(); h2UserMapper.insert(h2User); h2UserMapper.deleteById(h2User.getTestId()); h2User = h2UserMapper.getById(h2User.getTestId()); Assertions.assertNotNull(h2User.getLastUpdatedDt()); h2User = new H2User("test"); h2UserMapper.insert(h2User); h2UserMapper.deleteById(h2User.getTestId(), false); h2User = h2UserMapper.getById(h2User.getTestId()); Assertions.assertNull(h2User.getLastUpdatedDt()); h2User = new H2User("test"); h2UserMapper.insert(h2User); h2UserMapper.deleteById(String.valueOf(h2User.getTestId())); h2User = h2UserMapper.getById(h2User.getTestId()); Assertions.assertNotNull(h2User.getLastUpdatedDt()); } @Test void testPageOrderBy() { // test https://gitee.com/baomidou/mybatis-plus/issues/I4BGE2 Page page = Page.of(1, 10); Assertions.assertTrue(userService.page(page, Wrappers.query().select("test_id,name") .orderByDesc("test_id")).getPages() > 0); Assertions.assertTrue(userService.page(page, Wrappers.lambdaQuery() .orderByDesc(H2User::getTestId)).getPages() > 0); } @Test void testPageNegativeSize() { Page page = Page.of(1, -1); userService.lambdaQuery().page(page); Assertions.assertEquals(0, page.getTotal()); Assertions.assertEquals(userService.lambdaQuery().list(Page.of(1, -1, false)).size(), page.getRecords().size()); } @Test void testDeleteByFill() { H2User h2User = new H2User(3L, "test"); userService.removeById(1L); userService.removeById(1L, true); userService.removeById(1, true); userService.removeById("1", true); userService.removeById(1L, false); userService.removeById(h2User); userService.removeById(h2User, true); userService.removeById(h2User, false); userService.removeBatchByIds(Arrays.asList(1L, 2L, h2User)); } @Test @Order(25) void testServiceImplInnerLambdaQueryConstructorSetEntity() { H2User condition = new H2User(); condition.setName("Tomcat"); H2User user = userService.lambdaQuery(condition).one(); Assertions.assertNotNull(user); Assertions.assertEquals("Tomcat", user.getName()); H2User h2User = userService.lambdaQuery().setEntity(condition).one(); Assertions.assertNotNull(h2User); Assertions.assertEquals("Tomcat", h2User.getName()); } @Test @Order(26) void testServiceGetOptById() { H2User user = new H2User(1L, "Evan"); userService.save(user); Optional optional = userService.getOptById(1L); optional.ifPresent(u -> log(u.toString())); } @Test @Order(27) void testServiceGetOneOpt() { userService.getOneOpt(Wrappers.lambdaQuery().eq(H2User::getName, "David")) .ifPresent(u -> log(u.toString())); } @Test @Order(28) void testServiceGetOneOptThrowEx() { userService.getOneOpt(new LambdaQueryWrapper().eq(H2User::getName, "test1"), false) .ifPresent(u -> log(u.toString())); userService.getOneOpt(new LambdaQueryWrapper().eq(H2User::getName, "test"), false) .ifPresent(u -> log(u.toString())); // 异常情况 Assertions.assertThrows(TooManyResultsException.class, () -> userService.getOneOpt(Wrappers.lambdaQuery() .like(H2User::getName, "tes"))); } @Test void testInsertFill() { H2User h2User; h2User = new H2User("insertFillByCustomMethod1", AgeEnum.ONE); h2StudentMapper.insertFillByCustomMethod1(h2User); Assertions.assertNotNull(h2User.getTestType()); h2User = new H2User("insertFillByCustomMethod2", AgeEnum.ONE); h2StudentMapper.insertFillByCustomMethod2(h2User); Assertions.assertNotNull(h2User.getTestType()); h2User = new H2User("insertFillByCustomMethod3", AgeEnum.ONE); h2StudentMapper.insertFillByCustomMethod3(h2User, "fillByCustomMethod3"); Assertions.assertNotNull(h2User.getTestType()); List list; list = Arrays.asList(new H2User("insertFillByCustomMethod4-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod4-2", AgeEnum.ONE)); h2StudentMapper.insertFillByCustomMethod4(list); list.forEach(user -> Assertions.assertNotNull(user.getTestType())); list = Arrays.asList(new H2User("insertFillByCustomMethod5-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod5-2", AgeEnum.ONE)); h2StudentMapper.insertFillByCustomMethod5(list); list.forEach(user -> Assertions.assertNotNull(user.getTestType())); list = Arrays.asList(new H2User("insertFillByCustomMethod6-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod6-2", AgeEnum.ONE)); h2StudentMapper.insertFillByCustomMethod6(list); list.forEach(user -> Assertions.assertNotNull(user.getTestType())); list = Arrays.asList(new H2User("insertFillByCustomMethod7-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod7-2", AgeEnum.ONE)); h2StudentMapper.insertFillByCustomMethod7(list); list.forEach(user -> Assertions.assertNotNull(user.getTestType())); H2User[] h2Users; h2Users = new H2User[]{new H2User("insertFillByCustomMethod8-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod8-2", AgeEnum.ONE)}; h2StudentMapper.insertFillByCustomMethod8(h2Users); Arrays.stream(h2Users).forEach(user -> Assertions.assertNotNull(user.getTestType())); h2Users = new H2User[]{new H2User("insertFillByCustomMethod9-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod9-2", AgeEnum.ONE)}; h2StudentMapper.insertFillByCustomMethod9(h2Users); Arrays.stream(h2Users).forEach(user -> Assertions.assertNotNull(user.getTestType())); Map map; h2User = new H2User("insertFillByCustomMethod10", AgeEnum.ONE); map = new HashMap<>(); map.put("et", h2User); h2StudentMapper.insertFillByCustomMethod10(map); Assertions.assertNotNull(h2User.getTestType()); list = Arrays.asList(new H2User("insertFillByCustomMethod11-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod11-2", AgeEnum.ONE)); map = new HashMap<>(); map.put("list", list); h2StudentMapper.insertFillByCustomMethod11(map); list.forEach(user -> Assertions.assertNotNull(user.getTestType())); list = Arrays.asList(new H2User("insertFillByCustomMethod12-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod12-2", AgeEnum.ONE)); map = new HashMap<>(); map.put("coll", list); h2StudentMapper.insertFillByCustomMethod12(map); list.forEach(user -> Assertions.assertNotNull(user.getTestType())); h2Users = new H2User[]{new H2User("insertFillByCustomMethod13-1", AgeEnum.ONE), new H2User("insertFillByCustomMethod13-2", AgeEnum.ONE)}; map = new HashMap<>(); map.put("array", h2Users); h2StudentMapper.insertFillByCustomMethod13(map); list.forEach(user -> Assertions.assertNotNull(user.getTestType())); } @Test void testUpdateFill() { Map map; H2User h2User; h2User = new H2User(); map = new HashMap<>(); map.put("et", h2User); map.put("list", Arrays.asList(1L, 2L, 3L)); h2StudentMapper.updateFillByCustomMethod1(map); Assertions.assertNotNull(h2User.getLastUpdatedDt()); h2User = new H2User(); h2StudentMapper.updateFillByCustomMethod2(Arrays.asList(1L, 2L, 3L), h2User); Assertions.assertNotNull(h2User.getLastUpdatedDt()); h2User = new H2User(); h2StudentMapper.updateFillByCustomMethod3(Arrays.asList(1L, 2L, 3L), h2User); Assertions.assertNotNull(h2User.getLastUpdatedDt()); h2User = new H2User(); h2StudentMapper.updateFillByCustomMethod4(Arrays.asList(1L, 2L, 3L), h2User); Assertions.assertNotNull(h2User.getLastUpdatedDt()); } @Test void testListMapsByPage() { Assertions.assertEquals(userService.listMaps().size(), userService.count()); Assertions.assertEquals(userService.listMaps(new Page<>(1, 2)).size(), userService.page(new Page<>(1, 2)).getRecords().size()); Assertions.assertEquals(userService.listMaps(new Page<>(2, 2)).size(), userService.page(new Page<>(2, 2)).getRecords().size()); Assertions.assertEquals( userService.pageMaps(new Page<>(1, 2, false)).getRecords().size(), userService.listMaps(new Page<>(1, 2, false)).size() ); Assertions.assertEquals( userService.pageMaps(new Page<>(2, 2, false)).getRecords().size(), userService.listMaps(new Page<>(2, 2, false)).size() ); Assertions.assertEquals( userService.pageMaps(new Page<>(1, 2, false), Wrappers.emptyWrapper()).getRecords().size(), userService.listMaps(new Page<>(1, 2, false), Wrappers.emptyWrapper()).size() ); Assertions.assertEquals( userService.pageMaps(new Page<>(2, 2, false), Wrappers.emptyWrapper()).getRecords().size(), userService.listMaps(new Page<>(2, 2, false), Wrappers.emptyWrapper()).size() ); } @Test void testListByPage() { Assertions.assertEquals(userService.list().size(), userService.count()); Assertions.assertEquals(userService.list(new Page<>(1, 2)).size(), userService.page(new Page<>(1, 2)).getRecords().size()); Assertions.assertEquals(userService.list(new Page<>(2, 2)).size(), userService.page(new Page<>(2, 2)).getRecords().size()); Assertions.assertEquals( userService.list(new Page<>(1, 2, false), Wrappers.emptyWrapper()).size(), userService.page(new Page<>(1, 2, false), Wrappers.emptyWrapper()).getRecords().size() ); List list = userService.list(new Page<>(2, 2, false)); Assertions.assertEquals( userService.list(new Page<>(2, 2, false), Wrappers.emptyWrapper()).size(), userService.page(new Page<>(2, 2, false), Wrappers.emptyWrapper()).getRecords().size() ); } @Test void testUnchecked() { Wrappers.lambdaQuery() .select(H2User::getAge, H2User::getAge).select(true, H2User::getDeleted, H2User::getDeleted) .orderBy(true, true, H2User::getAge, H2User::getAge) .orderByAsc(H2User::getAge, H2User::getDeleted).orderByAsc(true, H2User::getAge, H2User::getTestType) .orderByDesc(H2User::getDeleted, H2User::getPrice).orderByDesc(true, H2User::getDeleted, H2User::getTestType) .groupBy(H2User::getAge, H2User::getTestType).groupBy(true, H2User::getAge, H2User::getTestType); new LambdaQueryChainWrapper<>(H2User.class) .select(H2User::getAge).select(true, H2User::getDeleted, H2User::getDeleted) .orderBy(true, true, H2User::getAge, H2User::getAge) .orderByAsc(H2User::getAge, H2User::getDeleted).orderByAsc(true, H2User::getAge, H2User::getTestType) .orderByDesc(H2User::getDeleted, H2User::getPrice).orderByDesc(true, H2User::getDeleted, H2User::getTestType) .groupBy(H2User::getAge, H2User::getTestType).groupBy(true, H2User::getAge, H2User::getTestType); // 重写方法保留支持. new LambdaQueryChainWrapper<>(H2User.class) { @Override protected LambdaQueryChainWrapper doOrderByDesc(boolean condition, SFunction column, List> columns) { System.out.println("-------处理OrderByDesc----------"); return super.doOrderByDesc(condition, column, columns); } @Override protected LambdaQueryChainWrapper doOrderByAsc(boolean condition, SFunction column, List> columns) { System.out.println("-------处理OrderByAsc----------"); return super.doOrderByAsc(condition, column, columns); } @Override protected LambdaQueryChainWrapper doOrderBy(boolean condition, boolean isAsc, SFunction column, List> columns) { System.out.println("-------处理OrderBy----------"); return super.doOrderBy(condition, isAsc, column, columns); } @Override protected LambdaQueryChainWrapper doGroupBy(boolean condition, SFunction column, List> columns) { System.out.println("-------处理GroupBy----------"); return super.doGroupBy(condition, column, columns); } @Override protected LambdaQueryChainWrapper doSelect(boolean condition, List> columns) { System.out.println("-------处理Select----------"); return super.doSelect(condition, columns); } } .select(H2User::getAge) .select(true, H2User::getDeleted, H2User::getDeleted) .orderBy(true, true, H2User::getAge, H2User::getAge) .orderByAsc(H2User::getAge, H2User::getDeleted).orderByAsc(true, H2User::getAge, H2User::getTestType) .orderByDesc(H2User::getDeleted, H2User::getPrice).orderByDesc(true, H2User::getDeleted, H2User::getTestType) .groupBy(H2User::getAge, H2User::getTestType).groupBy(true, H2User::getAge, H2User::getTestType); } @Test void testSelectObjs() { for (Object o : userService.listObjs()) { Assertions.assertEquals(Long.class, o.getClass()); } for (Long id : userService.listObjs()) { System.out.println(id); } } @Test void testResultSet() { BaseMapper baseMapper = userService.getBaseMapper(); Page page = new Page<>(1, 1000000); System.out.println("--------------------------------------------"); baseMapper.selectList(page, Wrappers.emptyWrapper()); List ids = new ArrayList<>(); System.out.println("---------------selectListByPage-------------------"); baseMapper.selectList(page, Wrappers.emptyWrapper(), resultContext -> { H2User resultObject = resultContext.getResultObject(); ids.add(resultObject.getTestId()); System.out.println(resultObject); }); System.out.println("---------------selectBatchIds-------------------"); baseMapper.selectByIds(ids, resultContext -> System.out.println(resultContext.getResultObject())); System.out.println("---------------selectList-------------------"); System.out.println("---------------selectObjs-------------------"); baseMapper.selectObjs(Wrappers.emptyWrapper(), (ResultHandler) resultContext -> System.out.println(resultContext.getResultObject())); System.out.println("---------------selectByMap-------------------"); baseMapper.selectByMap(new HashMap<>(), resultContext -> System.out.println(resultContext.getResultObject())); System.out.println("---------------selectMapsByPage-------------------"); baseMapper.selectMaps(Page.of(1, 100000), Wrappers.emptyWrapper(), resultContext -> resultContext.getResultObject().forEach((k, v) -> System.out.println(k + "--------" + v))); System.out.println("---------------selectMaps-------------------"); baseMapper.selectMaps(Wrappers.emptyWrapper(), resultContext -> resultContext.getResultObject().forEach((k, v) -> System.out.println(k + "--------" + v))); } @Test void testSelectOne() { Assertions.assertTrue(userService.list().size() > 2); Assertions.assertThrows(TooManyResultsException.class, () -> userService.getBaseMapper().selectOne(Wrappers.emptyWrapper())); Assertions.assertNotNull(userService.getBaseMapper().selectOne(Wrappers.emptyWrapper(), false)); } @Test void testSaveOrUpdateTransactional1() { var id = IdWorker.getId(); var userList = List.of(new H2User(id, "test-1"), new H2User(IdWorker.getId(), "test-2"), new H2User(id, "test-3")); Assertions.assertThrowsExactly(PersistenceException.class, () -> userService.testSaveOrUpdateTransactional1(userList)); } @Test void testSaveOrUpdateTransactional2() { var id = IdWorker.getId(); var userList = List.of(new H2User(id, "test-1"), new H2User(IdWorker.getId(), "test-2"), new H2User(id, "test-3")); userService.testSaveOrUpdateTransactional2(userList); Assertions.assertEquals("test-3", userService.getById(id).getName()); } @Test void testOrderByExpression() { Page page = new Page<>(); page.addOrder(OrderItem.withExpression(""" CASE WHEN age > 1 THEN 2 WHEN age < 1 THEN 1 END """)); page.addOrder(OrderItem.withExpression(""" CASE WHEN name IS NOT NULL THEN 0 ELSE 1 END """, false)); Assertions.assertDoesNotThrow(() -> userService.page(page)); } @Test @Transactional void selectUsersWithCursor() { // 使用 try-with-resources 确保 Cursor 被正确关闭 // 注意:必须添加 @Transactional 注解,保证 SqlSession 在迭代期间保持打开状态 try (Cursor cursor = userService.getBaseMapper().selectWithCursor(null)) { // 遍历游标,逐条处理数据 Iterator iterator = cursor.iterator(); while (iterator.hasNext()) { // 每次读取一条数据 H2User user = iterator.next(); // 处理单条记录 System.out.println("Processing user: " + user.getName()); } } catch (IOException e) { e.printStackTrace(); } H2User user = userService.getBaseMapper().selectOne(null); Assertions.assertNotNull(user); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2userNameJsonTypeHandler.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.test.h2.entity.H2User; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 只做演示其它方法未实现 */ @Slf4j @MappedTypes({Object.class}) @MappedJdbcTypes(JdbcType.VARCHAR) public class H2userNameJsonTypeHandler extends BaseTypeHandler { @Override public void setNonNullParameter(PreparedStatement ps, int i, String json, JdbcType jdbcType) throws SQLException { H2User h2User = JSON.parseObject(json, H2User.class); ps.setString(i, h2User.getName()); } @Override public String getNullableResult(ResultSet resultSet, String s) throws SQLException { return null; } @Override public String getNullableResult(ResultSet resultSet, int i) throws SQLException { return null; } @Override public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException { return null; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/LastSqlTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.test.h2.entity.H2Student; import com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.List; import java.util.Map; /** * OrderBy LastSql 混合测试 * * @author Dervish */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"}) public class LastSqlTest { @Autowired private H2StudentMapper mapper; @Test @Order(1) public void delete() { int res = mapper.delete(new LambdaUpdateWrapper().last(" and 1 = 2 ").eq(H2Student::getId, 0)); Assertions.assertTrue(res <= 0); } @Test @Order(2) public void selectCount() { long result = mapper.selectCount(new LambdaQueryWrapper().last("where 1 =2")); Assertions.assertTrue(result <= 0); } @Test @Order(3) public void selectList() { List h2Students = mapper.selectList(new LambdaQueryWrapper().last("limit 1")); Assertions.assertEquals(1, h2Students.size()); } @Test @Order(4) public void selectMaps() { List> maps = mapper.selectMaps(new LambdaQueryWrapper().last("limit 1")); Assertions.assertEquals(1, maps.size()); } @Test @Order(5) public void selectMapsPage() { IPage> page = Page.of(0, 10); mapper.selectMapsPage(page, new QueryWrapper().last(" /* testSql */ ").comment("test")); } @Test @Order(6) public void selectObjs() { List objects = mapper.selectObjs(new QueryWrapper().last(" limit 1")); Assertions.assertEquals(1, objects.size()); } @Test @Order(7) public void SelectOne() { H2Student h2Student = mapper.selectOne(new QueryWrapper().last(" where 1 = 2")); Assertions.assertNull(h2Student); } @Test @Order(8) public void selectPage() { IPage page = Page.of(0, 10); mapper.selectPage(page, new QueryWrapper().last(" /* testSql */ ")); } @Test @Order(9) public void update() { int res = mapper.update(null, new UpdateWrapper().set("name", "dog").last(" where 1 =2 ")); Assertions.assertTrue(res <= 0); } @Test @Order(10) public void selectListOrderBy() { List h2Students = mapper.selectList(null); Assertions.assertEquals(6, h2Students.size()); Assertions.assertEquals(6, mapper.selectList(new LambdaQueryWrapper() .eq(H2Student::getAge, 1)).size()); Assertions.assertEquals(6, mapper.selectList(new QueryWrapper() .orderByAsc("age")).size()); } @Test @Order(11) public void selectPageOrderBy() { mapper.selectPage(Page.of(0, 10), null); } @Test @Order(12) public void selectMapsOrderBy() { List> maps = mapper.selectMaps(null); Assertions.assertEquals(6, maps.size()); } @Test @Order(12) public void selectMapsPageOrderBy() { mapper.selectMapsPage(Page.of(0, 10), null); } @Test @Order(12) public void selectObjsOrderBy() { List objs = mapper.selectObjs(null); Assertions.assertEquals(6, objs.size()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/MybatisMapperRegistryTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.MybatisMapperRegistry; import com.baomidou.mybatisplus.core.override.MybatisMapperProxyFactory; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.baomidou.mybatisplus.test.h2.config.DBConfig; import com.baomidou.mybatisplus.test.h2.config.MybatisPlusConfig; import com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper; import com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; /** * @author nieqiurong 2019/4/12. */ @ExtendWith(SpringExtension.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @MapperScan(value = "com.baomidou.mybatisplus.test.h2") @ContextConfiguration(classes = {MybatisMapperRegistryTest.class, DBConfig.class}) class MybatisMapperRegistryTest extends BaseTest { private interface H2StudentChildrenMapper extends H2StudentMapper { } @Bean DBConfig dbConfig() { return new DBConfig(); } @Bean MybatisPlusConfig mybatisPlusConfig() { return new MybatisPlusConfig(); } @Bean SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); return sqlSessionFactory.getObject(); } @Autowired private SqlSessionFactory sqlSessionFactory; @SuppressWarnings("unchecked") @Test void test() throws ReflectiveOperationException { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { Configuration configuration = sqlSessionFactory.getConfiguration(); MybatisMapperRegistry mapperRegistry = (MybatisMapperRegistry) sqlSessionFactory.getConfiguration().getMapperRegistry(); Assertions.assertTrue(mapperRegistry.hasMapper(H2UserMapper.class)); Assertions.assertTrue(mapperRegistry.hasMapper(H2StudentChildrenMapper.class)); H2StudentMapper studentMapper = mapperRegistry.getMapper(H2StudentMapper.class, sqlSession); Assertions.assertTrue(configuration.hasStatement(H2StudentMapper.class.getName() + ".selectById")); studentMapper.selectById(1); Field field = mapperRegistry.getClass().getDeclaredField("knownMappers"); field.setAccessible(true); Map, MybatisMapperProxyFactory> knownMappers = (Map, MybatisMapperProxyFactory>) field.get(mapperRegistry); MybatisMapperProxyFactory mybatisMapperProxyFactory = knownMappers.get(H2StudentChildrenMapper.class); H2StudentChildrenMapper h2StudentChildrenMapper = mapperRegistry.getMapper(H2StudentChildrenMapper.class, sqlSession); Assertions.assertTrue(configuration.hasStatement(H2StudentChildrenMapper.class.getName() + ".selectById")); Map methodCache = mybatisMapperProxyFactory.getMethodCache(); Assertions.assertTrue(methodCache.isEmpty()); h2StudentChildrenMapper.selectById(2); methodCache = mybatisMapperProxyFactory.getMethodCache(); Assertions.assertFalse(methodCache.isEmpty()); } } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/SqlRunnerTest.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.toolkit.SqlRunner; import com.baomidou.mybatisplus.test.h2.entity.H2Student; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import com.baomidou.mybatisplus.test.h2.service.IH2StudentService; import lombok.AllArgsConstructor; import lombok.Data; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; import java.util.HashMap; import java.util.List; import java.util.Map; /** * SqlRunner测试 * * @author nieqiurong 2018/8/25 11:05. */ @ExtendWith(SpringExtension.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) @ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"}) class SqlRunnerTest { @Autowired private IH2StudentService studentService; @Test @Order(3) void testSelectCount() { long count = SqlRunner.db().selectCount("select count(1) from h2student"); Assertions.assertTrue(count > 0); count = SqlRunner.db().selectCount("select count(1) from h2student where id > {0}", 0); Assertions.assertTrue(count > 0); count = SqlRunner.db(H2Student.class).selectCount("select count(1) from h2student"); Assertions.assertTrue(count > 0); count = SqlRunner.db(H2Student.class).selectCount("select count(1) from h2student where id > {0}", 0); Assertions.assertTrue(count > 0); } @Test @Transactional @Order(1) void testInsert() { Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student ( name, age ) VALUES ( {0}, {1} )", "测试学生", 2)); Assertions.assertTrue(SqlRunner.db(H2Student.class).insert("INSERT INTO h2student ( name, age ) VALUES ( {0}, {1} )", "测试学生2", 3)); } @Test @Order(2) void testTransactional() { try { studentService.testSqlRunnerTransactional(); } catch (RuntimeException e) { List list = studentService.list(new QueryWrapper().like("name", "sqlRunnerTx")); Assertions.assertTrue(CollectionUtils.isEmpty(list)); } } @Test @Order(4) void testSelectPage() { IPage> page1 = SqlRunner.db().selectPage(new Page<>(1, 3), "select * from h2student"); Assertions.assertEquals(3, page1.getRecords().size()); IPage> page2 = SqlRunner.db().selectPage(new Page<>(1, 3), "select * from h2student where id >= {0}", 0); Assertions.assertEquals(3, page2.getRecords().size()); IPage> page3 = SqlRunner.db().selectPage(new Page<>(1, 3), "select * from h2student where id = {0}", 10086); Assertions.assertEquals(0, page3.getRecords().size()); } @Test @Order(5) void testInsertByDisorderParameter() { Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {3}, {2}, {1} )", "测试学生", 2, "'六翻了'", 10000)); Assertions.assertTrue(SqlRunner.db(H2Student.class).insert("INSERT INTO h2student ( name, age, id ) VALUES ( {0}, {1}, {2} )", "测试学生2", 3, 10001)); Assertions.assertEquals(2, SqlRunner.db().selectCount("select count(1) from h2student where (id = 10000 or id = 10001)")); } @Test @Order(6) void testSpecialParameters() { var name = "`测`的'的'\\//塞'2"; Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {3}, {0}, {1} )", name, 2, "'六翻了'", 10004)); Assertions.assertEquals(10004L, SqlRunner.db().selectObj("select id from h2student where name = {0}", name)); name = "`测`的'的'\\//塞'2" + "2"; Assertions.assertTrue(SqlRunner.db().update("update h2student set name = {0} where id = {1}", name, 10004L)); Assertions.assertEquals(10004L, SqlRunner.db().selectObj("select id from h2student where name = {0}", name)); } @Test @Order(7) void testByMap() { var map = Map.of("name", "test", "age", AgeEnum.TWO, "id", 11000L); Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {id}, {name}, {age} )", map)); Map resultMap = SqlRunner.db().selectOne("select * from h2student where id = {id}", map); Assertions.assertNotNull(resultMap); Assertions.assertEquals(map.get("name"), resultMap.get("NAME")); Assertions.assertEquals(AgeEnum.TWO.getValue(), resultMap.get("AGE")); Assertions.assertEquals(map.get("id"), resultMap.get("ID")); map = new HashMap<>(); map.put("name","test"); map.put("age", AgeEnum.TWO); map.put("id", 11100L); map.put("size", "测试size"); Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {id}, {size}, {age} )", map)); resultMap = SqlRunner.db().selectOne("select * from h2student where id = {id}", map); Assertions.assertNotNull(resultMap); Assertions.assertEquals(map.get("size"), resultMap.get("NAME")); Assertions.assertEquals(AgeEnum.TWO.getValue(), resultMap.get("AGE")); Assertions.assertEquals(map.get("id"), resultMap.get("ID")); } @Test @Order(8) void testByEntity() { var entity = new H2Student(); entity.setId(11001L); entity.setName("test"); entity.setAge(12); Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {id}, {name}, {age} )", entity)); } @Data @AllArgsConstructor static class StudentDto { private Long id; private String name; private AgeEnum age; } @Test @Order(9) void testByDto() { var studentDto = new StudentDto(11002L, "测试学生", AgeEnum.THREE); Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {id}, {name}, {age} )", studentDto)); Map resultMap = SqlRunner.db().selectOne("select * from h2student where id = {id}", studentDto); Assertions.assertNotNull(resultMap); Assertions.assertEquals(studentDto.getName(), resultMap.get("NAME")); Assertions.assertEquals(studentDto.getAge().getValue(), resultMap.get("AGE")); Assertions.assertEquals(studentDto.getId(), resultMap.get("ID")); } @Test @Order(10) void testByArray() { var array = new Object[]{11003L, "测试学生", AgeEnum.THREE}; Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {0}, {1}, {2} )", array)); Map resultMap = SqlRunner.db().selectOne("select * from h2student where id = {0}", array); Assertions.assertNotNull(resultMap); Assertions.assertEquals("测试学生", resultMap.get("NAME")); Assertions.assertEquals(AgeEnum.THREE.getValue(), resultMap.get("AGE")); resultMap = SqlRunner.db().selectOne("select * from h2student where id = {0}", new long[]{11003L}); Assertions.assertNotNull(resultMap); Assertions.assertEquals("测试学生", resultMap.get("NAME")); Assertions.assertEquals(AgeEnum.THREE.getValue(), resultMap.get("AGE")); Assertions.assertEquals(11003L, resultMap.get("ID")); Assertions.assertNull(SqlRunner.db().selectOne("select * from h2student where id = {0} and name= {1}", (Object) new Object[]{11003L, "234"})); Assertions.assertNull(SqlRunner.db().selectOne("select * from h2student where id = {0} and name= {1}", new Object[]{11003L, "234"})); Assertions.assertNull(SqlRunner.db().selectOne("select * from h2student where id = {0} and name= {1}", 11003L, "234")); } @Test @Order(11) void testByList() { var list = List.of(11004L, "测试学生", AgeEnum.THREE); Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {0}, {1}, {2} )", list)); Map resultMap = SqlRunner.db().selectOne("select * from h2student where id = {0}", list); Assertions.assertNotNull(resultMap); Assertions.assertEquals("测试学生", resultMap.get("NAME")); Assertions.assertEquals(AgeEnum.THREE.getValue(), resultMap.get("AGE")); Assertions.assertEquals(11004L, resultMap.get("ID")); } record StudentDtoRecord(Long id, String name, AgeEnum age) { } @Test @Order(12) void testByRecord() { var studentDto = new StudentDtoRecord(11005L, "测试学生", AgeEnum.THREE); Assertions.assertTrue(SqlRunner.db().insert("INSERT INTO h2student (id, name, age ) VALUES ( {id}, {name}, {age} )", studentDto)); Map resultMap = SqlRunner.db().selectOne("select * from h2student where id = {id}", studentDto); Assertions.assertNotNull(resultMap); Assertions.assertEquals(studentDto.name, resultMap.get("NAME")); Assertions.assertEquals(studentDto.age.getValue(), resultMap.get("AGE")); Assertions.assertEquals(studentDto.id, resultMap.get("ID")); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/TestXmlConfig.java ================================================ package com.baomidou.mybatisplus.test.h2; import com.baomidou.mybatisplus.test.h2.entity.H2User; import com.baomidou.mybatisplus.test.h2.service.IH2UserService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; /** * 测试XML配置 * @author nieqiurong 2018/8/14 13:30. */ @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-test-xml-h2.xml"}) class TestXmlConfig { @Autowired protected IH2UserService userService; @Test void test() { H2User user = userService.getById(101L); Assertions.assertNotNull(user); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/CacheConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.cache; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.cache.mapper") public class CacheConfig { @Bean("mybatisSqlSession") public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultExecutorType(ExecutorType.REUSE); configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); configuration.setCacheEnabled(true); sqlSessionFactory.setConfiguration(configuration); MybatisPlusInterceptor pagination = new MybatisPlusInterceptor(); pagination.addInnerInterceptor(new PaginationInnerInterceptor()); sqlSessionFactory.setPlugins(pagination); return sqlSessionFactory.getObject(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/CacheTest.java ================================================ package com.baomidou.mybatisplus.test.h2.cache; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.test.h2.cache.mapper.CacheMapper; import com.baomidou.mybatisplus.test.h2.cache.model.CacheModel; import com.baomidou.mybatisplus.test.h2.cache.service.ICacheService; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Arrays; import java.util.Collections; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-cache-h2.xml"}) class CacheTest { @Autowired private ICacheService cacheService; @Autowired private SqlSessionFactory sqlSessionFactory; @Test @Order(1) void testPageCache() { Cache cache = getCache(); IPage cacheModelIPage1 = cacheService.page(new Page<>(1, 3), new QueryWrapper<>()); IPage cacheModelIPage2 = cacheService.page(new Page<>(1, 3), new QueryWrapper<>()); Assertions.assertEquals(2, cache.getSize()); Assertions.assertEquals(cacheModelIPage1.getTotal(), cacheModelIPage2.getTotal()); Assertions.assertEquals(cacheModelIPage1.getRecords().size(), cacheModelIPage2.getRecords().size()); IPage cacheModelIPage3 = cacheService.page(new Page<>(2, 3), new QueryWrapper<>()); Assertions.assertEquals(cacheModelIPage1.getTotal(), cacheModelIPage3.getTotal()); Assertions.assertEquals(2, cacheModelIPage3.getRecords().size()); Assertions.assertEquals(3, cache.getSize()); IPage cacheModelIPage4 = cacheService.page(new Page<>(2, 3, false), new QueryWrapper<>()); Assertions.assertEquals(0L, cacheModelIPage4.getTotal()); Assertions.assertEquals(2, cacheModelIPage4.getRecords().size()); Assertions.assertEquals(3, cache.getSize()); IPage cacheModelIPage5 = cacheService.page(new Page<>(2, 3, true), new QueryWrapper<>()); Assertions.assertEquals(cacheModelIPage5.getTotal(), cacheModelIPage3.getTotal()); Assertions.assertEquals(2, cacheModelIPage5.getRecords().size()); IPage cacheModelIPage6 = cacheService.page(new Page<>(1, 3, true), new QueryWrapper().ge("id", 2L)); Assertions.assertEquals(4, cacheModelIPage6.getTotal()); Assertions.assertEquals(3, cacheModelIPage6.getRecords().size()); IPage cacheModelIPage7 = cacheService.page(new Page<>(1, 3, false), new QueryWrapper().ge("id", 2L)); Assertions.assertEquals(0L, cacheModelIPage7.getTotal()); Assertions.assertEquals(3, cacheModelIPage7.getRecords().size()); IPage cacheModelIPage8 = cacheService.page(new Page<>(1, 3, false), new QueryWrapper().ge("id", 3L)); Assertions.assertEquals(0L, cacheModelIPage8.getTotal()); Assertions.assertEquals(3, cacheModelIPage8.getRecords().size()); cacheModelIPage8 = cacheService.page(new Page<>(1, 3, false), new QueryWrapper().ge("id", 3L)); Assertions.assertEquals(0L, cacheModelIPage8.getTotal()); Assertions.assertEquals(3, cacheModelIPage8.getRecords().size()); IPage cacheModelIPage9 = cacheService.page(new Page<>(1, 3, true), new QueryWrapper().ge("id", 3L)); Assertions.assertEquals(3L, cacheModelIPage9.getTotal()); Assertions.assertEquals(3, cacheModelIPage9.getRecords().size()); cacheModelIPage9 = cacheService.page(new Page<>(1, 3, true), new QueryWrapper().ge("id", 3L)); Assertions.assertEquals(3L, cacheModelIPage9.getTotal()); Assertions.assertEquals(3, cacheModelIPage9.getRecords().size()); } @Test @Order(2) void testCleanBatchCache() { CacheModel model = new CacheModel("靓仔"); cacheService.save(model); Cache cache = getCache(); Assertions.assertEquals(0, cache.getSize()); cacheService.getById(model.getId()); Assertions.assertEquals(1, cache.getSize()); cacheService.updateBatchById(Collections.singletonList(new CacheModel(model.getId(), "旺仔"))); Assertions.assertEquals(0, cache.getSize()); Assertions.assertEquals("旺仔", cacheService.getById(model.getId()).getName()); Assertions.assertEquals(1, cache.getSize()); } @Test @Order(3) void testBatchTransactionalClear1() { Cache cache = getCache(); long id = cacheService.testBatchTransactionalClear1(); Assertions.assertEquals(0, cache.getSize()); CacheModel cacheModel = cacheService.getById(id); Assertions.assertEquals(1, cache.getSize()); Assertions.assertEquals("旺仔", cacheModel.getName()); } @Test @Order(4) void testBatchTransactionalClear2() { long id = cacheService.testBatchTransactionalClear2(); Cache cache = getCache(); Assertions.assertEquals(0, cache.getSize()); CacheModel cacheModel = cacheService.getById(id); Assertions.assertEquals(1, cache.getSize()); Assertions.assertEquals("小红", cacheModel.getName()); } @Test @Order(5) void testBatchTransactionalClear3() { long id = cacheService.testBatchTransactionalClear3(); Cache cache = getCache(); Assertions.assertEquals(1, cache.getSize()); CacheModel cacheModel = cacheService.getById(id); Assertions.assertEquals(1, cache.getSize()); Assertions.assertEquals("小红", cacheModel.getName()); } @Test @Order(6) void testBatchTransactionalClear4() { long id = cacheService.testBatchTransactionalClear4(); Cache cache = getCache(); Assertions.assertEquals(0, cache.getSize()); CacheModel cacheModel = cacheService.getById(id); Assertions.assertEquals(1, cache.getSize()); Assertions.assertEquals("旺仔", cacheModel.getName()); } @Test @Order(7) void testBatchTransactionalClear5() { long id = cacheService.testBatchTransactionalClear5(); Cache cache = getCache(); Assertions.assertEquals(0, cache.getSize()); CacheModel cacheModel = cacheService.getById(id); Assertions.assertEquals(1, cache.getSize()); Assertions.assertNull(cacheModel); } @Test @Order(8) void testBatchTransactionalClear6() { long id = cacheService.testBatchTransactionalClear6(); Cache cache = getCache(); Assertions.assertEquals(0, cache.getSize()); CacheModel cacheModel = cacheService.getById(id); Assertions.assertEquals(1, cache.getSize()); Assertions.assertNull(cacheModel); } @Test @Order(9) void testBatchTransactionalClear7() { long id = cacheService.testBatchTransactionalClear7(); Cache cache = getCache(); Assertions.assertEquals(0, cache.getSize()); CacheModel cacheModel = cacheService.getById(id); Assertions.assertEquals(1, cache.getSize()); Assertions.assertNull(cacheModel); } @Test void testOrder() { Cache cache = getCache(); cache.clear(); Page page = new Page<>(1, 10, false); page.setOrders(Collections.singletonList(OrderItem.asc("id"))); cacheService.page(page); Assertions.assertEquals(1, cache.getSize()); page.setOrders(Arrays.asList(OrderItem.asc("id"), OrderItem.asc("name"))); cacheService.page(page); Assertions.assertEquals(2, cache.getSize()); page.setOrders(Arrays.asList(OrderItem.asc("name"), OrderItem.asc("id"))); cacheService.page(page); Assertions.assertEquals(3, cache.getSize()); page.setOrders(Collections.singletonList(OrderItem.desc("id"))); cacheService.page(page); Assertions.assertEquals(4, cache.getSize()); page = new Page<>(1, 10, true); page.setOrders(Collections.singletonList(OrderItem.asc("id"))); cacheService.page(page); Assertions.assertEquals(5, cache.getSize()); page.setOrders(Arrays.asList(OrderItem.asc("id"), OrderItem.asc("name"))); cacheService.page(page); Assertions.assertEquals(5, cache.getSize()); page.setOrders(Arrays.asList(OrderItem.asc("name"), OrderItem.asc("id"))); cacheService.page(page); Assertions.assertEquals(5, cache.getSize()); page.setOrders(Collections.singletonList(OrderItem.desc("id"))); cacheService.page(page); Assertions.assertEquals(5, cache.getSize()); } @Test void testCustomOffset(){ Cache cache = getCache(); cache.clear(); CustomPage page1 = new CustomPage<>(2, 10, false); Assertions.assertEquals(0, cache.getSize()); cacheService.page(page1); Assertions.assertEquals(1, cache.getSize()); cacheService.page(page1); Assertions.assertEquals(1, cache.getSize()); //页数其他条件不变,改变分页偏移量的骚操作. page1.setOffset(12L); cacheService.page(page1); Assertions.assertEquals(2, cache.getSize()); cacheService.page(page1); Assertions.assertEquals(2, cache.getSize()); } // @Test // void testCustomSaveOrUpdateBatch(){ // Assertions.assertTrue(cacheService.testCustomSaveOrUpdateBatch()); // } private Cache getCache() { return sqlSessionFactory.getConfiguration().getCache(CacheMapper.class.getName()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/CustomCache.java ================================================ package com.baomidou.mybatisplus.test.h2.cache; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.cache.Cache; import java.util.HashMap; import java.util.Map; @Slf4j public class CustomCache implements Cache { private final String id; private Map cache = new HashMap<>(); public CustomCache(String id) { this.id = id; } @Override public String getId() { return id; } @Override public int getSize() { return cache.size(); } @Override public void putObject(Object key, Object value) { log.info("添加命名空间:{},缓存:{}", id, key); cache.put(key, value); } @Override public Object getObject(Object key) { log.info("获取命名空间:{},缓存:{}", id, key); return cache.get(key); } @Override public Object removeObject(Object key) { log.info("清除命名空间:{},缓存:{}", id, key); return cache.remove(key); } @Override public void clear() { log.info("清除命名空间:{}缓存", id); cache.clear(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/CustomPage.java ================================================ package com.baomidou.mybatisplus.test.h2.cache; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.Data; import lombok.EqualsAndHashCode; /** * @author nieqiurong 2020/4/27. */ @Data @EqualsAndHashCode(callSuper = true) public class CustomPage extends Page { private long offset; public CustomPage() { } public CustomPage(long current, long size) { super(current, size); } public CustomPage(long current, long size, long total) { super(current, size, total); } public CustomPage(long current, long size, boolean isSearchCount) { super(current, size, isSearchCount); } public CustomPage(long current, long size, long total, boolean isSearchCount) { super(current, size, total, isSearchCount); } @Override public long offset() { return offset == 0 ? super.offset() : offset; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/mapper/CacheMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.cache.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.cache.CustomCache; import com.baomidou.mybatisplus.test.h2.cache.model.CacheModel; import org.apache.ibatis.annotations.CacheNamespace; @CacheNamespace(implementation = CustomCache.class) public interface CacheMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/model/CacheModel.java ================================================ package com.baomidou.mybatisplus.test.h2.cache.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @NoArgsConstructor @AllArgsConstructor @TableName(value = "t_cache") public class CacheModel implements Serializable { private Long id; private String name; public CacheModel(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/service/ICacheService.java ================================================ package com.baomidou.mybatisplus.test.h2.cache.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.h2.cache.model.CacheModel; public interface ICacheService extends IService { long testBatchTransactionalClear1(); long testBatchTransactionalClear2(); long testBatchTransactionalClear3(); long testBatchTransactionalClear4(); long testBatchTransactionalClear5(); long testBatchTransactionalClear6(); long testBatchTransactionalClear7(); // boolean testCustomSaveOrUpdateBatch(); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/service/impl/CacheServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.h2.cache.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.h2.cache.mapper.CacheMapper; import com.baomidou.mybatisplus.test.h2.cache.model.CacheModel; import com.baomidou.mybatisplus.test.h2.cache.service.ICacheService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Collections; @Service public class CacheServiceImpl extends ServiceImpl implements ICacheService { @Override @Transactional public long testBatchTransactionalClear1() { CacheModel model = new CacheModel("靓仔"); save(model); getById(model.getId()); updateBatchById(Collections.singletonList(new CacheModel(model.getId(), "旺仔"))); return model.getId(); } @Override @Transactional public long testBatchTransactionalClear2() { CacheModel model = new CacheModel("靓仔"); save(model); getById(model.getId()); updateBatchById(Collections.singletonList(new CacheModel(model.getId(), "旺仔"))); model.setName("小红"); updateById(model); return model.getId(); } @Override @Transactional public long testBatchTransactionalClear3() { CacheModel model = new CacheModel("靓仔"); save(model); updateBatchById(Collections.singletonList(new CacheModel(model.getId(), "旺仔"))); model.setName("小红"); updateById(model); getById(model.getId()); return model.getId(); } @Override public long testBatchTransactionalClear4() { CacheModel model = new CacheModel("靓仔"); save(model); updateBatchById(Collections.singletonList(new CacheModel(model.getId(), "旺仔"))); return model.getId(); } @Override public long testBatchTransactionalClear5() { CacheModel model = new CacheModel("靓仔"); save(model); removeBatchByIds(Collections.singletonList(model.getId())); removeById(model.getId()); return model.getId(); } @Override @Transactional public long testBatchTransactionalClear6() { CacheModel model = new CacheModel("靓仔"); save(model); removeBatchByIds(Collections.singletonList(model.getId())); return model.getId(); } @Override @Transactional public long testBatchTransactionalClear7() { CacheModel model = new CacheModel("靓仔"); save(model); updateBatchById(Collections.singletonList(new CacheModel(model.getId(), "旺仔"))); removeBatchByIds(Collections.singletonList(model.getId())); return model.getId(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/DBConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.config; import org.h2.Driver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.jdbc.datasource.init.DataSourceInitializer; import org.springframework.jdbc.datasource.init.DatabasePopulator; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; import java.io.IOException; /** * H2 Memory Database config * * @author Caratacus * @since 2017/4/1 */ @Configuration @EnableTransactionManagement public class DBConfig { private String locationPattern = "classpath:/h2/*.sql"; @Bean public DataSource dataSource(){ SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new Driver()); dataSource.setUrl("jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } @Bean public DataSourceTransactionManager transactionManager(DataSource ds) { return new DataSourceTransactionManager(ds); } @Bean public DataSourceInitializer dataSourceInitializer(DataSource dataSource) throws IOException { final DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); initializer.setDatabasePopulator(databasePopulator()); initializer.setEnabled(true); return initializer; } private DatabasePopulator databasePopulator() throws IOException { ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator(); resourceDatabasePopulator.setContinueOnError(false); resourceDatabasePopulator.addScripts( new PathMatchingResourcePatternResolver().getResources(locationPattern) ); return resourceDatabasePopulator; } @Bean public JdbcTemplate jdbcTemplate(DataSource ds){ return new JdbcTemplate(ds); } public String getLocationPattern() { return locationPattern; } public void setLocationPattern(String locationPattern) { this.locationPattern = locationPattern; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/MybatisPlusConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.config; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById; import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.DataChangeRecorderInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.baomidou.mybatisplus.test.h2.H2MetaObjectHandler; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; import javax.sql.DataSource; import java.util.List; /** * Mybatis Plus Config * * @author Caratacus * @since 2017/4/1 */ @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.mapper") public class MybatisPlusConfig { @Bean("mybatisSqlSession") public SqlSessionFactory sqlSessionFactory(DataSource dataSource, GlobalConfig globalConfig) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); sqlSessionFactory.setTypeAliasesPackage("com.baomidou.mybatisplus.test.h2.entity.persistent"); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); /* * 下划线转驼峰开启 */ configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultExecutorType(ExecutorType.REUSE); configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); //默认枚举处理 sqlSessionFactory.setConfiguration(configuration); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); mybatisPlusInterceptor.addInnerInterceptor(new DataChangeRecorderInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); globalConfig.setMetaObjectHandler(new H2MetaObjectHandler()); globalConfig.setSqlInjector(new DefaultSqlInjector() { /** * 测试注入自定义方法 */ @Override public List getMethodList(org.apache.ibatis.session.Configuration configuration, Class mapperClass, TableInfo tableInfo) { List methodList = super.getMethodList(configuration, mapperClass, tableInfo); // methodList.add(new LogicDeleteByIdWithFill()); methodList.add(new AlwaysUpdateSomeColumnById(t -> t.getFieldFill() != FieldFill.INSERT)); methodList.add(new InsertBatchSomeColumn(t -> !(t.getFieldFill() == FieldFill.UPDATE || t.isLogicDelete() || t.getProperty().equals("version")))); return methodList; } }); sqlSessionFactory.setGlobalConfig(globalConfig); return sqlSessionFactory.getObject(); } @Bean public GlobalConfig globalConfiguration() { GlobalConfig conf = new GlobalConfig(); conf.setEnableSqlRunner(true) .setDbConfig(new GlobalConfig.DbConfig() .setLogicDeleteValue("1") .setLogicNotDeleteValue("0")); return conf; } @Bean public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager){ return new TransactionTemplate(platformTransactionManager); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/MybatisPlusConfigLogicDelete.java ================================================ package com.baomidou.mybatisplus.test.h2.config; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById; import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn; import com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ResourceLoader; import javax.sql.DataSource; import java.util.List; /** * Mybatis Plus Config * * @author Caratacus * @since 2017/4/1 */ @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.mapper") public class MybatisPlusConfigLogicDelete { @Bean("mybatisSqlSession") public SqlSessionFactory sqlSessionFactory(DataSource dataSource, ResourceLoader resourceLoader, GlobalConfig globalConfig) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); // sqlSessionFactory.setConfigLocation(resourceLoader.getResource("classpath:mybatis-config-object-factory.xml")); sqlSessionFactory.setTypeAliasesPackage("com.baomidou.mybatisplus.test.h2.entity.persistent"); MybatisConfiguration configuration = new MybatisConfiguration(); // configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class); // org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setJdbcTypeForNull(JdbcType.NULL); /* * 下划线转驼峰开启 */ configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultExecutorType(ExecutorType.REUSE); configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); //默认枚举处理 sqlSessionFactory.setConfiguration(configuration); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); globalConfig.setSqlInjector(new DefaultSqlInjector() { /** * 测试注入自定义方法 */ @Override public List getMethodList(org.apache.ibatis.session.Configuration configuration, Class mapperClass, TableInfo tableInfo) { List methodList = super.getMethodList(configuration, mapperClass, tableInfo); methodList.add(new LogicDeleteByIdWithFill()); methodList.add(new AlwaysUpdateSomeColumnById(t -> t.getFieldFill() != FieldFill.INSERT)); methodList.add(new InsertBatchSomeColumn(t -> !(t.getFieldFill() == FieldFill.UPDATE || t.isLogicDelete() || t.getProperty().equals("version")))); return methodList; } }); sqlSessionFactory.setGlobalConfig(globalConfig); return sqlSessionFactory.getObject(); } @Bean public GlobalConfig globalConfiguration() { GlobalConfig conf = new GlobalConfig(); conf.setEnableSqlRunner(true) .setDbConfig(new GlobalConfig.DbConfig() .setLogicDeleteValue("NOW()") .setLogicNotDeleteValue("NULL") .setIdType(IdType.ASSIGN_ID)); return conf; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/MybatisXmlConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.config; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import javax.sql.DataSource; /** * @author nieqiurong 2018/8/14 13:18. */ @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.mapper") public class MybatisXmlConfig { @Bean("mybatisSqlSession") public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); sqlSessionFactory.setTypeAliasesPackage("com.baomidou.mybatisplus.test.h2.entity.persistent"); sqlSessionFactory.setConfigLocation(new ClassPathResource("mybatis-config-object-factory.xml")); return sqlSessionFactory.getObject(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/customfill/CustomFillConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.customfill; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.handlers.AnnotationHandler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.baomidou.mybatisplus.test.h2.customfill.annotation.InsertUpdateFill; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.UnknownTypeHandler; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.customfill.mapper") public class CustomFillConfig { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultExecutorType(ExecutorType.REUSE); configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); sqlSessionFactory.setConfiguration(configuration); GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setMetaObjectHandler(metaObjectHandler()); globalConfig.setAnnotationHandler(annotationHandler()); sqlSessionFactory.setGlobalConfig(globalConfig); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); return sqlSessionFactory.getObject(); } @Bean public MetaObjectHandler metaObjectHandler() { return new MetaObjectHandler() { @Override public void insertFill(MetaObject metaObject) { Object object = metaObject.getOriginalObject(); Class clazz = object.getClass(); Field[] declaredFields = clazz.getDeclaredFields(); List fieldList = Arrays.stream(declaredFields) .filter(field -> metaObject.hasSetter(field.getName())) .filter(field -> { AnnotationHandler annotationHandler = GlobalConfigUtils.getGlobalConfig(TableInfoHelper.getTableInfo(clazz).getConfiguration()).getAnnotationHandler(); return annotationHandler.isAnnotationPresent(field, TableField.class); }) .collect(Collectors.toList()); for (Field field : fieldList) { // 此处没有做字段与列的自动转化 String columnName = field.getName(); setFieldValByName(columnName, "1234567890", metaObject); } } @Override public void updateFill(MetaObject metaObject) { } }; } @Bean public AnnotationHandler annotationHandler() { return new AnnotationHandler() { /** * 结合{@link com.baomidou.mybatisplus.test.h2.customfill.model.TestModel#getA()}简单实现{@link InsertUpdateFill} 的获取过程 * * @param field 字段 * @param annotationClass 要获取的注解class * @param * @return */ @Override public T getAnnotation(Field field, Class annotationClass) { T annotation = field.getAnnotation(annotationClass); if (annotationClass != TableField.class) { return annotation; } Annotation insertUpdateFillAnno = field.getAnnotation(InsertUpdateFill.class); if (insertUpdateFillAnno == null) { return annotation; } /* 如果是要获取TableField场景,尝试判断是否存在InsertUpdateFill,存在则假定存在@TableField(fill = FieldFill.INSERT_UPDATE)的配置, 实际应用场景,应采取更加通用的方式,例如Spring的AnnotationUtils等 */ if (annotation != null) { TableField finalAnnotation = (TableField) annotation; annotation = (T) new TableField() { @Override public Class annotationType() { return finalAnnotation.annotationType(); } @Override public String value() { return finalAnnotation.value(); } @Override public boolean exist() { return finalAnnotation.exist(); } @Override public String condition() { return finalAnnotation.value(); } @Override public String update() { return finalAnnotation.update(); } @Override public FieldStrategy insertStrategy() { return finalAnnotation.insertStrategy(); } @Override public FieldStrategy updateStrategy() { return finalAnnotation.updateStrategy(); } @Override public FieldStrategy whereStrategy() { return finalAnnotation.whereStrategy(); } @Override public FieldFill fill() { return FieldFill.INSERT_UPDATE; } @Override public boolean select() { return finalAnnotation.select(); } @Override public boolean keepGlobalFormat() { return finalAnnotation.keepGlobalFormat(); } @Override public String property() { return finalAnnotation.value(); } @Override public JdbcType jdbcType() { return finalAnnotation.jdbcType(); } @Override public Class typeHandler() { return finalAnnotation.typeHandler(); } @Override public boolean javaType() { return finalAnnotation.javaType(); } @Override public String numericScale() { return finalAnnotation.value(); } }; } else { annotation = (T) new TableField() { @Override public Class annotationType() { return TableField.class; } @Override public String value() { return ""; } @Override public boolean exist() { return true; } @Override public String condition() { return ""; } @Override public String update() { return ""; } @Override public FieldStrategy insertStrategy() { return FieldStrategy.DEFAULT; } @Override public FieldStrategy updateStrategy() { return FieldStrategy.DEFAULT; } @Override public FieldStrategy whereStrategy() { return FieldStrategy.DEFAULT; } @Override public FieldFill fill() { return FieldFill.INSERT_UPDATE; } @Override public boolean select() { return true; } @Override public boolean keepGlobalFormat() { return false; } @Override public String property() { return ""; } @Override public JdbcType jdbcType() { return JdbcType.UNDEFINED; } @Override public Class typeHandler() { return UnknownTypeHandler.class; } @Override public boolean javaType() { return false; } @Override public String numericScale() { return ""; } }; } return annotation; } @Override public boolean isAnnotationPresent(Field field, Class annotationClass) { return getAnnotation(field, annotationClass) != null; } }; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/customfill/annotation/InsertUpdateFill.java ================================================ package com.baomidou.mybatisplus.test.h2.customfill.annotation; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) @TableField(fill = FieldFill.INSERT_UPDATE) public @interface InsertUpdateFill { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/customfill/mapper/TestModelMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.customfill.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.customfill.model.TestModel; public interface TestModelMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/customfill/model/TestModel.java ================================================ package com.baomidou.mybatisplus.test.h2.customfill.model; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.test.h2.customfill.annotation.InsertUpdateFill; import lombok.Data; @Data @TableName(value = "t_fill_test") public class TestModel { private Long id; @InsertUpdateFill private String a; @TableField(fill = FieldFill.INSERT_UPDATE) private String b; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2Addr.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; /** * h2address entity. * * @author yuxiaobin * @since 2017/5/25 */ @Data @Accessors(chain = true) @TableName("h2address") public class H2Addr { @TableId("addr_id") private Long addrId; @TableField("addr_name") private String addrName; @TableField("test_id") private Long testId; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2Student.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.OrderBy; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import com.baomidou.mybatisplus.test.h2.enums.GenderEnum; import com.baomidou.mybatisplus.test.h2.enums.GradeEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** * 学生实体 * * @author nieqiurong 2018/7/27. */ @Data @Accessors(chain = true) @TableName("h2student") @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = true) public class H2Student extends Model { /** * serialVersionUID */ private static final long serialVersionUID = 1290051894415073936L; public H2Student(Long id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } @TableId(type = IdType.AUTO) private Long id; private String name; @OrderBy private GradeEnum grade; private GenderEnum gender; private Integer age; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2User.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Date; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.Version; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; /** * 测试用户类 * * @author hubin sjy */ /* 表名 value 注解【 驼峰命名可无 】, resultMap 注解测试【 映射 xml 的 resultMap 内容 】 */ @Data @Accessors(chain = true) @TableName("h2user") @EqualsAndHashCode(callSuper = true) public class H2User extends SuperEntity { /** * serialVersionUID */ private static final long serialVersionUID = 2043176352335589747L; /* 测试忽略验证 */ private String name; private AgeEnum age; /*BigDecimal 测试*/ private BigDecimal price; /* 测试下划线字段命名类型, 字段填充 */ @TableField(fill = FieldFill.INSERT) private Integer testType; /** * 转义关键字测试 */ @TableField("`desc`") private String desc; /** * 该注解 select 默认不注入 select 查询 */ @TableField(select = false) private Date testDate; @Version private Integer version; @TableLogic private Integer deleted; @TableField("created_dt") private LocalDateTime createdDt; public H2User() { } public H2User(String name) { this.name = name; } public H2User(Integer testType) { this.testType = testType; } public H2User(String name, AgeEnum age) { this.name = name; this.age = age; } public H2User(Long id, String name) { this.setTestId(id); this.name = name; } public H2User(Long id, AgeEnum age) { this.setTestId(id); this.age = age; } public H2User(Long id, String name, AgeEnum age, Integer testType) { this.setTestId(id); this.name = name; this.age = age; this.testType = testType; } public H2User(String name, AgeEnum age, Integer testType) { this.name = name; this.age = age; this.testType = testType; } public H2User(String name, Integer deleted) { this.name = name; this.deleted = deleted; } @Override public String toString() { return "h2user:{name=" + name + "," + "age=" + age + "," + "price=" + price + "," + "testType=" + testType + "," + "desc=" + desc + "," + "testDate=" + testDate + "," + "version=" + version; } public static H2User ofId(Long id) { H2User h2User = new H2User(); h2User.setTestId(id); return h2User; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2UserLogicDelete.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import lombok.Data; import lombok.experimental.Accessors; import java.math.BigDecimal; import java.util.Date; /** * 测试用户类 * * @author hubin sjy */ /* 表名 value 注解【 驼峰命名可无 】, resultMap 注解测试【 映射 xml 的 resultMap 内容 】 */ @Data @Accessors(chain = true) @TableName("h2user") public class H2UserLogicDelete { /** * serialVersionUID */ private static final long serialVersionUID = 2043176352335589747L; @TableId private Long testId; /* 测试忽略验证 */ private String name; private AgeEnum age; /*BigDecimal 测试*/ private BigDecimal price; /* 测试下划线字段命名类型, 字段填充 */ @TableField private Integer testType; /** * 转义关键字测试 */ @TableField("`desc`") private String desc; /** * 该注解 select 默认不注入 select 查询 */ @TableField(select = false) private Date testDate; @Version private Integer version; private Integer deleted; @TableLogic private Date lastUpdatedDt; public H2UserLogicDelete() { } public H2UserLogicDelete(String name) { this.name = name; } public H2UserLogicDelete(Integer testType) { this.testType = testType; } public H2UserLogicDelete(String name, AgeEnum age) { this.name = name; this.age = age; } public H2UserLogicDelete(Long id, String name) { this.setTestId(id); this.name = name; } public H2UserLogicDelete(Long id, AgeEnum age) { this.setTestId(id); this.age = age; } public H2UserLogicDelete(Long id, String name, AgeEnum age, Integer testType) { this.setTestId(id); this.name = name; this.age = age; this.testType = testType; } public H2UserLogicDelete(String name, AgeEnum age, Integer testType) { this.name = name; this.age = age; this.testType = testType; } public H2UserLogicDelete(String name, Integer deleted) { this.name = name; this.deleted = deleted; } @Override public String toString() { return "h2user:{name=" + name + "," + "age=" + age + "," + "price=" + price + "," + "testType=" + testType + "," + "desc=" + desc + "," + "testDate=" + testDate + "," + "version=" + version; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2UserStrategy.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.test.h2.enums.AgeEnum; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import java.math.BigDecimal; import java.util.Date; /** * 测试用户类 * * @author hubin sjy */ /* 表名 value 注解【 驼峰命名可无 】, resultMap 注解测试【 映射 xml 的 resultMap 内容 】 */ @Data @Accessors(chain = true) @TableName("h2user") @EqualsAndHashCode(callSuper = true) public class H2UserStrategy extends SuperEntity { /** * serialVersionUID */ private static final long serialVersionUID = 2043176352335589747L; /** * whereStrategy = FieldStrategy.IGNORED 在拼接where条件时是在带上该条件 */ @TableField(value = "name", whereStrategy = FieldStrategy.ALWAYS) private String name; private AgeEnum age; /*BigDecimal 测试*/ private BigDecimal price; /* 测试下划线字段命名类型, 字段填充 */ @TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.ALWAYS) private Integer testType; /** * 转义关键字测试 * * @since 2019-5-7 测试updateStrategy */ @TableField(value = "`desc`", updateStrategy = FieldStrategy.ALWAYS) private String desc; /** * 该注解 select 默认不注入 select 查询 */ @TableField(select = false) private Date testDate; @Version private Integer version; @TableLogic private Integer deleted; public H2UserStrategy() { } public H2UserStrategy(String name) { this.name = name; } public H2UserStrategy(Integer testType) { this.testType = testType; } public H2UserStrategy(String name, AgeEnum age) { this.name = name; this.age = age; } public H2UserStrategy(Long id, String name) { this.setTestId(id); this.name = name; } public H2UserStrategy(Long id, AgeEnum age) { this.setTestId(id); this.age = age; } public H2UserStrategy(Long id, String name, AgeEnum age, Integer testType) { this.setTestId(id); this.name = name; this.age = age; this.testType = testType; } public H2UserStrategy(String name, AgeEnum age, Integer testType) { this.name = name; this.age = age; this.testType = testType; } public H2UserStrategy(String name, Integer deleted) { this.name = name; this.deleted = deleted; } @Override public String toString() { return "h2user:{name=" + name + "," + "age=" + age + "," + "price=" + price + "," + "testType=" + testType + "," + "desc=" + desc + "," + "testDate=" + testDate + "," + "version=" + version; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/SuSuperEntity.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import lombok.experimental.Accessors; import java.util.Date; /** * 多层集成测试 *

github #170

* * @author yuxiaobin * @since 2017/12/7 */ @Data @Accessors(chain = true) public abstract class SuSuperEntity { @TableField(value = "last_updated_dt", fill = FieldFill.UPDATE) private Date lastUpdatedDt; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/SuSuperEntityCamel.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import java.util.Date; /** * 多层集成测试 *

github #170

* * @author yuxiaobin * @since 2017/12/7 */ public abstract class SuSuperEntityCamel { @TableField(value = "lastUpdatedDt", fill = FieldFill.UPDATE) private Date lastUpdatedDt; public Date getLastUpdatedDt() { return lastUpdatedDt; } public void setLastUpdatedDt(Date lastUpdatedDt) { this.lastUpdatedDt = lastUpdatedDt; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/SuperEntity.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import java.io.Serializable; /** * 测试父类情况 * * @author hubin * @since 2016-06-26 */ @Data @Accessors(chain = true) @EqualsAndHashCode(callSuper = true) public class SuperEntity extends SuSuperEntity implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = -3111558058262086115L; /* 主键ID 注解,value 字段名,type 用户输入ID */ @TableId private Long testId; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/SuperEntityCamel.java ================================================ package com.baomidou.mybatisplus.test.h2.entity; import com.baomidou.mybatisplus.annotation.TableId; import java.io.Serializable; /** * 测试父类情况 * * @author hubin * @since 2016-06-26 */ public class SuperEntityCamel extends SuSuperEntityCamel implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = -531147777357149891L; /* 主键ID 注解,value 字段名,type 用户输入ID */ @TableId(value = "testId") private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/enums/AgeEnum.java ================================================ package com.baomidou.mybatisplus.test.h2.enums; import com.baomidou.mybatisplus.annotation.IEnum; /** * 通用枚举注入演示,注意需要实现 IEnums 也需要扫描枚举包 * * @author hubin * @since 2018-08-15 */ public enum AgeEnum implements IEnum { ONE(1, "一岁"), TWO(2, "二岁"), THREE(3, "三岁"); private final int value; @SuppressWarnings("unused") private final String desc; AgeEnum(final int value, final String desc) { this.value = value; this.desc = desc; } public static AgeEnum parseValue(Integer v) { if (v == null) { return null; } for (AgeEnum e : AgeEnum.values()) { if (e.getValue().equals(v)) { return e; } } return null; } @Override public Integer getValue() { return value; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/enums/GenderEnum.java ================================================ package com.baomidou.mybatisplus.test.h2.enums; /** *

* * @author yuxiaobin * @date 2018/8/30 */ public enum GenderEnum { MALE, FEMALE } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/enums/GradeEnum.java ================================================ package com.baomidou.mybatisplus.test.h2.enums; import com.baomidou.mybatisplus.annotation.EnumValue; /** *

* * @author yuxiaobin * @date 2018/8/30 */ public enum GradeEnum { PRIMARY(1, "小学"), SECONDARY(2, "中学"), HIGH(3, "高中"); GradeEnum(int code, String descp) { this.code = code; this.descp = descp; } @EnumValue private final int code; private final String descp; public int getCode() { return code; } public String getDescp() { return descp; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/FillPerformanceConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.fillperformance; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.fillperformance.mapper") public class FillPerformanceConfig { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultExecutorType(ExecutorType.REUSE); configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); sqlSessionFactory.setConfiguration(configuration); sqlSessionFactory.setGlobalConfig(new GlobalConfig().setMetaObjectHandler(metaObjectHandler())); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); return sqlSessionFactory.getObject(); } @Bean public MetaObjectHandler metaObjectHandler() { return new MetaObjectHandler() { @Override public void insertFill(MetaObject metaObject) { // strictInsertFill(metaObject,"c",String.class,"1234567890"); // strictInsertFill(metaObject,"d",String.class,"1234567890"); // strictInsertFill(metaObject,"e",String.class,"1234567890"); // strictInsertFill(metaObject,"f",String.class,"1234567890"); // strictInsertFill(metaObject,"g",String.class,"1234567890"); // strictInsertFill(metaObject,"h",String.class,"1234567890"); // strictInsertFill(metaObject,"i",String.class,"1234567890"); // strictInsertFill(metaObject,"j",String.class,"1234567890"); // strictInsertFill(metaObject,"l",String.class,"1234567890"); // strictInsertFill(metaObject,"m",String.class,"1234567890"); setFieldValByName("c", "1234567890", metaObject); setFieldValByName("d", "1234567890", metaObject); setFieldValByName("e", "1234567890", metaObject); setFieldValByName("f", "1234567890", metaObject); setFieldValByName("g", "1234567890", metaObject); setFieldValByName("h", "1234567890", metaObject); setFieldValByName("i", "1234567890", metaObject); setFieldValByName("j", "1234567890", metaObject); setFieldValByName("l", "1234567890", metaObject); setFieldValByName("m", "1234567890", metaObject); // setInsertFieldValByName("c","1234567890",metaObject); // setInsertFieldValByName("d","1234567890",metaObject); // setInsertFieldValByName("e","1234567890",metaObject); // setInsertFieldValByName("f","1234567890",metaObject); // setInsertFieldValByName("g","1234567890",metaObject); // setInsertFieldValByName("h","1234567890",metaObject); // setInsertFieldValByName("i","1234567890",metaObject); // setInsertFieldValByName("j","1234567890",metaObject); // setInsertFieldValByName("l","1234567890",metaObject); // setInsertFieldValByName("m","1234567890",metaObject); // strictInsertFill(findTableInfo(metaObject),metaObject, Arrays.asList( // StrictFill.of("c",String.class,"1234567890"), // StrictFill.of("d",String.class,"1234567890"), // StrictFill.of("e",String.class,"1234567890"), // StrictFill.of("f",String.class,"1234567890"), // StrictFill.of("g",String.class,"1234567890"), // StrictFill.of("h",String.class,"1234567890"), // StrictFill.of("i",String.class,"1234567890"), // StrictFill.of("j",String.class,"1234567890"), // StrictFill.of("l",String.class,"1234567890"), // StrictFill.of("m",String.class,"1234567890") // )); } @Override public void updateFill(MetaObject metaObject) { } }; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/mapper/PerformanceModelMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.fillperformance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.fillperformance.model.PerformanceModel; public interface PerformanceModelMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/model/PerformanceModel.java ================================================ package com.baomidou.mybatisplus.test.h2.fillperformance.model; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; @TableName(value = "t_fill_performance") public class PerformanceModel { private Long id; private String a; private String b; @TableField(fill = FieldFill.INSERT_UPDATE) private String c; @TableField(fill = FieldFill.INSERT_UPDATE) private String d; @TableField(fill = FieldFill.INSERT_UPDATE) private String e; @TableField(fill = FieldFill.INSERT_UPDATE) private String f; @TableField(fill = FieldFill.INSERT_UPDATE) private String g; @TableField(fill = FieldFill.INSERT_UPDATE) private String h; @TableField(fill = FieldFill.INSERT_UPDATE) private String i; @TableField(fill = FieldFill.INSERT_UPDATE) private String j; private String k; @TableField(fill = FieldFill.INSERT_UPDATE) private String l; @TableField(fill = FieldFill.INSERT_UPDATE) private String m; private String n; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/service/IPerformanceModelService.java ================================================ package com.baomidou.mybatisplus.test.h2.fillperformance.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.h2.fillperformance.model.PerformanceModel; public interface IPerformanceModelService extends IService { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/service/impl/PerformanceModelServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.h2.fillperformance.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.h2.fillperformance.mapper.PerformanceModelMapper; import com.baomidou.mybatisplus.test.h2.fillperformance.model.PerformanceModel; import com.baomidou.mybatisplus.test.h2.fillperformance.service.IPerformanceModelService; import org.springframework.stereotype.Service; @Service public class PerformanceModelServiceImpl extends ServiceImpl implements IPerformanceModelService { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/CustomIdGenerator.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import org.apache.ibatis.reflection.SystemMetaObject; public class CustomIdGenerator implements IdentifierGenerator { @Override public Number nextId(Object entity) { //可以将当前传入的class全类名来作为bizKey,或者提取参数来生成bizKey进行分布式Id调用生成. String bizKey = entity.getClass().getName(); TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass()); String name = (String) SystemMetaObject.forObject(entity).getValue("name"); //long test if (tableInfo.getKeyType() == Long.class) { if ("旺仔".equals(name)) { return 666L; } else if ("靓仔".equals(name)) { return 777L; } return 1L; } else { // int test if ("旺仔".equals(name)) { return 666; } else if ("靓仔".equals(name)) { return 777; } return 1; } } @Override public String nextUUID(Object entity) { String name = (String) SystemMetaObject.forObject(entity).getValue("name"); if ("旺仔".equals(name)) { return "66666666666"; } else if ("靓仔".equals(name)) { return "77777777777"; } return IdWorker.get32UUID(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/IdGeneratorConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.idgenerator.mapper") public class IdGeneratorConfig { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource, GlobalConfig globalConfig) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultExecutorType(ExecutorType.REUSE); configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); sqlSessionFactory.setConfiguration(configuration); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); sqlSessionFactory.setGlobalConfig(globalConfig); return sqlSessionFactory.getObject(); } @Bean public GlobalConfig globalConfiguration() { GlobalConfig conf = new GlobalConfig(); //自定义Id生成器 conf.setIdentifierGenerator(new CustomIdGenerator()); return conf; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/IdentifierGeneratorTest.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator; import com.baomidou.mybatisplus.test.h2.idgenerator.mapper.*; import com.baomidou.mybatisplus.test.h2.idgenerator.model.*; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:h2/spring-id-generator-h2.xml"}) class IdentifierGeneratorTest { @Autowired private LongIdGeneratorMapper longIdGeneratorMapper; @Autowired private IntegerIdGeneratorMapper integerIdGeneratorMapper; @Autowired private StringIdGeneratorMapper stringIdGeneratorMapper; @Autowired private LongStringIdGeneratorMapper longStringIdGeneratorMapper; @Autowired private IntegerStringIdGeneratorMapper integerStringIdGeneratorMapper; @Autowired private BigDecimalIdGeneratorMapper bigDecimalIdGeneratorMapper; @Autowired private BigIntegerIdGeneratorMapper bigIntegerIdGeneratorMapper; @Test void test() { LongIdGeneratorModel longIdGeneratorModel1 = new LongIdGeneratorModel("旺仔"); longIdGeneratorMapper.insert(longIdGeneratorModel1); Assertions.assertEquals(666L, longIdGeneratorModel1.getId()); LongIdGeneratorModel longIdGeneratorModel2 = new LongIdGeneratorModel("靓仔"); longIdGeneratorMapper.insert(longIdGeneratorModel2); Assertions.assertEquals(777L, longIdGeneratorModel2.getId()); IntegerIdGeneratorModel integerIdGeneratorModel1 = new IntegerIdGeneratorModel("旺仔"); integerIdGeneratorMapper.insert(integerIdGeneratorModel1); Assertions.assertEquals(666, integerIdGeneratorModel1.getId()); IntegerIdGeneratorModel integerIdGeneratorModel2 = new IntegerIdGeneratorModel("靓仔"); integerIdGeneratorMapper.insert(integerIdGeneratorModel2); Assertions.assertEquals(777, integerIdGeneratorModel2.getId()); StringIdGeneratorModel stringIdGeneratorModel1 = new StringIdGeneratorModel("旺仔"); stringIdGeneratorMapper.insert(stringIdGeneratorModel1); Assertions.assertEquals("66666666666", stringIdGeneratorModel1.getId()); StringIdGeneratorModel stringIdGeneratorModel2 = new StringIdGeneratorModel("靓仔"); stringIdGeneratorMapper.insert(stringIdGeneratorModel2); Assertions.assertEquals("77777777777", stringIdGeneratorModel2.getId()); LongStringIdGeneratorModel longStringIdGeneratorModel1 = new LongStringIdGeneratorModel("旺仔"); longStringIdGeneratorMapper.insert(longStringIdGeneratorModel1); Assertions.assertEquals("666", longStringIdGeneratorModel1.getId()); LongStringIdGeneratorModel longStringIdGeneratorModel2 = new LongStringIdGeneratorModel("靓仔"); longStringIdGeneratorMapper.insert(longStringIdGeneratorModel2); Assertions.assertEquals("777", longStringIdGeneratorModel2.getId()); IntegerStringIdGeneratorModel integerStringIdGeneratorModel1 = new IntegerStringIdGeneratorModel("旺仔"); integerStringIdGeneratorMapper.insert(integerStringIdGeneratorModel1); Assertions.assertEquals("666", integerStringIdGeneratorModel1.getId()); IntegerStringIdGeneratorModel integerStringIdGeneratorModel2 = new IntegerStringIdGeneratorModel("靓仔"); integerStringIdGeneratorMapper.insert(integerStringIdGeneratorModel2); Assertions.assertEquals("777", integerStringIdGeneratorModel2.getId()); BigDecimalIdGeneratorModel bigDecimalIdGeneratorModel = new BigDecimalIdGeneratorModel("测试"); bigDecimalIdGeneratorMapper.insert(bigDecimalIdGeneratorModel); bigDecimalIdGeneratorMapper.deleteById(bigDecimalIdGeneratorModel); BigIntegerIdGeneratorModel bigIntegerIdGeneratorModel = new BigIntegerIdGeneratorModel("测试"); bigIntegerIdGeneratorMapper.insert(bigIntegerIdGeneratorModel); bigIntegerIdGeneratorMapper.deleteById(bigIntegerIdGeneratorModel); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/BigDecimalIdGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.idgenerator.model.BigDecimalIdGeneratorModel; public interface BigDecimalIdGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/BigIntegerIdGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.idgenerator.model.BigIntegerIdGeneratorModel; public interface BigIntegerIdGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/IntegerIdGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.idgenerator.model.IntegerIdGeneratorModel; public interface IntegerIdGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/IntegerStringIdGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.idgenerator.model.IntegerStringIdGeneratorModel; public interface IntegerStringIdGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/LongIdGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.idgenerator.model.LongIdGeneratorModel; public interface LongIdGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/LongStringIdGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.idgenerator.model.LongStringIdGeneratorModel; public interface LongStringIdGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/StringIdGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.idgenerator.model.StringIdGeneratorModel; public interface StringIdGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/BigDecimalIdGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigDecimal; @Data @NoArgsConstructor @TableName(value = "t_id_generator_bigdecimal") public class BigDecimalIdGeneratorModel { private BigDecimal id; private String name; public BigDecimalIdGeneratorModel(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/BigIntegerIdGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigInteger; @Data @NoArgsConstructor @TableName(value = "t_id_generator_biginteger") public class BigIntegerIdGeneratorModel { private BigInteger id; private String name; public BigIntegerIdGeneratorModel(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/IntegerIdGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @TableName(value = "t_id_generator_int") public class IntegerIdGeneratorModel { private Integer id; private String name; public IntegerIdGeneratorModel(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/IntegerStringIdGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @TableName(value = "t_id_generator_int_string") public class IntegerStringIdGeneratorModel { @TableId(type = IdType.ASSIGN_ID) private String id; private String name; public IntegerStringIdGeneratorModel(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/LongIdGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @TableName(value = "t_id_generator_long") public class LongIdGeneratorModel { private Long id; private String name; public LongIdGeneratorModel(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/LongStringIdGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @TableName(value = "t_id_generator_long_string") public class LongStringIdGeneratorModel { @TableId(type = IdType.ASSIGN_ID) private String id; private String name; public LongStringIdGeneratorModel(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/StringIdGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.idgenerator.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @TableName(value = "t_id_generator_string") public class StringIdGeneratorModel { @TableId(type = IdType.ASSIGN_UUID) private String id; private String name; public StringIdGeneratorModel(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/AopConfig1.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** * @author nieqiurong */ @Aspect @EnableAspectJAutoProxy public class AopConfig1 { private static final Logger LOGGER = LoggerFactory.getLogger(AopConfig1.class); @Pointcut("execution(* com.baomidou.mybatisplus.test.h2.issues.aop.mapper..*.*(..))") public void point() { } @Before("point()") public void before() { LOGGER.info("before ..."); } @After("point()") public void after() { LOGGER.info("After ..."); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/AopConfig2.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author nieqiurong */ @Aspect public class AopConfig2 { private static final Logger LOGGER = LoggerFactory.getLogger(AopConfig2.class); @Pointcut("execution(* com.baomidou.mybatisplus.test.h2.issues.aop.mapper..*.*(..))") public void point() { } @Before("point()") public void before() { LOGGER.info("before ..."); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/AppConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.SqlSessionFactory; import org.h2.Driver; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import javax.sql.DataSource; /** * @author nieqiurong */ @Configuration @ComponentScan("com.baomidou.mybatisplus.test.h2.issues.aop") @MapperScan("com.baomidou.mybatisplus.test.h2.issues.aop.mapper") public class AppConfig { @Bean public DataSource dataSource() { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new Driver()); dataSource.setUrl("jdbc:h2:mem:testa;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); mybatisSqlSessionFactoryBean.setDataSource(dataSource); mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration()); return mybatisSqlSessionFactoryBean.getObject(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/MultiAopTest.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop; import com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo; import com.baomidou.mybatisplus.test.h2.issues.aop.service.IDemoService; import org.apache.ibatis.jdbc.SqlRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; import java.sql.SQLException; import java.util.List; /** * @author nieqiurong */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {AppConfig.class, AopConfig1.class, AopConfig2.class}) public class MultiAopTest { @Autowired private IDemoService demoService; @Autowired private DataSource dataSource; @Test void test() throws SQLException { new SqlRunner(dataSource.getConnection()).run( """ CREATE TABLE IF NOT EXISTS demo ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); """ ); demoService.save(new Demo()); demoService.saveBatch(List.of(new Demo())); demoService.getBaseMapper().insert(List.of(new Demo())); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/NoAopTest.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop; import com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo; import com.baomidou.mybatisplus.test.h2.issues.aop.service.IDemoService; import org.apache.ibatis.jdbc.SqlRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; import java.sql.SQLException; import java.util.List; /** * @author nieqiurong */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {AppConfig.class}) public class NoAopTest { @Autowired private IDemoService demoService; @Autowired private DataSource dataSource; @Test void test() throws SQLException { new SqlRunner(dataSource.getConnection()).run( """ CREATE TABLE IF NOT EXISTS demo ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); """ ); demoService.save(new Demo()); demoService.saveBatch(List.of(new Demo())); demoService.getBaseMapper().insert(List.of(new Demo())); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/SingleAopTest.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop; import com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo; import com.baomidou.mybatisplus.test.h2.issues.aop.service.IDemoService; import org.apache.ibatis.jdbc.SqlRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; import java.sql.SQLException; import java.util.List; /** * @author nieqiurong */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {AppConfig.class, AopConfig1.class}) public class SingleAopTest { @Autowired private IDemoService demoService; @Autowired private DataSource dataSource; @Test void test() throws SQLException { new SqlRunner(dataSource.getConnection()).run( """ CREATE TABLE IF NOT EXISTS demo ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); """ ); demoService.save(new Demo()); demoService.saveBatch(List.of(new Demo())); demoService.getBaseMapper().insert(List.of(new Demo())); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/entity/Demo.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop.entity; import lombok.Data; /** * @author nieqiurong */ @Data public class Demo { private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/mapper/DemoMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo; import org.apache.ibatis.annotations.Mapper; /** * @author nieqiurong */ @Mapper public interface DemoMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/service/IDemoService.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo; /** * @author nieqiurong */ public interface IDemoService extends IService { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/service/impl/DemoServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.aop.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo; import com.baomidou.mybatisplus.test.h2.issues.aop.mapper.DemoMapper; import com.baomidou.mybatisplus.test.h2.issues.aop.service.IDemoService; import org.springframework.stereotype.Service; /** * @author nieqiurong */ @Service public class DemoServiceImpl extends ServiceImpl implements IDemoService { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/GenericIdTest.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.genericid; import com.baomidou.mybatisplus.test.h2.issues.genericid.entity.LongEntity; import com.baomidou.mybatisplus.test.h2.issues.genericid.entity.StringEntity; import com.baomidou.mybatisplus.test.h2.issues.genericid.mapper.LongEntityMapper; import com.baomidou.mybatisplus.test.h2.issues.genericid.mapper.StringEntityMapper; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; /** * 泛型主键问题 * https://gitee.com/baomidou/mybatis-plus/issues/I171CQ * * @author nieqiuqiu * @date 2019-12-20 */ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:issues/genericid/spring.xml"}) class GenericIdTest { @Autowired private LongEntityMapper longEntityMapper; @Autowired private StringEntityMapper stringEntityMapper; @Test void test() { longEntityMapper.insert(new LongEntity("testLong")); stringEntityMapper.insert(new StringEntity("testString")); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/MybatisPlusConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.genericid; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; /** * config * * @author nieqiuqiu * @date 2019-12-20 */ @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.issues.genericid.mapper") public class MybatisPlusConfig { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultExecutorType(ExecutorType.REUSE); configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); sqlSessionFactory.setConfiguration(configuration); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); return sqlSessionFactory.getObject(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/entity/LongEntity.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.genericid.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor @TableName("t_i171cq_long") @EqualsAndHashCode(callSuper = true) public class LongEntity extends SuperEntity { private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/entity/StringEntity.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.genericid.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor @TableName("t_i171cq_string") @EqualsAndHashCode(callSuper = true) public class StringEntity extends SuperEntity { private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/entity/SuperEntity.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.genericid.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.io.Serializable; @Data class SuperEntity { @TableId(type = IdType.ASSIGN_ID) private ID id; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/mapper/LongEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.genericid.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.issues.genericid.entity.LongEntity; /** * long mapper * * @author nieqiuqiu * @date 2019-12-20 */ public interface LongEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/mapper/StringEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.genericid.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.issues.genericid.entity.StringEntity; /** * string mapper * * @author nieqiuqiu * @date 2019-12-20 */ public interface StringEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/AppConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.repositoryscan; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.SqlSessionFactory; import org.h2.Driver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import javax.sql.DataSource; /** * @author nieqiurong */ @Configuration @ComponentScan("com.baomidou.mybatisplus.test.h2.issues.repositoryscan") public class AppConfig { @Bean public DataSource dataSource() { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new Driver()); dataSource.setUrl("jdbc:h2:mem:testa;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); mybatisSqlSessionFactoryBean.setDataSource(dataSource); mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration()); return mybatisSqlSessionFactoryBean.getObject(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/AppConfigWithMapperScan.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.repositoryscan; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.SqlSessionFactory; import org.h2.Driver; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.stereotype.Repository; import javax.sql.DataSource; /** * @author nieqiurong */ @Configuration @ComponentScan("com.baomidou.mybatisplus.test.h2.issues.repositoryscan") @MapperScan(value = "com.baomidou.mybatisplus.test.h2.issues.repositoryscan.mapper", annotationClass = Repository.class) public class AppConfigWithMapperScan { @Bean public DataSource dataSource() { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new Driver()); dataSource.setUrl("jdbc:h2:mem:testa;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); mybatisSqlSessionFactoryBean.setDataSource(dataSource); mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration()); return mybatisSqlSessionFactoryBean.getObject(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/RepositoryDefaultScanTest.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.repositoryscan; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service.IDemoRepositoryService; import org.apache.ibatis.jdbc.SqlRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; import java.sql.SQLException; import java.util.List; /** * @author nieqiurong */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {AppConfig.class}) public class RepositoryDefaultScanTest { @Autowired private IDemoRepositoryService demoService; @Autowired private DataSource dataSource; @Test void test() throws SQLException { new SqlRunner(dataSource.getConnection()).run( """ CREATE TABLE IF NOT EXISTS demo ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); """ ); demoService.save(new Demo()); demoService.saveBatch(List.of(new Demo())); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/RepositoryMapperScanTest.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.repositoryscan; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service.IDemoRepositoryService; import org.apache.ibatis.jdbc.SqlRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; import java.sql.SQLException; import java.util.List; /** * @author nieqiurong */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {AppConfigWithMapperScan.class}) public class RepositoryMapperScanTest { @Autowired private IDemoRepositoryService demoService; @Autowired private DataSource dataSource; @Test void test() throws SQLException { new SqlRunner(dataSource.getConnection()).run( """ CREATE TABLE IF NOT EXISTS demo ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); """ ); demoService.save(new Demo()); demoService.saveBatch(List.of(new Demo())); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/entity/Demo.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity; import lombok.Data; /** * @author nieqiurong */ @Data public class Demo { private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/mapper/DemoRepositoryMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.repositoryscan.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo; import org.springframework.stereotype.Repository; /** * @author nieqiurong */ @Repository public interface DemoRepositoryMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/service/IDemoRepositoryService.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo; /** * @author nieqiurong */ public interface IDemoRepositoryService extends IService { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/service/impl/DemoRepositoryServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.mapper.DemoRepositoryMapper; import com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service.IDemoRepositoryService; import org.springframework.stereotype.Service; /** * @author nieqiurong */ @Service public class DemoRepositoryServiceImpl extends ServiceImpl implements IDemoRepositoryService { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/KeyGeneratorConfig.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.EnumOrdinalTypeHandler; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.Arrays; @Configuration @MapperScan("com.baomidou.mybatisplus.test.h2.keygenerator.mapper") public class KeyGeneratorConfig { @Bean("mybatisSqlSession") public SqlSessionFactory sqlSessionFactory(DataSource dataSource, GlobalConfig globalConfig) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultExecutorType(ExecutorType.REUSE); configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); sqlSessionFactory.setConfiguration(configuration); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); sqlSessionFactory.setGlobalConfig(globalConfig); return sqlSessionFactory.getObject(); } @Bean public GlobalConfig globalConfiguration() { GlobalConfig conf = new GlobalConfig(); conf.setDbConfig(new GlobalConfig.DbConfig().setKeyGenerators(Arrays.asList( new H2KeyGenerator(), new IKeyGenerator() { @Override public String executeSql(String incrementerName) { return "select nextval('" + incrementerName + "')"; } @Override public DbType dbType() { return DbType.POSTGRE_SQL; } } ))); return conf; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/ExtendKeyGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.keygenerator.model.ExtendKeyGeneratorModel; public interface ExtendKeyGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/IntegerKeyGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.keygenerator.model.IntegerKeyGeneratorModel; public interface IntegerKeyGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/KeyGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.keygenerator.model.KeyGeneratorModel; public interface KeyGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/LongKeyGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.keygenerator.model.LongKeyGeneratorModel; public interface LongKeyGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/StringKeyGeneratorMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.keygenerator.model.StringKeyGeneratorModel; public interface StringKeyGeneratorMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/BaseMode.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.model; import com.baomidou.mybatisplus.annotation.KeySequence; @KeySequence(value = "key_generator_model_seq") class BaseMode { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/ExtendKeyGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @Data @TableName(value = "key_generator_model") @EqualsAndHashCode(callSuper = true) public class ExtendKeyGeneratorModel extends BaseMode { @TableId(type = IdType.INPUT, value = "id") private Long uid; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/IntegerKeyGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName(value = "key_generator_model") @KeySequence(value = "key_generator_model_seq") public class IntegerKeyGeneratorModel { @TableId(type = IdType.INPUT, value = "id") private Integer uid; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/KeyGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName(value = "key_generator_model") @KeySequence(value = "key_generator_model_seq") public class KeyGeneratorModel { @TableId(type = IdType.INPUT, value = "id") private Long uid; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/LongKeyGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.model; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; @Data @TableName(value = "key_generator_model") @KeySequence(value = "key_generator_model_seq", dbType = DbType.POSTGRE_SQL) public class LongKeyGeneratorModel { @TableId(type = IdType.INPUT) private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/StringKeyGeneratorModel.java ================================================ package com.baomidou.mybatisplus.test.h2.keygenerator.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName(value = "key_generator_model") @KeySequence(value = "key_generator_model_seq") public class StringKeyGeneratorModel { @TableId(type = IdType.INPUT) private String id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2StudentMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.mapper; import com.baomidou.mybatisplus.test.h2.entity.H2Student; import com.baomidou.mybatisplus.test.h2.entity.H2User; import org.apache.ibatis.annotations.Param; import java.util.Collection; import java.util.List; import java.util.Map; /** * 学生Mapper层 * @author nieqiurong 2018/7/27. */ public interface H2StudentMapper extends SuperMapper { long insertFillByCustomMethod1(H2User h2User); long insertFillByCustomMethod2(@Param("et") H2User h2User); long insertFillByCustomMethod3(@Param("et") H2User h2User, @Param("test") String test); long insertFillByCustomMethod4(Collection h2User); long insertFillByCustomMethod5(@Param("collection") Collection h2User); long insertFillByCustomMethod6(@Param("coll") Collection h2User); long insertFillByCustomMethod7(@Param("list") List h2User); long insertFillByCustomMethod8(H2User[] h2Users); long insertFillByCustomMethod9(@Param("array") H2User[] h2Users); long insertFillByCustomMethod10(Map paramMap); long insertFillByCustomMethod11(Map paramMap); long insertFillByCustomMethod12(Map paramMap); long insertFillByCustomMethod13(Map paramMap); long updateFillByCustomMethod1(Map paramMap); long updateFillByCustomMethod2(@Param("coll") Collection ids, @Param("et") H2User h2User); long updateFillByCustomMethod3(@Param("coll") Collection ids, @Param("user") H2User h2User); long updateFillByCustomMethod4(@Param("colls") Collection ids, @Param("et") H2User h2User); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2UserLogicDeleteMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.mapper; import com.baomidou.mybatisplus.test.h2.entity.H2UserLogicDelete; /** * 这里继承自定义父类 SuperMapper * * @author Caratacus * @since 2017/4/1 */ public interface H2UserLogicDeleteMapper extends SuperMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2UserMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.mapper; import java.util.List; import java.util.Map; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.apache.ibatis.mapping.StatementType; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.test.h2.entity.H2Addr; import com.baomidou.mybatisplus.test.h2.entity.H2User; /** * 这里继承自定义父类 SuperMapper * * @author Caratacus * @since 2017/4/1 */ public interface H2UserMapper extends SuperMapper { @Select( "select a.addr_id as addrId, a.addr_name as addrName from h2address a" + " join h2user u on u.test_id=a.test_id and u.test_id=#{userId}" ) List getAddrListByUserId(@Param("userId") Long userId); @Select( "select a.addr_id as addrId, a.addr_name as addrName from h2address a" + " join h2user u on u.test_id=a.test_id and u.test_id=#{userId}" ) List getAddrListByUserIdPage(@Param("userId") Long userId, IPage page); @Insert( "insert into h2user(name,version) values(#{name},#{version})" ) int myInsertWithNameVersion(@Param("name") String name, @Param("version") int version); @Update( "update h2user set version=version+1, name=#{name} where test_id=#{id} and test_type=1" ) int myUpdateWithNameId(@Param("id") Long id, @Param("name") String name); @Insert( "insert into h2user(name,version) values( #{user1.name}, #{user1.version})" ) int myInsertWithParam(@Param("user1") H2User user1); @Insert( "insert into h2user(name,version) values( #{name}, #{version})" ) int myInsertWithoutParam(H2User user1); @Select(" select test_id as testId, power(#{ageFrom},2), 'abc?zhazha', CAST(#{nameParam} AS VARCHAR) as name " + " from h2user " + " where age>#{ageFrom} and age<#{ageTo} ") List selectUserWithParamInSelectStatememt(Map param); @Select("select * from h2user ${ew.customSqlSegment}") List selectTestCustomSqlSegment(@Param(Constants.WRAPPER) Wrapper wrapper); @Select("select count(1) from (" + "select test_id as id, CAST(#{nameParam} AS VARCHAR) as name" + " from h2user " + " where age>#{ageFrom} and age<#{ageTo} " + ") a") int selectCountWithParamInSelectItems(Map param); @Select("select age,name,count(age) from h2user group by age,name order by age") List> mySelectMaps(IPage page); @Select("call 1") @Options(statementType = StatementType.CALLABLE) String testCall(); @Select("select * from h2user") IPage testPage1(@Param(value = "user") H2User h2User, @Param(value = "page") Page page); @Select("select * from h2user") IPage testPage2(@Param(value = "user") Page page, @Param(value = "page") H2User h2User); @Select("select count(*) from h2user") Long selectCountLong(); @Select("select * from h2user where test_id = #{id}") H2User getById(@Param("id") Long id); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2UserStrategyMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.mapper; import com.baomidou.mybatisplus.test.h2.entity.H2UserStrategy; /** * 这里继承自定义父类 SuperMapper * * @author Caratacus * @since 2017/4/1 */ public interface H2UserStrategyMapper extends SuperMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/SuperMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.mapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 自定义父类 SuperMapper * * @author hubin * @since 2017-06-26 */ public interface SuperMapper extends com.baomidou.mybatisplus.core.mapper.BaseMapper { /** * 这里注入自定义的公共方法 */ int alwaysUpdateSomeColumnById(@Param("et") T entity); int deleteByIdWithFill(T entity); int insertBatchSomeColumn(List entityList); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/IH2StudentService.java ================================================ package com.baomidou.mybatisplus.test.h2.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.h2.entity.H2Student; /** * Created by nieqiuqiu on 2018/9/6. */ public interface IH2StudentService extends IService { void testTransactional(); void testSqlRunnerTransactional(); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/IH2UserService.java ================================================ package com.baomidou.mybatisplus.test.h2.service; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.h2.entity.H2User; import java.util.List; import java.util.Map; /** * Service层测试 * * @author hubin * @since 2017-01-30 */ public interface IH2UserService extends IService { int myInsert(String name, int version); int myInsertWithParam(String name, int version); int myInsertWithoutParam(String name, int version); int myUpdate(Long id, String name); List queryWithParamInSelectStatememt(Map param); IPage queryWithParamInSelectStatememt4Page(Map param, IPage page); int selectCountWithParamInSelectItems(Map param); List> mySelectMaps(); void testBatchTransactional(); void testSimpleTransactional(); void testSaveOrUpdateBatchTransactional(); void testSimpleAndBatchTransactional(); void testSaveBatchNoTransactional1(); void testSaveBatchNoTransactional2(); List testCustomSqlSegment(Wrapper wrapper); void testSaveOrUpdateTransactional1(List users); void testSaveOrUpdateTransactional2(List users); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/impl/H2StudentServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.h2.service.impl; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.toolkit.SqlRunner; import com.baomidou.mybatisplus.test.h2.entity.H2Student; import com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper; import com.baomidou.mybatisplus.test.h2.service.IH2StudentService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * Created by Administrator on 2018/9/6. */ @Service public class H2StudentServiceImpl extends ServiceImpl implements IH2StudentService { @Override @Transactional(rollbackFor = RuntimeException.class) public void testTransactional() { new H2Student(null, "tx1", 2).insert(); new H2Student(null, "tx2", 2).insert(); new H2Student(null, "tx3", 2).insert(); throw new MybatisPlusException("测试AR事务回滚"); } @Override @Transactional(rollbackFor = RuntimeException.class) public void testSqlRunnerTransactional() { SqlRunner.db().insert("INSERT INTO h2student ( name, age ) VALUES ( {0}, {1} )","sqlRunnerTx1",2); SqlRunner.db().insert("INSERT INTO h2student ( name, age ) VALUES ( {0}, {1} )","sqlRunnerTx2",2); throw new MybatisPlusException("测试普通插入事务回滚"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/impl/H2UserServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.h2.service.impl; import com.baomidou.mybatisplus.core.batch.MybatisBatch; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.h2.entity.H2User; import com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper; import com.baomidou.mybatisplus.test.h2.service.IH2UserService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.List; import java.util.Map; /** * Service层测试 * * @author hubin * @since 2017-01-30 */ @Service public class H2UserServiceImpl extends ServiceImpl implements IH2UserService { @Override public int myInsert(String name, int version) { return baseMapper.myInsertWithNameVersion(name, version); } @Override public int myInsertWithParam(String name, int version) { H2User user = new H2User(); user.setName(name); user.setVersion(version); return baseMapper.myInsertWithParam(user); } @Override public int myInsertWithoutParam(String name, int version) { H2User user = new H2User(); user.setName(name); user.setVersion(version); return baseMapper.myInsertWithoutParam(user); } @Override public int myUpdate(Long id, String name) { return baseMapper.myUpdateWithNameId(id, name); } @Override public List queryWithParamInSelectStatememt(Map param) { return baseMapper.selectUserWithParamInSelectStatememt(param); } @Override public IPage queryWithParamInSelectStatememt4Page(Map param, IPage page) { //page.setSearchCount(true); // userMapper.selectUserWithParamInSelectStatememt4Page(param, page); return page; } @Override public int selectCountWithParamInSelectItems(Map param) { return baseMapper.selectCountWithParamInSelectItems(param); } @Override public List> mySelectMaps() { Page page = new Page<>(1,3); page.addOrder(OrderItem.asc("name")); return baseMapper.mySelectMaps(page); } @Override @Transactional(rollbackFor = RuntimeException.class) public void testBatchTransactional() { saveBatch(Arrays.asList(new H2User("batch1", 0), new H2User("batch2", 0), new H2User("batch3", 0))); saveBatch(Arrays.asList(new H2User("batch4", 0), new H2User("batch5", 0), new H2User("batch6", 0))); throw new MybatisPlusException("测试批量插入事务回滚"); } @Override @Transactional(rollbackFor = RuntimeException.class) public void testSimpleTransactional() { save(new H2User("simple1", 0)); save(new H2User("simple2", 0)); throw new MybatisPlusException("测试普通插入事务回滚"); } @Override @Transactional(rollbackFor = RuntimeException.class) public void testSaveOrUpdateBatchTransactional() { saveOrUpdateBatch(Arrays.asList(new H2User("savOrUpdate1", 0), new H2User("savOrUpdate2", 0), new H2User("savOrUpdate3", 0)), 1); saveOrUpdateBatch(Arrays.asList(new H2User("savOrUpdate4", 0), new H2User("savOrUpdate5", 0), new H2User("savOrUpdate6", 0)), 1); throw new MybatisPlusException("测试普通插入事务回滚"); } @Override @Transactional(rollbackFor = RuntimeException.class) public void testSimpleAndBatchTransactional() { save(new H2User("simpleAndBatchTx1", 0)); saveBatch(Arrays.asList(new H2User("simpleAndBatchTx2", 0), new H2User("simpleAndBatchTx3", 0), new H2User("simpleAndBatchTx4", 0)), 1); saveOrUpdateBatch(Arrays.asList(new H2User("simpleAndBatchTx5", 0), new H2User("simpleAndBatchTx6", 0), new H2User("simpleAndBatchTx7", 0)), 1); throw new MybatisPlusException("测试事务回滚"); } @Override public void testSaveBatchNoTransactional1() { saveBatch(Arrays.asList(new H2User("testSaveBatchNoTransactional1", 0), new H2User("testSaveBatchNoTransactional1", 0), new H2User("testSaveBatchNoTransactional1", 0)), 1); } @Override public void testSaveBatchNoTransactional2() { //非事物下,制造一个批量主键冲突 save(new H2User(1577431655447L, "testSaveBatchNoTransactional2")); saveBatch(Arrays.asList(new H2User("testSaveBatchNoTransactional2", 0), new H2User("testSaveBatchNoTransactional2", 0), new H2User(1577431655447L, "testSaveBatchNoTransactional2")), 1); } @Override public List testCustomSqlSegment(Wrapper wrapper) { return baseMapper.selectTestCustomSqlSegment(wrapper); } @Override @Transactional(rollbackFor = RuntimeException.class) public void testSaveOrUpdateTransactional1(List users) { var method = new MybatisBatch.Method(H2UserMapper.class); MybatisBatchUtils.saveOrUpdate(getSqlSessionFactory(), users, method.insert(), (sqlSession, user) -> this.getById(user.getTestId()) == null, method.updateById()); } @Override @Transactional(rollbackFor = RuntimeException.class) public void testSaveOrUpdateTransactional2(List users) { var method = new MybatisBatch.Method(H2UserMapper.class); MybatisBatchUtils.saveOrUpdate(getSqlSessionFactory(), users, method.insert(), (sqlSession, user) -> sqlSession.selectList(method.get("selectById").getStatementId(), user.getTestId()).isEmpty(), method.updateById()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/CustomCacheExecutor.java ================================================ //package com.baomidou.mybatisplus.test.h2.tenant; // //import com.baomidou.mybatisplus.core.executor.MybatisCachingExecutor; //import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler; //import org.apache.ibatis.cache.CacheKey; //import org.apache.ibatis.executor.Executor; //import org.apache.ibatis.mapping.BoundSql; //import org.apache.ibatis.mapping.MappedStatement; //import org.apache.ibatis.mapping.SqlCommandType; //import org.apache.ibatis.session.RowBounds; // //import java.util.Objects; // // ///** // * @author nieqiuqiu 2020/6/15 // */ //public class CustomCacheExecutor extends MybatisCachingExecutor { // // private TenantHandler tenantHandler; // // public CustomCacheExecutor(Executor delegate, TenantHandler tenantHandler) { // super(delegate); // this.tenantHandler = tenantHandler; // } // // @Override // public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { // if (ms.getSqlCommandType() == SqlCommandType.SELECT) { // CacheKey cacheKey = super.createCacheKey(ms, parameterObject, rowBounds, boundSql); // cacheKey.update(Objects.toString(tenantHandler.getTenantId(true))); // return cacheKey; // } // return super.createCacheKey(ms, parameterObject, rowBounds, boundSql); // } // // @Override // protected CacheKey getCountCacheKey(MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject, RowBounds rowBounds) { // CacheKey countCacheKey = super.getCountCacheKey(mappedStatement, boundSql, parameterObject, rowBounds); // countCacheKey.update(Objects.toString(tenantHandler.getTenantId(true))); // return countCacheKey; // } // //} ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/TenantConfig.java ================================================ //package com.baomidou.mybatisplus.test.h2.tenant; // //import com.baomidou.mybatisplus.core.MybatisConfiguration; //import com.baomidou.mybatisplus.core.executor.MybatisBatchExecutor; //import com.baomidou.mybatisplus.core.executor.MybatisReuseExecutor; //import com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor; //import com.baomidou.mybatisplus.core.parser.ISqlParser; //import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; //import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler; //import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser; //import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; //import net.sf.jsqlparser.expression.Expression; //import net.sf.jsqlparser.expression.LongValue; //import org.apache.ibatis.executor.Executor; //import org.apache.ibatis.session.ExecutorType; //import org.apache.ibatis.session.SqlSessionFactory; //import org.apache.ibatis.transaction.Transaction; //import org.apache.ibatis.type.EnumOrdinalTypeHandler; //import org.apache.ibatis.type.JdbcType; //import org.mybatis.spring.annotation.MapperScan; //import org.springframework.context.annotation.Bean; //import org.springframework.context.annotation.Configuration; // //import javax.sql.DataSource; //import java.util.ArrayList; //import java.util.List; // ///** // * @author nieqiuqiu 2019/12/8 // */ //@Configuration //@MapperScan("com.baomidou.mybatisplus.test.h2.tenant.mapper") //public class TenantConfig { // // //模拟用户切换 // static Long TENANT_ID = 1L; // // @Bean // public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { // MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); // sqlSessionFactory.setDataSource(dataSource); // TenantSqlParser tenantSqlParser = new TenantSqlParser(); // tenantSqlParser.setTenantHandler(new TenantHandler() { // @Override // public Expression getTenantId(boolean select) { // return new LongValue(TENANT_ID); // } // // @Override // public String getTenantIdColumn() { // return "tenant_id"; // } // // @Override // public boolean doTableFilter(String tableName) { // return false; // } // }); // MybatisConfiguration configuration = new MybatisConfiguration() { // @Override // public Executor newExecutor(Transaction transaction, ExecutorType executorType) { // executorType = executorType == null ? defaultExecutorType : executorType; // executorType = executorType == null ? ExecutorType.SIMPLE : executorType; // Executor executor; // if (ExecutorType.BATCH == executorType) { // executor = new MybatisBatchExecutor(this, transaction); // } else if (ExecutorType.REUSE == executorType) { // executor = new MybatisReuseExecutor(this, transaction); // } else { // executor = new MybatisSimpleExecutor(this, transaction); // } // if (cacheEnabled) { // executor = new CustomCacheExecutor(executor, tenantSqlParser.getTenantHandler()); // } // executor = (Executor) interceptorChain.pluginAll(executor); // return executor; // } // }; // configuration.setJdbcTypeForNull(JdbcType.NULL); // configuration.setMapUnderscoreToCamelCase(true); // configuration.setDefaultExecutorType(ExecutorType.REUSE); // configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class); // configuration.setCacheEnabled(true); // sqlSessionFactory.setConfiguration(configuration); // PaginationInterceptor pagination = new PaginationInterceptor(); // List sqlParserList = new ArrayList<>(); // sqlParserList.add(tenantSqlParser); // pagination.setSqlParserList(sqlParserList); // sqlSessionFactory.setPlugins(pagination); // return sqlSessionFactory.getObject(); // } // //} ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/TenantTest.java ================================================ //package com.baomidou.mybatisplus.test.h2.tenant; // //import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; //import com.baomidou.mybatisplus.extension.plugins.pagination.Page; //import com.baomidou.mybatisplus.test.h2.tenant.model.Student; //import com.baomidou.mybatisplus.test.h2.tenant.service.IStudentService; //import org.junit.jupiter.api.Assertions; //import org.junit.jupiter.api.MethodOrderer; //import org.junit.jupiter.api.Test; //import org.junit.jupiter.api.TestMethodOrder; //import org.junit.jupiter.api.extension.ExtendWith; //import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.test.context.ContextConfiguration; //import org.springframework.test.context.junit.jupiter.SpringExtension; // //import java.util.List; // ///** // * @author nieqiuqiu 2019/12/8 // */ //@TestMethodOrder(MethodOrderer.OrderAnnotation.class) //@ExtendWith(SpringExtension.class) //@ContextConfiguration(locations = {"classpath:h2/spring-tenant-h2.xml"}) //class TenantTest { // // @Autowired // private IStudentService studentService; // // @Test // void testSimple() { // TenantConfig.TENANT_ID = 2L; // Student student1 = studentService.getById(1L); // Assertions.assertNull(student1); // student1 = studentService.getById(1L); // Assertions.assertNull(student1); // TenantConfig.TENANT_ID = 1L; // Student student2 = studentService.getById(1L); // Assertions.assertNotNull(student2); // student2 = studentService.getById(1L); // Assertions.assertNotNull(student2); // } // // @Test // void testPage() { // TenantConfig.TENANT_ID = 2L; // Page page1 = studentService.page(new Page<>(0, 10), new QueryWrapper<>()); // Assertions.assertEquals(page1.getTotal(), 1); // page1 = studentService.page(new Page<>(0, 10), new QueryWrapper<>()); // Assertions.assertEquals(page1.getTotal(), 1); // TenantConfig.TENANT_ID = 3L; // Page page2 = studentService.page(new Page<>(0, 10), new QueryWrapper<>()); // Assertions.assertEquals(page2.getTotal(), 0); // page2 = studentService.page(new Page<>(0, 10), new QueryWrapper<>()); // Assertions.assertEquals(page2.getTotal(), 0); // } // // @Test // void testBatch() { // TenantConfig.TENANT_ID = 1L; // List list = studentService.list(); // list.forEach(student -> student.setName("小红")); // studentService.updateBatchById(list); // studentService.list().forEach(student -> Assertions.assertEquals("小红", student.getName())); // } //} ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/mapper/StudentMapper.java ================================================ package com.baomidou.mybatisplus.test.h2.tenant.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.h2.tenant.model.Student; import org.apache.ibatis.annotations.CacheNamespace; /** * @author nieqiuqiu 2019/12/8 */ @CacheNamespace public interface StudentMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/model/Student.java ================================================ package com.baomidou.mybatisplus.test.h2.tenant.model; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * @author nieqiuqiu 2019/12/8 */ @Data @TableName(value = "student") @NoArgsConstructor public class Student implements Serializable { private Long id; private String tenantId; private String name; public Student(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/service/IStudentService.java ================================================ package com.baomidou.mybatisplus.test.h2.tenant.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.h2.tenant.model.Student; public interface IStudentService extends IService { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/service/impl/StudentServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.h2.tenant.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.h2.tenant.mapper.StudentMapper; import com.baomidou.mybatisplus.test.h2.tenant.model.Student; import com.baomidou.mybatisplus.test.h2.tenant.service.IStudentService; import org.springframework.stereotype.Service; @Service public class StudentServiceImpl extends ServiceImpl implements IStudentService { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJson2Entity.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; import java.util.Map; /** * @author nieqiurong 2024年3月4日 */ @Data @NoArgsConstructor @AllArgsConstructor @TableName(value = "t_fastjson2_entity", autoResultMap = true) public class FastJson2Entity { @TableId(type = IdType.INPUT) private String id; private String name; @TableField(typeHandler = Fastjson2TypeHandler.class) private Card card; @TableField(typeHandler = Fastjson2TypeHandler.class) private List attr; @TableField(typeHandler = Fastjson2TypeHandler.class) private Map attr2; @TableField(typeHandler = Fastjson2TypeHandler.class) private Map attr3; @TableField(typeHandler = Fastjson2TypeHandler.class) private Map attr4; @Data @NoArgsConstructor @AllArgsConstructor public static class Attr { private String name; private String value; } @Data @NoArgsConstructor @AllArgsConstructor public static class Card { private String id; private String value; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJson2EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author nieqiurong 2024年3月4日 */ public interface FastJson2EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJsonEntity.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; import java.util.Map; /** * @author nieqiurong 2024年3月4日 */ @Data @NoArgsConstructor @AllArgsConstructor @TableName(value = "t_fastjson_entity", autoResultMap = true) public class FastJsonEntity { @TableId(type = IdType.INPUT) private String id; private String name; @TableField(typeHandler = FastjsonTypeHandler.class) private Card card; @TableField(typeHandler = FastjsonTypeHandler.class) private List attr; @TableField(typeHandler = FastjsonTypeHandler.class) private Map attr2; @TableField(typeHandler = FastjsonTypeHandler.class) private Map attr3; @TableField(typeHandler = FastjsonTypeHandler.class) private Map attr4; @Data @NoArgsConstructor @AllArgsConstructor public static class Attr { private String name; private String value; } @Data @NoArgsConstructor @AllArgsConstructor public static class Card { private String id; private String value; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJsonEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author nieqiurong 2024年3月4日 */ public interface FastJsonEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/Fastjson2Test.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author nieqiurong 2024年3月4日 */ public class Fastjson2Test extends BaseDbTest { @Override protected List tableSql() { return Arrays.asList("drop table if exists t_fastjson2_entity", "CREATE TABLE IF NOT EXISTS t_fastjson2_entity (" + "id VARCHAR(32) NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "card VARCHAR(255) NULL DEFAULT NULL," + "attr VARCHAR(255) NULL DEFAULT NULL," + "attr2 VARCHAR(255) NULL DEFAULT NULL," + "attr3 VARCHAR(255) NULL DEFAULT NULL," + "attr4 VARCHAR(255) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } @Test void test() { doTest(mapper -> { var entity = new FastJson2Entity("123", "秋秋", new FastJson2Entity.Card("1", "1111"), Arrays.asList(new FastJson2Entity.Attr("age", "18"), new FastJson2Entity.Attr("sex", "女")), new HashMap<>(Map.of("test", new FastJson2Entity.Attr("小红", "1"))), new HashMap<>(Map.of("name", "1", "test2", "2")), new HashMap<>(Map.of("test1", "1", "test2", 2)) ); mapper.insert(entity); FastJson2Entity jackJsonEntity = mapper.selectById(entity.getId()); Assertions.assertEquals("秋秋", jackJsonEntity.getName()); Assertions.assertEquals(2, jackJsonEntity.getAttr().size()); Assertions.assertNotNull(jackJsonEntity.getCard()); Assertions.assertEquals(1, jackJsonEntity.getAttr2().size()); Assertions.assertEquals("小红", jackJsonEntity.getAttr2().get("test").getName()); Assertions.assertEquals(2, jackJsonEntity.getAttr3().size()); Assertions.assertEquals("1", jackJsonEntity.getAttr3().get("name")); Assertions.assertEquals("2", jackJsonEntity.getAttr3().get("test2")); Assertions.assertEquals(2, jackJsonEntity.getAttr4().size()); Assertions.assertEquals(2, jackJsonEntity.getAttr4().get("test2")); }); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastjsonTest.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author nieqiurong 2024年3月4日 */ public class FastjsonTest extends BaseDbTest { @Override protected List tableSql() { return Arrays.asList("drop table if exists t_fastjson_entity", "CREATE TABLE IF NOT EXISTS t_fastjson_entity (" + "id VARCHAR(32) NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "card VARCHAR(255) NULL DEFAULT NULL," + "attr VARCHAR(255) NULL DEFAULT NULL," + "attr2 VARCHAR(255) NULL DEFAULT NULL," + "attr3 VARCHAR(255) NULL DEFAULT NULL," + "attr4 VARCHAR(255) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } @Test void test() { doTest(mapper -> { var entity = new FastJsonEntity("123", "秋秋", new FastJsonEntity.Card("1", "1111"), Arrays.asList(new FastJsonEntity.Attr("age", "18"), new FastJsonEntity.Attr("sex", "女")), new HashMap<>(Map.of("test", new FastJsonEntity.Attr("小红", "1"))), new HashMap<>(Map.of("name", "1", "test2", "2")), new HashMap<>(Map.of("test1", "1", "test2", 2)) ); mapper.insert(entity); FastJsonEntity jackJsonEntity = mapper.selectById(entity.getId()); Assertions.assertEquals("秋秋", jackJsonEntity.getName()); Assertions.assertEquals(2, jackJsonEntity.getAttr().size()); Assertions.assertNotNull(jackJsonEntity.getCard()); Assertions.assertEquals(1, jackJsonEntity.getAttr2().size()); Assertions.assertEquals("小红", jackJsonEntity.getAttr2().get("test").getName()); Assertions.assertEquals(2, jackJsonEntity.getAttr3().size()); Assertions.assertEquals("1", jackJsonEntity.getAttr3().get("name")); Assertions.assertEquals("2", jackJsonEntity.getAttr3().get("test2")); Assertions.assertEquals(2, jackJsonEntity.getAttr4().size()); Assertions.assertEquals(2, jackJsonEntity.getAttr4().get("test2")); }); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonEntity.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; import java.util.Map; /** * @author nieqiurong 2024年3月4日 */ @Data @NoArgsConstructor @AllArgsConstructor @TableName(value = "t_gson_entity", autoResultMap = true) public class GsonEntity { @TableId(type = IdType.INPUT) private String id; private String name; @TableField(typeHandler = GsonTypeHandler.class) private Card card; @TableField(typeHandler = GsonTypeHandler.class) private List attr; @TableField(typeHandler = GsonTypeHandler.class) private Map attr2; @TableField(typeHandler = GsonTypeHandler.class) private Map attr3; @TableField(typeHandler = GsonTypeHandler.class) private Map attr4; @Data @NoArgsConstructor @AllArgsConstructor public static class Attr { private String name; private String value; } @Data @NoArgsConstructor @AllArgsConstructor public static class Card { private String id; private String value; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author nieqiurong 2024年3月4日 */ public interface GsonEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonTest.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author nieqiurong 2024年3月4日 */ public class GsonTest extends BaseDbTest { @Override protected List tableSql() { return Arrays.asList("drop table if exists t_gson_entity", "CREATE TABLE IF NOT EXISTS t_gson_entity (" + "id VARCHAR(32) NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "card VARCHAR(255) NULL DEFAULT NULL," + "attr VARCHAR(255) NULL DEFAULT NULL," + "attr2 VARCHAR(255) NULL DEFAULT NULL," + "attr3 VARCHAR(255) NULL DEFAULT NULL," + "attr4 VARCHAR(255) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } @Test void test() { doTest(mapper -> { var entity = new GsonEntity("123", "秋秋", new GsonEntity.Card("1", "1111"), Arrays.asList(new GsonEntity.Attr("age", "18"), new GsonEntity.Attr("sex", "女")), new HashMap<>(Map.of("test", new GsonEntity.Attr("小红", "1"))), new HashMap<>(Map.of("name", "1", "test2", "2")), new HashMap<>(Map.of("test1", "1", "test2", 2)) ); mapper.insert(entity); GsonEntity jackJsonEntity = mapper.selectById(entity.getId()); Assertions.assertEquals("秋秋", jackJsonEntity.getName()); Assertions.assertEquals(2, jackJsonEntity.getAttr().size()); Assertions.assertNotNull(jackJsonEntity.getCard()); Assertions.assertEquals(1, jackJsonEntity.getAttr2().size()); Assertions.assertEquals("小红", jackJsonEntity.getAttr2().get("test").getName()); Assertions.assertEquals(2, jackJsonEntity.getAttr3().size()); Assertions.assertEquals("1", jackJsonEntity.getAttr3().get("name")); Assertions.assertEquals("2", jackJsonEntity.getAttr3().get("test2")); Assertions.assertEquals(2, jackJsonEntity.getAttr4().size()); //TODO 反序列化精度问题 Assertions.assertEquals(2.0, jackJsonEntity.getAttr4().get("test2")); }); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JackJsonEntity.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; import java.util.Map; /** * @author nieqiurong 2024年3月4日 */ @Data @NoArgsConstructor @AllArgsConstructor @TableName(value = "t_jackson_entity", autoResultMap = true) public class JackJsonEntity { @TableId(type = IdType.INPUT) private String id; private String name; @TableField(typeHandler = JacksonTypeHandler.class) private Card card; @TableField(typeHandler = JacksonTypeHandler.class) private List attr; @TableField(typeHandler = JacksonTypeHandler.class) private Map attr2; @TableField(typeHandler = JacksonTypeHandler.class) private Map attr3; @TableField(typeHandler = JacksonTypeHandler.class) private Map attr4; @Data @NoArgsConstructor @AllArgsConstructor public static class Attr { private String name; private String value; } @Data @NoArgsConstructor @AllArgsConstructor public static class Card { private String id; private String value; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JackJsonEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author nieqiurong 2024年3月4日 */ public interface JackJsonEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JacksonTest.java ================================================ package com.baomidou.mybatisplus.test.json; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author nieqiurong 2024年3月4日 */ public class JacksonTest extends BaseDbTest { @Override protected List tableSql() { return Arrays.asList("drop table if exists t_jackson_entity", "CREATE TABLE IF NOT EXISTS t_jackson_entity (" + "id VARCHAR(32) NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "card VARCHAR(255) NULL DEFAULT NULL," + "attr VARCHAR(255) NULL DEFAULT NULL," + "attr2 VARCHAR(255) NULL DEFAULT NULL," + "attr3 VARCHAR(255) NULL DEFAULT NULL," + "attr4 VARCHAR(255) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } @Test void test() { doTest(mapper -> { var entity = new JackJsonEntity("123", "秋秋", new JackJsonEntity.Card("1", "1111"), Arrays.asList(new JackJsonEntity.Attr("age", "18"), new JackJsonEntity.Attr("sex", "女")), new HashMap<>(Map.of("test", new JackJsonEntity.Attr("小红", "1"))), new HashMap<>(Map.of("name", "1", "test2", "2")), new HashMap<>(Map.of("test1", "1", "test2", 2)) ); mapper.insert(entity); JackJsonEntity jackJsonEntity = mapper.selectById(entity.getId()); Assertions.assertEquals("秋秋", jackJsonEntity.getName()); Assertions.assertEquals(2, jackJsonEntity.getAttr().size()); Assertions.assertNotNull(jackJsonEntity.getCard()); Assertions.assertEquals(1, jackJsonEntity.getAttr2().size()); Assertions.assertEquals("小红", jackJsonEntity.getAttr2().get("test").getName()); Assertions.assertEquals(2, jackJsonEntity.getAttr3().size()); Assertions.assertEquals("1", jackJsonEntity.getAttr3().get("name")); Assertions.assertEquals("2", jackJsonEntity.getAttr3().get("test2")); Assertions.assertEquals(2, jackJsonEntity.getAttr4().size()); Assertions.assertEquals(2, jackJsonEntity.getAttr4().get("test2")); }); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/logicdel/Entity.java ================================================ package com.baomidou.mybatisplus.test.logicdel; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; private String name; @TableField(fill = FieldFill.UPDATE) private String deleteBy; @TableField(fill = FieldFill.UPDATE) @TableLogic(delval = "true", value = "false") private Boolean deleted; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/logicdel/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.logicdel; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { @Select("select * from entity where id = #{id}") Entity byId(Long id); int testDeleteBatch(@Param(Constants.COLL) List entityList); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/logicdel/LogicDelTest.java ================================================ package com.baomidou.mybatisplus.test.logicdel; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteBatchByIds; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ public class LogicDelTest extends BaseDbTest { @Test void logicDel() { doTestAutoCommit(i -> { int delete = i.deleteById(1L); assertThat(delete).isEqualTo(1); delete = i.delete(Wrappers.lambdaQuery().eq(Entity::getId, 2)); assertThat(delete).isEqualTo(1); }); doTest(i -> { Entity entity = i.byId(1L); assertThat(entity).isNotNull(); assertThat(entity.getDeleted()).isTrue(); entity = i.byId(2L); assertThat(entity).isNotNull(); assertThat(entity.getDeleted()).isTrue(); }); doTest(mapper -> { Entity entity = new Entity(); entity.setName("测试根据实体删除"); mapper.insert(entity); assertThat(mapper.deleteById(entity)).isEqualTo(1); }); doTest(mapper -> { Entity entity1 = new Entity(); entity1.setName("测试根据实体主键批量删除"); mapper.insert(entity1); Entity entity2 = new Entity(); entity2.setName("测试根据实体主键批量删除"); mapper.insert(entity2); assertThat(mapper.deleteByIds(Arrays.asList(entity1.getId(), entity2.getId()))).isEqualTo(2); }); doTest(mapper -> { Entity entity1 = new Entity(); entity1.setName("测试根据实体批量删除"); mapper.insert(entity1); Entity entity2 = new Entity(); entity2.setName("测试根据实体批量删除"); mapper.insert(entity2); List entityList = new ArrayList<>(); entityList.add(entity1); entityList.add(entity2); assertThat(mapper.deleteByIds(entityList)).isEqualTo(2); entityList.forEach(entity -> { //TODO 3.5.7 修改为使用IN的方式删除而不是使用行记录删除. // Assertions.assertEquals("聂秋秋", entity.getDeleteBy()); }); }); doTest(mapper -> { Entity entity1 = new Entity(); entity1.setName("测试自定义方法根据实体批量删除"); mapper.insert(entity1); Entity entity2 = new Entity(); entity2.setName("测试自定义方法根据实体批量删除"); mapper.insert(entity2); List entityList = new ArrayList<>(); entityList.add(entity1); entityList.add(entity2); assertThat(mapper.testDeleteBatch(entityList)).isEqualTo(2); entityList.forEach(entity -> { Assertions.assertEquals("聂秋秋", entity.getDeleteBy()); }); }); } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1,'1'),(2,'2');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "delete_by VARCHAR(30) NULL DEFAULT NULL," + "deleted BOOLEAN NOT NULL DEFAULT false," + "PRIMARY KEY (id))"); } @Override protected GlobalConfig globalConfig() { GlobalConfig globalConfig = super.globalConfig(); globalConfig.setMetaObjectHandler(new MetaObjectHandler() { @Override public void insertFill(MetaObject metaObject) { } @Override public void updateFill(MetaObject metaObject) { strictUpdateFill(metaObject, "deleteBy", String.class, "聂秋秋"); } }); globalConfig.setSqlInjector(new DefaultSqlInjector() { @Override public List getMethodList(Configuration configuration, Class mapperClass, TableInfo tableInfo) { List methodList = super.getMethodList(configuration, mapperClass, tableInfo); methodList.add(new LogicDeleteBatchByIds("testDeleteBatch")); return methodList; } }); return globalConfig; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/AppConfig.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.SqlSessionFactory; import org.h2.Driver; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScans; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.transaction.support.TransactionTemplate; import javax.sql.DataSource; /** * @author nieqiurong */ @ComponentScan("com.baomidou.mybatisplus.test.multisqlsessionfactory") @MapperScans( { @MapperScan(value = "com.baomidou.mybatisplus.test.multisqlsessionfactory.a.mapper", sqlSessionFactoryRef = "sqlSessionFactory1"), @MapperScan(value = "com.baomidou.mybatisplus.test.multisqlsessionfactory.b.mapper", sqlSessionFactoryRef = "sqlSessionFactory2") } ) @Configuration public class AppConfig { @Bean public DataSource dataSourceA() { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new Driver()); dataSource.setUrl("jdbc:h2:mem:testa;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } @Bean public DataSourceTransactionManager transactionManagerA(DataSource dataSourceA) { return new DataSourceTransactionManager(dataSourceA); } @Bean public TransactionTemplate transactionTemplateA(DataSourceTransactionManager transactionManagerA) { return new TransactionTemplate(transactionManagerA); } @Bean public SqlSessionFactory sqlSessionFactory1(DataSource dataSourceA) throws Exception { MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); mybatisSqlSessionFactoryBean.setDataSource(dataSourceA); mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration()); return mybatisSqlSessionFactoryBean.getObject(); } @Bean public DataSource dataSourceB() { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new Driver()); dataSource.setUrl("jdbc:h2:mem:testb;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } @Bean public DataSourceTransactionManager transactionManagerB(DataSource dataSourceB) { return new DataSourceTransactionManager(dataSourceB); } @Bean public TransactionTemplate transactionTemplateB(DataSourceTransactionManager transactionManagerB) { return new TransactionTemplate(transactionManagerB); } @Bean public SqlSessionFactory sqlSessionFactory2(DataSource dataSourceB) throws Exception { MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); mybatisSqlSessionFactoryBean.setDataSource(dataSourceB); mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration()); return mybatisSqlSessionFactoryBean.getObject(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/MultiSqlSessionFactoryTest.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory; import com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity.AEntity; import com.baomidou.mybatisplus.test.multisqlsessionfactory.a.service.AEntityService; import com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity.BEntity; import com.baomidou.mybatisplus.test.multisqlsessionfactory.b.service.BEntityService; import org.apache.ibatis.jdbc.SqlRunner; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.sql.DataSource; import java.util.List; /** * @author nieqiurong */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {AppConfig.class}) public class MultiSqlSessionFactoryTest { @Autowired private AEntityService aEntityService; @Autowired private BEntityService bEntityService; @Autowired private DataSource dataSourceA; @Autowired private DataSource dataSourceB; @Test void test() throws Exception { new SqlRunner(dataSourceA.getConnection()).run( """ CREATE TABLE IF NOT EXISTS t_entity_a ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); """ ); new SqlRunner(dataSourceB.getConnection()).run( """ CREATE TABLE IF NOT EXISTS t_entity_b ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); """ ); Assertions.assertEquals(0L, aEntityService.count()); Assertions.assertEquals(0L, bEntityService.count()); aEntityService.testSaveBath(List.of(new AEntity("test1"), new AEntity("test2"))); Assertions.assertEquals(2L, aEntityService.count()); bEntityService.testSaveBath(List.of(new BEntity("test1"), new BEntity("test2"), new BEntity("test3"))); Assertions.assertEquals(3L, bEntityService.count()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/a/entity/AEntity.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * @author nieqiurong */ @Data @TableName("t_entity_a") public class AEntity { private Long id; private String name; public AEntity() { } public AEntity(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/a/mapper/AEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory.a.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity.AEntity; import org.apache.ibatis.annotations.Mapper; /** * @author nieqiurong */ @Mapper public interface AEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/a/service/AEntityService.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory.a.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity.AEntity; import java.util.List; /** * @author nieqiurong */ public interface AEntityService extends IService { void testSaveBath(List aEntityList); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/a/service/impl/AEntityServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory.a.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity.AEntity; import com.baomidou.mybatisplus.test.multisqlsessionfactory.a.mapper.AEntityMapper; import com.baomidou.mybatisplus.test.multisqlsessionfactory.a.service.AEntityService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import java.util.List; /** * @author nieqiurong */ @Service public class AEntityServiceImpl extends ServiceImpl implements AEntityService { @Autowired protected TransactionTemplate transactionTemplateA; @Override @Transactional(rollbackFor = RuntimeException.class, transactionManager = "transactionManagerA") public void testSaveBath(List aEntityList) { transactionTemplateA.execute((c) -> { this.saveBatch(aEntityList); return null; }); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/b/entity/BEntity.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * @author nieqiurong */ @Data @TableName("t_entity_b") public class BEntity { private Long id; private String name; public BEntity() { } public BEntity(String name) { this.name = name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/b/mapper/BEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory.b.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity.BEntity; import org.apache.ibatis.annotations.Mapper; /** * @author nieqiurong */ @Mapper public interface BEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/b/service/BEntityService.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory.b.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity.BEntity; import java.util.List; /** * @author nieqiurong */ public interface BEntityService extends IService { void testSaveBath(List bEntityList); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/b/service/impl/BEntityServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.multisqlsessionfactory.b.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity.BEntity; import com.baomidou.mybatisplus.test.multisqlsessionfactory.b.mapper.BEntityMapper; import com.baomidou.mybatisplus.test.multisqlsessionfactory.b.service.BEntityService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import java.util.List; /** * @author nieqiurong */ @Service public class BEntityServiceImpl extends ServiceImpl implements BEntityService { @Autowired private TransactionTemplate transactionTemplateB; @Override @Transactional(rollbackFor = RuntimeException.class, transactionManager = "transactionManagerB") public void testSaveBath(List bEntityList) { transactionTemplateB.execute((c) -> { saveBatch(bEntityList); return null; }); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/non/Entity.java ================================================ package com.baomidou.mybatisplus.test.non; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; @TableId("t_id") private Long id; @TableField("t_name") private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/non/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.non; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Select; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { @Select("select * from entity where id = #{id}") Entity byId(Long id); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/non/NonTest.java ================================================ package com.baomidou.mybatisplus.test.non; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ public class NonTest extends BaseDbTest { @Test void test() { doTest(i -> { Entity entity = i.selectOne(Wrappers.lambdaQuery().eq(Entity::getId, 1)); assertThat(entity).isNotNull(); assertThat(entity.getName()).isNotNull(); entity.setName("老吴"); int update = i.updateById(entity); assertThat(update).isEqualTo(1); }); } @Override protected String tableDataSql() { return "insert into entity(t_id,t_name) values(1,'1'),(2,'2');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "t_id BIGINT NOT NULL," + "t_name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (t_id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/optimisticlocker/Entity.java ================================================ package com.baomidou.mybatisplus.test.optimisticlocker; import com.baomidou.mybatisplus.annotation.Version; import lombok.Data; import lombok.experimental.Accessors; /** * @author miemie * @since 2020-06-24 */ @Data @Accessors(chain = true) public class Entity { private Long id; private String name; @Version private Integer version; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/optimisticlocker/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.optimisticlocker; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-24 */ public interface EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/optimisticlocker/OptimisticLockerTest.java ================================================ package com.baomidou.mybatisplus.test.optimisticlocker; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.plugin.Interceptor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-24 */ public class OptimisticLockerTest extends BaseDbTest { @Test void test() { doTestAutoCommit(m -> { Entity entity = new Entity().setId(1L).setName("老王"); int updateById = m.updateById(entity); assertThat(updateById).as("更新成功").isEqualTo(1); entity = m.selectById(1L); assertThat(entity.getVersion()).as("因为没给version赋值就没走插件").isEqualTo(0); updateById = m.updateById(entity); assertThat(updateById).as("更新成功").isEqualTo(1); entity = m.selectById(1L); assertThat(entity.getVersion()).as("因为version有值就走了插件").isEqualTo(1); }); doTestAutoCommit(m -> { int update = m.update(new Entity().setName("老王"), Wrappers.query().eq("id", 2)); assertThat(update).as("更新成功").isEqualTo(1); Entity entity = m.selectById(2L); assertThat(entity.getVersion()).as("因为没给version赋值就没走插件").isEqualTo(0); update = m.update(new Entity().setName("老王").setVersion(0), Wrappers.query().eq("id", 2)); assertThat(update).as("更新成功").isEqualTo(1); entity = m.selectById(2L); assertThat(entity.getVersion()).as("因为version有值就走了插件").isEqualTo(1); }); } @Test void testBatch() { var entity1 = new Entity().setName("testBatch").setVersion(1); var entity2 = new Entity().setName("testBatch").setVersion(1); var entity3 = new Entity().setName("testBatch").setVersion(1); var entityList = List.of(entity1, entity2, entity3); doTest(mapper -> { Assertions.assertTrue(SqlHelper.retBool(mapper.insert(entityList))); Assertions.assertTrue(SqlHelper.retBool(mapper.updateById(entityList))); entity2.setVersion(6); Assertions.assertFalse(SqlHelper.retBool(mapper.updateById(entityList))); }); } @Override protected List interceptors() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return Collections.singletonList(interceptor); } @Override protected String tableDataSql() { return "insert into entity (id,name) values (1,'1'),(2,'2'),(3,'3')"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (\n" + "id BIGINT(20) NOT NULL,\n" + "name VARCHAR(30) NULL DEFAULT NULL,\n" + "version integer not NULL default 0,\n" + "PRIMARY KEY (id)" + ")"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/orderby/OrderByEntity.java ================================================ package com.baomidou.mybatisplus.test.orderby; import com.baomidou.mybatisplus.annotation.OrderBy; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * @author nieqiurong */ @Data @TableName(value = "t_order_test") public class OrderByEntity { @OrderBy(sort = 3) @TableId(value = "oid") private Long id; private String name; @OrderBy(asc = true, sort = 2) @TableField("nl") private Long age; @OrderBy private String tel; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/orderby/OrderByMapper.java ================================================ package com.baomidou.mybatisplus.test.orderby; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author nieqiurong */ public interface OrderByMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/orderby/OrderByMapperTest.java ================================================ package com.baomidou.mybatisplus.test.orderby; import com.baomidou.mybatisplus.core.metadata.OrderFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; /** * @author nieqiurong */ public class OrderByMapperTest extends BaseDbTest { @Test void test() { doTest(mapper -> { mapper.selectList(Wrappers.emptyWrapper()); mapper.selectList(Wrappers.lambdaQuery().select(OrderByEntity::getName)); }); TableInfo tableInfo = TableInfoHelper.getTableInfo(OrderByEntity.class); for (OrderFieldInfo orderByField : tableInfo.getOrderByFields()) { if ("oid".equals(orderByField.getColumn())) { Assertions.assertEquals(3, orderByField.getSort()); Assertions.assertEquals(Constants.DESC, orderByField.getType()); } else if ("age".equals(orderByField.getColumn())) { Assertions.assertEquals(2, orderByField.getSort()); Assertions.assertEquals(Constants.ASC, orderByField.getType()); } else if ("tel".equals(orderByField.getColumn())) { Assertions.assertEquals(Short.MAX_VALUE, orderByField.getSort()); Assertions.assertEquals(Constants.DESC, orderByField.getType()); } } } @Override protected String tableDataSql() { return "insert into t_order_test(oid,name,nl,tel) values(1,'demo1',12, '13311111111'),(2,'demo2',13, '13322222222'),(3,'demo3',14, '13333333333');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists t_order_test", "CREATE TABLE IF NOT EXISTS t_order_test (" + "oid BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "tel VARCHAR(30) NULL DEFAULT NULL," + "nl INT NULL DEFAULT NULL," + "PRIMARY KEY (oid))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/PhoenixBaseMapper.java ================================================ package com.baomidou.mybatisplus.test.phoenix; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author: fly * Created date: 2019/12/21 16:35 */ public interface PhoenixBaseMapper extends BaseMapper { int upsert(T entity); @Override default int insert(T entity) { return upsert(entity); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/PhoenixTest.java ================================================ package com.baomidou.mybatisplus.test.phoenix; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; import com.baomidou.mybatisplus.test.phoenix.entity.PhoenixTestInfo; import com.baomidou.mybatisplus.test.phoenix.mapper.PhoenixTestInfoMapper; import org.apache.ibatis.session.SqlSession; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static com.baomidou.mybatisplus.core.enums.SqlMethod.UPSERT_ONE; import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author: fly * Created date: 2019/12/21 16:35 */ @DirtiesContext @TestMethodOrder(MethodOrderer.MethodName.class) @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:phoenix/spring-test-phoenix.xml"}) public class PhoenixTest { @Autowired PhoenixTestInfoMapper mapper; @Test void a00_upsertForeach() { int size = 10; for (int i = 0; i < 10; i++) { PhoenixTestInfo testInfo = new PhoenixTestInfo() .setId(i) .setName("test"); assertEquals (1, mapper.upsert(testInfo)); } assertEquals(size, mapper.selectList(Wrappers.emptyWrapper()).size()); } @Test void a01_upsertBatch() { int size = 10000; try (SqlSession sqlSession = SqlHelper.sqlSessionBatch(PhoenixTestInfo.class)) { String sqlStatement = SqlHelper.table(PhoenixTestInfo.class).getSqlStatement(UPSERT_ONE.getMethod()); for (int i = 0; i < size; i++) { PhoenixTestInfo testInfo = new PhoenixTestInfo() .setId(i) .setName("upsertBatch"); sqlSession.insert(sqlStatement, testInfo); } sqlSession.commit(); // sqlSession.flushStatements(); } assertEquals(size, mapper.selectList(Wrappers.emptyWrapper()).size()); } @Test void a02_page() { Page page = new Page<>(2, 100); IPage result = mapper.selectPage(page, Wrappers.emptyWrapper()); assertEquals(10000, result.getTotal()); assertEquals(2, result.getCurrent()); assertEquals(100, result.getPages()); assertEquals(100, result.getRecords().size()); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/config/DBConfig.java ================================================ package com.baomidou.mybatisplus.test.phoenix.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.jdbc.datasource.init.DataSourceInitializer; import org.springframework.jdbc.datasource.init.DatabasePopulator; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; import java.io.IOException; import java.sql.Driver; import java.util.Properties; /** * @author miemie * @since 2018/6/7 */ @Configuration @SuppressWarnings("unchecked") @EnableTransactionManagement public class DBConfig { @Bean("dataSource") public DataSource dataSource() throws ClassNotFoundException { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriverClass((Class) Class.forName("org.apache.phoenix.jdbc.PhoenixDriver")); dataSource.setUrl("jdbc:phoenix:dnode28,dnode29,dnode30:2181"); Properties properties = new Properties(); properties.setProperty("schema", "TEST"); dataSource.setConnectionProperties(properties); return dataSource; } @Bean public DataSourceTransactionManager transactionManager(DataSource ds) { return new DataSourceTransactionManager(ds); } @Bean public DataSourceInitializer dataSourceInitializer(DataSource dataSource) throws IOException { final DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); initializer.setDatabasePopulator(databasePopulator()); initializer.setEnabled(true); return initializer; } private DatabasePopulator databasePopulator() throws IOException { ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator(); resourceDatabasePopulator.setContinueOnError(false); resourceDatabasePopulator.addScripts( new PathMatchingResourcePatternResolver().getResources("classpath:/phoenix/*.sql") ); return resourceDatabasePopulator; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/config/MybatisPlusConfig.java ================================================ package com.baomidou.mybatisplus.test.phoenix.config; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory; import com.baomidou.mybatisplus.extension.injector.methods.Upsert; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.List; /** * Mybatis Plus Config * * @author Caratacus * @since 2017/4/1 */ @Configuration @MapperScan("com.baomidou.mybatisplus.test.phoenix.mapper") public class MybatisPlusConfig { @Bean("mybatisSqlSession") public SqlSessionFactory sqlSessionFactory( DataSource dataSource, GlobalConfig globalConfig ) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); /* 数据源 */ sqlSessionFactory.setDataSource(dataSource); /* xml扫描 */ // sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() // .getResources("classpath:/mapper/*.xml")); /* 扫描 typeHandler */ MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); /* 驼峰转下划线 */ configuration.setMapUnderscoreToCamelCase(true); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); /* map 下划线转驼峰 */ configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory()); sqlSessionFactory.setConfiguration(configuration); sqlSessionFactory.setGlobalConfig(globalConfig); return sqlSessionFactory.getObject(); } @Bean public GlobalConfig globalConfig() { GlobalConfig conf = new GlobalConfig(); // conf.setDbConfig(new GlobalConfig.DbConfig() // .setColumnFormat("`%s`")); DefaultSqlInjector phoenixSqlInjector = new DefaultSqlInjector() { /** * 注入自定义全局方法 */ @Override public List getMethodList(org.apache.ibatis.session.Configuration configuration, Class mapperClass, TableInfo tableInfo) { List methodList = super.getMethodList(configuration, mapperClass, tableInfo); methodList.add(new Upsert()); return methodList; } }; conf.setSqlInjector(phoenixSqlInjector); return conf; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/entity/PhoenixTestInfo.java ================================================ package com.baomidou.mybatisplus.test.phoenix.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; /** * @author fly * @date 2019/5/22 17:52 * description: phoenix hbase TEST_INFO table */ @Data @Accessors(chain = true) @TableName("test_info") public class PhoenixTestInfo { /** * Phoenix主键Mybatis不支持自增,需要插入传入或者设置key generator */ private Integer id; private String name; private String phone; private String position; private String department; private String company; private String fileName; private String posDepCom; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/mapper/PhoenixTestInfoMapper.java ================================================ package com.baomidou.mybatisplus.test.phoenix.mapper; import com.baomidou.mybatisplus.test.phoenix.PhoenixBaseMapper; import com.baomidou.mybatisplus.test.phoenix.entity.PhoenixTestInfo; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; /** * @author fly * @date 2019/5/22 17:55 * description: */ public interface PhoenixTestInfoMapper extends PhoenixBaseMapper { @Insert( "UPSERT INTO TEST_INFO " + "(ID, NAME) " + "VALUES(#{t.id},#{t.name})" ) void addTestInfo(@Param("t") PhoenixTestInfo t); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java ================================================ package com.baomidou.mybatisplus.test.pom; import jodd.io.FileUtil; import jodd.jerry.Jerry; import jodd.jerry.JerryParser; import jodd.lagarto.dom.LagartoDOMBuilder; import lombok.AllArgsConstructor; import lombok.Data; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** * 检查pom依赖 * * @author nieqiurong 2019/2/9. */ class GeneratePomTest { @Data @AllArgsConstructor private static class Dependency { private String artifactId; private String scope; private boolean optional; } @Test void test() throws IOException { try (InputStream inputStream = new FileInputStream("build/publications/mavenJava/pom-default.xml")) { JerryParser jerryParser = Jerry.create((new LagartoDOMBuilder().enableXmlMode())); Jerry doc = jerryParser.parse(FileUtil.readUTFString(inputStream)); Jerry dependencies = doc.s("dependencies dependency"); Map dependenciesMap = new HashMap<>(); dependencies.forEach($this -> { String artifactId = $this.s("artifactId").text(); dependenciesMap.put(artifactId, new Dependency(artifactId, $this.s("scope").text(), Boolean.parseBoolean($this.s("optional").text()))); }); Dependency extension = dependenciesMap.get("mybatis-plus-spring"); Assertions.assertEquals("compile", extension.getScope()); Assertions.assertFalse(extension.isOptional()); Dependency mybatisSpring = dependenciesMap.get("mybatis-spring"); Assertions.assertEquals("compile", mybatisSpring.getScope()); Assertions.assertTrue(mybatisSpring.isOptional()); Dependency kotlinStdlib = dependenciesMap.get("kotlin-stdlib-jdk8"); Assertions.assertEquals("compile", kotlinStdlib.getScope()); Assertions.assertTrue(kotlinStdlib.isOptional()); Dependency jsqlparserLib = dependenciesMap.get("mybatis-plus-jsqlparser-4.9"); Assertions.assertEquals("compile", jsqlparserLib.getScope()); Assertions.assertTrue(jsqlparserLib.isOptional()); } } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/PostgresqlTest.java ================================================ package com.baomidou.mybatisplus.test.postgresql; import com.baomidou.mybatisplus.test.postgresql.entity.Pgtable; import com.baomidou.mybatisplus.test.postgresql.mapper.PgtableMappper; import com.baomidou.mybatisplus.test.postgresql.service.IPgtableService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.List; @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = {"classpath:postgresql/spring-test-postgresql.xml"}) public class PostgresqlTest { @Autowired private PgtableMappper mapper; @Autowired private IPgtableService pgtableService; @Test void test() { Pgtable pgtable = Pgtable.builder().compName("test").age(10).build(); Assertions.assertTrue(mapper.insert(pgtable) > 0); System.out.println(pgtable.getId()); List pgtableList = mapper.selectList(null); Assertions.assertNotNull(pgtableList); Assertions.assertTrue(pgtableList.size() > 0); } @Test void testTask() throws InterruptedException { // 测试线程 pgtableService.testTask(); // 主线程休眠 Thread.sleep(10000); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/config/PostgresqlConfig.java ================================================ package com.baomidou.mybatisplus.test.postgresql.config; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.annotation.MapperScan; import org.postgresql.Driver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; @Configuration @SuppressWarnings("unchecked") @EnableTransactionManagement @MapperScan("com.baomidou.mybatisplus.test.postgresql.mapper") public class PostgresqlConfig { @Bean("dataSource") public DataSource dataSource() throws ClassNotFoundException { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new Driver()); dataSource.setUrl("jdbc:postgresql://localhost:5432/test"); dataSource.setUsername("postgres"); dataSource.setPassword("123456"); return dataSource; } @Bean("mybatisSqlSession") public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); /* 数据源 */ sqlSessionFactory.setDataSource(dataSource); /* 扫描 typeHandler */ MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); /* 驼峰转下划线 */ configuration.setMapUnderscoreToCamelCase(true); MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); sqlSessionFactory.setPlugins(mybatisPlusInterceptor); sqlSessionFactory.setConfiguration(configuration); GlobalConfig globalConfig = new GlobalConfig(); GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig(); // TODO 这里申明所有字段为大写 dbConfig.setCapitalMode(true); dbConfig.setColumnFormat("\"%s\""); globalConfig.setDbConfig(dbConfig); sqlSessionFactory.setGlobalConfig(globalConfig); return sqlSessionFactory.getObject(); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/entity/Pgtable.java ================================================ package com.baomidou.mybatisplus.test.postgresql.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Builder; import lombok.Getter; import lombok.Setter; @Getter @Setter @Builder public class Pgtable { @TableId(type = IdType.AUTO) private Long id; private String compName; private Integer age; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/mapper/PgtableMappper.java ================================================ package com.baomidou.mybatisplus.test.postgresql.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.test.postgresql.entity.Pgtable; public interface PgtableMappper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/service/IPgtableService.java ================================================ package com.baomidou.mybatisplus.test.postgresql.service; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.test.postgresql.entity.Pgtable; import org.springframework.stereotype.Service; @Service public interface IPgtableService extends IService { void testTask(); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/service/impl/PgtableServiceImpl.java ================================================ package com.baomidou.mybatisplus.test.postgresql.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.test.postgresql.entity.Pgtable; import com.baomidou.mybatisplus.test.postgresql.mapper.PgtableMappper; import com.baomidou.mybatisplus.test.postgresql.service.IPgtableService; import org.springframework.stereotype.Service; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; @Service public class PgtableServiceImpl extends ServiceImpl implements IPgtableService { @Override public void testTask() { ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1); executorService.schedule(() -> { try { this.updateById(Pgtable.builder().id(1L).compName("testTask01").age(11).build()); baseMapper.updateById(Pgtable.builder().id(1L).compName("testTask02").age(12).build()); } catch (Exception exception) { exception.getStackTrace(); } finally { executorService.shutdown(); } }, 3, TimeUnit.SECONDS); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/A.java ================================================ package com.baomidou.mybatisplus.test.puginsome; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; /** * @author miemie * @since 2022-05-11 */ @Data public class A { private int id; private String name; @TableField(exist = false) private B b; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/AMapper.java ================================================ package com.baomidou.mybatisplus.test.puginsome; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import java.util.List; /** * @author miemie * @since 2022-05-11 */ public interface AMapper extends BaseMapper { List list(); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/B.java ================================================ package com.baomidou.mybatisplus.test.puginsome; import lombok.Data; /** * @author miemie * @since 2022-05-11 */ @Data public class B { private int id; private int aId; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/BMapper.java ================================================ package com.baomidou.mybatisplus.test.puginsome; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2022-05-11 */ public interface BMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/PluginSomeTest.java ================================================ package com.baomidou.mybatisplus.test.puginsome; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.SQLException; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * @author miemie * @since 2020-06-24 */ public class PluginSomeTest extends BaseDbTest { @Test void test() { doTest(m -> { List a = m.list(); a.forEach(System.out::println); }); } @Override protected List interceptors() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new InnerInterceptor() { @Override public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { System.out.println("willDoQuery"); return true; } @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { System.out.println("beforeQuery"); } @Override public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException { System.out.println("willDoUpdate"); return true; } @Override public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException { System.out.println("beforeUpdate"); } @Override public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { System.out.println("beforePrepare"); } @Override public void beforeGetBoundSql(StatementHandler sh) { System.out.println("beforeGetBoundSql"); } }); return Collections.singletonList(interceptor); } @Override protected List> otherMapper() { return Collections.singletonList(BMapper.class); } @Override protected String tableDataSql() { return "insert into a (id,name) values (1,'1'),(2,'2');" + "insert into b (id,a_id,name) values (1,'1','1-1'),(2,'2','2-2')"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists a", "CREATE TABLE IF NOT EXISTS a (\n" + "id integer NOT NULL,\n" + "name VARCHAR(30) NULL DEFAULT NULL,\n" + "PRIMARY KEY (id)" + ")", "drop table if exists b", "CREATE TABLE IF NOT EXISTS b (\n" + "id integer NOT NULL,\n" + "a_id integer NOT NULL,\n" + "name VARCHAR(30) NULL DEFAULT NULL,\n" + "PRIMARY KEY (id)" + ")"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/record/RecordEntity.java ================================================ package com.baomidou.mybatisplus.test.record; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; /** * @author nieqiurong 2023年9月14日 */ @TableName(value = "t_record") public record RecordEntity(Long id, String name, @TableField(value = "tel") String phone) { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/record/RecordEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.record; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author nieqiurong 2023年9月14日 */ public interface RecordEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/record/RecordEntityTest.java ================================================ package com.baomidou.mybatisplus.test.record; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; /** * @author nieqiurong 2023年9月14日 */ public class RecordEntityTest extends BaseDbTest { @Test void testInsert() { doTest(mapper -> { //TODO 无法使用自动填充与主键生成,不建议充当PO使用 RecordEntity recordEntity = new RecordEntity(123L, "苗人凤", "13388888888"); Assertions.assertEquals(1, mapper.insert(recordEntity)); }); } @Test void testUpdate() { doTest(mapper -> { RecordEntity recordEntity = new RecordEntity(2L, "苗人凤2", null); Assertions.assertEquals(1, mapper.updateById(recordEntity)); recordEntity = mapper.selectById(2L); Assertions.assertEquals("苗人凤2", recordEntity.name()); Assertions.assertNotNull(recordEntity.phone(), "13322222222"); }); } @Test void testSelect() { doTest(mapper -> { RecordEntity recordEntity = mapper.selectById(3L); Assertions.assertEquals("demo3", recordEntity.name()); Assertions.assertNotNull(recordEntity.phone(), "13333333333"); }); } @Test void testDelete() { doTest(mapper -> Assertions.assertEquals(1, mapper.deleteById(new RecordEntity(1L, "-----", "1")))); } @Override protected String tableDataSql() { return "insert into t_record(id,name,tel) values(1,'demo1','13311111111'),(2,'demo2','13322222222'),(3,'demo3','13333333333');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists t_record", "CREATE TABLE IF NOT EXISTS t_record (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "tel VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/reflection/ExampleObjectFactory.java ================================================ package com.baomidou.mybatisplus.test.reflection; import org.apache.ibatis.reflection.factory.DefaultObjectFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.List; import java.util.Properties; /** * 对象工厂 * @author nieqiurong 2018/8/14 13:12. */ public class ExampleObjectFactory extends DefaultObjectFactory { /** * serialVersionUID */ private static final long serialVersionUID = -2878759377109110945L; private static final Logger LOGGER = LoggerFactory.getLogger(ExampleObjectFactory.class); public T create(Class type) { LOGGER.debug("生成一个对象 type = [" + type + "]"); return super.create(type); } @Override public T create(Class type, List> constructorArgTypes, List constructorArgs) { LOGGER.info("生成一个对象 type = [" + type + "], constructorArgTypes = [" + constructorArgTypes + "], constructorArgs = [" + constructorArgs + "]"); return super.create(type, constructorArgTypes, constructorArgs); } public void setProperties(Properties properties) { LOGGER.debug("设置属性 properties = [" + properties + "]"); super.setProperties(properties); } public boolean isCollection(Class type) { return Collection.class.isAssignableFrom(type); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/replaceplaceholder/Entity.java ================================================ package com.baomidou.mybatisplus.test.replaceplaceholder; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; @TableField("`name`") private String name; private Integer age; @TableField(exist = false) private EntitySub es; @Data public static class EntitySub { private Long id; private String name; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/replaceplaceholder/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.replaceplaceholder; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Select; import java.util.List; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { @Select("select {@entity-name,id} from entity") List selectAll(); @Select("select {@entity:e},{@entity_sub-id:es:es} from entity e left join entity_sub es on e.id = es.id") List selectAll2(); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/replaceplaceholder/EntitySubMapper.java ================================================ package com.baomidou.mybatisplus.test.replaceplaceholder; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-23 */ public interface EntitySubMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/replaceplaceholder/ReplacePlaceholderTest.java ================================================ package com.baomidou.mybatisplus.test.replaceplaceholder; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.ReplacePlaceholderInnerInterceptor; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.plugin.Interceptor; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ public class ReplacePlaceholderTest extends BaseDbTest { @Test void replace() { doTest(i -> { List list = i.selectAll(); System.out.println(list); assertThat(list.getFirst().getId()).isNull(); assertThat(list.getFirst().getName()).isNull(); assertThat(list.getFirst().getAge()).isNotNull(); list = i.selectAll2(); System.out.println(list); assertThat(list.getFirst().getEs().getId()).isNull(); assertThat(list.getFirst().getEs().getName()).isNotBlank(); }); } @Override protected List interceptors() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new ReplacePlaceholderInnerInterceptor("\"")); return Collections.singletonList(interceptor); } // 全局 gradle test 不支持,全局缓存串台了 // @Override // protected GlobalConfig globalConfig() { // GlobalConfig config = super.globalConfig(); // config.getDbConfig().setReplacePlaceholder(true); // return config; // } @Override protected String tableDataSql() { return "insert into entity(id,name,age) values(1,'1',1),(2,'2',2);" + "insert into entity_sub(id,name) values(1,'1'),(2,'2');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "drop table if exists entity_sub", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "age int NULL DEFAULT NULL," + "PRIMARY KEY (id))", "CREATE TABLE IF NOT EXISTS entity_sub (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } @Override protected List> otherMapper() { return List.of(EntitySubMapper.class); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/Entity.java ================================================ package com.baomidou.mybatisplus.test.resultmap; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.io.Serializable; import java.util.List; import java.util.Map; /** * @author miemie * @since 2020-06-23 */ @Data @Accessors(chain = true) @TableName(resultMap = "resultMap") public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; @TableField(typeHandler = GsonTypeHandler.class) private Gg gg1; @TableField(typeHandler = GsonTypeHandler.class) private Gg gg2; @TableField(typeHandler = GsonTypeHandler.class) private List gg3; @TableField(typeHandler = GsonTypeHandler.class) private List gg4; @TableField(typeHandler = GsonTypeHandler.class) private String[] str; @Data @NoArgsConstructor @AllArgsConstructor public static class Gg { private String name; } @Data @NoArgsConstructor @AllArgsConstructor public static class Gg4 { private String name; private Gg gg; private List ggList; private Map ggMap; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.resultmap; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/ResultMapTest.java ================================================ package com.baomidou.mybatisplus.test.resultmap; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ public class ResultMapTest extends BaseDbTest { @Test void test() { doTestAutoCommit(m -> m.insert(new Entity().setGg1(new Entity.Gg("老王")) .setStr(new String[]{"hello"}) .setGg2(new Entity.Gg("老李")) .setGg3(List.of(new Entity.Gg("老张"))) .setGg4(List.of(new Entity.Gg4("秋秋", new Entity.Gg("小红"), List.of(new Entity.Gg("小猫")), Map.of("test", new Entity.Gg("小明"))))) )); doTest(m -> { Entity entity = m.selectOne(null); assertThat(entity).as("插入正常").isNotNull(); assertThat(entity.getGg1()).as("typeHandler正常").isNotNull(); assertThat(entity.getGg1().getName()).as("是老王").isEqualTo("老王"); assertThat(entity.getGg2()).as("typeHandler正常").isNotNull(); assertThat(entity.getGg2().getName()).as("是老李").isEqualTo("老李"); assertThat(entity.getGg3()).as("typeHandler正常").isNotNull(); assertThat(entity.getGg3().getFirst().getName()).as("是老张").isEqualTo("老张"); assertThat(entity.getGg4()).as("typeHandler正常").isNotNull(); assertThat(entity.getGg4().getFirst().getName()).as("是秋秋").isEqualTo("秋秋"); assertThat(entity.getGg4().getFirst().getGg().getName()).as("是小红").isEqualTo("小红"); assertThat(entity.getGg4().getFirst().getGgList().getFirst().getName()).as("是小猫").isEqualTo("小猫"); assertThat(entity.getGg4().getFirst().getGgMap().get("test").getName()).as("是小明").isEqualTo("小明"); assertThat(entity.getStr()).as("typeHandler正常").isNotNull(); assertThat(entity.getStr()[0]).isEqualTo("hello"); }); } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (\n" + "id BIGINT(20) NOT NULL,\n" + "gg1 VARCHAR(255) NULL DEFAULT NULL,\n" + "gg2 VARCHAR(255) NULL DEFAULT NULL,\n" + "gg3 VARCHAR(255) NULL DEFAULT NULL,\n" + "gg4 VARCHAR(255) NULL DEFAULT NULL,\n" + "str VARCHAR(255) NULL DEFAULT NULL,\n" + "PRIMARY KEY (id)" + ")"); } @Override protected String mapperXml() { return "com/baomidou/mybatisplus/test/resultmap/EntityMapper.xml"; } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/rewrite/Entity.java ================================================ package com.baomidou.mybatisplus.test.rewrite; import lombok.Data; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/rewrite/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.rewrite; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/rewrite/RewriteTest.java ================================================ package com.baomidou.mybatisplus.test.rewrite; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-23 */ public class RewriteTest extends BaseDbTest { @Test void replace() { doTest(i -> { Entity entity = i.selectById(1); assertThat(entity.getName()).isNull(); }); } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1,'1'),(2,'2');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/scheam/SchemaEntity.java ================================================ package com.baomidou.mybatisplus.test.scheam; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * @author nieqiurong */ @Data @TableName(value = "${my.tableName}", schema = "${my.schema}") public class SchemaEntity { private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/scheam/SchemaEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.scheam; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; /** * @author nieqiurong */ @Mapper public interface SchemaEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/scheam/SchemaEntityTest.java ================================================ package com.baomidou.mybatisplus.test.scheam; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.Properties; import java.util.function.Consumer; /** * @author nieqiurong */ public class SchemaEntityTest extends BaseDbTest { @Test void test() { doTest(mapper -> { SchemaEntity schemaEntity = mapper.selectById(1); Assertions.assertNotNull(schemaEntity); }); } @Override protected Consumer consumer() { return configuration -> { Properties properties = new Properties(); properties.put("my.schema", "public"); properties.put("my.tableName", "SCHEMA_ENTITY"); configuration.setVariables(properties); }; } @Override protected String tableDataSql() { return "insert into SCHEMA_ENTITY(id,name) values(1,'1'),(2,'2');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists SCHEMA_ENTITY", "CREATE TABLE IF NOT EXISTS SCHEMA_ENTITY (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/sqlrunner/Entity.java ================================================ package com.baomidou.mybatisplus.test.sqlrunner; import lombok.Data; /** * @author miemie * @since 2021-03-16 */ @Data public class Entity { private Long id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/sqlrunner/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.sqlrunner; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2021-03-16 */ public interface EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/sqlrunner/SqlRunnerTest.java ================================================ package com.baomidou.mybatisplus.test.sqlrunner; import com.baomidou.mybatisplus.core.injector.SqlRunnerInjector; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; import com.baomidou.mybatisplus.extension.toolkit.SqlRunner; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.plugin.Interceptor; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; /** * @author miemie * @since 2021-03-16 */ public class SqlRunnerTest extends BaseDbTest { public SqlRunnerTest() { SqlHelper.FACTORY = sqlSessionFactory; new SqlRunnerInjector().inject(sqlSessionFactory.getConfiguration()); } @Test void test() { assertThat(SqlRunner.db().insert("insert into entity(id,name) values({0},{1})", 6, "6")).isTrue(); assertThat(SqlRunner.db().update("update entity set name = {0} where id = {1}", "老王", 6)).isTrue(); assertThat(SqlRunner.db().delete("delete from entity where id = {0}", 6)).isTrue(); } @Override protected List interceptors() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return Collections.singletonList(interceptor); } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1,'1'),(2,'2'),(3,'3'),(4,'4'),(5,'5');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/strategy/Entity.java ================================================ package com.baomidou.mybatisplus.test.strategy; import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; /** * @author miemie * @since 2021-01-27 */ @Data public class Entity { private Long id; private String name; @TableField(insertStrategy = FieldStrategy.NOT_EMPTY) private String insertStr; @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) private String updateStr; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/strategy/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.strategy; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/strategy/StrategyTest.java ================================================ package com.baomidou.mybatisplus.test.strategy; import com.baomidou.mybatisplus.test.BaseDbTest; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2021-01-27 */ public class StrategyTest extends BaseDbTest { @Test void test() { long id = 1L; doTestAutoCommit(i -> { Entity entity = new Entity(); entity.setId(id); entity.setName("entity"); entity.setInsertStr(""); i.insert(entity); }); doTest(i -> { Entity entity = i.selectById(id); assertThat(entity).isNotNull(); assertThat(entity.getInsertStr()).isNull(); assertThat(entity.getUpdateStr()).isNull(); }); doTestAutoCommit(i -> { Entity entity = new Entity(); entity.setId(id); entity.setName("entity"); entity.setUpdateStr(""); i.updateById(entity); }); doTest(i -> { Entity entity = i.selectById(id); assertThat(entity).isNotNull(); assertThat(entity.getInsertStr()).isNull(); assertThat(entity.getUpdateStr()).isNull(); }); } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "insert_str VARCHAR(30) NULL DEFAULT NULL," + "update_str VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/tenant/Entity.java ================================================ package com.baomidou.mybatisplus.test.tenant; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; /** * @author miemie * @since 2020-06-24 */ @Data @Accessors(chain = true) public class Entity implements Serializable { private static final long serialVersionUID = 6389385437936113455L; private Long id; private String name; private Integer tenantId; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/tenant/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.tenant; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import org.apache.ibatis.annotations.CacheNamespace; import java.io.Serializable; /** * @author miemie * @since 2020-06-24 */ @CacheNamespace public interface EntityMapper extends BaseMapper { /* * 请注意: Mybatis的接口方法虽然支持重载,但是底层MappedStatement是只能有一份的,也就是MappedStatement(类名+方法名)组成唯一性. * * 低版本(<3.5.10)下,忽略BaseMapper上的方法,可通过重写其中方法来标记 * 例如忽略deleteById方法,直接覆写 int deleteById(Entity entity); 这样就会把deleteById相关的重载方法都会重写掉,因为忽略方式是对MappedStatement级别生效的 * * (高版本才支持) * 但是建议按照如果有需要跳过一些插件的方法,通过自定义方法标记作用跳过会好点. * 例如我调用deleteById,默认情况下是需要拼接租户条件的,但如果有些特殊需求,想忽略跳过租户的时候,可以直接自定义个默认方法(例如deleteByIdWithIgnore)来调用baseMapper方法得deleteById */ @InterceptorIgnore(tenantLine = "true") default int deleteByIdWithIgnore(Serializable id) { return deleteById(id); } @InterceptorIgnore(tenantLine = "true") default Entity selectByIdWithIgnore(Serializable id) { return selectById(id); } @InterceptorIgnore(tenantLine = "false") default Entity selectByIdWithIgnore2(Serializable id) { return selectByIdWithIgnore(id); } default Entity selectByIdWithIgnore3(Serializable id) { return selectByIdWithIgnore(id); } default Entity selectByIdWithIgnore4(Serializable id) { return selectByIdWithIgnore2(id); } default Entity selectByIdWithIgnore5(Serializable id) { return selectById(id); } default Entity selectByIdWithIgnore6(IgnoreStrategy ignoreStrategy, Serializable id) { return InterceptorIgnoreHelper.execute(ignoreStrategy, ()-> selectById(id)); } default Entity selectByIdWithIgnore7(IgnoreStrategy ignoreStrategy, Serializable id) { return InterceptorIgnoreHelper.execute(ignoreStrategy, ()-> selectByIdWithIgnore(id)); } default Entity selectByIdWithIgnore8(IgnoreStrategy ignoreStrategy, Serializable id) { return InterceptorIgnoreHelper.execute(ignoreStrategy, ()-> selectByIdWithIgnore2(id)); } // /** // * //TODO 由于是对ms级别的忽略,所以不考虑重载方法, 忽略deleteById方法 // */ // @InterceptorIgnore(tenantLine = "true") // int deleteById(Entity entity); } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/tenant/TenantTest.java ================================================ package com.baomidou.mybatisplus.test.tenant; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.test.BaseDbTest; import net.sf.jsqlparser.expression.LongValue; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.plugin.Interceptor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * @author miemie * @since 2020-06-24 */ public class TenantTest extends BaseDbTest { @Test void test() { Cache cache = sqlSessionFactory.getConfiguration().getCache(EntityMapper.class.getName()); assertThat(cache).as("使用 @CacheNamespace 指定了使用缓存").isNotNull(); Long id = 1L; doTestAutoCommit(m -> { int insert = m.insert(new Entity().setId(id)); assertThat(insert).as("插入成功").isEqualTo(1); }); doTest(m -> { Entity entity = m.selectById(id); assertThat(entity).as("插入成功").isNotNull(); assertThat(entity.getTenantId()).as("有租户信息").isEqualTo(1); }); assertThat(cache.getSize()).as("有一条缓存").isEqualTo(1); doTest(m -> { Entity entity = m.selectById(id); assertThat(entity).as("插入成功").isNotNull(); assertThat(entity.getTenantId()).as("有租户信息").isEqualTo(1); }); assertThat(cache.getSize()).as("依然只有一条缓存,命中了缓存").isEqualTo(1); doTestAutoCommit(m -> { int delete = m.deleteById(id); assertThat(delete).as("删除成功").isEqualTo(1); }); assertThat(cache.getSize()).as("update操作清空了缓存").isEqualTo(0); doTestAutoCommit(m -> { int insert = m.insert(new Entity().setId(id).setTenantId(2)); assertThat(insert).as("故意插入一个其他租户的信息,插入成功").isEqualTo(1); }); doTest(m -> { Entity entity = m.selectById(id); assertThat(entity).as("搜索不到数据").isNull(); }); assertThat(cache.getSize()).as("缓存了个寂寞").isEqualTo(1); doTest(m -> { Entity entity = m.selectById(id); assertThat(entity).as("搜索不到数据").isNull(); }); assertThat(cache.getSize()).as("依然缓存了个寂寞,说明命中的缓存").isEqualTo(1); doTest(m -> { Page page = m.selectPage(new Page<>(), null); assertThat(page.getTotal()).as("count 正常").isEqualTo(0); }); doTest(m -> { Entity entity = new Entity().setName("秋秋").setTenantId(2); m.insert(entity); Assertions.assertNull(m.selectById(entity.getId())); Assertions.assertNotNull(m.selectByIdWithIgnore(entity.getId())); Assertions.assertNull(m.selectByIdWithIgnore2(entity.getId())); Assertions.assertNull(m.selectByIdWithIgnore3(entity.getId())); Assertions.assertNull(m.selectByIdWithIgnore4(entity.getId())); Assertions.assertNull(m.selectByIdWithIgnore5(entity.getId())); Assertions.assertNotNull(m.selectByIdWithIgnore6(IgnoreStrategy.builder().tenantLine(true).build(), entity.getId())); Assertions.assertNull(m.selectByIdWithIgnore6(IgnoreStrategy.builder().tenantLine(false).build(), entity.getId())); Assertions.assertNotNull(m.selectByIdWithIgnore7(IgnoreStrategy.builder().tenantLine(true).build(), entity.getId())); Assertions.assertNull(m.selectByIdWithIgnore7(IgnoreStrategy.builder().tenantLine(false).build(), entity.getId())); Assertions.assertNotNull(m.selectByIdWithIgnore8(IgnoreStrategy.builder().tenantLine(true).build(), entity.getId())); Assertions.assertNull(m.selectByIdWithIgnore8(IgnoreStrategy.builder().tenantLine(false).build(), entity.getId())); Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore(entity.getId()))); Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore(entity.getId()))); Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore2(entity.getId()))); Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore2(entity.getId()))); Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore3(entity.getId()))); Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore3(entity.getId()))); Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore4(entity.getId()))); Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore4(entity.getId()))); Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore5(entity.getId()))); Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore5(entity.getId()))); Assertions.assertEquals(0, m.deleteById(entity.getId())); Assertions.assertEquals(1, m.deleteByIdWithIgnore(entity.getId())); }); doTest(m -> { Entity entity = new Entity().setName("秋秋").setTenantId(2); m.insert(entity); Assertions.assertNull(m.selectById(entity.getId())); Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(), () -> m.selectById(entity.getId()))); Assertions.assertEquals(0, m.deleteById(entity.getId())); Assertions.assertEquals(1, InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(), () -> m.deleteById(entity.getId()))); }); doTest(m -> { Entity entity = new Entity().setName("秋秋").setTenantId(2); m.insert(entity); Assertions.assertNull(m.selectById(entity.getId())); InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(), () -> { Assertions.assertNotNull(m.selectById(entity.getId())); Assertions.assertEquals(1, m.deleteById(entity.getId())); }); }); } @Override protected List interceptors() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(() -> new LongValue(1))); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); return Collections.singletonList(interceptor); } @Override protected String tableDataSql() { return "insert into entity values(1111,'娇妹',3)"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (\n" + "id BIGINT(20) NOT NULL,\n" + "name VARCHAR(30) NULL DEFAULT NULL,\n" + "tenant_id integer not NULL,\n" + "PRIMARY KEY (id)" + ")"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/DbTest.java ================================================ package com.baomidou.mybatisplus.test.toolkit; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.spi.CompatibleHelper; import com.baomidou.mybatisplus.core.spi.CompatibleSet; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper; import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.toolkit.Db; import com.baomidou.mybatisplus.test.BaseDbTest; import com.baomidou.mybatisplus.test.sqlrunner.Entity; import com.baomidou.mybatisplus.test.sqlrunner.EntityMapper; import org.apache.ibatis.exceptions.TooManyResultsException; import org.apache.ibatis.plugin.Interceptor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import java.util.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * 以静态方式调用Service中的函数 * * @author VampireAchao * @since 2022-05-03 */ class DbTest extends BaseDbTest { @Test void testSave() { Entity entity = new Entity(); entity.setName("ruben"); boolean isSuccess = Db.save(entity); Assertions.assertTrue(isSuccess); assertEquals(3L, Db.count(Entity.class)); } @Test void testSaveBatch() { List list = Arrays.asList(new Entity(), new Entity()); boolean isSuccess = Db.saveBatch(list); Assertions.assertTrue(isSuccess); assertEquals(4, Db.count(Entity.class)); } @Test void testSaveOrUpdateBatch() { Entity entity = new Entity(); entity.setId(1L); entity.setName("cat"); List list = Arrays.asList(new Entity(), entity); boolean isSuccess = Db.saveOrUpdateBatch(list); Assertions.assertTrue(isSuccess); assertEquals(3, Db.count(Entity.class)); } @Test void testRemoveById() { Entity entity = new Entity(); entity.setId(1L); boolean isSuccess = Db.removeById(entity); Assertions.assertTrue(isSuccess); assertEquals(1, Db.count(Entity.class)); isSuccess = Db.removeById(2L, Entity.class); Assertions.assertTrue(isSuccess); assertEquals(0, Db.count(Entity.class)); } @Test void testUpdateById() { Entity entity = new Entity(); entity.setId(1L); entity.setName("bee bee I'm a sheep"); boolean isSuccess = Db.updateById(entity); Assertions.assertTrue(isSuccess); assertEquals("bee bee I'm a sheep", Db.getById(1L, Entity.class).getName()); } @Test void testUpdate() { boolean isSuccess = Db.update(Wrappers.lambdaUpdate(Entity.class).eq(Entity::getId, 1L).set(Entity::getName, "be better")); Assertions.assertTrue(isSuccess); assertEquals("be better", Db.getById(1L, Entity.class).getName()); Entity entity = new Entity(); entity.setId(1L); entity.setName("bee bee I'm a sheep"); isSuccess = Db.update(entity, Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L)); Assertions.assertTrue(isSuccess); assertEquals("bee bee I'm a sheep", Db.getById(1L, Entity.class).getName()); } @Test void testUpdateBatchById() { Entity sheep = new Entity(); sheep.setId(1L); sheep.setName("bee bee I'm a sheep"); Entity ruben = new Entity(); ruben.setId(2L); ruben.setName("rabbit"); boolean isSuccess = Db.updateBatchById(Arrays.asList(sheep, ruben)); Assertions.assertTrue(isSuccess); assertEquals("bee bee I'm a sheep", Db.getById(1L, Entity.class).getName()); assertEquals("rabbit", Db.getById(2L, Entity.class).getName()); } @Test void testRemove() { boolean isSuccess = Db.remove(Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L)); Assertions.assertTrue(isSuccess); assertEquals(1, Db.count(Entity.class)); } @Test void testRemoveByIds() { boolean isSuccess = Db.removeByIds(Arrays.asList(1L, 2L), Entity.class); Assertions.assertTrue(isSuccess); assertEquals(0, Db.count(Entity.class)); } @Test void testRemoveByMap() { boolean isSuccess = Db.removeByMap(Collections.singletonMap("id", 1L), Entity.class); Assertions.assertTrue(isSuccess); assertEquals(1, Db.count(Entity.class)); } @Test void testSaveOrUpdate() { Entity entity = new Entity(); entity.setId(null); entity.setName("bee bee I'm a sheep"); boolean isSuccess = Db.saveOrUpdate(entity); Assertions.assertTrue(isSuccess); assertEquals("bee bee I'm a sheep", Db.getById(entity.getId(), Entity.class).getName()); entity.setName("be better"); isSuccess = Db.saveOrUpdate(entity, Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, entity.getId())); Assertions.assertTrue(isSuccess); assertEquals("be better", Db.getById(entity.getId(), Entity.class).getName()); } @Test void testGetOne() { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(Entity.class); Assertions.assertThrows(TooManyResultsException.class, () -> Db.getOne(wrapper)); Entity one = Db.getOne(wrapper, false); Assertions.assertNotNull(one); Entity entity = new Entity(); entity.setId(1L); one = Db.getOne(entity); Assertions.assertNotNull(one); } @Test void testListByMap() { Map map = new HashMap<>(); map.put("id", 1L); List list = Db.listByMap(map, Entity.class); assertEquals(1, list.size()); assertEquals("ruben", list.getFirst().getName()); } @Test void testByIds() { List list = Db.listByIds(Arrays.asList(1L, 2L), Entity.class); assertEquals(2, list.size()); } @Test void testGetMap() { Map map = Db.getMap(Wrappers.lambdaQuery(Entity.class)); Assertions.assertNotNull(map); Entity entity = new Entity(); entity.setId(1L); map = Db.getMap(entity); Assertions.assertNotNull(map); } @Test void testList() { List list = Db.list(Wrappers.lambdaQuery(Entity.class)); assertEquals(2, list.size()); list = Db.list(Entity.class); assertEquals(2, list.size()); Entity entity = new Entity(); entity.setId(1L); list = Db.list(entity); assertEquals(1, list.size()); } @Test void testListMaps() { List> list = Db.listMaps(Wrappers.lambdaQuery(Entity.class)); assertEquals(2, list.size()); list = Db.listMaps(Entity.class); assertEquals(2, list.size()); Entity entity = new Entity(); entity.setId(1L); list = Db.listMaps(entity); assertEquals(1, list.size()); } @Test void testListObjs() { List list = Db.listObjs(Entity.class); assertEquals(2, list.size()); List objectList = Db.listObjs(Wrappers.lambdaQuery(Entity.class), Entity::getId); assertEquals(2, objectList.size()); List names = Db.listObjs(Entity.class, Entity::getName); Assertions.assertArrayEquals(new String[]{"ruben", "chocolate"}, names.toArray()); } @Override protected List interceptors() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQLITE)); return Collections.singletonList(interceptor); } @Test void testPageMaps() { Page> page = Db.pageMaps(new Page<>(1, 1), Entity.class); assertEquals(2, page.getTotal()); assertEquals(Db.listMaps(new Page<>(1, 1, false), Entity.class).size(), page.getRecords().size()); page = Db.pageMaps(new Page<>(1, 1), Wrappers.lambdaQuery(Entity.class)); assertEquals(1, page.getRecords().size()); assertEquals(Db.listMaps(new Page<>(1, 1, false), Wrappers.lambdaQuery(Entity.class)).size(), page.getRecords().size()); } @Test void testPage() { IPage page = Db.page(new Page<>(1, 1), Entity.class); assertEquals(2, page.getTotal()); assertEquals(Db.list(new Page(1, 1), Entity.class).size(), page.getRecords().size()); page = Db.page(new Page<>(1, 1), Wrappers.lambdaQuery(Entity.class)); assertEquals(1, page.getRecords().size()); assertEquals(Db.list(new Page(1, 1), Wrappers.lambdaQuery(Entity.class)).size(), page.getRecords().size()); } @Test void testChain() { QueryChainWrapper query = Db.query(Entity.class); List list = query.eq("id", 1L).list(); assertEquals(1, list.size()); LambdaQueryChainWrapper lambdaQuery = Db.lambdaQuery(Entity.class); list = lambdaQuery.eq(Entity::getId, 1L).list(); assertEquals(1, list.size()); UpdateChainWrapper update = Db.update(Entity.class); update.eq("id", 1L).set("name", "bee bee I'm a sheep").update(); assertEquals("bee bee I'm a sheep", lambdaQuery.eq(Entity::getId, 1L).one().getName()); LambdaUpdateChainWrapper lambdaUpdate = Db.lambdaUpdate(Entity.class); lambdaUpdate.eq(Entity::getId, 1L).set(Entity::getName, "be better").update(); assertEquals("be better", lambdaQuery.eq(Entity::getId, 1L).one().getName()); } @Test void testGetObj() { String name = Db.getObj(Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L), Entity::getName); assertEquals("ruben", name); } @Test void testCount() { verifyCount(0, null); verifyCount(0, 0L); verifyCount(1, 1L); verifyCount(12, 12L); } private void verifyCount(long expected, Long mockValue) { EntityMapper entityMapper = mock(EntityMapper.class); when(entityMapper.selectCount(any())).thenReturn(mockValue); CompatibleSet compatibleSet = mock(CompatibleSet.class); when(compatibleSet.getBean(EntityMapper.class)).thenReturn(entityMapper); try (MockedStatic compatibleHelperMockedStatic = Mockito.mockStatic(CompatibleHelper.class)) { compatibleHelperMockedStatic.when(CompatibleHelper::hasCompatibleSet).thenReturn(true); compatibleHelperMockedStatic.when(CompatibleHelper::getCompatibleSet).thenReturn(compatibleSet); assertEquals(expected, Db.count(Entity.class)); assertEquals(expected, Db.count(new Entity())); assertEquals(expected, Db.count(Wrappers.lambdaQuery(new Entity()))); assertEquals(expected, Db.count(Wrappers.lambdaQuery(Entity.class))); } } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1,'ruben'),(2,'chocolate');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/JdbcUtilsTest.java ================================================ package com.baomidou.mybatisplus.test.toolkit; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Jdbc 工具类测试 * * @author hubin * @since 2020-09-15 */ public class JdbcUtilsTest { @Test public void testPattern(){ String regex = ":dm\\d*:"; Assertions.assertTrue(JdbcUtils.regexFind(regex, ":dm:")); Assertions.assertTrue(JdbcUtils.regexFind(regex, ":dm8:")); Assertions.assertTrue(JdbcUtils.regexFind(regex, "123:dm6:abc")); Assertions.assertTrue(JdbcUtils.regexFind(regex, ":dm7:abc")); Assertions.assertTrue(JdbcUtils.regexFind(regex, "a12ds:dm71:")); Assertions.assertFalse(JdbcUtils.regexFind(regex, "a12ds:dmc1:abc")); } @Test void testGetDbType(){ Assertions.assertEquals(DbType.GAUSS_DB, JdbcUtils.getDbType("jdbc:gaussdb://127.0.0.1:8000/baomidou")); // zenith 为第三方驱动,非官方标准驱动 Assertions.assertEquals(DbType.GAUSS, JdbcUtils.getDbType("jdbc:zenith://127.0.0.1:8000/baomidou")); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/SimpleQueryTest.java ================================================ package com.baomidou.mybatisplus.test.toolkit; import static com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery; import static java.util.stream.Collectors.*; import static java.util.stream.Collectors.mapping; import static org.apache.ibatis.util.MapUtil.entry; import static org.assertj.core.api.Assertions.assertThat; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.toolkit.SimpleQuery; import com.baomidou.mybatisplus.test.BaseDbTest; import com.baomidou.mybatisplus.test.rewrite.Entity; import com.baomidou.mybatisplus.test.rewrite.EntityMapper; import org.junit.jupiter.api.Test; import java.util.*; /** * 简单查询工具类测试 * * @author * @since 2021/11/9 18:30 */ class SimpleQueryTest extends BaseDbTest { @Test void testList() { // 我要这张表里的ids List entityIds = SimpleQuery.list(lambdaQuery(), Entity::getId); assertThat(entityIds).containsExactly(1L, 2L); // 可叠加后续操作 List names = SimpleQuery.list(lambdaQuery(), Entity::getName, e -> Optional.ofNullable(e.getName()) .map(String::toUpperCase) .ifPresent(e::setName)); assertThat(names).containsExactly("RUBEN", null); } @Test void testMap() { // 我要这个表里对应条件的用户,用id作为key给我一个map Map idEntityMap = SimpleQuery.keyMap( Wrappers.lambdaQuery().eq(Entity::getId, 1L), Entity::getId); // 校验结果 Entity entity = new Entity(); entity.setId(1L); entity.setName("ruben"); Assert.isTrue(idEntityMap.equals(Collections.singletonMap(1L, entity)), "Ops!"); // 如果我只想要id和name组成的map Map idNameMap = SimpleQuery.map(lambdaQuery(), Entity::getId, Entity::getName); // 校验结果 Map map = new HashMap<>(1 << 2); map.put(1L, "ruben"); map.put(2L, null); Assert.isTrue(idNameMap.equals(map), "Ops!"); } @Test void testGroup() { // 我需要相同名字的用户的分为一组,再造一条数据 doTestAutoCommit(m -> { Entity entity = new Entity(); entity.setId(3L); entity.setName("ruben"); m.insert(entity); }); // 简单查询 Map> nameUsersMap = SimpleQuery.group(lambdaQuery(), Entity::getName); // 校验结果 Map> map = new HashMap<>(1 << 2); Entity chao = new Entity(); chao.setId(2L); chao.setName(null); map.put(null, Collections.singletonList(chao)); Entity ruben = new Entity(); ruben.setId(1L); ruben.setName("ruben"); Entity ruben2 = new Entity(); ruben2.setId(3L); ruben2.setName("ruben"); map.put("ruben", Arrays.asList(ruben, ruben2)); Assert.isTrue(nameUsersMap.equals(map), "Ops!"); // 解锁高级玩法: // 获取Map> Map> nameIdMap = SimpleQuery.group(lambdaQuery(), Entity::getName, mapping(Entity::getId, toList())); assertThat(nameIdMap).containsExactly(entry(null, Arrays.asList(2L)), entry("ruben", Arrays.asList(1L, 3L))); // 获取Map Map nameCountMap = SimpleQuery.group(lambdaQuery(), Entity::getName, counting()); assertThat(nameCountMap).containsExactly(entry(null, 1L), entry("ruben", 2L)); // ...超多花样 } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1, 'ruben'), (2, null);"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/SqlHelperTest.java ================================================ package com.baomidou.mybatisplus.test.toolkit; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; import com.baomidou.mybatisplus.test.BaseDbTest; import com.baomidou.mybatisplus.test.rewrite.Entity; import com.baomidou.mybatisplus.test.rewrite.EntityMapper; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; /** * SqlHelper 工具类测试 * * @author * @since 2020-09-15 */ public class SqlHelperTest extends BaseDbTest { @Test public void testGetMapperAndExecute() { List entityList = SqlHelper.execute(Entity.class, m -> m.selectList(Wrappers.lambdaQuery())); Entity ruben = new Entity(); ruben.setId(1L); ruben.setName("ruben"); Entity aChao = new Entity(); aChao.setId(2L); aChao.setName("a chao"); Assert.isTrue(entityList.equals(Arrays.asList(ruben, aChao)), "There is something wrong,please check your environment!"); } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1,'ruben'),(2,'a chao');"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (" + "id BIGINT NOT NULL," + "name VARCHAR(30) NULL DEFAULT NULL," + "PRIMARY KEY (id))"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/StringUtilsTest.java ================================================ package com.baomidou.mybatisplus.test.toolkit; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import org.junit.jupiter.api.Test; /** * 测试 StringUtils工具类测试 * * @author XiaoBingBy * @since 2019-08-30 */ class StringUtilsTest { @Test void isBlankTest() { Assert.isTrue(StringUtils.isBlank(""), "error not empty"); Assert.isTrue(StringUtils.isBlank(null), "error not empty"); Assert.isTrue(StringUtils.isBlank(" "), "error not empty"); } @Test void sqlInjectionBlackTest(){ String originalStr = "(select*from(select sleep(if(length(database())=13,3,0))union/**/select+1)a)" ; Assert.isTrue("(selectfrom(selectsleep(if(length(database())13,3,0))union//select1)a)" .equals(StringUtils.sqlInjectionReplaceBlank(originalStr)),"error"); } @Test void sqlInjectionBlackCharTest(){ Assert.isTrue("".equals(StringUtils.sqlInjectionReplaceBlank("'\"<>&*+=#-; \n\t")),"error"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/DeleteByIdDto.java ================================================ package com.baomidou.mybatisplus.test.uuid; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @NoArgsConstructor @AllArgsConstructor public class DeleteByIdDto implements Serializable { private T id; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDEntity.java ================================================ package com.baomidou.mybatisplus.test.uuid; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.util.UUID; @Data @TableName("entity") public class UUIDEntity { @TableId(value = "id",type = IdType.INPUT) private UUID id; private String name; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.uuid; import com.baomidou.mybatisplus.core.mapper.BaseMapper; public interface UUIDEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDEntityTest.java ================================================ package com.baomidou.mybatisplus.test.uuid; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.extension.toolkit.SqlRunner; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; public class UUIDEntityTest extends BaseDbTest { @Test void test() { doTest(m -> { var uuidEntity = new UUIDEntity(); uuidEntity.setId(UUID.randomUUID()); uuidEntity.setName("test1"); Assertions.assertEquals(1, m.insert(uuidEntity)); Assertions.assertNotNull(m.selectById(uuidEntity.getId())); Assertions.assertEquals(1, m.deleteById(uuidEntity)); }); doTest(m -> { var uuidEntity = new UUIDEntity(); uuidEntity.setId(UUID.randomUUID()); uuidEntity.setName("test2"); Assertions.assertEquals(1, m.insert(uuidEntity)); Assertions.assertEquals(1, m.deleteByIds(List.of(uuidEntity.getId()))); }); doTest(m -> { var uuidEntity = new UUIDEntity(); uuidEntity.setId(UUID.randomUUID()); uuidEntity.setName("test3"); Assertions.assertEquals(1, m.insert(uuidEntity)); Assertions.assertEquals(1, m.deleteByIds(List.of(uuidEntity))); }); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteByIds( List.of( UUID.randomUUID().toString(), UUID.randomUUID(), 123, 321L, Map.of("id", UUID.randomUUID()), Map.of("id", UUID.randomUUID().toString()), new DeleteByIdDto<>(UUID.randomUUID()), new DeleteByIdDto<>(UUID.randomUUID().toString()) )))); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(UUID.randomUUID()))); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(UUID.randomUUID().toString()))); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new UUIDEntity(){}))); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new DeleteByIdDto<>(UUID.randomUUID())))); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new DeleteByIdDto<>(UUID.randomUUID().toString())))); doTest(m -> Assertions.assertDoesNotThrow(()-> SqlRunner.db(UUIDEntity.class).delete("delete from entity where id = {0}", UUID.randomUUID()))); } @Override protected Consumer consumer() { return configuration -> configuration.getTypeHandlerRegistry().register(UUIDTypeHandler.class); } @Override protected String tableDataSql() { return "insert into entity values('0824eb71-e124-5ba1-56b9-87185d91f309','test')"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (\n" + "id VARCHAR(50) NOT NULL,\n" + "name VARCHAR(30) NULL DEFAULT NULL,\n" + "PRIMARY KEY (id)" + ")"); } @Override protected GlobalConfig globalConfig() { return super.globalConfig().setEnableSqlRunner(true); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDLogicEntity.java ================================================ package com.baomidou.mybatisplus.test.uuid; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.util.UUID; @Data @TableName("entity") public class UUIDLogicEntity { @TableId(value = "id",type = IdType.INPUT) private UUID id; private String name; @TableField(fill = FieldFill.UPDATE) private String deleteBy; @TableField(fill = FieldFill.UPDATE) @TableLogic(delval = "true", value = "false") private Boolean deleted; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDLogicEntityMapper.java ================================================ package com.baomidou.mybatisplus.test.uuid; import com.baomidou.mybatisplus.core.mapper.BaseMapper; public interface UUIDLogicEntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDLogicEntityTest.java ================================================ package com.baomidou.mybatisplus.test.uuid; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; public class UUIDLogicEntityTest extends BaseDbTest { @Test void test() { doTest(m -> { var uuidEntity = new UUIDLogicEntity(); uuidEntity.setId(UUID.randomUUID()); uuidEntity.setName("test1"); uuidEntity.setDeleted(false); Assertions.assertEquals(1, m.insert(uuidEntity)); Assertions.assertNotNull(m.selectById(uuidEntity.getId())); Assertions.assertEquals(1, m.deleteById(uuidEntity)); }); doTest(m -> { var uuidEntity = new UUIDLogicEntity(); uuidEntity.setId(UUID.randomUUID()); uuidEntity.setName("test2"); uuidEntity.setDeleted(false); Assertions.assertEquals(1, m.insert(uuidEntity)); Assertions.assertEquals(1, m.deleteByIds(List.of(uuidEntity.getId()))); }); doTest(m -> { var uuidEntity = new UUIDLogicEntity(); uuidEntity.setId(UUID.randomUUID()); uuidEntity.setName("test3"); uuidEntity.setDeleted(false); Assertions.assertEquals(1, m.insert(uuidEntity)); Assertions.assertEquals(1, m.deleteByIds(List.of(uuidEntity))); }); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteByIds( List.of( UUID.randomUUID().toString(), UUID.randomUUID(), 123, 321L, Map.of("id", UUID.randomUUID()), Map.of("id", UUID.randomUUID().toString()), new DeleteByIdDto<>(UUID.randomUUID()), new DeleteByIdDto<>(UUID.randomUUID().toString()) )))); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(UUID.randomUUID()))); doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new UUIDLogicEntity(){}))); // TODO 下面三种类型无法转为UUID,看是否增加一个类型转换器让用户能注册处理自己的类型转换 // doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(UUID.randomUUID().toString()))); // doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new DeleteByIdDto<>(UUID.randomUUID())))); // doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new DeleteByIdDto<>(UUID.randomUUID().toString())))); } @Override protected Consumer consumer() { return configuration -> configuration.getTypeHandlerRegistry().register(UUIDTypeHandler.class); } @Override protected GlobalConfig globalConfig() { return super.globalConfig().setMetaObjectHandler(new MetaObjectHandler() { @Override public void insertFill(MetaObject metaObject) { } @Override public void updateFill(MetaObject metaObject) { metaObject.setValue("deleteBy", "baomidou"); } }); } @Override protected String tableDataSql() { return "insert into entity values('0824eb71-e124-5ba1-56b9-87185d91f309','test',null, false)"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (\n" + "id VARCHAR(50) NOT NULL,\n" + "name VARCHAR(30) NULL DEFAULT NULL,\n" + "delete_by VARCHAR(30) NULL DEFAULT NULL," + "deleted BOOLEAN NOT NULL DEFAULT false," + "PRIMARY KEY (id)" + ")"); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDTypeHandler.java ================================================ package com.baomidou.mybatisplus.test.uuid; import org.apache.ibatis.type.*; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.UUID; @MappedTypes(UUID.class) @MappedJdbcTypes(JdbcType.VARCHAR) public class UUIDTypeHandler extends BaseTypeHandler { @Override public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) throws SQLException { ps.setObject(i, parameter); } @Override public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException { String uuidStr = rs.getString(columnName); return uuidStr == null ? null : UUID.fromString(uuidStr); } @Override public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String uuidStr = rs.getString(columnIndex); return uuidStr == null ? null : UUID.fromString(uuidStr); } @Override public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String uuidStr = cs.getString(columnIndex); return uuidStr == null ? null : UUID.fromString(uuidStr); } } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/version/Entity.java ================================================ package com.baomidou.mybatisplus.test.version; import com.baomidou.mybatisplus.annotation.Version; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; /** * @author miemie * @since 2020-06-23 */ @Data @Accessors(chain = true) public class Entity implements Serializable { private static final long serialVersionUID = 6962439201546719734L; private Long id; private String name; @Version private Integer version; } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/version/EntityMapper.java ================================================ package com.baomidou.mybatisplus.test.version; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * @author miemie * @since 2020-06-23 */ public interface EntityMapper extends BaseMapper { } ================================================ FILE: mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/version/VersionTest.java ================================================ package com.baomidou.mybatisplus.test.version; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.test.BaseDbTest; import org.apache.ibatis.plugin.Interceptor; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import lombok.extern.slf4j.Slf4j; /** * @author miemie * @author raylax * @since 2020-07-04 */ @Slf4j public class VersionTest extends BaseDbTest { @Test void testWrapperMode() { log.info("[wrapper mode] test"); doTestAutoCommit(i -> { int result = i.update(null, Wrappers.update() .eq("id", 3) .set("version", 1) ); assertThat(result).as("[wrapper mode] 设置version值成功").isEqualTo(1); }); doTestAutoCommit(i -> { int result = i.update(null, Wrappers.update() .eq("id", 3) .eq("version", 1) ); assertThat(result).as("[wrapper mode] 设置version值匹配更新成功").isEqualTo(1); final Entity entity = i.selectById(3); assertThat(entity.getVersion()).isEqualTo(2); }); doTestAutoCommit(i -> { int result = i.update(null, Wrappers.update() .eq("id", 3) .eq("version", 1) ); assertThat(result).as("[wrapper mode] 设置version值匹配更新失败").isEqualTo(0); final Entity entity = i.selectById(3); assertThat(entity.getVersion()).isEqualTo(2); }); } @Test void test() { doTestAutoCommit(i -> { int result = i.updateById(new Entity().setId(1L).setName("老张")); assertThat(result).as("没放入version值更新成功").isEqualTo(1); }); doTestAutoCommit(i -> { int result = i.updateById(new Entity().setId(1L).setName("老张").setVersion(1)); assertThat(result).as("放入的version值不匹配更新失败").isEqualTo(0); }); doTestAutoCommit(i -> { Entity entity = new Entity().setId(1L).setName("老张").setVersion(0); int result = i.updateById(entity); assertThat(result).as("放入的version值匹配更新成功").isEqualTo(1); assertThat(entity.getVersion()).isEqualTo(1); }); doTestAutoCommit(i -> { int result = i.update(new Entity().setName("老张"), Wrappers.update().eq("id", 2)); assertThat(result).as("没放入version值更新成功").isEqualTo(1); }); doTestAutoCommit(i -> { int result = i.update(new Entity().setName("老张").setVersion(1), Wrappers.update().eq("id", 2)); assertThat(result).as("放入的version值不匹配更新失败").isEqualTo(0); }); doTestAutoCommit(i -> { Entity entity = new Entity().setName("老张").setVersion(0); int result = i.update(entity, Wrappers.update().eq("id", 2)); assertThat(result).as("放入的version值匹配更新成功").isEqualTo(1); assertThat(entity.getVersion()).isEqualTo(1); }); } @Override protected List interceptors() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor(true)); return Collections.singletonList(interceptor); } @Override protected String tableDataSql() { return "insert into entity(id,name) values(1,'老王'),(2,'老李'),(3,'老赵')"; } @Override protected List tableSql() { return Arrays.asList("drop table if exists entity", "CREATE TABLE IF NOT EXISTS entity (\n" + "id BIGINT(20) NOT NULL,\n" + "name VARCHAR(30) NULL DEFAULT NULL,\n" + "version integer NOT NULL DEFAULT 0,\n" + "PRIMARY KEY (id)" + ")"); } } ================================================ FILE: mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/KtTestConfig.kt ================================================ package com.baomidou.mybatisplus.test.h2 import com.baomidou.mybatisplus.core.MybatisConfiguration import com.baomidou.mybatisplus.core.config.GlobalConfig import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean import com.baomidou.mybatisplus.test.h2.config.DBConfig import org.apache.ibatis.session.ExecutorType import org.apache.ibatis.session.SqlSessionFactory import org.apache.ibatis.type.EnumOrdinalTypeHandler import org.apache.ibatis.type.JdbcType import org.mybatis.spring.annotation.MapperScan import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Import import javax.sql.DataSource /** * @author nieqiurong */ @Configuration @Import(DBConfig::class) @ComponentScan("com.baomidou.mybatisplus.test.h2.kotlin") @MapperScan("com.baomidou.mybatisplus.test.h2.kotlin.mapper") open class KtTestConfig { @Bean("sqlSessionFactory") open fun sqlSessionFactory(dataSource: DataSource): SqlSessionFactory? { val sqlSessionFactory = MybatisSqlSessionFactoryBean() sqlSessionFactory.setDataSource(dataSource) val configuration = MybatisConfiguration() configuration.jdbcTypeForNull = JdbcType.NULL configuration.isMapUnderscoreToCamelCase = true configuration.defaultExecutorType = ExecutorType.REUSE configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler::class.java) //默认枚举处理 sqlSessionFactory.configuration = configuration val mybatisPlusInterceptor = MybatisPlusInterceptor() mybatisPlusInterceptor.addInnerInterceptor(PaginationInnerInterceptor()) sqlSessionFactory.setPlugins(mybatisPlusInterceptor) val globalConfig = GlobalConfig() globalConfig.setMetaObjectHandler(MyMetaObjectHandler()) globalConfig.setSqlInjector(DefaultSqlInjector()) sqlSessionFactory.setGlobalConfig(globalConfig) return sqlSessionFactory.getObject() } } ================================================ FILE: mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/MetaObjectHandlerTest.kt ================================================ package com.baomidou.mybatisplus.test.h2 import com.baomidou.mybatisplus.annotation.FieldFill import com.baomidou.mybatisplus.annotation.TableField import com.baomidou.mybatisplus.core.MybatisConfiguration import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler import com.baomidou.mybatisplus.core.metadata.TableInfoHelper import org.apache.ibatis.builder.MapperBuilderAssistant import org.apache.ibatis.reflection.MetaObject import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import java.math.BigDecimal import java.math.BigInteger import java.sql.Time import java.sql.Timestamp import java.time.LocalDate import java.time.LocalDateTime import java.util.* /** * * @author nieqiurong */ class MetaObjectHandlerTest { @Test fun println() { val clzs = arrayOf( String::class, Long::class, Int::class, Double::class, Float::class, Short::class, Byte::class, Boolean::class, Date::class, Time::class, Timestamp::class, java.sql.Date::class, LocalDate::class, LocalDateTime::class, BigInteger::class, BigDecimal::class, BigInteger::class ) for (clz in clzs) { println("kotlinType:" + clz.simpleName + "----->" + "JavaObjectType:" + clz.javaObjectType.name + "---->" + "JavaType:" + clz.java.name) } } @Test fun test() { val configuration = MybatisConfiguration() val mapperBuilderAssistant = MapperBuilderAssistant(configuration, "") val tableInfo = TableInfoHelper.initTableInfo(mapperBuilderAssistant, Demo::class.java) for (tableFieldInfo in tableInfo.fieldList) { println(tableFieldInfo.property + "----->" + tableFieldInfo.propertyType.name) } val demo = Demo() val metaObjectHandler = object : MetaObjectHandler { override fun insertFill(metaObject: MetaObject) { this.strictInsertFill(metaObject, "testString", String::class.java, "123") this.strictInsertFill(metaObject, "testLong", Long::class.javaObjectType, 123456L) this.strictInsertFill(metaObject, "testInt", Int::class.javaObjectType, 123) this.strictInsertFill( metaObject, "testLocalDateTime", LocalDateTime::class.javaObjectType, LocalDateTime.now() ) this.strictInsertFill(metaObject, "testBoolean", Boolean::class.javaObjectType, false) this.strictInsertFill(metaObject, "testDate", Date::class.javaObjectType, Date()) this.strictInsertFill(metaObject, "testLocalDate", LocalDate::class.javaObjectType, LocalDate.now()) } override fun updateFill(metaObject: MetaObject) { } } val metaObject: MetaObject = configuration.newMetaObject(demo) metaObjectHandler.insertFill(metaObject) Assertions.assertNotNull(demo.testString) Assertions.assertNotNull(demo.testInt) Assertions.assertNotNull(demo.testLong) Assertions.assertNotNull(demo.testDate) Assertions.assertNotNull(demo.testLocalDateTime) Assertions.assertNotNull(demo.testBoolean) Assertions.assertNotNull(demo.testLocalDate) println(demo) } } class Demo { @TableField(fill = FieldFill.INSERT_UPDATE) var testString: String? = null @TableField(fill = FieldFill.INSERT_UPDATE) var testInt: Int? = null @TableField(fill = FieldFill.INSERT_UPDATE) var testLong: Long? = null @TableField(fill = FieldFill.INSERT_UPDATE) var testDate: Date? = null @TableField(fill = FieldFill.INSERT_UPDATE) var testLocalDateTime: LocalDateTime? = null @TableField(fill = FieldFill.INSERT_UPDATE) var testBoolean: Boolean? = null @TableField(fill = FieldFill.INSERT_UPDATE) var testLocalDate: LocalDate? = null override fun toString(): String { return "Demo(testBoolean=$testBoolean, testString=$testString, testInt=$testInt, testLong=$testLong, testDate=$testDate, testLocalDateTime=$testLocalDateTime, testLocalDate=$testLocalDate)" } } ================================================ FILE: mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/MyMetaObjectHandler.kt ================================================ package com.baomidou.mybatisplus.test.h2 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler import org.apache.ibatis.reflection.MetaObject import java.time.LocalDateTime /** * @author nieqiurong */ class MyMetaObjectHandler : MetaObjectHandler { override fun insertFill(metaObject: MetaObject) { this.strictInsertFill(metaObject, "createdDt", LocalDateTime::class.javaObjectType, LocalDateTime.now()) } override fun updateFill(metaObject: MetaObject) { this.strictUpdateFill(metaObject, "lastUpdatedDt", LocalDateTime::class.javaObjectType, LocalDateTime.now()) } } ================================================ FILE: mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/KtH2UserTest.kt ================================================ package com.baomidou.mybatisplus.test.h2.kotlin import com.baomidou.mybatisplus.test.h2.KtTestConfig import com.baomidou.mybatisplus.test.h2.kotlin.entity.KtH2User import com.baomidou.mybatisplus.test.h2.enums.AgeEnum import com.baomidou.mybatisplus.test.h2.kotlin.service.KtH2UserService import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.junit.jupiter.SpringExtension /** * Kotlin h2user test * * @author FlyInWind * @since 2020/10/18 */ @ExtendWith(SpringExtension::class) @ContextConfiguration(classes = [KtTestConfig::class]) class KtH2UserTest { @Autowired private lateinit var userService: KtH2UserService @Test fun testSave() { val user = KtH2User() user.age = AgeEnum.ONE user.name = "Demo" userService.save(user) Assertions.assertNotNull(user.createdDt) } @Test fun testUpdate() { val user = KtH2User() user.age = AgeEnum.ONE user.name = "Demo" userService.save(user) user.name = "Update" userService.updateById(user) Assertions.assertNotNull(user.lastUpdatedDt) } @Test fun testDelete() { val user = KtH2User() user.age = AgeEnum.ONE user.name = "Delete" userService.save(user) userService.removeById(user) Assertions.assertNull(userService.getById(user.testId)) } @Test fun testServiceImplInnerKtChain() { var tomcat = userService.ktQuery().eq(KtH2User::name, "Tomcat").one() Assertions.assertNotNull(tomcat) Assertions.assertNotEquals(0, userService.ktQuery().like(KtH2User::name, "a").count()) val users = userService.ktQuery() .like(KtH2User::age, AgeEnum.TWO) .ne(KtH2User::version, 1) .isNull(KtH2User::price) .list() Assertions.assertTrue(users.isEmpty()) userService.ktUpdate() .set(KtH2User::name, "Tomcat2") .eq(KtH2User::name, "Tomcat") .update() tomcat = userService.ktQuery().eq(KtH2User::name, "Tomcat").one() Assertions.assertNull(tomcat) } } ================================================ FILE: mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/entity/KtH2User.kt ================================================ package com.baomidou.mybatisplus.test.h2.kotlin.entity import com.baomidou.mybatisplus.annotation.* import com.baomidou.mybatisplus.test.h2.enums.AgeEnum import java.math.BigDecimal import java.time.LocalDateTime import java.util.* @TableName("h2user") class KtH2User { @TableId var testId: Long? = null var name: String? = null var age: AgeEnum? = null var price: BigDecimal? = null var testType: Int? = null @TableField("`desc`") var desc: String? = null @TableField(select = false) var testDate: Date? = null @Version var version: Int? = null @TableLogic val deleted: Int? = null @TableField(fill = FieldFill.INSERT) var createdDt: LocalDateTime? = null @TableField(fill = FieldFill.UPDATE) var lastUpdatedDt: LocalDateTime? = null } ================================================ FILE: mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/mapper/KtUserMapper.kt ================================================ package com.baomidou.mybatisplus.test.h2.kotlin.mapper import com.baomidou.mybatisplus.core.mapper.BaseMapper import com.baomidou.mybatisplus.test.h2.kotlin.entity.KtH2User import org.apache.ibatis.annotations.Mapper @Mapper interface KtUserMapper : BaseMapper { } ================================================ FILE: mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/service/KtH2UserService.kt ================================================ package com.baomidou.mybatisplus.test.h2.kotlin.service import com.baomidou.mybatisplus.extension.service.IService import com.baomidou.mybatisplus.test.h2.kotlin.entity.KtH2User interface KtH2UserService : IService { } ================================================ FILE: mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/service/impl/KtH2UserServiceImpl.kt ================================================ package com.baomidou.mybatisplus.test.h2.kotlin.service.impl import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl import com.baomidou.mybatisplus.test.h2.kotlin.entity.KtH2User import com.baomidou.mybatisplus.test.h2.kotlin.mapper.KtUserMapper import com.baomidou.mybatisplus.test.h2.kotlin.service.KtH2UserService import org.springframework.stereotype.Service @Service class KtH2UserServiceImpl : ServiceImpl(), KtH2UserService { } ================================================ FILE: mybatis-plus/src/test/resources/cache/init.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS t_cache ( id BIGINT(20) NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); insert into t_cache values (1,'a'); insert into t_cache values (2,'b'); insert into t_cache values (3,'c'); insert into t_cache values (4,'d'); insert into t_cache values (5,'e'); ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/cache/package-info.java ================================================ /** * @author miemie * @since 2022-03-07 */ package com.baomidou.mybatisplus.test.cache; ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/cache/xml/XmlCacheMapper.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/enums/EntityMapper.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/fill/FillMapper.xml ================================================ insert into t_fill(id, name) values (#{item.id}, #{item.name}) insert into t_fill(id, name) values (#{item.id}, #{item.name}) insert into t_fill(id, name) values (#{item.id}, #{item.name}) insert into t_fill(id, name) values (#{item.id}, #{item.name}) ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/h2/mapper/H2StudentMapper.xml ================================================ insert into h2user(test_id, name, age, test_type) values (#{testId}, #{name}, #{age}, #{testType}) insert into h2user(test_id, name, age, test_type) values (#{et.testId}, #{et.name}, #{et.age}, #{et.testType}) insert into h2user(test_id, name, age, test_type) values (#{et.testId}, #{test}, #{et.age}, #{et.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) insert into h2user(test_id, name, age, test_type) values (#{et.testId}, #{et.name}, #{et.age}, #{et.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) insert into h2user(test_id, name, age, test_type) values (#{col.testId}, #{col.name}, #{col.age}, #{col.testType}) update h2user set deleted = 1,last_updated_dt = #{et.lastUpdatedDt} where test_id in ( #{col} ) update h2user set deleted = 1,last_updated_dt = #{et.lastUpdatedDt} where test_id in ( #{col} ) update h2user set deleted = 1,last_updated_dt = #{user.lastUpdatedDt} where test_id in ( #{col} ) update h2user set deleted = 1,last_updated_dt = #{et.lastUpdatedDt} where test_id in ( #{col} ) ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/puginsome/AMapper.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/puginsome/BMapper.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/resultmap/EntityMapper.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/rewrite/EntityMapper.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/customfilltest/init.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS t_fill_test ( id BIGINT(20) NOT NULL, a VARCHAR(30) NULL DEFAULT NULL , b VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); ================================================ FILE: mybatis-plus/src/test/resources/fillperformance/init.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS t_fill_performance ( id BIGINT(20) NOT NULL, a VARCHAR(30) NULL DEFAULT NULL , b VARCHAR(30) NULL DEFAULT NULL , c VARCHAR(30) NULL DEFAULT NULL , d VARCHAR(30) NULL DEFAULT NULL , e VARCHAR(30) NULL DEFAULT NULL , f VARCHAR(30) NULL DEFAULT NULL , g VARCHAR(30) NULL DEFAULT NULL , h VARCHAR(30) NULL DEFAULT NULL , i VARCHAR(30) NULL DEFAULT NULL , j VARCHAR(30) NULL DEFAULT NULL , k VARCHAR(30) NULL DEFAULT NULL , l VARCHAR(30) NULL DEFAULT NULL , m VARCHAR(30) NULL DEFAULT NULL , n VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-cache-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-custom-fill-test-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-fill-performance-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-id-generator-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-keygenerator-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-logic-delete-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-sharding-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-tenant-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-test-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/spring-test-xml-h2.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/h2/student.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS h2student ( id BIGINT(20) NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , grade INT(3) NULL, gender VARCHAR(10) NULL, age INT(11) NULL DEFAULT NULL , PRIMARY KEY (id) ); ================================================ FILE: mybatis-plus/src/test/resources/h2/student.insert.sql ================================================ delete from h2student; insert into h2student (id, name, age)values (1, 'Tom', 1); insert into h2student (id, name, age)values (2, 'Jerry', 1); insert into h2student (id, name, age)values (12, '要开除的学生', 1); insert into h2student (id, name, age)values (13, 'test1', 1); insert into h2student (id, name, age)values (14, 'test2', 1); insert into h2student (id, name, age)values (15, 'test3', 1); ================================================ FILE: mybatis-plus/src/test/resources/h2/user.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS h2user ( test_id BIGINT(20) NOT NULL AUTO_INCREMENT, name VARCHAR(30) NULL DEFAULT NULL , age INT(11) NULL DEFAULT NULL , test_type INT(11) NULL , test_date DATETIME NULL DEFAULT NULL, price DECIMAL(10,2) NULL DEFAULT NULL, desc VARCHAR(30) NULL DEFAULT NULL , version INT(5) NULL DEFAULT NULL, created_dt TIMESTAMP NULL, last_updated_dt TIMESTAMP NULL, deleted INT(1) NULL DEFAULT 0 , PRIMARY KEY (test_id) ); COMMENT ON COLUMN h2user.test_id IS 'PK'; COMMENT ON COLUMN h2user.name IS 'USERNAME'; ================================================ FILE: mybatis-plus/src/test/resources/h2/user.insert.sql ================================================ delete from h2user; insert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (101, 'Tomcat', '2017-1-1 1:1:1', 3, 9.99, 1, 1, 0); insert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (102, 'Jerry', '2017-3-1 1:1:1', 2, 19.99, 2, 2, 0); insert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (103, 'Bob', '2017-4-1 1:1:1', 1, 99.99, 3, 3, 0); insert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (104, 'Joe', '2017-2-1 1:1:1', 1, 1.99, 1, 4, 0); insert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (105, 'Tony', '2017-2-1 1:1:1', 3, 1.99, 1, 5, 0); insert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (1010, '1010', '2017-2-1 1:1:1', 3, 1.99, 1, 5, 0); insert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (1011, '1011', '2017-2-1 1:1:1', 3, 1.99, 1, 5, 0); insert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (1012, '1012', '2017-2-1 1:1:1', 3, 1.99, 1, 5, 0); ================================================ FILE: mybatis-plus/src/test/resources/hbase-site.xml ================================================ phoenix.schema.isNamespaceMappingEnabled true ================================================ FILE: mybatis-plus/src/test/resources/idgenerator/init.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS t_id_generator_long ( id BIGINT(20) NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS t_id_generator_bigdecimal ( id BIGINT(20) NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS t_id_generator_biginteger ( id BIGINT(20) NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS t_id_generator_long_string ( id varchar NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS t_id_generator_int ( id int NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS t_id_generator_int_string ( id varchar NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS t_id_generator_string ( id varchar (50) NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); ================================================ FILE: mybatis-plus/src/test/resources/issues/genericid/spring.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/issues/genericid/sql/init.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS t_i171cq_long ( id BIGINT(20) NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS t_i171cq_string ( id varchar NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); ================================================ FILE: mybatis-plus/src/test/resources/keygenerator/init.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS key_generator_model ( id BIGINT(20) NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (id) ); CREATE SEQUENCE key_generator_model_seq START WITH 1 INCREMENT BY 1; ================================================ FILE: mybatis-plus/src/test/resources/logback.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: mybatis-plus/src/test/resources/mybatis-config-object-factory.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/mybatis-config.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/phoenix/spring-test-phoenix.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/phoenix/test_info.ddl.sql ================================================ -- DROP SCHEMA TEST; -- CREATE SCHEMA TEST; USE TEST; DROP TABLE IF EXISTS TEST_INFO; CREATE TABLE TEST_INFO ( id INTEGER NOT NULL PRIMARY KEY, name VARCHAR, phone VARCHAR, position VARCHAR, department VARCHAR, company VARCHAR, file_name VARCHAR, pos_dep_com VARCHAR, gmt_updated DATE, gmt_create DATE ); ================================================ FILE: mybatis-plus/src/test/resources/postgresql/spring-test-postgresql.xml ================================================ ================================================ FILE: mybatis-plus/src/test/resources/tenant/init.ddl.sql ================================================ CREATE TABLE IF NOT EXISTS student ( id BIGINT(20) NOT NULL, name VARCHAR(30) NULL DEFAULT NULL , tenant_id BIGINT(20) NOT NULL , PRIMARY KEY (id) ); insert into student values (1,'a',1); insert into student values (2,'b',2); ================================================ FILE: mybatis-plus-annotation/build.gradle ================================================ dependencies { implementation "${lib.mybatis}" } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/DbType.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import lombok.AllArgsConstructor; import lombok.Getter; /** * MybatisPlus 支持的数据库类型,主要用于分页方言 * * @author hubin * @since 2018-06-23 */ @Getter @AllArgsConstructor public enum DbType { /** * MYSQL */ MYSQL("mysql", "MySql数据库"), /** * MARIADB */ MARIADB("mariadb", "MariaDB数据库"), /** * ORACLE */ ORACLE("oracle", "Oracle11g及以下数据库(高版本推荐使用ORACLE_NEW)"), /** * oracle12c new pagination */ ORACLE_12C("oracle12c", "Oracle12c+数据库"), /** * DB2 */ DB2("db2", "DB2数据库"), /** * H2 */ H2("h2", "H2数据库"), /** * HSQL */ HSQL("hsql", "HSQL数据库"), /** * SQLITE */ SQLITE("sqlite", "SQLite数据库"), /** * POSTGRE */ POSTGRE_SQL("postgresql", "Postgre数据库"), /** * SQLSERVER2005 */ SQL_SERVER2005("sqlserver2005", "SQLServer2005数据库"), /** * SQLSERVER */ SQL_SERVER("sqlserver", "SQLServer数据库"), /** * DM */ DM("dm", "达梦数据库"), /** * xugu */ XU_GU("xugu", "虚谷数据库"), /** * Kingbase */ KINGBASE_ES("kingbasees", "人大金仓数据库"), /** * Phoenix */ PHOENIX("phoenix", "Phoenix HBase数据库"), /** * Gauss *

* 低版本为 zenith,为贡献者提供,非标准官方驱动,3.5.11 修改成 gauss *

* * @see #GAUSS_DB * @deprecated 3.5.13 使用官方标准名称 */ @Deprecated GAUSS("gauss", "Gauss 数据库"), /** * GaussDB * * @since 3.5.13 */ GAUSS_DB("gaussDB", "GaussDB 数据库"), /** * ClickHouse */ CLICK_HOUSE("clickhouse", "clickhouse 数据库"), /** * GBase */ GBASE("gbase", "南大通用(华库)数据库"), /** * GBase-8s */ GBASE_8S("gbase-8s", "南大通用数据库 GBase 8s"), /** * use {@link #GBASE_8S} * * @deprecated 2022-05-30 */ @Deprecated GBASEDBT("gbasedbt", "南大通用数据库"), /** * use {@link #GBASE_8S} * * @deprecated 2022-05-30 */ @Deprecated GBASE_INFORMIX("gbase 8s", "南大通用数据库 GBase 8s"), /** * GBase8sPG */ GBASE8S_PG("gbase8s-pg", "南大通用数据库 GBase 8s兼容pg"), /** * GBase8c */ GBASE_8C("gbase8c", "南大通用数据库 GBase 8c"), /** * Sinodb */ SINODB("sinodb", "星瑞格数据库"), /** * Oscar */ OSCAR("oscar", "神通数据库"), /** * Sybase */ SYBASE("sybase", "Sybase ASE 数据库"), /** * OceanBase */ OCEAN_BASE("oceanbase", "OceanBase 数据库"), /** * Firebird */ FIREBIRD("Firebird", "Firebird 数据库"), /** * HighGo */ HIGH_GO("highgo", "瀚高数据库"), /** * CUBRID */ CUBRID("cubrid", "CUBRID数据库"), /** * SUNDB */ SUNDB("sundb", "SUNDB数据库"), /** * Hana */ SAP_HANA("hana", "SAP_HANA数据库"), /** * Impala */ IMPALA("impala", "impala数据库"), /** * Vertica */ VERTICA("vertica", "vertica数据库"), /** * xcloud */ XCloud("xcloud", "行云数据库"), /** * redshift */ REDSHIFT("redshift", "亚马逊redshift数据库"), /** * openGauss */ OPENGAUSS("openGauss", "华为 opengauss 数据库"), /** * TDengine */ TDENGINE("TDengine", "TDengine数据库"), /** * Informix */ INFORMIX("informix", "Informix数据库"), /** * uxdb */ UXDB("uxdb", "优炫数据库"), /** * lealone */ LEALONE("lealone", "Lealone数据库"), /** * trino */ TRINO("trino", "Trino数据库"), /** * presto */ PRESTO("presto", "Presto数据库"), /** * derby */ DERBY("derby", "Derby数据库"), /** * vastbase */ VASTBASE("vastbase", "Vastbase数据库"), /** * goldendb */ GOLDENDB("goldendb", "GoldenDB数据库"), /** * duckdb */ DUCKDB("duckdb", "duckdb数据库"), /** * yasdb */ YASDB("yasdb", "崖山数据库"), /** * Hadoop的数据仓库 */ HIVE2("hive2", "Hadoop数据仓库"), /** * UNKNOWN DB */ OTHER("other", "其他数据库"); /** * 数据库名称 */ private final String db; /** * 描述 */ private final String desc; /** * 获取数据库类型 * * @param dbType 数据库类型字符串 */ public static DbType getDbType(String dbType) { for (DbType type : DbType.values()) { if (type.db.equalsIgnoreCase(dbType)) { return type; } } return OTHER; } public boolean mysqlSameType() { return this == DbType.MYSQL || this == DbType.MARIADB || this == DbType.GBASE || this == DbType.OSCAR || this == DbType.XU_GU || this == DbType.CLICK_HOUSE || this == DbType.OCEAN_BASE || this == DbType.CUBRID || this == DbType.SUNDB || this == DbType.GOLDENDB || this == DbType.YASDB; } public boolean oracleSameType() { return this == DbType.ORACLE || this == DbType.DM || this == DbType.GAUSS; } public boolean postgresqlSameType() { return this == DbType.POSTGRE_SQL || this == DbType.H2 || this == DbType.LEALONE || this == DbType.SQLITE || this == DbType.HSQL || this == DbType.KINGBASE_ES || this == DbType.PHOENIX || this == DbType.SAP_HANA || this == DbType.IMPALA || this == DbType.HIGH_GO || this == DbType.VERTICA || this == DbType.REDSHIFT || this == DbType.OPENGAUSS || this == DbType.TDENGINE || this == DbType.UXDB || this == DbType.GBASE8S_PG || this == DbType.GBASE_8C || this == DbType.VASTBASE || this == DbType.DUCKDB; } } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/EnumValue.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.lang.annotation.*; /** * 支持普通枚举类字段, 只用在enum类的字段上 *

当实体类的属性是普通枚举,且是其中一个字段,使用该注解来标注枚举类里的那个属性对应字段

*

* 使用方式参考 com.baomidou.mybatisplus.test.h2.H2StudentMapperTest *

 * @TableName("student")
 * class Student {
 *     private Integer id;
 *     private String name;
 *     private GradeEnum grade;//数据库grade字段类型为int
 * }
 *
 * public enum GradeEnum {
 *     PRIMARY(1,"小学"),
 *     SECONDORY("2", "中学"),
 *     HIGH(3, "高中");
 *
 *     @EnumValue
 *     private final int code;
 *     private final String descp;
 * }
 * 
*

* * @author yuxiaobin * @date 2018/8/30 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface EnumValue { } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/FieldFill.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; /** * 字段填充策略枚举类 * *

* 判断注入的 insert 和 update 的 sql 脚本是否在对应情况下忽略掉字段的 if 标签生成 * ...... * 判断优先级比 {@link FieldStrategy} 高 *

* * @author hubin * @since 2017-06-27 */ public enum FieldFill { /** * 默认不处理 */ DEFAULT, /** * 插入时填充字段 */ INSERT, /** * 更新时填充字段 */ UPDATE, /** * 插入和更新时填充字段 */ INSERT_UPDATE } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/FieldStrategy.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; /** * 字段策略枚举类 *

* 如果字段是基本数据类型则最终效果等同于 {@link #ALWAYS} * * @author hubin * @since 2016-09-09 */ public enum FieldStrategy { /** * 任何时候都加入 SQL */ ALWAYS, /** * 非NULL判断 */ NOT_NULL, /** * 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断) */ NOT_EMPTY, /** * 默认的,一般只用于注解里 *

1. 在全局里代表 NOT_NULL

*

2. 在注解里代表 跟随全局

*/ DEFAULT, /** * 不加入 SQL */ NEVER } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/IEnum.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.io.Serializable; /** * 自定义枚举接口 * * @author hubin * @since 3.4.0 */ public interface IEnum { /** * 枚举数据库存储值 */ T getValue(); } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/IdType.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import lombok.Getter; /** * 生成ID类型枚举类 * * @author hubin * @since 2015-11-10 */ @Getter public enum IdType { /** * 数据库ID自增 *

该类型请确保数据库设置了 ID自增 否则无效

*/ AUTO(0), /** * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) */ NONE(1), /** * 用户输入ID *

该类型可以通过自己注册自动填充插件进行填充

*/ INPUT(2), /* 以下2种类型、只有当插入对象ID 为空,才自动填充。 */ /** * 分配ID (主键类型为number或string), * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法) * * @since 3.3.0 */ ASSIGN_ID(3), /** * 分配UUID (主键类型为 string) * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-","")) */ ASSIGN_UUID(4); private final int key; IdType(int key) { this.key = key; } } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/InterceptorIgnore.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.lang.annotation.*; /** * 内置插件的一些过滤规则 *

* 支持注解在 Mapper 上以及 Mapper.Method 上 * 同时存在则 Mapper.method 比 Mapper 优先级高 *

* 支持: * true 和 false , 1 和 0 , on 和 off *

* 各属性返回 true 表示不走插件(在配置了插件的情况下,不填则默认表示 false) * * @author miemie * @since 2020-07-31 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface InterceptorIgnore { /** * 行级租户 {@link com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor} */ String tenantLine() default ""; /** * 动态表名 {@link com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor} */ String dynamicTableName() default ""; /** * 攻击 SQL 阻断解析器,防止全表更新与删除 {@link com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor} */ String blockAttack() default ""; /** * 垃圾SQL拦截 {@link com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor} */ String illegalSql() default ""; /** * 数据权限 {@link com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor} *

* 默认关闭,需要注解打开 */ String dataPermission() default "1"; /** * 其他的 *

* 格式应该为: "key"+"@"+可选项[false,true,1,0,on,off] * 例如: "xxx@1" 或 "xxx@true" 或 "xxx@on" *

* 如果配置了该属性的注解是注解在 Mapper 上的,则如果该 Mapper 的一部分 Method 需要取反则需要在 Method 上注解并配置此属性为反值 * 例如: "xxx@1" 在 Mapper 上, 则 Method 上需要 "xxx@0" */ String[] others() default {}; } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/KeySequence.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 序列主键策略 *

oracle

* * @author zashitou * @since 2017.4.20 */ @Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) public @interface KeySequence { /** * 序列名 */ String value() default ""; /** * 数据库类型,未配置默认使用注入 IKeyGenerator 实现,多个实现必须指定 */ DbType dbType() default DbType.OTHER; } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/OrderBy.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.lang.annotation.*; /** * 自动排序,用法与SpringDtaJpa的OrderBy类似 * 在执行MybatisPlus的方法selectList(),Page()等非手写查询时自动带上. * @author Dervish * @date 2021-04-13 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface OrderBy { /** * 默认倒序,设置 true 顺序 */ boolean asc() default false; /** * 数字越小越靠前 */ short sort() default Short.MAX_VALUE; } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/SqlCondition.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; /** * SQL 比较条件常量定义类 * * @author hubin * @since 2018-01-05 */ public class SqlCondition { /** * 等于 */ public static final String EQUAL = "%s=#{%s}"; /** * 不等于 */ public static final String NOT_EQUAL = "%s<>#{%s}"; /** * % 两边 % */ public static final String LIKE = "%s LIKE CONCAT('%%',#{%s},'%%')"; /** * % 两边 % [oracle使用] */ public static final String ORACLE_LIKE = "%s LIKE CONCAT(CONCAT('%%',#{%s}),'%%')"; /** * % 左 */ public static final String LIKE_LEFT = "%s LIKE CONCAT('%%',#{%s})"; /** * 右 % */ public static final String LIKE_RIGHT = "%s LIKE CONCAT(#{%s},'%%')"; } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableField.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ResultMapping; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.UnknownTypeHandler; import java.lang.annotation.*; /** * 表字段标识 * * @author hubin sjy tantan * @since 2016-09-09 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface TableField { /** * 数据库字段值 *

* 不需要配置该值的情况: *

  • 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 true 时, * (mp下默认是true,mybatis默认是false), 数据库字段值.replace("_","").toUpperCase() == 实体属性名.toUpperCase()
  • *
  • 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 false 时, * 数据库字段值.toUpperCase() == 实体属性名.toUpperCase()
  • */ String value() default ""; /** * 是否为数据库表字段 *

    * 默认 true 存在,false 不存在 */ boolean exist() default true; /** * 字段 where 实体查询比较条件 *

    * 默认 {@link SqlCondition#EQUAL} */ String condition() default ""; /** * 字段 update set 部分注入, 该注解优于 el 注解使用 *

    * 例1:@TableField(.. , update="%s+1") 其中 %s 会填充为字段 * 输出 SQL 为:update 表 set 字段=字段+1 where ... *

    * 例2:@TableField(.. , update="now()") 使用数据库时间 * 输出 SQL 为:update 表 set 字段=now() where ... */ String update() default ""; /** * 字段验证策略之 insert: 当insert操作时,该字段拼接insert语句时的策略 *

    * ALWAYS: 直接拼接 insert into table_a(column) values (#{columnProperty}); * NOT_NULL: insert into table_a(column) values (#{columnProperty}) * NOT_EMPTY: insert into table_a(column) values (#{columnProperty}) * NEVER: 该字段不参加 insert 语句 *

    * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL * * @since 3.1.2 */ FieldStrategy insertStrategy() default FieldStrategy.DEFAULT; /** * 字段验证策略之 update: 当更新操作时,该字段拼接set语句时的策略 *

    * ALWAYS: 直接拼接 update table_a set column=#{columnProperty}, 属性为null/空string都会被set进去 * NOT_NULL: update table_a set column=#{columnProperty} * NOT_EMPTY: update table_a set column=#{columnProperty} * NEVER: 该字段不参加 update 语句 *

    * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL * * @since 3.1.2 */ FieldStrategy updateStrategy() default FieldStrategy.DEFAULT; /** * 字段验证策略之 where: 表示该字段在拼接where条件时的策略 *

    * ALWAYS: 直接拼接 column=#{columnProperty} * NOT_NULL: column=#{columnProperty} * NOT_EMPTY: column=#{columnProperty} * NEVER: 该字段不参加 where 语句 *

    * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL * * @since 3.1.2 */ FieldStrategy whereStrategy() default FieldStrategy.DEFAULT; /** * 字段自动填充策略 *

    * 在对应模式下将会忽略 insertStrategy 或 updateStrategy 的配置,等于断言该字段必有值 */ FieldFill fill() default FieldFill.DEFAULT; /** * 是否进行 select 查询 *

    * 大字段可设置为 false 不加入 select 查询范围 */ boolean select() default true; /** * 是否保持使用全局的 columnFormat 的值 *

    * 只生效于 既设置了全局的 columnFormat 也设置了上面 {@link #value()} 的值 * 如果是 false , 全局的 columnFormat 不生效 * * @since 3.1.1 */ boolean keepGlobalFormat() default false; /** * {@link ResultMapping#property} and {@link ParameterMapping#property} * * @since 3.4.4 */ String property() default ""; /** * JDBC类型 (该默认值不代表会按照该值生效), * 只生效于 mp 自动注入的 method, * 建议配合 {@link TableName#autoResultMap()} 一起使用 *

    * {@link ResultMapping#jdbcType} and {@link ParameterMapping#jdbcType} * * @since 3.1.2 */ JdbcType jdbcType() default JdbcType.UNDEFINED; /** * 类型处理器 (该默认值不代表会按照该值生效), * 只生效于 mp 自动注入的 method, * 建议配合 {@link TableName#autoResultMap()} 一起使用 *

    * {@link ResultMapping#typeHandler} and {@link ParameterMapping#typeHandler} * * @since 3.1.2 */ Class typeHandler() default UnknownTypeHandler.class; /** * 只在使用了 {@link #typeHandler()} 时判断是否辅助追加 javaType *

    * 一般情况下不推荐使用 * {@link ParameterMapping#javaType} * * @since 3.4.0 @2020-07-23 */ boolean javaType() default false; /** * 指定小数点后保留的位数, * 只生效于 mp 自动注入的 method, * 建议配合 {@link TableName#autoResultMap()} 一起使用 *

    * {@link ParameterMapping#numericScale} * * @since 3.1.2 */ String numericScale() default ""; } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableId.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.lang.annotation.*; /** * 表主键标识 * * @author hubin * @since 2016-01-23 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface TableId { /** * 字段名(该值可无) */ String value() default ""; /** * 主键类型 * {@link IdType} */ IdType type() default IdType.NONE; } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableLogic.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.lang.annotation.*; /** * 表字段逻辑处理注解(逻辑删除) * * @author hubin * @since 2017-09-09 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface TableLogic { /** * 默认逻辑未删除值(该值可无、会自动获取全局配置) */ String value() default ""; /** * 默认逻辑删除值(该值可无、会自动获取全局配置) */ String delval() default ""; } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableName.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.lang.annotation.*; /** * 数据库表相关 * * @author hubin, hanchunlin * @since 2016-01-23 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) public @interface TableName { /** * 实体对应的表名 */ String value() default ""; /** * schema *

    * 配置此值将覆盖全局配置的 schema * * @since 3.1.1 */ String schema() default ""; /** * 是否保持使用全局的 tablePrefix 的值 *

    只生效于 既设置了全局的 tablePrefix 也设置了上面 {@link #value()} 的值

    *
  • 如果是 false , 全局的 tablePrefix 不生效
  • * * @since 3.1.1 */ boolean keepGlobalPrefix() default false; /** * 实体映射结果集, * 只生效于 mp 自动注入的 method */ String resultMap() default ""; /** * 是否自动构建 resultMap 并使用, * 只生效于 mp 自动注入的 method, * 如果设置 resultMap 则不会进行 resultMap 的自动构建并注入, * 只适合个别字段 设置了 typeHandler 或 jdbcType 的情况 * * @since 3.1.2 */ boolean autoResultMap() default false; /** * 只需要的属性名 *

    * 与{@link #excludeProperty()} 二选一配置,都配置了则只有此配置生效 * * @since 3.5.10 */ String[] properties() default {}; /** * 需要排除的属性名 *

    * 与{@link #properties()} 二选一配置,都配置了则{@link #properties()} 配置生效 * * @since 3.3.1 */ String[] excludeProperty() default {}; } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/Version.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import java.lang.annotation.*; /** * 乐观锁注解 *

    * 支持的字段类型: * long, * Long, * int, * Integer, * java.util.Date, * java.sql.Timestamp, * java.time.LocalDateTime * java.time.Instant * * @author TaoYu * @since 2016-01-23 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface Version { } ================================================ FILE: mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 注解方法类 */ package com.baomidou.mybatisplus.annotation; ================================================ FILE: mybatis-plus-bom/build.gradle ================================================ buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } maven { url "https://repo.spring.io/plugins-release" } } dependencies { classpath "io.spring.gradle:dependency-management-plugin:1.1.6" } } apply plugin: "io.spring.dependency-management" dependencyManagement { dependencies { dependency "com.baomidou:mybatis-plus-annotation:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-core:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-extension:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-generator:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-solon-plugin:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-spring:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-boot-starter:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-boot-starter-test:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-spring-boot-autoconfigure:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-spring-boot-test-autoconfigure:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-spring-boot3-starter:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-spring-boot3-starter-test:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-spring-boot4-starter:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-spring-boot4-starter-test:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-jsqlparser:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-jsqlparser-4.9:${APP_VERSION}" dependency "com.baomidou:mybatis-plus-jsqlparser-5.0:${APP_VERSION}" } } ================================================ FILE: mybatis-plus-core/build.gradle ================================================ dependencies { api project(":mybatis-plus-annotation") api "${lib.mybatis}" implementation "${lib.cglib}" implementation "${lib.'spring-aop'}" implementation "${lib.'imadcn'}" testImplementation "${lib.'logback-classic'}" testImplementation "${lib."mybatis-spring"}" testImplementation "${lib.'spring-jdbc'}" } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/InjectorResolver.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import org.apache.ibatis.builder.annotation.MethodResolver; /** * 继承 {@link MethodResolver} * * @author miemie * @since 2019-01-05 */ public class InjectorResolver extends MethodResolver { private final MybatisMapperAnnotationBuilder annotationBuilder; public InjectorResolver(MybatisMapperAnnotationBuilder annotationBuilder) { super(annotationBuilder, null); this.annotationBuilder = annotationBuilder; } @Override public void resolve() { annotationBuilder.parserInjector(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisConfiguration.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler; import com.baomidou.mybatisplus.core.mapper.Mapper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; import com.baomidou.mybatisplus.core.toolkit.StringPool; import lombok.Getter; import lombok.Setter; import org.apache.ibatis.binding.MapperRegistry; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.keygen.KeyGenerator; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMap; import org.apache.ibatis.mapping.ResultMap; import org.apache.ibatis.parsing.XNode; import org.apache.ibatis.scripting.LanguageDriver; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.transaction.Transaction; import org.apache.ibatis.type.TypeHandler; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import java.util.stream.Collectors; /** * replace default Configuration class *

    Caratacus 2016/9/25 replace mapperRegistry

    * * @author hubin * @since 2016-01-23 */ public class MybatisConfiguration extends Configuration { private static final Log logger = LogFactory.getLog(MybatisConfiguration.class); /** * Mapper 注册 */ protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this); protected final Map caches = new StrictMap<>("Caches collection"); protected final Map resultMaps = new StrictMap<>("Result Maps collection"); protected final Map parameterMaps = new StrictMap<>("Parameter Maps collection"); protected final Map keyGenerators = new StrictMap<>("Key Generators collection"); protected final Map sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers"); protected final Map mappedStatements = new StrictMap("Mapped Statements collection") .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource()); /** * 是否生成短key缓存 * * @since 3.4.0 */ @Setter @Getter private boolean useGeneratedShortKey = true; public MybatisConfiguration(Environment environment) { this(); this.environment = environment; } /** * 初始化调用 */ public MybatisConfiguration() { super(); this.mapUnderscoreToCamelCase = true; typeHandlerRegistry.setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class); languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class); } /** * MybatisPlus 加载 SQL 顺序: *

    1、加载 XML中的 SQL

    *

    2、加载 SqlProvider 中的 SQL

    *

    3、XmlSql 与 SqlProvider不能包含相同的 SQL

    *

    调整后的 SQL优先级:XmlSql > sqlProvider > CurdSql

    */ @Override public void addMappedStatement(MappedStatement ms) { if (mappedStatements.containsKey(ms.getId())) { /* * 说明已加载了xml中的节点; 忽略mapper中的 SqlProvider 数据 */ logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file"); return; } mappedStatements.put(ms.getId(), ms); } /** * 使用自己的 MybatisMapperRegistry */ @Override public MapperRegistry getMapperRegistry() { return mybatisMapperRegistry; } /** * 使用自己的 MybatisMapperRegistry */ @Override public void addMapper(Class type) { mybatisMapperRegistry.addMapper(type); } /** * 新增注入新的 Mapper 信息,新增前会清理之前的缓存信息 * * @param type Mapper Type * @deprecated 3.5.8 不建议在实际生产环境中使用. */ @Deprecated public void addNewMapper(Class type) { this.removeMapper(type); this.addMapper(type); } /** * 移除 Mapper 相关缓存,支持 GroovyClassLoader 动态注入 Mapper * * @param type Mapper Type * @deprecated 3.5.8 不建议在实际生产环境中使用. */ @Deprecated public void removeMapper(Class type) { Set mapperRegistryCache = GlobalConfigUtils.getGlobalConfig(this).getMapperRegistryCache(); final String mapperType = type.toString(); if (mapperRegistryCache.contains(mapperType)) { // 清空实体表信息映射信息 TableInfoHelper.remove(ReflectionKit.getSuperClassGenericType(type, Mapper.class, 0)); // 清空 Mapper 缓存信息 this.mybatisMapperRegistry.removeMapper(type); this.loadedResources.remove(type.toString()); this.loadedResources.remove(type.getName().replace(StringPool.DOT, StringPool.SLASH) + ".xml"); mapperRegistryCache.remove(mapperType); // 清空 Mapper 方法 mappedStatement 缓存信息 String typeKey = type.getName() + StringPool.DOT; String simpleName = type.getSimpleName(); mappedStatements.keySet().stream().filter(ms -> ms.startsWith(typeKey) || ms.equals(simpleName)).collect(Collectors.toSet()).forEach(mappedStatements::remove); resultMaps.keySet().stream().filter(r -> r.startsWith(typeKey)).collect(Collectors.toSet()).forEach(resultMaps::remove); parameterMaps.keySet().stream().filter(p -> p.startsWith(typeKey)).collect(Collectors.toSet()).forEach(parameterMaps::remove); keyGenerators.keySet().stream().filter(k -> k.startsWith(typeKey)).collect(Collectors.toSet()).forEach(keyGenerators::remove); sqlFragments.keySet().stream().filter(s -> s.startsWith(typeKey)).collect(Collectors.toSet()).forEach(sqlFragments::remove); caches.keySet().stream().filter(p -> p.equals(type.getName()) || p.equals(simpleName)).collect(Collectors.toSet()).forEach(caches::remove); } } /** * 使用自己的 MybatisMapperRegistry */ @Override public void addMappers(String packageName, Class superType) { mybatisMapperRegistry.addMappers(packageName, superType); } /** * 使用自己的 MybatisMapperRegistry */ @Override public void addMappers(String packageName) { mybatisMapperRegistry.addMappers(packageName); } /** * 使用自己的 MybatisMapperRegistry */ @Override public T getMapper(Class type, SqlSession sqlSession) { return mybatisMapperRegistry.getMapper(type, sqlSession); } /** * 使用自己的 MybatisMapperRegistry */ @Override public boolean hasMapper(Class type) { return mybatisMapperRegistry.hasMapper(type); } /** * 指定动态SQL生成的默认语言 * * @param driver LanguageDriver */ @Override public void setDefaultScriptingLanguage(Class driver) { if (driver == null) { driver = MybatisXMLLanguageDriver.class; } getLanguageRegistry().setDefaultDriverClass(driver); } @Override public void setDefaultEnumTypeHandler(Class typeHandler) { if (typeHandler != null) { CompositeEnumTypeHandler.setDefaultEnumTypeHandler(typeHandler); } } @Override public void addKeyGenerator(String id, KeyGenerator keyGenerator) { keyGenerators.put(id, keyGenerator); } @Override public Collection getKeyGeneratorNames() { return keyGenerators.keySet(); } @Override public Collection getKeyGenerators() { return keyGenerators.values(); } @Override public KeyGenerator getKeyGenerator(String id) { return keyGenerators.get(id); } @Override public boolean hasKeyGenerator(String id) { return keyGenerators.containsKey(id); } @Override public void addCache(Cache cache) { caches.put(cache.getId(), cache); } @Override public Collection getCacheNames() { return caches.keySet(); } @Override public Collection getCaches() { return caches.values(); } @Override public Cache getCache(String id) { return caches.get(id); } @Override public boolean hasCache(String id) { return caches.containsKey(id); } @Override public void addResultMap(ResultMap rm) { resultMaps.put(rm.getId(), rm); checkLocallyForDiscriminatedNestedResultMaps(rm); checkGloballyForDiscriminatedNestedResultMaps(rm); } @Override public Collection getResultMapNames() { return resultMaps.keySet(); } @Override public Collection getResultMaps() { return resultMaps.values(); } @Override public ResultMap getResultMap(String id) { return resultMaps.get(id); } @Override public boolean hasResultMap(String id) { return resultMaps.containsKey(id); } @Override public void addParameterMap(ParameterMap pm) { parameterMaps.put(pm.getId(), pm); } @Override public Collection getParameterMapNames() { return parameterMaps.keySet(); } @Override public Collection getParameterMaps() { return parameterMaps.values(); } @Override public ParameterMap getParameterMap(String id) { return parameterMaps.get(id); } @Override public boolean hasParameterMap(String id) { return parameterMaps.containsKey(id); } @Override public Map getSqlFragments() { return sqlFragments; } @Override public Collection getMappedStatementNames() { buildAllStatements(); return mappedStatements.keySet(); } @Override public Collection getMappedStatements() { buildAllStatements(); return mappedStatements.values(); } @Override public MappedStatement getMappedStatement(String id) { return this.getMappedStatement(id, true); } @Override public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) { if (validateIncompleteStatements) { buildAllStatements(); } return mappedStatements.get(id); } @Override public boolean hasStatement(String statementName, boolean validateIncompleteStatements) { if (validateIncompleteStatements) { buildAllStatements(); } return mappedStatements.containsKey(statementName); } @Override public Executor newExecutor(Transaction transaction, ExecutorType executorType) { return super.newExecutor(transaction, executorType); } // Slow but a one time cost. A better solution is welcome. @Override public void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) { if (rm.hasNestedResultMaps()) { final String resultMapId = rm.getId(); for (Object resultMapObject : resultMaps.values()) { if (resultMapObject instanceof ResultMap) { ResultMap entryResultMap = (ResultMap) resultMapObject; if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) { Collection discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap() .values(); if (discriminatedResultMapNames.contains(resultMapId)) { entryResultMap.forceNestedResultMaps(); } } } } } } // Slow but a one time cost. A better solution is welcome. @Override protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) { if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) { for (String discriminatedResultMapName : rm.getDiscriminator().getDiscriminatorMap().values()) { if (hasResultMap(discriminatedResultMapName)) { ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName); if (discriminatedResultMap.hasNestedResultMaps()) { rm.forceNestedResultMaps(); break; } } } } } protected class StrictMap extends ConcurrentHashMap { private static final long serialVersionUID = -4950446264854982944L; private final String name; private BiFunction conflictMessageProducer; private final Object AMBIGUITY_INSTANCE = new Object(); public StrictMap(String name, int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); this.name = name; } public StrictMap(String name, int initialCapacity) { super(initialCapacity); this.name = name; } public StrictMap(String name) { super(); this.name = name; } public StrictMap(String name, Map m) { super(m); this.name = name; } /** * Assign a function for producing a conflict error message when contains value with the same key. *

    * function arguments are 1st is saved value and 2nd is target value. * * @param conflictMessageProducer A function for producing a conflict error message * @return a conflict error message * @since 3.5.0 */ public StrictMap conflictMessageProducer(BiFunction conflictMessageProducer) { this.conflictMessageProducer = conflictMessageProducer; return this; } @Override @SuppressWarnings("unchecked") public V put(String key, V value) { if (containsKey(key)) { throw new IllegalArgumentException(name + " already contains value for " + key + (conflictMessageProducer == null ? StringPool.EMPTY : conflictMessageProducer.apply(super.get(key), value))); } if (useGeneratedShortKey) { if (key.contains(StringPool.DOT)) { final String shortKey = getShortName(key); if (super.get(shortKey) == null) { super.put(shortKey, value); } else { super.put(shortKey, (V) AMBIGUITY_INSTANCE); } } } return super.put(key, value); } @Override public boolean containsKey(Object key) { if (key == null) { return false; } return super.get(key) != null; } @Override public V get(Object key) { V value = super.get(key); if (value == null) { throw new IllegalArgumentException(name + " does not contain value for " + key); } if (useGeneratedShortKey && AMBIGUITY_INSTANCE == value) { throw new IllegalArgumentException(key + " is ambiguous in " + name + " (try using the full name including the namespace, or rename one of the entries)"); } return value; } private String getShortName(String key) { final String[] keyParts = key.split("\\."); return keyParts[keyParts.length - 1]; } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.core.toolkit.StringPool; import org.apache.ibatis.annotations.*; import org.apache.ibatis.annotations.ResultMap; import org.apache.ibatis.annotations.Options.FlushCachePolicy; import org.apache.ibatis.binding.MapperMethod; import org.apache.ibatis.builder.BuilderException; import org.apache.ibatis.builder.CacheRefResolver; import org.apache.ibatis.builder.IncompleteElementException; import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder; import org.apache.ibatis.builder.annotation.ProviderSqlSource; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; import org.apache.ibatis.executor.keygen.KeyGenerator; import org.apache.ibatis.executor.keygen.NoKeyGenerator; import org.apache.ibatis.executor.keygen.SelectKeyGenerator; import org.apache.ibatis.io.Resources; import org.apache.ibatis.mapping.*; import org.apache.ibatis.parsing.PropertyParser; import org.apache.ibatis.reflection.TypeParameterResolver; import org.apache.ibatis.scripting.LanguageDriver; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.UnknownTypeHandler; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.*; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 继承 *

    * 只重写了 {@link MapperAnnotationBuilder#parse} 和 #getReturnType * 没有XML配置文件注入基础CRUD方法 *

    * * @author Caratacus * @since 2017-01-04 */ public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder { private static final Set> statementAnnotationTypes = Stream .of(Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class, InsertProvider.class, DeleteProvider.class) .collect(Collectors.toSet()); private final Configuration configuration; private final MybatisMapperBuilderAssistant assistant; private final Class type; public MybatisMapperAnnotationBuilder(Configuration configuration, Class type) { super(configuration, type); String resource = type.getName().replace(StringPool.DOT, StringPool.SLASH) + ".java (best guess)"; this.assistant = new MybatisMapperBuilderAssistant(configuration, resource); this.configuration = configuration; this.type = type; } @Override public void parse() { String resource = type.toString(); if (!configuration.isResourceLoaded(resource)) { loadXmlResource(); configuration.addLoadedResource(resource); String mapperName = type.getName(); assistant.setCurrentNamespace(mapperName); parseCache(); parseCacheRef(); IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.initSqlParserInfoCache(type); for (Method method : type.getMethods()) { InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method); if (!canHaveStatement(method)) { continue; } if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent() && method.getAnnotation(ResultMap.class) == null) { parseResultMap(method); } try { parseStatement(method); } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MybatisMethodResolver(this, method)); } } try { // https://github.com/baomidou/mybatis-plus/issues/3038 if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) { parserInjector(); } } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new InjectorResolver(this)); } } configuration.parsePendingMethods(false); } void parserInjector() { GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type); } private static boolean canHaveStatement(Method method) { // issue #237 return !method.isBridge() && !method.isDefault(); } private void loadXmlResource() { // Spring may not know the real resource name so we check a flag // to prevent loading again a resource twice // this flag is set at XMLMapperBuilder#bindMapperForNamespace if (!configuration.isResourceLoaded("namespace:" + type.getName())) { String xmlResource = type.getName().replace(StringPool.DOT, StringPool.SLASH) + ".xml"; // #1347 InputStream inputStream = type.getResourceAsStream(StringPool.SLASH + xmlResource); if (inputStream == null) { // Search XML mapper that is not in the module but in the classpath. try { inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource); } catch (IOException e2) { // ignore, resource is not required } } if (inputStream != null) { MybatisXMLMapperBuilder xmlParser = new MybatisXMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName()); xmlParser.parse(); } } } private void parseCache() { CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class); if (cacheDomain != null) { Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size(); Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval(); Properties props = convertToProperties(cacheDomain.properties()); assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props); } } private Properties convertToProperties(Property[] properties) { if (properties.length == 0) { return null; } Properties props = new Properties(); for (Property property : properties) { props.setProperty(property.name(), PropertyParser.parse(property.value(), configuration.getVariables())); } return props; } private void parseCacheRef() { CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class); if (cacheDomainRef != null) { Class refType = cacheDomainRef.value(); String refName = cacheDomainRef.name(); if (refType == void.class && refName.isEmpty()) { throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef"); } if (refType != void.class && !refName.isEmpty()) { throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef"); } String namespace = refType != void.class ? refType.getName() : refName; try { assistant.useCacheRef(namespace); } catch (IncompleteElementException e) { configuration.addIncompleteCacheRef(new CacheRefResolver(assistant, namespace)); } } } private String parseResultMap(Method method) { Class returnType = getReturnType(method, type); Arg[] args = method.getAnnotationsByType(Arg.class); Result[] results = method.getAnnotationsByType(Result.class); TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class); String resultMapId = generateResultMapName(method); applyResultMap(resultMapId, returnType, args, results, typeDiscriminator); return resultMapId; } private String generateResultMapName(Method method) { Results results = method.getAnnotation(Results.class); if (results != null && !results.id().isEmpty()) { return type.getName() + "." + results.id(); } StringBuilder suffix = new StringBuilder(); for (Class c : method.getParameterTypes()) { suffix.append("-"); suffix.append(c.getSimpleName()); } if (suffix.length() < 1) { suffix.append("-void"); } return type.getName() + "." + method.getName() + suffix; } private void applyResultMap(String resultMapId, Class returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) { List resultMappings = new ArrayList<>(); applyConstructorArgs(args, returnType, resultMappings); applyResults(results, returnType, resultMappings); Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator); // TODO add AutoMappingBehaviour assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null); createDiscriminatorResultMaps(resultMapId, returnType, discriminator); } private void createDiscriminatorResultMaps(String resultMapId, Class resultType, TypeDiscriminator discriminator) { if (discriminator != null) { for (Case c : discriminator.cases()) { String caseResultMapId = resultMapId + "-" + c.value(); List resultMappings = new ArrayList<>(); // issue #136 applyConstructorArgs(c.constructArgs(), resultType, resultMappings); applyResults(c.results(), resultType, resultMappings); // TODO add AutoMappingBehaviour assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null); } } } private Discriminator applyDiscriminator(String resultMapId, Class resultType, TypeDiscriminator discriminator) { if (discriminator != null) { String column = discriminator.column(); Class javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType(); JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType(); @SuppressWarnings("unchecked") Class> typeHandler = (Class>) (discriminator .typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler()); Case[] cases = discriminator.cases(); Map discriminatorMap = new HashMap<>(); for (Case c : cases) { String value = c.value(); String caseResultMapId = resultMapId + "-" + value; discriminatorMap.put(value, caseResultMapId); } return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap); } return null; } void parseStatement(Method method) { final Class parameterTypeClass = getParameterType(method); final LanguageDriver languageDriver = getLanguageDriver(method); getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> { final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method); final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType(); final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation()) .orElse(null); final String mappedStatementId = type.getName() + "." + method.getName(); final KeyGenerator keyGenerator; String keyProperty = null; String keyColumn = null; if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { // first check for SelectKey annotation - that overrides everything else SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class) .map(x -> (SelectKey) x.getAnnotation()).orElse(null); if (selectKey != null) { keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver); keyProperty = selectKey.keyProperty(); } else if (options == null) { keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } else { keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; keyProperty = options.keyProperty(); keyColumn = options.keyColumn(); } } else { keyGenerator = NoKeyGenerator.INSTANCE; } Integer fetchSize = null; Integer timeout = null; StatementType statementType = StatementType.PREPARED; ResultSetType resultSetType = configuration.getDefaultResultSetType(); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = !isSelect; boolean useCache = isSelect; if (options != null) { if (FlushCachePolicy.TRUE.equals(options.flushCache())) { flushCache = true; } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) { flushCache = false; } useCache = options.useCache(); // issue #348 fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; timeout = options.timeout() > -1 ? options.timeout() : null; statementType = options.statementType(); if (options.resultSetType() != ResultSetType.DEFAULT) { resultSetType = options.resultSetType(); } } String resultMapId = null; if (isSelect) { ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class); if (resultMapAnnotation != null) { resultMapId = String.join(",", resultMapAnnotation.value()); } else { resultMapId = generateResultMapName(method); } } assistant.addMappedStatement(mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout, // ParameterMapID null, parameterTypeClass, resultMapId, getReturnType(method, type), resultSetType, flushCache, useCache, // TODO gcode issue #577 false, keyGenerator, keyProperty, keyColumn, statementAnnotation.getDatabaseId(), languageDriver, // ResultSets options != null ? nullOrEmpty(options.resultSets()) : null, statementAnnotation.isDirtySelect()); }); } private LanguageDriver getLanguageDriver(Method method) { Lang lang = method.getAnnotation(Lang.class); Class langClass = null; if (lang != null) { langClass = lang.value(); } return configuration.getLanguageDriver(langClass); } private Class getParameterType(Method method) { Class parameterType = null; Class[] parameterTypes = method.getParameterTypes(); for (Class currentParameterType : parameterTypes) { if (!RowBounds.class.isAssignableFrom(currentParameterType) && !ResultHandler.class.isAssignableFrom(currentParameterType)) { if (parameterType == null) { parameterType = currentParameterType; } else { // issue #135 parameterType = MapperMethod.ParamMap.class; } } } return parameterType; } private static Class getReturnType(Method method, Class type) { Class returnType = method.getReturnType(); Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type); if (resolvedReturnType instanceof Class) { returnType = (Class) resolvedReturnType; if (returnType.isArray()) { returnType = returnType.getComponentType(); } // gcode issue #508 if (void.class.equals(returnType)) { ResultType rt = method.getAnnotation(ResultType.class); if (rt != null) { returnType = rt.value(); } } } else if (resolvedReturnType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType; Class rawType = (Class) parameterizedType.getRawType(); if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); if (actualTypeArguments != null && actualTypeArguments.length == 1) { Type returnTypeParameter = actualTypeArguments[0]; if (returnTypeParameter instanceof Class) { returnType = (Class) returnTypeParameter; } else if (returnTypeParameter instanceof ParameterizedType) { // (gcode issue #443) actual type can be a also a parameterized type returnType = (Class) ((ParameterizedType) returnTypeParameter).getRawType(); } else if (returnTypeParameter instanceof GenericArrayType) { Class componentType = (Class) ((GenericArrayType) returnTypeParameter).getGenericComponentType(); // (gcode issue #525) support List returnType = Array.newInstance(componentType, 0).getClass(); } } } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) { // (gcode issue 504) Do not look into Maps if there is not MapKey annotation Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); if (actualTypeArguments != null && actualTypeArguments.length == 2) { Type returnTypeParameter = actualTypeArguments[1]; if (returnTypeParameter instanceof Class) { returnType = (Class) returnTypeParameter; } else if (returnTypeParameter instanceof ParameterizedType) { // (gcode issue 443) actual type can be a also a parameterized type returnType = (Class) ((ParameterizedType) returnTypeParameter).getRawType(); } } } else if (Optional.class.equals(rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); Type returnTypeParameter = actualTypeArguments[0]; if (returnTypeParameter instanceof Class) { returnType = (Class) returnTypeParameter; } } else if (IPage.class.isAssignableFrom(rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); Type returnTypeParameter = actualTypeArguments[0]; if (returnTypeParameter instanceof Class) { returnType = (Class) returnTypeParameter; } else if (returnTypeParameter instanceof ParameterizedType) { returnType = (Class) ((ParameterizedType) returnTypeParameter).getRawType(); } } } return returnType; } private void applyResults(Result[] results, Class resultType, List resultMappings) { for (Result result : results) { List flags = new ArrayList<>(); if (result.id()) { flags.add(ResultFlag.ID); } @SuppressWarnings("unchecked") Class> typeHandler = (Class>) (result .typeHandler() == UnknownTypeHandler.class ? null : result.typeHandler()); boolean hasNestedResultMap = hasNestedResultMap(result); ResultMapping resultMapping = assistant.buildResultMapping(resultType, nullOrEmpty(result.property()), nullOrEmpty(result.column()), result.javaType() == void.class ? null : result.javaType(), result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(), hasNestedSelect(result) ? nestedSelectId(result) : null, hasNestedResultMap ? nestedResultMapId(result) : null, null, hasNestedResultMap ? findColumnPrefix(result) : null, typeHandler, flags, null, null, isLazy(result)); resultMappings.add(resultMapping); } } private String findColumnPrefix(Result result) { String columnPrefix = result.one().columnPrefix(); if (columnPrefix.length() < 1) { columnPrefix = result.many().columnPrefix(); } return columnPrefix; } private String nestedResultMapId(Result result) { String resultMapId = result.one().resultMap(); if (resultMapId.length() < 1) { resultMapId = result.many().resultMap(); } if (!resultMapId.contains(StringPool.DOT)) { resultMapId = type.getName() + StringPool.DOT + resultMapId; } return resultMapId; } private boolean hasNestedResultMap(Result result) { if (result.one().resultMap().length() > 0 && result.many().resultMap().length() > 0) { throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result"); } return result.one().resultMap().length() > 0 || result.many().resultMap().length() > 0; } private String nestedSelectId(Result result) { String nestedSelect = result.one().select(); if (nestedSelect.length() < 1) { nestedSelect = result.many().select(); } if (!nestedSelect.contains(StringPool.DOT)) { nestedSelect = type.getName() + StringPool.DOT + nestedSelect; } return nestedSelect; } private boolean isLazy(Result result) { boolean isLazy = configuration.isLazyLoadingEnabled(); if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) { isLazy = result.one().fetchType() == FetchType.LAZY; } else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) { isLazy = result.many().fetchType() == FetchType.LAZY; } return isLazy; } private boolean hasNestedSelect(Result result) { if (result.one().select().length() > 0 && result.many().select().length() > 0) { throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result"); } return result.one().select().length() > 0 || result.many().select().length() > 0; } private void applyConstructorArgs(Arg[] args, Class resultType, List resultMappings) { for (Arg arg : args) { List flags = new ArrayList<>(); flags.add(ResultFlag.CONSTRUCTOR); if (arg.id()) { flags.add(ResultFlag.ID); } @SuppressWarnings("unchecked") Class> typeHandler = (Class>) (arg .typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler()); ResultMapping resultMapping = assistant.buildResultMapping(resultType, nullOrEmpty(arg.name()), nullOrEmpty(arg.column()), arg.javaType() == void.class ? null : arg.javaType(), arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(), nullOrEmpty(arg.select()), nullOrEmpty(arg.resultMap()), null, nullOrEmpty(arg.columnPrefix()), typeHandler, flags, null, null, false); resultMappings.add(resultMapping); } } private String nullOrEmpty(String value) { return value == null || value.trim().length() == 0 ? null : value; } private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class parameterTypeClass, LanguageDriver languageDriver) { String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX; Class resultTypeClass = selectKeyAnnotation.resultType(); StatementType statementType = selectKeyAnnotation.statementType(); String keyProperty = selectKeyAnnotation.keyProperty(); String keyColumn = selectKeyAnnotation.keyColumn(); boolean executeBefore = selectKeyAnnotation.before(); // defaults boolean useCache = false; KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE; Integer fetchSize = null; Integer timeout = null; boolean flushCache = false; String parameterMap = null; String resultMap = null; ResultSetType resultSetTypeEnum = null; String databaseId = selectKeyAnnotation.databaseId().isEmpty() ? null : selectKeyAnnotation.databaseId(); SqlSource sqlSource = buildSqlSource(selectKeyAnnotation, parameterTypeClass, languageDriver, null); SqlCommandType sqlCommandType = SqlCommandType.SELECT; assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, false, keyGenerator, keyProperty, keyColumn, databaseId, languageDriver, null, false); id = assistant.applyCurrentNamespace(id, false); MappedStatement keyStatement = configuration.getMappedStatement(id, false); SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore); configuration.addKeyGenerator(id, answer); return answer; } private SqlSource buildSqlSource(Annotation annotation, Class parameterType, LanguageDriver languageDriver, Method method) { if (annotation instanceof Select) { return buildSqlSourceFromStrings(((Select) annotation).value(), parameterType, languageDriver); } if (annotation instanceof Update) { return buildSqlSourceFromStrings(((Update) annotation).value(), parameterType, languageDriver); } else if (annotation instanceof Insert) { return buildSqlSourceFromStrings(((Insert) annotation).value(), parameterType, languageDriver); } else if (annotation instanceof Delete) { return buildSqlSourceFromStrings(((Delete) annotation).value(), parameterType, languageDriver); } else if (annotation instanceof SelectKey) { return buildSqlSourceFromStrings(((SelectKey) annotation).statement(), parameterType, languageDriver); } return new ProviderSqlSource(assistant.getConfiguration(), annotation, type, method); } private SqlSource buildSqlSourceFromStrings(String[] strings, Class parameterTypeClass, LanguageDriver languageDriver) { return languageDriver.createSqlSource(configuration, String.join(" ", strings).trim(), parameterTypeClass); } @SafeVarargs private final Optional getAnnotationWrapper(Method method, boolean errorIfNoMatch, Class... targetTypes) { return getAnnotationWrapper(method, errorIfNoMatch, Arrays.asList(targetTypes)); } private Optional getAnnotationWrapper(Method method, boolean errorIfNoMatch, Collection> targetTypes) { String databaseId = configuration.getDatabaseId(); Map statementAnnotations = targetTypes.stream() .flatMap(x -> Arrays.stream(method.getAnnotationsByType(x))).map(AnnotationWrapper::new) .collect(Collectors.toMap(AnnotationWrapper::getDatabaseId, x -> x, (existing, duplicate) -> { throw new BuilderException( String.format("Detected conflicting annotations '%s' and '%s' on '%s'.", existing.getAnnotation(), duplicate.getAnnotation(), method.getDeclaringClass().getName() + "." + method.getName())); })); AnnotationWrapper annotationWrapper = null; if (databaseId != null) { annotationWrapper = statementAnnotations.get(databaseId); } if (annotationWrapper == null) { annotationWrapper = statementAnnotations.get(""); } if (errorIfNoMatch && annotationWrapper == null && !statementAnnotations.isEmpty()) { // Annotations exist, but there is no matching one for the specified databaseId throw new BuilderException(String.format( "Could not find a statement annotation that correspond a current database or default statement on method '%s.%s'. Current database id is [%s].", method.getDeclaringClass().getName(), method.getName(), databaseId)); } return Optional.ofNullable(annotationWrapper); } public static Class getMethodReturnType(String mapperFqn, String localStatementId) { if (mapperFqn == null || localStatementId == null) { return null; } try { Class mapperClass = Resources.classForName(mapperFqn); for (Method method : mapperClass.getMethods()) { if (method.getName().equals(localStatementId) && canHaveStatement(method)) { return getReturnType(method, mapperClass); } } } catch (ClassNotFoundException e) { // No corresponding mapper interface which is OK } return null; } private static class AnnotationWrapper { private final Annotation annotation; private final String databaseId; private final SqlCommandType sqlCommandType; private boolean dirtySelect; AnnotationWrapper(Annotation annotation) { this.annotation = annotation; if (annotation instanceof Select) { databaseId = ((Select) annotation).databaseId(); sqlCommandType = SqlCommandType.SELECT; dirtySelect = ((Select) annotation).affectData(); } else if (annotation instanceof Update) { databaseId = ((Update) annotation).databaseId(); sqlCommandType = SqlCommandType.UPDATE; } else if (annotation instanceof Insert) { databaseId = ((Insert) annotation).databaseId(); sqlCommandType = SqlCommandType.INSERT; } else if (annotation instanceof Delete) { databaseId = ((Delete) annotation).databaseId(); sqlCommandType = SqlCommandType.DELETE; } else if (annotation instanceof SelectProvider) { databaseId = ((SelectProvider) annotation).databaseId(); sqlCommandType = SqlCommandType.SELECT; dirtySelect = ((SelectProvider) annotation).affectData(); } else if (annotation instanceof UpdateProvider) { databaseId = ((UpdateProvider) annotation).databaseId(); sqlCommandType = SqlCommandType.UPDATE; } else if (annotation instanceof InsertProvider) { databaseId = ((InsertProvider) annotation).databaseId(); sqlCommandType = SqlCommandType.INSERT; } else if (annotation instanceof DeleteProvider) { databaseId = ((DeleteProvider) annotation).databaseId(); sqlCommandType = SqlCommandType.DELETE; } else { sqlCommandType = SqlCommandType.UNKNOWN; if (annotation instanceof Options) { databaseId = ((Options) annotation).databaseId(); } else if (annotation instanceof SelectKey) { databaseId = ((SelectKey) annotation).databaseId(); } else { databaseId = StringPool.EMPTY; } } } Annotation getAnnotation() { return annotation; } SqlCommandType getSqlCommandType() { return sqlCommandType; } String getDatabaseId() { return databaseId; } boolean isDirtySelect() { return dirtySelect; } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperBuilderAssistant.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler; import com.baomidou.mybatisplus.core.toolkit.MybatisUtils; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.apache.ibatis.mapping.ResultFlag; import org.apache.ibatis.mapping.ResultMapping; import org.apache.ibatis.reflection.MetaClass; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.UnknownTypeHandler; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; /** * 重写了原生方法. * * @author nieqiurong * @see MapperBuilderAssistant * @since 3.5.6 */ public class MybatisMapperBuilderAssistant extends MapperBuilderAssistant { public MybatisMapperBuilderAssistant(Configuration configuration, String resource) { super(configuration, resource); } @Override public ResultMapping buildResultMapping(Class resultType, String property, String column, Class javaType, JdbcType jdbcType, String nestedSelect, String nestedResultMap, String notNullColumn, String columnPrefix, Class> typeHandler, List flags, String resultSet, String foreignColumn, boolean lazy) { Class javaTypeClass = resolveResultJavaType(resultType, property, javaType); TypeHandler typeHandlerInstance = null; if (typeHandler != null) { if (IJsonTypeHandler.class.isAssignableFrom(typeHandler)) { try { Field field = resultType.getDeclaredField(property); typeHandlerInstance = MybatisUtils.newJsonTypeHandler(typeHandler, javaTypeClass, field); } catch (NoSuchFieldException e) { //ignore 降级兼容处理 typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler); } } else { typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler); } } List composites; if ((nestedSelect == null || nestedSelect.isEmpty()) && (foreignColumn == null || foreignColumn.isEmpty())) { composites = Collections.emptyList(); } else { composites = parseCompositeColumnName(column); } return new ResultMapping.Builder(configuration, property, column, javaTypeClass).jdbcType(jdbcType) .nestedQueryId(applyCurrentNamespace(nestedSelect, true)) .nestedResultMapId(applyCurrentNamespace(nestedResultMap, true)).resultSet(resultSet) .typeHandler(typeHandlerInstance).flags(flags == null ? new ArrayList<>() : flags).composites(composites) .notNullColumns(parseMultipleColumnNames(notNullColumn)).columnPrefix(columnPrefix).foreignColumn(foreignColumn) .lazy(lazy).build(); } private Set parseMultipleColumnNames(String columnName) { Set columns = new HashSet<>(); if (columnName != null) { if (columnName.indexOf(',') > -1) { StringTokenizer parser = new StringTokenizer(columnName, "{}, ", false); while (parser.hasMoreTokens()) { String column = parser.nextToken(); columns.add(column); } } else { columns.add(columnName); } } return columns; } private List parseCompositeColumnName(String columnName) { List composites = new ArrayList<>(); if (columnName != null && (columnName.indexOf('=') > -1 || columnName.indexOf(',') > -1)) { StringTokenizer parser = new StringTokenizer(columnName, "{}=, ", false); while (parser.hasMoreTokens()) { String property = parser.nextToken(); String column = parser.nextToken(); ResultMapping complexResultMapping = new ResultMapping.Builder(configuration, property, column, configuration.getTypeHandlerRegistry().getUnknownTypeHandler()).build(); composites.add(complexResultMapping); } } return composites; } private Class resolveResultJavaType(Class resultType, String property, Class javaType) { if (javaType == null && property != null) { try { MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory()); javaType = metaResultType.getSetterType(property); } catch (Exception e) { // ignore, following null check statement will deal with the situation } } if (javaType == null) { javaType = Object.class; } return javaType; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperRegistry.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import com.baomidou.mybatisplus.core.override.MybatisMapperProxyFactory; import org.apache.ibatis.binding.BindingException; import org.apache.ibatis.binding.MapperRegistry; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 继承至MapperRegistry * * @author Caratacus hubin * @since 2017-04-19 */ public class MybatisMapperRegistry extends MapperRegistry { private final Configuration config; private final Map, MybatisMapperProxyFactory> knownMappers = new ConcurrentHashMap<>(); public MybatisMapperRegistry(Configuration config) { super(config); this.config = config; } @SuppressWarnings("unchecked") @Override public T getMapper(Class type, SqlSession sqlSession) { // fix https://github.com/baomidou/mybatis-plus/issues/4247 MybatisMapperProxyFactory mapperProxyFactory = (MybatisMapperProxyFactory) knownMappers.get(type); if (mapperProxyFactory == null) { mapperProxyFactory = (MybatisMapperProxyFactory) knownMappers.entrySet().stream() .filter(t -> t.getKey().getName().equals(type.getName())).findFirst().map(Map.Entry::getValue) .orElseThrow(() -> new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.")); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } @Override public boolean hasMapper(Class type) { return knownMappers.containsKey(type); } /** * 清空 Mapper 缓存信息 */ protected void removeMapper(Class type) { knownMappers.entrySet().stream().filter(t -> t.getKey().getName().equals(type.getName())) .findFirst().ifPresent(t -> knownMappers.remove(t.getKey())); } @Override public void addMapper(Class type) { if (type.isInterface()) { if (hasMapper(type)) { return; // throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MybatisMapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } /** * 使用自己的 knownMappers */ @Override public Collection> getMappers() { return Collections.unmodifiableCollection(knownMappers.keySet()); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMethodResolver.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import org.apache.ibatis.builder.annotation.MethodResolver; import java.lang.reflect.Method; /** * 继承 {@link MethodResolver} * * @author miemie * @since 2019-01-05 */ public class MybatisMethodResolver extends MethodResolver { private final MybatisMapperAnnotationBuilder annotationBuilder; private final Method method; public MybatisMethodResolver(MybatisMapperAnnotationBuilder annotationBuilder, Method method) { super(null, null); this.annotationBuilder = annotationBuilder; this.method = method; } @Override public void resolve() { annotationBuilder.parseStatement(method); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisParameterHandler.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.ArrayUtils; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import org.apache.ibatis.logging.Log; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.ognl.OgnlOps; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.SimpleTypeRegistry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * 自定义 ParameterHandler 重装构造函数,填充插入方法主键 ID * * @author nieqiuqiu 2020/6/5 * @since 3.4.0 */ public class MybatisParameterHandler extends DefaultParameterHandler { /** * 填充的key值 * * @since 3.5.3.2 * @deprecated 3.5.4 */ @Deprecated public static final String[] COLLECTION_KEYS = new String[]{"collection", "coll", "list", "array"}; private final Configuration configuration; private final SqlCommandType sqlCommandType; private final MappedStatement mappedStatement; private final Log log; public MybatisParameterHandler(MappedStatement mappedStatement, Object parameter, BoundSql boundSql) { super(mappedStatement, parameter, boundSql); this.mappedStatement = mappedStatement; this.log = mappedStatement.getStatementLog(); this.configuration = mappedStatement.getConfiguration(); this.sqlCommandType = mappedStatement.getSqlCommandType(); processParameter(parameter); } public void processParameter(Object parameter) { /* 只处理插入或更新操作 */ if (parameter != null && !SimpleTypeRegistry.isSimpleType(parameter.getClass())) { if (SqlCommandType.INSERT == this.sqlCommandType || SqlCommandType.UPDATE == this.sqlCommandType) { extractParameters(parameter).forEach(this::process); } } } private void process(Object parameter) { if (parameter != null) { TableInfo tableInfo = null; Object entity = parameter; if (parameter instanceof Map) { // 处理单参数使用注解标记的时候,尝试提取et来获取实体参数 Map map = (Map) parameter; Object et = null; if(map.containsKey(Constants.ENTITY)){ et = map.get(Constants.ENTITY); } else if(map.containsKey(Constants.MP_FILL_ET)){ et = map.get(Constants.MP_FILL_ET); } if (et != null) { entity = et; tableInfo = TableInfoHelper.getTableInfo(entity.getClass()); } } else { tableInfo = TableInfoHelper.getTableInfo(parameter.getClass()); } if (tableInfo != null) { //到这里就应该转换到实体参数对象了,因为填充和ID处理都是针对实体对象处理的,不用传递原参数对象下去. MetaObject metaObject = this.configuration.newMetaObject(entity); if (SqlCommandType.INSERT == this.sqlCommandType) { populateKeys(tableInfo, metaObject, entity); insertFill(metaObject, tableInfo); } else { updateFill(metaObject, tableInfo); } } } } protected void populateKeys(TableInfo tableInfo, MetaObject metaObject, Object entity) { final IdType idType = tableInfo.getIdType(); final String keyProperty = tableInfo.getKeyProperty(); if (StringUtils.isNotBlank(keyProperty) && null != idType && idType.getKey() >= 3) { final IdentifierGenerator identifierGenerator = GlobalConfigUtils.getGlobalConfig(this.configuration).getIdentifierGenerator(); Object idValue = metaObject.getValue(keyProperty); if (identifierGenerator.assignId(idValue)) { if (idType.getKey() == IdType.ASSIGN_ID.getKey()) { Number id = identifierGenerator.nextId(entity); metaObject.setValue(keyProperty, OgnlOps.convertValue(id, tableInfo.getKeyType())); } else if (idType.getKey() == IdType.ASSIGN_UUID.getKey()) { if(String.class.equals(tableInfo.getKeyType())) { metaObject.setValue(keyProperty, identifierGenerator.nextUUID(entity)); } else { log.warn("The current ID generation strategy does not support: " + tableInfo.getKeyType()); } } } } } protected void insertFill(MetaObject metaObject, TableInfo tableInfo) { GlobalConfigUtils.getMetaObjectHandler(this.configuration).ifPresent(metaObjectHandler -> { if (metaObjectHandler.openInsertFill() && metaObjectHandler.openInsertFill(mappedStatement) && tableInfo.isWithInsertFill()) { metaObjectHandler.insertFill(metaObject); } }); } protected void updateFill(MetaObject metaObject, TableInfo tableInfo) { GlobalConfigUtils.getMetaObjectHandler(this.configuration).ifPresent(metaObjectHandler -> { if (metaObjectHandler.openUpdateFill() && metaObjectHandler.openUpdateFill(mappedStatement) && tableInfo.isWithUpdateFill()) { metaObjectHandler.updateFill(metaObject); } }); } /** * 处理正常批量插入逻辑 *

    * org.apache.ibatis.session.defaults.DefaultSqlSession$StrictMap 该类方法 * wrapCollection 实现 StrictMap 封装逻辑 *

    * * @return 集合参数 * @deprecated 3.5.3.2 */ @SuppressWarnings({"rawtypes", "unchecked"}) @Deprecated protected Collection getParameters(Object parameterObject) { Collection parameters = null; if (parameterObject instanceof Collection) { parameters = (Collection) parameterObject; } else if (ArrayUtils.isArray(parameterObject)) { parameters = toCollection(parameterObject); } else if (parameterObject instanceof Map) { Map parameterMap = (Map) parameterObject; // 约定 coll collection list array 这四个特殊key值处理批量. // 尝试提取参数进行填充,如果是多参数时,在使用注解时,请注意使用collection,list,array进行声明 if (parameterMap.containsKey("collection")) { return toCollection(parameterMap.get("collection")); } else if (parameterMap.containsKey(Constants.COLL)) { // 兼容逻辑删除对象填充,这里的集合字段后面重构的时候应该和原生保持一致,使用collection parameters = toCollection(parameterMap.get(Constants.COLL)); } else if (parameterMap.containsKey(Constants.LIST)) { parameters = toCollection(parameterMap.get(Constants.LIST)); } else if (parameterMap.containsKey(Constants.ARRAY)) { parameters = toCollection(parameterMap.get(Constants.ARRAY)); } } return parameters; } /** * 提取特殊key值 (只支持外层参数,嵌套参数不考虑) * List虽然这种写法目前可以进去提取et,但不考虑再提取list等其他类型,只做简单参数提取 * * @param parameterObject 参数 * @return 预期可能为填充参数值 */ @SuppressWarnings({"rawtypes", "unchecked"}) private Collection extractParameters(Object parameterObject) { if (parameterObject instanceof Collection) { return (Collection) parameterObject; } else if (ArrayUtils.isArray(parameterObject)) { return toCollection(parameterObject); } else if (parameterObject instanceof Map) { Collection parameters = new ArrayList<>(); Map parameterMap = (Map) parameterObject; Set objectSet = new HashSet<>(); parameterMap.forEach((k, v) -> { if (objectSet.add(v)) { Collection collection = toCollection(v); parameters.addAll(collection); } }); return parameters; } else { return Collections.singleton(parameterObject); } } @SuppressWarnings("unchecked") protected Collection toCollection(Object value) { if (value == null) { return Collections.emptyList(); } if (ArrayUtils.isArray(value) && !value.getClass().getComponentType().isPrimitive()) { return Arrays.asList((Object[]) value); } else if (Collection.class.isAssignableFrom(value.getClass())) { return (Collection) value; } else { return Collections.singletonList(value); } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisPlusVersion.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import java.io.File; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLConnection; import java.security.CodeSource; import java.util.jar.Attributes; import java.util.jar.JarFile; /** * 获取Mybatis-Plus版本 * * @author zengzhihong */ public class MybatisPlusVersion { private MybatisPlusVersion() { } public static String getVersion() { return determineSpringBootVersion(); } private static String determineSpringBootVersion() { final Package pkg = MybatisPlusVersion.class.getPackage(); if (pkg != null && pkg.getImplementationVersion() != null) { return pkg.getImplementationVersion(); } CodeSource codeSource = MybatisPlusVersion.class.getProtectionDomain().getCodeSource(); if (codeSource == null) { return null; } URL codeSourceLocation = codeSource.getLocation(); try { URLConnection connection = codeSourceLocation.openConnection(); if (connection instanceof JarURLConnection) { return getImplementationVersion(((JarURLConnection) connection).getJarFile()); } try (JarFile jarFile = new JarFile(new File(codeSourceLocation.toURI()))) { return getImplementationVersion(jarFile); } } catch (Exception ex) { return null; } } private static String getImplementationVersion(JarFile jarFile) throws IOException { return jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisSqlSessionFactoryBuilder.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.core.injector.SqlRunnerInjector; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.NetUtils; import org.apache.ibatis.exceptions.ExceptionFactory; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.net.InetAddress; import java.util.Properties; /** * 重写SqlSessionFactoryBuilder * * @author nieqiurong 2019/2/23. */ public class MybatisSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder { @SuppressWarnings("Duplicates") @Override public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } @SuppressWarnings("Duplicates") @Override public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } @Override public SqlSessionFactory build(Configuration configuration) { GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration); IdentifierGenerator identifierGenerator; if (null == globalConfig.getIdentifierGenerator()) { GlobalConfig.Sequence sequence = globalConfig.getSequence(); if (sequence.getWorkerId() != null && sequence.getDatacenterId() != null) { identifierGenerator = new DefaultIdentifierGenerator(sequence.getWorkerId(), sequence.getDatacenterId()); } else { NetUtils.NetProperties netProperties = new NetUtils.NetProperties(sequence.getPreferredNetworks(), sequence.getIgnoredInterfaces()); try { InetAddress inetAddress = new NetUtils(netProperties).findFirstNonLoopbackAddress(); identifierGenerator = new DefaultIdentifierGenerator(inetAddress); } catch (Exception e) { Log log = LogFactory.getLog(MybatisSqlSessionFactoryBuilder.class); identifierGenerator = DefaultIdentifierGenerator.getFixedIdentifierGenerator(log); } } globalConfig.setIdentifierGenerator(identifierGenerator); } else { identifierGenerator = globalConfig.getIdentifierGenerator(); } IdWorker.setIdentifierGenerator(identifierGenerator); if (globalConfig.isEnableSqlRunner()) { new SqlRunnerInjector().inject(configuration); } SqlSessionFactory sqlSessionFactory = super.build(configuration); // 缓存 sqlSessionFactory globalConfig.setSqlSessionFactory(sqlSessionFactory); return sqlSessionFactory; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import org.apache.ibatis.builder.BaseBuilder; import org.apache.ibatis.builder.BuilderException; import org.apache.ibatis.builder.xml.XMLConfigBuilder; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; import org.apache.ibatis.datasource.DataSourceFactory; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.executor.loader.ProxyFactory; import org.apache.ibatis.io.Resources; import org.apache.ibatis.io.VFS; import org.apache.ibatis.logging.Log; import org.apache.ibatis.mapping.DatabaseIdProvider; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.parsing.XNode; import org.apache.ibatis.parsing.XPathParser; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.reflection.DefaultReflectorFactory; import org.apache.ibatis.reflection.MetaClass; import org.apache.ibatis.reflection.ReflectorFactory; import org.apache.ibatis.reflection.factory.ObjectFactory; import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; import org.apache.ibatis.session.*; import org.apache.ibatis.transaction.TransactionFactory; import org.apache.ibatis.type.JdbcType; import javax.sql.DataSource; import java.io.InputStream; import java.io.Reader; import java.util.Properties; /** * 从 {@link XMLConfigBuilder} copy 过来, 使用自己的 MybatisConfiguration 而不是 Configuration * * @author hubin * @since 2017-01-04 */ public class MybatisXMLConfigBuilder extends BaseBuilder { private boolean parsed; private final XPathParser parser; private String environment; private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); public MybatisXMLConfigBuilder(Reader reader) { this(reader, null, null); } public MybatisXMLConfigBuilder(Reader reader, String environment) { this(reader, environment, null); } public MybatisXMLConfigBuilder(Reader reader, String environment, Properties props) { this(MybatisConfiguration.class, reader, environment, props); } public MybatisXMLConfigBuilder(Class configClass, Reader reader, String environment, Properties props) { this(configClass, new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } public MybatisXMLConfigBuilder(InputStream inputStream) { this(inputStream, null, null); } public MybatisXMLConfigBuilder(InputStream inputStream, String environment) { this(inputStream, environment, null); } public MybatisXMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(MybatisConfiguration.class, inputStream, environment, props); } public MybatisXMLConfigBuilder(Class configClass, InputStream inputStream, String environment, Properties props) { this(configClass, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } private MybatisXMLConfigBuilder(Class configClass, XPathParser parser, String environment, Properties props) { super(newConfig(configClass)); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; } public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { // issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfsImpl(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginsElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlersElement(root.evalNode("typeHandlers")); mappersElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } private Properties settingsAsProperties(XNode context) { if (context == null) { return new Properties(); } Properties props = context.getChildrenAsProperties(); // Check that all settings are known to the configuration class MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); for (Object key : props.keySet()) { if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException( "The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } return props; } private void loadCustomVfsImpl(Properties props) throws ClassNotFoundException { String value = props.getProperty("vfsImpl"); if (value == null) { return; } String[] clazzes = value.split(","); for (String clazz : clazzes) { if (!clazz.isEmpty()) { @SuppressWarnings("unchecked") Class vfsImpl = (Class) Resources.classForName(clazz); configuration.setVfsImpl(vfsImpl); } } } private void loadCustomLogImpl(Properties props) { Class logImpl = resolveClass(props.getProperty("logImpl")); configuration.setLogImpl(logImpl); } private void typeAliasesElement(XNode context) { if (context == null) { return; } for (XNode child : context.getChildren()) { if ("package".equals(child.getName())) { String typeAliasPackage = child.getStringAttribute("name"); configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); } else { String alias = child.getStringAttribute("alias"); String type = child.getStringAttribute("type"); try { Class clazz = Resources.classForName(type); if (alias == null) { typeAliasRegistry.registerAlias(clazz); } else { typeAliasRegistry.registerAlias(alias, clazz); } } catch (ClassNotFoundException e) { throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); } } } } private void pluginsElement(XNode context) throws Exception { if (context != null) { for (XNode child : context.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor() .newInstance(); interceptorInstance.setProperties(properties); configuration.addInterceptor(interceptorInstance); } } } private void objectFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties properties = context.getChildrenAsProperties(); ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance(); factory.setProperties(properties); configuration.setObjectFactory(factory); } } private void objectWrapperFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance(); configuration.setObjectWrapperFactory(factory); } } private void reflectorFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance(); configuration.setReflectorFactory(factory); } } private void propertiesElement(XNode context) throws Exception { if (context == null) { return; } Properties defaults = context.getChildrenAsProperties(); String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException( "The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } parser.setVariables(defaults); configuration.setVariables(defaults); } private void settingsElement(Properties props) { configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false)); configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType"))); configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler"))); configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false)); configuration.setArgNameBasedConstructorAutoMapping(booleanValueOf(props.getProperty("argNameBasedConstructorAutoMapping"), false)); configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType"))); configuration.setNullableOnForEach(booleanValueOf(props.getProperty("nullableOnForEach"), false)); // TODO 这里改变了原生行为 configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), true)); ((MybatisConfiguration) configuration).setUseGeneratedShortKey(booleanValueOf(props.getProperty("useGeneratedShortKey"), true)); } private void environmentsElement(XNode context) throws Exception { if (context == null) { return; } if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); break; } } } private void databaseIdProviderElement(XNode context) throws Exception { if (context == null) { return; } String type = context.getStringAttribute("type"); // awful patch to keep backward compatibility if ("VENDOR".equals(type)) { type = "DB_VENDOR"; } Properties properties = context.getChildrenAsProperties(); DatabaseIdProvider databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor() .newInstance(); databaseIdProvider.setProperties(properties); Environment environment = configuration.getEnvironment(); if (environment != null) { String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); configuration.setDatabaseId(databaseId); } } private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); } private DataSourceFactory dataSourceElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory."); } private void typeHandlersElement(XNode context) { if (context == null) { return; } for (XNode child : context.getChildren()) { if ("package".equals(child.getName())) { String typeHandlerPackage = child.getStringAttribute("name"); typeHandlerRegistry.register(typeHandlerPackage); } else { String javaTypeName = child.getStringAttribute("javaType"); String jdbcTypeName = child.getStringAttribute("jdbcType"); String handlerTypeName = child.getStringAttribute("handler"); Class javaTypeClass = resolveClass(javaTypeName); JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class typeHandlerClass = resolveClass(handlerTypeName); if (javaTypeClass != null) { if (jdbcType == null) { typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { typeHandlerRegistry.register(typeHandlerClass); } } } } private void mappersElement(XNode context) throws Exception { if (context == null) { return; } for (XNode child : context.getChildren()) { if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); try (InputStream inputStream = Resources.getResourceAsStream(resource)) { XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); try (InputStream inputStream = Resources.getUrlAsStream(url)) { XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } } else if (resource == null && url == null && mapperClass != null) { Class mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException( "A mapper element may only specify a url, resource or class, but not more than one."); } } } } private boolean isSpecifiedEnvironment(String id) { if (environment == null) { throw new BuilderException("No environment specified."); } if (id == null) { throw new BuilderException("Environment requires an id attribute."); } return environment.equals(id); } private static Configuration newConfig(Class configClass) { try { return configClass.getDeclaredConstructor().newInstance(); } catch (Exception ex) { throw new BuilderException("Failed to create a new Configuration instance.", ex); } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLLanguageDriver.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils; import org.apache.ibatis.builder.IncompleteElementException; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.parsing.XNode; import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; import org.apache.ibatis.session.Configuration; import java.util.List; /** * 继承 {@link XMLLanguageDriver} 重装构造函数, 使用自己的 MybatisParameterHandler * * @author hubin * @since 2016-03-11 */ public class MybatisXMLLanguageDriver extends XMLLanguageDriver { @Override public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { // 使用 MybatisParameterHandler 而不是 ParameterHandler return new MybatisParameterHandler(mappedStatement, parameterObject, boundSql); } @Override public SqlSource createSqlSource(Configuration configuration, XNode script, Class parameterType) { MybatisXMLScriptBuilder builder = new MybatisXMLScriptBuilder(configuration, script, parameterType); return builder.parseScriptNode(); } @Override public SqlSource createSqlSource(Configuration configuration, String script, Class parameterType) { GlobalConfig.DbConfig config = GlobalConfigUtils.getDbConfig(configuration); if (config.isReplacePlaceholder()) { List find = SqlUtils.findPlaceholder(script); if (CollectionUtils.isNotEmpty(find)) { try { script = SqlUtils.replaceSqlPlaceholder(script, find, config.getEscapeSymbol()); } catch (MybatisPlusException e) { throw new IncompleteElementException(e); } } } return super.createSqlSource(configuration, script, parameterType); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLMapperBuilder.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import org.apache.ibatis.builder.BaseBuilder; import org.apache.ibatis.builder.BuilderException; import org.apache.ibatis.builder.CacheRefResolver; import org.apache.ibatis.builder.IncompleteElementException; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.apache.ibatis.builder.ResultMapResolver; import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; import org.apache.ibatis.builder.xml.XMLStatementBuilder; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.io.Resources; import org.apache.ibatis.mapping.Discriminator; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.mapping.ResultFlag; import org.apache.ibatis.mapping.ResultMap; import org.apache.ibatis.mapping.ResultMapping; import org.apache.ibatis.parsing.XNode; import org.apache.ibatis.parsing.XPathParser; import org.apache.ibatis.reflection.MetaClass; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import java.io.InputStream; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; /** * 重写了{@link org.apache.ibatis.builder.xml.XMLMapperBuilder} 替换了 {@link org.apache.ibatis.builder.MapperBuilderAssistant} * * @author nieqiurong * @since 3.5.6 */ public class MybatisXMLMapperBuilder extends BaseBuilder { private final XPathParser parser; private final MapperBuilderAssistant builderAssistant; private final Map sqlFragments; private final String resource; @Deprecated public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map sqlFragments, String namespace) { this(reader, configuration, resource, sqlFragments); this.builderAssistant.setCurrentNamespace(namespace); } @Deprecated public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map sqlFragments) { this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration, resource, sqlFragments); } public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map sqlFragments, String namespace) { this(inputStream, configuration, resource, sqlFragments); this.builderAssistant.setCurrentNamespace(namespace); } public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map sqlFragments) { this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration, resource, sqlFragments); } private MybatisXMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map sqlFragments) { super(configuration); this.builderAssistant = new MybatisMapperBuilderAssistant(configuration, resource); this.parser = parser; this.sqlFragments = sqlFragments; this.resource = resource; } public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } configuration.parsePendingResultMaps(false); configuration.parsePendingCacheRefs(false); configuration.parsePendingStatements(false); } public XNode getSqlFragment(String refid) { return sqlFragments.get(refid); } private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.isEmpty()) { throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); parameterMapElement(context.evalNodes("/mapper/parameterMap")); resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } } private void buildStatementFromContext(List list) { if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null); } private void buildStatementFromContext(List list, String requiredDatabaseId) { for (XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } } private void cacheRefElement(XNode context) { if (context != null) { configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace")); CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace")); try { cacheRefResolver.resolveCacheRef(); } catch (IncompleteElementException e) { configuration.addIncompleteCacheRef(cacheRefResolver); } } } private void cacheElement(XNode context) { if (context != null) { String type = context.getStringAttribute("type", "PERPETUAL"); Class typeClass = typeAliasRegistry.resolveAlias(type); String eviction = context.getStringAttribute("eviction", "LRU"); Class evictionClass = typeAliasRegistry.resolveAlias(eviction); Long flushInterval = context.getLongAttribute("flushInterval"); Integer size = context.getIntAttribute("size"); boolean readWrite = !context.getBooleanAttribute("readOnly", false); boolean blocking = context.getBooleanAttribute("blocking", false); Properties props = context.getChildrenAsProperties(); builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } } private void parameterMapElement(List list) { for (XNode parameterMapNode : list) { String id = parameterMapNode.getStringAttribute("id"); String type = parameterMapNode.getStringAttribute("type"); Class parameterClass = resolveClass(type); List parameterNodes = parameterMapNode.evalNodes("parameter"); List parameterMappings = new ArrayList<>(); for (XNode parameterNode : parameterNodes) { String property = parameterNode.getStringAttribute("property"); String javaType = parameterNode.getStringAttribute("javaType"); String jdbcType = parameterNode.getStringAttribute("jdbcType"); String resultMap = parameterNode.getStringAttribute("resultMap"); String mode = parameterNode.getStringAttribute("mode"); String typeHandler = parameterNode.getStringAttribute("typeHandler"); Integer numericScale = parameterNode.getIntAttribute("numericScale"); ParameterMode modeEnum = resolveParameterMode(mode); Class javaTypeClass = resolveClass(javaType); JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); Class> typeHandlerClass = resolveClass(typeHandler); ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale); parameterMappings.add(parameterMapping); } builderAssistant.addParameterMap(id, parameterClass, parameterMappings); } } private void resultMapElements(List list) { for (XNode resultMapNode : list) { try { resultMapElement(resultMapNode); } catch (IncompleteElementException e) { // ignore, it will be retried } } } private ResultMap resultMapElement(XNode resultMapNode) { return resultMapElement(resultMapNode, Collections.emptyList(), null); } private ResultMap resultMapElement(XNode resultMapNode, List additionalResultMappings, Class enclosingType) { ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); Class typeClass = resolveClass(type); if (typeClass == null) { typeClass = inheritEnclosingType(resultMapNode, enclosingType); } Discriminator discriminator = null; List resultMappings = new ArrayList<>(additionalResultMappings); List resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) { if ("constructor".equals(resultChild.getName())) { processConstructorElement(resultChild, typeClass, resultMappings); } else if ("discriminator".equals(resultChild.getName())) { discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else { List flags = new ArrayList<>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); String extend = resultMapNode.getStringAttribute("extends"); Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; } } protected Class inheritEnclosingType(XNode resultMapNode, Class enclosingType) { if ("association".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute("resultMap") == null) { String property = resultMapNode.getStringAttribute("property"); if (property != null && enclosingType != null) { MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory()); return metaResultType.getSetterType(property); } } else if ("case".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute("resultMap") == null) { return enclosingType; } return null; } private void processConstructorElement(XNode resultChild, Class resultType, List resultMappings) { List argChildren = resultChild.getChildren(); for (XNode argChild : argChildren) { List flags = new ArrayList<>(); flags.add(ResultFlag.CONSTRUCTOR); if ("idArg".equals(argChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags)); } } private Discriminator processDiscriminatorElement(XNode context, Class resultType, List resultMappings) { String column = context.getStringAttribute("column"); String javaType = context.getStringAttribute("javaType"); String jdbcType = context.getStringAttribute("jdbcType"); String typeHandler = context.getStringAttribute("typeHandler"); Class javaTypeClass = resolveClass(javaType); Class> typeHandlerClass = resolveClass(typeHandler); JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); Map discriminatorMap = new HashMap<>(); for (XNode caseChild : context.getChildren()) { String value = caseChild.getStringAttribute("value"); String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings, resultType)); discriminatorMap.put(value, resultMap); } return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap); } private void sqlElement(List list) { if (configuration.getDatabaseId() != null) { sqlElement(list, configuration.getDatabaseId()); } sqlElement(list, null); } private void sqlElement(List list, String requiredDatabaseId) { for (XNode context : list) { String databaseId = context.getStringAttribute("databaseId"); String id = context.getStringAttribute("id"); id = builderAssistant.applyCurrentNamespace(id, false); if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) { sqlFragments.put(id, context); } } } private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) { if (requiredDatabaseId != null) { return requiredDatabaseId.equals(databaseId); } if (databaseId != null) { return false; } if (!this.sqlFragments.containsKey(id)) { return true; } // skip this fragment if there is a previous one with a not null databaseId XNode context = this.sqlFragments.get(id); return context.getStringAttribute("databaseId") == null; } private ResultMapping buildResultMappingFromContext(XNode context, Class resultType, List flags) { String property; if (flags.contains(ResultFlag.CONSTRUCTOR)) { property = context.getStringAttribute("name"); } else { property = context.getStringAttribute("property"); } String column = context.getStringAttribute("column"); String javaType = context.getStringAttribute("javaType"); String jdbcType = context.getStringAttribute("jdbcType"); String nestedSelect = context.getStringAttribute("select"); String nestedResultMap = context.getStringAttribute("resultMap", () -> processNestedResultMappings(context, Collections.emptyList(), resultType)); String notNullColumn = context.getStringAttribute("notNullColumn"); String columnPrefix = context.getStringAttribute("columnPrefix"); String typeHandler = context.getStringAttribute("typeHandler"); String resultSet = context.getStringAttribute("resultSet"); String foreignColumn = context.getStringAttribute("foreignColumn"); boolean lazy = "lazy" .equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager")); Class javaTypeClass = resolveClass(javaType); Class> typeHandlerClass = resolveClass(typeHandler); JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy); } private String processNestedResultMappings(XNode context, List resultMappings, Class enclosingType) { if (Arrays.asList("association", "collection", "case").contains(context.getName()) && context.getStringAttribute("select") == null) { validateCollection(context, enclosingType); ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType); return resultMap.getId(); } return null; } protected void validateCollection(XNode context, Class enclosingType) { if ("collection".equals(context.getName()) && context.getStringAttribute("resultMap") == null && context.getStringAttribute("javaType") == null) { MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory()); String property = context.getStringAttribute("property"); if (!metaResultType.hasSetter(property)) { throw new BuilderException( "Ambiguous collection type for property '" + property + "'. You must specify 'javaType' or 'resultMap'."); } } } private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class boundType = null; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { // ignore, bound type is not required } if (boundType != null && !configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType); } } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLScriptBuilder.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import org.apache.ibatis.builder.BaseBuilder; import org.apache.ibatis.builder.BuilderException; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.parsing.XNode; import org.apache.ibatis.scripting.defaults.RawSqlSource; import org.apache.ibatis.scripting.xmltags.ChooseSqlNode; import org.apache.ibatis.scripting.xmltags.DynamicSqlSource; import org.apache.ibatis.scripting.xmltags.ForEachSqlNode; import org.apache.ibatis.scripting.xmltags.IfSqlNode; import org.apache.ibatis.scripting.xmltags.MixedSqlNode; import org.apache.ibatis.scripting.xmltags.SetSqlNode; import org.apache.ibatis.scripting.xmltags.SqlNode; import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode; import org.apache.ibatis.scripting.xmltags.TextSqlNode; import org.apache.ibatis.scripting.xmltags.TrimSqlNode; import org.apache.ibatis.scripting.xmltags.VarDeclSqlNode; import org.apache.ibatis.scripting.xmltags.WhereSqlNode; import org.apache.ibatis.scripting.xmltags.XMLScriptBuilder; import org.apache.ibatis.session.Configuration; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** *

    试验性功能,解决mybatis堆内存过大的问题(看后期mybatis官方会不会解决堆内存占用问题)

    *

    由于大量重复sql节点,导致堆内存过大(本质上属于string导致的堆内存增大问题)

    *

    例如: {@code create_time=#{createTime}}等公共字段

    *

    * 解决方案: 将生成的xml节点值写入字符串常量池,减少后面重复字符串导致的问题 *

  • * 方案一: 缓存一些特定的mybatis-plus生成的占位符与表达式和项目公共字段(改动有点多,需要增加一些特定xml属性来标记是mybatis-plus生成的节点,减少堆内存较少) *
  • *
  • * 方案二: 直接将节点内容intern写入至字符串常量池(改动少,减少堆内存多,弊端可能会将一些无重复的字符串写入至常量池) *
  • *
  • * 方案三: 模拟字符串常量池,减少重复字符串写入至堆内存(代码相对来说不好看点) *
  • *

    * * @author nieqiurong * @see XMLScriptBuilder * @since 3.5.3.2 */ public class MybatisXMLScriptBuilder extends BaseBuilder { private final XNode context; private boolean isDynamic; private final Class parameterType; private final Map nodeHandlerMap = new HashMap<>(); private static final Map> CACHE_STRING = new WeakHashMap<>(); public MybatisXMLScriptBuilder(Configuration configuration, XNode context) { this(configuration, context, null); } public MybatisXMLScriptBuilder(Configuration configuration, XNode context, Class parameterType) { super(configuration); this.context = context; this.parameterType = parameterType; initNodeHandlerMap(); } private void initNodeHandlerMap() { nodeHandlerMap.put("trim", new TrimHandler()); nodeHandlerMap.put("where", new WhereHandler()); nodeHandlerMap.put("set", new SetHandler()); nodeHandlerMap.put("foreach", new ForEachHandler()); nodeHandlerMap.put("if", new IfHandler()); nodeHandlerMap.put("choose", new ChooseHandler()); nodeHandlerMap.put("when", new IfHandler()); nodeHandlerMap.put("otherwise", new OtherwiseHandler()); nodeHandlerMap.put("bind", new BindHandler()); } public SqlSource parseScriptNode() { MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource; if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; } /** * 也可以将XNode节点包裹增强一下,来减少方法的引用,但需要创建对象,这里就直接将每个地方手动改一下了. */ private synchronized static String cacheStr(String str) { if (str == null) { return null; } String value = CACHE_STRING.computeIfAbsent(str, WeakReference::new).get(); //增强安全处理一下,如果实在是GC处理掉了(可能性小),就返回原来. if (StringUtils.isNotBlank(value)) { return value; } return str; } private static final StaticTextSqlNode SPACE_SQL_NODE = new StaticTextSqlNode(StringPool.SPACE); private static final Pattern PATTERN = Pattern.compile("^\\s+|\\s+$"); private static final Map CACHE_EMPTY_SQL_NODE = new ConcurrentHashMap<>(); /** * 将前后空白符替换成空格 * * @param str 字符串 (非空) * @return 处理后文本 * @since 3.5.10.1 */ public static String replaceLeadingAndTrailingWhitespace(String str) { Matcher matcher = PATTERN.matcher(str); return matcher.replaceAll(StringPool.SPACE); } protected MixedSqlNode parseDynamicTags(XNode node) { List contents = new ArrayList<>(); NodeList children = node.getNode().getChildNodes(); for (int i = 0; i < children.getLength(); i++) { XNode child = node.newXNode(children.item(i)); if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) { String text = cacheStr(child.getStringBody("")); if (text.trim().isEmpty()) { StaticTextSqlNode staticTextSqlNode = CACHE_EMPTY_SQL_NODE.computeIfAbsent(text, StaticTextSqlNode::new); contents.add(staticTextSqlNode); continue; } TextSqlNode textSqlNode = new TextSqlNode(text); if (textSqlNode.isDynamic()) { contents.add(textSqlNode); isDynamic = true; } else { contents.add(new StaticTextSqlNode(text)); } } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628 String nodeName = child.getNode().getNodeName(); NodeHandler handler = nodeHandlerMap.get(nodeName); if (handler == null) { throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement."); } handler.handleNode(child, contents); isDynamic = true; } } return new MixedSqlNode(contents); } private interface NodeHandler { void handleNode(XNode nodeToHandle, List targetContents); } private class BindHandler implements NodeHandler { public BindHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List targetContents) { final String name = cacheStr(nodeToHandle.getStringAttribute("name")); final String expression = cacheStr(nodeToHandle.getStringAttribute("value")); final VarDeclSqlNode node = new VarDeclSqlNode(name, expression); targetContents.add(node); } } private class TrimHandler implements NodeHandler { public TrimHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String prefix = cacheStr(nodeToHandle.getStringAttribute("prefix")); String prefixOverrides = cacheStr(nodeToHandle.getStringAttribute("prefixOverrides")); String suffix = cacheStr(nodeToHandle.getStringAttribute("suffix")); String suffixOverrides = cacheStr(nodeToHandle.getStringAttribute("suffixOverrides")); TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides); targetContents.add(trim); } } private class WhereHandler implements NodeHandler { public WhereHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode); targetContents.add(where); } } private class SetHandler implements NodeHandler { public SetHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode); targetContents.add(set); } } private class ForEachHandler implements NodeHandler { public ForEachHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String collection = cacheStr(nodeToHandle.getStringAttribute("collection")); Boolean nullable = nodeToHandle.getBooleanAttribute("nullable"); String item = cacheStr(nodeToHandle.getStringAttribute("item")); String index = cacheStr(nodeToHandle.getStringAttribute("index")); String open = cacheStr(nodeToHandle.getStringAttribute("open")); String close = cacheStr(nodeToHandle.getStringAttribute("close")); String separator = cacheStr(nodeToHandle.getStringAttribute("separator")); ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, nullable, index, item, open, close, separator); targetContents.add(forEachSqlNode); } } private class IfHandler implements NodeHandler { public IfHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String test = cacheStr(nodeToHandle.getStringAttribute("test")); IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test); targetContents.add(ifSqlNode); } } private class OtherwiseHandler implements NodeHandler { public OtherwiseHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); targetContents.add(mixedSqlNode); } } private class ChooseHandler implements NodeHandler { public ChooseHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List targetContents) { List whenSqlNodes = new ArrayList<>(); List otherwiseSqlNodes = new ArrayList<>(); handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes); SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes); ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode); targetContents.add(chooseSqlNode); } private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List ifSqlNodes, List defaultSqlNodes) { List children = chooseSqlNode.getChildren(); for (XNode child : children) { String nodeName = child.getNode().getNodeName(); NodeHandler handler = nodeHandlerMap.get(nodeName); if (handler instanceof IfHandler) { handler.handleNode(child, ifSqlNodes); } else if (handler instanceof OtherwiseHandler) { handler.handleNode(child, defaultSqlNodes); } } } private SqlNode getDefaultSqlNode(List defaultSqlNodes) { SqlNode defaultSqlNode = null; if (defaultSqlNodes.size() == 1) { defaultSqlNode = defaultSqlNodes.get(0); } else if (defaultSqlNodes.size() > 1) { throw new BuilderException("Too many default (otherwise) elements in choose statement."); } return defaultSqlNode; } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/assist/AbstractSqlRunner.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.assist; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import org.apache.ibatis.parsing.GenericTokenParser; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; import static com.baomidou.mybatisplus.core.toolkit.StringPool.DOT; import static com.baomidou.mybatisplus.core.toolkit.StringPool.HASH_LEFT_BRACE; import static com.baomidou.mybatisplus.core.toolkit.StringPool.LEFT_BRACE; import static com.baomidou.mybatisplus.core.toolkit.StringPool.LEFT_SQ_BRACKET; import static com.baomidou.mybatisplus.core.toolkit.StringPool.RIGHT_BRACE; import static com.baomidou.mybatisplus.core.toolkit.StringPool.RIGHT_SQ_BRACKET; /** * @author nieqiurong * @since 3.5.12 */ public abstract class AbstractSqlRunner implements ISqlRunner { /** * 默认分词处理器 * * @since 3.5.12 */ private static final GenericTokenParser DEFAULT_TOKEN_PARSER = new GenericTokenParser(LEFT_BRACE, RIGHT_BRACE, content -> HASH_LEFT_BRACE + content + RIGHT_BRACE); /** * 校验索引正则 * * @since 3.5.12 */ private static final Pattern INDEX_PATTERN = Pattern.compile("^\\d+$"); /** * 第一个值参数key * * @since 3.5.12 */ private static final String ARG0 = "arg0"; /** * 获取执行语句 (将原始占位符语句转换为标准占位符语句) * * @param sql 原始sql * @param args 参数 * @return 执行语句 (带参数占位符) * @since 3.5.12 */ protected String parse(String sql, Object... args) { if (args != null && args.length == 1) { Object arg = args[0]; Class clazz = arg.getClass(); return new GenericTokenParser(LEFT_BRACE, RIGHT_BRACE, content -> { if (INDEX_PATTERN.matcher(content).matches()) { if (arg instanceof Collection || clazz.isArray()) { return HASH_LEFT_BRACE + ARG0 + LEFT_SQ_BRACKET + content + RIGHT_SQ_BRACKET + RIGHT_BRACE; } return HASH_LEFT_BRACE + content + RIGHT_BRACE; } else { return HASH_LEFT_BRACE + ARG0 + DOT + content + RIGHT_BRACE; } }).parse(sql); } return DEFAULT_TOKEN_PARSER.parse(sql); } /** * 获取参数列表 * * @param args 参数(单参数时,支持使用Map,List,Array,JavaBean访问) * @return 参数map * @since 3.5.12 */ protected Map getParams(Object... args) { if (args != null && args.length > 0) { Map params = CollectionUtils.newHashMapWithExpectedSize(args.length); for (int i = 0; i < args.length; i++) { Object arg = args[i]; if (i == 0) { params.put(ARG0, arg); } params.put(String.valueOf(i), arg); } return params; } return new HashMap<>(); } /** * 获取sqlMap参数 *

    * 自3.5.12开始,(当传入的参数是单参数时,支持使用Map,Array,List,JavaBean) *

  • 当参数为 Map 时可通过{key}进行属性访问 *
  • 当参数为 JavaBean 时可通过{property}进行属性访问 *
  • 当参数为 List 时直接访问索引 {0}
  • *
  • 当参数为 Array 时直接访问索引 {0}
  • *

    * * @param sql 指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @return 参数集合 */ protected Map sqlMap(String sql, Object... args) { Map sqlMap = getParams(args); sqlMap.put(SQL, parse(sql, args)); return sqlMap; } /** *

    * 自3.5.12开始,(当传入的参数是单参数时,支持使用Map,Array,List,JavaBean) *

  • 当参数为 Map 时可通过{key}进行属性访问 *
  • 当参数为 JavaBean 时可通过{property}进行属性访问 *
  • 当参数为 List 时直接访问索引 {0}
  • *
  • 当参数为 Array 时直接访问索引 {0}
  • *

    * * @param sql 指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param page 分页模型 * @param args 参数 * @return 参数集合 */ protected Map sqlMap(String sql, IPage page, Object... args) { Map sqlMap = getParams(args); sqlMap.put(PAGE, page); sqlMap.put(SQL, parse(sql, args)); return sqlMap; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/assist/ISqlRunner.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.assist; import com.baomidou.mybatisplus.core.injector.SqlRunnerInjector; import com.baomidou.mybatisplus.core.metadata.IPage; import java.util.List; import java.util.Map; /** * SqlRunner执行接口 *

    * 自3.5.12开始,(当传入的参数是单参数时,支持使用Map,Array,List,JavaBean) *

  • 当参数为 Map 时可通过{key}进行属性访问 *
  • 当参数为 JavaBean 时可通过{property}进行属性访问 *
  • 当参数为 List 时直接访问索引 {0}
  • *
  • 当参数为 Array 时直接访问索引 {0}
  • *

    * * @author yuxiaobin, nieqiurong * @since 2018/2/7 */ public interface ISqlRunner { /** * INSERT 语句 */ String INSERT = "com.baomidou.mybatisplus.core.mapper.SqlRunner.Insert"; /** * DELETE 语句 */ String DELETE = "com.baomidou.mybatisplus.core.mapper.SqlRunner.Delete"; /** * UPDATE 语句 */ String UPDATE = "com.baomidou.mybatisplus.core.mapper.SqlRunner.Update"; /** * SELECT_LIST 语句 */ String SELECT_LIST = "com.baomidou.mybatisplus.core.mapper.SqlRunner.SelectList"; /** * SELECT_OBJS 语句 */ String SELECT_OBJS = "com.baomidou.mybatisplus.core.mapper.SqlRunner.SelectObjs"; /** * COUNT 语句 */ String COUNT = "com.baomidou.mybatisplus.core.mapper.SqlRunner.Count"; /** * 注入SQL脚本 * * @deprecated 3.5.12 {@link SqlRunnerInjector#SQL_SCRIPT} */ @Deprecated String SQL_SCRIPT = "${sql}"; /** * sql访问参数 */ String SQL = "sql"; /** * page访问参数 */ String PAGE = "page"; /** * 执行插入语句 * * @param sql 指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @return 插入结果 */ boolean insert(String sql, Object... args); /** * 执行删除语句 * * @param sql 指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @return 删除结果 */ boolean delete(String sql, Object... args); /** * 执行更新语句 * * @param sql 指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @return 更新结果 */ boolean update(String sql, Object... args); /** * 根据sql查询Map结果集 *

    SqlRunner.db().selectList("select * from tbl_user where name={0}", "Caratacus")

    * * @param sql sql语句,可添加参数,指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数列表 * @return 结果集 */ List> selectList(String sql, Object... args); /** * 根据sql查询一个字段值的结果集 *

    注意:该方法只会返回一个字段的值, 如果需要多字段,请参考{@link #selectList(String, Object...)}

    * * @param sql sql语句,可添加参数,指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @return 结果集 */ List selectObjs(String sql, Object... args); /** * 根据sql查询一个字段值的一条结果 *

    注意:该方法只会返回一个字段的值, 如果需要多字段,请参考{@link #selectOne(String, Object...)}

    * * @param sql sql语句,可添加参数,指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @return 结果 */ Object selectObj(String sql, Object... args); /** * 查询总数 * * @param sql sql语句,可添加参数,指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @return 总记录数 */ long selectCount(String sql, Object... args); /** * 获取单条记录 * * @param sql sql语句,可添加参数,指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @return 单行结果集 (当执行语句返回多条记录时,只会选取第一条记录) */ Map selectOne(String sql, Object... args); /** * 分页查询 * * @param page 分页对象 * @param sql sql语句,可添加参数,指定参数的格式: {0}, {1} 或者 {property1}, {property2} * @param args 参数 * @param E * @return 分页数据 */ >> E selectPage(E page, String sql, Object... args); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/assist/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 增强辅助相关 */ package com.baomidou.mybatisplus.core.assist; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/batch/BatchMethod.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.batch; import org.apache.ibatis.mapping.MappedStatement; /** * 批量执行方法 * * @author nieqiurong * @since 3.5.4 */ public class BatchMethod { /** * 执行的{@link MappedStatement#getId()} */ private final String statementId; /** * 方法参数转换器,默认传递批量的entity的参数 */ private ParameterConvert parameterConvert = (entity) -> entity; public BatchMethod(String statementId) { this.statementId = statementId; } public BatchMethod(String statementId, ParameterConvert parameterConvert) { this.statementId = statementId; this.parameterConvert = parameterConvert; } public String getStatementId() { return statementId; } public ParameterConvert getParameterConvert() { return parameterConvert; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/batch/BatchSqlSession.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.batch; import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 当使用Batch混合查询时,每次都会将原来的结果集清空,建议使用Batch时就不要混合使用select了 (后面看看要不要改成动态代理把...) * * @author nieqiurong * @since 3.5.4 */ public class BatchSqlSession { private final SqlSession sqlSession; private final List resultBatchList = new ArrayList<>(); public BatchSqlSession(SqlSession sqlSession) { this.sqlSession = sqlSession; } public T selectOne(String statement) { resultBatchList.addAll(sqlSession.flushStatements()); return sqlSession.selectOne(statement); } public T selectOne(String statement, Object parameter) { resultBatchList.addAll(sqlSession.flushStatements()); return sqlSession.selectOne(statement, parameter); } public List selectList(String statement) { resultBatchList.addAll(sqlSession.flushStatements()); return sqlSession.selectList(statement); } public List selectList(String statement, Object parameter) { resultBatchList.addAll(sqlSession.flushStatements()); return sqlSession.selectList(statement, parameter); } public List selectList(String statement, Object parameter, RowBounds rowBounds) { resultBatchList.addAll(sqlSession.flushStatements()); return sqlSession.selectList(statement, parameter, rowBounds); } public Map selectMap(String statement, String mapKey) { resultBatchList.addAll(sqlSession.flushStatements()); return sqlSession.selectMap(statement, mapKey); } public Map selectMap(String statement, Object parameter, String mapKey) { resultBatchList.addAll(sqlSession.flushStatements()); return sqlSession.selectMap(statement, parameter, mapKey); } public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { resultBatchList.addAll(sqlSession.flushStatements()); return sqlSession.selectMap(statement, parameter, mapKey, rowBounds); } public List getResultBatchList() { return resultBatchList; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/batch/MybatisBatch.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.batch; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.StringPool; import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.BiPredicate; import java.util.function.Function; /** *
  • 事务需要自行控制
  • *
  • 批次数据尽量自行切割处理
  • *
  • 关于事务必须执行到flushStatements才具有意义{@link org.apache.ibatis.executor.BatchExecutor#doFlushStatements(boolean)} *
  • 返回值为批处理结果,如果对返回值比较关心的可接收判断处理
  • *
  • saveOrUpdate尽量少用把,保持批处理为简单的插入或更新
  • *
  • 关于saveOrUpdate中的sqlSession,如果执行了select操作的话,BatchExecutor都会触发一次flushStatements,为了保证结果集,故使用包装了部分sqlSession查询操作
  • *
  • autoCommit参数,在spring下使用的是{@link org.mybatis.spring.transaction.SpringManagedTransaction},控制无效,只能通过datasource控制(建议不要修改),单独使用mybatis下{@link org.apache.ibatis.transaction.jdbc.JdbcTransaction}是可用的
  • *
     *     Spring示例:
     * 		transactionTemplate.execute(new TransactionCallback>() {
     *            {@code @Override}
     * 			public List doInTransaction(TransactionStatus status) {
     * 				MybatisBatch.Method method = new MybatisBatch.Method<>(DemoMapper.class);
     * 				return new MybatisBatch<>(sqlSessionFactory,demoList).execute(true, method.insert());
     *            }
     *        });
     * 
    * * @author nieqiurong * @since 3.5.4 */ public class MybatisBatch { private final SqlSessionFactory sqlSessionFactory; private final Collection dataList; private final int batchSize; public MybatisBatch(SqlSessionFactory sqlSessionFactory, Collection dataList) { this.sqlSessionFactory = sqlSessionFactory; this.dataList = dataList; this.batchSize = Constants.DEFAULT_BATCH_SIZE; } public MybatisBatch(SqlSessionFactory sqlSessionFactory, Collection dataList, int batchSize) { this.sqlSessionFactory = sqlSessionFactory; this.dataList = dataList; this.batchSize = batchSize; } /** * 执行批量操作 * * @param statement 执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert ) * @return 批处理结果 */ public List execute(String statement) { return execute(false, statement, (entity) -> entity); } /** * 执行批量操作 * * @param statement 执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert ) * @param parameterConvert 参数转换器 * @return 批处理结果 */ public List execute(String statement, ParameterConvert parameterConvert) { return execute(false, statement, parameterConvert); } /** * 执行批量操作 * * @param autoCommit 是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction}) * @param statement 执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert ) * @return 批处理结果 */ public List execute(boolean autoCommit, String statement) { return execute(autoCommit, statement, (entity) -> entity); } /** * 执行批量操作 * * @param batchMethod 批量操作方法 * @return 批处理结果 */ public List execute(BatchMethod batchMethod) { return execute(false, batchMethod); } /** * 执行批量操作 * * @param autoCommit 是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction}) * @param batchMethod 批量操作方法 * @return 批处理结果 */ public List execute(boolean autoCommit, BatchMethod batchMethod) { return execute(autoCommit, batchMethod.getStatementId(), batchMethod.getParameterConvert()); } /** * 执行批量操作 * * @param autoCommit 是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction}) * @param statement 执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert ) * @param parameterConvert 参数转换器 * @return 批处理结果 */ public List execute(boolean autoCommit, String statement, ParameterConvert parameterConvert) { List resultList = new ArrayList<>(dataList.size()); try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, autoCommit)) { List> split = CollectionUtils.split(dataList, batchSize); for (List splitedList : split) { for (T data : splitedList) { sqlSession.update(statement, toParameter(parameterConvert, data)); } resultList.addAll(sqlSession.flushStatements()); if (!autoCommit) { sqlSession.commit(); } } return resultList; } } /** * 批量保存或更新 * 这里需要注意一下,如果在insertPredicate里判断调用其他sqlSession(类似mapper.xxx)时,要注意一级缓存问题或数据感知问题(因为当前会话数据还未提交) * 举个例子(事务开启状态下): * 如果当前批次里面执行两个主键相同的数据,当调用mapper.selectById时,如果数据库未有这条记录,在同个sqlSession下,由于一级缓存的问题,下次再查就还是null,导致插入主键冲突, * 但使用 {@link BatchSqlSession}时,由于每次select操作都会触发一次flushStatements,就会执行更新操作 * * @param insertMethod 插入方法 * @param insertPredicate 插入条件 (当条件满足时执行插入方法,否则执行更新方法) * @param updateMethod 更新方法 * @return 批处理结果 */ public List saveOrUpdate(BatchMethod insertMethod, BiPredicate insertPredicate, BatchMethod updateMethod) { return saveOrUpdate(false, insertMethod, insertPredicate, updateMethod); } /** * 批量保存或更新 * 这里需要注意一下,如果在insertPredicate里判断调用其他sqlSession(类似mapper.xxx)时,要注意一级缓存问题或数据感知问题(因为当前会话数据还未提交) * 举个例子(事务开启状态下): * 如果当前批次里面执行两个主键相同的数据,当调用mapper.selectById时,如果数据库未有这条记录,在同个sqlSession下,由于一级缓存的问题,下次再查就还是null,导致插入主键冲突, * 但使用 {@link BatchSqlSession}时,由于每次select操作都会触发一次flushStatements,就会执行更新操作 * * @param autoCommit 是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction}) * @param insertMethod 插入方法 * @param insertPredicate 插入条件 (当条件满足时执行插入方法,否则执行更新方法) * @param updateMethod 更新方法 * @return 批处理结果 */ public List saveOrUpdate(boolean autoCommit, BatchMethod insertMethod, BiPredicate insertPredicate, BatchMethod updateMethod) { List resultList = new ArrayList<>(); try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, autoCommit)) { BatchSqlSession session = new BatchSqlSession(sqlSession); for (T data : dataList) { if (insertPredicate.test(session, data)) { sqlSession.insert(insertMethod.getStatementId(), toParameter(insertMethod.getParameterConvert(), data)); } else { sqlSession.update(updateMethod.getStatementId(), toParameter(updateMethod.getParameterConvert(), data)); } } resultList.addAll(sqlSession.flushStatements()); resultList.addAll(session.getResultBatchList()); if (!autoCommit) { sqlSession.commit(); } return resultList; } } /** * 参数转换 * * @param parameterConvert 参数转换器 * @param data 参数 * @return 方法参数 */ protected Object toParameter(ParameterConvert parameterConvert, T data) { return parameterConvert != null ? parameterConvert.convert(data) : data; } /** * 内置方法简化调用 * * @param 泛型参数(实体) */ public static class Method { /** * 命名空间 */ private final String namespace; public Method(Class mapperClass) { this.namespace = mapperClass.getName(); } /** * 新增方法 * * @return 新增方法 */ public BatchMethod insert() { return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.INSERT_ONE.getMethod()); } /** * 新增方法 * * @param function 转换函数 * @param 实体 * @return 新增方法 */ public BatchMethod insert(Function function) { return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.INSERT_ONE.getMethod(), function::apply); } /** * 更新方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#updateById(java.lang.Object)} * * @return 更新方法 */ public BatchMethod updateById() { return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.UPDATE_BY_ID.getMethod(), (entity) -> { Map param = new HashMap<>(); param.put(Constants.ENTITY, entity); return param; }); } /** * 更新方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#updateById(java.lang.Object)} * * @param etFunction 实体转换 * @param 实体 * @return 更新方法 */ public BatchMethod updateById(Function etFunction) { return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.UPDATE_BY_ID.getMethod(), (parameter) -> { Map param = new HashMap<>(); param.put(Constants.ENTITY, etFunction.apply(parameter)); return param; }); } /** * 更新方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#update(java.lang.Object, com.baomidou.mybatisplus.core.conditions.Wrapper)} * * @param wrapperFunction 更新条件(不能为null) * @param 实体 * @return 更新方法 */ public BatchMethod update(Function> wrapperFunction) { return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.UPDATE.getMethod(), (parameter) -> { Map param = new HashMap<>(); param.put(Constants.WRAPPER, wrapperFunction.apply(parameter)); return param; }); } /** * 更新方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#update(java.lang.Object, com.baomidou.mybatisplus.core.conditions.Wrapper)} * * @param entityFunction 实体参数 * @param wrapperFunction wrapper参数 * @param 实体 * @return 更新方法 */ public BatchMethod update(Function entityFunction, Function> wrapperFunction) { return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.UPDATE.getMethod(), (parameter) -> { Map param = new HashMap<>(); param.put(Constants.ENTITY, entityFunction.apply(parameter)); param.put(Constants.WRAPPER, wrapperFunction.apply(parameter)); return param; }); } /** * 删除方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#deleteById(Object)} or {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#deleteById(Serializable)} * * @param function 参数转换 * @param 实体 * @return 删除方法 */ public BatchMethod deleteById(Function function) { return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.DELETE_BY_ID.getMethod(), function::apply); } /** * 删除方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#deleteById(Object)} or {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#deleteById(Serializable)} * * @param 实体 * @return 删除方法 */ @SuppressWarnings("TypeParameterHidesVisibleType") public BatchMethod deleteById() { return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.DELETE_BY_ID.getMethod()); } public BatchMethod get(String method) { return new BatchMethod<>(namespace + StringPool.DOT + method); } public BatchMethod get(String method, ParameterConvert parameterConvert) { return new BatchMethod<>(namespace + StringPool.DOT + method, parameterConvert); } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/batch/ParameterConvert.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.batch; /** * @author nieqiurong * @since 3.5.4 */ @FunctionalInterface public interface ParameterConvert { /** * 转换当前实体参数为mapper方法参数 * * @param parameter 参数对象 * @return mapper方法参数. */ Object convert(T parameter); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/AbstractLambdaWrapper.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.LambdaUtils; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache; import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import org.apache.ibatis.reflection.property.PropertyNamer; import java.util.List; import java.util.Map; import static java.util.stream.Collectors.joining; /** * Lambda 语法使用 Wrapper *

    统一处理解析 lambda 获取 column

    * * @author hubin miemie HCL * @since 2017-05-26 */ public abstract class AbstractLambdaWrapper> extends AbstractWrapper, Children> { private Map columnMap = null; private boolean initColumnMap = false; @Override @SafeVarargs protected final String columnsToString(SFunction... columns) { return columnsToString(true, columns); } @SafeVarargs protected final String columnsToString(boolean onlyColumn, SFunction... columns) { return columnsToString(onlyColumn, CollectionUtils.toList(columns)); } protected final String columnsToString(boolean onlyColumn, List> columns) { return columns.stream().map(i -> columnToString(i, onlyColumn)).collect(joining(StringPool.COMMA)); } @Override protected String columnToString(SFunction column) { return columnToString(column, true); } protected String columnToString(SFunction column, boolean onlyColumn) { ColumnCache cache = getColumnCache(column); return onlyColumn ? cache.getColumn() : cache.getColumnSelect(); } @Override @SafeVarargs public final Children groupBy(boolean condition, SFunction column, SFunction... columns) { return super.groupBy(condition, column, columns); } @Override @SafeVarargs public final Children orderBy(boolean condition, boolean isAsc, SFunction column, SFunction... columns) { return orderBy(condition, isAsc, column, CollectionUtils.toList(columns)); } @Override @SafeVarargs public final Children groupBy(SFunction column, SFunction... columns) { return doGroupBy(true, column, CollectionUtils.toList(columns)); } @Override public Children groupBy(boolean condition, SFunction column, List> columns) { return doGroupBy(condition,column,columns); } @Override @SafeVarargs public final Children orderByAsc(SFunction column, SFunction... columns) { return super.orderByAsc(column, columns); } @Override @SafeVarargs public final Children orderByAsc(boolean condition, SFunction column, SFunction... columns) { return super.orderByAsc(condition, column, columns); } @Override @SafeVarargs public final Children orderByDesc(SFunction column, SFunction... columns) { return super.orderByDesc(column, columns); } @Override @SafeVarargs public final Children orderByDesc(boolean condition, SFunction column, SFunction... columns) { return super.orderByDesc(condition, column, columns); } /** * 获取 SerializedLambda 对应的列信息,从 lambda 表达式中推测实体类 *

    * 如果获取不到列信息,那么本次条件组装将会失败 * * @return 列 * @throws com.baomidou.mybatisplus.core.exceptions.MybatisPlusException 获取不到列信息时抛出异常 */ protected ColumnCache getColumnCache(SFunction column) { LambdaMeta meta = LambdaUtils.extract(column); String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName()); Class instantiatedClass = meta.getInstantiatedClass(); tryInitCache(instantiatedClass); return getColumnCache(fieldName, instantiatedClass); } private void tryInitCache(Class lambdaClass) { if (!initColumnMap) { final Class entityClass = getEntityClass(); if (entityClass != null) { lambdaClass = entityClass; } columnMap = LambdaUtils.getColumnMap(lambdaClass); Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", lambdaClass.getName()); initColumnMap = true; } } private ColumnCache getColumnCache(String fieldName, Class lambdaClass) { ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName)); Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]", fieldName, lambdaClass.getName()); return columnCache; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/AbstractWrapper.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions; import com.baomidou.mybatisplus.annotation.OrderBy; import com.baomidou.mybatisplus.core.conditions.interfaces.Compare; import com.baomidou.mybatisplus.core.conditions.interfaces.Func; import com.baomidou.mybatisplus.core.conditions.interfaces.Join; import com.baomidou.mybatisplus.core.conditions.interfaces.Nested; import com.baomidou.mybatisplus.core.conditions.segments.ColumnSegment; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.enums.SqlKeyword; import com.baomidou.mybatisplus.core.enums.SqlLike; import com.baomidou.mybatisplus.core.toolkit.*; import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils; import lombok.Getter; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.baomidou.mybatisplus.core.enums.SqlKeyword.*; import static com.baomidou.mybatisplus.core.enums.WrapperKeyword.APPLY; import static java.util.stream.Collectors.joining; /** * 查询条件封装 * * @author hubin miemie HCL * @since 2017-05-26 */ @SuppressWarnings({"unchecked"}) public abstract class AbstractWrapper> extends Wrapper implements Compare, Nested, Join, Func { /** * 占位符 */ protected final Children typedThis = (Children) this; /** * 必要度量 */ protected AtomicInteger paramNameSeq; @Getter protected Map paramNameValuePairs; /** * 其他 */ protected SharedString paramAlias; protected SharedString lastSql; /** * SQL注释 */ protected SharedString sqlComment; /** * SQL起始语句 */ protected SharedString sqlFirst; /** * 数据库表映射实体类 */ private T entity; protected MergeSegments expression; /** * 实体类型(主要用于确定泛型以及取TableInfo缓存) */ private Class entityClass; @Override public T getEntity() { return entity; } public Children setEntity(T entity) { this.entity = entity; return typedThis; } public Class getEntityClass() { if (entityClass == null && entity != null) { entityClass = (Class) entity.getClass(); } return entityClass; } public Children setEntityClass(Class entityClass) { if (entityClass != null) { this.entityClass = entityClass; } return typedThis; } @Override public Children allEq(boolean condition, Map params, boolean null2IsNull) { if (condition && CollectionUtils.isNotEmpty(params)) { params.forEach((k, v) -> { if (StringUtils.checkValNotNull(v)) { eq(k, v); } else { if (null2IsNull) { isNull(k); } } }); } return typedThis; } @Override public Children allEq(boolean condition, BiPredicate filter, Map params, boolean null2IsNull) { if (condition && CollectionUtils.isNotEmpty(params)) { params.forEach((k, v) -> { if (filter.test(k, v)) { if (StringUtils.checkValNotNull(v)) { eq(k, v); } else { if (null2IsNull) { isNull(k); } } } }); } return typedThis; } @Override public Children eq(boolean condition, R column, Object val) { return addCondition(condition, column, EQ, val); } @Override public Children ne(boolean condition, R column, Object val) { return addCondition(condition, column, NE, val); } @Override public Children gt(boolean condition, R column, Object val) { return addCondition(condition, column, GT, val); } @Override public Children ge(boolean condition, R column, Object val) { return addCondition(condition, column, GE, val); } @Override public Children lt(boolean condition, R column, Object val) { return addCondition(condition, column, LT, val); } @Override public Children le(boolean condition, R column, Object val) { return addCondition(condition, column, LE, val); } @Override public Children like(boolean condition, R column, Object val) { return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT); } @Override public Children notLike(boolean condition, R column, Object val) { return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT); } @Override public Children likeLeft(boolean condition, R column, Object val) { return likeValue(condition, LIKE, column, val, SqlLike.LEFT); } @Override public Children likeRight(boolean condition, R column, Object val) { return likeValue(condition, LIKE, column, val, SqlLike.RIGHT); } @Override public Children notLikeLeft(boolean condition, R column, Object val) { return likeValue(condition, NOT_LIKE, column, val, SqlLike.LEFT); } @Override public Children notLikeRight(boolean condition, R column, Object val) { return likeValue(condition, NOT_LIKE, column, val, SqlLike.RIGHT); } @Override public Children between(boolean condition, R column, Object val1, Object val2) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), BETWEEN, () -> formatParam(null, val1), AND, () -> formatParam(null, val2))); } @Override public Children notBetween(boolean condition, R column, Object val1, Object val2) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_BETWEEN, () -> formatParam(null, val1), AND, () -> formatParam(null, val2))); } @Override public Children and(boolean condition, Consumer consumer) { return and(condition).addNestedCondition(condition, consumer); } @Override public Children or(boolean condition, Consumer consumer) { return or(condition).addNestedCondition(condition, consumer); } @Override public Children nested(boolean condition, Consumer consumer) { return addNestedCondition(condition, consumer); } @Override public Children not(boolean condition, Consumer consumer) { return not(condition).addNestedCondition(condition, consumer); } @Override public Children or(boolean condition) { return maybeDo(condition, () -> appendSqlSegments(OR)); } @Override public Children apply(boolean condition, String applySql, Object... values) { return maybeDo(condition, () -> appendSqlSegments(APPLY, () -> formatSqlMaybeWithParam(applySql, values))); } @Override public Children last(boolean condition, String lastSql) { if (condition) { this.lastSql.setStringValue(StringPool.SPACE + lastSql); } return typedThis; } @Override public Children comment(boolean condition, String comment) { if (condition) { this.sqlComment.setStringValue(comment); } return typedThis; } @Override public Children first(boolean condition, String firstSql) { if (condition) { this.sqlFirst.setStringValue(firstSql); } return typedThis; } @Override public Children exists(boolean condition, String existsSql, Object... values) { return maybeDo(condition, () -> appendSqlSegments(EXISTS, () -> String.format("(%s)", formatSqlMaybeWithParam(existsSql, values)))); } @Override public Children notExists(boolean condition, String existsSql, Object... values) { return not(condition).exists(condition, existsSql, values); } @Override public Children isNull(boolean condition, R column) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NULL)); } @Override public Children isNotNull(boolean condition, R column) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NOT_NULL)); } @Override public Children in(boolean condition, R column, Collection coll) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(coll))); } @Override public Children in(boolean condition, R column, Object... values) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(values))); } @Override public Children notIn(boolean condition, R column, Collection coll) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(coll))); } @Override public Children notIn(boolean condition, R column, Object... values) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(values))); } @Override public Children eqSql(boolean condition, R column, String eqValue) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), EQ, () -> String.format("(%s)", eqValue))); } @Override public Children inSql(boolean condition, R column, String inValue) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, () -> String.format("(%s)", inValue))); } @Override public Children gtSql(boolean condition, R column, String inValue) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), GT, () -> String.format("(%s)", inValue))); } @Override public Children geSql(boolean condition, R column, String inValue) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), GE, () -> String.format("(%s)", inValue))); } @Override public Children ltSql(boolean condition, R column, String inValue) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), LT, () -> String.format("(%s)", inValue))); } @Override public Children leSql(boolean condition, R column, String inValue) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), LE, () -> String.format("(%s)", inValue))); } @Override public Children notInSql(boolean condition, R column, String inValue) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, () -> String.format("(%s)", inValue))); } @Override public Children groupBy(boolean condition, R column, R... columns) { return doGroupBy(condition, column, CollectionUtils.toList(columns)); } @Override public Children groupBy(boolean condition, R column, List columns) { return doGroupBy(condition, column, columns); } public Children doGroupBy(boolean condition, R column, List columns) { return maybeDo(condition, () -> { String one = StringPool.EMPTY; if (column != null) { one = columnToString(column); } if (CollectionUtils.isNotEmpty(columns)) { one += column != null ? StringPool.COMMA + columnsToString(columns) : columnsToString(columns); } final String finalOne = one; appendSqlSegments(GROUP_BY, () -> finalOne); }); } public Children doOrderBy(boolean condition, boolean isAsc, R column, List columns) { return maybeDo(condition, () -> { final SqlKeyword mode = isAsc ? ASC : DESC; if (column != null) { appendSqlSegments(ORDER_BY, columnToSqlSegment(column), mode); } if (CollectionUtils.isNotEmpty(columns)) { columns.forEach(c -> appendSqlSegments(ORDER_BY, columnToSqlSegment(c), mode)); } }); } @Override public Children orderBy(boolean condition, boolean isAsc, R column, R... columns) { return doOrderBy(condition, isAsc, column, CollectionUtils.toList(columns)); } @Override public Children orderBy(boolean condition, boolean isAsc, R column, List columns) { return doOrderBy(condition, isAsc, column, columns); } @Override public Children groupBy(boolean condition, R column) { return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnToString(column))); } @Override public Children groupBy(boolean condition, List columns) { return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnsToString(columns))); } @Override public Children orderBy(boolean condition, boolean isAsc, R column) { return maybeDo(condition, () -> appendSqlSegments(ORDER_BY, columnToSqlSegment(column), isAsc ? ASC : DESC)); } @Override public Children orderBy(boolean condition, boolean isAsc, List columns) { return maybeDo(condition, () -> columns.forEach(c -> appendSqlSegments(ORDER_BY, columnToSqlSegment(c), isAsc ? ASC : DESC))); } @Override public Children having(boolean condition, String sqlHaving, Object... params) { return maybeDo(condition, () -> appendSqlSegments(HAVING, () -> formatSqlMaybeWithParam(sqlHaving, params))); } @Override public Children func(boolean condition, Consumer consumer) { return maybeDo(condition, () -> consumer.accept(typedThis)); } /** * 内部自用 *

    NOT 关键词

    */ protected Children not(boolean condition) { return maybeDo(condition, () -> appendSqlSegments(NOT)); } /** * 内部自用 *

    拼接 AND

    */ protected Children and(boolean condition) { return maybeDo(condition, () -> appendSqlSegments(AND)); } /** * 内部自用 *

    拼接 LIKE 以及 值

    */ protected Children likeValue(boolean condition, SqlKeyword keyword, R column, Object val, SqlLike sqlLike) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), keyword, () -> formatParam(null, SqlUtils.concatLike(val, sqlLike)))); } /** * 普通查询条件 * * @param condition 是否执行 * @param column 属性 * @param sqlKeyword SQL 关键词 * @param val 条件值 */ protected Children addCondition(boolean condition, R column, SqlKeyword sqlKeyword, Object val) { return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), sqlKeyword, () -> formatParam(null, val))); } /** * 多重嵌套查询条件 * * @param condition 查询条件值 */ protected Children addNestedCondition(boolean condition, Consumer consumer) { return maybeDo(condition, () -> { final Children instance = instance(); consumer.accept(instance); appendSqlSegments(APPLY, instance); }); } /** * 子类返回一个自己的新对象 */ protected abstract Children instance(); /** * 格式化 sql *

    * 支持 "{0}" 这种,或者 "sql {0} sql" 这种 * 也支持 "sql {0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler} sql" 这种 * * @param sqlStr 可能是sql片段 * @param params 参数 * @return sql片段 */ @SuppressWarnings("SameParameterValue") protected final String formatSqlMaybeWithParam(String sqlStr, Object... params) { if (StringUtils.isBlank(sqlStr)) { return null; } if (ArrayUtils.isNotEmpty(params)) { for (int i = 0; i < params.length; ++i) { String target = Constants.LEFT_BRACE + i + Constants.RIGHT_BRACE; if (sqlStr.contains(target)) { sqlStr = sqlStr.replace(target, formatParam(null, params[i])); } else { Matcher matcher = Pattern.compile("[{]" + i + ",[a-zA-Z0-9.,=]+}").matcher(sqlStr); if (!matcher.find()) { throw ExceptionUtils.mpe("Please check the syntax correctness! sql not contains: \"%s\"", target); } String group = matcher.group(); sqlStr = sqlStr.replace(group, formatParam(group.substring(target.length(), group.length() - 1), params[i])); } } } return sqlStr; } /** * 处理入参 * * @param mapping 例如: "javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler" 这种 * @param param 参数 * @return value */ protected final String formatParam(String mapping, Object param) { final String genParamName = Constants.WRAPPER_PARAM + paramNameSeq.incrementAndGet(); final String paramStr = getParamAlias() + Constants.WRAPPER_PARAM_MIDDLE + genParamName; paramNameValuePairs.put(genParamName, param); return SqlScriptUtils.safeParam(paramStr, mapping); } /** * 函数化的做事 * * @param condition 做不做 * @param something 做什么 * @return Children */ protected final Children maybeDo(boolean condition, DoSomething something) { if (condition) { something.doIt(); } return typedThis; } /** * 获取in表达式 包含括号 * * @param value 集合 */ protected ISqlSegment inExpression(Collection value) { if (CollectionUtils.isEmpty(value)) { return () -> "()"; } return () -> value.stream().map(i -> formatParam(null, i)) .collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET)); } /** * 获取in表达式 包含括号 * * @param values 数组 */ protected ISqlSegment inExpression(Object[] values) { if (ArrayUtils.isEmpty(values)) { return () -> "()"; } return () -> Arrays.stream(values).map(i -> formatParam(null, i)) .collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET)); } /** * 必要的初始化 */ protected void initNeed() { paramNameSeq = new AtomicInteger(0); paramNameValuePairs = new HashMap<>(16); expression = new MergeSegments(); lastSql = SharedString.emptyString(); sqlComment = SharedString.emptyString(); sqlFirst = SharedString.emptyString(); } @Override public void clear() { entity = null; paramNameSeq.set(0); paramNameValuePairs.clear(); expression.clear(); lastSql.toEmpty(); sqlComment.toEmpty(); sqlFirst.toEmpty(); } /** * 添加 where 片段 * * @param sqlSegments ISqlSegment 数组 */ protected void appendSqlSegments(ISqlSegment... sqlSegments) { expression.add(sqlSegments); } /** * 是否使用默认注解 {@link OrderBy} 排序 * * @return true 使用 false 不使用 */ public boolean isUseAnnotationOrderBy() { final String _sqlSegment = this.getSqlSegment(); if (StringUtils.isBlank(_sqlSegment)) { return true; } final String _sqlSegmentUpper = _sqlSegment.toUpperCase(); return !(_sqlSegmentUpper.contains(Constants.ORDER_BY) || _sqlSegmentUpper.contains(Constants.LIMIT)); } @Override public String getSqlSegment() { return expression.getSqlSegment() + lastSql.getStringValue(); } @Override public String getSqlComment() { if (StringUtils.isNotBlank(sqlComment.getStringValue())) { return "/*" + sqlComment.getStringValue() + "*/"; } return null; } @Override public String getSqlFirst() { if (StringUtils.isNotBlank(sqlFirst.getStringValue())) { return sqlFirst.getStringValue(); } return null; } @Override public MergeSegments getExpression() { return expression; } public String getParamAlias() { return paramAlias == null ? Constants.WRAPPER : paramAlias.getStringValue(); } /** * 参数别名设置,初始化时优先设置该值、重复设置异常 * * @param paramAlias 参数别名 * @return Children */ @SuppressWarnings("unused") public Children setParamAlias(String paramAlias) { Assert.notEmpty(paramAlias, "paramAlias can not be empty!"); Assert.isEmpty(paramNameValuePairs, "Please call this method before working!"); Assert.isNull(this.paramAlias, "Please do not call the method repeatedly!"); this.paramAlias = new SharedString(paramAlias); return typedThis; } /** * 获取 columnName */ protected final ColumnSegment columnToSqlSegment(R column) { return () -> columnToString(column); } /** * 获取 columnName */ protected String columnToString(R column) { return (String) column; } /** * 获取 columnNames */ protected String columnsToString(R... columns) { return Arrays.stream(columns).map(this::columnToString).collect(joining(StringPool.COMMA)); } /** * 多字段转换为逗号 "," 分割字符串 * * @param columns 多字段 */ protected String columnsToString(List columns) { return columns.stream().map(this::columnToString).collect(joining(StringPool.COMMA)); } @Override @SuppressWarnings("all") public Children clone() { return SerializationUtils.clone(typedThis); } /** * 做事函数 */ @FunctionalInterface public interface DoSomething { void doIt(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/ISqlSegment.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions; import java.io.Serializable; /** * SQL 片段接口 * * @author hubin miemie HCL * @since 2018-05-28 */ @FunctionalInterface public interface ISqlSegment extends Serializable { /** * SQL 片段 */ String getSqlSegment(); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/SharedString.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions; import com.baomidou.mybatisplus.core.toolkit.StringPool; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.io.Serializable; /** * 共享查询字段 * * @author miemie * @since 2018-11-20 */ @Data @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor public class SharedString implements Serializable { private static final long serialVersionUID = -1536422416594422874L; /** * 共享的 string 值 */ private String stringValue; /** * SharedString 里是 "" */ public static SharedString emptyString() { return new SharedString(StringPool.EMPTY); } /** * 置 empty * * @since 3.3.1 */ public void toEmpty() { stringValue = StringPool.EMPTY; } /** * 置 null * * @since 3.3.1 */ public void toNull() { stringValue = null; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/Wrapper.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.conditions.segments.NormalSegmentList; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import java.util.Objects; /** * 条件构造抽象类 * * @author hubin * @since 2018-05-25 */ @SuppressWarnings("all") public abstract class Wrapper implements ISqlSegment { /** * 实体对象(子类实现) * * @return 泛型 T */ public abstract T getEntity(); public String getSqlSelect() { return null; } public String getSqlSet() { return null; } public String getSqlComment() { return null; } public String getSqlFirst() { return null; } /** * 获取 MergeSegments */ public abstract MergeSegments getExpression(); /** * 获取自定义SQL 简化自定义XML复杂情况 *

    * 使用方法: `select xxx from table` + ${ew.customSqlSegment} *

    * 注意事项: * 1. 逻辑删除需要自己拼接条件 (之前自定义也同样) * 2. 不支持wrapper中附带实体的情况 (wrapper自带实体会更麻烦) * 3. 用法 ${ew.customSqlSegment} (不需要where标签包裹,切记!) * 4. ew是wrapper定义别名,不能使用其他的替换 */ public String getCustomSqlSegment() { MergeSegments expression = getExpression(); if (Objects.nonNull(expression)) { NormalSegmentList normal = expression.getNormal(); String sqlSegment = getSqlSegment(); if (StringUtils.isNotBlank(sqlSegment)) { if (normal.isEmpty()) { return sqlSegment; } else { return Constants.WHERE + StringPool.SPACE + sqlSegment; } } } return StringPool.EMPTY; } /** * 查询条件为空(包含entity) */ public boolean isEmptyOfWhere() { return isEmptyOfNormal() && isEmptyOfEntity(); } /** * 查询条件不为空(包含entity) */ public boolean isNonEmptyOfWhere() { return !isEmptyOfWhere(); } @Deprecated public boolean nonEmptyOfWhere() { return isNonEmptyOfWhere(); } /** * 查询条件为空(不包含entity) */ public boolean isEmptyOfNormal() { return CollectionUtils.isEmpty(getExpression().getNormal()); } /** * 查询条件为空(不包含entity) */ public boolean isNonEmptyOfNormal() { return !isEmptyOfNormal(); } @Deprecated public boolean nonEmptyOfNormal() { return isNonEmptyOfNormal(); } /** * 深层实体判断属性 * * @return true 不为空 */ public boolean isNonEmptyOfEntity() { T entity = getEntity(); if (entity == null) { return false; } TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass()); if (tableInfo == null) { return false; } if (tableInfo.getFieldList().stream().anyMatch(e -> fieldStrategyMatch(tableInfo, entity, e))) { return true; } return StringUtils.isNotBlank(tableInfo.getKeyProperty()) ? Objects.nonNull(tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty())) : false; } @Deprecated public boolean nonEmptyOfEntity() { return isNonEmptyOfEntity(); } /** * 根据实体FieldStrategy属性来决定判断逻辑 */ private boolean fieldStrategyMatch(TableInfo tableInfo, T entity, TableFieldInfo e) { switch (e.getWhereStrategy()) { case NOT_NULL: return Objects.nonNull(tableInfo.getPropertyValue(entity, e.getProperty())); case ALWAYS: return true; case NOT_EMPTY: return StringUtils.checkValNotNull(tableInfo.getPropertyValue(entity, e.getProperty())); case NEVER: return false; default: return Objects.nonNull(tableInfo.getPropertyValue(entity, e.getProperty())); } } /** * 深层实体判断属性 * * @return true 为空 */ public boolean isEmptyOfEntity() { return !isNonEmptyOfEntity(); } /** * 获取格式化后的执行sql * * @return sql * @since 3.3.1 */ public String getTargetSql() { return getSqlSegment().replaceAll("#\\{.+?}", "?"); } /** * 条件清空 * * @since 3.3.1 */ abstract public void clear(); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Compare.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.interfaces; import java.io.Serializable; import java.util.Map; import java.util.function.BiPredicate; /** * 查询条件封装 *

    比较值

    * * @author hubin miemie HCL * @since 2017-05-26 */ public interface Compare extends Serializable { /** * map 所有非空属性等于 = * * @param params map 类型的参数, key 是字段名, value 是字段值 * @return children */ default Children allEq(Map params) { return allEq(params, true); } /** * map 所有非空属性等于 = * * @param params map 类型的参数, key 是字段名, value 是字段值 * @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段 * @return children */ default Children allEq(Map params, boolean null2IsNull) { return allEq(true, params, null2IsNull); } /** * map 所有非空属性等于 = * * @param condition 执行条件 * @param params map 类型的参数, key 是字段名, value 是字段值 * @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段 * @return children */ Children allEq(boolean condition, Map params, boolean null2IsNull); /** * 字段过滤接口,传入多参数时允许对参数进行过滤 * * @param filter 返回 true 来允许字段传入比对条件中 * @param params map 类型的参数, key 是字段名, value 是字段值 * @return children */ default Children allEq(BiPredicate filter, Map params) { return allEq(filter, params, true); } /** * 字段过滤接口,传入多参数时允许对参数进行过滤 * * @param filter 返回 true 来允许字段传入比对条件中 * @param params map 类型的参数, key 是字段名, value 是字段值 * @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段 * @return children */ default Children allEq(BiPredicate filter, Map params, boolean null2IsNull) { return allEq(true, filter, params, null2IsNull); } /** * 字段过滤接口,传入多参数时允许对参数进行过滤 * * @param condition 执行条件 * @param filter 返回 true 来允许字段传入比对条件中 * @param params map 类型的参数, key 是字段名, value 是字段值 * @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段 * @return children */ Children allEq(boolean condition, BiPredicate filter, Map params, boolean null2IsNull); /** * 等于 = * * @param column 字段 * @param val 值 * @return children */ default Children eq(R column, Object val) { return eq(true, column, val); } /** * 等于 = * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children eq(boolean condition, R column, Object val); /** * 不等于 <> * * @param column 字段 * @param val 值 * @return children */ default Children ne(R column, Object val) { return ne(true, column, val); } /** * 不等于 <> * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children ne(boolean condition, R column, Object val); /** * 大于 > * * @param column 字段 * @param val 值 * @return children */ default Children gt(R column, Object val) { return gt(true, column, val); } /** * 大于 > * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children gt(boolean condition, R column, Object val); /** * 大于等于 >= * * @param column 字段 * @param val 值 * @return children */ default Children ge(R column, Object val) { return ge(true, column, val); } /** * 大于等于 >= * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children ge(boolean condition, R column, Object val); /** * 小于 < * * @param column 字段 * @param val 值 * @return children */ default Children lt(R column, Object val) { return lt(true, column, val); } /** * 小于 < * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children lt(boolean condition, R column, Object val); /** * 小于等于 <= * * @param column 字段 * @param val 值 * @return children */ default Children le(R column, Object val) { return le(true, column, val); } /** * 小于等于 <= * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children le(boolean condition, R column, Object val); /** * BETWEEN 值1 AND 值2 * * @param column 字段 * @param val1 值1 * @param val2 值2 * @return children */ default Children between(R column, Object val1, Object val2) { return between(true, column, val1, val2); } /** * BETWEEN 值1 AND 值2 * * @param condition 执行条件 * @param column 字段 * @param val1 值1 * @param val2 值2 * @return children */ Children between(boolean condition, R column, Object val1, Object val2); /** * NOT BETWEEN 值1 AND 值2 * * @param column 字段 * @param val1 值1 * @param val2 值2 * @return children */ default Children notBetween(R column, Object val1, Object val2) { return notBetween(true, column, val1, val2); } /** * NOT BETWEEN 值1 AND 值2 * * @param condition 执行条件 * @param column 字段 * @param val1 值1 * @param val2 值2 * @return children */ Children notBetween(boolean condition, R column, Object val1, Object val2); /** * LIKE '%值%' * * @param column 字段 * @param val 值 * @return children */ default Children like(R column, Object val) { return like(true, column, val); } /** * LIKE '%值%' * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children like(boolean condition, R column, Object val); /** * NOT LIKE '%值%' * * @param column 字段 * @param val 值 * @return children */ default Children notLike(R column, Object val) { return notLike(true, column, val); } /** * NOT LIKE '%值%' * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children notLike(boolean condition, R column, Object val); /** * NOT LIKE '%值' * * @param column 字段 * @param val 值 * @return children */ default Children notLikeLeft(R column, Object val) { return notLikeLeft(true, column, val); } /** * NOT LIKE '%值' * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children notLikeLeft(boolean condition, R column, Object val); /** * NOT LIKE '值%' * * @param column 字段 * @param val 值 * @return children */ default Children notLikeRight(R column, Object val) { return notLikeRight(true, column, val); } /** * NOT LIKE '值%' * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children notLikeRight(boolean condition, R column, Object val); /** * LIKE '%值' * * @param column 字段 * @param val 值 * @return children */ default Children likeLeft(R column, Object val) { return likeLeft(true, column, val); } /** * LIKE '%值' * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children likeLeft(boolean condition, R column, Object val); /** * LIKE '值%' * * @param column 字段 * @param val 值 * @return children */ default Children likeRight(R column, Object val) { return likeRight(true, column, val); } /** * LIKE '值%' * * @param condition 执行条件 * @param column 字段 * @param val 值 * @return children */ Children likeRight(boolean condition, R column, Object val); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Func.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.interfaces; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.function.Consumer; /** * 查询条件封装 * * @author hubin miemie HCL * @since 2017-05-26 */ @SuppressWarnings("unchecked") public interface Func extends Serializable { /** * 字段 IS NULL *

    例: isNull("name")

    * * @param column 字段 * @return children */ default Children isNull(R column) { return isNull(true, column); } /** * 字段 IS NULL *

    例: isNull(true, "name")

    * * @param condition 执行条件 * @param column 字段 * @return children */ Children isNull(boolean condition, R column); /** * 字段 IS NOT NULL *

    例: isNotNull("name")

    * * @param column 字段 * @return children */ default Children isNotNull(R column) { return isNotNull(true, column); } /** * 字段 IS NOT NULL *

    例: isNotNull(true, "name")

    * * @param condition 执行条件 * @param column 字段 * @return children */ Children isNotNull(boolean condition, R column); /** * 字段 IN (value.get(0), value.get(1), ...) *

    例: in("id", Arrays.asList(1, 2, 3, 4, 5))

    * *
  • 注意!当集合为 空或null 时, sql会拼接为:WHERE (字段名 IN ()), 执行时报错
  • *
  • 若要在特定条件下不拼接, 可在 condition 条件中判断
  • * * @param column 字段 * @param coll 数据集合 * @return children */ default Children in(R column, Collection coll) { return in(true, column, coll); } /** * 字段 IN (value.get(0), value.get(1), ...) *

    例: in(true, "id", Arrays.asList(1, 2, 3, 4, 5))

    * *
  • 注意!当集合为 空或null 时, sql会拼接为:WHERE (字段名 IN ()), 执行时报错
  • *
  • 若要在特定条件下不拼接, 可在 condition 条件中判断
  • * * @param condition 执行条件 * @param column 字段 * @param coll 数据集合 * @return children */ Children in(boolean condition, R column, Collection coll); /** * 字段 IN (v0, v1, ...) *

    例: in("id", 1, 2, 3, 4, 5)

    * *
  • 注意!当数组为 空或null 时, sql会拼接为:WHERE (字段名 IN ()), 执行时报错
  • *
  • 若要在特定条件下不拼接, 可在 condition 条件中判断
  • * * @param column 字段 * @param values 数据数组 * @return children */ default Children in(R column, Object... values) { return in(true, column, values); } /** * 字段 IN (v0, v1, ...) *

    例: in(true, "id", 1, 2, 3, 4, 5)

    * *
  • 注意!当数组为 空或null 时, sql会拼接为:WHERE (字段名 IN ()), 执行时报错
  • *
  • 若要在特定条件下不拼接, 可在 condition 条件中判断
  • * * @param condition 执行条件 * @param column 字段 * @param values 数据数组 * @return children */ Children in(boolean condition, R column, Object... values); /** * 字段 NOT IN (value.get(0), value.get(1), ...) *

    例: notIn("id", Arrays.asList(1, 2, 3, 4, 5))

    * *
  • 注意!当集合为 空或null 时, sql会拼接为:WHERE (字段名 NOT IN ()), 执行时报错
  • *
  • 若要在特定条件下不拼接, 可在 condition 条件中判断
  • * * @param column 字段 * @param coll 数据集合 * @return children */ default Children notIn(R column, Collection coll) { return notIn(true, column, coll); } /** * 字段 NOT IN (value.get(0), value.get(1), ...) *

    例: notIn(true, "id", Arrays.asList(1, 2, 3, 4, 5))

    * *
  • 注意!当集合为 空或null 时, sql会拼接为:WHERE (字段名 NOT IN ()), 执行时报错
  • *
  • 若要在特定条件下不拼接, 可在 condition 条件中判断
  • * * @param condition 执行条件 * @param column 字段 * @param coll 数据集合 * @return children */ Children notIn(boolean condition, R column, Collection coll); /** * 字段 NOT IN (v0, v1, ...) *

    例: notIn("id", 1, 2, 3, 4, 5)

    * *
  • 注意!当数组为 空或null 时, sql会拼接为:WHERE (字段名 NOT IN ()), 执行时报错
  • *
  • 若要在特定条件下不拼接, 可在 condition 条件中判断
  • * * @param column 字段 * @param values 数据数组 * @return children */ default Children notIn(R column, Object... values) { return notIn(true, column, values); } /** * 字段 NOT IN (v0, v1, ...) *

    例: notIn(true, "id", 1, 2, 3, 4, 5)

    * *
  • 注意!当数组为 空或null 时, sql会拼接为:WHERE (字段名 NOT IN ()), 执行时报错
  • *
  • 若要在特定条件下不拼接, 可在 condition 条件中判断
  • * * @param condition 执行条件 * @param column 字段 * @param values 数据数组 * @return children */ Children notIn(boolean condition, R column, Object... values); /** * 字段 EQ ( sql语句 ) *

    !! sql 注入方式的 eq 方法 !!

    *

    例1: eqSql("id", "1")

    *

    例2: eqSql("id", "select MAX(id) from table")

    * * @param column 字段 * @param sql sql语句 * @return children * @since 3.5.6 */ default Children eqSql(R column, String sql) { return eqSql(true, column, sql); } /** * 字段 EQ ( sql语句 ) *

    !! sql 注入方式的 eq 方法 !!

    *

    例1: eqSql("id", "1")

    *

    例2: eqSql("id", "select MAX(id) from table")

    * * @param condition 执行条件 * @param column 字段 * @param sql sql语句 * @return children * @since 3.5.6 */ Children eqSql(boolean condition, R column, String sql); /** * 字段 IN ( sql语句 ) *

    !! sql 注入方式的 in 方法 !!

    *

    例1: inSql("id", "1")

    *

    例2: inSql("id", "select id from table where id < 3")

    * * @param column 字段 * @param sql sql语句 * @return children */ default Children inSql(R column, String sql) { return inSql(true, column, sql); } /** * 字段 IN ( sql语句 ) *

    !! sql 注入方式的 in 方法 !!

    *

    例1: inSql(true, "id", "1")

    *

    例2: inSql(true, "id", "select id from table where id < 3")

    * * @param condition 执行条件 * @param column 字段 * @param sql sql语句 * @return children */ Children inSql(boolean condition, R column, String sql); /** * 字段 > ( sql语句 ) *

    例1: gtSql(true, "id", "1")

    *

    例1: gtSql(true, "id", "select id from table where name = 'JunJun'")

    * * @param condition 执行条件 * @param column 字段 * @param sql sql语句 * @return children */ Children gtSql(boolean condition, R column, String sql); /** * 字段 > ( sql语句 ) *

    例1: gtSql("id", "1")

    *

    例1: gtSql("id", "select id from table where name = 'JunJun'")

    * * @param column 字段 * @param sql sql语句 * @return children */ default Children gtSql(R column, String sql) { return gtSql(true, column, sql); } /** * 字段 >= ( sql语句 ) *

    例1: geSql(true, "id", "1")

    *

    例1: geSql(true, "id", "select id from table where name = 'JunJun'")

    * * @param condition 执行条件 * @param column 字段 * @param sql sql语句 * @return children */ Children geSql(boolean condition, R column, String sql); /** * 字段 >= ( sql语句 ) *

    例1: geSql("id", "1")

    *

    例1: geSql("id", "select id from table where name = 'JunJun'")

    * * @param column 字段 * @param sql sql语句 * @return children */ default Children geSql(R column, String sql) { return geSql(true, column, sql); } /** * 字段 < ( sql语句 ) *

    例1: ltSql(true, "id", "1")

    *

    例1: ltSql(true , "id", "select id from table where name = 'JunJun'")

    * * @param condition 执行条件 * @param column 字段 * @param sql sql语句 * @return children */ Children ltSql(boolean condition, R column, String sql); /** * 字段 < ( sql语句 ) *

    例1: ltSql("id", "1")

    *

    例1: ltSql("id", "select id from table where name = 'JunJun'")

    * * @param column 字段 * @param sql sql语句 * @return children */ default Children ltSql(R column, String sql) { return ltSql(true, column, sql); } /** * 字段 <= ( sql语句 ) *

    例1: leSql(true, "id", "1")

    *

    例1: leSql(true ,"id", "select id from table where name = 'JunJun'")

    * * @param condition 执行条件 * @param column 字段 * @param sql sql语句 * @return children */ Children leSql(boolean condition, R column, String sql); /** * 字段 <= ( sql语句 ) *

    例1: leSql("id", "1")

    *

    例1: leSql("id", "select id from table where name = 'JunJun'")

    * * @param column 字段 * @param inValue sql语句 * @return children */ default Children leSql(R column, String inValue) { return leSql(true, column, inValue); } /** * 字段 NOT IN ( sql语句 ) *

    !! sql 注入方式的 not in 方法 !!

    *

    例1: notInSql("id", "1, 2, 3, 4, 5, 6")

    *

    例2: notInSql("id", "select id from table where id < 3")

    * * @param column 字段 * @param inValue sql语句 ---> 1,2,3,4,5,6 或者 select id from table where id < 3 * @return children */ default Children notInSql(R column, String inValue) { return notInSql(true, column, inValue); } /** * 字段 NOT IN ( sql语句 ) *

    !! sql 注入方式的 not in 方法 !!

    *

    例1: notInSql(true, "id", "1, 2, 3, 4, 5, 6")

    *

    例2: notInSql(true, "id", "select id from table where id < 3")

    * * @param condition 执行条件 * @param column 字段 * @param inValue sql语句 ---> 1,2,3,4,5,6 或者 select id from table where id < 3 * @return children */ Children notInSql(boolean condition, R column, String inValue); /** * 分组:GROUP BY 字段, ... *

    例: groupBy(true, "id")

    * * @param condition 执行条件 * @param column 单个字段 * @return children */ Children groupBy(boolean condition, R column); /** * 分组:GROUP BY 字段, ... *

    例: groupBy("id")

    * * @param column 单个字段 * @return children */ default Children groupBy(R column) { return groupBy(true, column); } /** * 分组:GROUP BY 字段, ... *

    例: groupBy(true, Arrays.asList("id", "name"))

    * * @param condition 执行条件 * @param columns 字段数组 * @return children */ Children groupBy(boolean condition, List columns); /** * 分组:GROUP BY 字段, ... *

    例: groupBy(Arrays.asList("id", "name"))

    * * @param columns 字段数组 * @return children */ default Children groupBy(List columns) { return groupBy(true, columns); } /** * 分组:GROUP BY 字段, ... *

    例: groupBy("id", "name")

    * * @param column 单个字段 * @param columns 字段数组 * @return children */ default Children groupBy(R column, R... columns) { return groupBy(true, column, columns); } /** * 分组:GROUP BY 字段, ... *

    例: groupBy(true, "id", "name")

    * * @param condition 执行条件 * @param column 单个字段 * @param columns 字段数组 * @return children */ Children groupBy(boolean condition, R column, R... columns); /** * 分组:GROUP BY 字段, ... *

    例: groupBy(true, "id", Arrays.asList("name"))

    * * @param condition 执行条件 * @param column 单个字段 * @param columns 字段数组 * @return children * @since 3.5.4 */ Children groupBy(boolean condition, R column, List columns); /** * 排序:ORDER BY 字段, ... ASC *

    例: orderByAsc(true, "id")

    * * @param condition 执行条件 * @param column 单个字段 * @return children */ default Children orderByAsc(boolean condition, R column) { return orderBy(condition, true, column); } /** * 排序:ORDER BY 字段, ... ASC *

    例: orderByAsc("id")

    * * @param column 单个字段 * @return children */ default Children orderByAsc(R column) { return orderByAsc(true, column); } /** * 排序:ORDER BY 字段, ... ASC *

    例: orderByAsc(true, Arrays.asList("id", "name"))

    * * @param condition 执行条件 * @param columns 字段数组 * @return children */ default Children orderByAsc(boolean condition, List columns) { return orderBy(condition, true, columns); } /** * 排序:ORDER BY 字段, ... ASC *

    例: orderByAsc(Arrays.asList("id", "name"))

    * * @param columns 字段数组 * @return children */ default Children orderByAsc(List columns) { return orderByAsc(true, columns); } /** * 排序:ORDER BY 字段, ... ASC * * @param column 字段 * @param columns 字段数组 * @return children */ default Children orderByAsc(R column, R... columns) { return orderByAsc(true, column, columns); } /** * 排序:ORDER BY 字段, ... ASC * * @param condition 执行条件 * @param column 字段 * @param columns 字段数组 */ default Children orderByAsc(boolean condition, R column, R... columns) { return orderBy(condition, true, column, columns); } /** * 排序:ORDER BY 字段, ... ASC *

    例: orderByAsc(true, Arrays.asList("id", "name"))

    * * @param condition 执行条件 * @param columns 字段数组 * @return children * @since 3.5.4 */ default Children orderByAsc(boolean condition, R column, List columns) { return orderBy(condition, true, column, columns); } /** * 排序:ORDER BY 字段, ... DESC *

    例: orderByDesc(true, "id")

    * * @param condition 执行条件 * @param column 字段 * @return children */ default Children orderByDesc(boolean condition, R column) { return orderBy(condition, false, column); } /** * 排序:ORDER BY 字段, ... DESC *

    例: orderByDesc("id")

    * * @param column 字段 * @return children */ default Children orderByDesc(R column) { return orderByDesc(true, column); } /** * 排序:ORDER BY 字段, ... DESC *

    例: orderByDesc(true, Arrays.asList("id", "name"))

    * * @param condition 执行条件 * @param columns 字段列表 * @return children */ default Children orderByDesc(boolean condition, List columns) { return orderBy(condition, false, columns); } /** * 排序:ORDER BY 字段, ... DESC * * @param columns 字段列表 */ default Children orderByDesc(List columns) { return orderByDesc(true, columns); } /** * 排序:ORDER BY 字段, ... DESC * * @param column 单个字段 * @param columns 字段列表 */ default Children orderByDesc(R column, R... columns) { return orderByDesc(true, column, columns); } /** * 排序:ORDER BY 字段, ... DESC * * @param condition 执行条件 * @param column 单个字段 * @param columns 字段列表 */ default Children orderByDesc(boolean condition, R column, R... columns) { return orderBy(condition, false, column, CollectionUtils.toList(columns)); } /** * 排序:ORDER BY 字段, ... DESC * * @param condition 执行条件 * @param column 单个字段 * @param columns 字段列表 * @since 3.5.4 */ default Children orderByDesc(boolean condition, R column, List columns) { return orderBy(condition, false, column, columns); } /** * 排序:ORDER BY 字段, ... *

    例: orderBy(true, "id")

    * * @param condition 执行条件 * @param isAsc 是否是 ASC 排序 * @param column 单个字段 * @return children */ Children orderBy(boolean condition, boolean isAsc, R column); /** * 排序:ORDER BY 字段, ... *

    例: orderBy(true, Arrays.asList("id", "name"))

    * * @param condition 执行条件 * @param isAsc 是否是 ASC 排序 * @param columns 字段列表 * @return children */ Children orderBy(boolean condition, boolean isAsc, List columns); /** * 排序:ORDER BY 字段, ... * * @param condition 执行条件 * @param isAsc 是否是 ASC 排序 * @param columns 字段列表 * @return children */ Children orderBy(boolean condition, boolean isAsc, R column, R... columns); /** * 排序:ORDER BY 字段, ... * * @param condition 执行条件 * @param isAsc 是否是 ASC 排序 * @param columns 字段列表 * @return children * @since 3.5.4 */ Children orderBy(boolean condition, boolean isAsc, R column, List columns); /** * HAVING ( sql语句 ) *

    例1: having("sum(age) > 10")

    *

    例2: having("sum(age) > {0}", 10)

    * * @param sqlHaving sql 语句 * @param params 参数数组 * @return children */ default Children having(String sqlHaving, Object... params) { return having(true, sqlHaving, params); } /** * HAVING ( sql语句 ) *

    例1: having(true, "sum(age) > 10")

    *

    例2: having(true, "sum(age) > {0}", 10)

    * * @param condition 执行条件 * @param sqlHaving sql 语句 * @param params 参数数组 * @return children */ Children having(boolean condition, String sqlHaving, Object... params); /** * 消费函数 * * @param consumer 消费函数 * @return children */ default Children func(Consumer consumer) { return func(true, consumer); } /** * 消费函数 * * @param condition 执行条件 * @param consumer 消费函数 * @return children * @since 3.3.1 */ Children func(boolean condition, Consumer consumer); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Join.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.interfaces; import java.io.Serializable; /** * 查询条件封装 *

    拼接

    * * @author hubin miemie HCL * @since 2017-05-26 */ public interface Join extends Serializable { /** * 拼接 OR * * @return children */ default Children or() { return or(true); } /** * 拼接 OR * * @param condition 执行条件 * @return children */ Children or(boolean condition); /** * 拼接 sql *

    !! 会有 sql 注入风险 !!

    *

    例1: apply("id = 1")

    *

    例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

    *

    例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())

    *

    例4: apply("type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "待处理字符串")

    * * @param values 数据数组 * @return children */ default Children apply(String applySql, Object... values) { return apply(true, applySql, values); } /** * 拼接 sql *

    !! 会有 sql 注入风险 !!

    *

    例1: apply("id = 1")

    *

    例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

    *

    例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())

    *

    例4: apply("type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "待处理字符串")

    * * @param condition 执行条件 * @param values 数据数组 * @return children */ Children apply(boolean condition, String applySql, Object... values); /** * 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用) *

    例: last("limit 1")

    *

    注意只能调用一次,多次调用以最后一次为准

    * * @param lastSql sql语句 * @return children */ default Children last(String lastSql) { return last(true, lastSql); } /** * 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用) *

    例: last("limit 1")

    *

    注意只能调用一次,多次调用以最后一次为准

    * * @param condition 执行条件 * @param lastSql sql语句 * @return children */ Children last(boolean condition, String lastSql); /** * sql 注释(会拼接在 sql 的最后面) *

    * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)} *

    * * @param comment sql注释 * @return children */ default Children comment(String comment) { return comment(true, comment); } /** * sql 注释(会拼接在 sql 的最后面) *

    * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)} *

    * * @param condition 执行条件 * @param comment sql注释 * @return children */ Children comment(boolean condition, String comment); /** * sql 起始句(会拼接在SQL语句的起始处) *

    * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)} *

    * * @param firstSql 起始语句 * @return children */ default Children first(String firstSql) { return first(true, firstSql); } /** * sql 起始句(会拼接在SQL语句的起始处) *

    * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)} *

    * * @param condition 执行条件 * @param firstSql 起始语句 * @return children * @since 3.3.1 */ Children first(boolean condition, String firstSql); /** * 拼接 EXISTS ( sql语句 ) *

    !! sql 注入方法 !!

    *

    例: exists("select id from table where age = 1")

    * * @param existsSql sql语句 * @param values 数据数组 * @return children */ default Children exists(String existsSql, Object... values) { return exists(true, existsSql, values); } /** * 拼接 EXISTS ( sql语句 ) *

    !! sql 注入方法 !!

    *

    例: exists("select id from table where age = 1")

    * * @param condition 执行条件 * @param existsSql sql语句 * @param values 数据数组 * @return children */ Children exists(boolean condition, String existsSql, Object... values); /** * 拼接 NOT EXISTS ( sql语句 ) *

    !! sql 注入方法 !!

    *

    例: notExists("select id from table where age = 1")

    * * @param existsSql sql语句 * @param values 数据数组 * @return children */ default Children notExists(String existsSql, Object... values) { return notExists(true, existsSql, values); } /** * 拼接 NOT EXISTS ( sql语句 ) *

    !! sql 注入方法 !!

    *

    例: notExists("select id from table where age = 1")

    * * @param condition 执行条件 * @param existsSql sql语句 * @param values 数据数组 * @return children */ Children notExists(boolean condition, String existsSql, Object... values); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Nested.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.interfaces; import java.io.Serializable; import java.util.function.Consumer; /** * 查询条件封装 *

    嵌套

    *
  • 泛型 Param 是具体需要运行函数的类(也是 wrapper 的子类)
  • * * @author hubin miemie HCL * @since 2017-05-26 */ public interface Nested extends Serializable { /** * AND 嵌套 *

    * 例: and(i -> i.eq("name", "李白").ne("status", "活着")) *

    * * @param consumer 消费函数 * @return children */ default Children and(Consumer consumer) { return and(true, consumer); } /** * AND 嵌套 *

    * 例: and(i -> i.eq("name", "李白").ne("status", "活着")) *

    * * @param condition 执行条件 * @param consumer 消费函数 * @return children */ Children and(boolean condition, Consumer consumer); /** * OR 嵌套 *

    * 例: or(i -> i.eq("name", "李白").ne("status", "活着")) *

    * * @param consumer 消费函数 * @return children */ default Children or(Consumer consumer) { return or(true, consumer); } /** * OR 嵌套 *

    * 例: or(i -> i.eq("name", "李白").ne("status", "活着")) *

    * * @param condition 执行条件 * @param consumer 消费函数 * @return children */ Children or(boolean condition, Consumer consumer); /** * 正常嵌套 不带 AND 或者 OR *

    * 例: nested(i -> i.eq("name", "李白").ne("status", "活着")) *

    * * @param consumer 消费函数 * @return children */ default Children nested(Consumer consumer) { return nested(true, consumer); } /** * 正常嵌套 不带 AND 或者 OR *

    * 例: nested(i -> i.eq("name", "李白").ne("status", "活着")) *

    * * @param condition 执行条件 * @param consumer 消费函数 * @return children */ Children nested(boolean condition, Consumer consumer); /** * not嵌套 *

    * 例: not(i -> i.eq("name", "李白").ne("status", "活着")) *

    * * @param consumer 消费函数 * @return children */ default Children not(Consumer consumer) { return not(true, consumer); } /** * not嵌套 *

    * 例: not(i -> i.eq("name", "李白").ne("status", "活着")) *

    * * @param condition 执行条件 * @param consumer 消费函数 * @return children */ Children not(boolean condition, Consumer consumer); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Wrapper 接口 */ package com.baomidou.mybatisplus.core.conditions.interfaces; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 条件构造区域,测试是否可以提交 */ package com.baomidou.mybatisplus.core.conditions; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/query/LambdaQueryWrapper.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.query; import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper; import com.baomidou.mybatisplus.core.conditions.SharedString; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; /** * Lambda 语法使用 Wrapper * * @author hubin miemie HCL * @since 2017-05-26 */ public class LambdaQueryWrapper extends AbstractLambdaWrapper> implements Query, T, SFunction> { /** * 查询字段 */ private SharedString sqlSelect = new SharedString(); public LambdaQueryWrapper() { this((T) null); } public LambdaQueryWrapper(T entity) { super.setEntity(entity); super.initNeed(); } public LambdaQueryWrapper(Class entityClass) { super.setEntityClass(entityClass); super.initNeed(); } LambdaQueryWrapper(T entity, Class entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq, Map paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) { super.setEntity(entity); super.setEntityClass(entityClass); this.paramNameSeq = paramNameSeq; this.paramNameValuePairs = paramNameValuePairs; this.expression = mergeSegments; this.sqlSelect = sqlSelect; this.paramAlias = paramAlias; this.lastSql = lastSql; this.sqlComment = sqlComment; this.sqlFirst = sqlFirst; } @Override public LambdaQueryWrapper select(boolean condition, List> columns) { return doSelect(condition, columns); } /** * 过滤查询的字段信息(主键除外!) *

    例1: 只要 java 字段名以 "test" 开头的 -> select(i -> i.getProperty().startsWith("test"))

    *

    例2: 只要 java 字段属性是 CharSequence 类型的 -> select(TableFieldInfo::isCharSequence)

    *

    例3: 只要 java 字段没有填充策略的 -> select(i -> i.getFieldFill() == FieldFill.DEFAULT)

    *

    例4: 要全部字段 -> select(i -> true)

    *

    例5: 只要主键字段 -> select(i -> false)

    * * @param predicate 过滤方式 * @return this */ @Override public LambdaQueryWrapper select(Class entityClass, Predicate predicate) { if (entityClass == null) { entityClass = getEntityClass(); } else { setEntityClass(entityClass); } Assert.notNull(entityClass, "entityClass can not be null"); this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate)); return typedThis; } @Override @SafeVarargs public final LambdaQueryWrapper select(SFunction... columns) { return doSelect(true, CollectionUtils.toList(columns)); } @Override @SafeVarargs public final LambdaQueryWrapper select(boolean condition, SFunction... columns) { return doSelect(condition, CollectionUtils.toList(columns)); } /** * @since 3.5.4 */ protected LambdaQueryWrapper doSelect(boolean condition, List> columns) { if (condition && CollectionUtils.isNotEmpty(columns)) { this.sqlSelect.setStringValue(columnsToString(false, columns)); } return typedThis; } @Override public String getSqlSelect() { return sqlSelect.getStringValue(); } /** * 用于生成嵌套 sql *

    故 sqlSelect 不向下传递

    */ @Override protected LambdaQueryWrapper instance() { return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs, new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString()); } @Override public void clear() { super.clear(); sqlSelect.toNull(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/query/Query.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.query; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import java.io.Serializable; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; /** * @author miemie * @since 2018-12-12 */ public interface Query extends Serializable { /** * 指定查询字段 * * @param columns 字段列表 * @return children */ @SuppressWarnings("unchecked") default Children select(R... columns) { return select(true, columns); } /** * 指定查询字段 * * @param condition 执行条件 * @param columns 字段列表 * @return children */ @SuppressWarnings("unchecked") default Children select(boolean condition, R... columns) { return select(condition, Arrays.asList(columns)); } /** * 指定查询字段 * * @param columns 字段列表 * @return children */ default Children select(List columns) { return select(true, columns); } /** * 指定查询字段 * * @param condition 执行条件 * @param columns 字段列表 * @return children */ Children select(boolean condition, List columns); /** * 过滤查询的字段信息(主键除外!) *

    注意只有内部有 entity 才能使用该方法

    */ default Children select(Predicate predicate) { return select(null, predicate); } /** * 过滤查询的字段信息(主键除外!) *

    例1: 只要 java 字段名以 "test" 开头的 -> select(i -> i.getProperty().startsWith("test"))

    *

    例2: 只要 java 字段属性是 CharSequence 类型的 -> select(TableFieldInfo::isCharSequence)

    *

    例3: 只要 java 字段没有填充策略的 -> select(i -> i.getFieldFill() == FieldFill.DEFAULT)

    *

    例4: 要全部字段 -> select(i -> true)

    *

    例5: 只要主键字段 -> select(i -> false)

    * * @param predicate 过滤方式 * @return children */ Children select(Class entityClass, Predicate predicate); /** * 查询条件 SQL 片段 */ String getSqlSelect(); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/query/QueryWrapper.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.query; import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; import com.baomidou.mybatisplus.core.conditions.SharedString; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; /** * Entity 对象封装操作类 * * @author hubin miemie HCL * @since 2018-05-25 */ public class QueryWrapper extends AbstractWrapper> implements Query, T, String> { /** * 查询字段 */ protected final SharedString sqlSelect = new SharedString(); public QueryWrapper() { this((T) null); } public QueryWrapper(T entity) { super.setEntity(entity); super.initNeed(); } public QueryWrapper(Class entityClass) { super.setEntityClass(entityClass); super.initNeed(); } public QueryWrapper(T entity, String... columns) { super.setEntity(entity); super.initNeed(); this.select(columns); } /** * 非对外公开的构造方法,只用于生产嵌套 sql * * @param entityClass 本不应该需要的 */ private QueryWrapper(T entity, Class entityClass, AtomicInteger paramNameSeq, Map paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) { super.setEntity(entity); super.setEntityClass(entityClass); this.paramNameSeq = paramNameSeq; this.paramNameValuePairs = paramNameValuePairs; this.expression = mergeSegments; this.paramAlias = paramAlias; this.lastSql = lastSql; this.sqlComment = sqlComment; this.sqlFirst = sqlFirst; } /** * 检查 SQL 注入过滤 */ private boolean checkSqlInjection; /** * 开启检查 SQL 注入 */ public QueryWrapper checkSqlInjection() { this.checkSqlInjection = true; return this; } @Override protected String columnToString(String column) { if (checkSqlInjection && SqlInjectionUtils.check(column)) { throw new MybatisPlusException("Discovering SQL injection column: " + column); } return column; } @Override public QueryWrapper select(boolean condition, List columns) { if (condition && CollectionUtils.isNotEmpty(columns)) { this.sqlSelect.setStringValue(String.join(StringPool.COMMA, columns)); } return typedThis; } @Override public QueryWrapper select(Class entityClass, Predicate predicate) { super.setEntityClass(entityClass); this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(getEntityClass()).chooseSelect(predicate)); return typedThis; } @Override public String getSqlSelect() { return sqlSelect.getStringValue(); } /** * 返回一个支持 lambda 函数写法的 wrapper */ public LambdaQueryWrapper lambda() { return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), sqlSelect, paramNameSeq, paramNameValuePairs, expression, paramAlias, lastSql, sqlComment, sqlFirst); } /** * 用于生成嵌套 sql *

    * 故 sqlSelect 不向下传递 *

    */ @Override protected QueryWrapper instance() { return new QueryWrapper<>(getEntity(), getEntityClass(), paramNameSeq, paramNameValuePairs, new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString()); } @Override public void clear() { super.clear(); sqlSelect.toNull(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/query/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 查询 Wrapper */ package com.baomidou.mybatisplus.core.conditions.query; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/AbstractISegmentList.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.segments; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import com.baomidou.mybatisplus.core.toolkit.StringPool; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * SQL 片段集合 处理的抽象类 * * @author miemie * @since 2018-06-27 */ public abstract class AbstractISegmentList extends ArrayList implements ISqlSegment, StringPool { /** * 最后一个值 */ ISqlSegment lastValue = null; /** * 刷新 lastValue */ boolean flushLastValue = false; /** * 结果集缓存 */ private String sqlSegment = EMPTY; /** * 是否缓存过结果集 */ private boolean cacheSqlSegment = true; /** * 重写方法,做个性化适配 * * @param c 元素集合 * @return 是否添加成功 */ @Override public boolean addAll(Collection c) { List list = new ArrayList<>(c); boolean goon = transformList(list, list.get(0), list.get(list.size() - 1)); if (goon) { cacheSqlSegment = false; if (flushLastValue) { this.flushLastValue(list); } return super.addAll(list); } return false; } /** * 在其中对值进行判断以及更改 list 的内部元素 * * @param list 传入进来的 ISqlSegment 集合 * @param firstSegment ISqlSegment 集合里第一个值 * @param lastSegment ISqlSegment 集合里最后一个值 * @return true 是否继续向下执行; false 不再向下执行 */ protected abstract boolean transformList(List list, ISqlSegment firstSegment, ISqlSegment lastSegment); /** * 刷新属性 lastValue */ private void flushLastValue(List list) { lastValue = list.get(list.size() - 1); } /** * 删除元素里最后一个值
    * 并刷新属性 lastValue */ void removeAndFlushLast() { remove(size() - 1); flushLastValue(this); } @Override public String getSqlSegment() { if (cacheSqlSegment) { return sqlSegment; } cacheSqlSegment = true; sqlSegment = childrenSqlSegment(); return sqlSegment; } /** * 只有该类进行过 addAll 操作,才会触发这个方法 *

    * 方法内可以放心进行操作 * * @return sqlSegment */ protected abstract String childrenSqlSegment(); @Override public void clear() { super.clear(); lastValue = null; sqlSegment = EMPTY; cacheSqlSegment = true; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/ColumnSegment.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.segments; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; @FunctionalInterface public interface ColumnSegment extends ISqlSegment { } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/GroupBySegmentList.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.segments; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import java.util.List; import static com.baomidou.mybatisplus.core.enums.SqlKeyword.GROUP_BY; import static java.util.stream.Collectors.joining; /** * Group By SQL 片段 * * @author miemie * @since 2018-06-27 */ public class GroupBySegmentList extends AbstractISegmentList { @Override protected boolean transformList(List list, ISqlSegment firstSegment, ISqlSegment lastSegment) { list.remove(0); return true; } @Override protected String childrenSqlSegment() { if (isEmpty()) { return EMPTY; } return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(COMMA, SPACE + GROUP_BY.getSqlSegment() + SPACE, EMPTY)); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/HavingSegmentList.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.segments; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import com.baomidou.mybatisplus.core.enums.SqlKeyword; import java.util.List; import static com.baomidou.mybatisplus.core.enums.SqlKeyword.HAVING; import static java.util.stream.Collectors.joining; /** * Having SQL 片段 * * @author miemie * @since 2018-06-27 */ public class HavingSegmentList extends AbstractISegmentList { @Override protected boolean transformList(List list, ISqlSegment firstSegment, ISqlSegment lastSegment) { if (!isEmpty()) { this.add(SqlKeyword.AND); } list.remove(0); return true; } @Override protected String childrenSqlSegment() { if (isEmpty()) { return EMPTY; } return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(SPACE, SPACE + HAVING.getSqlSegment() + SPACE, EMPTY)); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/MatchSegment.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.segments; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import com.baomidou.mybatisplus.core.enums.SqlKeyword; import com.baomidou.mybatisplus.core.enums.WrapperKeyword; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import java.util.function.Predicate; /** * 匹配片段 * * @author miemie * @since 2018-06-27 */ @Getter @AllArgsConstructor(access = AccessLevel.PACKAGE) public enum MatchSegment { GROUP_BY(i -> i == SqlKeyword.GROUP_BY), ORDER_BY(i -> i == SqlKeyword.ORDER_BY), NOT(i -> i == SqlKeyword.NOT), AND(i -> i == SqlKeyword.AND), OR(i -> i == SqlKeyword.OR), AND_OR(i -> i == SqlKeyword.AND || i == SqlKeyword.OR), EXISTS(i -> i == SqlKeyword.EXISTS), HAVING(i -> i == SqlKeyword.HAVING), APPLY(i -> i == WrapperKeyword.APPLY); private final Predicate predicate; public boolean match(ISqlSegment segment) { return getPredicate().test(segment); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/MergeSegments.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.segments; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import com.baomidou.mybatisplus.core.toolkit.StringPool; import lombok.AccessLevel; import lombok.Getter; import java.util.Arrays; import java.util.List; /** * 合并 SQL 片段 * * @author miemie * @since 2018-06-27 */ @Getter public class MergeSegments implements ISqlSegment { private final NormalSegmentList normal = new NormalSegmentList(); private final GroupBySegmentList groupBy = new GroupBySegmentList(); private final HavingSegmentList having = new HavingSegmentList(); private final OrderBySegmentList orderBy = new OrderBySegmentList(); @Getter(AccessLevel.NONE) private String sqlSegment = StringPool.EMPTY; @Getter(AccessLevel.NONE) private boolean cacheSqlSegment = true; public void add(ISqlSegment... iSqlSegments) { List list = Arrays.asList(iSqlSegments); ISqlSegment firstSqlSegment = list.get(0); if (MatchSegment.ORDER_BY.match(firstSqlSegment)) { orderBy.addAll(list); } else if (MatchSegment.GROUP_BY.match(firstSqlSegment)) { groupBy.addAll(list); } else if (MatchSegment.HAVING.match(firstSqlSegment)) { having.addAll(list); } else { normal.addAll(list); } cacheSqlSegment = false; } @Override public String getSqlSegment() { if (cacheSqlSegment) { return sqlSegment; } cacheSqlSegment = true; if (normal.isEmpty()) { if (!groupBy.isEmpty() || !orderBy.isEmpty()) { sqlSegment = groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment(); } } else { sqlSegment = normal.getSqlSegment() + groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment(); } return sqlSegment; } /** * 清理 * * @since 3.3.1 */ public void clear() { sqlSegment = StringPool.EMPTY; cacheSqlSegment = true; normal.clear(); groupBy.clear(); having.clear(); orderBy.clear(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/NormalSegmentList.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.segments; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import com.baomidou.mybatisplus.core.enums.SqlKeyword; import java.util.List; import java.util.stream.Collectors; /** * 普通片段 * * @author miemie * @since 2018-06-27 */ public class NormalSegmentList extends AbstractISegmentList { /** * 是否处理了的上个 not */ private boolean executeNot = true; NormalSegmentList() { this.flushLastValue = true; } @Override protected boolean transformList(List list, ISqlSegment firstSegment, ISqlSegment lastSegment) { if (list.size() == 1) { /* 只有 and() 以及 or() 以及 not() 会进入 */ if (!MatchSegment.NOT.match(firstSegment)) { //不是 not if (isEmpty()) { //sqlSegment是 and 或者 or 并且在第一位,不继续执行 return false; } boolean matchLastAnd = MatchSegment.AND.match(lastValue); boolean matchLastOr = MatchSegment.OR.match(lastValue); if (matchLastAnd || matchLastOr) { //上次最后一个值是 and 或者 or if (matchLastAnd && MatchSegment.AND.match(firstSegment)) { return false; } else if (matchLastOr && MatchSegment.OR.match(firstSegment)) { return false; } else { //和上次的不一样 removeAndFlushLast(); } } } else { executeNot = false; return false; } } else { if (MatchSegment.APPLY.match(firstSegment)) { list.remove(0); } if (!MatchSegment.AND_OR.match(lastValue) && !isEmpty()) { add(SqlKeyword.AND); } if (!executeNot) { list.add(0, SqlKeyword.NOT); executeNot = true; } } return true; } @Override protected String childrenSqlSegment() { if (MatchSegment.AND_OR.match(lastValue)) { removeAndFlushLast(); } final String str = this.stream().map(ISqlSegment::getSqlSegment).collect(Collectors.joining(SPACE)); return (LEFT_BRACKET + str + RIGHT_BRACKET); } @Override public void clear() { super.clear(); flushLastValue = true; executeNot = true; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/OrderBySegmentList.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.segments; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import java.util.ArrayList; import java.util.List; import static com.baomidou.mybatisplus.core.enums.SqlKeyword.ORDER_BY; import static java.util.stream.Collectors.joining; /** * Order By SQL 片段 * * @author miemie * @since 2018-06-27 */ public class OrderBySegmentList extends AbstractISegmentList { @Override protected boolean transformList(List list, ISqlSegment firstSegment, ISqlSegment lastSegment) { list.remove(0); final List sqlSegmentList = new ArrayList<>(list); list.clear(); list.add(() -> sqlSegmentList.stream().map(ISqlSegment::getSqlSegment).collect(joining(SPACE))); return true; } @Override protected String childrenSqlSegment() { if (isEmpty()) { return EMPTY; } return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(COMMA, SPACE + ORDER_BY.getSqlSegment() + SPACE, EMPTY)); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * SQL 片段相关类 */ package com.baomidou.mybatisplus.core.conditions.segments; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/update/LambdaUpdateWrapper.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.update; import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper; import com.baomidou.mybatisplus.core.conditions.SharedString; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * Lambda 更新封装 * * @author hubin miemie HCL * @since 2018-05-30 */ public class LambdaUpdateWrapper extends AbstractLambdaWrapper> implements Update, SFunction> { /** * SQL 更新字段内容,例如:name='1', age=2 */ private final List sqlSet; public LambdaUpdateWrapper() { // 如果无参构造函数,请注意实体 NULL 情况 SET 必须有否则 SQL 异常 this((T) null); } public LambdaUpdateWrapper(T entity) { super.setEntity(entity); super.initNeed(); this.sqlSet = new ArrayList<>(); } public LambdaUpdateWrapper(Class entityClass) { super.setEntityClass(entityClass); super.initNeed(); this.sqlSet = new ArrayList<>(); } LambdaUpdateWrapper(T entity, Class entityClass, List sqlSet, AtomicInteger paramNameSeq, Map paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) { super.setEntity(entity); super.setEntityClass(entityClass); this.sqlSet = sqlSet; this.paramNameSeq = paramNameSeq; this.paramNameValuePairs = paramNameValuePairs; this.expression = mergeSegments; this.paramAlias = paramAlias; this.lastSql = lastSql; this.sqlComment = sqlComment; this.sqlFirst = sqlFirst; } @Override public LambdaUpdateWrapper set(boolean condition, SFunction column, Object val, String mapping) { return maybeDo(condition, () -> { String sql = formatParam(mapping, val); sqlSet.add(columnToString(column) + Constants.EQUALS + sql); }); } @Override public LambdaUpdateWrapper setSql(boolean condition, String setSql, Object... params) { return maybeDo(condition && StringUtils.isNotBlank(setSql), () -> { sqlSet.add(formatSqlMaybeWithParam(setSql, params)); }); } @Override public LambdaUpdateWrapper setIncrBy(boolean condition, SFunction column, Number val) { return maybeDo(condition, () -> { String realColumn = columnToString(column); sqlSet.add(String.format("%s=%s + %s", realColumn, realColumn, val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : val)); }); } @Override public LambdaUpdateWrapper setDecrBy(boolean condition, SFunction column, Number val) { return maybeDo(condition, () -> { String realColumn = columnToString(column); sqlSet.add(String.format("%s=%s - %s", realColumn, realColumn, val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : val)); }); } @Override public String getSqlSet() { if (CollectionUtils.isEmpty(sqlSet)) { return null; } return String.join(Constants.COMMA, sqlSet); } @Override protected LambdaUpdateWrapper instance() { return new LambdaUpdateWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs, new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString()); } @Override public void clear() { super.clear(); sqlSet.clear(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/update/Update.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.update; import java.io.Serializable; /** * @author miemie * @since 2018-12-12 */ public interface Update extends Serializable { /** * 设置 更新 SQL 的 SET 片段 * * @param column 字段 * @param val 值 * @return children */ default Children set(R column, Object val) { return set(true, column, val); } /** * 设置 更新 SQL 的 SET 片段 * * @param condition 是否加入 set * @param column 字段 * @param val 值 * @return children */ default Children set(boolean condition, R column, Object val) { return set(condition, column, val, null); } /** * 设置 更新 SQL 的 SET 片段 * * @param column 字段 * @param val 值 * @param mapping 例: javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler * @return children */ default Children set(R column, Object val, String mapping) { return set(true, column, val, mapping); } /** * 设置 更新 SQL 的 SET 片段 * * @param condition 是否加入 set * @param column 字段 * @param val 值 * @param mapping 例: javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler * @return children */ Children set(boolean condition, R column, Object val, String mapping); /** * 设置 更新 SQL 的 SET 片段 * * @param setSql set sql * 例1: setSql("id=1") * 例2: setSql("dateColumn={0}", LocalDate.now()) * 例4: setSql("type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "待处理字符串") * @return children */ default Children setSql(String setSql, Object... params) { return setSql(true, setSql, params); } /** * 设置 更新 SQL 的 SET 片段 * * @param condition 执行条件 * @param setSql set sql * 例1: setSql("id=1") * 例2: setSql("dateColumn={0}", LocalDate.now()) * 例4: setSql("type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "待处理字符串") * @return children */ Children setSql(boolean condition, String setSql, Object... params); /** * 字段自增变量 val 值 * * @param column 字段 * @param val 变量值 1 字段自增 + 1 */ default Children setIncrBy(R column, Number val) { return setIncrBy(true, column, val); } /** * 字段自增变量 val 值 * * @param condition 是否加入 set * @param column 字段 * @param val 变量值 1 字段自增 + 1 */ Children setIncrBy(boolean condition, R column, Number val); /** * 字段自减变量 val 值 * * @param column 字段 * @param val 变量值 1 字段自减 - 1 */ default Children setDecrBy(R column, Number val) { return setDecrBy(true, column, val); } /** * 字段自减变量 val 值 * * @param condition 是否加入 set * @param column 字段 * @param val 变量值 1 字段自减 - 1 */ Children setDecrBy(boolean condition, R column, Number val); /** * 获取 更新 SQL 的 SET 片段 */ String getSqlSet(); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/update/UpdateWrapper.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.conditions.update; import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; import com.baomidou.mybatisplus.core.conditions.SharedString; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * Update 条件封装 * * @author hubin miemie HCL * @since 2018-05-30 */ public class UpdateWrapper extends AbstractWrapper> implements Update, String> { /** * SQL 更新字段内容,例如:name='1', age=2 */ private final List sqlSet; public UpdateWrapper() { // 如果无参构造函数,请注意实体 NULL 情况 SET 必须有否则 SQL 异常 this(null); } public UpdateWrapper(T entity) { super.setEntity(entity); super.initNeed(); this.sqlSet = new ArrayList<>(); } private UpdateWrapper(T entity, List sqlSet, AtomicInteger paramNameSeq, Map paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) { super.setEntity(entity); this.sqlSet = sqlSet; this.paramNameSeq = paramNameSeq; this.paramNameValuePairs = paramNameValuePairs; this.expression = mergeSegments; this.paramAlias = paramAlias; this.lastSql = lastSql; this.sqlComment = sqlComment; this.sqlFirst = sqlFirst; } /** * 检查 SQL 注入过滤 */ private boolean checkSqlInjection; /** * 开启检查 SQL 注入 */ public UpdateWrapper checkSqlInjection() { this.checkSqlInjection = true; return this; } @Override protected String columnToString(String column) { if (checkSqlInjection && SqlInjectionUtils.check(column)) { throw new MybatisPlusException("Discovering SQL injection column: " + column); } return column; } @Override public String getSqlSet() { if (CollectionUtils.isEmpty(sqlSet)) { return null; } return String.join(Constants.COMMA, sqlSet); } @Override public UpdateWrapper set(boolean condition, String column, Object val, String mapping) { return maybeDo(condition, () -> { String sql = formatParam(mapping, val); sqlSet.add(column + Constants.EQUALS + sql); }); } @Override public UpdateWrapper setSql(boolean condition, String setSql, Object... params) { return maybeDo(condition && StringUtils.isNotBlank(setSql), () -> { sqlSet.add(formatSqlMaybeWithParam(setSql, params)); }); } @Override public UpdateWrapper setIncrBy(boolean condition, String column, Number val) { return maybeDo(condition, () -> { sqlSet.add(String.format("%s=%s + %s", column, column, val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : val)); }); } @Override public UpdateWrapper setDecrBy(boolean condition, String column, Number val) { return maybeDo(condition, () -> { sqlSet.add(String.format("%s=%s - %s", column, column, val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : val)); }); } /** * 返回一个支持 lambda 函数写法的 wrapper */ public LambdaUpdateWrapper lambda() { return new LambdaUpdateWrapper<>(getEntity(), getEntityClass(), sqlSet, paramNameSeq, paramNameValuePairs, expression, paramAlias, lastSql, sqlComment, sqlFirst); } @Override protected UpdateWrapper instance() { return new UpdateWrapper<>(getEntity(), null, paramNameSeq, paramNameValuePairs, new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString()); } @Override public void clear() { super.clear(); sqlSet.clear(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/update/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 更新 Wrapper */ package com.baomidou.mybatisplus.core.conditions.update; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/config/GlobalConfig.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.config; import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.handlers.AnnotationHandler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.injector.ISqlInjector; import com.baomidou.mybatisplus.core.mapper.Mapper; import lombok.Data; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; import org.apache.ibatis.session.SqlSessionFactory; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentSkipListSet; /** * Mybatis 全局缓存 * * @author Caratacus * @since 2016-12-06 */ @Data @Accessors(chain = true) public class GlobalConfig implements Serializable { /** * 是否开启 LOGO */ private boolean banner = true; /** * 是否初始化 SqlRunner */ private boolean enableSqlRunner = false; /** * 数据库相关配置 */ private DbConfig dbConfig; /** * SQL注入器 */ private ISqlInjector sqlInjector = new DefaultSqlInjector(); /** * Mapper父类 */ private Class superMapperClass = Mapper.class; /** * 仅用于缓存 SqlSessionFactory(外部勿进行set,set了也没用) * * @deprecated 3.5.3.2 */ @Deprecated private SqlSessionFactory sqlSessionFactory; /** * 缓存已注入CRUD的Mapper信息 */ private Set mapperRegistryCache = new ConcurrentSkipListSet<>(); /** * 元对象字段填充控制器 */ private MetaObjectHandler metaObjectHandler; /** * 注解控制器 */ private AnnotationHandler annotationHandler = new AnnotationHandler() { }; /** * 参与 TableInfo 的初始化 */ private PostInitTableInfoHandler postInitTableInfoHandler = new PostInitTableInfoHandler() { }; /** * 主键生成器 */ private IdentifierGenerator identifierGenerator; /** * 数据库相关配置 */ private Sequence sequence = new Sequence(); @Data public static class DbConfig { /** * 主键类型 */ private IdType idType = IdType.ASSIGN_ID; /** * 表名前缀 */ private String tablePrefix; /** * schema * * @since 3.1.1 */ private String schema; /** * db字段 format *

    * 例: `%s` *

    * 对主键无效 * * @since 3.1.1 */ private String columnFormat; /** * db 表 format *

    * 例: `%s` *

    * * @since 3.5.3.2 */ private String tableFormat; /** * entity 的字段(property)的 format,只有在 column as property 这种情况下生效 *

    * 例: `%s` *

    * 对主键无效 * * @since 3.3.0 */ private String propertyFormat; /** * 实验性功能,占位符替换,等同于 {@link com.baomidou.mybatisplus.extension.plugins.inner.ReplacePlaceholderInnerInterceptor}, * 只是这个属于启动时替换,用得地方多会启动慢一点点,不适用于其他的 {@link org.apache.ibatis.scripting.LanguageDriver} * * @since 3.4.2 */ private boolean replacePlaceholder; /** * 转义符 *

    * 配合 {@link #replacePlaceholder} 使用时有效 *

    * 例: " 或 ' 或 ` * * @since 3.4.2 */ private String escapeSymbol; /** * 表名是否使用驼峰转下划线命名,只对表名生效 */ private boolean tableUnderline = true; /** * 大写命名,对表名和字段名均生效 */ private boolean capitalMode = false; /** * 表主键生成器 */ private List keyGenerators; /** * 逻辑删除全局属性名 */ private String logicDeleteField; /** * 逻辑删除全局值(默认 1、表示已删除) */ private String logicDeleteValue = "1"; /** * 逻辑未删除全局值(默认 0、表示未删除) */ private String logicNotDeleteValue = "0"; /** * 字段验证策略之 insert * * @since 3.1.2 */ private FieldStrategy insertStrategy = FieldStrategy.NOT_NULL; /** * 字段验证策略之 update * * @since 3.1.2 */ private FieldStrategy updateStrategy = FieldStrategy.NOT_NULL; /** * 字段验证策略之 where * * @since 3.4.4 */ private FieldStrategy whereStrategy = FieldStrategy.NOT_NULL; /** * 生成INSERT语句时忽略自增主键字段(默认不忽略,主键有值时写入主键值,无值自增). *

    当设置为true时,执行生成SQL语句无论ID是否有值都会忽视 (此为3.4.3.1版本下策略,如果升级遇到问题可以考虑开启此配置来兼容升级)

    * * @since 3.5.6 */ private boolean insertIgnoreAutoIncrementColumn = false; } /** * 雪花ID配置 *

    * 1. 手动指定{@link #workerId} 和 {@link #datacenterId} *

    *

    * 2. 基于网卡信息和进程PID计算 {@link #workerId} 和 {@link #datacenterId} *

    * * @since 3.5.7 */ @Getter @Setter public static class Sequence { /** * 工作机器 ID */ private Long workerId; /** * 数据标识 ID 部分 */ private Long datacenterId; /** * 首选网络地址 (例如: 192.168.1,支持正则) */ private List preferredNetworks = new ArrayList<>(); /** * 忽略网卡(例如:eth0,,支持正则) */ private List ignoredInterfaces = new ArrayList<>(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/config/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 全局默认配置 */ package com.baomidou.mybatisplus.core.config; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/SqlKeyword.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.enums; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import com.baomidou.mybatisplus.core.toolkit.StringPool; import lombok.AllArgsConstructor; /** * SQL 保留关键字枚举 * * @author hubin * @since 2018-05-28 */ @AllArgsConstructor public enum SqlKeyword implements ISqlSegment { AND("AND"), OR("OR"), NOT("NOT"), IN("IN"), NOT_IN("NOT IN"), LIKE("LIKE"), NOT_LIKE("NOT LIKE"), EQ(StringPool.EQUALS), NE("<>"), GT(StringPool.RIGHT_CHEV), GE(">="), LT(StringPool.LEFT_CHEV), LE("<="), IS_NULL("IS NULL"), IS_NOT_NULL("IS NOT NULL"), GROUP_BY("GROUP BY"), HAVING("HAVING"), ORDER_BY("ORDER BY"), EXISTS("EXISTS"), NOT_EXISTS("NOT EXISTS"), BETWEEN("BETWEEN"), NOT_BETWEEN("NOT BETWEEN"), ASC("ASC"), DESC("DESC"); private final String keyword; @Override public String getSqlSegment() { return this.keyword; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/SqlLike.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.enums; /** * SQL like 枚举 * * @author Caratacus * @since 2016-12-4 */ public enum SqlLike { /** * %值 */ LEFT, /** * 值% */ RIGHT, /** * %值% */ DEFAULT } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/SqlMethod.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.enums; /** * MybatisPlus 支持 SQL 方法 * * @author hubin * @since 2016-01-23 */ public enum SqlMethod { /** * 插入 */ INSERT_ONE("insert", "插入一条数据(选择字段插入)", ""), UPSERT_ONE("upsert", "Phoenix插入一条数据(选择字段插入)", ""), /** * 删除 */ DELETE_BY_ID("deleteById", "根据ID 删除一条数据", "DELETE FROM %s WHERE %s=#{%s}"), @Deprecated DELETE_BY_MAP("deleteByMap", "根据columnMap 条件删除记录", ""), DELETE("delete", "根据 entity 条件删除记录", ""), /** * @deprecated 3.5.7 {@link #DELETE_BY_IDS} */ @Deprecated DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量删除数据", ""), /** * @since 3.5.7 */ DELETE_BY_IDS("deleteByIds", "根据ID集合,批量删除数据", ""), /** * 逻辑删除 */ LOGIC_DELETE_BY_ID("deleteById", "根据ID 逻辑删除一条数据", ""), LOGIC_DELETE_BY_MAP("deleteByMap", "根据columnMap 条件逻辑删除记录", ""), LOGIC_DELETE("delete", "根据 entity 条件逻辑删除记录", ""), /** * @deprecated 3.5.7 {@link #LOGIC_DELETE_BY_IDS} */ @Deprecated LOGIC_DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量逻辑删除数据", ""), /** * @since 3.5.7 */ LOGIC_DELETE_BY_IDS("deleteByIds", "根据ID集合,批量逻辑删除数据", ""), /** * 修改 */ UPDATE_BY_ID("updateById", "根据ID 选择修改数据", ""), UPDATE("update", "根据 whereEntity 条件,更新记录", ""), /** * 逻辑删除 -> 修改 */ LOGIC_UPDATE_BY_ID("updateById", "根据ID 修改数据", ""), /** * 查询 */ SELECT_BY_ID("selectById", "根据ID 查询一条数据", "SELECT %s FROM %s WHERE %s=#{%s} %s"), @Deprecated SELECT_BY_MAP("selectByMap", "根据columnMap 查询一条数据", ""), /** * @deprecated 3.5.8 {@link #SELECT_BY_IDS} */ @Deprecated SELECT_BATCH_BY_IDS("selectBatchIds", "根据ID集合,批量查询数据", ""), /** * @since 3.5.8 */ SELECT_BY_IDS("selectByIds", "根据ID集合,批量查询数据", ""), @Deprecated SELECT_ONE("selectOne", "查询满足条件一条数据", ""), SELECT_COUNT("selectCount", "查询满足条件总记录数", ""), SELECT_LIST("selectList", "查询满足条件所有数据", ""), @Deprecated SELECT_PAGE("selectPage", "查询满足条件所有数据(并翻页)", ""), SELECT_MAPS("selectMaps", "查询满足条件所有数据", ""), @Deprecated SELECT_MAPS_PAGE("selectMapsPage", "查询满足条件所有数据(并翻页)", ""), SELECT_OBJS("selectObjs", "查询满足条件所有数据", ""); private final String method; private final String desc; private final String sql; SqlMethod(String method, String desc, String sql) { this.method = method; this.desc = desc; this.sql = sql; } public String format(Object... args) { return String.format(sql, args); } public String getMethod() { return method; } public String getDesc() { return desc; } public String getSql() { return sql; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/WrapperKeyword.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.enums; import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import lombok.AllArgsConstructor; /** * wrapper 内部使用枚举 * * @author miemie * @since 2018-07-30 */ @AllArgsConstructor public enum WrapperKeyword implements ISqlSegment { /** * 只用作于辨识,不用于其他 */ APPLY(null); private final String keyword; @Override public String getSqlSegment() { return keyword; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 枚举相关 */ package com.baomidou.mybatisplus.core.enums; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/exceptions/MybatisPlusException.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.exceptions; import org.apache.ibatis.exceptions.PersistenceException; /** * MybatisPlus 异常类 * * @author hubin * @since 2016-01-23 */ public class MybatisPlusException extends PersistenceException { private static final long serialVersionUID = 1L; public MybatisPlusException(String message) { super(message); } public MybatisPlusException(Throwable throwable) { super(throwable); } public MybatisPlusException(String message, Throwable throwable) { super(message, throwable); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/exceptions/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 异常 */ package com.baomidou.mybatisplus.core.exceptions; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/AnnotationHandler.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.handlers; import com.baomidou.mybatisplus.core.toolkit.AnnotationUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * @author 唐振超 * @since 2023-02-25 */ public interface AnnotationHandler { /** * 从类上获取注解 * * @param beanClass 类的class * @param annotationClass 要获取的注解class * @param 具体注解 * @return 注解 */ default T getAnnotation(Class beanClass, Class annotationClass) { return AnnotationUtils.findFirstAnnotation(annotationClass, beanClass); } /** * 判断类上是否存在注解 * * @param beanClass 类的class * @param annotationClass 要获取的注解class * @param 具体注解 * @return 是否包含该注解 */ default boolean isAnnotationPresent(Class beanClass, Class annotationClass) { return AnnotationUtils.findFirstAnnotation(annotationClass, beanClass) != null; } /** * 从字段上获取注解 * * @param field 字段 * @param annotationClass 要获取的注解class * @param 具体注解 * @return 注解 */ default T getAnnotation(Field field, Class annotationClass) { return AnnotationUtils.findFirstAnnotation(annotationClass, field); } /** * 判断字段上是否存在注解 * * @param field 字段 * @param annotationClass 要获取的注解class * @param 具体注解 * @return 是否包含该注解 */ default boolean isAnnotationPresent(Field field, Class annotationClass) { return AnnotationUtils.findFirstAnnotation(annotationClass, field) != null; } /** * 从方法上获取注解 * * @param method 方法 * @param annotationClass 要获取的注解class * @param 具体注解 * @return 注解 */ default T getAnnotation(Method method, Class annotationClass) { return AnnotationUtils.findFirstAnnotation(annotationClass, method); } /** * 判断方法上是否存在注解 * * @param method 方法 * @param annotationClass 要获取的注解class * @param 具体注解 * @return 是否包含该注解 */ default boolean isAnnotationPresent(Method method, Class annotationClass) { return AnnotationUtils.findFirstAnnotation(annotationClass, method) != null; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/CompositeEnumTypeHandler.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.handlers; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import org.apache.ibatis.type.EnumTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeException; import org.apache.ibatis.type.TypeHandler; import java.lang.reflect.Constructor; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @author miemie * @since 2022-03-07 */ public class CompositeEnumTypeHandler> implements TypeHandler { private static final Map, Boolean> MP_ENUM_CACHE = new ConcurrentHashMap<>(); private static Class defaultEnumTypeHandler = EnumTypeHandler.class; private final TypeHandler delegate; public CompositeEnumTypeHandler(Class enumClassType) { if (enumClassType == null) { throw new IllegalArgumentException("Type argument cannot be null"); } if (CollectionUtils.computeIfAbsent(MP_ENUM_CACHE, enumClassType, MybatisEnumTypeHandler::isMpEnums)) { delegate = new MybatisEnumTypeHandler<>(enumClassType); } else { delegate = getInstance(enumClassType, defaultEnumTypeHandler); } } public static void setDefaultEnumTypeHandler(Class defaultEnumTypeHandler) { if (defaultEnumTypeHandler != null && !MybatisEnumTypeHandler.class.isAssignableFrom(defaultEnumTypeHandler)) { CompositeEnumTypeHandler.defaultEnumTypeHandler = defaultEnumTypeHandler; } } @Override public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { delegate.setParameter(ps, i, parameter, jdbcType); } @Override public E getResult(ResultSet rs, String columnName) throws SQLException { return delegate.getResult(rs, columnName); } @Override public E getResult(ResultSet rs, int columnIndex) throws SQLException { return delegate.getResult(rs, columnIndex); } @Override public E getResult(CallableStatement cs, int columnIndex) throws SQLException { return delegate.getResult(cs, columnIndex); } @SuppressWarnings("unchecked") public TypeHandler getInstance(Class javaTypeClass, Class typeHandlerClass) { if (javaTypeClass != null) { try { Constructor c = typeHandlerClass.getConstructor(Class.class); return (TypeHandler) c.newInstance(javaTypeClass); } catch (NoSuchMethodException ignored) { // ignored } catch (Exception e) { throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e); } } try { Constructor c = typeHandlerClass.getConstructor(); return (TypeHandler) c.newInstance(); } catch (Exception e) { throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e); } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/IJsonTypeHandler.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.handlers; import com.baomidou.mybatisplus.annotation.TableName; /** * Json类型处理器接口(实现类确保为多例状态). *

    * 注意:查询返回时需要使用resultMap * *

     * Example:
     *     <result property="xx" column="xx" javaType="list" typeHandler="com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler"/>
     *     <result property="xx" column="xx" typeHandler="com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler"/>
     * 
    *

    * * @author nieqiurong 2024年3月4日 * @see TableName#autoResultMap() 自动构建 * @see TableName#resultMap() 手动指定 * @since 3.5.6 */ public interface IJsonTypeHandler { /** * 反序列化json * * @param json json字符串 * @return T */ T parse(String json); /** * 序列化json * * @param obj 对象信息 * @return json字符串 */ String toJson(T obj); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/MetaObjectHandler.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.handlers; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.reflection.MetaObject; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Supplier; /** * 元对象字段填充控制器抽象类,实现公共字段自动写入

    *

    * 所有入参的 MetaObject 必定是 entity 或其子类的 MetaObject * * @author hubin * @since 2016-08-28 */ public interface MetaObjectHandler { /** * 是否开启了插入填充 * * @deprecated 3.5.6 {@link #openInsertFill(MappedStatement)} */ @Deprecated default boolean openInsertFill() { return true; } /** * 是否开启插入填充 * * @param mappedStatement {@link MappedStatement} * @return 是否开启 * @since 3.5.6 */ default boolean openInsertFill(MappedStatement mappedStatement) { return true; } /** * 是否开启了更新填充 * * @deprecated 3.5.6 {@link #openUpdateFill(MappedStatement)} */ @Deprecated default boolean openUpdateFill() { return true; } /** * 是否开启了更新填充 * * @param mappedStatement {@link MappedStatement} * @return 是否开启 * @since 3.5.6 */ default boolean openUpdateFill(MappedStatement mappedStatement) { return true; } /** * 插入元对象字段填充(用于插入时对公共字段的填充) * * @param metaObject 元对象 */ void insertFill(MetaObject metaObject); /** * 更新元对象字段填充(用于更新时对公共字段的填充) * * @param metaObject 元对象 */ void updateFill(MetaObject metaObject); /** * 通用填充 * * @param fieldName java bean property name * @param fieldVal java bean property value * @param metaObject meta object parameter */ default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) { if (Objects.nonNull(fieldVal) && metaObject.hasSetter(fieldName)) { metaObject.setValue(fieldName, fieldVal); } return this; } /** * get value from java bean by propertyName * * @param fieldName java bean property name * @param metaObject parameter wrapper * @return 字段值 */ default Object getFieldValByName(String fieldName, MetaObject metaObject) { return metaObject.hasGetter(fieldName) ? metaObject.getValue(fieldName) : null; } /** * find the tableInfo cache by metaObject

    * 获取 TableInfo 缓存 * * @param metaObject meta object parameter * @return TableInfo * @since 3.3.0 */ default TableInfo findTableInfo(MetaObject metaObject) { return TableInfoHelper.getTableInfo(metaObject.getOriginalObject().getClass()); } /** * @param metaObject metaObject meta object parameter * @return this * @since 3.3.0 */ default MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Class fieldType, E fieldVal) { return strictInsertFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal))); } /** * @param metaObject metaObject meta object parameter * @return this * @since 3.3.0 */ default MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Supplier fieldVal, Class fieldType) { return strictInsertFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldVal, fieldType))); } /** * @param metaObject metaObject meta object parameter * @return this * @since 3.3.0 */ default MetaObjectHandler strictInsertFill(TableInfo tableInfo, MetaObject metaObject, List> strictFills) { return strictFill(true, tableInfo, metaObject, strictFills); } /** * @param metaObject metaObject meta object parameter * @return this * @since 3.3.0 */ default MetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Supplier fieldVal, Class fieldType) { return strictUpdateFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldVal, fieldType))); } /** * @param metaObject metaObject meta object parameter * @return this * @since 3.3.0 */ default MetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Class fieldType, E fieldVal) { return strictUpdateFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal))); } /** * @param metaObject metaObject meta object parameter * @return this * @since 3.3.0 */ default MetaObjectHandler strictUpdateFill(TableInfo tableInfo, MetaObject metaObject, List> strictFills) { return strictFill(false, tableInfo, metaObject, strictFills); } /** * 严格填充,只针对非主键的字段,只有该表注解了fill 并且 字段名和字段属性 能匹配到才会进行填充(null 值不填充) * * @param insertFill 是否验证在 insert 时填充 * @param tableInfo cache 缓存 * @param metaObject metaObject meta object parameter * @param strictFills 填充信息 * @return this * @since 3.3.0 */ default MetaObjectHandler strictFill(boolean insertFill, TableInfo tableInfo, MetaObject metaObject, List> strictFills) { if ((insertFill && tableInfo.isWithInsertFill()) || (!insertFill && tableInfo.isWithUpdateFill())) { strictFills.forEach(i -> { final String fieldName = i.getFieldName(); final Class fieldType = i.getFieldType(); tableInfo.getFieldList().stream() .filter(j -> j.getProperty().equals(fieldName) && fieldType.equals(j.getPropertyType()) && ((insertFill && j.isWithInsertFill()) || (!insertFill && j.isWithUpdateFill()))).findFirst() .ifPresent(j -> strictFillStrategy(metaObject, fieldName, i.getFieldVal())); }); } return this; } /** * 填充策略,默认有值不覆盖,如果提供的值为null也不填充 * * @param metaObject metaObject meta object parameter * @param fieldName java bean property name * @param fieldVal java bean property value of Supplier * @return this * @since 3.3.0 */ default MetaObjectHandler fillStrategy(MetaObject metaObject, String fieldName, Object fieldVal) { if (getFieldValByName(fieldName, metaObject) == null) { setFieldValByName(fieldName, fieldVal, metaObject); } return this; } /** * 严格模式填充策略,默认有值不覆盖,如果提供的值为null也不填充 * * @param metaObject metaObject meta object parameter * @param fieldName java bean property name * @param fieldVal java bean property value of Supplier * @return this * @since 3.3.0 */ default MetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier fieldVal) { if (metaObject.getValue(fieldName) == null) { Object obj = fieldVal.get(); if (Objects.nonNull(obj)) { metaObject.setValue(fieldName, obj); } } return this; } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/MybatisEnumTypeHandler.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.handlers; import com.baomidou.mybatisplus.annotation.EnumValue; import com.baomidou.mybatisplus.annotation.IEnum; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils; import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import org.apache.ibatis.reflection.DefaultReflectorFactory; import org.apache.ibatis.reflection.MetaClass; import org.apache.ibatis.reflection.ReflectorFactory; import org.apache.ibatis.reflection.invoker.Invoker; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.lang.reflect.Field; import java.math.BigDecimal; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; /** * 自定义枚举属性转换器 * * @author hubin * @since 2017-10-11 */ public final class MybatisEnumTypeHandler> extends BaseTypeHandler { private static final Map TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>(); private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory(); private final Class enumClassType; private final Class propertyType; private final Invoker getInvoker; public MybatisEnumTypeHandler(Class enumClassType) { if (enumClassType == null) { throw new IllegalArgumentException("Type argument cannot be null"); } this.enumClassType = enumClassType; MetaClass metaClass = MetaClass.forClass(enumClassType, REFLECTOR_FACTORY); String name = "value"; if (!IEnum.class.isAssignableFrom(enumClassType)) { name = findEnumValueFieldName(this.enumClassType).orElseThrow(() -> new IllegalArgumentException(String.format("Could not find @EnumValue in Class: %s.", this.enumClassType.getName()))); } this.propertyType = ReflectionKit.resolvePrimitiveIfNecessary(metaClass.getGetterType(name)); this.getInvoker = metaClass.getGetInvoker(name); } /** * 查找标记标记EnumValue字段 * * @param clazz class * @return EnumValue字段 * @since 3.3.1 */ public static Optional findEnumValueFieldName(Class clazz) { if (clazz != null && clazz.isEnum()) { String className = clazz.getName(); return Optional.ofNullable(CollectionUtils.computeIfAbsent(TABLE_METHOD_OF_ENUM_TYPES, className, key -> { Optional fieldOptional = findEnumValueAnnotationField(clazz); return fieldOptional.map(Field::getName).orElse(null); })); } return Optional.empty(); } private static Optional findEnumValueAnnotationField(Class clazz) { return Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumValue.class)).findFirst(); } /** * 判断是否为MP枚举处理 * * @param clazz class * @return 是否为MP枚举处理 * @since 3.3.1 */ public static boolean isMpEnums(Class clazz) { return clazz != null && clazz.isEnum() && (IEnum.class.isAssignableFrom(clazz) || findEnumValueFieldName(clazz).isPresent()); } @SuppressWarnings("Duplicates") @Override public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { if (jdbcType == null) { ps.setObject(i, this.getValue(parameter)); } else { // see r3589 ps.setObject(i, this.getValue(parameter), jdbcType.TYPE_CODE); } } @Override public E getNullableResult(ResultSet rs, String columnName) throws SQLException { Object value = rs.getObject(columnName, this.propertyType); if (null == value || rs.wasNull()) { return null; } return this.valueOf(value); } @Override public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { Object value = rs.getObject(columnIndex, this.propertyType); if (null == value || rs.wasNull()) { return null; } return this.valueOf(value); } @Override public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { Object value = cs.getObject(columnIndex, this.propertyType); if (null == value || cs.wasNull()) { return null; } return this.valueOf(value); } private E valueOf(Object value) { E[] es = this.enumClassType.getEnumConstants(); return Arrays.stream(es).filter((e) -> equalsValue(value, getValue(e))).findAny().orElse(null); } /** * 值比较 * * @param sourceValue 数据库字段值 * @param targetValue 当前枚举属性值 * @return 是否匹配 * @since 3.3.0 */ protected boolean equalsValue(Object sourceValue, Object targetValue) { String sValue = StringUtils.toStringTrim(sourceValue); String tValue = StringUtils.toStringTrim(targetValue); if (sourceValue instanceof Number && targetValue instanceof Number && new BigDecimal(sValue).compareTo(new BigDecimal(tValue)) == 0) { return true; } return Objects.equals(sValue, tValue); } private Object getValue(Object object) { try { return this.getInvoker.invoke(object, new Object[0]); } catch (ReflectiveOperationException e) { throw ExceptionUtils.mpe(e); } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/PostInitTableInfoHandler.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.handlers; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.session.Configuration; /** * 初始化 TableInfo 同时进行一些操作 * * @author miemie * @since 2022-09-20 */ public interface PostInitTableInfoHandler { /** * 提供对 TableInfo 增强的能力 * * @param configuration MybatisConfiguration * @param entityType 实体类型 * @return {@link TableInfo} */ default TableInfo creteTableInfo(Configuration configuration, Class entityType) { return new TableInfo(configuration, entityType); } /** * 参与 TableInfo 初始化 * * @param tableInfo TableInfo * @param configuration Configuration */ default void postTableInfo(TableInfo tableInfo, Configuration configuration) { // ignore } /** * 参与 TableFieldInfo 初始化 * * @param fieldInfo TableFieldInfo * @param configuration Configuration */ default void postFieldInfo(TableFieldInfo fieldInfo, Configuration configuration) { // ignore } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/StrictFill.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.handlers; import lombok.AllArgsConstructor; import lombok.Data; import java.util.function.Supplier; /** * 严格填充模式 model * * @author miemie * @since 2019-11-26 */ @Data @AllArgsConstructor public class StrictFill { /** * 字段名 */ private String fieldName; /** * 字段类型 */ private Class fieldType; /** * 获取字段值的函数 */ private Supplier fieldVal; public static StrictFill of(String fieldName, Class fieldType, E fieldVal) { return new StrictFill<>(fieldName, fieldType, () -> fieldVal); } public static StrictFill of(String fieldName, Supplier fieldVal, Class fieldType) { return new StrictFill<>(fieldName, fieldType, fieldVal); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 处理器 */ package com.baomidou.mybatisplus.core.handlers; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/DefaultIdentifierGenerator.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.incrementer; import com.baomidou.mybatisplus.core.toolkit.Sequence; import org.apache.ibatis.logging.Log; import java.net.InetAddress; /** * 默认生成器 * * @author nieqiuqiu * @since 2019-10-15 * @since 3.3.0 */ public class DefaultIdentifierGenerator implements IdentifierGenerator { private final Sequence sequence; /** * @see #getInstance() * @deprecated 3.5.3.2 共享默认单例 */ @Deprecated public DefaultIdentifierGenerator() { this.sequence = new Sequence(null); } public DefaultIdentifierGenerator(InetAddress inetAddress) { this.sequence = new Sequence(inetAddress); } public DefaultIdentifierGenerator(long workerId, long dataCenterId) { this.sequence = new Sequence(workerId, dataCenterId); } public DefaultIdentifierGenerator(Sequence sequence) { this.sequence = sequence; } /** * 获取固定的生成器实例 *

    当无法通过网卡信息获取时,使用固定的一个主键生成器实例.

    * * @param log 日志对象 * @return 主键生成器 * @since 3.5.13 */ public static IdentifierGenerator getFixedIdentifierGenerator(Log log) { log.warn("Unable to obtain correct IP address information, the machine ID and serial number of the fixed machine will be used to generate the primary key."); return new DefaultIdentifierGenerator(1, 1); } @Override public Long nextId(Object entity) { return sequence.nextId(); } public static DefaultIdentifierGenerator getInstance() { return DefaultInstance.INSTANCE; } private static class DefaultInstance { public static final DefaultIdentifierGenerator INSTANCE = new DefaultIdentifierGenerator(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/IKeyGenerator.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.incrementer; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.KeySequence; /** * 表主键生成器接口 (sql) * * @author hubin * @since 2017-05-08 */ public interface IKeyGenerator { /** * 执行 key 生成 SQL * * @param incrementerName 序列名称(对应类上注解 {@link KeySequence#value()} 的值) * @return sql */ String executeSql(String incrementerName); /** * 数据库类型 */ DbType dbType(); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/IdentifierGenerator.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.incrementer; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.StringUtils; /** * Id生成器接口 * * @author nieqiuqiu * @since 2019-10-15 * @since 3.3.0 */ public interface IdentifierGenerator { /** * 判断是否分配 ID * * @param idValue 主键值 * @return true 分配 false 无需分配 */ default boolean assignId(Object idValue) { return StringUtils.checkValNull(idValue); } /** * 生成Id * * @param entity 实体 * @return id */ Number nextId(Object entity); /** * 生成uuid * * @param entity 实体 * @return uuid */ default String nextUUID(Object entity) { return IdWorker.get32UUID(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/ImadcnIdentifierGenerator.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.incrementer; import com.imadcn.framework.idworker.config.ApplicationConfiguration; import com.imadcn.framework.idworker.config.ZookeeperConfiguration; import com.imadcn.framework.idworker.generator.CompressUUIDGenerator; import com.imadcn.framework.idworker.generator.SnowflakeGenerator; import com.imadcn.framework.idworker.register.zookeeper.ZookeeperWorkerRegister; import com.imadcn.framework.idworker.registry.zookeeper.ZookeeperRegistryCenter; import java.io.Closeable; import java.io.IOException; /** * 用 idworker 的实现 * * @author miemie * @since 3.4.0 * @since 2020-08-11 */ public class ImadcnIdentifierGenerator implements IdentifierGenerator, Closeable { private final SnowflakeGenerator idGenerator; private final CompressUUIDGenerator uuidGenerator = new CompressUUIDGenerator(); public ImadcnIdentifierGenerator(String serverLists) { this(configuration(serverLists)); } public ImadcnIdentifierGenerator(ZookeeperConfiguration zookeeperConfiguration) { this(zookeeperConfiguration, new ApplicationConfiguration()); } public ImadcnIdentifierGenerator(ZookeeperConfiguration zookeeperConfiguration, ApplicationConfiguration applicationConfiguration) { ZookeeperRegistryCenter center = new ZookeeperRegistryCenter(zookeeperConfiguration); ZookeeperWorkerRegister register = new ZookeeperWorkerRegister(center, applicationConfiguration); idGenerator = new SnowflakeGenerator(register); idGenerator.init(); } private static ZookeeperConfiguration configuration(String serverLists) { ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration(); zookeeperConfiguration.setServerLists(serverLists); return zookeeperConfiguration; } @Override public Number nextId(Object entity) { return idGenerator.nextId(); } @Override public String nextUUID(Object entity) { return uuidGenerator.nextStringId(); } @Override public void close() throws IOException { idGenerator.close(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * key 生成器 */ package com.baomidou.mybatisplus.core.incrementer; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/AbstractMethod.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector; import com.baomidou.mybatisplus.core.metadata.OrderFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.apache.ibatis.builder.SqlSourceBuilder; import org.apache.ibatis.executor.keygen.KeyGenerator; import org.apache.ibatis.executor.keygen.NoKeyGenerator; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.mapping.StatementType; import org.apache.ibatis.scripting.LanguageDriver; import org.apache.ibatis.session.Configuration; import java.util.Comparator; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import static java.util.stream.Collectors.joining; /** * 抽象的注入方法类 * * @author hubin * @since 2018-04-06 */ public abstract class AbstractMethod implements Constants { protected final Log logger = LogFactory.getLog(getClass()); protected Configuration configuration; protected LanguageDriver languageDriver; protected MapperBuilderAssistant builderAssistant; /** * 方法名称 * * @since 3.5.0 */ protected final String methodName; /** * @param methodName 方法名 * @since 3.5.0 */ protected AbstractMethod(String methodName) { Assert.notNull(methodName, "方法名不能为空"); this.methodName = methodName; } /** * 注入自定义方法 */ public void inject(MapperBuilderAssistant builderAssistant, Class mapperClass, Class modelClass, TableInfo tableInfo) { this.configuration = builderAssistant.getConfiguration(); this.builderAssistant = builderAssistant; this.languageDriver = configuration.getDefaultScriptingLanguageInstance(); /* 注入自定义方法 */ injectMappedStatement(mapperClass, modelClass, tableInfo); } /** * 是否已经存在MappedStatement * * @param mappedStatement MappedStatement * @return true or false */ private boolean hasMappedStatement(String mappedStatement) { return configuration.hasStatement(mappedStatement, false); } /** * SQL 更新 set 语句 * * @param table 表信息 * @return sql set 片段 */ protected String sqlLogicSet(TableInfo table) { return "SET " + table.getLogicDeleteSql(false, false); } /** * SQL 更新 set 语句 * * @param logic 是否逻辑删除注入器 * @param ew 是否存在 UpdateWrapper 条件 * @param table 表信息 * @param alias 别名 * @param prefix 前缀 * @return sql */ protected String sqlSet(boolean logic, boolean ew, TableInfo table, boolean judgeAliasNull, final String alias, final String prefix) { String sqlScript = table.getAllSqlSet(logic, prefix); if (judgeAliasNull) { sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", alias), true); } if (ew) { sqlScript = sqlScript + NEWLINE + convertIfEwParam(U_WRAPPER_SQL_SET, false); } return SqlScriptUtils.convertSet(sqlScript); } /** * SQL 注释 * * @return sql */ protected String sqlComment() { return NEWLINE + convertIfEwParam(Q_WRAPPER_SQL_COMMENT, true); } /** * SQL 注释 * * @return sql */ protected String sqlFirst() { return convertIfEwParam(Q_WRAPPER_SQL_FIRST, true); } protected String convertIfEwParam(final String param, final boolean newLine) { return SqlScriptUtils.convertIf(SqlScriptUtils.unSafeParam(param), String.format("%s != null and %s != null", WRAPPER, param), newLine); } /** * SQL 查询所有表字段 * * @param table 表信息 * @param queryWrapper 是否为使用 queryWrapper 查询 * @return sql 脚本 */ protected String sqlSelectColumns(TableInfo table, boolean queryWrapper) { /* 假设存在用户自定义的 resultMap 映射返回 */ String selectColumns = ASTERISK; if (table.getResultMap() == null || table.isAutoInitResultMap()) { /* 未设置 resultMap 或者 resultMap 是自动构建的,视为属于mp的规则范围内 */ selectColumns = table.getAllSqlSelect(); } if (!queryWrapper) { return selectColumns; } return convertChooseEwSelect(selectColumns); } /** * SQL 查询记录行数 * * @return count sql 脚本 */ protected String sqlCount() { return convertChooseEwSelect(ASTERISK); } /** * SQL 设置selectObj sql select * * @param table 表信息 */ protected String sqlSelectObjsColumns(TableInfo table) { return convertChooseEwSelect(table.getAllSqlSelect()); } protected String convertChooseEwSelect(final String otherwise) { return SqlScriptUtils.convertChoose(String.format("%s != null and %s != null", WRAPPER, Q_WRAPPER_SQL_SELECT), SqlScriptUtils.unSafeParam(Q_WRAPPER_SQL_SELECT), otherwise); } /** * SQL map 查询条件 */ @Deprecated protected String sqlWhereByMap(TableInfo table) { if (table.isWithLogicDelete()) { // 逻辑删除 String sqlScript = SqlScriptUtils.convertChoose("v == null", " ${k} IS NULL ", " ${k} = #{v} "); sqlScript = SqlScriptUtils.convertForeach(sqlScript, COLUMN_MAP, "k", "v", "AND"); sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and !%s.isEmpty", COLUMN_MAP, COLUMN_MAP), true); sqlScript += (NEWLINE + table.getLogicDeleteSql(true, true)); return SqlScriptUtils.convertWhere(sqlScript); } else { String sqlScript = SqlScriptUtils.convertChoose("v == null", " ${k} IS NULL ", " ${k} = #{v} "); sqlScript = SqlScriptUtils.convertForeach(sqlScript, COLUMN_MAP, "k", "v", "AND"); sqlScript = SqlScriptUtils.convertWhere(sqlScript); return SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and !%s", COLUMN_MAP, COLUMN_MAP_IS_EMPTY), true); } } private static final String BIND_SQL_SEGMENT = ""; private static final String AND_SQL_SEGMENT = SqlScriptUtils.convertIf(" AND ${" + WRAPPER_SQLSEGMENT + "}", "_sgEs_ and " + WRAPPER_NONEMPTYOFNORMAL, true); private static final String LAST_SQL_SEGMENT = SqlScriptUtils.convertIf(" ${" + WRAPPER_SQLSEGMENT + "}", "_sgEs_ and " + WRAPPER_EMPTYOFNORMAL, true); /** * EntityWrapper方式获取select where * * @param newLine 是否提到下一行 * @param table 表信息 * @return String */ protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) { /* * 存在逻辑删除 SQL 注入 */ if (table.isWithLogicDelete()) { String sqlScript = table.getAllSqlWhere(true, true, true, WRAPPER_ENTITY_DOT); sqlScript = SqlScriptUtils.convertIf(sqlScript, WRAPPER_ENTITY + " != null", true); sqlScript = SqlScriptUtils.convertIf(BIND_SQL_SEGMENT + NEWLINE + sqlScript + NEWLINE + AND_SQL_SEGMENT + NEWLINE + LAST_SQL_SEGMENT, WRAPPER + " != null", true); sqlScript = SqlScriptUtils.convertWhere(table.getLogicDeleteSql(false, true) + NEWLINE + sqlScript); return newLine ? NEWLINE + sqlScript : sqlScript; } /* * 普通 SQL 注入 */ String sqlScript = table.getAllSqlWhere(false, false, true, WRAPPER_ENTITY_DOT); sqlScript = SqlScriptUtils.convertIf(sqlScript, WRAPPER_ENTITY + " != null", true); sqlScript = SqlScriptUtils.convertWhere(sqlScript + NEWLINE + AND_SQL_SEGMENT) + NEWLINE + LAST_SQL_SEGMENT; sqlScript = SqlScriptUtils.convertIf(BIND_SQL_SEGMENT + NEWLINE + sqlScript, WRAPPER + " != null", true); return newLine ? NEWLINE + sqlScript : sqlScript; } protected String sqlOrderBy(TableInfo tableInfo) { /* 不存在排序字段,直接返回空 */ List orderByFields = tableInfo.getOrderByFields(); if (CollectionUtils.isEmpty(orderByFields)) { return StringPool.EMPTY; } orderByFields.sort(Comparator.comparingInt(OrderFieldInfo::getSort)); StringBuilder sql = new StringBuilder(); sql.append(NEWLINE).append(" ORDER BY "); sql.append(orderByFields.stream().map(orderFieldInfo -> String.format("%s %s", orderFieldInfo.getColumn(), orderFieldInfo.getType())).collect(joining(","))); /* 当wrapper中传递了orderBy属性,@orderBy注解失效 */ return SqlScriptUtils.convertIf(sql.toString(), String.format("%s == null or %s", WRAPPER, WRAPPER_EXPRESSION_ORDER), true); } /** * 过滤 TableFieldInfo 集合, join 成字符串 */ protected String filterTableFieldInfo(List fieldList, Predicate predicate, Function function, String joiningVal) { Stream infoStream = fieldList.stream(); if (predicate != null) { return infoStream.filter(predicate).map(function).collect(joining(joiningVal)); } return infoStream.map(function).collect(joining(joiningVal)); } /** * 获取乐观锁相关 * * @param tableInfo 表信息 * @return String */ protected String optlockVersion(TableInfo tableInfo) { if (tableInfo.isWithVersion()) { return tableInfo.getVersionFieldInfo().getVersionOli(ENTITY, ENTITY_DOT); } return EMPTY; } /** * 查询 */ protected MappedStatement addSelectMappedStatementForTable(Class mapperClass, String id, SqlSource sqlSource, TableInfo table) { String resultMap = table.getResultMap(); if (null != resultMap) { /* 返回 resultMap 映射结果集 */ return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, resultMap, null, NoKeyGenerator.INSTANCE, null, null); } else { /* 普通查询 */ return addSelectMappedStatementForOther(mapperClass, id, sqlSource, table.getEntityType()); } } /** * 查询 * * @since 3.5.0 */ protected MappedStatement addSelectMappedStatementForTable(Class mapperClass, SqlSource sqlSource, TableInfo table) { return addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, table); } /** * 查询 */ protected MappedStatement addSelectMappedStatementForOther(Class mapperClass, String id, SqlSource sqlSource, Class resultType) { return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, null, resultType, NoKeyGenerator.INSTANCE, null, null); } /** * 查询 * * @since 3.5.0 */ protected MappedStatement addSelectMappedStatementForOther(Class mapperClass, SqlSource sqlSource, Class resultType) { return addSelectMappedStatementForOther(mapperClass, this.methodName, sqlSource, resultType); } /** * 插入 */ protected MappedStatement addInsertMappedStatement(Class mapperClass, Class parameterType, String id, SqlSource sqlSource, KeyGenerator keyGenerator, String keyProperty, String keyColumn) { return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, parameterType, null, Integer.class, keyGenerator, keyProperty, keyColumn); } /** * 插入 * * @since 3.5.0 */ protected MappedStatement addInsertMappedStatement(Class mapperClass, Class parameterType, SqlSource sqlSource, KeyGenerator keyGenerator, String keyProperty, String keyColumn) { return addInsertMappedStatement(mapperClass, parameterType, this.methodName, sqlSource, keyGenerator, keyProperty, keyColumn); } /** * 删除 */ protected MappedStatement addDeleteMappedStatement(Class mapperClass, String id, SqlSource sqlSource) { return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null, null, Integer.class, NoKeyGenerator.INSTANCE, null, null); } /** * @since 3.5.0 */ protected MappedStatement addDeleteMappedStatement(Class mapperClass, SqlSource sqlSource) { return addDeleteMappedStatement(mapperClass, this.methodName, sqlSource); } /** * 更新 */ protected MappedStatement addUpdateMappedStatement(Class mapperClass, Class parameterType, String id, SqlSource sqlSource) { return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.UPDATE, parameterType, null, Integer.class, NoKeyGenerator.INSTANCE, null, null); } /** * 更新 * * @since 3.5.0 */ protected MappedStatement addUpdateMappedStatement(Class mapperClass, Class parameterType, SqlSource sqlSource) { return addUpdateMappedStatement(mapperClass, parameterType, this.methodName, sqlSource); } /** * 添加 MappedStatement 到 Mybatis 容器 */ protected MappedStatement addMappedStatement(Class mapperClass, String id, SqlSource sqlSource, SqlCommandType sqlCommandType, Class parameterType, String resultMap, Class resultType, KeyGenerator keyGenerator, String keyProperty, String keyColumn) { String statementName = mapperClass.getName() + DOT + id; if (hasMappedStatement(statementName)) { logger.warn(LEFT_SQ_BRACKET + statementName + "] Has been loaded by XML or SqlProvider or Mybatis's Annotation, so ignoring this injection for [" + getClass() + RIGHT_SQ_BRACKET); return null; } /* 缓存逻辑处理 */ boolean isSelect = sqlCommandType == SqlCommandType.SELECT; return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType, null, null, null, parameterType, resultMap, resultType, null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn, configuration.getDatabaseId(), languageDriver, null); } /** * @since 3.5.0 */ protected MappedStatement addMappedStatement(Class mapperClass, SqlSource sqlSource, SqlCommandType sqlCommandType, Class parameterType, String resultMap, Class resultType, KeyGenerator keyGenerator, String keyProperty, String keyColumn) { return addMappedStatement(mapperClass, this.methodName, sqlSource, sqlCommandType, parameterType, resultMap, resultType, keyGenerator, keyProperty, keyColumn); } /** * 注入自定义 MappedStatement * * @param mapperClass mapper 接口 * @param modelClass mapper 泛型 * @param tableInfo 数据库表反射信息 * @return MappedStatement */ public abstract MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo); /** * @param configuration 配置对象 * @param script (统一去除空白行) * @param parameterType 参数类型 * @return SqlSource * @since 3.5.3.2 */ public SqlSource createSqlSource(Configuration configuration, String script, Class parameterType) { return languageDriver.createSqlSource(configuration, SqlSourceBuilder.removeExtraWhitespaces(script), parameterType); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/AbstractSqlInjector.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector; import com.baomidou.mybatisplus.core.mapper.Mapper; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.session.Configuration; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * SQL 自动注入器 * * @author hubin * @since 2018-04-07 */ public abstract class AbstractSqlInjector implements ISqlInjector { protected final Log logger = LogFactory.getLog(this.getClass()); @Override public void inspectInject(MapperBuilderAssistant builderAssistant, Class mapperClass) { Class modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0); if (modelClass != null) { String className = mapperClass.toString(); Set mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration()); if (!mapperRegistryCache.contains(className)) { TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass); List methodList = this.getMethodList(mapperClass, tableInfo); // 兼容旧代码 if (CollectionUtils.isEmpty(methodList)) { methodList = this.getMethodList(builderAssistant.getConfiguration(), mapperClass, tableInfo); } if (CollectionUtils.isNotEmpty(methodList)) { // 循环注入自定义方法 methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo)); } else { logger.debug(className + ", No effective injection method was found."); } mapperRegistryCache.add(className); } } } /** *

    * 获取 注入的方法 *

    * * @param mapperClass 当前mapper * @param tableInfo 表信息 * @return 注入的方法集合 * @since 3.1.2 add mapperClass * @deprecated 3.5.6 {@link #getMethodList(Configuration, Class, TableInfo)} */ @Deprecated public List getMethodList(Class mapperClass, TableInfo tableInfo) { return getMethodList(tableInfo.getConfiguration(), mapperClass, tableInfo); } /** * 获取注入的方法 * * @param configuration 配置对象 * @param mapperClass 当前mapper * @param tableInfo 表信息 * @return 注入方法集合 * @since 3.5.6 */ public List getMethodList(Configuration configuration, Class mapperClass, TableInfo tableInfo) { return new ArrayList<>(); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/DefaultSqlInjector.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.core.injector.methods.*; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import org.apache.ibatis.session.Configuration; import java.util.List; import java.util.stream.Stream; import static java.util.stream.Collectors.toList; /** * SQL 默认注入器 * * @author hubin * @since 2018-04-10 */ public class DefaultSqlInjector extends AbstractSqlInjector { @Override public List getMethodList(Configuration configuration, Class mapperClass, TableInfo tableInfo) { GlobalConfig.DbConfig dbConfig = GlobalConfigUtils.getDbConfig(configuration); Stream.Builder builder = Stream.builder() .add(new Insert(dbConfig.isInsertIgnoreAutoIncrementColumn())) .add(new Delete()) .add(new Update()) .add(new SelectCount()) .add(new SelectMaps()) .add(new SelectObjs()) .add(new SelectList()); if (tableInfo.havePK()) { builder.add(new DeleteById()) .add(new DeleteByIds()) .add(new UpdateById()) .add(new SelectById()) .add(new SelectByIds()); } else { logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.", tableInfo.getEntityType())); } return builder.build().collect(toList()); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/ISqlInjector.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector; import org.apache.ibatis.builder.MapperBuilderAssistant; /** * SQL 自动注入器接口 * * @author hubin * @since 2016-07-24 */ public interface ISqlInjector { /** * 检查SQL是否注入(已经注入过不再注入) * * @param builderAssistant mapper 信息 * @param mapperClass mapper 接口的 class 对象 */ void inspectInject(MapperBuilderAssistant builderAssistant, Class mapperClass); } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/SqlRunnerInjector.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector; import com.baomidou.mybatisplus.core.assist.ISqlRunner; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ResultMap; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.scripting.LanguageDriver; import org.apache.ibatis.session.Configuration; import java.util.ArrayList; import java.util.Map; /** * SqlRunner 注入器 * * @author hubin * @since 2018-04-08 */ public class SqlRunnerInjector { private static final Log logger = LogFactory.getLog(SqlRunnerInjector.class); protected Configuration configuration; protected LanguageDriver languageDriver; /** * 注入动态执行脚本 * * @since 3.5.12 */ public static final String SQL_SCRIPT = ""; public void inject(Configuration configuration) { this.configuration = configuration; this.languageDriver = configuration.getDefaultScriptingLanguageInstance(); this.initSelectList(); this.initSelectObjs(); this.initInsert(); this.initUpdate(); this.initDelete(); this.initCount(); } /** * 是否已经存在MappedStatement * * @param mappedStatement mappedStatement * @return 是否存在 */ private boolean hasMappedStatement(String mappedStatement) { return configuration.hasStatement(mappedStatement, false); } /** * 创建查询MappedStatement * * @param mappedStatement mappedStatement * @param sqlSource 执行的sqlSource * @param resultType 返回的结果类型 */ private void createSelectMappedStatement(String mappedStatement, SqlSource sqlSource, final Class resultType) { MappedStatement ms = new MappedStatement.Builder(configuration, mappedStatement, sqlSource, SqlCommandType.SELECT) .resultMaps(new ArrayList() { { add(new ResultMap.Builder(configuration, "defaultResultMap", resultType, new ArrayList<>(0)) .build()); } }).build(); // 缓存 configuration.addMappedStatement(ms); } /** * 创建一个MappedStatement * * @param mappedStatement mappedStatement * @param sqlSource 执行的sqlSource * @param sqlCommandType 执行的sqlCommandType */ private void createUpdateMappedStatement(String mappedStatement, SqlSource sqlSource, SqlCommandType sqlCommandType) { MappedStatement ms = new MappedStatement.Builder(configuration, mappedStatement, sqlSource, sqlCommandType).resultMaps( new ArrayList() { { add(new ResultMap.Builder(configuration, "defaultResultMap", int.class, new ArrayList<>(0)) .build()); } }).build(); // 缓存 configuration.addMappedStatement(ms); } /** * initSelectList */ private void initSelectList() { if (hasMappedStatement(ISqlRunner.SELECT_LIST)) { logger.warn("MappedStatement 'SqlRunner.SelectList' Already Exists"); return; } SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class); createSelectMappedStatement(ISqlRunner.SELECT_LIST, sqlSource, Map.class); } /** * initSelectObjs */ private void initSelectObjs() { if (hasMappedStatement(ISqlRunner.SELECT_OBJS)) { logger.warn("MappedStatement 'SqlRunner.SelectObjs' Already Exists"); return; } SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Object.class); createSelectMappedStatement(ISqlRunner.SELECT_OBJS, sqlSource, Object.class); } /** * initCount */ private void initCount() { if (hasMappedStatement(ISqlRunner.COUNT)) { logger.warn("MappedStatement 'SqlRunner.Count' Already Exists"); return; } SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class); createSelectMappedStatement(ISqlRunner.COUNT, sqlSource, Long.class); } /** * initInsert */ private void initInsert() { if (hasMappedStatement(ISqlRunner.INSERT)) { logger.warn("MappedStatement 'SqlRunner.Insert' Already Exists"); return; } SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class); createUpdateMappedStatement(ISqlRunner.INSERT, sqlSource, SqlCommandType.INSERT); } /** * initUpdate */ private void initUpdate() { if (hasMappedStatement(ISqlRunner.UPDATE)) { logger.warn("MappedStatement 'SqlRunner.Update' Already Exists"); return; } SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class); createUpdateMappedStatement(ISqlRunner.UPDATE, sqlSource, SqlCommandType.UPDATE); } /** * initDelete */ private void initDelete() { if (hasMappedStatement(ISqlRunner.DELETE)) { logger.warn("MappedStatement 'SqlRunner.Delete' Already Exists"); return; } SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class); createUpdateMappedStatement(ISqlRunner.DELETE, sqlSource, SqlCommandType.DELETE); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/Delete.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 根据 entity 条件删除记录 * * @author hubin * @since 2018-04-06 */ public class Delete extends AbstractMethod { public Delete() { this(SqlMethod.DELETE.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public Delete(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { if (tableInfo.isWithLogicDelete()) { /* * 逻辑删除 */ String sql = SqlMethod.LOGIC_DELETE.format(tableInfo.getTableName(), sqlLogicSet(tableInfo), sqlWhereEntityWrapper(true, tableInfo), sqlComment()); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource); } // 真实删除 String sql = SqlMethod.DELETE.format(tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlComment()); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addDeleteMappedStatement(mapperClass, methodName, sqlSource); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/DeleteBatchByIds.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; /** * 根据 ID 集合删除 * * @author hubin * @since 2018-04-06 * @deprecated 3.5.7 {@link DeleteByIds} */ @Deprecated public class DeleteBatchByIds extends DeleteByIds { } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/DeleteById.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import java.util.List; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; /** * 根据 ID 删除 * * @author hubin * @since 2018-04-06 */ public class DeleteById extends AbstractMethod { public DeleteById() { this(SqlMethod.DELETE_BY_ID.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public DeleteById(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql; if (tableInfo.isWithLogicDelete()) { List fieldInfos = tableInfo.getFieldList().stream() .filter(TableFieldInfo::isWithUpdateFill) .filter(f -> !f.isLogicDelete()) .collect(toList()); if (CollectionUtils.isNotEmpty(fieldInfos)) { String sqlSet = "SET " + SqlScriptUtils.convertIf(fieldInfos.stream() .map(i -> i.getSqlSet(EMPTY)).collect(joining(EMPTY)), "@org.apache.ibatis.reflection.SystemMetaObject@forObject(_parameter).findProperty('" + tableInfo.getKeyProperty() + "', false) != null", true) + tableInfo.getLogicDeleteSql(false, false); sql = SqlMethod.LOGIC_DELETE_BY_ID.format(tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true)); } else { sql = SqlMethod.LOGIC_DELETE_BY_ID.format(tableInfo.getTableName(), sqlLogicSet(tableInfo), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true)); } SqlSource sqlSource = super.createSqlSource(configuration, sql, Object.class); return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource); } else { sql = SqlMethod.DELETE_BY_ID.format(tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty()); return this.addDeleteMappedStatement(mapperClass, methodName, super.createSqlSource(configuration, sql, Object.class)); } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/DeleteByIds.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import java.util.List; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; /** * 根据 ID 集合删除 * * @author nieqiurong * @since 3.5.7 */ public class DeleteByIds extends AbstractMethod { public DeleteByIds() { this(SqlMethod.DELETE_BY_IDS.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public DeleteByIds(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql; if (tableInfo.isWithLogicDelete()) { sql = logicDeleteScript(tableInfo, SqlMethod.LOGIC_DELETE_BY_IDS); SqlSource sqlSource = super.createSqlSource(configuration, sql, Object.class); return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource); } else { sql = SqlMethod.DELETE_BY_IDS.format(tableInfo.getTableName(), tableInfo.getKeyColumn(), getConvertForeachScript(tableInfo)); SqlSource sqlSource = super.createSqlSource(configuration, sql, Object.class); return this.addDeleteMappedStatement(mapperClass, methodName, sqlSource); } } protected String getConvertForeachScript(TableInfo tableInfo) { return SqlScriptUtils.convertForeach( SqlScriptUtils.convertChoose("item!=null and @org.apache.ibatis.reflection.SystemMetaObject@forObject(item).findProperty('" + tableInfo.getKeyProperty() + "', false) != null", "#{item." + tableInfo.getKeyProperty() + "}", "#{item}"), COLL, null, "item", COMMA); } /** * @param tableInfo 表信息 * @return 逻辑删除脚本 * @since 3.5.0 */ public String logicDeleteScript(TableInfo tableInfo, SqlMethod sqlMethod) { List fieldInfos = tableInfo.getFieldList().stream() .filter(TableFieldInfo::isWithUpdateFill) .filter(f -> !f.isLogicDelete()) .collect(toList()); String sqlSet = "SET "; if (CollectionUtils.isNotEmpty(fieldInfos)) { sqlSet += SqlScriptUtils.convertIf(fieldInfos.stream() .map(i -> i.getSqlSet(Constants.MP_FILL_ET + StringPool.DOT)).collect(joining(EMPTY)), String.format("%s != null", Constants.MP_FILL_ET), true); } sqlSet += StringPool.EMPTY + tableInfo.getLogicDeleteSql(false, false); return sqlMethod.format(tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(), getConvertForeachScript(tableInfo), tableInfo.getLogicDeleteSql(true, true)); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/DeleteByMap.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import java.util.Map; /** * 根据columnMap 条件删除记录 * * @author hubin * @since 2018-04-06 */ @Deprecated public class DeleteByMap extends AbstractMethod { public DeleteByMap() { this(SqlMethod.DELETE_BY_MAP.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public DeleteByMap(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql; if (tableInfo.isWithLogicDelete()) { sql = SqlMethod.LOGIC_DELETE_BY_MAP.format(tableInfo.getTableName(), sqlLogicSet(tableInfo), sqlWhereByMap(tableInfo)); SqlSource sqlSource = super.createSqlSource(configuration, sql, Map.class); return addUpdateMappedStatement(mapperClass, Map.class, methodName, sqlSource); } else { sql = SqlMethod.DELETE_BY_MAP.format(tableInfo.getTableName(), this.sqlWhereByMap(tableInfo)); SqlSource sqlSource = super.createSqlSource(configuration, sql, Map.class); return this.addDeleteMappedStatement(mapperClass, methodName, sqlSource); } } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/Insert.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils; import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; import org.apache.ibatis.executor.keygen.KeyGenerator; import org.apache.ibatis.executor.keygen.NoKeyGenerator; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 插入一条数据(选择字段插入) * * @author hubin * @since 2018-04-06 */ public class Insert extends AbstractMethod { /** * 自增主键字段是否忽略 * * @since 3.5.4 */ private boolean ignoreAutoIncrementColumn; public Insert() { this(SqlMethod.INSERT_ONE.getMethod()); } /** * @param ignoreAutoIncrementColumn 是否忽略自增长主键字段 * @since 3.5.4 */ public Insert(boolean ignoreAutoIncrementColumn) { this(SqlMethod.INSERT_ONE.getMethod()); this.ignoreAutoIncrementColumn = ignoreAutoIncrementColumn; } /** * @param name 方法名 * @since 3.5.0 */ public Insert(String name) { super(name); } /** * @param name 方法名 * @param ignoreAutoIncrementColumn 是否忽略自增长主键字段 * @since 3.5.4 */ public Insert(String name, boolean ignoreAutoIncrementColumn) { super(name); this.ignoreAutoIncrementColumn = ignoreAutoIncrementColumn; } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE; String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null, ignoreAutoIncrementColumn), LEFT_BRACKET, RIGHT_BRACKET, null, COMMA); String valuesScript = LEFT_BRACKET + NEWLINE + SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null, ignoreAutoIncrementColumn), null, null, null, COMMA) + NEWLINE + RIGHT_BRACKET; String keyProperty = null; String keyColumn = null; // 表包含主键处理逻辑,如果不包含主键当普通字段处理 if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) { if (tableInfo.getIdType() == IdType.AUTO) { /* 自增主键 */ keyGenerator = Jdbc3KeyGenerator.INSTANCE; keyProperty = tableInfo.getKeyProperty(); // 去除转义符 keyColumn = SqlInjectionUtils.removeEscapeCharacter(tableInfo.getKeyColumn()); } else if (null != tableInfo.getKeySequence()) { keyGenerator = TableInfoHelper.genKeyGenerator(methodName, tableInfo, builderAssistant); keyProperty = tableInfo.getKeyProperty(); keyColumn = tableInfo.getKeyColumn(); } } String sql = SqlMethod.INSERT_ONE.format(tableInfo.getTableName(), columnScript, valuesScript); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectBatchByIds.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; /** * 根据ID集合,批量查询数据 * * @author hubin * @since 2018-04-06 * @deprecated 3.5.8 {@link SelectByIds} */ @Deprecated public class SelectBatchByIds extends SelectByIds { } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectById.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 根据ID 查询一条数据 * * @author hubin * @since 2018-04-06 */ public class SelectById extends AbstractMethod { public SelectById() { this(SqlMethod.SELECT_BY_ID.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectById(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { SqlSource sqlSource = super.createSqlSource(configuration, SqlMethod.SELECT_BY_ID.format( sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true)), Object.class); return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectByIds.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 根据ID集合,批量查询数据 * * @author hubin * @since 2018-04-06 */ public class SelectByIds extends AbstractMethod { public SelectByIds() { this(SqlMethod.SELECT_BY_IDS.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectByIds(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { SqlSource sqlSource = super.createSqlSource(configuration, SqlMethod.SELECT_BY_IDS.format( sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), SqlScriptUtils.convertForeach("#{item}", COLL, null, "item", COMMA), tableInfo.getLogicDeleteSql(true, true)), Object.class); return addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectByMap.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import java.util.Map; /** * 根据columnMap 查询一条数据 * * @author hubin * @since 2018-04-06 */ @Deprecated public class SelectByMap extends AbstractMethod { public SelectByMap() { this(SqlMethod.SELECT_BY_MAP.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectByMap(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql = SqlMethod.SELECT_BY_MAP.format(sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), sqlWhereByMap(tableInfo)); SqlSource sqlSource = super.createSqlSource(configuration, sql, Map.class); return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectCount.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 查询满足条件总记录数 * * @author hubin * @since 2018-04-08 */ public class SelectCount extends AbstractMethod { public SelectCount() { this(SqlMethod.SELECT_COUNT.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectCount(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql = SqlMethod.SELECT_COUNT.format(sqlFirst(), sqlCount(), tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlComment()); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addSelectMappedStatementForOther(mapperClass, methodName, sqlSource, Long.class); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectList.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import org.apache.ibatis.builder.annotation.ProviderContext; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 查询满足条件所有数据 * * @author hubin * @since 2018-04-06 */ public class SelectList extends AbstractMethod { public SelectList() { this(SqlMethod.SELECT_LIST.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectList(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { SqlSource sqlSource = super.createSqlSource(configuration, getSql(tableInfo), modelClass); return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo); } protected String getSql(TableInfo tableInfo) { return SqlMethod.SELECT_LIST.format(sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment()); } /** * 游标查询方法 */ public String selectWithCursor(ProviderContext context) { return TableInfoHelper.getSqlScript(context, this::getSql); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectMaps.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import java.util.Map; /** * 查询满足条件所有数据 * * @author hubin * @since 2018-04-06 */ public class SelectMaps extends AbstractMethod { public SelectMaps() { this(SqlMethod.SELECT_MAPS.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectMaps(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql = SqlMethod.SELECT_MAPS.format(sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo),sqlOrderBy(tableInfo), sqlComment()); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addSelectMappedStatementForOther(mapperClass, methodName, sqlSource, Map.class); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectMapsPage.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import java.util.Map; /** * 查询满足条件所有数据(并翻页) * * @author hubin * @deprecated 3.5.3.2 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#selectMaps(com.baomidou.mybatisplus.core.metadata.IPage, com.baomidou.mybatisplus.core.conditions.Wrapper)} * @since 2018-04-06 */ @Deprecated public class SelectMapsPage extends AbstractMethod { public SelectMapsPage() { this(SqlMethod.SELECT_MAPS_PAGE.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectMapsPage(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql = SqlMethod.SELECT_MAPS_PAGE.format(sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment()); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addSelectMappedStatementForOther(mapperClass, methodName, sqlSource, Map.class); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectObjs.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 查询满足条件所有数据 * * @author hubin * @since 2018-04-06 */ public class SelectObjs extends AbstractMethod { public SelectObjs() { this(SqlMethod.SELECT_OBJS.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectObjs(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql = SqlMethod.SELECT_OBJS.format(sqlFirst(), sqlSelectObjsColumns(tableInfo), tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo),sqlOrderBy(tableInfo), sqlComment()); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addSelectMappedStatementForOther(mapperClass, methodName, sqlSource, Object.class); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectOne.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 查询满足条件一条数据,为了精简注入方法,该方法采用 list.get(0) 处理后续不再使用 * * @author hubin * @since 2018-04-06 */ @Deprecated public class SelectOne extends AbstractMethod { public SelectOne() { this(SqlMethod.SELECT_ONE.getMethod()); } /** * @param name 方法名 * @since 3.5.0 */ public SelectOne(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { SqlSource sqlSource = super.createSqlSource(configuration, SqlMethod.SELECT_ONE.format(sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlComment()), modelClass); return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectPage.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 查询满足条件所有数据(并翻页) * * @author hubin * @since 2018-04-06 * @deprecated 3.5.3.2 {@link BaseMapper#selectList(IPage, Wrapper)} */ @Deprecated public class SelectPage extends AbstractMethod { public SelectPage() { this(SqlMethod.SELECT_PAGE.getMethod()); } /** * @since 3.5.0 * @param name 方法名 */ public SelectPage(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql = SqlMethod.SELECT_PAGE.format(sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment()); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/Update.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 根据 whereEntity 条件,更新记录 * * @author hubin * @since 2018-04-06 */ public class Update extends AbstractMethod { public Update() { this(SqlMethod.UPDATE.getMethod()); } /** * @since 3.5.0 * @param name 方法名 */ public Update(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql = SqlMethod.UPDATE.format(tableInfo.getTableName(), sqlSet(true, true, tableInfo, true, ENTITY, ENTITY_DOT), sqlWhereEntityWrapper(true, tableInfo), sqlComment()); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return this.addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/UpdateById.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.injector.methods; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; /** * 根据 ID 更新有值字段 * * @author hubin * @since 2018-04-06 */ public class UpdateById extends AbstractMethod { public UpdateById() { this(SqlMethod.UPDATE_BY_ID.getMethod()); } /** * @since 3.5.0 * @param name 方法名 */ public UpdateById(String name) { super(name); } @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { final String additional = optlockVersion(tableInfo) + tableInfo.getLogicDeleteSql(true, true); String sql = SqlMethod.UPDATE_BY_ID.format(tableInfo.getTableName(), sqlSet(tableInfo.isWithLogicDelete(), false, tableInfo, false, ENTITY, ENTITY_DOT), tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), additional); SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass); return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource); } } ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 注入 SQL 操作方法相关类 * * @author hubin * @since 2018-04-06 */ package com.baomidou.mybatisplus.core.injector.methods; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/package-info.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 注入核心代码 * * @author hubin * @since 2018-04-06 */ package com.baomidou.mybatisplus.core.injector; ================================================ FILE: mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/BaseMapper.java ================================================ /* * Copyright (c) 2011-2025, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.core.mapper; import com.baomidou.mybatisplus.core.batch.BatchSqlSession; import com.baomidou.mybatisplus.core.batch.MybatisBatch; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.methods.SelectList; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.MapperProxyMetadata; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.*; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.SelectProvider; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.exceptions.TooManyResultsException; import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.ognl.OgnlOps; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import java.io.IOException; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.BiPredicate; /* :` .:, :::,,. :: `:::::: ::` `,:,` .:` `:: `::::::::.:` `:';,` ::::, .:::` `@++++++++: `` :::` @+++++++++++# :::, #++++++++++++++` ,: `::::::;'##++++++++++ .@#@;` ::::::::::::::::::::; #@####@, :::::::::::::::+#;::. @@######+@:::::::::::::. #@:; , @@########':::::::::::: .#''':` ;##@@@+:##########@::::::::::: @#;.,:. #@@@######++++#####'::::::::: .##+,:#` @@@@@#####+++++'#####+::::::::` ,`::@#:` `@@@@#####++++++'#####+#':::::::::::@. @@@@######+++++''#######+##';::::;':,` @@@@#####+++++'''#######++++++++++` #@@#####++++++''########++++++++' `#@######+++++''+########+++++++; `@@#####+++++''##########++++++, @@######+++++'##########+++++#` @@@@#####+++++############++++; ;#@@@@@####++++##############+++, @@@@@@@@@@@###@###############++' @#@@@@@@@@@@@@###################+: `@#@@@@@@@@@@@@@@###################'` :@#@@@@@@@@@@@@@@@@@##################, ,@@@@@@@@@@@@@@@@@@@@################; ,#@@@@@@@@@@@@@@@@@@@##############+` .#@@@@@@@@@@@@@@@@@@#############@, @@@@@@@@@@@@@@@@@@@###########@, :#@@@@@@@@@@@@@@@@##########@, `##@@@@@@@@@@@@@@@########+, `+@@@@@@@@@@@@@@@#####@:` `:@@@@@@@@@@@@@@##@;. `,'@@@@##@@@+;,` ``...`` _ _ /_ _ _/_. ____ / _ / / //_//_//_|/ /_\ /_///_/_\ Talk is cheap. Show me the code. _/ / */ /** * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能 *

    这个 Mapper 支持 id 泛型

    * * @author hubin * @since 2016-01-23 */ public interface BaseMapper extends Mapper { /** * 插入一条记录 * * @param entity 实体对象 */ int insert(T entity); /** * 根据 ID 删除 * * @param id 主键ID */ default int deleteById(Serializable id) { return deleteById(id, true); } /** * 根据 ID 删除 * * @param useFill 是否填充 * @param obj 主键ID或实体 * @since 3.5.7 */ default int deleteById(Object obj, boolean useFill) { Class entityClass = ReflectionKit.getSuperClassGenericType(getClass(), BaseMapper.class, 0); Assert.notNull(entityClass, "entityClass must not be null"); if (!entityClass.isAssignableFrom(obj.getClass()) && useFill) { TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass); Assert.notNull(tableInfo, "Can not get TableInfo for entity " + entityClass); String keyProperty = tableInfo.getKeyProperty(); Assert.notEmpty(keyProperty, "The current table has no primary key."); if (tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill()) { T instance = tableInfo.newInstance(); tableInfo.setPropertyValue(instance, keyProperty, OgnlOps.convertValue(obj, tableInfo.getKeyType())); return this.deleteById(instance); } } MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this); SqlSession sqlSession = mapperProxyMetadata.getSqlSession(); return sqlSession.delete(mapperProxyMetadata.getMapperInterface().getName() + Constants.DOT + SqlMethod.DELETE_BY_ID.getMethod(), obj); } /** * 根据实体(ID)删除 * * @param entity 实体对象 * @since 3.4.4 */ int deleteById(T entity); /** * 根据 columnMap 条件,删除记录 * * @param columnMap 表字段 map 对象 */ default int deleteByMap(Map columnMap) { return this.delete(Wrappers.query().allEq(columnMap)); } /** * 根据 entity 条件,删除记录 * * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) */ int delete(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 删除(根据ID或实体 批量删除) * * @param idList 主键ID列表或实体列表(不能为 null 以及 empty) * @deprecated 3.5.7 {@link #deleteByIds(Collection)} */ @Deprecated default int deleteBatchIds(@Param(Constants.COLL) Collection idList) { return deleteByIds(idList); } /** * 删除(根据ID或实体 批量删除) * * @param idList 主键ID列表或实体列表(不能为 null 以及 empty) * @since 3.5.7 */ default int deleteByIds(@Param(Constants.COLL) Collection idList) { return deleteByIds(idList, true); } /** * 删除(根据ID或实体 批量删除) *

    * 普通删除: DELETE FROM h2user WHERE id IN ( ? , ? ) *

    *

    * 逻辑删除: UPDATE h2user SET deleted=1 WHERE id IN ( ? , ? ) AND deleted=0 *

    *

    * 逻辑删除(填充): UPDATE h2user SET delete_user = 'xxx', deleted=1 WHERE id IN ( ? , ? ) AND deleted=0 *