SpringBoot整合Druid+全局事務管理+Mybatis-Plus+代碼生成器

SpringBoot整合Druid+全局事務管理+Mybatis-Plus+代碼生成器

在springboot開發當中,Druid,全局事務管理,代碼生成器都是非常實用的,特此記錄下整合的過程

整合Druid連接池

springboot默認的連接池是:HikariCP,但是Druid的功能相對來說比較全面。
數據庫連接池瞭解和常用連接池對比
Druid連接池官網

第一步:引入相關JAR

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

第二步:配置相關參數

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      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
      # 連接池的配置信息
      # 初始化大小,最小,最大
      initial-size: 5
      min-idle: 5
      maxActive: 20
      # 配置獲取連接等待超時的時間
      maxWait: 60000
      # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一個連接在池中最小生存的時間,單位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # 打開PSCache,並且指定每個連接上PSCache的大小
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      # 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
      filters: stat,wall,slf4j,config
      # 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
      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可視化界面
        login-password: 123456 # 登錄密碼
        reset-enable: false
        allow: ""  # 白名單 表示所有
        deny: 192.168.1.12 # 黑名單

第三步:在瀏覽器當中輸入:http://127.0.0.1:8080/druid/index.html 即可進入可視化界面

全局事務管理器

springboot當中添加事務直接使用註解@Transactional 即可,但是每個方法都要添加比較麻煩,可以直接通過切面的方式添加一個全局的事務管理器。注意事項是,要注意方法名開頭的問題

@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {

    /**
     * 配置全局事務的切點爲service層的所有方法  AOP切面表達式 可參考(https://blog.csdn.net/ycf921244819/article/details/106599489)
     * TODO 設置service層所在位置
     */
    private static final String AOP_POINTCUT_EXPRESSION = "execution (* cn.hjljy.fastboot..*.service..*.*(..))";

    /**
     * 注入事務管理器
     */
    @Autowired
    private TransactionManager transactionManager;


    /**
     * 配置事務攔截器
     */
    @Bean
    public TransactionInterceptor txAdvice() {

        RuleBasedTransactionAttribute txAttrRequired = new RuleBasedTransactionAttribute();
        txAttrRequired.setName("REQUIRED事務");
        //設置事務傳播機制,默認是PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務
        txAttrRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //設置異常回滾爲Exception  默認是RuntimeException
        List<RollbackRuleAttribute> rollbackRuleAttributes = new ArrayList<>();
        rollbackRuleAttributes.add(new RollbackRuleAttribute(Exception.class));
        txAttrRequired.setRollbackRules(rollbackRuleAttributes);

        RuleBasedTransactionAttribute txAttrRequiredReadOnly = new RuleBasedTransactionAttribute();
        txAttrRequiredReadOnly.setName("SUPPORTS事務");
        //設置事務傳播機制,PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行
        txAttrRequiredReadOnly.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
        //設置異常回滾爲Exception  默認是RuntimeException
        txAttrRequiredReadOnly.setRollbackRules(rollbackRuleAttributes);
        txAttrRequiredReadOnly.setReadOnly(true);

        /*事務管理規則,聲明具備事務管理的方法名*/
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        //方法名規則限制,必須以下列開頭纔會加入事務管理當中
        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);

        //對於查詢方法,根據實際情況添加事務管理 可能存在查詢多個數據時,已查詢出來的數據剛好被改變的情況
        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);
    }
    
    /**
     *  設置切面
     */
    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}

整合Mybatis-Plus

第一步:引入JAR包

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.2</version>
    </dependency>

第二步:添加配置信息

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml  #xml所在位置  不設置默認是在mapper類同級
  configuration:
    mapUnderscoreToCamelCase: true  # 開啓駝峯匹配  默認爲true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印sql語句和入參數據
  global-config:
    db-config:
      logic-delete-value: 1 #邏輯刪除  配合@TableLogic註解
      logic-not-delete-value: 0 #邏輯不刪除
      update-strategy: not_null # 更新時字段如果爲null,就不進行更新該字段。
      insert-strategy: not_null # 插入時如果字段爲null,就不插入數據,建議數據庫表字段設置默認值

