@[TOC]

SpringBoot integration Druid+ Global transaction management +Mybatis-Plus+ code generator

Druid, global transaction management, and code generators are all very useful in SpringBoot development

Integrate Druid connection pool

Springboot’s default connection pool is HikariCP, but Druid’s capabilities are relatively comprehensive. Druid connection pooling

Step 1: Introduce the relevant JARS

< the dependency > < groupId > com. Alibaba < / groupId > < artifactId > druid - spring - the boot - starter < / artifactId > < version > 1.1.22 < / version >  </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>Copy the code

Step 2: Configure related parameters

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      name: Data source name
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password: 123456
      url: JDBC: mysql: / / 127.0.0.1:3306 / springboot? characterEncoding=utf8&useSSL=false
      Connection pool configuration information
      # initialize size, min, Max
      initial-size: 5
      min-idle: 5
      maxActive: 20
      Set the connection wait timeout
      maxWait: 60000
      Configure how often to detect idle connections that need to be closed, in milliseconds
      timeBetweenEvictionRunsMillis: 60000
      Set the minimum time for a connection to live in the pool in milliseconds
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      Turn on PSCache and specify the size of PSCache on each connection
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      # Configure the filters for monitoring statistics interception. After removing the filters, the MONITORING interface SQL cannot be counted. 'wall' is used for the firewall
      filters: stat,wall,slf4j,config
      Enable mergeSql via connectProperties; Slow SQL record
      connectionProperties: druid.stat.mergeSql\=true; druid.stat.slowSqlMillis\=5000
      web-stat-filter:
        enabled: true
        url-pattern: "/"
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      stat-view-servlet:
        enabled: true
        url-pattern: "/druid/*"
        login-username: admin  Druid allows users to access the Druid visual interface without having to log in
        login-password: 123456 # login password
        reset-enable: false
        allow: ""  # whitelist indicates all
        deny: 192.1681.12. # blacklist
Copy the code

Step 3: in the middle of the browser input: http://127.0.0.1:8080/druid/index.html can enter the visual interface

Global transaction manager

Adding transactions to SpringBoot can be done directly using the @Transactional annotation, but it is more difficult to add transactions to each method. You can add a global transaction manager directly by cutting the Transactional annotation. The caveat is to pay attention to the problem at the beginning of the method name

@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {

    / * * * configuration of global transaction point of contact for all methods of the service layer AOP aspects expression (https://blog.csdn.net/ycf921244819/article/details/106599489) * TODO for reference Set the location of the service layer */
    private static final String AOP_POINTCUT_EXPRESSION = "execution (* cn.hjljy.fastboot.. *.service.. *. * (..) )";

    /** * inject transaction manager */
    @Autowired
    private TransactionManager transactionManager;


    /** * Configure transaction interceptor */
    @Bean
    public TransactionInterceptor txAdvice(a) {

        RuleBasedTransactionAttribute txAttrRequired = new RuleBasedTransactionAttribute();
        txAttrRequired.setName("The REQUIRED transaction");
        // Set the transaction propagation mechanism, PROPAGATION_REQUIRED: If a transaction currently exists, join it; If there is no transaction currently, a new transaction is created
        txAttrRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // Set the Exception rollback to Exception the default is RuntimeException
        List<RollbackRuleAttribute> rollbackRuleAttributes = new ArrayList<>();
        rollbackRuleAttributes.add(new RollbackRuleAttribute(Exception.class));
        txAttrRequired.setRollbackRules(rollbackRuleAttributes);

        RuleBasedTransactionAttribute txAttrRequiredReadOnly = new RuleBasedTransactionAttribute();
        txAttrRequiredReadOnly.setName("The SUPPORTS transaction");
        // Set the transaction propagation mechanism, PROPAGATION_SUPPORTS: If a transaction currently exists, join it; If there is no transaction currently, it continues in a non-transactional manner
        txAttrRequiredReadOnly.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
        // Set the Exception rollback to Exception the default is RuntimeException
        txAttrRequiredReadOnly.setRollbackRules(rollbackRuleAttributes);
        txAttrRequiredReadOnly.setReadOnly(true);

        /* Transaction management rule, which declares the name of the method with transaction management */
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        // The method name must start with the following before it is added to transaction management
        source.addTransactionalMethod("add*", txAttrRequired);
        source.addTransactionalMethod("save*", txAttrRequired);
        source.addTransactionalMethod("create*", txAttrRequired);
        source.addTransactionalMethod("insert*", txAttrRequired);
        source.addTransactionalMethod("submit*", txAttrRequired);
        source.addTransactionalMethod("del*", txAttrRequired);
        source.addTransactionalMethod("remove*", txAttrRequired);
        source.addTransactionalMethod("update*", txAttrRequired);
        source.addTransactionalMethod("exec*", txAttrRequired);
        source.addTransactionalMethod("set*", txAttrRequired);

        // For the query method, add transaction management according to the actual situation
        source.addTransactionalMethod("get*", txAttrRequiredReadOnly);
        source.addTransactionalMethod("select*", txAttrRequiredReadOnly);
        source.addTransactionalMethod("query*", txAttrRequiredReadOnly);
        source.addTransactionalMethod("find*", txAttrRequiredReadOnly);
        source.addTransactionalMethod("list*", txAttrRequiredReadOnly);
        source.addTransactionalMethod("count*", txAttrRequiredReadOnly);
        source.addTransactionalMethod("is*", txAttrRequiredReadOnly);
        return new TransactionInterceptor(transactionManager, source);
    }
    
    /** * sets the section */
    @Bean
    public Advisor txAdviceAdvisor(a) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return newDefaultPointcutAdvisor(pointcut, txAdvice()); }}Copy the code

