Master Fluent Mybatis: A Deep Dive into Java ORM Framework
This article introduces Fluent Mybatis, a Java ORM framework that enhances Mybatis with features from Mybatis Plus and JPA, demonstrates its core concepts, compares it with native Mybatis and Mybatis Plus through a student score query example, and provides step‑by‑step setup, code generation, and CRUD usage.
Fluent Mybatis Introduction
Fluent Mybatis is a Mybatis‑enhancement framework that integrates the advantages of Mybatis Plus, Dynamic SQL, and JPA. It uses an annotation processor to generate code, allowing developers to write complex business SQL with a fluent Java API instead of XML.
Key Highlights
No XML files are required; SQL is built through Java code.
Code and SQL logic are unified, reducing the need for separate DAO assembly.
Annotation‑processor generates boilerplate mapper and entity code automatically.
Project address: https://gitee.com/fluent-mybatis/fluent-mybatis/wikis
Disclaimer: This framework is maintained personally. Use it only after thorough evaluation; it may not be suitable for production projects.
Core Concepts and Features
Fluent Mybatis combines Mybatis Plus, Dynamic SQL, and JPA features, generating code via annotation processing.
Feature Overview
Underlying Principle
Fluent Mybatis vs Mybatis vs Mybatis Plus
To illustrate differences, we implement a typical business requirement: calculate the minimum, maximum, and average scores for three subjects (English, Math, Chinese) per term, with at least two records per group.
Database Schema
create table `student_score` (
id bigint auto_increment comment '主键ID' primary key,
student_id bigint not null comment '学号',
gender_man tinyint default 0 not null comment '性别, 0:女; 1:男',
school_term int null comment '学期',
subject varchar(30) null comment '学科',
score int null comment '成绩',
gmt_create datetime not null comment '记录创建时间',
gmt_modified datetime not null comment '记录最后修改时间',
is_deleted tinyint default 0 not null comment '逻辑删除标识'
) engine = InnoDB default charset=utf8;SQL Query
select school_term,
subject,
count(score) as count,
min(score) as min_score,
max(score) as max_score,
avg(score) as avg_score
from student_score
where school_term >= 2000
and subject in ('英语','数学','语文')
and score >= 60
and is_deleted = 0
group by school_term, subject
having count(score) > 1
order by school_term, subject;Implementation with Fluent Mybatis
Fluent Mybatis can express the same query using a fluent API without writing XML:
// Example code omitted for brevity – the fluent API builds the query and executes it directly.Implementation with Native Mybatis
Requires a Mapper interface, a parameter POJO, and an XML mapper file:
public interface MyStudentScoreMapper {
List<Map<String, Object>> summaryScore(SummaryQuery paras);
}
public class SummaryQuery {
private Integer schoolTerm;
private List<String> subjects;
private Integer score;
private Integer minCount;
}
<select id="summaryScore" resultType="map" parameterType="cn.org.fluent.Mybatis.springboot.demo.mapper.SummaryQuery">
select school_term, subject, count(score) as count,
min(score) as min_score, max(score) as max_score, avg(score) as avg_score
from student_score
where school_term >= #{schoolTerm}
and subject in
<foreach collection="subjects" item="item" open="(" close=")" separator=",">#{item}</foreach>
and score >= #{score}
and is_deleted = 0
group by school_term, subject
having count(score) > #{minCount}
order by school_term, subject;
</select>Implementation with Mybatis Plus
Mybatis Plus simplifies the code but still relies on string literals for column names, which can cause maintenance issues:
// Example Mybatis Plus service code (omitted for brevity)Comparison Summary
Fluent Mybatis eliminates XML and reduces boilerplate.
Mybatis Plus still requires hard‑coded column strings, leading to memorization and runtime errors when schema changes.
Native Mybatis is the most verbose and error‑prone.
Code Generation
Fluent Mybatis Code Generator
public class AppEntityGenerator {
static final String url = "jdbc:mysql://localhost:3306/fluent_Mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8";
public static void main(String[] args) {
FileGenerator.build(Abc.class);
}
@Tables(
url = url, username = "root", password = "password",
basePack = "cn.org.fluent.Mybatis.springboot.demo",
srcDir = "spring-boot-demo/src/main/java",
daoDir = "spring-boot-demo/src/main/java",
gmtCreated = "gmt_create", gmtModified = "gmt_modified", logicDeleted = "is_deleted",
tables = @Table(value = {"student_score"})
)
static class Abc {}
}Mybatis Plus Code Generator
public class CodeGenerator {
static String dbUrl = "jdbc:mysql://localhost:3306/fluent_Mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8";
@Test
public void generateCode() {
GlobalConfig config = new GlobalConfig();
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL)
.setUrl(dbUrl)
.setUsername("root")
.setPassword("password")
.setDriverName(Driver.class.getName());
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setCapitalMode(true)
.setEntityLombokModel(false)
.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityTableFieldAnnotationEnable(true)
.setFieldPrefix(new String[]{"test_"})
.setInclude(new String[]{"student_score"})
.setLogicDeleteFieldName("is_deleted")
.setTableFillList(Arrays.asList(
new TableFill("gmt_create", FieldFill.INSERT),
new TableFill("gmt_modified", FieldFill.INSERT_UPDATE)));
config.setActiveRecord(false)
.setIdType(IdType.AUTO)
.setOutputDir(System.getProperty("user.dir") + "/src/main/java/")
.setFileOverride(true);
new AutoGenerator().setGlobalConfig(config)
.setDataSource(dataSourceConfig)
.setStrategy(strategyConfig)
.setPackageInfo(new PackageConfig()
.setParent("com.mp.demo")
.setController("controller")
.setEntity("entity"))
.execute();
}
}Generated Files Overview
Fluent Mybatis generates the following components for each entity:
mapper/*Mapper – Mybatis mapper interface with generic CRUD methods.
dao/*BaseDao – Base DAO implementation.
wrapper/*Query – Dynamic query builder.
wrapper/*Updater – Dynamic update builder.
helper/*Mapping – Field‑to‑property mapping definitions.
helper/*Segment – Implementations for select, where, group‑by, having, order‑by, limit.
IEntityRelation – Interface for handling entity relationships.
Ref – Shortcut utility for accessing generated objects.
Practical Example: HelloWorld Entity
Table Definition
create schema fluent_mybatis;
create table hello_world (
id bigint unsigned auto_increment primary key,
say_hello varchar(100) null,
your_name varchar(100) null,
gmt_created datetime default null comment '创建时间',
gmt_modified datetime default null comment '更新时间',
is_deleted tinyint(2) default 0 comment '是否逻辑删除'
) ENGINE = InnoDB comment='简单演示表';Entity Class
@FluentMybatis
public class HelloWorldEntity extends RichEntity {
private Long id;
private String sayHello;
private String yourName;
private Date gmtCreated;
private Date gmtModified;
private Boolean isDeleted;
@Override
public Class<? extends IEntity> entityClass() {
return HelloWorldEntity.class;
}
}Configuration
@ComponentScan(basePackages = "cn.org.atool.fluent.mybatis.demo1")
@MapperScan("cn.org.atool.fluent.mybatis.demo1.entity.mapper")
@Configuration
public class HelloWorldConfig {
@Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/fluent_mybatis?useUnicode=true&characterEncoding=utf8");
ds.setUsername("root");
ds.setPassword("password");
return ds;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean;
}
@Bean
public MapperFactory mapperFactory() {
return new MapperFactory();
}
}Test Case
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = HelloWorldConfig.class)
public class HelloWorldTest {
@Autowired
HelloWorldMapper mapper;
@Test
public void testHelloWorld() {
// Delete any existing record with id=1
mapper.delete(mapper.query().where.id().eq(1L).end());
// Insert a new record
HelloWorldEntity entity = new HelloWorldEntity();
entity.setId(1L);
entity.setSayHello("hello world");
entity.setYourName("Fluent Mybatis");
entity.setIsDeleted(false);
mapper.insert(entity);
// Query the inserted record
HelloWorldEntity result1 = mapper.findOne(mapper.query().where.id().eq(1L).end());
System.out.println("1. HelloWorldEntity:" + result1);
// Update the record
mapper.updateBy(mapper.updater()
.set.sayHello().is("say hello, say hello!")
.set.yourName().is("Fluent Mybatis is powerful!")
.where.id().eq(1L).end());
// Query after update
HelloWorldEntity result2 = mapper.findOne(mapper.query()
.where.sayHello().like("hello")
.and.isDeleted().eq(false).end()
.limit(1));
System.out.println("2. HelloWorldEntity:" + result2);
}
}Running the test prints the entity before and after the update, confirming that Fluent Mybatis provides full CRUD capabilities with minimal boilerplate.
Conclusion
Fluent Mybatis offers a concise, annotation‑driven approach to Mybatis development, eliminating XML configuration and reducing repetitive code. By generating mappers, DAOs, and query/updater builders at compile time, it streamlines CRUD operations while keeping the flexibility of native Mybatis.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
