View Javadoc
1   /*
2    *    Copyright 2009-2021 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    *       http://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  
18  import java.sql.Statement;
19  import java.util.List;
20  
21  import org.apache.ibatis.executor.Executor;
22  import org.apache.ibatis.executor.ExecutorException;
23  import org.apache.ibatis.mapping.MappedStatement;
24  import org.apache.ibatis.reflection.MetaObject;
25  import org.apache.ibatis.session.Configuration;
26  import org.apache.ibatis.session.ExecutorType;
27  import org.apache.ibatis.session.RowBounds;
28  
29  /**
30   * @author Clinton Begin
31   * @author Jeff Butler
32   */
33  public class SelectKeyGenerator implements KeyGenerator {
34  
35    public static final String SELECT_KEY_SUFFIX = "!selectKey";
36    private final boolean executeBefore;
37    private final MappedStatement keyStatement;
38  
39    public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {
40      this.executeBefore = executeBefore;
41      this.keyStatement = keyStatement;
42    }
43  
44    @Override
45    public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
46      if (executeBefore) {
47        processGeneratedKeys(executor, ms, parameter);
48      }
49    }
50  
51    @Override
52    public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
53      if (!executeBefore) {
54        processGeneratedKeys(executor, ms, parameter);
55      }
56    }
57  
58    private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
59      try {
60        if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
61          String[] keyProperties = keyStatement.getKeyProperties();
62          final Configuration configuration = ms.getConfiguration();
63          final MetaObject metaParam = configuration.newMetaObject(parameter);
64          // Do not close keyExecutor.
65          // The transaction will be closed by parent executor.
66          Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
67          List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
68          if (values.size() == 0) {
69            throw new ExecutorException("SelectKey returned no data.");
70          } else if (values.size() > 1) {
71            throw new ExecutorException("SelectKey returned more than one value.");
72          } else {
73            MetaObject metaResult = configuration.newMetaObject(values.get(0));
74            if (keyProperties.length == 1) {
75              if (metaResult.hasGetter(keyProperties[0])) {
76                setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
77              } else {
78                // no getter for the property - maybe just a single value object
79                // so try that
80                setValue(metaParam, keyProperties[0], values.get(0));
81              }
82            } else {
83              handleMultipleProperties(keyProperties, metaParam, metaResult);
84            }
85          }
86        }
87      } catch (ExecutorException e) {
88        throw e;
89      } catch (Exception e) {
90        throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
91      }
92    }
93  
94    private void handleMultipleProperties(String[] keyProperties,
95        MetaObject metaParam, MetaObject metaResult) {
96      String[] keyColumns = keyStatement.getKeyColumns();
97  
98      if (keyColumns == null || keyColumns.length == 0) {
99        // no key columns specified, just use the property names
100       for (String keyProperty : keyProperties) {
101         setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
102       }
103     } else {
104       if (keyColumns.length != keyProperties.length) {
105         throw new ExecutorException("If SelectKey has key columns, the number must match the number of key properties.");
106       }
107       for (int i = 0; i < keyProperties.length; i++) {
108         setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
109       }
110     }
111   }
112 
113   private void setValue(MetaObject metaParam, String property, Object value) {
114     if (metaParam.hasSetter(property)) {
115       metaParam.setValue(property, value);
116     } else {
117       throw new ExecutorException("No setter found for the keyProperty '" + property + "' in " + metaParam.getOriginalObject().getClass().getName() + ".");
118     }
119   }
120 }