mybatis3笔记

306 阅读6分钟

mybatis

参考资料https://mybatis.org/mybatis-3/zh/index.html

注解形式

@Mapper
public interface LoginUserMapper{
    @Select("select * from login_user where id=#{id}")
    public LoginUser getLoginUserById(Integer id);
    //主键回写
    @Options(userGeneratedKeys=true,keyProperty="id")
    @Insert("insert into login_user (username) values(#{username})")
    //会自动获取对象中对应字段值
    //#{}内要么是形参名,要么是形参对应对象内字段值
    public int insertLoginUser(LoginUser loginUser);
    @Select("<script> " +
            "select username from login_user where id in" +
            "<foreach collection='ids' open='(' item='id_' separator=',' close=')'> #    {id_}</foreach>\n" +
            "        </script>")
    String[] findUsernameByIds(@Param("ids") int[] ids);
    
}

使用时只要

@Autowired
private LoginUserMapper loginUserMapper即可

对于开启驼峰自动转换

@Configuration
public class MybatisConfig{
    @Bean
    public ConfigurationCustomizer configurationCustomizer(){
        return new ConfigurationCustomizer(){
            @Overrider
            public void customize(Configuration configuration){
                configuration.setMapUnderscoreToCamelCase(true)
            }
        }
    }
}

如果不使用@Mapper注解则用@MapperScan扫描所有

//value是文件夹名,一般是com.xxx.xxx.Mapper
@MapperScan(value ="......")
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

xml文件形式

DAO层

@Mapper
public interface LoginUserMapper{
     LoginUser find(Integer id);
    
}

在resource下创建mybatis->mybatis下创建mapper文件夹

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.DAO.LoginUserMapper">
    <select id="selectLoginUserById" resultType="com.example.demo.Entity.LoginUser">
    select * from login_user where id = #{id}
  </select>
</mapper>
_______________________________________________________________________
<!-->
或者。其中的键是列名,值便是结果行中的对应值。
如果 User 类型的参数对象传递到了语句中,id、username 和 password 属性将会被查找,然后将它们的值传入预处理语句的参数中。
此处实体类必须有对应getter语句
<!-->
<mapper namespace="com.techmybatis.demo.Mapper.LoginUserMapper">
    <select id="find" resultType="hashmap" parameterType="Integer">
    select * from login_user where id = #{id}
  </select>
