- FluentMybatis特性
- FluentMybatis原理
- 创建一个示例的数据库表
DROP TABLE IF EXISTS `your_table`;
create table `your_table`
(
id bigint auto_increment comment '主键ID' primary key,
name varchar(30) charset utf8 null comment '姓名',
age int null comment '年龄',
email varchar(50) charset utf8 null comment '邮箱',
gmt_create datetime null comment '记录创建时间',
gmt_modified datetime null comment '记录最后修改时间',
is_deleted tinyint(2) default 0 null comment '逻辑删除标识'
);
- 初始化 SpringBoot 项目
spring boot: 基于spring boot开发,肯定是必须的
lombok: 省略get, set, toString代码的神器,个人比较喜欢;你也可以手动生成get set方法
mysql-connector-java: 数据库驱动
fluent-mybatis: fluent-mybatis运行时依赖
fluent-mybatis-processor: fluent-mybatis编译时依赖
fluent-mybatis-generator: fluent-mybatis代码生成依赖
测试依赖的jar包: spring-test, junit - List item
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.url=jdbc:mysql://localhost:3306/fluent_mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
- 创建实体类 可以手工创建Entity类,或者任何手段创建的Entity类,然后加上下面注解
在Entity类上加上 @FluentMybatis注解
在主键字段加 @TableId注解
在一般字段加 @TableField注解
这里直接使用fluent mybatis提供的工具类生成代码
public class AppEntityGenerator {
static final String url = "jdbc:mysql://localhost:3306/fluent_mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8";
public static void main(String[] args) {
FileGenerator.build(Abc.class);
}
@Tables(
/** 数据库连接信息 **/
url = url, username = "root", password = "password",
/** Entity类parent package路径 **/
basePack = "cn.org.fluent.mybatis.springboot.demo",
/** Entity代码源目录 **/
srcDir = "spring-boot-demo/src/main/java",
/** Dao代码源目录 **/
daoDir = "spring-boot-demo/src/main/java",
/** 如果表定义记录创建,记录修改,逻辑删除字段 **/
gmtCreated = "gmt_create", gmtModified = "gmt_modified", logicDeleted = "is_deleted",
/** 需要生成文件的表 **/
tables = @Table(value = {"your_table"})
)
static class Abc {
}
}
- gmt_create, 记录创建时间,会设置记录插入的默认值,对应生成Entity字段上的注解 @TableField(insert=“now()”)
- gmt_modified, 记录最后更新时间,会设置记录插入和更新默认值,对应生成代码Entity字段上注解 @TableField(insert=“now()”, update=“now()”)
- is_deleted, 记录逻辑删除标识,字段类型为Boolean,且设置记录插入的默认值,对应注解 @TableField(insert=“0”)
- 执行生成代码main函数, 在工程main/src/java目录下产出 Entity, DaoIntf, DaoImpl文件; 观察YourEntity的主键 id, gmt_create, gmt_modified, is_deleted这几个字段的注解
@Data
@Accessors(chain = true)
@FluentMybatis(table = "your_table")
public class YourEntity implements IEntity{
private static final long serialVersionUID = 1L;
@TableId(value = "id")
private Long id;
@TableField(value = "gmt_create", insert = "now()")
private Date gmtCreate;
@TableField(value = "gmt_modified", insert = "now()", update = "now()")
private Date gmtModified;
@TableField(value = "is_deleted", insert = "0")
private Boolean isDeleted;
@TableField(value = "age")
private Integer age;
@TableField(value = "email")
private String email;
@TableField(value = "name")
private String name;
@Override
public Serializable findPk() {
return id;
}
}
- 生成的Dao文件,引用到了YourTableBaseDao类,这个类需要根据Entity类编译生成,在重新编译前会有编译错误,所以生成代码后需要重新Rebuild下
@Repository
public class YourDaoImpl extends YourBaseDao implements YourDao {
// 在这里添加你自己的业务逻辑代码
}
- 启动SpringBoot测试,验证效果
@SpringBootTest(classes = QuickStartApplication.class)
public class FluentMybatisApplicationTest {
@Autowired
private YourMapper yourMapper;
@Test
void contextLoads() {
List<YourEntity> list = yourMapper.listEntity(yourMapper.query());
for (YourEntity entity : list) {
System.out.println(entity);
}
}
}
-
EntityMapper提供的insert操作
-
insert->单条插入操作->Mapper方法
public interface YourMapper extends IEntityMapper<YourEntity> {
/**
1. 插入一条记录
2. 3. @param entity
4. @return
*/
@Override
@InsertProvider(
type = YourSqlProvider.class,
method = "insert"
)
@Options(
useGeneratedKeys = true,
keyProperty = "id",
keyColumn = "id"
)
int insert(YourEntity entity);
}
- 动态SQL组装
public class YourSqlProvider {
public String insert(YourEntity entity) {
assertNotNull("entity", entity);
MapperSql sql = new MapperSql();
sql.INSERT_INTO("your_table");
List<String> columns = new ArrayList<>();
List<String> values = new ArrayList<>();
if (entity.getId() != null) {
columns.add("id");
values.add("#{id}");
}
columns.add("gmt_create");
if (entity.getGmtCreate() != null) {
values.add("#{gmtCreate}");
} else {
values.add("now()");
}
columns.add("gmt_modified");
if (entity.getGmtModified() != null) {
values.add("#{gmtModified}");
} else {
values.add("now()");
}
columns.add("is_deleted");
if (entity.getIsDeleted() != null) {
values.add("#{isDeleted}");
} else {
values.add("0");
}
if (entity.getAge() != null) {
columns.add("age");
values.add("#{age}");
}
if (entity.getEmail() != null) {
columns.add("email");
values.add("#{email}");
}
if (entity.getName() != null) {
columns.add("name");
values.add("#{name}");
}
sql.INSERT_COLUMNS(columns);
sql.VALUES();
sql.INSERT_VALUES(values);
return sql.toString();
}
}
- 执行insert测试方法, 查看控制台输出log信息
DEBUG - ==> Preparing: INSERT INTO your_table(gmt_create, gmt_modified, is_deleted, age, email, name) VALUES (now(), now(), ?, ?, ?, ?)
DEBUG - ==> Parameters: false(Boolean), 1(Integer), darui.wu@163.com(String), Fluent Mybatis(String)
DEBUG - <== Updates: 1
count:1
entity:YourEntity(id=18, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis)
注意:
- Entity主键值的自增和回写 根据控制台输出,可以看到Entity的id属性已经是根据数据库自增主键值回写过的。 自增主键的设置是通过 @TableId 注解来的,其属性方法auto()默认值是true。
- fluent mybatis根据@TableId注解生成的Mapper类上@Options注解如下:
@Options(
useGeneratedKeys = true,
keyProperty = "id",
keyColumn = "id"
)
- gmt_created, gmt_modified, is_deleted 默认值插入处理 我们先看一下Entity上这3个字段的@TableField注解, 他们都定义了一个属性方法insert,设置了insert的默认值(即程序编码insert时,如果没有设置该字段,则使用默认值)
@TableField(value = "gmt_create", insert = "now()")
private Date gmtCreate;
@TableField(value = "gmt_modified", insert = "now()", update = "now()")
private Date gmtModified;
@TableField(value = "is_deleted", insert = "0")
private Boolean isDeleted;
- 在测试例子中,gmt_created和gmt_modified在初始化Entity时,没有设置任何值; is_deleted设置了值false。 在构建sql是,gmt_created, gmt_modified直接使用默认值 “now()”, is_deleted使用预编译变量(?)设置(实际值false)。
INSERT INTO your_table
(gmt_create, gmt_modified, is_deleted, age, email, name)
VALUES
(now(), now(), ?, ?, ?, ?)
- 我们再看一下对应的SQLProvider的SQL构造, 我们只看着3个字段的构造
public class YourSqlProvider {
public String insert(YourEntity entity) {
List<String> columns = new ArrayList<>();
List<String> values = new ArrayList<>();
// 省略 ... ...
columns.add("gmt_create");
if (entity.getGmtCreate() != null) {
values.add("#{gmtCreate}");
} else {
values.add("now()");
}
columns.add("gmt_modified");
if (entity.getGmtModified() != null) {
values.add("#{gmtModified}");
} else {
values.add("now()");
}
columns.add("is_deleted");
if (entity.getIsDeleted() != null) {
values.add("#{isDeleted}");
} else {
values.add("0");
}
if (entity.getAge() != null) {
columns.add("age");
values.add("#{age}");
}
// 省略... ...
return sql.toString();
}
}
我们看到,没有 insert属性的字段,只判断了是否为空; 有insert属性的字段,如果entity不为空,则把默认值赋值给sql语句。
insertBatch 批量插入
public class YourSqlProvider {
public String insertBatch(Map map) {
assertNotEmpty("map", map);
MapperSql sql = new MapperSql();
List<YourEntity> entities = getParas(map, "list");
sql.INSERT_INTO("your_table");
sql.INSERT_COLUMNS(ALL_ENTITY_FIELDS);
sql.VALUES();
for (int index = 0; index < entities.size(); index++) {
if (index > 0) {
sql.APPEND(", ");
}
sql.INSERT_VALUES(
"#{list[" + index + "].id}",
entities.get(index).getGmtCreate() == null ? "now()" : "#{list[" + index + "].gmtCreate}",
entities.get(index).getGmtModified() == null ? "now()" : "#{list[" + index + "].gmtModified}",
entities.get(index).getIsDeleted() == null ? "0" : "#{list[" + index + "].isDeleted}",
"#{list[" + index + "].age}",
"#{list[" + index + "].email}",
"#{list[" + index + "].name}"
);
}
return sql.toString();
}
}
SQL构造语句是通过一个for循环遍历实体列表,构造出下列SQL语句, 其中对有insert默认值属性处理方式同单条insert一样, 这里就不再重复。
NSERT INTO your_table ('Entity对应的字段列表') VALUES ('实例1值'), ('实例2值')
写个测试看看具体效果
@SpringBootTest(classes = QuickStartApplication.class)
public class FluentMybatisApplicationTest {
@Autowired
private YourMapper yourMapper;
void insertBatch(){
List<YourEntity> entities = new ArrayList<>();
entities.add(new YourEntity().setName("Fluent Mybatis").setEmail("darui.wu@163.com"));
entities.add(new YourEntity().setName("Fluent Mybatis Demo").setEmail("darui.wu@163.com"));
entities.add(new YourEntity().setName("Test4J").setEmail("darui.wu@163.com"));
int count = yourMapper.insertBatch(entities);
System.out.println("count:" + count);
System.out.println("entity:" + entities);
}
}
执行测试,查看控制台输出
DEBUG - ==> Preparing: INSERT INTO your_table(id, gmt_create, gmt_modified, is_deleted, age, email, name) VALUES (?, now(), now(), 0, ?, ?, ?) , (?, now(), now(), 0, ?, ?, ?) , (?, now(), now(), 0, ?, ?, ?)
DEBUG - ==> Parameters: null, null, darui.wu@163.com(String), Fluent Mybatis(String), null, null, darui.wu@163.com(String), Fluent Mybatis Demo(String), null, null, darui.wu@163.com(String), Test4J(String)
DEBUG - <== Updates: 3
count:3
entity:[YourEntity(id=null, gmtCreate=null, gmtModified=null, isDeleted=null, age=null, email=darui.wu@163.com, name=Fluent Mybatis), YourEntity(id=null, gmtCreate=null, gmtModified=null, isDeleted=null, age=null, email=darui.wu@163.com, name=Fluent Mybatis Demo), YourEntity(id=null, gmtCreate=null, gmtModified=null, isDeleted=null, age=null, email=darui.wu@163.com, name=Test4J)]