MybatisGeneratorをいじくる、その2

Java

どうも、またまた更新が途絶えてましたね(笑)

さて、前回の続きでMyBatisGeneratorでカスタマイズする例を紹介したいと思います。

まずはMySQLのLimit句、Offset句を追加です。

public class AddLimitOffsetPlugin extends PluginAdapter {
    @Override
    public boolean validate(List<String> paramList) {
        return true;
    }
    @Override
    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        PrimitiveTypeWrapper integerWrapper = FullyQualifiedJavaType.getIntInstance().getPrimitiveTypeWrapper();
        Field limitClause = new Field();
        limitClause.setName("limitClause");
        limitClause.setVisibility(JavaVisibility.PROTECTED);
        limitClause.setType(integerWrapper);
        topLevelClass.addField(limitClause);
        Method limitClauseSet = new Method();
        limitClauseSet.setVisibility(JavaVisibility.PUBLIC);
        limitClauseSet.setName("setLimitClause");
        limitClauseSet.addParameter(new Parameter(integerWrapper, "limitClause"));
        limitClauseSet.addBodyLine("this.limitClause = limitClause;");
        topLevelClass.addMethod(limitClauseSet);
        Method limitClauseGet = new Method();
        limitClauseGet.setVisibility(JavaVisibility.PUBLIC);
        limitClauseGet.setReturnType(integerWrapper);
        limitClauseGet.setName("getLimitClause");
        limitClauseGet.addBodyLine("return limitClause;");
        topLevelClass.addMethod(limitClauseGet);
        Field offsetClause = new Field();
        offsetClause.setName("offsetClause");
        offsetClause.setVisibility(JavaVisibility.PROTECTED);
        offsetClause.setType(integerWrapper);
        topLevelClass.addField(offsetClause);
        Method offsetClauseSet = new Method();
        offsetClauseSet.setVisibility(JavaVisibility.PUBLIC);
        offsetClauseSet.setName("setOffsetClause");
        offsetClauseSet.addParameter(new Parameter(integerWrapper, "offsetClause"));
        offsetClauseSet.addBodyLine("this.offsetClause = offsetClause;");
        topLevelClass.addMethod(offsetClauseSet);
        Method offsetClauseGet = new Method();
        offsetClauseGet.setVisibility(JavaVisibility.PUBLIC);
        offsetClauseGet.setReturnType(integerWrapper);
        offsetClauseGet.setName("getOffsetClause");
        offsetClauseGet.addBodyLine("return offsetClause;");
        topLevelClass.addMethod(offsetClauseGet);
        return true;
    }
    @Override
    public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element,
            IntrospectedTable introspectedTable) {
        XmlElement isNotNullElement = new XmlElement("if");
        isNotNullElement.addAttribute(new Attribute("test", "limitClause != null"));
        isNotNullElement.addElement(new TextElement("limit ${limitClause}"));
        element.getElements().add(isNotNullElement);
        isNotNullElement = new XmlElement("if");
        isNotNullElement.addAttribute(new Attribute("test", "offsetClause != null"));
        isNotNullElement.addElement(new TextElement("offset ${offsetClause}"));
        element.getElements().add(isNotNullElement);
        return true;
    }
}

細かい説明は省きますが、
1.ExampleクラスにLimitClause、OffsetClauseというインスタンス変数を追加
2.追加した変数のSetter、Getterメソッド追加
3.SQLマップファイルでインスタンス変数を参照して、あればSQLに追加して実行
という流れです。
BLOB型のカラムは扱わない前提なので
sqlMapSelectByExampleWithoutBLOBsElementGenerated
をオーバーライドしてます。
BLOB型のカラムを持っていないテーブルのSQLマップファイル生成時に呼び出されます。

続いて、数値型のインクリメントSQL生成プラグイン

