1. Dependency introduction
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
2. Plug-in introduction
The plug-in here is so that we can directly introduce the Def class into the code and perform operations. If there is no such class, we need to manually go to the generated-sources of the target and set the code to the root directory. For convenience, we Just use the plug-in
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
<!-- <path>-->
<!-- <groupId>org.projectlombok</groupId>-->
<!-- <artifactId>lombok-mapstruct-binding</artifactId>-->
<!-- <version>${lombok-mapstruct-binding.version}</version>-->
<!-- </path>-->
<!-- <path>-->
<!-- <groupId>org.mapstruct</groupId>-->
<!-- <artifactId>mapstruct-processor</artifactId>-->
<!-- <version>${org.mapstruct.version}</version>-->
<!-- </path>-->
<path>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
3. Practical operation
Introduce into the class that needs to be queried:
import static com.mybatis.flex.entity.table.PersonTableDef.PERSON;
1. New operations:
What should be noted here is that the default new method does not ignore null values. For example, if your entity class does not set the value of createtime, then in the insert statement, createtime will be set to null, but we need to set it through filling. value, so it is recommended to useinsertSelective,会帮助我们自动忽略null值,当然也可以直接使用insert(entity,ignoreNulls)
insert(entity)
: Insert entity class data without ignoringnull
values.insertSelective(entity)
: Insert entity class data, but ignorenull
the data and only insert content with value. The advantage of this is that the database has already been configured with some default values, and these default values will take effect.insert(entity, ignoreNulls)
: Insert entity class data.insertWithPk(entity)
: Insert entity class with primary key,null
value is not ignored.insertSelectiveWithPk(entity)
: Insert an entity class with a primary key, ignoringnull
the value.insertWithPk(entity, ignoreNulls)
: Insertion with primary key. At this time, the entity class will not generate the primary key through the primary key generator.insertBatch(entities)
: Insert entity class data in batches, and only the inserted field content will be constructed based on the first piece of data.insertBatch(entities, size)
: Insert entity class data in batches and split according to size.
2. Delete operation
deleteById(id)
: Delete data based on primary key. If there are multiple primary keys, you need to pass in an array, for example:new Integer[]{100,101}
.deleteBatchByIds(ids)
: Delete data in batches based on multiple primary keys.deleteBatchByIds(ids, size)
: Delete data in batches based on multiple primary keys.deleteByMap(whereConditions)
: Delete data according to the conditions constructed by Map.deleteByCondition(whereConditions)
: Delete data based on query conditions.deleteByQuery(queryWrapper)
: Delete data based on query conditions.
3. Modification operation
1. Simple update
update(entity)
: Update data based on the primary key. If the entity class attribute data isnull
, the attribute will not be new to the database.update(entity, ignoreNulls)
: Update data to the database based on the primary key.updateByMap(entity, whereConditions)
: Update data according to the conditions constructed by Map.updateByMap(entity, ignoreNulls, whereConditions)
: Update data according to the conditions constructed by Map.updateByCondition(entity, whereConditions)
: Update data based on query conditions.updateByCondition(entity, ignoreNulls, whereConditions)
: Update data based on query conditions.updateByQuery(entity, queryWrapper)
: Update data based on query conditions.updateByQuery(entity, ignoreNulls, queryWrapper)
: Update data based on query conditions.- ~
updateNumberAddByQuery(fieldName, value, queryWrapper)
: Execute similarupdate table set field = field + 1 where ...
scenarios. ~ - ~
updateNumberAddByQuery(column, value, queryWrapper)
: Execute similarupdate table set field = field + 1 where ...
scenarios. ~ - ~
updateNumberAddByQuery(fn, value, queryWrapper)
: Execute similarupdate table set field = field + 1 where ...
scenarios. ~
2. Update some fields (for example, set the value of some fields to null)
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);
3. Some fields are updated and enhanced, for example, if you want to add one to a certain field on the original basis
//比如这样的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);
4. Query operation
1. Simple query
There’s not much to say about these, the operation is very simple
selectOneById(id)
: Query data based on primary key.selectOneByMap(whereConditions)
: Query data according to the conditions constructed by Map.selectOneByCondition(whereConditions)
: Query data based on query conditions.selectOneByQuery(queryWrapper)
: Query 1 piece of data based on query conditions.selectListByIds(ids)
: Query multiple pieces of data based on multiple primary keys.selectListByMap(whereConditions)
: Build query conditions based on Map and query multiple pieces of data.selectListByMap(whereConditions, count)
: Build query conditions based on Map and query multiple pieces of data.selectListByCondition(whereConditions)
: Query multiple pieces of data based on query conditions.selectListByCondition(whereConditions, count)
: Query multiple pieces of data based on query conditions.selectListByQuery(queryWrapper)
: Query the data list according to the query conditions.selectListByQuery(queryWrapper, consumers)
: Query the data list according to the query conditions.
2. Cursor query
As we all know, when the amount of data queried at one time is too large, if the system memory is full at once, memory overflow problems will occur. For example, when exporting large amounts of data to Excel, it is necessary to use this
Cursor<T> selectCursorByQuery(QueryWrapper queryWrapper);
Usage examples are as follows:
Db.tx(() -> {
Cursor<Account> accounts = accountMapper.selectCursorByQuery(query);
for (Account account : accounts) {
System.out.println(account);
}
return true;
});
In the above example, the database does not return all the data to the application at once. Instead, it will fetch one piece of data from the database every time it loops. In this way, even if there are 1 million levels of data, it will not cause our application memory to overflow. ,At the same time, in the for loop, we can terminate data reading at any time.
But because the cursor query is in the for loop, it goes to the database to get the data. Therefore, it is necessary to ensure selectCursorByQuery
that the method and its processing must be performed within a transaction to ensure that its link is not disconnected from the database.
3.Paging query
Single table paging
paginate(pageNumber, pageSize, queryWrapper)
:Paging query.paginate(pageNumber, pageSize, whereConditions)
:Paging query.
Multiple table joint query
paginateAs(pageNumber, pageSize, queryWrapper, asType)
:Paging query.paginateAs(page, queryWrapper, asType)
:Paging query.
4. Multi-table joint query
In fact, there are many ways. I will only use the join method as an example here. For simple scenarios, you can use flex. I think for complex sql, it is faster to write xml.
1. One-to-one correspondence between fields
1. Define ArticleDTO
the class and ArticleDTO
define tb_account
the field mapping of the table.
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. Use QueryWrapper
build left join
query, and the query results ArticleDTO
are received through types.
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<ArticleDTO> results = mapper.selectListByQueryAs(query, ArticleDTO.class);
System.out.println(results);
2. Field names are inconsistent
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;
}
Then, QueryWrapper
you need to add as
and modify it as follows:
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<ArticleDTO> results = mapper.selectListByQueryAs(query, ArticleDTO.class);
System.out.println(results);
3. One-to-many automatic mapping
public class AccountVO {
private Long id;
private String userName;
private int age;
//账户拥有的 图书列表
private List<Book> books;
}
List<AccountVO> 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);
If the outer layer and the inner layer have the same field name, flex will help us set the alias and will also help us automatically map it.
public class AccountVO {
private Long id;
private String name;
private int age;
//账户拥有的 图书列表
private List<Book> book;
}
public class Book {
private Long id;
private Long accountId;
private String name;
}
In the above nested definitions, AccountVO
and Book
all contain the definitions of id
and name
, assuming our query method is as follows:
List<AccountVO> 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);
The SQL executed is as follows:
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
At this point, the queried data can be mapped to AccountVO
the class normally.
Note that in QueryWrapper
select(...)
, when MyBatis-Flex queries multiple tables and has the same field name, MyBatis-Flex will actively help the user add an as alias internally. The default is:表名$字段名
Note: If there is a complex nested query and the field names are the same, the specific query field must be specified, and * query cannot be used.
5. Batch operations
MyBatis-Flex provides many methods for batch operations (batch insert, batch update, etc.). When there are multiple methods, it often leads to misuse.
BaseMapper.insertBatch 方法
When BaseMapper.insertBatch
executed, accounts
a SQL as follows will be assembled:
insert into tb_account(id,nickname, .....) values
(100,"miachel100", ....),
(101,"miachel101", ....),
(102,"miachel102", ....),
(103,"miachel103", ....),
(104,"miachel104", ....),
(105,"miachel105", ....);
This has a characteristic: when inserting small batches of data, the efficiency is very high; but when there are too many data lists, the SQL generated may be very large, and this large SQL will become Very slow.
Therefore, BaseMapper.insertBatch
the method is only suitable for scenarios where small batches of data are inserted, such as within 100 pieces of data.
Db.executeBatch
method
Db.executeBatch
It can be used for batch insertion, modification and deletion. The following is Db.executeBatch
an example of batch insertion:
List<Account> accounts = ....
Db.executeBatch(accounts.size(), 1000, AccountMapper.class, (mapper, index) -> {
Account account = accounts.get(index);
mapper.insert(account);
});
The general meaning is that if you use insertbatch in basemapper, you must use the above method. If you use the method in Iservice, you don’t need to wrap the method yourself. You can just use it directly, because Flex has already used it for batch addition in Iservice. It has helped us encapsulate it
6. Chain operation (the most recommended way of writing flex)
1. Query list
List<Article> articles = articleService.queryChain()
.select(ARTICLE.ALL_COLUMNS)
.from(ARTICLE)
.where(ARTICLE.ID.ge(100))
.list();
//或者如果不在service,可以通过mapper获取
List<Article> articles = QueryChain.of(mapper)
.select(ARTICLE.ALL_COLUMNS)
.from(ARTICLE)
.where(ARTICLE.ID.ge(100))
.list();
2. Query a single
List<Article> articles = articleService.queryChain()
.select(ARTICLE.ALL_COLUMNS)
.from(ARTICLE)
.where(ARTICLE.ID.ge(100))
.one();
3.Modify
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();