Mybatis中SqlMapper配置
来源:互联网 发布:安卓纪元 矩阵潜袭 编辑:程序博客网 时间:2024/05/20 16:36
在Mybatis中的SqlMapper配置文件中引入的几个扩展机制:
1.引入SQL配置函数,简化配置、屏蔽DB底层差异性
2.引入自定义命名空间,允许自定义语句级元素、脚本级元素
3.引入表达式配置,扩充SqlMapper配置的表达能力
前面两条已经举过例子,现在来看看怎么使用表达式配置。说到表达式语言,最为富丽堂皇的自然就是OGNL,但这也正是Mybatis内部访问数据的固有方式,所以也轮不到我们在这里来扩充了(事实上Mybatis的参数设置并不能使用完全的OGNL)。那么,除了OGNL,还有哪些表达式语言呢?别忘了,我们的前提是Spring环境,自然,SpEL表达式也就走入我们的视野,因此这篇文章就重点记录在SqlMapper中使用SpEL表达式。
四、在Mybatis中的SqlMapper使用SpEL表达式
1.SpEL工具类
SpEL就是Spring提供的EL表达式,虽然到Spring3才开始推出,但已经是Spring的一个基础核心模块了,地位已经差不多等同于IoC和AOP了。SpEL和OGNL类似,也有表达式、上下文环境、root对象等概念,但和OGNL不同的是,SpEL还提供了访问Spring中bean的能力——这是非常强悍的,试问一个Spring应用有多少类不是Spring管理的呢?具体的SpEL语法细节可以参考Spring的官方文档。
SpEL目前主要应用于Spring的配置,使用起来非常方便,但是在Java类中使用则比较繁琐,稍微实用一点的例子都需要创建解析器实例、创建执行环境、解析表达式、对表达式求值等步骤,如果需要访问Spring的Bean,还要设置BeanFactoryResolver等,因此,为了简化SpEL在Java中的应用,我编写了一个SpEL的帮助类:
这个工具类分成四个部分:
- 实现ApplicationContextAware接口,注入ApplicationContext(BeanFactory)对象
- 表达式求值方法
- 对表达式简单求值(还可指定返回的目标类型)
- 指定root对象,对表达式求值(还可指定返回的目标类型)
- 指定root对象和其它变量,对表达式求值(还可指定返回的目标类型)
- 表达式设置方法
- 设置表达式的值
- 指定root对象,设置表达式的值
- 指定root对象和其它变量,设置表达式的值
- 变量管理方法
- 添加变量
- 移除变量
此外,还内置了一个保护变量Tool。
编写一个测试类验证一下:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations={ "classpath:applicationContext.xml" })@Component // 该测试类本身作为一个Spring管理的bean,便于后面的测试public class SpringHelpTest { public String getBeanValue(String arg){//bean的一个方法 return "beanValue:"+arg; } @Test public void testSpelHelp(){ // 准备root对象 {key1 : 'root-value1', key2 : 'root-value2'} Root root = new Root("root-value1", "root-value2"); // 准备一般变量 Map<String, Object> vars = new HashMap<String, Object>(); vars.put("var1", "value1"); vars.put("var2", "value2"); // 直接计算简单表达式 Object rs = SpringHelp.evaluate("1+2"); Assert.assertEquals(3, rs); // 按指定类型计算简单表达式 rs = SpringHelp.evaluate("1+2", String.class); Assert.assertEquals("3", rs); // 访问root对象的属性 rs = SpringHelp.evaluate(root, "key1"); Assert.assertEquals("root-value1", rs); // 访问一般变量 rs = SpringHelp.evaluate(root, "#var2", vars); Assert.assertEquals("value2", rs); // 访问root对象 rs = SpringHelp.evaluate(root, "#root", vars); Assert.assertTrue(rs == root); // 访问Spring管理的bean,同时传入的参数又是root对象的属性 rs = SpringHelp.evaluate(root, "@springHelpTest.getBeanValue(key2)", vars); Assert.assertEquals("beanValue:root-value2", rs); // 设置root对象的属性 SpringHelp.setValue(root, "key1", "new-root-value1"); rs = SpringHelp.evaluate(root, "key1"); Assert.assertEquals("new-root-value1", rs); //访问工具类,其中Tool.DATE.getDate()的作用是获取当前日期 rs = SpringHelp.evaluate("#Tool.DATE.getDate()"); Assert.assertEquals(Tool.DATE.getDate(), rs); } public class Root{ String key1; String key2; Root(String key1, String key2){ this.key1 = key1; this.key2 = key2; } // 省略getter/setter方法 }}
有了这个静态帮助类,在Java中使用SpEL就方便很多了。
2.编写表达式处理器
利用SpEL帮助类,编写表达式处理器IExpressionHandler的实现,具体逻辑参看代码中的注释
public class SpelExpressionHandler implements IExpressionHandler { /** * 直接返回true,也就是说不做进一步判断,支持所有的${(exp)}、#{(exp)}内的表达式 * 由于支持所有表达式,实际上起到了一种拦截作用,所以需要注意,注册该实现时必须最低优先级 */ @Override public boolean isSupport(String expression) { return true; } /** * 对SqlMapper配置中的表达式求值 */ @Override public Object eval(String expression, Object parameter, String databaseId) { /** * 如果以spel:为前缀,则将mybatis包装后的参数、数据库id以及表达式自身一起封装一个新的root对象 * 因此在exp表达式中可以通过params.paramName、databaseId等形式访问 */ if(expression.toLowerCase().startsWith("spel:")){ expression = expression.substring(5); Root root = new Root(parameter, databaseId, expression); return SpringHelp.evaluate(root, expression); } /** * 否则将databaseId作为一个特殊名称的变量 * 因此在exp表达式中可以通过paramName、#databaseId等形式访问 */ else{ Map<String, Object> vars = new HashMap<String, Object>(); vars.put("databaseId", databaseId); return SpringHelp.evaluate(parameter, expression, vars); } } public class Root { private final Object params; private final String databaseId; private final String expression; public Root(Object params, String databaseId, String expression) { this.params = params; this.databaseId = databaseId; this.expression = expression; } // 省略getter/setter方法 }}
3.注册表达式处理器
如上面的注释,注册的时候需要注意一点,优先级要最低,以避免所有表达式都被拦截,导致其它的处理器不生效。
保证优先级最低,有一种方法,就是实现Spring中的Order接口,并且将该实现类的order值设置为最大,然后按Order排序;另外一种方法,就是干脆另起炉灶,单独一个属性保存默认处理器,只有其它处理器都不支持的时候才使用默认处理器,请看下面的代码:
/** * 表达式处理器 */private static final Set<IExpressionHandler> expressions = new LinkedHashSet<IExpressionHandler>();/** * 默认表达式处理器 */private static final IExpressionHandler defaultExpressionHandler = new SpelExpressionHandler();/** * 获取表达式处理器 * @param node * @return */public static IExpressionHandler getExpressionHandler(String expression){ for(IExpressionHandler handler : expressions){ if(handler.isSupport(expression)){ return handler; } } return defaultExpressionHandler;}
4.修改SqlMapper中配置
<?xml version="1.0" encoding="UTF-8" ?><mapper xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://dysd.org/schema/sqlmapper" xmlns:e="http://dysd.org/schema/sqlmapper-extend" xsi:schemaLocation="http://dysd.org/schema/sqlmapper http://dysd.org/schema/sqlmapper.xsd http://dysd.org/schema/sqlmapper-extend http://dysd.org/schema/sqlmapper-extend.xsd" namespace="org.dysd.dao.mybatis.mapper.IExampleDao"> <select id="selectString" resultType="string"> select PARAM_NAME, ${(@spelBean.param(paramName))} AS TEST_SPEL from BF_PARAM_ENUM_DEF where PARAM_NAME $like{#{(spel:@spelBean.root(#root,params.paramName)), jdbcType=VARCHAR}} order by SEQNO </select></mapper>
5.编写配置中的bean
@Component("spelBean")public class SpelBean { public String param(String paramName){ // 测试的是${()},所以返回结果中添加单引号 return "'PARAM-"+paramName+"'"; } public String root(SpelExpressionHandler.Root root,String paramName){ // 测试spel:为前缀的表达式,所以可以直接访问SpelExpressionHandler.Root对象 return "ROOT-"+root.getDatabaseId()+"-"+paramName; }}
6.编写Dao接口
@Repositorypublic interface IExampleDao { public String selectString(@Param("paramName")String paramName);}
7.编写JUnit测试类
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations={ "classpath:spring/applicationContext.xml" })@Servicepublic class ExampleDaoTest{ @Resource private IExampleDao dao; @Test public void testSelectString(){ try { String a = dao.selectString("DISPLAY_AREA"); Assert.assertEquals("显示区域", a); } catch (Exception e) { e.printStackTrace(); } }}
8.执行测试
20161119 19:00:44,298 [main]-[DEBUG] ==> Preparing: select PARAM_NAME, 'PARAM-DISPLAY_AREA' AS TEST_SPEL from BF_PARAM_ENUM_DEF where PARAM_NAME LIKE CONCAT('%',?,'%') order by SEQNO 20161119 19:00:48,001 [main]-[DEBUG] ==> Parameters: ROOT-MySQL-DISPLAY_AREA(String)
可以看到,无论是${(exp)}还是#{(exp)},其中的exp都已经得到正确的解析了。
在SqlMapper中可以调用Spring的Bean,大大丰富了SqlMapper的表达能力,但是对于${(exp)}这种情形,由于是字符串的简单替换,也存在SQL注入的风险,因此一般只使用#{(exp)}。
- Mybatis中SqlMapper配置
- Mybatis中SqlMapper配置的扩展与应用(3)
- mybatis SqlMapper resultmap配置timestamp类型
- Mybatis学习笔记-sqlMapper
- Mybatis之SQLMapper大全
- Mybatis 通用数据库接口 SqlMapper
- Mybatis中sqlmapper的xml文件中对“<”二义性的处理
- (外键)mybatis的sqlmapper配置文件中处理外键提供两种解决方案
- 在mybatis的sqlMapper中使用Ognl判断动态sql语句
- MyBatis直接执行SQL的工具SqlMapper
- MyBatis直接执行SQL的工具SqlMapper
- MyBatis直接执行SQL的工具SqlMapper
- MyBatis直接执行SQL的工具SqlMapper
- MyBatis-sqlMapper传入List类型参数,返回List类型参数。
- mybatis sqlmapper test 判断非字符串类型的值
- eclipse中配置myBatis
- eclipse中配置myBatis
- mybatis中配置mapper
- On make and cmake
- Java 初学
- 百万并发量苹果官网准备好了吗?——一分钟学会服务器压力测试
- 文章标题
- CodeForces 235E
- Mybatis中SqlMapper配置
- 代理服务器的分类
- 编程求 n 这个数有多少个0~9?
- javaIOC框架Guice
- linux busybox 1.25.x
- 信号量之间的PV操作
- 协议森林16 小美的桌号(DHCP协议)
- spark配置详解
- 转发代理服务器与反向代理服务器