MyBatis3提供了新的基于注解的配置。次要在MapperAnnotationBuilder中,定义了相干的注解:

public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {    ...    sqlAnnotationTypes.add(Select.class);    sqlAnnotationTypes.add(Insert.class);    sqlAnnotationTypes.add(Update.class);    sqlAnnotationTypes.add(Delete.class);    ......    sqlProviderAnnotationTypes.add(SelectProvider.class);    sqlProviderAnnotationTypes.add(InsertProvider.class);    sqlProviderAnnotationTypes.add(UpdateProvider.class);    sqlProviderAnnotationTypes.add(DeleteProvider.class);}

增删改查占据了绝大部分的业务操作,通过注解不在须要配置繁冗的xml文件,越来越多的sql交互均通过注解来实现。从MapperAnnotationBuilder能够看到Mybatis提供了以下相干的注解:
@Select
@Insert
@Update
@Delete
@SelectProvider
@InsertProvider
@UpdateProvider
@DeleteProvider
例如如下例子,应用@Select注解间接编写SQL实现数据查问:

@Mapperpublic interface UserMapper {    @Select("select * from t_user")    List<User> list();}

应用相似@SelectProvider高级注解能够指定某个工具类的办法来动静编写SQL,以应答简单的业务需要。
以@SelectProvider 为例,查看具体的实现,次要蕴含两个注解属性,其中type示意工具类,method 示意工具类的某个办法,用于返回具体的SQL:

@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface InsertProvider {    // 用于指定获取 sql 语句的指定类    Class<?> type();    // 指定类中要执行获取 sql 语句的办法    String method();}

应用办法如下,在ProjectSql类的getContentByProjectIds办法定义相干的sql即可,sql的定义能够通过org.apache.ibatis.jdbc.SQL来疾速实现:
@SelectProvider(type = ProjectSql.class, method = "getContentByProjectIds")
List<Integer> getContentByProjectIds(List<Integer> projectIds);

常见注入场景

2.1一般注解
实际上跟xml配置中对应的标签语法是一样的(例如@Select对应<select>标签),所以注入场景也是相似的。
在Mybatis中,#的作用次要是替换预编译语句(PrepareStatement)中的占位符?,$是间接的SQL拼接。以like含糊查问 为例子:
例如如下例子:
跟xml配置一样,like含糊查问间接应用#预编译的形式进行注解的话会触发异样,所以很多时候间接应用$进行注解:

@Select("SELECT id, name, age, email FROM user where name like '${name}'")List<User> queryUserByName(@Param("name") String name);

那么此时name前端用户可控的话,将导致SQL注入危险。
图片查看sql日志,胜利执行1/0触发sql error,阐明注入胜利:

解决这类SQL问题也很简略,应用sql的内置函数进行拼接,拼接后再采纳#预编译的形式进行查问。例如下面案例是h2数据库的,应用'||'拼接再进行预编译解决即可:

@Select("SELECT id, name, age, email FROM user where name like '%'||#{name}||'%'")List<User> queryUserByName(@Param("name") String name);

此时已应用预编译进行SQL查问:

此外,相似Order by、动静表名,无奈采纳预编译的形式状况,能够在在代码层应用间接援用的形式进行解决。
对于范畴查问in,相熟mybatis注入的话,是须要应用MyBatis自带的循环指令foreach来解决SQL语句动静拼接的,当应用注解时,就须要应用< script>标签来引入foreach了。
2.2 动静sql
2.2.1 应用< script>
要在带一般注解的映射器接口类中应用动静 SQL,能够应用script 元素。跟xml相似,次要是如下的元素:

ifchoose (when, otherwise)trim (where, set)foreach

相干的注入场景跟2.1也是相似的。也是离不开$。此外,在进行同条件多值查问(例如范畴查问in)的时候,能够应用MyBatis自带的循环指令foreach来解决SQL语句动静拼接的问题。
2.2.2 应用Provider注解
能够通过应用Provider注解指定某个工具类的办法来动静编写SQL。以@SelectProvider为例:
首先在mapper中应用@SelectProvider定义相干的办法,其中type示意工具类,method 示意工具类的某个办法,用于返回具体的SQL。例如上面的例子:
通过传递userIds以及name,查问相干的用户信息,在UserInfoSql类的getUserInfoByids办法定义了具体的SQL内容:

/**   * @param userIds 必填   * @param name 可选   * @return   */  @SelectProvider(type = UserInfoSql.class, method = "getUserInfoByids")  List<User> getUserInfoByids(List<Long> userIds, String name);     class UserInfoSql {    public String getUserInfoByids(List<Long> userIds, String name) {      SQL sql = new SQL();      sql.SELECT("id, name, age, email");      sql.FROM("user");      sql.WHERE("id in(" + Joiner.on(',').join(userIds) + ")");      if(StringUtil.isNotBlank(name)){        sql.WHERE("name like '%" + name + "%'");      }      sql.ORDER_BY("id desc");      return sql.toString();    }  }

在Controller调用具体方法就能够进行sql查问了:

 @RequestMapping(value = "/getUserInfoByids") public List<User> getUserInfoByids(String name,@RequestParam List<Long> userIds){         List<User> userList = userMapper.getUserInfoByids(userIds,name);         return userList; }

失常申请返回对应的用户信息:

后面是通过MyBatis 3 提供的工具类org.apache.ibatis.jdbc.SQL来生成SQL的。该类提供了相似select、where、ORDER_BY等办法来实现SQL生成的操作。这里有个误区,很多开发认为这里工具类会进行相干的预编译解决。
实际上Provider其实只须要返回一个SQL字符串,工具类只不过用了一些关键字做格式化而已,甚至能够间接应用StringBuffer拼接SQL语句。同样是下面的例子,List userIds是long类型,然而name是String类型,能够尝试注入:

查看相干日志,胜利执行1/0逻辑触发SQL error,也印证了Provider实际上只是 SQL 拼接,没有做相干的平安解决 :

相比@Select@,SelectProvider 只是在定义注解的形式上有所不同, 前者是间接定义 sql, 一个是在内部定义好 sql 间接援用, 没实质上的区别,所以解决办法是在对应的sql场景,应用#进行预编译进行解决,例如这里的like含糊查问和in范畴查问:

@SelectProvider(type = UserInfoSql.class, method = "getUserInfoByids")  List<User> getUserInfoByids(@Param("userIds")List<Long> userIds,@Param("name")String name);     class UserInfoSql {    public String getUserInfoByids(@Param("userIds")List<Long> userIds, @Param("name")String name) {            StringBuilder sql = new StringBuilder(128);            sql.append("< script>SELECT id, name, age, email FROM user WHERE (id in");            sql.append("<foreach item='item' collection='userIds' open='(' separator=',' close=')'>#{item}</foreach>");      if(StringUtil.isNotBlank(name)){              sql.append("and name like '%'||#{name}||'%')");      }      sql.append("ORDER BY id desc</script>");      return sql.toString();    }  }

查看sql日志,此时应用预编译进行sql解决,防止了SQL注入危险。

关键词:java培训