最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis Generator Plugin悲觀鎖實現(xiàn)示例

 更新時間:2021年09月28日 08:40:20   作者:raledong  
本文將從悲觀鎖為例,讓你快速了解如何實現(xiàn)Mybatis Generator Plugin。文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

Mybatis Generator插件可以快速的實現(xiàn)基礎的數據庫CRUD操作,它同時支持JAVA語言和Kotlin語言,將程序員從重復的Mapper和Dao層代碼編寫中釋放出來。Mybatis Generator可以自動生成大部分的SQL代碼,如update,updateSelectively,insert,insertSelectively,select語句等。但是,當程序中需要SQL不在自動生成的SQL范圍內時,就需要使用自定義Mapper來實現(xiàn),即手動編寫DAO層和Mapper文件(這里有一個小坑,當數據庫實體增加字段時,對應的自定義Mapper也要及時手動更新)。拋開復雜的定制化SQL如join,group by等,其實還是有一些比較常用的SQL在基礎的Mybatis Generator工具中沒有自動生成,比如分頁能力,悲觀鎖,樂觀鎖等,而Mybatis Generator也為這些訴求提供了Plugin的能力。通過自定義實現(xiàn)Plugin可以改變Mybatis Generator在生成Mapper和Dao文件時的行為。本文將從悲觀鎖為例,讓你快速了解如何實現(xiàn)Mybatis Generator Plugin。

實現(xiàn)背景:

  • 數據庫:MYSQL
  • mybatis generator runtime:MyBatis3

實現(xiàn)Mybatis悲觀鎖

當業(yè)務出現(xiàn)需要保證強一致的場景時,可以通過在事務中對數據行上悲觀鎖后再進行操作來實現(xiàn),這就是經典的”一鎖二判三更新“。在交易或是支付系統(tǒng)中,這種訴求非常普遍。Mysql提供了Select...For Update語句來實現(xiàn)對數據行上悲觀鎖。本文將不對Select...For Update進行詳細的介紹,有興趣的同學可以查看其它文章深入了解。

Mybatis Generator Plugin為這種具有通用性的SQL提供了很好的支持。通過繼承org.mybatis.generator.api.PluginAdapter類即可自定義SQL生成邏輯并在在配置文件中使用。PluginAdapter是Plugin接口的實現(xiàn)類,提供了Plugin的默認實現(xiàn),本文將介紹其中比較重要的幾個方法:

public interface Plugin {
    /**
    * 將Mybatis Generator配置文件中的上下文信息傳遞到Plugin實現(xiàn)類中
    * 這些信息包括數據庫鏈接,類型映射配置等
    */
    void setContext(Context context);

    /**
    * 配置文件中的所有properties標簽
    **/
    void setProperties(Properties properties);

    /**
    * 校驗該Plugin是否執(zhí)行,如果返回false,則該插件不會執(zhí)行
    **/
    boolean validate(List<String> warnings);

    /**
    * 當DAO文件完成生成后會觸發(fā)該方法,可以通過實現(xiàn)該方法在DAO文件中新增方法或屬性
    **/
    boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass,
            IntrospectedTable introspectedTable);

    /**
    * 當SQL XML 文件生成后會調用該方法,可以通過實現(xiàn)該方法在MAPPER XML文件中新增XML定義
    **/
    boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable);
}

這里結合Mybatis Generator的配置文件和生成的DAO(也稱為Client文件)和Mapper XML文件可以更好的理解。Mybatis Generator配置文件樣例如下,其中包含了主要的一些配置信息,如用于描述數據庫鏈接的<jdbcConnection>標簽,用于定義數據庫和Java類型轉換的<javaTypeResolver>標簽等。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
  <classPathEntry location="/Program Files/IBM/SQLLIB/java/db2java.zip" />

  <context id="DB2Tables" targetRuntime="MyBatis3">
    <jdbcConnection driverClass="COM.ibm.db2.jdbc.app.DB2Driver"
        connectionURL="jdbc:db2:TEST"
        userId="db2admin"
        password="db2admin">
    </jdbcConnection>

    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

    <javaModelGenerator targetPackage="test.model" targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <sqlMapGenerator targetPackage="test.xml"  targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

    <javaClientGenerator type="XMLMAPPER" targetPackage="test.dao"  targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

    <property name="printLog" value="true"/>

    <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
      <property name="useActualColumnNames" value="true"/>
      <generatedKey column="ID" sqlStatement="DB2" identity="true" />
      <columnOverride column="DATE_FIELD" property="startDate" />
      <ignoreColumn column="FRED" />
      <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
    </table>

  </context>