public class AddIncrementalPlugin extends PluginAdapter {
    @Override
    public boolean validate(List<String> paramList) {
        return true;
    }
    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass,
            IntrospectedTable introspectedTable) {
        if (!isTargetColmunExist(introspectedTable)) {
            return true;
        }
        Method method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(PrimitiveTypeWrapper.getIntInstance());
        method.setName("incrementalUpdateByPrimaryKeySelective");
        
        method.addParameter(new Parameter(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()), "record"));
        interfaze.addMethod(method);
        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);
        return true;
    }
    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        if (!isTargetColmunExist(introspectedTable)) {
            return true;
        }
        XmlElement element = new XmlElement("update");
        element.addAttribute(new Attribute("id", "incrementalUpdateByPrimaryKeySelective"));
        element.addAttribute(new Attribute("parameterType", introspectedTable.getBaseRecordType()));
        context.getCommentGenerator().addComment(element);
        
        StringBuilder sb = new StringBuilder();
        sb.append("update ");
        sb.append(introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime());
        element.addElement(new TextElement(sb.toString()));
        XmlElement dynamicElement = new XmlElement("set");
        element.addElement(dynamicElement);
        for (IntrospectedColumn introspectedColumn : introspectedTable.getNonPrimaryKeyColumns()) {
            XmlElement isNotNullElement = new XmlElement("if");
            sb.setLength(0);
            sb.append(introspectedColumn.getJavaProperty());
            sb.append(" != null");
            isNotNullElement.addAttribute(new Attribute("test", sb.toString()));
            dynamicElement.addElement(isNotNullElement);
            sb.setLength(0);
            sb.append(MyBatis3FormattingUtilities.getAliasedEscapedColumnName(introspectedColumn));
            sb.append(" = ");
            if (introspectedColumn.getJdbcTypeName().equals("INTEGER")
                    || introspectedColumn.getJdbcTypeName().equals("BIGINT")) {
                sb.append(MyBatis3FormattingUtilities.getAliasedEscapedColumnName(introspectedColumn));
                sb.append(" + ");
            }
            sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn));
            sb.append(',');
            isNotNullElement.addElement(new TextElement(sb.toString()));
        }
        boolean and = false;
        for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
            sb.setLength(0);
            if (and) {
                sb.append(" and ");
            } else {
                sb.append("where ");
                and = true;
            }
            sb.append(MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn));
            sb.append(" = ");
            sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn));
            element.addElement(new TextElement(sb.toString()));
        }
        if (context.getPlugins().sqlMapUpdateByPrimaryKeySelectiveElementGenerated(element, introspectedTable)) {
            document.getRootElement().addElement(element);
        }
        return true;
    }
    private boolean isTargetColmunExist(IntrospectedTable introspectedTable) {
        boolean targetExist = false;
        for (IntrospectedColumn introspectedColumn : introspectedTable.getNonPrimaryKeyColumns()) {
            if (introspectedColumn.getJdbcTypeName().equals("INTEGER")
                    || introspectedColumn.getJdbcTypeName().equals("BIGINT")) {
                targetExist = true;
                break;
            }
        }
        return targetExist;
    }
}

ちと長いですが、
1.DAO(Mapper)クラスにincrementalUpdateByPrimaryKeySelectiveというメソッドを追加
2.SQLマップファイルに追加したメソッドで呼び出すクエリIDを追加
ってだけです。

if(introspectedColumn.getJdbcTypeName().equals("INTEGER")
        || introspectedColumn.getJdbcTypeName().equals("BIGINT")) {
    targetExist = true;
    break;
}

この判定で良いのかはちょっと不安なのですが、INTEGERとBIGINT以外でインクリメント処理はしないという前提ですね(笑)

とまぁ、コード貼り付けただけの手抜き記事みたいになってしまいましたが、
このアダプタパターンによる実装で、やろうと思えば生成する中身をすべて書き換えるなんてこともできます。
MyBatisGeneratorの意味がなくなるので普通はやらないですがね(笑)

MyBatisは現在も活発に更新されてるっぽいので、クエリレベルでマッピングする軽量なORMをお探しな方にオススメデス。
いつの間にかScala対応なんかもしてるようです。
公式MyBatisブログ

コメント

タイトルとURLをコピーしました