1717
1818import java .lang .reflect .Method ;
1919import java .util .HashMap ;
20+ import java .util .Map ;
2021
22+ import org .apache .ibatis .annotations .Param ;
2123import org .apache .ibatis .builder .BuilderException ;
2224import org .apache .ibatis .builder .SqlSourceBuilder ;
2325import org .apache .ibatis .mapping .BoundSql ;
2628
2729/**
2830 * @author Clinton Begin
31+ * @author Kazuki Shimizu
2932 */
3033public class ProviderSqlSource implements SqlSource {
3134
3235 private SqlSourceBuilder sqlSourceParser ;
3336 private Class <?> providerType ;
3437 private Method providerMethod ;
35- private boolean providerTakesParameterObject ;
38+ private String [] providerMethodArgumentNames ;
3639
3740 public ProviderSqlSource (Configuration config , Object provider ) {
3841 String providerMethodName = null ;
@@ -43,13 +46,19 @@ public ProviderSqlSource(Configuration config, Object provider) {
4346
4447 for (Method m : this .providerType .getMethods ()) {
4548 if (providerMethodName .equals (m .getName ())) {
46- if (m .getParameterTypes ().length < 2
47- && m .getReturnType () == String .class ) {
49+ if (m .getReturnType () == String .class ) {
50+ if (providerMethod != null ){
51+ throw new BuilderException ("Error creating SqlSource for SqlProvider. Method '"
52+ + providerMethodName + "' is found multiple in SqlProvider '" + this .providerType .getName ()
53+ + "'. Sql provider method can not overload." );
54+ }
4855 this .providerMethod = m ;
49- this .providerTakesParameterObject = m . getParameterTypes (). length == 1 ;
56+ this .providerMethodArgumentNames = extractProviderMethodArgumentNames ( m ) ;
5057 }
5158 }
5259 }
60+ } catch (BuilderException e ) {
61+ throw e ;
5362 } catch (Exception e ) {
5463 throw new BuilderException ("Error creating SqlSource for SqlProvider. Cause: " + e , e );
5564 }
@@ -67,19 +76,59 @@ public BoundSql getBoundSql(Object parameterObject) {
6776
6877 private SqlSource createSqlSource (Object parameterObject ) {
6978 try {
79+ Class <?>[] parameterTypes = providerMethod .getParameterTypes ();
7080 String sql ;
71- if (providerTakesParameterObject ) {
81+ if (parameterTypes .length == 0 ) {
82+ sql = (String ) providerMethod .invoke (providerType .newInstance ());
83+ } else if (parameterTypes .length == 1 ) {
7284 sql = (String ) providerMethod .invoke (providerType .newInstance (), parameterObject );
85+ } else if (parameterObject instanceof Map ) {
86+ @ SuppressWarnings ("unchecked" )
87+ Map <String , Object > params = (Map <String , Object >) parameterObject ;
88+ sql = (String ) providerMethod .invoke (providerType .newInstance (), extractProviderMethodArguments (params , providerMethodArgumentNames ));
7389 } else {
74- sql = (String ) providerMethod .invoke (providerType .newInstance ());
90+ throw new BuilderException ("Error invoking SqlProvider method ("
91+ + providerType .getName () + "." + providerMethod .getName ()
92+ + "). Cannot invoke a method that holds multiple arguments using a specifying parameterObject. In this case, please specify a 'java.util.Map' object." );
7593 }
7694 Class <?> parameterType = parameterObject == null ? Object .class : parameterObject .getClass ();
7795 return sqlSourceParser .parse (sql , parameterType , new HashMap <String , Object >());
96+ } catch (BuilderException e ) {
97+ throw e ;
7898 } catch (Exception e ) {
7999 throw new BuilderException ("Error invoking SqlProvider method ("
80100 + providerType .getName () + "." + providerMethod .getName ()
81101 + "). Cause: " + e , e );
82102 }
83103 }
84104
105+ private String [] extractProviderMethodArgumentNames (Method providerMethod ) {
106+ String [] argumentNames = new String [providerMethod .getParameterTypes ().length ];
107+ for (int i = 0 ; i < argumentNames .length ; i ++) {
108+ Param param = findParamAnnotation (providerMethod , i );
109+ argumentNames [i ] = param != null ? param .value () : "param" + (i + 1 );
110+ }
111+ return argumentNames ;
112+ }
113+
114+ private Param findParamAnnotation (Method providerMethod , int parameterIndex ) {
115+ final Object [] annotations = providerMethod .getParameterAnnotations ()[parameterIndex ];
116+ Param param = null ;
117+ for (Object annotation : annotations ) {
118+ if (annotation instanceof Param ) {
119+ param = Param .class .cast (annotation );
120+ break ;
121+ }
122+ }
123+ return param ;
124+ }
125+
126+ private Object [] extractProviderMethodArguments (Map <String , Object > params , String [] argumentNames ) {
127+ Object [] args = new Object [argumentNames .length ];
128+ for (int i = 0 ; i < args .length ; i ++) {
129+ args [i ] = params .get (argumentNames [i ]);
130+ }
131+ return args ;
132+ }
133+
85134}
0 commit comments