Mybatis-Flex actual combat

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 ignoring  null values.
  • insertSelective(entity): Insert entity class data, but ignore  null 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, ignoring  null 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 is  null, 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 similar  update table set field = field + 1 where ... scenarios. ~
  • ~ updateNumberAddByQuery(column, value, queryWrapper): Execute similar  update table set field = field + 1 where ... scenarios. ~
  • ~ updateNumberAddByQuery(fn, value, queryWrapper): Execute similar  update 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  asand 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();

 

Guess you like

Origin blog.csdn.net/weixin_59244784/article/details/132004661