第三步:添加分頁和mapper掃描

@Configuration
@MapperScan("cn.hjljy.fastboot.mapper")
public class MybatisPlusConfiguration {
    /**
     * mybatis-plus分頁插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor page = new PaginationInterceptor();
        //設置分頁數據庫類型
        page.setDbType(DbType.MYSQL);
        page.setDialect(new MySqlDialect());
        //優化count sql
        page.setCountSqlParser(new JsqlParserCountOptimize(true));
        //設置每頁最大值
        page.setLimit(999L);
        return page;
    }
}

第四步:創建一個Mapper類繼承BaseMapper,就可以簡單使用了。
可以參考官方文檔入門:https://mp.baomidou.com/guide/quick-start.html

整合代碼生成器

AutoGenerator 是 MyBatis-Plus 的代碼生成器,通過 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提升了開發效率

考慮到dto和po在大部分情況下字段都是一樣的,官方未提供DTO,所以可以拷貝一份entity.java.vm修改爲dto.java.vm放在resources目錄下面。然後根據自定義提示進行修改。
具體結果如下:

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對象", 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 字段循環遍歷  ----------
#foreach($field in ${table.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})
## 主鍵
  #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
## 普通字段
#elseif(${field.fill})
## -----   存在字段填充設置   -----
  #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
## 樂觀鎖註解
#if(${versionFieldName}==${field.name})
    @Version
#end
## 邏輯刪除註解
#if(${logicDeleteFieldName}==${field.name})
    @TableLogic
#end
    private ${field.propertyType} ${field.propertyName};
#end
## ----------  END 字段循環遍歷  ----------

#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() {
  #if(${keyPropertyName})
        return this.${keyPropertyName};
  #else
        return null;
  #end
    }

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

具體代碼生成器的執行代碼如下:

public class CodeGenerator {

    public static void main(String[] args) {
        // 代碼生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("海加爾金鷹(www.hjljy.cn)");
        gc.setOpen(false);
        //設置實體類後綴
        gc.setEntityName("%sPo");
        //實體屬性 Swagger2 註解
        gc.setSwagger2(true);
        gc.setBaseColumnList(true);
        gc.setBaseResultMap(true);
        mpg.setGlobalConfig(gc);

        // 數據源配置
        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);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(null);
        String scanner = scanner("請輸入整體業務包名");
        String modelName = StringUtils.isBlank(scanner) ? "" : "."+scanner;
        //moduleName是整體分模塊

        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";
        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 不輸出默認的XML 默認生成的xml在mapper層裏面
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        //配置自定義輸出的文件   xml和dto
        //模板引擎是 velocity
        String xmlTemplatePath = "/templates/mapper.xml.vm";
        // 自定義輸出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義配置會被優先輸出
        focList.add(new FileOutConfig(xmlTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸出文件名 , 如果你 Entity 設置了前後綴、此處注意 xml 的名稱會跟着發生變化!!
                return projectPath + "/src/main/resources/mapper/" + scanner
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        String dtoTemplatePath = "/dto.java.vm";
        // 自定義配置會被優先輸出
        focList.add(new FileOutConfig(dtoTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸出文件名 , 如果你 Entity 設置了前後綴、此處注意 xml 的名稱會跟着發生變化!!
                return projectPath + "/src/main/java/cn/hjljy/fastboot/pojo/"+scanner+"/dto/" +
                        tableInfo.getEntityName() + "Dto" + StringPool.DOT_JAVA;
            }
        });

        // 自定義配置
        InjectionConfig cfg = new InjectionConfig() {

            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("dtoPackage", dtoPath);
                this.setMap(map);
            }
        };
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        // 策略配置
        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("表名,多個英文逗號分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        //設置邏輯刪除字段
        strategy.setLogicDeleteFieldName("status");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new VelocityTemplateEngine());
        mpg.execute();
    }

    /**
     * <p>
     * 讀取控制檯內容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("請輸入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("請輸入正確的" + tip + "!");
    }
}

總結

算是框架裏面非常基礎的一些東西。不過能夠提高不少的開發效率!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章