Integration of Mybatis – Plus

Step 1: Introduce the JAR package

< the dependency > < groupId > com. Baomidou < / groupId > < artifactId > mybatis - plus - the boot - starter < / artifactId > < version > 3.3.2 rainfall distribution on 10-12 < / version >  </dependency>Copy the code

Step 2: Add configuration information

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml  The default location of XML is the mapper class sibling
  configuration:
    mapUnderscoreToCamelCase: true  # Enable Hump matching Default is true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  Print the SQL statement and input parameter data
  global-config:
    db-config:
      logic-delete-value: 1 # Logical delete with @tablelogic annotation
      logic-not-delete-value: 0 # Logic does not delete
      update-strategy: not_null If a field is null, the field will not be updated.
      insert-strategy: not_null Mysql > insert table (s) into table (s)
Copy the code

Step 3: Add paging and Mapper scanning

@Configuration
@MapperScan("cn.hjljy.fastboot.mapper")
public class MybatisPlusConfiguration {
    /** * Mybatis -plus paging plugin */
    @Bean
    public PaginationInterceptor paginationInterceptor(a) {
        PaginationInterceptor page = new PaginationInterceptor();
        // Set the paging database type
        page.setDbType(DbType.MYSQL);
        page.setDialect(new MySqlDialect());
        // Optimize count SQL
        page.setCountSqlParser(new JsqlParserCountOptimize(true));
        // Set the maximum value per page
        page.setLimit(999L);
        returnpage; }}Copy the code

Step 4: Create a Mapper class derived from BaseMapper, which is easy to use. Can refer to the official documentation introduction: mp.baomidou.com/guide/quick…

Integrated code generator

AutoGenerator is the code generator of MyBatis-Plus, through which the code of Entity, Mapper, Mapper XML, Service, Controller and other modules can be generated quickly. Greatly improved the development efficiency

Since the dTO and Po fields are the same in most cases, the DTO is not officially provided, so you can make a copy of entity.java.vm and change it to to.java.vm under the Resources directory. Then follow the custom prompts to modify. The specific results are as follows:

package$! {cfg.dtoPackage}; #foreach($pkg in ${table.importPackages})import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
#if(${entityLombokModel})
import lombok.Data;
import lombok.EqualsAndHashCode;
#if(${chainModel})
import lombok.experimental.Accessors;
#end
#end

/** * <p> * $! {table.comment} * </p> * *@author ${author}
 * @since ${date}
 */
#if(${entityLombokModel})
@Data
  #if(${superEntityClass})
@EqualsAndHashCode(callSuper = true)
  #else
@EqualsAndHashCode(callSuper = false)
  #end
  #if(${chainModel})
@Accessors(chain = true)
  #end
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@apiModel (value="${entity}Dto object ", description="$! {table.comment}")
#end
#if(${superEntityClass})
public class ${entity}Dto extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity}Dto extends Model<${entity}> {
#else
public class ${entity}Dto implements Serializable {
#end

#if(${entitySerialVersionUID})
    private static final long serialVersionUID=1L; # end # # -- -- -- -- -- -- -- -- -- -- BEGIN field to iterate over -- -- -- -- -- -- -- -- -- -- # foreach ($field in ${table. The fields}) #if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!!! field.comment"! ="")
  #if(${swagger2})
    @ApiModelProperty(value = "${field.comment}")
  #else
    /** * ${field.comment} */
  #end
#end
#if(${field.keyflag}) ## primary key #if(${field.keyIdentityFlag})
    @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)#elseif(! $null.isNull(${idType}) && "$!!! idType"! ="")
    @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
  #elseif(${field.convert})
    @TableId("${field.annotationColumnName}")# end # # # ordinary field elseif (${field. The fill}) # # -- -- -- -- -- is the fields Settings -- -- -- -- - #if(${field.convert})
    @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
  #else
    @TableField(fill = FieldFill.${field.fill})
  #end
#elseif(${field.convert})
    @TableField("${field.annotationColumnName}")#end ## Optimistic lock annotation #if(${versionFieldName}==${field.name})
    @Version#end ## Remove comment # logicallyif(${logicDeleteFieldName}==${field.name})
    @TableLogic
#end
    private${field.propertyType} ${field.propertyName}; The #end ## ---------- end field loops over ---------- #if(! ${entityLombokModel}) #foreach($field in ${table.fields}) #if(${field.propertyType.equals("boolean")})
    #set($getprefix="is")
  #else
    #set($getprefix="get")
  #end

    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

