之前的博文讲述了mybatis注解的简单用法,包括@Select,@Insert,@Update,@Delete,@Result,@Param和@Options。本文将记录多个Provider的用法。
如何理解Provider呢?只是换了一种形式。将原来的SQL注解绑定放到了一个类里,再将该类注解绑定至原有位置。
先对比看一下形式吧。
原有SQL注解绑定
@Insert("Insert into hero(name,age) values(#{sName},#{nAge})")
@Options(useGeneratedKeys=true, keyColumn="id", keyProperty="id")
void addHero(Hero hero);
使用provider后的形式
@InsertProvider(type=HeroProvider.class,method = "addHero")
void addHero(Hero hero);
即,HeroProvider类中的addHero方法所返回的SQL语句,跟@Insert所绑定的SQL语句一致。
我们注意到插入provider的绑定是使用了@InsertProvider注解。
类似的注解,闭着眼也能想到。@SelectProvider,@UpdateProvider,@DeleteProvider,对应的功能,不用闭眼也能想到。
接下来,我们看看HeroProvider的实现。
package com.breakloop.mybatis.provider;
import com.breakloop.mybatis.entity.Hero;
import org.apache.ibatis.jdbc.SQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HeroProvider {
Logger logger= LoggerFactory.getLogger(HeroProvider.class);
public String addHero(Hero hero){
String sqlStr;
SQL sql= new SQL();
sql.INSERT_INTO("hero");
if(hero.getAge()>0 && hero.getName()!=null){
sql.VALUES("name","'"+hero.getName()+"'");
sql.VALUES("age", String.valueOf(hero.getAge()));
}
sqlStr=sql.toString();
logger.info("sql="+sqlStr);
return sqlStr;
}
}
这里需要注意的是,SQL的VALUES方法的传参都是String类型。如果某一字段为String类型,则需要给字段数据加单引号!否则,字段数据将会被识别为字段名称,导致SQL语句异常。
再来为HeroProvider 添加选择方法。看看@SelectProvider注解的使用。
public String selectByAge(int age){
String sqlStr;
SQL sql= new SQL();
sql.SELECT("*");
sql.FROM("hero");
sql.WHERE("age="+age);
sqlStr = sql.toString();
logger.info("sql="+sqlStr);
return sqlStr;
}
同时,在Mapper中进行绑定。
@SelectProvider(type = HeroProvider.class,method = "selectByAge")
List<Hero> selectByAge(int age);
从面儿上看,没什么问题。但运行时报错。
Error invoking SqlProvider method (com.breakloop.mybatis.provider.HeroProvider.selectByAge). Cannot invoke a method that holds named argument(@Param) using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.]
这种情况出现在传参不是实体类或者java.util.Map时。
解决方案有两种
(1)使用@Param对参数进行绑定,即
@SelectProvider(type = HeroProvider.class,method = "selectByAge")
List<Hero> selectByAge(@Param("aa") int age);
public String selectByAge(@Param("aa") int age){
String sqlStr;
SQL sql= new SQL();
sql.SELECT("*");
sql.FROM("hero");
sql.WHERE("age=#{aa}");
//或者
//sql.WHERE("age="+age);
sqlStr = sql.toString();
logger.info("sql="+sqlStr);
return sqlStr;
}
(2)使用Map传参
@SelectProvider(type = HeroProvider.class,method = "selectByAge")
List<Hero> selectByAge(Map params) ;
public String selectByAge(Map<String,Integer> params){
String sqlStr;
SQL sql= new SQL();
sql.SELECT("*");
sql.FROM("hero");
sql.WHERE("age="+params.get("age"));
sqlStr = sql.toString();
logger.info("sql="+sqlStr);
return sqlStr;
}
照葫芦画瓢,@UpdateProvider和@DeleteProvider也就没什么了。这里我们给出代码示例。
@UpdateProvider(type = HeroProvider.class,method = "updateHero")
void updateHero(Hero hero);
@DeleteProvider(type = HeroProvider.class,method = "deleteHero")
void deleteHeroByName(@Param("aa") String name,@Param("bb") int age);
public String updateHero(Hero hero){
String sql= new SQL(){
{
UPDATE("hero");
if(hero.getAge()>0 && hero.getName()!=null){
SET("name='"+hero.getName()+"'");
SET("age="+hero.getAge());
WHERE("id="+hero.getId());
}
}
}.toString();
logger.info("sql="+sql);
return sql;
}
public String deleteHero(@Param("aa") String sName, @Param("bb") int nAge){
String sql= new SQL(){
{
DELETE_FROM("hero");
if(nAge>0){
WHERE("age="+nAge);
}
if(sName!=null){
WHERE("name='"+sName+"'");
}
}
}.toString();
logger.info("sql="+sql);
return sql;
}
至此,注解方式的动态SQL方法小结完毕。
感觉mybatis的水很深。很多点都要深看,短时间内只能了解如何使用。至于为什么,怎么个原理,需要再挖。当然,JPA的存在,使得选择方式变多,深挖也变犹豫。呵呵~