I "solved" the problem of "global parameters" with a crude interceptor plug-in hack, in my case to provide the TableNames dynamically :
I introduced a new parameter type %{gobalParameter} (Note the "%")
// The interceptor code:
@Intercepts({@Signature(type= Executor.class, method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class InterceptQuery extends InterceptBase implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.getMethod().invoke(invocation.getTarget(), inteceptArgs(invocation.getArgs()));
}
}
// The base class
public abstract class InterceptBase implements Interceptor {
private TableNameService tableNameService;
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Object[] inteceptArgs(Object[] args) {
MappedStatement st = (MappedStatement) args[0];
try {
Field field = st.getClass().getDeclaredField("sqlSource");
field.setAccessible(true);
SqlSource sqlSource = (SqlSource) field.get(st);
field.set(st, new DynamicSqlSourceProxy(sqlSource, tableNameService) );
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return args;
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
public TableNameService getTableNameService() {
return tableNameService;
}
public void setTableNameService(TableNameService tableNameService) {
this.tableNameService = tableNameService;
}
}
/**
* This proxy class wraps around the generated sql statements and replaces all %{} occurrences
* with strings from Tokenhandler service
*/
public class DynamicSqlSourceProxy implements SqlSource {
SqlSource impl;
GenericTokenParser parser;
public DynamicSqlSourceProxy(SqlSource impl, TokenHandler handler) {
this.impl = impl;
this.parser = new GenericTokenParser("%{", "}", handler);
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
BoundSql sql = impl.getBoundSql(parameterObject);
String newsql = parser.parse(sql.getSql());
try {
Field sqlField = BoundSql.class.getDeclaredField("sql");
sqlField.setAccessible(true);
sqlField.set(sql,newsql);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return sql;
}
}
With this @Intercept annotation, all select queries are intercepted. Apply this for other query types as well, for example:
@Intercepts({@Signature(type= Executor.class, method = "update",args = {MappedStatement.class, Object.class})})
public class InterceptUpdate extends InterceptBase implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.getMethod().invoke(invocation.getTarget(), inteceptArgs(invocation.getArgs()));
}
}
This code does not work with nested selects because the @Intercept annotation does not work on them, as I posted earlier with no answer.
Now a Statement can look like
select * from %{TableName} where .....
Yes, I know, a really crude hack. Any ideas to do this better?
Cheers, Felix