目录

Mybatis笔记

MapperScan注解包含多个包

单个包

1
@MapperScan("com.mysiteforme.admin.dao")

多个包

1
@MapperScan({"com.mysiteforme.admin.dao","com.zipon.tpf.dao"})

./1.png

实体类忽略映射字段

非数据库字段,如果不做处理,mybatis执行sql过程中会报错,Cause: java.sql.SQLSyntaxErrorException: Unknown column ‘…’ in ‘field list’ 对非数据库字段使用注解@TableField(exist = false)

1
2
@TableField(exist = false)//:表示该属性不为数据库表字段,但又是必须使用的。
@TableField(exist = true)//:表示该属性为数据库表字段。

Mybatis-Plus 插件有这个功能,可以看一下

1
2
3
4
@TableName//:数据库表相关
@TableId//:表主键标识
@TableField//:表字段标识
@TableLogic//:表字段逻辑处理注解(逻辑删除)

script标签作用

1、用script标签包围,然后像xml语法一样书写

1
2
3
4
5
6
7
@Select({"<script>",
    "SELECT * FROM tbl_order",
    "WHERE 1=1",
    "<when test='title!=null'>",
    "AND mydate = #{mydate}",
    "</when>",
    "</script>"})

2、用Provider去实现SQL拼接,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class OrderProvider {
    private final String TBL_ORDER = "tbl_order";
    public String queryOrderByParam(OrderPara param) {
        SQL sql = new SQL().SELECT("*").FROM(TBL_ORDER);
        String room = param.getRoom();
        if (StringUtils.hasText(room)) {
            sql.WHERE("room LIKE #{room}");
        }
        Date myDate = param.getMyDate();
        if (myDate != null) {
            sql.WHERE("mydate LIKE #{mydate}");
        }
        return sql.toString();
    }
}
public interface OrderDAO {
    @SelectProvider(type = OrderProvider.class, method = "queryOrderByParam")
    List<Order> queryOrderByParam(OrderParam param);
}

注意:方式1有个隐患就是当传入参数为空的时候,可能会造成全表查询。

复杂SQL用方式2会比较灵活(当然,并不建议写复杂SQL),而且可以抽象成通用的基类,使每个DAO都可以通过这个基类实现基本的通用查询,原理类似Spring JDBC Template

3、说明:

如果XML元素嵌入在<script>XML元素中,则可以在注释值中为动态SQL使用XML元素:

1
@Select("<script>SELECT ...</script>")

但是使用<include>元素会触发SQL Mapper配置解析异常,由以下原因引起:

1
org.apache.ibatis.builder.BuilderException: Unknown element in SQL statement. at org.apache.ibatis.scripting.xmltags.XMLScriptBuilder.parseDynamicTags

如果nodeHandlers在课堂中检查方法org.apache.ibatis.builder.BuilderException,将注意到支持的元素有:

  • trim
  • where
  • set
  • foreach
  • if
  • choose
  • when
  • otherwise
  • bind

然而,包括基于注释的查询中的片段是不可能的。

参考:

https://segmentfault.com/q/1010000006875476(以上内容转自此篇回答)

https://stackoverflow.com/questions/6685655/how-to-use-dynamic-sql-query-in-mybatis-with-annotationhow-to-use-selectprovide

http://www.blogjava.net/dbstar/archive/2011/08/08/355825.html

http://www.jianshu.com/p/03642b807688

https://stackoverflow.com/questions/33129344/is-there-a-way-to-reuse-sql-fragments-in-annotation-based-mybatis-ibatis

