All Downloads are FREE. Search and download functionalities are using the official Maven repository.

app.myoss.cloud.mybatis.executor.keygen.SequenceKeyGenerator Maven / Gradle / Ivy

/*
 * Copyright 2018-2018 https://github.com/myoss
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package app.myoss.cloud.mybatis.executor.keygen;

import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.binding.BindingException;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.springframework.util.CollectionUtils;

import app.myoss.cloud.mybatis.table.Sequence;
import app.myoss.cloud.mybatis.table.TableInfo;
import app.myoss.cloud.mybatis.table.TableMetaObject;
import app.myoss.cloud.mybatis.table.TableSequence;
import app.myoss.cloud.mybatis.table.annotation.GenerationType;
import app.myoss.cloud.mybatis.table.annotation.SequenceKey;

/**
 * 用于 {@link GenerationType#SEQUENCE_KEY} 策略,触发 {@link Sequence}
 * 接口实现类来生成序列值,并更新到实体对象中
 *
 * @author Jerry.Chen
 * @since 2018年4月30日 下午5:12:53
 * @see SequenceKey
 * @see Sequence
 */
public class SequenceKeyGenerator implements KeyGenerator {
    /**
     * sequence key 默认的后缀名
     */
    public static final String SEQUENCE_KEY_SUFFIX = "!sequenceKey";
    private String[]           keyProperties;
    private String[]           keyColumns;
    private boolean            executeBefore;
    private Sequence           sequence;

    /**
     * 初始化序列生成器
     *
     * @param tableInfo 数据库表结构信息
     * @param sqlId mappedStatement id
     * @param executeBefore 在 INSERT 之前/之后执行
     */
    public SequenceKeyGenerator(TableInfo tableInfo, String sqlId, boolean executeBefore) {
        TableSequence tableSequence = tableInfo.getTableSequence();
        this.keyProperties = tableSequence.getKeyProperties();
        this.keyColumns = tableSequence.getKeyColumns();
        this.executeBefore = executeBefore;

        // 获取 Sequence 实例对象
        String sequenceBeanName = tableSequence.getSequenceBeanName();
        if (StringUtils.isNotBlank(sequenceBeanName)) {
            this.sequence = TableMetaObject.getSequenceBean(sequenceBeanName);
            if (this.sequence == null) {
                throw new BindingException(
                        "no instance of [" + sequenceBeanName + "] in TableMetaObject, please check!");
            }
        } else {
            try {
                this.sequence = tableSequence.getSequenceClass().newInstance();
                String name = StringUtils.defaultString(tableSequence.getSequenceName(), SEQUENCE_KEY_SUFFIX);
                TableMetaObject.addSequenceBean(sqlId + "." + name, this.sequence);
            } catch (InstantiationException | IllegalAccessException e) {
                throw new BindingException("new instance of [" + tableSequence.getSequenceClass() + "] failed", e);
            }
        }
        this.sequence.setTableInfo(tableInfo);
    }

    @Override
    public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
        if (executeBefore) {
            processGeneratedKeys(ms, parameter);
        }
    }

    @Override
    public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
        if (!executeBefore) {
            processGeneratedKeys(ms, parameter);
        }
    }

    private void processGeneratedKeys(MappedStatement ms, Object parameter) {
        try {
            if (parameter != null && keyProperties != null && keyProperties.length > 0) {
                if (keyColumns != null && keyColumns.length != keyProperties.length) {
                    throw new ExecutorException(
                            "If SelectKey has key columns, the number must match the number of key properties.");
                }
                final Configuration configuration = ms.getConfiguration();
                final MetaObject metaParam = configuration.newMetaObject(parameter);
                List keys = new ArrayList<>(keyProperties.length);
                List columns = new ArrayList<>(keyProperties.length);
                for (int i = 0; i < keyProperties.length; i++) {
                    String keyProperty = keyProperties[i];
                    if (!(metaParam.hasGetter(keyProperty) && checkValueIsNotNull(metaParam, keyProperty))) {
                        keys.add(keyProperty);
                        if (keyColumns != null) {
                            columns.add(keyColumns[i]);
                        }
                    }
                }
                if (keys.size() == 0) {
                    // 主键字段已经有值,不生成
                    return;
                }

                Object value = sequence.nextValue(parameter);
                MetaObject metaResult = configuration.newMetaObject(value);
                if (keys.size() == 1) {
                    String keyProperty = keys.get(0);
                    if (metaResult.hasGetter(keyProperty)) {
                        setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
                    } else {
                        // no getter for the property - maybe just a single value object
                        // so try that
                        setValue(metaParam, keyProperty, value);
                    }
                } else {
                    handleMultipleProperties(keys, columns, metaParam, metaResult);
                }
            }
        } catch (ExecutorException e) {
            throw e;
        } catch (Exception e) {
            throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
        }
    }

    private void handleMultipleProperties(List keyProperties, List columns, MetaObject metaParam,
                                          MetaObject metaResult) {
        if (CollectionUtils.isEmpty(columns)) {
            // no key columns specified, just use the property names
            for (String keyProperty : keyProperties) {
                setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
            }
        } else {
            if (keyProperties.size() != columns.size()) {
                throw new ExecutorException(
                        "If SelectKey has key columns, the number must match the number of key properties.");
            }
            for (int i = 0; i < keyProperties.size(); i++) {
                setValue(metaParam, keyProperties.get(i), metaResult.getValue(columns.get(i)));
            }
        }
    }

    private void setValue(MetaObject metaParam, String property, Object value) {
        if (metaParam.hasSetter(property)) {
            metaParam.setValue(property, value);
        } else {
            throw new ExecutorException("No setter found for the keyProperty '" + property + "' in "
                    + metaParam.getOriginalObject().getClass().getName() + ".");
        }
    }

    private boolean checkValueIsNotNull(MetaObject metaParam, String keyProperty) {
        Object value = metaParam.getValue(keyProperty);
        if (value instanceof CharSequence && StringUtils.isBlank((CharSequence) value)) {
            return true;
        }
        return value != null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy