View Javadoc
1   /**
2    *    Copyright 2009-2015 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 boolean executeBefore;
37    private 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          if (keyProperties != null) {
65            // Do not close keyExecutor.
66            // The transaction will be closed by parent executor.
67            Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
68            List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
69            if (values.size() == 0) {
70              throw new ExecutorException("SelectKey returned no data.");            
71            } else 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        }
89      } catch (ExecutorException e) {
90        throw e;
91      } catch (Exception e) {
92        throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
93      }
94    }
95  
96    private void handleMultipleProperties(String[] keyProperties,
97        MetaObject metaParam, MetaObject metaResult) {
98      String[] keyColumns = keyStatement.getKeyColumns();
99        
100     if (keyColumns == null || keyColumns.length == 0) {
101       // no key columns specified, just use the property names
102       for (int i = 0; i < keyProperties.length; i++) {
103         setValue(metaParam, keyProperties[i], metaResult.getValue(keyProperties[i]));
104       }
105     } else {
106       if (keyColumns.length != keyProperties.length) {
107         throw new ExecutorException("If SelectKey has key columns, the number must match the number of key properties.");
108       }
109       for (int i = 0; i < keyProperties.length; i++) {
110         setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
111       }
112     }
113   }
114 
115   private void setValue(MetaObject metaParam, String property, Object value) {
116     if (metaParam.hasSetter(property)) {
117       metaParam.setValue(property, value);
118     } else {
119       throw new ExecutorException("No setter found for the keyProperty '" + property + "' in " + metaParam.getOriginalObject().getClass().getName() + ".");
120     }
121   }
122 }