SelectKeyGenerator.java

  1. /*
  2.  *    Copyright 2009-2024 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.executor.keygen;

  17. import java.sql.Statement;
  18. import java.util.List;

  19. import org.apache.ibatis.executor.Executor;
  20. import org.apache.ibatis.executor.ExecutorException;
  21. import org.apache.ibatis.mapping.MappedStatement;
  22. import org.apache.ibatis.reflection.MetaObject;
  23. import org.apache.ibatis.session.Configuration;
  24. import org.apache.ibatis.session.ExecutorType;
  25. import org.apache.ibatis.session.RowBounds;

  26. /**
  27.  * @author Clinton Begin
  28.  * @author Jeff Butler
  29.  */
  30. public class SelectKeyGenerator implements KeyGenerator {

  31.   public static final String SELECT_KEY_SUFFIX = "!selectKey";
  32.   private final boolean executeBefore;
  33.   private final MappedStatement keyStatement;

  34.   public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {
  35.     this.executeBefore = executeBefore;
  36.     this.keyStatement = keyStatement;
  37.   }

  38.   @Override
  39.   public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
  40.     if (executeBefore) {
  41.       processGeneratedKeys(executor, ms, parameter);
  42.     }
  43.   }

  44.   @Override
  45.   public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
  46.     if (!executeBefore) {
  47.       processGeneratedKeys(executor, ms, parameter);
  48.     }
  49.   }

  50.   private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
  51.     try {
  52.       if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
  53.         String[] keyProperties = keyStatement.getKeyProperties();
  54.         final Configuration configuration = ms.getConfiguration();
  55.         final MetaObject metaParam = configuration.newMetaObject(parameter);
  56.         // Do not close keyExecutor.
  57.         // The transaction will be closed by parent executor.
  58.         Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
  59.         List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
  60.         if (values.isEmpty()) {
  61.           throw new ExecutorException("SelectKey returned no data.");
  62.         }
  63.         if (values.size() > 1) {
  64.           throw new ExecutorException("SelectKey returned more than one value.");
  65.         } else {
  66.           MetaObject metaResult = configuration.newMetaObject(values.get(0));
  67.           if (keyProperties.length == 1) {
  68.             if (metaResult.hasGetter(keyProperties[0])) {
  69.               setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
  70.             } else {
  71.               // no getter for the property - maybe just a single value object
  72.               // so try that
  73.               setValue(metaParam, keyProperties[0], values.get(0));
  74.             }
  75.           } else {
  76.             handleMultipleProperties(keyProperties, metaParam, metaResult);
  77.           }
  78.         }
  79.       }
  80.     } catch (ExecutorException e) {
  81.       throw e;
  82.     } catch (Exception e) {
  83.       throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
  84.     }
  85.   }

  86.   private void handleMultipleProperties(String[] keyProperties, MetaObject metaParam, MetaObject metaResult) {
  87.     String[] keyColumns = keyStatement.getKeyColumns();

  88.     if (keyColumns == null || keyColumns.length == 0) {
  89.       // no key columns specified, just use the property names
  90.       for (String keyProperty : keyProperties) {
  91.         setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
  92.       }
  93.     } else {
  94.       if (keyColumns.length != keyProperties.length) {
  95.         throw new ExecutorException(
  96.             "If SelectKey has key columns, the number must match the number of key properties.");
  97.       }
  98.       for (int i = 0; i < keyProperties.length; i++) {
  99.         setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
  100.       }
  101.     }
  102.   }

  103.   private void setValue(MetaObject metaParam, String property, Object value) {
  104.     if (!metaParam.hasSetter(property)) {
  105.       throw new ExecutorException("No setter found for the keyProperty '" + property + "' in "
  106.           + metaParam.getOriginalObject().getClass().getName() + ".");
  107.     }
  108.     metaParam.setValue(property, value);
  109.   }
  110. }