</generatorConfiguration>

這些都被映射成Context對象,并通過setContext(Context context)方法傳遞到具體的Plugin實現(xiàn)中:

public class Context extends PropertyHolder{

    /**
    * <context>標簽的id屬性
    */
    private String id;

    /**
    * jdbc鏈接信息,對應<jdbcConnection>標簽中的信息
    */
    private JDBCConnectionConfiguration jdbcConnectionConfiguration;

    /**
    * 類型映射配置,對應<javaTypeResolver>
    */
    private JavaTypeResolverConfiguration javaTypeResolverConfiguration;

    /**
    * ...其它標簽對應的配置信息
    */
}

setProperties則將context下的<properties>標簽收集起來并映射成Properties類,它實際上是一個Map容器,正如Properties類本身就繼承了Hashtable。以上文中的配置文件為例,可以通過properties.get("printLog")獲得值"true"。

validate方法則代表了這個Plugin是否執(zhí)行,它通常進行一些非?;A的校驗,比如是否兼容對應的數據庫驅動或者是Mybatis版本:

    public boolean validate(List<String> warnings) {
        if (StringUtility.stringHasValue(this.getContext().getTargetRuntime()) && !"MyBatis3".equalsIgnoreCase(this.getContext().getTargetRuntime())) {
            logger.warn("itfsw:插件" + this.getClass().getTypeName() + "要求運行targetRuntime必須為MyBatis3!");
            return false;
        } else {
            return true;
        }
    }

如果validate方法返回false,則無論什么場景下都不會運行這個Plugin。

接著是最重要的兩個方法,分別是用于在DAO中生成新的方法clientGenerated和在XML文件中生成新的SQL sqlMapDocumentGenerated。

先說clientGenerated,這個方法共有三個參數,interfaze是當前已經生成的客戶端Dao接口,topLevelClass是指生成的實現(xiàn)類,這個類可能為空,introspectedTable是指當前處理的數據表,這里包含了從數據庫中獲取的關于表的各種信息,包括列名稱,列類型等。這里可以看一下introspectedTable中幾個比較重要的方法:

public abstract class IntrospectedTable {
    /**
    * 該方法可以獲得配置文件中該表對應<table>標簽下的配置信息,包括映射成的Mapper名稱,PO名稱等
    * 也可以在table標簽下自定義<property>標簽并通過getProperty方法獲得值
    */
    public TableConfiguration getTableConfiguration() {
        return tableConfiguration;
    }

    /**
    * 這個方法中定義了默認的生成規(guī)則,可以通過calculateAllFieldsClass獲得返回類型
    */
    public Rules getRules() {
        return rules;
    }
}

悲觀鎖的clientGenerated方法如下:

    // Plugin配置,是否要生成selectForUpdate語句
    private static final String CONFIG_XML_KEY = "implementSelectForUpdate";

    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        String implementUpdate = introspectedTable.getTableConfiguration().getProperty(CONFIG_XML_KEY);
        if (StringUtility.isTrue(implementUpdate)) {
            Method method = new Method(METHOD_NAME);
            FullyQualifiedJavaType returnType = introspectedTable.getRules().calculateAllFieldsClass();
            method.setReturnType(returnType);
            method.addParameter(new Parameter(new FullyQualifiedJavaType("java.lang.Long"), "id"));
            String docComment = "/**\n" +
                    "      * 使用id對數據行上悲觀鎖\n" +
                    "      */";
            method.addJavaDocLine(docComment);
            interfaze.addMethod(method);
            log.debug("(悲觀鎖插件):" + interfaze.getType().getShortName() + "增加" + METHOD_NAME + "方法。");
        }

        return super.clientGenerated(interfaze, topLevelClass, introspectedTable);
    }

這里可以通過在對應table下新增property標簽來決定是否要為這張表生成對應的悲觀鎖方法,配置樣例如下:

 <table tableName="demo" domainObjectName="DemoPO" mapperName="DemoMapper"
               enableCountByExample="true"
               enableUpdateByExample="true"
               enableDeleteByExample="true"
               enableSelectByExample="true"
               enableInsert="true"
               selectByExampleQueryId="true">
    <property name="implementUpdateWithCAS" value="true"/>
 </table>

代碼中通過mybatis提供的Method方法,定義了方法的名稱,參數,返回類型等,并使用interfaze.addMethod方法將方法添加到客戶端的接口中。

再到sqlMapDocumentGenerated這個方法,這個方法中傳入了Document對象,它對應生成的XML文件,并通過XmlElement來映射XML文件中的元素。通過document.getRootElement().addElement可以將自定義的XML元素插入到Mapper文件中。自定義XML元素就是指拼接XmlElement,XmlElement的addAttribute方法可以為XML元素設置屬性,addElement則可以為XML標簽添加子元素。有兩種類型的子元素,分別是TextElement和XmlElement本身,TextElement則直接填充標簽中的內容,而XmlElement則對應新的標簽,如<where> <include>等。悲觀鎖的SQL生成邏輯如下:

    // Plugin配置,是否要生成selectForUpdate語句
    private static final String CONFIG_XML_KEY = "implementSelectForUpdate";

    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        String implementUpdate = introspectedTable.getTableConfiguration().getProperty(CONFIG_XML_KEY);
        if (!StringUtility.isTrue(implementUpdate)) {
            return super.sqlMapDocumentGenerated(document, introspectedTable);
        }

        XmlElement selectForUpdate = new XmlElement("select");
        selectForUpdate.addAttribute(new Attribute("id", METHOD_NAME));
        StringBuilder sb;

        String resultMapId = introspectedTable.hasBLOBColumns() ? introspectedTable.getResultMapWithBLOBsId() : introspectedTable.getBaseResultMapId();
        selectForUpdate.addAttribute(new Attribute("resultMap", resultMapId));
        selectForUpdate.addAttribute(new Attribute("parameterType", introspectedTable.getExampleType()));
        selectForUpdate.addElement(new TextElement("select"));

        sb = new StringBuilder();
        if (StringUtility.stringHasValue(introspectedTable.getSelectByExampleQueryId())) {
            sb.append('\'');
            sb.append(introspectedTable.getSelectByExampleQueryId());
            sb.append("' as QUERYID,");
            selectForUpdate.addElement(new TextElement(sb.toString()));
        }

        XmlElement baseColumn = new XmlElement("include");
        baseColumn.addAttribute(new Attribute("refid", introspectedTable.getBaseColumnListId()));
        selectForUpdate.addElement(baseColumn);
        if (introspectedTable.hasBLOBColumns()) {
            selectForUpdate.addElement(new TextElement(","));
            XmlElement blobColumns = new XmlElement("include");
            blobColumns.addAttribute(new Attribute("refid", introspectedTable.getBaseColumnListId()));
            selectForUpdate.addElement(blobColumns);
        }

        sb.setLength(0);
        sb.append("from ");
        sb.append(introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime());
        selectForUpdate.addElement(new TextElement(sb.toString()));
        TextElement whereXml = new TextElement("where id = #{id} for update");
        selectForUpdate.addElement(whereXml);

        document.getRootElement().addElement(selectForUpdate);
        log.debug("(悲觀鎖插件):" + introspectedTable.getMyBatis3XmlMapperFileName() + "增加" + METHOD_NAME + "方法(" + (introspectedTable.hasBLOBColumns() ? "有" : "無") + "Blob類型))。");
        return super.sqlMapDocumentGenerated(document, introspectedTable);
    }

完整代碼

@Slf4j
public class SelectForUpdatePlugin extends PluginAdapter {

    private static final String CONFIG_XML_KEY = "implementSelectForUpdate";

    private static final String METHOD_NAME = "selectByIdForUpdate";

    @Override
    public boolean validate(List<String> list) {
        return true;
    }

    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        String implementUpdate = introspectedTable.getTableConfiguration().getProperty(CONFIG_XML_KEY);
        if (StringUtility.isTrue(implementUpdate)) {
            Method method = new Method(METHOD_NAME);
            FullyQualifiedJavaType returnType = introspectedTable.getRules().calculateAllFieldsClass();
            method.setReturnType(returnType);
            method.addParameter(new Parameter(new FullyQualifiedJavaType("java.lang.Long"), "id"));
            String docComment = "/**\n" +
                    "      * 使用id對數據行上悲觀鎖\n" +
                    "      */";
            method.addJavaDocLine(docComment);
            interfaze.addMethod(method);
            log.debug("(悲觀鎖插件):" + interfaze.getType().getShortName() + "增加" + METHOD_NAME + "方法。");
        }

        return super.clientGenerated(interfaze, topLevelClass, introspectedTable);
    }

    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        String implementUpdate = introspectedTable.getTableConfiguration().getProperty(CONFIG_XML_KEY);
        if (!StringUtility.isTrue(implementUpdate)) {
            return super.sqlMapDocumentGenerated(document, introspectedTable);
        }

        XmlElement selectForUpdate = new XmlElement("select");
        selectForUpdate.addAttribute(new Attribute("id", METHOD_NAME));
        StringBuilder sb;

        String resultMapId = introspectedTable.hasBLOBColumns() ? introspectedTable.getResultMapWithBLOBsId() : introspectedTable.getBaseResultMapId();
        selectForUpdate.addAttribute(new Attribute("resultMap", resultMapId));
        selectForUpdate.addAttribute(new Attribute("parameterType", introspectedTable.getExampleType()));
        selectForUpdate.addElement(new TextElement("select"));

        sb = new StringBuilder();
        if (StringUtility.stringHasValue(introspectedTable.getSelectByExampleQueryId())) {
            sb.append('\'');
            sb.append(introspectedTable.getSelectByExampleQueryId());
            sb.append("' as QUERYID,");
            selectForUpdate.addElement(new TextElement(sb.toString()));
        }

        XmlElement baseColumn = new XmlElement("include");
        baseColumn.addAttribute(new Attribute("refid", introspectedTable.getBaseColumnListId()));
        selectForUpdate.addElement(baseColumn);
        if (introspectedTable.hasBLOBColumns()) {
            selectForUpdate.addElement(new TextElement(","));
            XmlElement blobColumns = new XmlElement("include");
            blobColumns.addAttribute(new Attribute("refid", introspectedTable.getBaseColumnListId()));
            selectForUpdate.addElement(blobColumns);
        }

        sb.setLength(0);
        sb.append("from ");
        sb.append(introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime());
        selectForUpdate.addElement(new TextElement(sb.toString()));
        TextElement whereXml = new TextElement("where id = #{id} for update");
        selectForUpdate.addElement(whereXml);

        document.getRootElement().addElement(selectForUpdate);
        log.debug("(悲觀鎖插件):" + introspectedTable.getMyBatis3XmlMapperFileName() + "增加" + METHOD_NAME + "方法(" + (introspectedTable.hasBLOBColumns() ? "有" : "無") + "Blob類型))。");
        return super.sqlMapDocumentGenerated(document, introspectedTable);
    }
}

