Article directory
- Preface
- 1. springBoot integrates mybatis-plus
-
- 1.1 maven introduces dependencies:
- 1.2 Configure data source::
- 2. Use:
-
- 2.1 mysql prints the executed sql settings:
- 2.2 Paging query:
- 2.3 Conditional constructor:
-
- 2.3.1 QueryWrapper query:
- 2.3.2 UpdateWrapper update:
- 2.3.3 LambdaQueryWrapper query:
- 2.3. LambdaUpdateWrapper update:
- 3. Use of plug-ins:
-
- 3.1 Custom ID generation:
- 3.2 Logical deletion:
- 3.3 Autofill:
- 3.4 Execute sql printing:
-
- 3.4.1 Dependencies:
- 3.4.2 Driver modification:
- 3.4.3 Define py.properties configuration:
- 3.4.4 Optional idea SQL beautification plug-in:
- 3.5 Database security protection:
- 3.6 Database optimistic locking:
- 3.7 Code Generator:
-
- 3.7.1 jar in springboot web project:
- 3.7.2 Define business class generation:
- 3.7.3 Run business class generation:
- Summarize:
- refer to:
Foreword
After Mybatis-plus is introduced in the Spring-boot project, how should the data source be configured and how to quickly implement CRUD through the Mybatis-plus interface.
1. springBoot integrates mybatis-plus
1.1 maven introduces dependencies:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency>
1.2 Configure data source::
@Configuration public class MyConfig {<!-- --> @Bean public DataSource dataSource() {<!-- --> DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3406/mybatis?useUnicode=true & amp;characterEncoding=UTF-8 & amp;allowMultiQueries=true & amp;useAffectedRows=true & amp;useSSL=false & amp; zeroDateTimeBehavior=convertToNull & amp;serverTimezone=GMT+8"); dataSource.setUsername("root"); dataSource.setPassword("ddsoft"); return dataSource; } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) {<!-- --> return new JdbcTemplate(dataSource); } @Bean public DataSourceTransactionManager transactionManager(DataSource dataSource) {<!-- --> return new DataSourceTransactionManager(dataSource); } @Bean public JdbcTemplate jdbcTemplate() {<!-- --> return new JdbcTemplate(dataSource()); } @Bean public DataSourceTransactionManager transactionManager() {<!-- --> return new DataSourceTransactionManager(dataSource()); } }
2. Use:
2.1 mysql prints the executed sql settings:
# mysql log logging.level.root=info logging.level.com.example.springdabaihua=debug
tip: Dao layer uses Mapper’s crud, which usually starts with the name of sql’s crud; when service uses Tongcha, we will not directly reference the Mapper layer, but will use the service layer: service Tongchang has business names starting with listXxx, getXxx; these APIs are not here To expand, you can learn about it through the CRUD interface on the official website;
2.2 Paging query:
Define paging plug-in to intercept SQL:
/** * Add paging plugin */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() {<!-- --> MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//If you configure multiple plug-ins, remember to add pagination last //interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); If there are multiple data sources, you do not need to match the specific type. Otherwise, it is recommended to match the specific DbType. return interceptor; })
Pagination at the business layer:
@Test public void testPage1() {<!-- --> IPage<TbUser> ipage = new Page<TbUser>(1, 2); IPage<TbUser> page = userService.page(ipage); System.out.println(page.getRecords()); System.out.println(page.getPages()); System.out.println(page.getTotal()); }
Pagination of mapper layer:
@Test public void testPage2() {<!-- --> IPage<TbUser> ipage = new Page<TbUser>(1, 2); IPage<TbUser> page = userMapper.getPage(ipage);; System.out.println(page.getRecords()); System.out.println(page.getPages()); System.out.println(page.getTotal()); } public interface TbUserMapper extends BaseMapper<TbUser> {<!-- --> IPage<TbUser> getPage(IPage<?>page ); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.springdabaihua.mapper.TbUserMapper"> <select id="getPage" resultType="com.example.springdabaihua.entity.TbUser"> select * from tb_user </select> </mapper>
2.3 Conditional constructor:
2.3.1 QueryWrapper query:
@Test public void testWraaper1(){<!-- --> QueryWrapper<TbUser> wrapper = new QueryWrapper<>(); // select can limit column names wrapper.select("id","name").like("name","Zhang"); List<TbUser> list = userService.list(wrapper); list.stream().forEach(e->{<!-- --> System.out.println(e); }); }
2.3.2 UpdateWrapper update:
@Test public void testWraaper2() {<!-- --> UpdateWrapper<TbUser> wrapper = new UpdateWrapper<>(); wrapper.like("name", "1"); wrapper.set("name", "Zhao Liu"); userService.update(wrapper); }
2.3.3 LambdaQueryWrapper query:
Added use of Lambda expressions so you don’t have to receive incoming column names
@Test public void testWraaper3() {<!-- --> LambdaQueryWrapper<TbUser> wrapper = new LambdaQueryWrapper<>(); wrapper.select(TbUser::getId,TbUser::getName).like(TbUser::getName, "张"); List<TbUser> list = userService.list(wrapper); list.stream().forEach(e -> {<!-- --> System.out.println(e); }); }
2.3. LambdaUpdateWrapper update:
Added use of Lambda expressions so you don’t have to receive incoming column names
@Test public void testWraaper4() {<!-- --> LambdaUpdateWrapper<TbUser> wrapper = new LambdaUpdateWrapper<>(); wrapper.like(TbUser::getName, "Zhao"); wrapper.set(TbUser::getName,"Xiao Li Flying Knife"); userService.update(wrapper); }
3. Use of plug-ins:
3.1 Custom ID generation:
Define id generation: (For the setting of distributed globally unique id, you can refer to other articles in my blog and search for the keyword “distributed globally unique id” to view)
@Component public class CustomIdGenerator implements IdentifierGenerator {<!-- --> @Override public Long nextId(Object entity) {<!-- --> //You can use the full class name of the currently passed in class as bizKey, or extract parameters to generate bizKey for distributed Id call generation. String bizKey = entity.getClass().getName(); //Call distributed ID generation based on bizKey long id = ....; //Return the generated id value. return id; } }
Use a custom id defined in the entity class:
@TableId(value = "id", type = IdType.ASSIGN_ID) private Integer id;
3.2 Logical deletion:
Add logically deleted fields to mysql table
tip: Supports all data types (Integer, Boolean, LocalDateTime are recommended). If the database field uses datetime, the logical undeleted value and the deleted value can be configured as the string null, and the other value can be configured as Function to get the value such as now();
Single table fields can be set:
@TableLogic(value = "1",delval = "0") private Integer isDelete;
Global configuration is also possible:
mybatis-plus: global-config: db-config: logic-delete-field: flag # Entity field name for global logical deletion (since 3.3.0, you can ignore step 2 after configuration) logic-delete-value: 1 # Logical deleted value (default is 1) logic-not-delete-value: 0 # Logical not deleted value (default is 0)
tip: Only effective for automatically injected sql (the api results provided by mybatis-plus are not supported if you write your own xml):
- Insertion: No restrictions
- Search: Add a where condition to filter out deleted data. If you use the where condition generated by wrapper.entity, this field will also be automatically added.
- Update: Add a where condition to prevent updating to deleted data. If you use the where condition generated by wrapper.entity, this field will also be automatically added.
- Delete: Convert to Update
3.3 Autocomplete:
Fill creation, update person and time: annotation fill field @TableField(… fill = FieldFill.INSERT) The generator strategy part can also be configured
Custom padding:
@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()); // Starting version 3.3.0 (recommended) // or this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // Starting version 3.3.3 (recommended) // or this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // It can also be used (this method has a bug in 3.3.0) } @Override public void updateFill(MetaObject metaObject) {<!-- --> log.info("start update fill ...."); this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // Starting version 3.3.0 (recommended) // or this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // Starting version 3.3.3 (recommended) // or this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // It can also be used (this method has a bug in 3.3.0) } }
Filling time:
public enum FieldFill {<!-- --> /** * Not processed by default */ DEFAULT, /** * Insert filler fields */ INSERT, /** * Update fill fields */ UPDATE, /** * Insert and update populated fields */ INSERT_UPDATE }
3.4 Execute sql printing:
3.4.1 Dependencies:
<dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency>
3.4.2 Driver modification:
datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:h2:mem:test
tip: driver-class-name and url are added to p6spy
3.4.3 Define py.properties configuration:
#3.2.1 or above used modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory #3.2.1 The following is used or not configured. #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # Custom log printing logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #Log output to console appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # Use the log system to record sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # Set up p6spy driver agent deregisterdrivers=true # Cancel JDBC URL prefix useprefix=true # Configure the log exception. The result sets that can be removed include error, info, batch, debug, statement, commit, rollback, result, and resultsset. excludecategories=info,debug,result,commit,resultset # Date format dateformat=yyyy-MM-dd HH:mm:ss # There can be multiple actual drivers #driverlist=org.h2.Driver # Whether to enable slow SQL logging outagedetection=true # Slow SQL logging standard 2 seconds outagedetectioninterval=2
Effect:
3.4.4 Optional idea SQL beautification plug-in:
After beautification, the printed SQL will be formatted and displayed instead of only displayed on one line;
Attention!
- driver-class-name is the driver class provided by p6spy
- The url prefix is jdbc:p6spy followed by a colon as the corresponding database connection address.
- Print out sql as null, add commit in excludecategories
- Batch operation does not print sql, remove batch in excludecategories
- For batch operation printing duplicate problems, please use MybatisPlusLogFactory (new in 3.2.1)
- This plug-in has performance losses and is not recommended for use in production environments.
3.5 Database security protection:
Encrypt sensitive data, such as mysql connection, user name, password, etc.:
public static void main(String[] args) {<!-- --> // Generate a 16-bit random AES key String randomKey = AES.generateRandomKey(); System.out.println("randomKey = " + randomKey); // Random key encryption String result = AES.encrypt("root", randomKey); System.out.println(result); }
The obtained randomKey is the encryption key, and sensitive data can be encrypted by changing the secret key;
Effect:
Complete the decryption of the customized ciphertext content when the project is started:
Define the attribute value annotation to be decrypted: DecryptedValue
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface DecryptedValue {<!-- --> String value() default ""; }
Add the DecryptedValue annotation to the field to be encrypted:
@Value("${spring.datasource.username}") @DecryptedValue("spring.datasource.username") private String userName; @Value("${spring.datasource.password}") @DecryptedValue("spring.datasource.password") private String passWord;
Define decryption class: DataSourceDecryptionProcessor
import com.baomidou.mybatisplus.core.toolkit.AES; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.lang.reflect.Field; @Component public class DataSourceDecryptionProcessor implements BeanPostProcessor {<!-- --> @Value("${spring.datasource.key:xxxx}") private String dataSourceKey; private final Environment environment; public DataSourceDecryptionProcessor(Environment environment) {<!-- --> this.environment = environment; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {<!-- --> Class<?> clazz = bean.getClass(); do {<!-- --> for (Field field : clazz.getDeclaredFields()) {<!-- --> DecryptedValue decryptedValueAnnotation = AnnotationUtils.findAnnotation(field, DecryptedValue.class); if (decryptedValueAnnotation != null) {<!-- --> String propertyValue = environment.getProperty(decryptedValueAnnotation.value()); if (!StringUtils.isEmpty(propertyValue)) {<!-- --> field.setAccessible(true); try {<!-- --> //Decrypt the encrypted value String value = AES.decrypt(propertyValue, dataSourceKey); field.set(bean, value); } catch (IllegalAccessException e) {<!-- --> e.printStackTrace(); } } } } clazz = clazz.getSuperclass(); } while (clazz != null); return bean; } }
3.6 Database Optimistic Locking:
Add plugin:
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() {<!-- --> MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }
Add the @Version annotation to the fields of the entity class:
@Version private Integer version;
Add version int type field to the table;
Test 1:
@Test public void testLock() throws InterruptedException {<!-- --> CountDownLatch countDownLatch =new CountDownLatch(2); for (int i = 0; i < 2; i + + ) {<!-- --> int finalI = i; new Thread(()->{<!-- --> TbUser byId = userService.getById(860069891); byId.setName("lisi" + finalI); try {<!-- --> Thread.sleep(2000); } catch (InterruptedException e) {<!-- --> countDownLatch.countDown(); throw new RuntimeException(e); } userService.updateById(byId); countDownLatch.countDown(); },"thread" + i).start(); } countDownLatch.await(); }
Test 2:
@Test public void testLock1() throws InterruptedException {<!-- --> TbUser byId1 = userService.getById(860069891); TbUser byId2 = userService.getById(860069891); byId1.setName("Zhang Fei1"); System.out.println("userService.updateById(byId1) = " + userService.updateById(byId1)); byId2.setName("Zhao Yun1"); System.out.println("userService.updateById(byId2) = " + userService.updateById(byId2)); }
3.7 Code Generator:
Used to quickly generate entities, services, mappers and controllers to facilitate business development;
3.7.1 jar in springboot web project:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.1</version> </dependency> <!--Code Generator--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.1.1</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.28</version> </dependency> <!--Code Generator--> </dependencies>
3.7.2 Define business class generation:
package com.example.mybatiscodegenerage; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.InjectionConfig; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class GenerateCode {<!-- --> /** * <p> * Read the console content *</p> */ public static String scanner(String tip) {<!-- --> Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("Please enter" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) {<!-- --> String ipt = scanner.next(); if (StringUtils.hasText(ipt)) {<!-- --> return ipt; } } throw new MybatisPlusException("Please enter the correct one" + tip + "!"); } public static void main(String[] args) {<!-- --> // Code generator AutoGenerator mpg = new AutoGenerator(); //Global configuration GlobalConfig gc = new GlobalConfig(); // Get the current project path String projectPath = System.getProperty("user.dir"); //output directory gc.setOutputDir(projectPath + "/spring-batch/src/main/java"); //Set author gc.setAuthor("jobob"); // Do you need to open the folder after the code is generated? gc.setOpen(false); // Whether to generate Swagger2 annotations // gc.setSwagger2(true); Entity attribute Swagger2 annotation // Whether to generate a BaseResultMap mapping all fields in xml //gc.setBaseResultMap(true); //The same file generates overwrite gc.setFileOverride(true); // Time format for generating code gc.setDateType(DateType.ONLY_DATE); // https://baomidou.com/pages/061573/#datetype // Use the name of the table to directly generate entities, %s table name does not add the Entity suffix // gc.setEntityName("%s"); // Mapper interface name %sMapper table name + Mapper // gc.setMapperName("%sMapper"); // Mapper.xml generates file name, table name + Mapper.xml // gc.setXmlName("%sMapper"); //Service interface name table name + Service // gc.setServiceName("%sService") //Service interface implementation class name table name + ImplService // gc.setServiceImplName("%sImplService"); //Set to global configuration mpg.setGlobalConfig(gc); //Data source configuration DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3406/mybatis?useUnicode=true & amp;characterEncoding=UTF-8 & amp;allowMultiQueries=true & amp;useAffectedRows=true & amp;useSSL=false & amp; zeroDateTimeBehavior=convertToNull & amp;serverTimezone=GMT+8"); // dsc.setSchemaName("public"); // dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("ddsoft"); mpg.setDataSource(dsc); // package configuration PackageConfig pc = new PackageConfig(); // For example, com.xxx.xxx.user, com.xxx.xxx is the package name, user is the module name //Set module name pc.setModuleName(scanner("module name")); //Set package name pc.setParent("com.baomidou.ant"); mpg.setPackageInfo(pc); // Custom configuration Custom properties InjectionConfig cfg = new InjectionConfig() {<!-- --> @Override public void initMap() {<!-- --> // to do nothing } }; // If the template engine is freemarker String templatePath = "/templates/mapper.xml.ftl"; // If the template engine is velocity // String templatePath = "/templates/mapper.xml.vm"; // Custom output configuration List<FileOutConfig> focList = new ArrayList<>(); // Custom configuration will be output first mapper.xml file location settings focList.add(new FileOutConfig(templatePath) {<!-- --> @Override public String outputFile(TableInfo tableInfo) {<!-- --> // Customize the output file name. If your Entity sets the prefix and suffix, please note that the name of the xml will change accordingly! ! return projectPath + "/spring-batch/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); /* cfg.setFileCreate(new IFileCreate() { @Override public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) { // Determine whether a custom folder needs to be created checkDir("Directory created by calling the default method, use " for custom directories); if (fileType == FileType.MAPPER) { // The mapper file has been generated and is judged to exist. If you do not want to regenerate it, return false. return !new File(filePath).exists(); } //Allow template file generation return true; } }); */ //Set custom configuration cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); //Configure template TemplateConfig templateConfig = new TemplateConfig(); //Configure custom output template //Specify the custom template path, be careful not to bring .ftl/.vm, it will be automatically recognized based on the template engine used. // templateConfig.setEntity("templates/entity2.java"); // templateConfig.setService(); // templateConfig.setController(); // Invalidate the existing xml configuration generated at the code level templateConfig.setXml(null); mpg.setTemplate(templateConfig); //Strategy configuration, database table configuration StrategyConfig strategy = new StrategyConfig(); //Naming strategy for database tables mapped to entities, underline to camel case strategy.setNaming(NamingStrategy.underline_to_camel); //Naming strategy for mapping database table fields to entity classes strategy.setColumnNaming(NamingStrategy.underline_to_camel); //Customize the inherited entity class. Adding this one will inherit the entity when generating the entity class. //strategy.setSuperEntityClass("com.wy.testCodeGenerator.entity"); //Whether the entity is a lombok model strategy.setEntityLombokModel(true); //Generate @RestController controller strategy.setRestControllerStyle(true); //Public parent class Controller whether it has a parent class // strategy.setSuperControllerClass("Your own parent class controller, no need to set it if you don’t have one!"); //Public fields written in the parent class // strategy.setSuperEntityColumns("id"); // If you want to generate according to the prefix setTablePrefix("pms_") // strategy.setTablePrefix("pms_"); strategy.setInclude(scanner("Table name, multiple English commas separated").split(",")); // Camel case transfer string mapper path setting in RestController. If set, the path to the table name will be generated, such as pmp_user strategy.setControllerMappingHyphenStyle(true); //Table prefix such as pms_xxx The generated class does not include pms // strategy.setTablePrefix( "pms"); strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); } }
3.7.3 Run business class generation:
Summary:
This article organizes the commonly used CRUD APIs and commonly used plug-ins of Spring-boot Mybatis-plus.
Reference:
Mybatis-plus official website;