View Javadoc
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  
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.isEmpty()) {
69            throw new ExecutorException("SelectKey returned no data.");
70          }
71          if (values.size() > 1) {
72            throw new ExecutorException("SelectKey returned more than one value.");
73          } else {
74            MetaObject metaResult = configuration.newMetaObject(values.get(0));
75            if (keyProperties.length == 1) {
76              if (metaResult.hasGetter(keyProperties[0])) {
77                setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
78              } else {
79                // no getter for the property - maybe just a single value object
80                // so try that
81                setValue(metaParam, keyProperties[0], values.get(0));
82              }
83            } else {
84              handleMultipleProperties(keyProperties, metaParam, metaResult);
85            }
86          }
87        }
88      } catch (ExecutorException e) {
89        throw e;
90      } catch (Exception e) {
91        throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
92      }
93    }
94  
95    private void handleMultipleProperties(String[] keyProperties, 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(
106             "If SelectKey has key columns, the number must match the number of key properties.");
107       }
108       for (int i = 0; i < keyProperties.length; i++) {
109         setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
110       }
111     }
112   }
113 
114   private void setValue(MetaObject metaParam, String property, Object value) {
115     if (!metaParam.hasSetter(property)) {
116       throw new ExecutorException("No setter found for the keyProperty '" + property + "' in "
117           + metaParam.getOriginalObject().getClass().getName() + ".");
118     }
119     metaParam.setValue(property, value);
120   }
121 }