  #if(${chainModel})
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
  #else
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
  #end
        this.${field.propertyName} = ${field.propertyName};
  #if(${chainModel})
        return this;
  #end
    }
#end
## --foreach end---
#end
## --end of #if(! ${entityLombokModel})-- #if(${entityColumnConstant})
  #foreach($field in ${table.fields})
    public static final String ${field.name.toUpperCase()} = "${field.name}";

  #end
#end
#if(${activeRecord})
    @Override
    protected Serializable pkVal(a) {#if(${keyPropertyName})
        return this.${keyPropertyName};
  #else
        return null;
  #end
    }

#end
#if(! ${entityLombokModel})@Override
    public String toString(a) {
        return "${entity}{" +
  #foreach($field in ${table.fields})
    #if($! {foreach.index}==0)
        "${field.propertyName}=" + ${field.propertyName} +
    #else
        ", ${field.propertyName}=" + ${field.propertyName} +
    #end
  #end
        "}";
    }
#end
}
Copy the code

The execution code of the specific code generator is as follows:

public class CodeGenerator {

    public static void main(String[] args) {
        // Code generator
        AutoGenerator mpg = new AutoGenerator();

        // Global configuration
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("Haijal Golden Eagle (www.hjljy.cn)");
        gc.setOpen(false);
        // Set the entity class suffix
        gc.setEntityName("%sPo");
        // Entity attribute Swagger2 annotation
        gc.setSwagger2(true);
        gc.setBaseColumnList(true);
        gc.setBaseResultMap(true);
        mpg.setGlobalConfig(gc);

        // Data source configuration
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/springboot? serverTimezone=GMT&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);

        / / package configuration
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(null);
        String scanner = scanner("Please enter the overall business package name");
        String modelName = StringUtils.isBlank(scanner) ? "" : "."+scanner;
        //moduleName is an integral module

        pc.setParent("cn.hjljy.fastboot");
        pc.setMapper("mapper"+modelName);
        pc.setService("service"+modelName);
        pc.setServiceImpl("service"+modelName+".impl");
        pc.setEntity("pojo"+modelName+".po");
        pc.setController("controller"+modelName);
        mpg.setPackageInfo(pc);


        String dtoPath = pc.getParent() + ".pojo.dto";
        // Configure the template
        TemplateConfig templateConfig = new TemplateConfig();
        The default generated XML is in the mapper layer
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // Configure the custom output file XML and DTO
        // The template engine is Velocity
        String xmlTemplatePath = "/templates/mapper.xml.vm";
        // Customize the output configuration
        List<FileOutConfig> focList = new ArrayList<>();
        // Custom configurations are printed first
        focList.add(new FileOutConfig(xmlTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // Customize the output file name. If your Entity has a prefix or suffix, note that the XML name will change accordingly!!
                return projectPath + "/src/main/resources/mapper/" + scanner
                        + "/" + tableInfo.getEntityName() + "Mapper"+ StringPool.DOT_XML; }}); String dtoTemplatePath ="/dto.java.vm";
        // Custom configurations are printed first
        focList.add(new FileOutConfig(dtoTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // Customize the output file name. If your Entity has a prefix or suffix, note that the XML name will change accordingly!!
                return projectPath + "/src/main/java/cn/hjljy/fastboot/pojo/"+scanner+"/dto/" +
                        tableInfo.getEntityName() + "Dto"+ StringPool.DOT_JAVA; }});// Custom configuration
        InjectionConfig cfg = new InjectionConfig() {

            @Override
            public void initMap(a) {
                Map<String, Object> map = new HashMap<>();
                map.put("dtoPackage", dtoPath);
                this.setMap(map); }}; cfg.setFileOutConfigList(focList); mpg.setCfg(cfg);// Policy configuration
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude(scanner("Table name, separated by multiple Commas").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        // Set the logical delete field
        strategy.setLogicDeleteFieldName("status");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new VelocityTemplateEngine());
        mpg.execute();
    }

    /** * 

* read the console contents *

*/
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.isNotEmpty(ipt)) { returnipt; }}throw new MybatisPlusException("Please enter the correct one" + tip + "!"); }}Copy the code

conclusion

It’s a very basic thing in the framework. But can improve a lot of development efficiency!!