SpringBoot introductory tutorial 07-integrating mybatis-plus (3)

SpringBoot introductory tutorial 07-integrating mybatis-plus (3)

Outline

  • Getting started with springboot integrating mybatis-plus, and getting started with mybatis-plus code generation tool, portal
  • Springboot integrates mybatis-plus to achieve transaction control, paging, custom SQL and conditional builder Wrapper entry, portal
  • Batch insert and update
  • Field fill
  • Tombstone
  • Conditional constructor usage in lambda expression format
  • SetEntity usage of conditional constructor Wrapper

Batch insert and update

The stock market is very hot recently. I wrote a crawler to crawl stock data from financial websites and store it in a local database. Since the amount of data is still relatively large, I use the bulk insert and update functions.

mybatis-plus provides 2 sets of CRUD interfaces, one is Mapper interface, and the other is Service interface. Both interfaces can be generated by mybatis-plus code generator.

The first two articles have introduced the usage of the Mapper interface in detail, but the Mapper itself does not support the bulk insert function. Fortunately, the Service interface provides this function.

Service interface

public interface IStockDetailService extends IService<StockDetail> {
    
    

}

Service interface default implementation class

@Service
public class StockDetailServiceImpl extends ServiceImpl<StockDetailMapper, StockDetail> implements IStockDetailService {
    
    

}

Business code

@Autowired
private IStockDetailService stockDetailService;

@Transactional
public void loadAndSave() {
    
    
    List<StockDetail> list = new ArrayList<>(5000);
    //往list中添加数据省略
    stockDetailService.saveBatch(list);
}

As shown above, you can directly use the saveBatch(Collection c) method provided by the Service interface.

Field fill

Every trading day, we may go to the financial website to crawl data multiple times. When we want to record the creation time and modification time of the data when saving the data, we use field filling.

Create a custom MetaObjectHandler and inject it into the spring container

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    

    @Override
    public void insertFill(MetaObject metaObject) {
    
    
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
    }

    @Override
    public void updateFill(MetaObject metaObject) {
    
    
        log.info("start update fill ....");
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)  
    }
}

Entity entity class

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_stock_detail")
public class StockDetail implements Serializable {
    
    
    /**
     * 创建时间
     */
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

As shown above, the createTime filling strategy is specified as insert filling, and the updateTime field filling strategy is specified as filling during insert and update.

Tombstone

On the same trading day, every time you get the latest data, you need to delete the historical data of the day. Anyone who has done development knows that the data is best to be deleted logically. If you delete it wrong, you can find a way to restore it.

Logical deletion requires adding the configuration of mybatis-plus in application.yml