到此這篇關于Mybatis Generator Plugin悲觀鎖實現(xiàn)示例的文章就介紹到這了,更多相關Mybatis Generator Plugin悲觀鎖 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java圖片對比度調整示例代碼

    java圖片對比度調整示例代碼

    這篇文章主要給大家介紹了關于java圖片對比度調整的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用java具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-07-07
  • Java實現(xiàn)Ip地址獲取的示例代碼

    Java實現(xiàn)Ip地址獲取的示例代碼

    這篇文章主要為大家詳細介紹了Java實現(xiàn)Ip地址獲取的兩種方式,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考一下
    2023-09-09
  • Java中seata框架的XA模式詳解

    Java中seata框架的XA模式詳解

    這篇文章主要介紹了Java中seata框架的XA模式詳解,Seata?是一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務,Seata?將為用戶提供了?AT、TCC、SAGA?和?XA?事務模式,為用戶打造一站式的分布式解決方案,需要的朋友可以參考下
    2023-08-08
  • SpringMVC + jquery.uploadify實現(xiàn)上傳文件功能

    SpringMVC + jquery.uploadify實現(xiàn)上傳文件功能

    文件上傳是很多項目都會使用到的功能,SpringMVC當然也提供了這個功能。不過小編不建議在項目中通過form表單來提交文件上傳,這樣做的局限性很大。下面這篇文章主要介紹了利用SpringMVC + jquery.uploadify實現(xiàn)上傳文件功能的相關資料,需要的朋友可以參考下。
    2017-06-06
  • spring.profiles使用的方法步驟

    spring.profiles使用的方法步驟

    本文主要介紹了spring.profiles使用與spring.profiles.active和spring.profiles.include區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-07-07
  • Java進階:Struts多模塊的技巧

    Java進階:Struts多模塊的技巧

    Java進階:Struts多模塊的技巧...
    2006-12-12
  • 如何用JAVA判斷當前時間是否為節(jié)假日、周末、工作日及調休日(不報錯:IOException!)

    如何用JAVA判斷當前時間是否為節(jié)假日、周末、工作日及調休日(不報錯:IOException!)

    最近公司有個業(yè)務需要判斷工作日,但是每年的節(jié)假日不一樣,下面這篇文章主要給大家介紹了關于如何用JAVA判斷當前時間是否為節(jié)假日、周末、工作日及調休日的相關資料,且不報錯:IOException!,需要的朋友可以參考下
    2023-12-12
  • Java中Spring Boot支付寶掃碼支付及支付回調的實現(xiàn)代碼

    Java中Spring Boot支付寶掃碼支付及支付回調的實現(xiàn)代碼

    這篇文章主要介紹了Java中Spring Boot支付寶掃碼支付及支付回調的實現(xiàn)代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02
  • JDK、J2EE、J2SE、J2ME四個易混淆概念區(qū)分

    JDK、J2EE、J2SE、J2ME四個易混淆概念區(qū)分

    這篇文章將向你詳細介紹JDK、J2EE、J2SE、J2ME的概念以及他們的關系區(qū)別。
    2015-09-09
  • java實現(xiàn)短地址服務的方法(附代碼)

    java實現(xiàn)短地址服務的方法(附代碼)

    大多數情況下URL太長,字符多,不便于發(fā)布復制和存儲,本文就介紹了通過java實現(xiàn)短地址服務,減少了許多使用太長URL帶來的不便,需要的朋友可以參考下
    2015-07-07

最新評論

瑞丽市| 封丘县| 平凉市| 海丰县| 德令哈市| 湘阴县| 自贡市| 治多县| 嘉黎县| 营山县| 清涧县| 德庆县| 雷州市| 吉安县| 象州县| 渭源县| 布尔津县| 紫云| 九江市| 太湖县| 建宁县| 黎川县| 昔阳县| 阜城县| 灵寿县| 保靖县| 霍林郭勒市| 丰宁| 济阳县| 阿拉善右旗| 积石山| 信宜市| 神木县| 东山县| 扶绥县| 巴林右旗| 马鞍山市| 贡觉县| 客服| 岱山县| 扶沟县|