</mapper>
_______________________________________________________________________
<!-->
主键回写
<!-->
<insert id="insert"  useGeneratedKeys="true" keyProperty="id">
        INSERT INTO login_user (username,password,student_number)VALUE (#{username},#{password},#{studentNumber})
    </insert>

批量sql(注解里面也能这么写)

<insert id="insert"  useGeneratedKeys="true" keyProperty="id">
        INSERT INTO login_user (username,password,student_number)VALUE

        <foreach collection="loginUsers" item="item" separator=","  >
            (#{item.username},#{item.password},#{item.studentNumber})
        </foreach>
    </insert>

对应Mapper

 Integer insert(@Param("loginUsers") LoginUser[] loginUser

@Param注解

如果你的映射方法的形参有多个,这个注解使用在映射方法的参数上就能为它们取自定义名字。若不给出自定义名字,多参数(不包括 RowBounds 参数)则先以 "param" 作前缀,再加上它们的参数位置作为参数别名。例如 #{param1}, #{param2},这个是默认值。如果注解是 @Param("person"),那么参数就会被命名为 #{person}。

配置

spring.datasource.driver-class-userName=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.url.=jdbc:mysql://localhost:3306/second_hand?characterEncoding=UTF-8&serverTimezone=GMT%2B8

mybatis.mapper-locations=/mybatis/mapper/*.xml

动态sql

xml文件

if语句

  <select id="find" resultType="com.techmybatis.demo.Entity.LoginUser" >
    select * from login_user where enable = 1
    <if test="username !=null">
        AND username= #{username}
    </if>
  </select>
————————————————————————————————————————————————————————————————————————
<select id="find" resultType="com.techmybatis.demo.Entity.LoginUser" >
    select * from login_user where enable = 1
    <if test="username !=null">
        AND username= #{username}
    </if>
    <if test="loginUser!=null and loginUser.studentNumber!=null">
            AND student_number= #{loginUser.studentNumber}
    </if>
  </select>

DAO层

LoginUser find(String username);
——————————————————————————————————————————————————
LoginUser find(String username,LoginUser loginUser);

对应语句,也就是说如果传入的username不为空则在后面加AND语句(支持like模糊搜索)

loginUserMapper.find(null);
select * from login_user where enable = 1

loginUserMapper.find(“sun”);
select * from login_user where enable = 1 AND username= ?
    
//两个if是并列的,满足多少加多少    
LoginUser user = LoginUser.builder().studentNumber("0").build();
LoginUser loginUser = loginUserMapper.find(null,user);
System.out.println(loginUser);    
select * from login_user where enable = 1 AND student_number= ? 

choose语句

<select id="find" resultType="com.techmybatis.demo.Entity.LoginUser" >
    select * from login_user where enable = 1
  <choose>
      <when test="username!=null">
          AND username=#{username}
      </when>
      <when test="loginUser!=null and loginUser.password!=null" >
          AND password=#{loginUser.password}
      </when>
      <otherwise>
          AND student_number =#{studentNumber}
      </otherwise>
  </choose>

DAO层

 LoginUser find(String username,LoginUser loginUser,String studentNumber);

实验

    //类似于switch结构    
 LoginUser user = LoginUser.builder().password("0").studentNumber("0").build();
        LoginUser loginUser = loginUserMapper.find(null,user,user.getStudentNumber());    
select * from login_user where enable = 1 AND password=? 

trim,where,set语句

如果默认语句where字句缺失,比如

<select id="find" resultType="com.techmybatis.demo.Entity.LoginUser" >
    select * from login_user where
    <if test="username !=null">
        username= #{username}
    </if>
    <if test="loginUser!=null and loginUser.studentNumber!=null">
            AND student_number= #{loginUser.studentNumber}
    </if>
  </select>

如果没有匹配,就会导致sql变为 SELECT * FROM login_user where

如果只有第二个匹配,

SELECT * FROM BLOG
WHERE
AND student_number ="xxxxx"

方便起见可以使用

where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。

  <select id="find"  resultType="com.techmybatis.demo.Entity.LoginUser">
    select * from login_user
    <where>
        <if test="username!=null">
            username=#{username}
        </if>
        <if test="enable!=null">
            AND enable=#{enable}
        </if>
    </where>
</select>

DAO层

ArrayList<LoginUser> find(String username, Boolean enable);

测试

 loginUserMapper.find(null, true);
select * from login_user WHERE enable=? 

如果 where 元素没有按正常套路出牌,我们可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它的作用是移除所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容。

类似的用于动态更新语句的解决方案叫做 setset 元素可以用于动态包含需要更新的列,而舍去其它的。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

这里,set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可

能就会在生成的 SQL 语句的后面留下这些逗号。(译者注:因为用的是“if”元素,若最后一个“if”没有匹配上而前面的匹配上,SQL 语句的最后就会有一个逗号遗留)

若你对 set 元素等价的自定义 trim 元素的代码感兴趣,那这就是它的真面目:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

注意这里我们删去的是后缀值,同时添加了前缀值。

foreach

动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
  open="(" separator="," close=")">
      <!-->
      以(开头,每一项分割为,以)结尾 (()是,遍历完的)
      <!-->
        #{item}
  </foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

注意 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

如何使用注解实现动态sql

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素

就和写xml一样

@Select("<script> " +
            "select username from login_user where id in" +
            "<foreach collection='ids' open='(' item='id_' separator=',' close=')'> #    {id_}</foreach>\n" +
            "        </script>")
    String[] findUsernameByIds(@Param("ids") int[] ids);

@Param注解

@Param是MyBatis所提供的(org.apache.ibatis.annotations.Param),作为Dao层的注解,作用是用于传递参数,从而可以与SQL中的的字段名相对应,一般在2=<参数数<=5时使用最佳。

当DAO层方法函数中存在多个形参,尤其是多个javabean时,必须使用@Param注解区分

同时不必使用parameterType属性指定参数类型

它和@RequestParam没关系