  • Specify the global logical deletion field logic-delete-field: delFlag
  • Set logic-not-delete-value: 0
  • Set logic deleted value logic-delete-value: 1

Business code

//逻辑删除旧数据
LambdaUpdateWrapper<StockDetail> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(StockDetail::getTradeDate, localDate);
stockDetailMapper.delete(updateWrapper);

Same as the normal delete code, but in the end it just updates the del_flag value of the current trading day data in the database to 1.

But there is a pit here. After setting the global logical deletion field, when adding data, the del_flag value is null, which results in no data query. At this time, the field filling function is used. When inserting is set, the delFlag field can be filled with a value of 0. .

Complete mybatis-plus configuration

mybatis-plus:
  #外部化xml配置
  #config-location: classpath:mybatis-config.xml
  #指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
  #configuration-properties: classpath:mybatis/config.properties
  #xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
  mapper-locations: classpath*:/mapper/*.xml
  #MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名
  #type-aliases-package: net.xinhuamm.noah.api.model.entity,net.xinhuamm.noah.api.model.dto
  #如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
  #type-aliases-super-type: java.lang.Object
  #枚举类 扫描路径,如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
  #type-enums-package: com.baomidou.mybatisplus.samples.quickstart.enums
  #项目启动会检查xml配置存在(只在开发时候打开)
  check-config-location: true
  #SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句,REUSE:该执行器类型会复用预处理语句,BATCH:该执行器类型会批量执行所有的更新语句
  default-executor-type: REUSE
  configuration:
    # 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
    map-underscore-to-camel-case: false
    # 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true
    cache-enabled: false
    #懒加载
    #aggressive-lazy-loading: true
    #NONE:不启用自动映射 PARTIAL:只对非嵌套的 resultMap 进行自动映射 FULL:对所有的 resultMap 都进行自动映射
    #auto-mapping-behavior: partial
    #NONE:不做任何处理 (默认值)WARNING:以日志的形式打印相关警告信息 FAILING:当作映射失败处理,并抛出异常和详细信息
    #auto-mapping-unknown-column-behavior: none
    #如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      #表名下划线命名默认true
      table-underline: true
      #id类型
      id-type: auto
      #是否开启大写命名,默认不开启
      #capital-mode: false
      #全局逻辑删除字段
      logic-delete-field: delFlag
      #逻辑未删除值,(逻辑删除下有效)
      logic-not-delete-value: 0
      #逻辑已删除值,(逻辑删除下有效)
      logic-delete-value: 1
      #数据库类型
      db-type: mysql

LambdaQueryWrapper和LambdaUpdateWrapper

As the name knows, it is a conditional constructor that supports lambda syntax.

QueryWrapper usage

QueryWrapper<StockTradeDate> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("is_open",1)
    .le("trade_date","2020-0714")
    .orderByDesc("trade_date")
    .last("limit 1");

LambdaQueryWrapper usage

LambdaQueryWrapper<StockTradeDate> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StockTradeDate::getIsOpen, 1)
    .le(StockTradeDate::getTradeDate,date)
    .orderByDesc(StockTradeDate::getTradeDate)
    .last("limit 1");

By comparison, it can be seen that the parameter passed by queryWrapper is the database column name, while the parameter passed by LambdaQueryWrapper is the Entity entity class field. Obviously, the latter syntax is more in line with java development habits and can avoid spelling errors in database fields.

The setEntity method of the conditional constructor Wrapper

Back-end development often has such a requirement. The front-end page has a search box with many fields. If the user enters some field values, these fields must be used as filter conditions to filter the data.

If you judge that the field is not empty, and then call the QueryWrapper method one by one is a bit too stupid, you can use the setEntity method of the wrapper at this time

@RequestMapping("/list1")
public Object list1(){
    
    
    QueryWrapper<StockTradeDate> wrapper = new QueryWrapper<>();
    StockTradeDate stockTradeDate = new StockTradeDate();
    stockTradeDate.setIsOpen(1L);
    stockTradeDate.setTradeDate("2020-07-14");
    wrapper.setEntity(stockTradeDate);
    List<StockTradeDate> list = tradeDateMapper.selectList(wrapper);
    return list;
}

The code is simple and clear, so I won't say more.

How to implement the setEntity method at the bottom of mybatis-plus I did not follow the code deeply, but I have some ideas on how to implement the method by myself.

  • The method parameter is the entity object
  • According to the entity object, you can get the attribute Field of the entity entity class
  • Determine whether there are @TableField annotations on each Field of the entity entity class
  • If so, you can get the field name of the database, and you can also get the value of the field
  • If not, it is considered that the field name of the entity class is the field name of the database (or the database field name is obtained in the reverse direction according to the camel case strategy)
  • Finally, a map of database field names and corresponding values ​​can be returned
  • Based on this map, query sql can be spliced ​​(the conditional constructor Wrapper supports passing map parameters)

The simple implementation code is as follows:

public class MybatisPlusUtil<T> {
    
    

    public Map<String, Object> convert(T t) {
    
    
        Map<String, Object> map = new HashMap<>();
        try {
    
    

            Field[] fields = t.getClass().getDeclaredFields();
            if (fields != null) {
    
    
                for (Field field : fields) {
    
    
                    if (Modifier.isStatic(field.getModifiers())) {
    
    
                        continue;
                    }
                    if (!field.isAccessible()) {
    
    
                        field.setAccessible(true);
                    }

                    Object o = field.get(t);
                    if(o!=null){
    
    
                        TableField annotation = field.getAnnotation(TableField.class);
                        if (annotation != null) {
    
    
                            String value = annotation.value();
                            if (value != null) {
    
    
                                map.put(value, o);
                            }
                        }else{
    
    
                            map.put(field.getName(), o);
                        }
                    }
                }
            }
        } catch (Exception e) {
    
    
            System.out.println(e);
        }

        return map;
    }
}

Guess you like

Origin blog.csdn.net/l229568441/article/details/107350335