mysql
mysql-connector-java
8.0.12
org.projectlombok
lombok
com.mybatis-flex
mybatis-flex-spring-boot-starter
1.5.4
com.zaxxer
HikariCP
这里的插件是为了,我们能在代码中直接引入Def类,进行操作,如果没有这个类,需要我们手动去target的generated-sources中,把代码设置为root根目录下才可以,为了方便,我们直接用插件即可
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
1.8
1.8
org.projectlombok
lombok
com.mybatis-flex
mybatis-flex-processor
在需要查询的类中引入:
import static com.mybatis.flex.entity.table.PersonTableDef.PERSON;
这里要注意的是,默认的新增方法,没有忽略null值,比如你的实体类没有设置createtime的值,那在插入语句中,就会把createtime设置为null,但是我们需要通过填充手段设置它的值,所以,这块建议使用insertSelective,会帮助我们自动忽略null值,当然也可以直接使用insert(entity,ignoreNulls)
insert(entity)
:插入实体类数据,不忽略 null
值。insertSelective(entity)
:插入实体类数据,但是忽略 null
的数据,只对有值的内容进行插入。这样的好处是数据库已经配置了一些默认值,这些默认值才会生效。insert(entity, ignoreNulls)
:插入实体类数据。insertWithPk(entity)
:插入带有主键的实体类,不忽略 null
值。insertSelectiveWithPk(entity)
:插入带有主键的实体类,忽略 null
值。insertWithPk(entity, ignoreNulls)
:带有主键的插入,此时实体类不会经过主键生成器生成主键。insertBatch(entities)
:批量插入实体类数据,只会根据第一条数据来构建插入的字段内容。insertBatch(entities, size)
:批量插入实体类数据,按 size 切分。deleteById(id)
:根据主键删除数据。如果是多个主键的情况下,需要传入数组,例如:new Integer[]{100,101}
。deleteBatchByIds(ids)
:根据多个主键批量删除数据。deleteBatchByIds(ids, size)
:根据多个主键批量删除数据。deleteByMap(whereConditions)
:根据 Map 构建的条件来删除数据。deleteByCondition(whereConditions)
:根据查询条件来删除数据。deleteByQuery(queryWrapper)
:根据查询条件来删除数据。update(entity)
:根据主键来更新数据,若实体类属性数据为 null
,该属性不会新到数据库。update(entity, ignoreNulls)
:根据主键来更新数据到数据库。updateByMap(entity, whereConditions)
:根据 Map 构建的条件来更新数据。updateByMap(entity, ignoreNulls, whereConditions)
:根据 Map 构建的条件来更新数据。updateByCondition(entity, whereConditions)
:根据查询条件来更新数据。updateByCondition(entity, ignoreNulls, whereConditions)
:根据查询条件来更新数据。updateByQuery(entity, queryWrapper)
:根据查询条件来更新数据。updateByQuery(entity, ignoreNulls, queryWrapper)
:根据查询条件来更新数据。updateNumberAddByQuery(fieldName, value, queryWrapper)
:执行类似 update table set field = field + 1 where ...
的场景。~updateNumberAddByQuery(column, value, queryWrapper)
:执行类似 update table set field = field + 1 where ...
的场景。~updateNumberAddByQuery(fn, value, queryWrapper)
:执行类似 update table set field = field + 1 where ...
的场景。~Account account = UpdateEntity.of(Account.class, 100);
//Account account = UpdateEntity.of(Account.class);
//account.setId(100);
account.setUserName(null);
account.setAge(10);
accountMapper.update(account);
//比如这样的sql:
update tb_account
set user_name = ?, age = age + 1 where id = ?
//那么我们需要这样写
Account account = UpdateEntity.of(Account.class, 100);
account.setUserName(null);
// 通过 UpdateWrapper 操作 account 数据
UpdateWrapper wrapper = UpdateWrapper.of(account);
wrapper.setRaw("age", "age + 1")
accountMapper.update(account);
//或者通过def类
Account account = UpdateEntity.of(Account.class, 100);
account.setUserName("Michael");
// 通过 UpdateWrapper 操作 account 数据
UpdateWrapper wrapper = UpdateWrapper.of(account);
wrapper.set(ACCOUNT.AGE, ACCOUNT.AGE.add(1))
accountMapper.update(account);
//或者
Account account = UpdateEntity.of(Account.class, 100);
account.setUserName("Michael");
// 通过 UpdateWrapper 操作 account 数据
UpdateWrapper wrapper = UpdateWrapper.of(account);
wrapper.set(ACCOUNT.AGE, select().from(...))
accountMapper.update(account);
这几个就没啥说的了,操作很简单
selectOneById(id)
:根据主键查询数据。selectOneByMap(whereConditions)
:根据 Map 构建的条件来查询数据。selectOneByCondition(whereConditions)
:根据查询条件查询数据。selectOneByQuery(queryWrapper)
:根据查询条件来查询 1 条数据。selectListByIds(ids)
:根据多个主键来查询多条数据。selectListByMap(whereConditions)
:根据 Map 来构建查询条件,查询多条数据。selectListByMap(whereConditions, count)
:根据 Map 来构建查询条件,查询多条数据。selectListByCondition(whereConditions)
:根据查询条件查询多条数据。selectListByCondition(whereConditions, count)
:根据查询条件查询多条数据。selectListByQuery(queryWrapper)
:根据查询条件查询数据列表。selectListByQuery(queryWrapper, consumers)
:根据查询条件查询数据列表。众所周知,当一次查询数据量太多时,如果一下把系统内存打满,会出现内存溢出的问题,例如在大数据量的excel导出时,就很有必要使用这个了
Cursor selectCursorByQuery(QueryWrapper queryWrapper);
使用实例如下:
Db.tx(() -> {
Cursor accounts = accountMapper.selectCursorByQuery(query);
for (Account account : accounts) {
System.out.println(account);
}
return true;
});
以上的示例中,数据库并不是把所有的数据一次性返回给应用,而是每循环 1 次才会去数据库里拿 1 条数据,这样,就算有 100w 级数据,也不会导致我们应用内存溢出,同时,在 for 循环中, 我们可以随时终止数据读取。
但由于游标查询是在 for 循环的时候,才去数据库拿数据。因此必须保证 selectCursorByQuery
方法及其处理必须是在事务中进行,才能保证其链接并未与数据库断开。
单表分页
paginate(pageNumber, pageSize, queryWrapper)
:分页查询。paginate(pageNumber, pageSize, whereConditions)
:分页查询。多表联查
paginateAs(pageNumber, pageSize, queryWrapper, asType)
:分页查询。paginateAs(page, queryWrapper, asType)
:分页查询。其实有很多种方式,我这里只举例用join的方式,简单的场景可以用flex,我觉得复杂的sql,大家还是写xml来的比较快
1、定义 ArticleDTO
类,ArticleDTO
里定义 tb_account
表的字段映射。
public class ArticleDTO {
private Long id;
private Long accountId;
private String title;
private String content;
//以下用户相关字段
private String userName;
private int age;
private Date birthday;
}
2、使用 QueryWrapper
构建 left join
查询,查询结果通过 ArticleDTO
类型接收。
QueryWrapper query = QueryWrapper.create()
.select(ARTICLE.ALL_COLUMNS)
.select(ACCOUNT.USER_NAME,ACCOUNT.AGE,ACCOUNT.BIRTHDAY)
.from(ARTICLE)
.leftJoin(ACCOUNT).on(ARTICLE.ACCOUNT_ID.eq(ACCOUNT.ID))
.where(ACCOUNT.ID.ge(0));
List results = mapper.selectListByQueryAs(query, ArticleDTO.class);
System.out.println(results);
public class ArticleDTO {
private Long id;
private Long accountId;
private String title;
private String content;
//以下用户字段 和 用户表定义的列不一致,表定义的列为 user_name
private String authorName;
private int authorAge;
private Date birthday;
}
那么, QueryWrapper
需要添加 as
,修改如下:
QueryWrapper query = QueryWrapper.create()
.select(ARTICLE.ALL_COLUMNS)
.select(ACCOUNT.USER_NAME.as(ArticleDTO::getAuthorName)
,ACCOUNT.AGE.as(ArticleDTO::getAuthorAge)
,ACCOUNT.BIRTHDAY
)
.from(ARTICLE)
.leftJoin(ACCOUNT).on(ARTICLE.ACCOUNT_ID.eq(ACCOUNT.ID))
.where(ACCOUNT.ID.ge(0));
List results = mapper.selectListByQueryAs(query, ArticleDTO.class);
System.out.println(results);
public class AccountVO {
private Long id;
private String userName;
private int age;
//账户拥有的 图书列表
private List books;
}
List bookVos = QueryChain.of(accountMapper)
.select(
ACCOUNT.ID,
ACCOUNT.USER_NAME,
ACCOUNT.AGE,
BOOK.TITLE,
BOOK.CONTENT,
)
.from(ACCOUNT)
.leftJoin(BOOK).on(ACCOUNT.ID.eq(BOOK.ACCOUNT_ID))
.where(ACCOUNT.ID.ge(100))
.listAs(AccountVO.java);
如果外边的一层和里边的一层有的字段名相同,flex帮我们设置了别名,同样会帮我们自动映射
public class AccountVO {
private Long id;
private String name;
private int age;
//账户拥有的 图书列表
private List book;
}
public class Book {
private Long id;
private Long accountId;
private String name;
}
在以上的嵌套定义中, AccountVO
以及 Book
都包含了 id
和 name
的定义,假设我们查询的方法如下:
List bookVos = QueryChain.of(accountMapper)
.select(
ACCOUNT.ID,
ACCOUNT.NAME,
ACCOUNT.AGE,
BOOK.ID,
BOOK.NAME,
)
.from(ACCOUNT)
.leftJoin(BOOK).on(ACCOUNT.ID.eq(BOOK.ACCOUNT_ID))
.where(ACCOUNT.ID.ge(100))
.listAs(AccountVO.java);
其执行的 SQL 如下:
select tb_account.id, tb_account.name, tb_account.age,
tb_book.id as tb_book$id, -- Flex 发现有重名时,会自动添加上 as 别名
tb_book.name as tb_book$name -- Flex 发现有重名时,会自动添加上 as 别名
from tb_account
left join tb_book on tb_account.id = tb_book.account_id
where tb_account.id >= 100
此时,查询的数据可以正常映射到 AccountVO
类。
注意,在 QueryWrapper 的
select(...)
中,MyBatis-Flex 在 多表查询 的情况下,且有相同的字段名时,MyBatis-Flex 内部会主动帮助用户添加上 as 别名,默认为:表名$字段名
注:如果复杂的嵌套查询,有字段名是相同的,必须指定具体的查询字段,不可以使用*查询
在 MyBatis-Flex 中,提供了许多批量操作(批量插入、批量更新等)的方法,当有多个方法的时候,会经常导致误用的情况
BaseMapper.insertBatch 方法
通过 BaseMapper.insertBatch
执行时,会通过 accounts
去组装一个如下的 SQL:
insert into tb_account(id,nickname, .....) values
(100,"miachel100", ....),
(101,"miachel101", ....),
(102,"miachel102", ....),
(103,"miachel103", ....),
(104,"miachel104", ....),
(105,"miachel105", ....);
这种有一个特点:在小批量数据执行插入的时候,效率是非常高;但是当数据列表过多时,其生成的 SQL 可能会非常大, 这个大的 SQL 在传输和执行的时候就会变得很慢了。
因此,BaseMapper.insertBatch
方法只适用于在小批量数据插入的场景,比如 100 条数据以内
Db.executeBatch
方法Db.executeBatch
可以用于进行批量的插入、修改和删除,以下是使用 Db.executeBatch
进行批量插入的示例:
List accounts = ....
Db.executeBatch(accounts.size(), 1000, AccountMapper.class, (mapper, index) -> {
Account account = accounts.get(index);
mapper.insert(account);
});
大概意思就是,如果使用basemapper中的insertbatch,那么就要用上边的方法,如果使用Iservice中的方法,就不需要自己包装方法了,直接使用即可,因为Iservice中的批量新增,Flex已经使用它,帮我们封装过了
List articles = articleService.queryChain()
.select(ARTICLE.ALL_COLUMNS)
.from(ARTICLE)
.where(ARTICLE.ID.ge(100))
.list();
//或者如果不在service,可以通过mapper获取
List articles = QueryChain.of(mapper)
.select(ARTICLE.ALL_COLUMNS)
.from(ARTICLE)
.where(ARTICLE.ID.ge(100))
.list();
List articles = articleService.queryChain()
.select(ARTICLE.ALL_COLUMNS)
.from(ARTICLE)
.where(ARTICLE.ID.ge(100))
.one();
UpdateChain.of(Account.class)
.set(Account::getUserName, "张三")
.setRaw(Account::getAge, "age + 1")
.where(Account::getId).eq(1)
.update();
//或者
//更新数据
UpdateChain.of(Account.class)
.set(Account::getAge, ACCOUNT.AGE.add(1))
.where(Account::getId).ge(100)
.and(Account::getAge).eq(18)
.update();