<![CDATA[ ]]>的作用、使用、注意事项。

  1. sql中字符串拼接

    1
    
    SELECT * FROM tableName WHERE name LIKE CONCAT(CONCAT('%', #{text}), '%');
    
  2. 使用 ${…} 代替 #{…} (不能防止sql注入 #{}—>可以防止sql注入的问题)

    1
    
    SELECT * FROM tableName WHERE name LIKE '%${text}%'; 
    
  3. 程序中拼接

    Java

    1
    2
    3
    
    // or String searchText = "%" + text + "%";
    String searchText = new StringBuilder("%").append(text).append("%").toString();
    parameterMap.put("text", searchText);
    

    SqlMap.xml

    1
    
    SELECT * FROM tableName WHERE name LIKE #{text};
    

<![CDATA[ ]]> 标记避免Sql中与xml规范相冲突的字符对xml映射文件的合法性造成影响

如:<![CDATA[price_from >= #{price_from}]]>

limit参数

参数page=1,rows=3

错误写法:

1
select   *   from  xxx  limit  #{page},#{rows}

报错:

1
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1','3'' at line 1

可以看出mybatis自动给我们的参数加了 引号 ' ‘,所以报错

解决方法:#{}改为${}

正确写法:

1
select   *   from  xxx  limit  ${page},${rows}

结果封装为List

1.结果封装为List 接口示例:

1
2
接口示例
public List<Members> selectMembersListByName(String name);

配置文件示例:

1
2
3
<select id="selectMembersListByName" resultType="members">
    select * from members where member_name like #{member_name}
</select>

运行结果:

1
2
3
运行结果
[Member [id=3, member_name=关云长, password=123456, age=54], 
 Member [id=4, member_name=关云长, password=123456, age=54]]

返回值类型resultType还是对象类型并不是list,mybatis会自动将对象封装成list集合

将单条记录封装成Map<String,Object>,其中key就是表的列名,value就是对应的值

接口示例:

1
public Map<String, Object> selectMembersByIdReturnMap(Integer id);

配置文件示例: 其中resultType是map,因为mybatis为常用类已经起了别名(typeAliases)

1
2
3
<select id="selectMembersByIdReturnMap" resultType="map">
    select * from members where id = #{id}
</select>

运行结果:

1
{pass_word=123456, id=1, member_name=张三, age=25}

将多条记录封装成Map其中key是我们指定字段的名字,value是记录对象Map<String,Members>

接口示例: @MapKey就是指定map中key是什么

1
2
@MapKey("id")
public Map<String, Members> selectMembersByNameReturnMap(String name);

配置文件示例: 返回值类型还是对象

1
2
3
<select id="selectMembersByNameReturnMap" resultType="members">
    select * from members where member_name like #{member_name}
</select>

运行结果:

1
2
{3=Member [id=3, member_name=关云长, password=123456, age=54], 
 4=Member [id=4, member_name=关云长, password=123456, age=54]}

MyBatis动态传入表名

mybatis里#{}与${}的用法:

  在动态sql解析过程,#{}与${}的效果是不一样的:

#{ } 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符。

  如以下sql语句

1
  select * from user where name = #{name};

  会被解析为:

1
  select * from user where name = ?;

  可以看到#{}被解析为一个参数占位符?。

${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换

  如以下sql语句:

1
  select * from user where name = ${name};

  当我们传递参数“sprite”时,sql会解析为:

1
  select * from user where name = "sprite";

  可以看到预编译之前的sql语句已经不包含变量name了。

  综上所得, ${ } 的变量的替换阶段是在动态 SQL 解析阶段,而 #{ }的变量的替换是在 DBMS 中。

#{}与${}的区别可以简单总结如下:

  • #{}将传入的参数当成一个字符串,会给传入的参数加一个双引号
  • ${}将传入的参数直接显示生成在sql中,不会添加引号
  • #{}能够很大程度上防止sql注入,${}无法防止sql注入

  ${}在预编译之前已经被变量替换了,这会存在sql注入的风险。如下sql

1
  select * from ${tableName} where name = ${name}

  如果传入的参数tableName为user; delete user; –,那么sql动态解析之后,预编译之前的sql将变为:

1
  select * from user; delete user; -- where name = ?;

  –之后的语句将作为注释不起作用,顿时我和我的小伙伴惊呆了!!!看到没,本来的查询语句,竟然偷偷的包含了一个删除表数据的sql,是删除,删除,删除!!!重要的事情说三遍,可想而知,这个风险是有多大。

  • ${}一般用于传输数据库的表名、字段名等
  • 能用#{}的地方尽量别用${}

  进入正题,通过上面的分析,相信大家可能已经对如何动态调用表名和字段名有些思路了。示例如下:

1
2
3
4
5
6
  <select id="getUser" resultType="java.util.Map" parameterType="java.lang.String" statementType="STATEMENT">
    select 
         ${columns}
    from ${tableName}
        where         COMPANY_REMARK = ${company}
  </select>

  要实现动态调用表名和字段名,就不能使用预编译了,需添加statementType=“STATEMENT”" 。

statementType:STATEMENT(非预编译),PREPARED(预编译)或CALLABLE中的任意一个,这就告诉 MyBatis 分别使用Statement,PreparedStatement或者CallableStatement。 默认:PREPARED。这里显然不能使用预编译,要改成非预编译。

  其次,sql里的变量取值是${xxx},不是#{xxx}。

  因为${}是将传入的参数直接显示生成sql,如${xxx}传入的参数为字符串数据,需在参数传入前加上引号,如:

1
2
   String name = "sprite";
   name = "'" + name + "'";

@ Transient注解

ORM框架中是否和数据库的列做映射,帮助理解XmlTransient

Mybatis传递多个参数的4种方式

现在大多项目都是使用Mybatis了,但也有些公司使用Hibernate。使用Mybatis最大的特性就是sql需要自己写,而写sql就需要传递多个参数。面对各种复杂的业务场景,传递参数也是一种学问。

下面给大家总结了以下几种多参数传递的方法。

方法1:顺序传参法

1
public User selectUser(String name, int deptId);
1
2
3
4
<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{0} and dept_id = #{1}
</select>

#{}里面的数字代表你传入参数的顺序。

这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。

方法2:@Param注解传参法

1
public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);
1
2
3
4
<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

#{}里面的名称对应的是注解 @Param括号里面修饰的名称。 这种方法在参数不多的情况还是比较直观的,推荐使用。

方法3:Map传参法

1
public User selectUser(Map<String, Object> params);
1
2
3
4
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

#{}里面的名称对应的是 Map里面的key名称。

这种方法适合传递多个参数,且参数易变能灵活传递的情况。

方法4:Java Bean传参法

1
public User selectUser(User params);
1
2
3
4
<select id="selectUser" parameterType="com.test.User" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

#{}里面的名称对应的是 User类里面的成员属性。

这种方法很直观,但需要建一个实体类,扩展不容易,需要加属性,看情况使用。

Mybatis使用in传入List的三种方法

1.非xml方式,使用注解传in,要使用

1
2
3
4
5
6
7
8
@Select("<script>" +
            "SELECT count(DISTINCT member_id) as memberCount\n" +
            "from member_analysis\n" +
            "WHERE agent_id in <foreach item='item' index='index' collection='memberIds' open='(' separator=',' close=')'>" +
            "#{item}" +
            "</foreach>" +
            "</script>")
Integer getCountWithAgentId(@Param("memberIds") List<String> memberIds);

其中的foreach的collection直接写成@param中的值即可。

2.在入参前进行字符串封装,拼成(,,,,)传值

1
2
@Select("SELECT count(DISTINCT member_id) as memberCount from member_analysis where access_pat_id in (#{memberIds})")
    Integer getCountWithAgentId(@Param("memberIds")String memberIds);

使用方法返回

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 public static String indexForm(List<String> s){
        String content="";
        int i=0;
        for (String ss:s){
            i++;
            if (i==s.size()){
                content+=ss;
            }else {
                content+=ss+",";
            }
        }
        return content;
    }

或者使用

1
StringUtils.join(memberIds.toArray(),",")

3.正常流程的xml方法

1
2
3
4
5
6
7
8
9
    <select id="findByIds" resultMap="BaseResultMap">
        select * from dictionaries
        <where>
            dictionaries.key  in 
            <foreach  item="item" index="index" collection="memberTypes" open="(" separator="," close=" )">
                 #{item}
        	</foreach>
        </where>
    </select>

type-aliases-package配置多个

C:\Program_Green\hugo_extended_Windows-64bit\sites\KKKPJSKEY's-Case-Archives\content\posts\Mybatis-Note\2.webp

问题

现在扫描的是com.ruoyi.project.domain这个路径,我想扫描多个路径,如:com.test.project.domain这个路径,该如何配置?

我试了加逗号及空格都不行

typeAliasesPackage: com.ruoyi.project..domain,com.test.project..domain

解决

typeAliasesPackage: com.ruoyi.project..domain,;com.test.project..domain,使用,;

详细讲解可以参考:type-aliases-package配置多个