xbatis之springboot启动器
xbatis代码生成器core
xbatis之 solon 容器 启动插件
多数据库路由器 - 又名为多数据源(可以独立于xbatis使用)
基于RuoYi,替换mybatis为xbatis,更强大、更好用、更方便!!!
Official site: https://xbatis.cn
DeepWiki documentation: https://deepwiki.xbatis.cn
Knowledge pack for AI agents working with the Xbatis framework. It distills the official Chinese materials into a self-contained explanation so that automated assistants can understand the framework capabilities, common patterns, and key APIs.
LEFT JOIN / ORDER BY, smarter COUNT).RETURNING support, batch insert/update chains, native function wrappers, SQL templates.BasicMapper to cover every entity.dbAdapt, dynamic datasource routing.JOIN, COUNT, ORDER BY, and ships with a built-in pager.| Module | Typical package | Core types | Purpose |
|---|---|---|---|
| Core Mapper | cn.xbatis.core.mybatis.mapper |
MybatisMapper<T>, BasicMapper
|
Provides base CRUD plus single-mapper capabilities |
| Fluent DSL | cn.xbatis.core.chain |
QueryChain, InsertChain, UpdateChain, DeleteChain
|
Build complex SQL, batch ops, returning clauses |
| Global config | cn.xbatis.core.config |
XbatisGlobalConfig |
Unified naming rules, interceptors, dynamic values, paging |
| Annotation suite | cn.xbatis.db.annotations |
@Table, @TableId, @TableField, @LogicDelete,@LogicDeleteTime, @TenantId, @Version, @Condition, @Fetch, etc. |
Entity mapping, injection rules, condition objects, result shaping |
| Database functions | db.sql.api.impl.cmd |
Methods |
Cross-database functions, SQL templates, fluent wrappers |
| Multi-tenant | cn.xbatis.core.tenant |
TenantContext, TenantId
|
Register and propagate tenant IDs globally |
| Dynamic datasource | cn.xbatis.datasource.routing |
@DS, JdbcConfigDecryptor
|
Runtime datasource switching, encrypted configs, grouped routing |
| Logical delete | cn.xbatis.core.logic |
LogicDeleteSwitch, LogicDeleteUtil
|
Toggle logical delete, easy overrides |
| Dynamic values | cn.xbatis.core.dynamic |
XbatisGlobalConfig#setDynamicValue |
Define tokens like {NOW}, {TODAY}
|
| Code generation | cn.xbatis.codegen |
GeneratorConfig and sub modules |
One-stop generation for entities, mapper, service skeletons, etc. |
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.xbatis</groupId>
<artifactId>xbatis-bom</artifactId>
<version>1.9.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.xbatis</groupId>
<artifactId>xbatis-spring-boot3-starter</artifactId>
</dependency>
</dependencies>
spring:
datasource:
url: jdbc:mysql://localhost:3306/dbName
username: dbusername
password: dbpassword
@SpringBootApplication
@MapperScan("com.xx.xxx.mapper")
public class XbatisApplication {
public static void main(String[] args) {
SpringApplication.run(XbatisApplication.class, args);
}
}
xbatis-spring-boot-starter):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.xbatis</groupId>
<artifactId>xbatis-spring-boot-parent</artifactId>
<version>1.9.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.xbatis</groupId>
<artifactId>xbatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
application.yml and @MapperScan.<!-- Order matters: register Xbatis plugin before mybatis-solon-plugin -->
<dependency>
<groupId>cn.xbatis</groupId>
<artifactId>xbatis-solon-plugin</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>mybatis-solon-plugin</artifactId>
<version>${mybatis.solon.version}</version>
</dependency>
# solon.yml
ds:
schema: demo # recommend matching the database name
jdbcUrl: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
driverClassName: com.mysql.cj.jdbc.Driver
username: root
password: 123456
# Rule: mybatis.<datasource bean name>
mybatis.master:
mappers:
- "com.demo.mapper" # package scan
- "classpath:mapper/**/*.xml" # XML scan
@Configuration
public class MybatisConfig {
@Bean(name = "master", typed = true)
public DataSource dataSource(@Inject("${ds}") HikariDataSource ds) {
return ds;
}
}
@Controller
public class DemoController {
@Db // omit name for single datasource; specify @Db("master") for multi-datasource
UserMapper mapper;
@Get
@Mapping("/test")
public List<User> test() {
return QueryChain.of(mapper)
.like(User::getName, "abc")
.list();
}
}
mybatis-solon-plugin; chaining DSL, sharding, and multi-tenancy remain available.
Entity, mapper, and service examples:@Data
@Table
public class SysUser {
@TableId
private Integer id;
private String userName;
private String password;
private Integer roleId;
private LocalDateTime createTime;
}
public interface SysUserMapper extends MybatisMapper<SysUser> {}
@Service
public class TestService {
@Autowired
private SysUserMapper sysUserMapper;
public Pager<SysUser> demo() {
return QueryChain.of(sysUserMapper)
.eq(SysUser::getId, 1)
.like(SysUser::getUserName, "xxx")
.paging(Pager.of(1, 10));
}
}
> Pager resides in `cn.xbatis.core.mybatis.mapper.context.Pager` and implements `cn.xbatis.page.IPager`. Common usage:
> - Static factories: `Pager.of(size)`, `Pager.of(number, size)`.
> - `setExecuteCount(boolean)`: control whether to run the count query.
> - `paging(Pager)`: pass into chain APIs and read `getResults()`, `getTotal()`, `getNumber()`, `getSize()`, `getTotalPage()`, etc.
> - `PagerGetSetUtil` enables dynamic read/write of extension fields by `PagerField`.
XbatisGlobalConfig (cn.xbatis.core.config.XbatisGlobalConfig)ConfigurationCustomizer or @PostConstruct.setSingleMapperClass(MybatisBasicMapper.class): enable single-mapper mode with BasicMapper.setTableUnderline(boolean) / setColumnUnderline(boolean): control table/column naming.setDatabaseCaseRule(DatabaseCaseRule rule) or setDatabaseCaseRule(DbType, rule): adjust casing strategy.setDynamicValue("{KEY}", (clazz, type) -> {...}) and getDynamicValue(clazz, type, key): register and use dynamic placeholders.setLogicDeleteInterceptor((entity, update) -> {...}) and setLogicDeleteSwitch(boolean): standardize logical delete metadata & toggles.addMapperMethodInterceptor(new MyInterceptor()), enableInterceptOfficialMapperMethod(): register custom mapper interceptors, optionally extending official methods.setPagingProcessor(DbType, PagingProcessor): customize pagination for each database.@Table (cn.xbatis.db.annotations.Table)| Property | Nullable | Default | Description |
|---|---|---|---|
value |
Yes | – | Table name; defaults to camelCase-to-snake-case of entity name |
schema |
Yes | – | Database schema |
columnNameRule |
Yes | columnNameRule.IGNORE |
Rule to derive column names when not specified |
databaseCaseRule |
Yes | DatabaseCaseRule.DEFAULT |
Controls casing; works with XbatisGlobalConfig.setDatabaseCaseRule(...)
|
Example:
@Table(databaseCaseRule = DatabaseCaseRule.UPPERCASE)
public class SysUser { }
@TableId (cn.xbatis.db.annotations.TableId)| Property | Nullable | Default | Description |
|---|---|---|---|
value |
Yes | IdAutoType.AUTO |
Primary key strategy: AUTO, NONE, SQL, GENERATOR
|
dbType |
Yes | – | Target database type for differentiating strategies |
sql |
Yes | – | Required when value = SQL; custom SQL for ID retrieval |
generatorName |
Yes | – | Required when value = GENERATOR; refers to registered ID generator |
Repeatable to accommodate multiple database strategies.
@TableField (cn.xbatis.db.annotations.TableField)| Property | Nullable | Default | Description |
|---|---|---|---|
value |
Yes | – | Column name; can be omitted when camelCase-to-snake-case applies |
select |
Yes | true |
Whether to participate when calling select(Entity.class)
|
insert |
Yes | true |
Whether to write during save
|
update |
Yes | true |
Does it update when updating? It will update when forced |
neverUpdate |
Yes | false |
Never update, unless specified with Updating Chain, it will only be updated, and it will not be forced to update either |
jdbcType |
Yes | – | Explicit JDBC type |
typeHandler |
Yes | – | Custom type handler |
defaultValue |
Yes | – | Insert default; supports static values and dynamic tokens like {NOW}
|
updateDefaultValue |
Yes | – | Default during updates |
defaultValueFillAlways |
Yes | false |
Always apply defaultValue on insert |
updateDefaultValueFillAlways |
Yes | false |
Always apply updateDefaultValue
|
exists |
Yes | true |
Whether the field physically exists (false for transient fields) |
@LogicDelete (cn.xbatis.db.annotations.LogicDelete)Logical delete annotation.
| Property | Nullable | Default | Description |
|---|---|---|---|
beforeValue |
Yes | – | Value before deletion; null treated as NULL
|
afterValue |
No | – | Value after deletion; supports dynamic tokens {NOW}, etc. |
Combine with XbatisGlobalConfig.setLogicDeleteInterceptor to fill deleter metadata.
@LogicDeleteTime (cn.xbatis.db.annotations.LogicDeleteTime)Field name for logic delete timestamp; supports LocalDateTime, Date, Long (ms), Integer (seconds)
@TenantId (cn.xbatis.db.annotations.TenantId)No extra properties. Marks tenant ID fields; automatically filled on save/update/delete and appended to query conditions. Register tenant suppliers through TenantContext.registerTenantGetter (cn.xbatis.core.tenant.TenantContext).
@Version (cn.xbatis.db.annotations.Version)No extra properties. Marks optimistic-lock version fields. save sets it to 0; update/delete append WHERE version = ? and successful updates increment it automatically.
@OnInsert (cn.xbatis.db.annotations.OnInsert), @OnUpdate (cn.xbatis.db.annotations.OnUpdate)| Annotation | Property | Nullable | Default | Description |
|---|---|---|---|---|
@OnInsert |
value |
Yes | – | Assign pre-insert listener implementing OnInsertListener<T>
|
@OnUpdate |
value |
Yes | – | Assign pre-update listener implementing OnUpdateListener<T>
|
Besides local listeners, register global handlers via XbatisGlobalConfig.setGlobalOnInsertListener / setGlobalOnUpdateListener.
@ResultEntity / @ResultField / @Fetch, etc. under cn.xbatis.db.annotations)@ResultEntity: declare VO ↔ entity mapping and auto-complete SELECT columns.
@ResultField:
| Property | Nullable | Default | Description |
|---|---|---|---|
value |
Yes | – | Column(s) mapped to the field; supports multi-column inputs |
jdbcType |
Yes | – | JDBC type |
typeHandler |
Yes | – | Custom type handler |
@Fetch:
| Property | Nullable | Default | Description |
|---|---|---|---|
column |
Yes | – | Matching column; alternate to property, takes precedence |
property |
No | – | Source entity property |
source |
No | – | Source entity type |
storey |
Yes | -1 |
Source hierarchy; autodetect by default |
target |
No | – | Target entity (to be fetched) |
targetProperty |
No | – | Target entity join column |
targetSelectProperty |
Yes | – | Selected columns or expressions |
middle |
No | – | Middle entity (join table) |
middleSourceProperty |
No | – | Middle entity column bound to source |
middleTargetProperty |
No | – | Middle entity column bound to target |
orderBy |
Yes | – | Order clause, e.g., "[{xx} desc]" or "field desc"
|
multiValueErrorIgnore |
Yes | false |
Ignore multi-row result when expecting 1-to-1 |
limit |
Yes | 0 |
Limit result size; 0 = unlimited |
memoryLimit |
Yes | false |
Apply limit in-memory with IN to reduce queries |
nullFillValue |
Yes | null |
Default when result is empty |
otherConditions |
Yes | – | Extra conditions, e.g., [{Type} = 2]
|
AI generation tip: prefer using @ResultEntity and the related annotations (@ResultField, @NestedResultEntity, @NestedResultEntityField, @ResultCalcField, @Fetch, @PutEnumValue, @PutValue, etc.) on VO/DTOs for direct return types, eliminating manual transformation. Do not annotate entities with these; keep @TypeHandler, @Put*** on result objects only to avoid polluting entity models.
@NestedResultEntity (cn.xbatis.db.annotations.NestedResultEntity):
| Property | Nullable | Default | Description |
|---|---|---|---|
target |
Yes | – | Target entity; defaults to @ResultEntity target |
storey |
Yes | 1 |
Storage level; differentiate layers in self-joins |
| Declaring VO fields as nested objects automatically fills nested results recursively. |
@NestedResultEntityField (cn.xbatis.db.annotations.NestedResultEntityField):
| Property | Nullable | Default | Description |
|---|---|---|---|
value |
Yes | – | Field name inside the nested entity |
Use when VO field names differ from entity fields. Works well with Lombok @FieldNameConstants. |
@ResultCalcField (cn.xbatis.db.annotations.ResultCalcField):
| Property | Nullable | Default | Description |
|---|---|---|---|
value |
No | – | Calculation SQL, e.g., count(1), sum({id})
|
target |
Yes | – | Target entity; defaults to @ResultEntity target |
storey |
Yes | 1 |
Storage level |
When select(VO.class), automatically produces aggregate columns. Use {field} placeholders for dynamic columns. |
@PutEnumValue (cn.xbatis.db.annotations.PutEnumValue):
| Property | Nullable | Default | Description |
|---|---|---|---|
source |
No | – | Source entity |
property |
No | – | Field storing enum code |
storey |
Yes | 1 |
Storage level |
target |
No | – | Enum class |
code |
No | code |
Field name representing the code |
value |
No | name |
Field name representing display value |
required |
No | false |
Throw if enum not found |
defaultValue |
Yes | – | Fallback when enum missing |
| Suitable for auto-converting status codes to labels; supports nested mappings and multi-column matching. |
@PutValue (cn.xbatis.db.annotations.PutValue):
| Property | Nullable | Default | Description |
|---|---|---|---|
source |
No | – | Source entity |
property |
No | – | Source field |
storey |
Yes | 1 |
Storage level |
factory |
No | – | Factory class providing static method |
method |
No | – | Method name inside factory |
required |
No | false |
Throw if value missing |
defaultValue |
Yes | – | Default value |
Supports injecting dynamic values based on multiple fields. Results are cached per session using factory+method+args. |
Combine these annotations: use @ResultEntity for overall mapping, @NestedResultEntity for nested structure, @ResultCalcField for aggregates, and @PutEnumValue/@PutValue for extra data—delivering near-entity auto-wiring for VOs.
@SplitTable, @SplitTableKey, TableSplitter in cn.xbatis.db.annotations)@SplitTable:
| Property | Nullable | Default | Description |
|---|---|---|---|
value |
No | – |
TableSplitter implementation used to compute actual table name |
@SplitTableKey: marks the sharding key field; supports a single column and takes sharding input at runtime.
TableSplitter interface methods:
boolean support(Class<?> type);
String split(String sourceTableName, Object splitValue);
support declares supported key types; split returns actual table names like sys_user_0 ~ sys_user_9. Queries/updates must include the shard key so Xbatis can locate the table.
@ConditionTarget (cn.xbatis.db.annotations.ConditionTarget)| Property | Nullable | Default | Description |
|---|---|---|---|
value |
No | – | Target entity; defaults to current class if omitted |
logic |
Yes | Logic.AND |
Default top-level logic; set to Logic.OR for OR combinations |
@Condition (cn.xbatis.db.annotations.Condition)| Property | Nullable | Default | Description |
|---|---|---|---|
value |
Yes | Condition.Type.EQ |
Condition type, e.g., LIKE, GT, BETWEEN, EXISTS
|
target |
Yes | – | Target entity; defaults to @ConditionTarget or current class |
property |
Yes | – | Target property name; defaults to field name |
storey |
Yes | – | Source storage level for nested objects |
likeMode |
Yes | – |
LEFT, RIGHT, BOTH, etc. |
toEndDayTime |
Yes | false |
Auto-extend to end of day for LTE or second parameter of BETWEEN
|
defaultValue |
Yes | – | Static value or dynamic key {NOW}, {TODAY}, custom entries |
@Conditions (cn.xbatis.db.annotations.Conditions)| Property | Nullable | Default | Description |
|---|---|---|---|
value |
Yes | – | Multiple @Condition elements, often for keyword multi-column search |
logic |
Yes | Logic.OR |
Logic among combined conditions |
@ConditionGroup (cn.xbatis.db.annotations.ConditionGroup)| Property | Nullable | Default | Description |
|---|---|---|---|
value |
Yes | – | Grouped fields |
logic |
Yes | Logic.AND |
Group logic; change to Logic.OR as needed |
If DTO implements ObjectConditionLifeCycle (cn.xbatis.core.sql.ObjectConditionLifeCycle), it can preprocess input in beforeBuildCondition() (e.g., derive past 7 days based on timeType) and cooperate with the annotations above for deep nesting.
MybatisMapper<T> (cn.xbatis.core.mybatis.mapper.MybatisMapper) ships with:
get, list, listAll, listByIds, cursor, exists, count, mapWithKey, page, etc., accepting lambda Where builders.save, saveOrUpdate, saveBatch, saveModel, saveModelBatch, saveBatch(list, fields), supporting entity and Model.update, updateBatch, update(model, where), update(list, forceFields), saveOrUpdate, plus forced update fields and batch CASE WHEN updates.deleteById, deleteByIds, delete(entity), delete(where), deleteAll, and DeleteChain.public interface MybatisBasicMapper extends BasicMapper {
}
BasicMapper lives in cn.xbatis.core.mybatis.mapper.BasicMapper and wraps all general CRUD.@MapperScan(basePackageClasses = MybatisBasicMapper.class,
markerInterface = BasicMapper.class)
XbatisGlobalConfig.setSingleMapperClass(MybatisBasicMapper.class); during startup to designate the default entry.@Autowired
private MybatisBasicMapper mybatisBasicMapper;
public void demo() {
// Entity CRUD
mybatisBasicMapper.save(new SysUser());
mybatisBasicMapper.deleteById(SysUser.class, 1);
// Fluent DSL: explicitly pass entity class
QueryChain.of(mybatisBasicMapper, SysUser.class)
.eq(SysUser::getId, 1)
.list();
UpdateChain.of(mybatisBasicMapper, SysUser.class)
.set(SysUser::getUserName, "basic")
.eq(SysUser::getId, 1)
.execute();
}
BasicDaoImpl (cn.xbatis.core.mvc.impl.BasicDaoImpl) to keep a classic three-layer architecture.xxx.MybatisBasicMapper and withSqlSession:
List<SysRole> roles = mybatisBasicMapper.withSqlSession(
SysRole.class,
"selectByIds",
params,
(statement, p, sqlSession) -> sqlSession.selectList(statement, p)
);
<select id="EntityName:method"> naming must align with withSqlSession calls.Xbatis save / saveBatch / InsertChain support cross-database duplicate-key strategies (ignore or update on conflict).
| Database | Ignore on conflict | Update on conflict |
|---|---|---|
| MySQL | ✅ | ✅ |
| MariaDB | ✅ | ✅ |
| Oracle | ✅ | ❌ |
| H2 | ✅ | ✅ |
| PostgreSQL | ✅ | ✅ |
| Kingbase | ✅ | ✅ |
| SQLite | ✅ | ✅ |
| openGauss | ✅ | ✅ |
onConflict lives in cn.xbatis.core.mybatis.mapper.context.strategy.SaveStrategy / SaveBatchStrategy and the chain SQL interface db.sql.api.cmd.executor.IInsert, building portable statements via IConflictAction.
MultiPk entity = new MultiPk();
entity.setId1(1);
entity.setId2(2);
entity.setName("init");
mapper.save(entity, strategy -> {
strategy.onConflict(action -> action.doNothing()); // ignore
// or update on conflict (overwrite all fields)
// strategy.onConflict(action -> action.doUpdate(update -> update.overwriteAll()));
});
List<MultiPk> list = List.of(entity1, entity2);
// mapper.save(List<T>, ...)
mapper.save(list, strategy ->
strategy.onConflict(action -> action.doUpdate(update -> update.overwrite(MultiPk::getName)))
);
// mapper.saveBatch(List<T>, ...)
mapper.saveBatch(list, strategy ->
strategy.onConflict(action -> action.doNothing())
);
overwriteAll() updates every column; overwrite(Entity::getField) targets specific columns.
InsertChain.of(sysUserMapper)
.insert(SysUser.class)
.values(Arrays.asList("basic", "123456"))
.onConflict(action -> action.doUpdate(update -> update.overwrite(SysUser::getPassword)))
.execute();
Use dbAdapt inside the strategy for database-specific tuning.
cn.xbatis.core.chain.QueryChain constructs combinations of select, from, join, where, groupBy, having, orderBy, pagination, nested conditions, and subqueries.
.forSearch(true) or eq(field, value, predicate) / eq(boolean, field, value) instead of manual if/else on WHERE clauses to keep code tidy and robust.Example:
SysUserRoleVo vo = QueryChain.of(sysUserMapper)
.select(SysUser.class, SysRole.class)
.from(SysUser.class)
.join(SysUser::getRoleId, SysRole::getId)
.eq(SysUser::getId, 1)
.like(SysUser::getUserName, "abc")
.groupBy(SysUser::getId)
.having(SysUser::getId, c -> c.count().gt(0))
.orderBy(SysUser::getId)
.returnType(SysUserRoleVo.class)
.get();
Key capabilities:
select accepts entities, VO classes, lambda fields, function wrappers, ignored columns.join supports inner, left, right; leverage @ForeignKey for automatic join conditions.andNested / orNested add parentheses; and() / or() control subsequent logic.eq, ne, gt, gte, lt, lte, between, notBetween, in, notIn, like, notLike, isNull, isNotNull, empty, notEmpty, exists, notExists. Each accepts a predicate argument for conditional inclusion (e.g., eq(SysUser::getId, id, Objects::nonNull)). Global toggles include .ignoreNullValueInCondition(true), .ignoreEmptyInCondition(true).forSearch(true) simultaneously ignores null/blank values and trims strings—ideal for search forms.returnType(...) is recommended right before terminal operations, after conditions/limits.where(queryObject) converts annotated DTO fields into conditions.dbAdapt(selector -> ...) builds DB-specific behavior.get, list, count, exists, paging(Pager), cursor, mapWithKey.cn.xbatis.core.chain.InsertChain supports INSERT ... VALUES and INSERT ... SELECT combos:
InsertChain.of(sysUserMapper)
.insert(SysUser.class)
.fields(SysUser::getUserName, SysUser::getRoleId)
.fromSelect(Query.create()
.select(SysUser2::getUserName, SysUser2::getRoleId)
.from(SysUser2.class)
)
.execute();
Add values repeatedly for batch inserts.
cn.xbatis.core.chain.UpdateChain enables dynamic set, arithmetic updates, RETURNING, nested conditions:
SysUser user = UpdateChain.of(sysUserMapper)
.update(SysUser.class)
.set(SysUser::getUserName, "new name")
.set(SysUser::getVersion, c -> c.plus(1))
.eq(SysUser::getId, 1)
.returning(SysUser.class)
.returnType(SysUser.class)
.executeAndReturning();
cn.xbatis.core.chain.DeleteChain builds delete conditions and can return affected entities:
List<SysUser> removed = DeleteChain.of(sysUserMapper)
.in(SysUser::getId, 1, 2)
.returning(SysUser.class)
.returnType(SysUser.class)
.executeAndReturningList();
@ConditionTarget.@Condition for EQ, NE, GT, LT, BETWEEN, EXISTS, LIKE, etc., specifying target property, defaults, toEndDayTime, likeMode.@Conditions maps a single field to multiple columns with AND/OR control.@ConditionGroup groups and nests logic.ObjectConditionLifeCycle for preprocessing (e.g., deriving date ranges from enums) before building conditions.Example:
@Data
@ConditionTarget(SysUser.class)
public class QueryREQ {
private Integer id;
@Condition(value = Condition.Type.LIKE)
private String userName;
@Conditions(
logic = Logic.OR,
value = {
@Condition(property = SysUser.Fields.userName, value = Condition.Type.LIKE),
@Condition(property = SysUser.Fields.password, value = Condition.Type.LIKE)
}
)
private String keyword;
}
import static db.sql.api.impl.cmd.Methods.*; for unified functions.count, sum, avg, min, max, abs, ceil, floor, rand, sign, pi, truncate, round, pow, sqrt, exp, mod, log, sin, cos, tan, charLength, concat, upper, lower, substring, currentDate, dateDiff, dateAdd, inetAton, plus MySQL-specific findInSet, md5, jsonExtract, groupConcat, etc.Methods.tpl – general SQL template with placeholders {0}, {1}.Methods.fTpl – function template, chainable with other functions.Methods.cTpl – condition template for complex WHERE fragments.Methods.cTpl(true, "...", ...) for automatic ' handling.Example:
QueryChain.of(sysUserMapper)
.select(SysUser::getRoleId, c -> Methods.tpl("count({0})+{1}", c, "1"))
.and(GetterFields.of(SysUser::getId, SysUser::getId),
cs -> Methods.cTpl("{0}+{1}={2}", cs[0], cs[1], 2));
QueryChain, UpdateChain, DeleteChain, InsertChain all support dbAdapt:QueryChain.of(sysUserMapper)
.select(SysUser::getId)
.dbAdapt((query, selector) -> selector
.when(DbType.H2, db -> query.eq(SysUser::getId, 3))
.when(DbType.MYSQL, db -> query.eq(SysUser::getId, 2))
.otherwise(db -> query.eq(SysUser::getId, 1))
)
.get();
mapper.dbAdapt(selector -> {...}) for entirely different flows.cn.xbatis:xbatis-datasource-routing.spring.ds.routing.* for multiple datasources, master-slave, replica groups, and pool properties (supports Hikari, Druid, custom).@DS("master"), @DS("slave") to switch datasource.@Transactional(propagation = Propagation.NOT_SUPPORTED) or REQUIRES_NEW when switching datasources mid-call.AopContext.currentProxy() or split the class.spring.ds.jdbc-config-decrypt=true with JdbcConfigDecryptor enables encrypted JDBC configs.spring.ds.seata=true).@TenantId; automatically fills tenant ID on write and appends conditions for query/update/delete.TenantContext.registerTenantGetter(() -> tenantId) (cn.xbatis.core.tenant.TenantContext), returning a single ID or TenantId (multi-tenant set).null from supplier to temporarily disable tenant enforcement.TenantTLUtil to set/clear tenants in filters/interceptors and return via TenantContext.@LogicDelete(beforeValue = "0", afterValue = "1") controls before/after markers and timestamp.@LogicDeleteTime controls logic delete time markersXbatisGlobalConfig.setLogicDeleteInterceptor can fill deleter info automatically.setLogicDeleteSwitch(true/false); locally via:try (LogicDeleteSwitch ignored = LogicDeleteSwitch.with(false)) {
mapper.getById(1);
}
// or
LogicDeleteUtil.execute(false, () -> mapper.getById(1));
DeleteChain performs physical delete; only mapper delete methods respect logical delete.@Version fields default to 0 on save.update / delete append WHERE version=?; updates increment version on success.@SplitTable(TableSplitterClass.class); value points to TableSplitter implementation.@SplitTableKey; only single-column sharding is supported.TableSplitter#support(Class<?>) declares supported key types; split(String, Object) returns real table name (e.g., sys_user_3).{BLANK}, {NOW} (supports LocalDateTime, LocalDate, Date, Long, Integer, String), {TODAY} (date range).XbatisGlobalConfig.setDynamicValue("{day7}", (clazz, type) -> new LocalDate[]{
LocalDate.now().minusDays(7), LocalDate.now()
});
XbatisGlobalConfig.getDynamicValue(clazz, LocalDate[].class, "{day7}").XbatisGlobalConfig.setGlobalOnInsertListener(entity -> {
// fill creator, etc.
});
XbatisGlobalConfig.setGlobalOnUpdateListener(entity -> {
// fill updater, etc.
});
<mapper namespace="xxx.MybatisBasicMapper">
<select id="SysRole:selectByIds" resultType="com.xbatis.core.test.DO.SysRole">
select * from sys_role
where id >= #{id} and id <= #{id2}
order by id asc
</select>
</mapper>
Invocation:
List<SysRole> roleList = mybatisBasicMapper.withSqlSession(
SysRole.class, "selectByIds", params,
(statement, p, sqlSession) -> sqlSession.selectList(statement, p)
);
Pager handles pagination: Pager.of(current, size), QueryChain.paging(pager) automatically executes query + count.LEFT JOIN, ORDER BY, replaces SELECT with COUNT(*) to improve speed.XbatisGlobalConfig.setPagingProcessor for Oracle, SQL Server, etc.customizePager for custom count queries or serialization..limit(n) for top-N results and .forSearch(true) to optimize search endpoints.GeneratorConfig configures generation of entities, Mapper interfaces, Mapper XML, DAO, Service, Controller, DTO, etc.mapperConfig.enable(false).superClass(MybatisBasicMapper.class).@Mapper, generics, resultMap, and column info.Regardless of manual or AI-generated scaffolding, enable POJO safety checks during development: Xbatis validates VO (@ResultEntity), Model (cn.xbatis.core.mvc.Model), and condition/sort objects (@ConditionTarget, @OrderByTarget) at startup to catch mapping or annotation gaps. AI agents must generate the following config by default:
// Required: enable POJO checks at the main application entry
@SpringBootApplication
@XbatisPojoCheckScan(basePackages = "com.example.project.pojo")
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
// Recommended: dev-only config, can be generated by AI
@Profile("dev")
@Configuration
@XbatisPojoCheckScan(
basePackages = "com.example.api.dto",
modelPackages = "com.example.api.model",
resultEntityPackages = "com.example.api.vo"
)
public class XbatisSafeCheckConfig {
}
Annotation attributes (org.mybatis.spring.boot.autoconfigure.XbatisPojoCheckScan):
| Attribute | Description |
|---|---|
basePackages |
Base package path |
modelPackages |
Model implementations; defaults to basePackages
|
resultEntityPackages |
VO (@ResultEntity) package; defaults to basePackages
|
conditionTargetPackages |
Condition DTO (@ConditionTarget) package |
orderByTargetPackages |
Sorting DTO (@OrderByTarget) package |
solon.yml:
mybatis.master:
pojoCheck:
basePackages: com.example.**.pojo
modelPackages: com.example.**.model
resultEntityPackages: com.example.**.vo
conditionTargetPackages: com.example.**.condition
orderByTargetPackages: com.example.**.orderby
mappers:
- "com.example.mapper"
pojoCheck mirrors @XbatisPojoCheckScan; separate multiple paths with commas. Enable during dev/test to reduce production startup overhead.<logger name="cn.xbatis" level="trace"/> to print generated SQL and chain traces.QueryChain offers .ignoreNullValueInCondition(true), .ignoreEmptyInCondition(true), .trimStringInCondition(true), or simply .forSearch(true).UpdateChain.set accepts lambdas such as c -> c.plus(1) for increments.saveBatch, updateBatch, InsertChain.values, or the batchOpt module for efficient batch handling.@PutEnumValue injects enum labels; @ResultField handles field-level mapping; @Fetch automates cascading queries.QueryChain, InsertChain, UpdateChain, DeleteChain plus annotations to cover most CRUD/statistics tasks.SysUser::getId) instead of hard-coded column names..forSearch(true) or rely on annotated DTOs to avoid manual null checks.dbAdapt; multi-tenant scenarios should wire TenantContext automatically..executeAndReturning() or .executeAndReturningList().BasicMapper with DSL for unified data access.GeneratorConfig for initial scaffolding, then have the agent generate incremental chains/service logic.@OnInsert, @OnUpdate, logical delete interceptors, tenant context to auto-fill creator/update/tenant fields and satisfy audit requirements.fast-dev.md, essential for AI projects)Set databaseId explicitly to avoid detection overhead:
mybatis:
configuration:
databaseId: MYSQL # see db-support.md for available IDs
from, and returnType all target the mapper entity, omit them:SysUser user = QueryChain.of(sysUserMapper)
.eq(SysUser::getId, 1)
.get();
If select(VO.class) equals returnType(VO.class), just call returnType; the framework auto-selects columns based on VO annotations.
Bulk ignore: .forSearch(true) enables ignore-null/blank and auto-trim; use it by default.
Precise ignore:
QueryChain.of(sysUserMapper)
.eq(SysUser::getId, id, Objects::nonNull)
.like(SysUser::getUserName, userName, StringUtils::isNotBlank)
.get();
@ResultEntity + @NestedResultEntity for nested structures; omit attributes when VO fields match entity fields.@FieldNameConstants and referencing X.Fields.xxx in annotations to avoid string literals.connect
connect exposes the chain itself for correlated subqueries:
QueryChain.of(sysUserMapper)
.select(SysUser::getId, SysUser::getUserName)
.connect(query -> query.exists(SubQuery.create()
.select1()
.from(SysUser.class)
.eq(SysUser::getId, query.$(SysUser::getId))
.isNotNull(SysUser::getPassword)
.limit(1)))
.list();
Static-import db.sql.api.impl.cmd.Methods.* for concise chains:
Integer id = QueryChain.of(sysUserMapper)
.select(SysUser::getId, c -> sum(add(c, 1)))
.returnType(Integer.TYPE)
.get();
These convenience patterns greatly enhance productivity and should be default assumptions when designing AI generation strategies.
Bundle this knowledge base with project source code and dependency API docs to build a vector store so AI agents can generate Xbatis-compliant code and configuration accurately.***