Mybatis反射修改SQL值
某一些情況下我們需要動態的改變Mybtis的執行的sql語句,有兩種方法:1)使用攔截器,2)使用反射,攔截器的比較簡單點,這里使用反射實現一次,有一點小坑,記錄一下:
特別說明:環境配置就不列出來了,下面的所有操作是假設spring已經整合好了mybatis的基礎上的。具體的代碼連接見文章結尾:
重點在於org.apache.ibatis.builder.StaticSqlSource.getBoundSql(Object)
@Override
public BoundSql getBoundSql(Object parameterObject) {
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
// 每次獲得是一個新的對象,這里使用反射修改是無效的,所以需要直接修改 BoundSql 的 sql 子彈
代碼:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext2.xml"})
public class MapperTest {
@Autowired
private SqlSessionFactoryBean sqlSessionFactoryBean;
// 動態修改sql
// 1. 使用 mybatis 的插件來完成參考分頁插件改變查詢的SQL com.bart.plugins.MyBatisPagePlugi
// 2. 使用反射修改MappedStatement的boundSql
@Test
public void testDynamicModifySQL2() throws Exception {
SqlSessionFactory sessionFactory = sqlSessionFactoryBean.getObject();
Configuration configuration = sessionFactory.getConfiguration();
MappedStatement mappedStatement = configuration.getMappedStatement("com.bart.common.mapper.dao.EmployeeMapper.selectAll");
// org.apache.ibatis.scripting.defaults.RawSqlSource
// 該 sqlSource 中還有一個 org.apache.ibatis.builder.StaticSqlSource 實例
// getSqlSource() 實際上是調用的內部的 StaticSqlSource#getSqlSource() 方法
// 而StaticSqlSource#getSqlSource()每次返回的是一個新的BoundSql對象直接修改這個
// 是無效的,所以我們需要使用反射直接修改那個 StaticSqlSource 中的 sql 字符串的值
SqlSource rowSqlSource = mappedStatement.getSqlSource();
System.out.println(String.format("修改前的SQL = %s", rowSqlSource.getBoundSql(null).getSql()));
// 獲得 private final SqlSource sqlSource; 對象
Field staticsqlSourceField = rowSqlSource.getClass().getDeclaredField("sqlSource");
staticsqlSourceField.setAccessible(true);
Object staticsqlSourceObject = staticsqlSourceField.get(rowSqlSource);
// 修改 sqlSource 的 sql 字段值
Field sqlField = staticsqlSourceObject.getClass().getDeclaredField("sql");
sqlField.setAccessible(true);
String sqlFieldValue = (String)sqlField.get(staticsqlSourceObject);
sqlField.set(staticsqlSourceObject, sqlFieldValue +" limit 1");
System.out.println(String.format("修改前的SQL = %s", rowSqlSource.getBoundSql(null).getSql()));
System.out.println("============分割線===============");
List<Employee> list = employeeMapper.selectAll();
list.stream().forEach(System.out::println); // 查詢出來就是一條了OK
}