MyBatis的使用


${} 为字符拼接,需要手动加单引号。

#{} 为占位符拼接,当为字段进行赋值时,会自动在sql语句中添加单引号。

单个字面量类型的参数

若mapper接口中的方法参数为单个的字面量类型,可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加单引号。

mapper接口方法:
User getUserByUsername(String username);
xml映射文件:
<select id="getUserByUsername" resultType="User">
        SELECT * FROM t_user WHERE username = '${username}'
</select>
或者
<select id="getUserByUsername" resultType="User">
        SELECT * FROM t_user WHERE username = #{username}
</select>

多个字面量类型的参数

以下默认使用占位符***#{}***进行变量赋值。

一、默认情况

当mapper接口方法有多个变量参数时,Mybatis会自动将这些变量参数放入到一个自行创建的map集合中,其以两种键值对方式进行存储:

  1. arg0arg1 …为键,以传进来的参数为值
  2. param1param2 …为键,以传进来的参数为值
mapper接口方法:
User checkLogin(String username, String password);
xml映射文件:
<select id="checkLogin" resultType="User">
        SELECT * FROM t_user WHERE username = #{arg0} AND password = #{arg1}
</select>
或者
<select id="checkLogin" resultType="User">
        SELECT * FROM t_user WHERE username = #{param0} AND password = #{param1}
</select>

二、map集合存储多个变量参数

通过一个Map集合自行定义键值,并用***#{}*** 进行变量赋值。

Service业务层:
Map<String, Object> map = new HashMap<>();
map.put("username", "admin");
map.put("password", "123456");
mapper接口方法:
User checkLoginByMap(Map<String, Object> map);
xml映射文件:
<select id="checkLoginByMap" resultType="User">
        SELECT * FROM t_user WHERE username = #{username} AND password = #{password}
</select>

三、实体类型的参数

当mapper接口方法的参数是实体类型的参数时,只需要通过#{}以键的方式访问属性值即可。

mapper接口方法:
Integer insertUser(User user);
xml映射文件:
<insert id="insertUser">
        INSERT INTO t_user VALUES (null, #{username}, #{password}, #{age}, #{sex}, 
                                   #{email})
</insert>

四、@Param注解

当使用@Param注解时,同样MyBatis会创建一个map集合,这时会将@Param注解的参数为键,传进来的参数为值放到到map集合中,最后用***#{}*** 进行变量赋值。

mapper接口方法:
User checkLoginByParam(@Param("username") String username, @Param("password") String password);
xml映射文件:
<select id="checkLoginByParam" resultType="User">
        SELECT * FROM t_user WHERE username = #{username} AND password = #{password}
</select>

MyBatis查询功能

一、查询的数据只有单条

  1. 用实体类对象接收

    mapper接口方法:
    User getUserById();
    xml映射文件:
    <select id="getUserById" resultType="com.hda.pojo.User">
            SELECT * FROM t_user WHERE id = 4
    </select>
    
  2. 用map集合接收

mapper接口方法:
Map<String, Object> getUserByIdToMap(@Param("id") Integer id);
xml映射文件:
<select id="getUserByIdToMap" resultType="map">
        SELECT * FROM t_user WHERE id = #{id}
</select>
结果以字段名为键,以数据库查询出字段名对应的值为值:
{password=123456, sex=, id=4, age=23, email=123456@qq.com, username=张三}

二、查询的数据有多条

  1. 用实体类类型的list集合接收
mapper接口方法:
List<User> getAllUser();
xml映射文件:
<select id="getAllUser" resultType="User">
        SELECT * FROM t_user
</select>
  1. 用map类型的list集合接收
mapper接口方法:
@MapKey("id")
Map<String, Object> getAllUserByMapKeyToMap();
xml映射文件:
<select id="getAllUserByMapKeyToMap" resultType="map">
        SELECT * FROM t_user
</select>
结果以@MapKey注解的字段为键(一般以id为键),以查询出的每条数据转换的map集合作为值。

特殊SQL执行

一、模糊查询

mapper接口方法:
List<User> getUserByLike(@Param("username") String username);
xml映射文件:
<select id="getUserByLike" resultType="User">
    <!-- 三种模糊查询 -->
    <!--SELECT * FROM t_user WHERE username LIKE '%${username}%'-->
    <!--SELECT * FROM t_user WHERE username LIKE CONCAT('%', #{username}, '%')-->
    SELECT * FROM t_user WHERE username LIKE "%"#{username}"%"
</select>

二、批量删除

mapper接口方法:
Integer deleteMore(@Param("ids") String ids);
xml映射文件:
<delete id="deleteMore">
        DELETE FROM t_user WHERE id IN (${ids})
</delete>

三、获取自增的主键值

service业务层:
User user = new User();
user.setUsername("赵六");
user.setPassword("123");
user.setAge(23);
user.setSex("女");
user.setEmail("3341@qq.com");
mapper.insertUser(user);
System.out.println(user.id);
mapper接口方法:
void insertUser(User user);
xml映射文件:
<!--
    useGeneratedKeys: 设置当前sql语句使用了自增的主键(即id)
    keyProperty: 将自增主键的值赋值给传输到映射文件中参数的某个属性
-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
     INSERT INTO t_user VALUES (null, #{username}, #{password}, #{age}, #{sex}, #{email})
</insert>
结果为:
3(即插入数据库一条记录自动生成的自增id的值)

数据库的字段名和实体类的属性名不一致的解决方法

一、给字段名起一个与属性名相同名字的别名

mapper接口:
List<Emp> getAllEmp();
xml映射文件:
<select id="getAllEmp" resultType="Emp">
     	SELECT eid, emp_name empName, age, sex, email FROM t_emp
</select>

二、设置全局配置,自动映射为驼峰

<setting name="mapUnderscoreToCamelCase" value="true"/>

三、resultMap设置自定义的映射关系

mapper接口:
List<Emp> getAllEmp();
xml映射文件:
<resultMap id="empResultMap" type="Emp">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
</resultMap>
<select id="getAllEmpByResultMap" resultMap="empResultMap">
        SELECT * FROM t_emp
</select>

多对一映射处理

当实体类型中嵌套其他实体类型时,该实体对象是多个对象

public class Emp {
    private Integer eid;
    private String empName;
    private Integer age;
    private String sex;
    private String email;
    private Dept dept;//包含Dept对象
}
public class Dept {
    private Integer did;
    private String deptName;
}

一、级联属性赋值

mapper接口:
/**
 * 查询员工以及员工所对应的部门信息
 */
Emp getEmpAndDept(@Param("eid") Integer eid);
xml映射文件:
<!--级联属性赋值-->
<resultMap id="empAndDeptResultMapOne" type="Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <result property="dept.did" column="did"/>
    <result property="dept.deptName" column="dept_name"/>
</resultMap>

<select id="getEmpAndDept" resultMap="empAndDeptResultMapOne">
    SELECT * FROM t_emp
    LEFT JOIN t_dept ON t_emp.did = t_dept.did
    WHERE t_emp.eid = #{eid}
</select>

二、association属性赋值

mapper接口:
/**
 * 查询员工以及员工所对应的部门信息
 */
Emp getEmpAndDept(@Param("eid") Integer eid);
xml映射文件:
<!--通过association属性赋值-->
<resultMap id="empAndDeptResultMapTwo" type="Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <!--
        association:处理多对一的映射关系
        property:需要处理多对一的映射关系的属性名
        javaType:该属性的类型
    -->
    <association property="dept" javaType="Dept">
        <id property="did" column="did"/>
        <result property="deptName" column="dept_name"/>
    </association>
</resultMap>

<select id="getEmpAndDept" resultMap="empAndDeptResultMapTwo">
    SELECT * FROM t_emp
    LEFT JOIN t_dept ON t_emp.did = t_dept.did
    WHERE t_emp.eid = #{eid}
</select>

三、分步查询

全局设置:
<!--开启延迟加载(与分步查询有关)-->
<setting name="lazyLoadingEnabled" value="true"/>

mapper接口:
/**
 * 通过分步查询查询员工以及员工所对应的部门信息
 * 分步查询第一步: 查询员工信息
 * 分步查询第二步: 通过did查询员工对应的部门
 */
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);
Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);

xml映射文件:
<resultMap id="getEmpAndDeptByStepResultMap" type="Emp">
    <id property="eid" column="eid"/>
    <result property="empName" column="emp_name"/>
    <!--
        select:设置分布查询的sql的唯一标识(唯一标识指mapper接口的全类名.方法名)
        column:设置分步查询的条件
        fetchType:当开启了全局的延迟加载之后,可设置该属性来控制延迟加载的效果
            eager:立即加载 lazy:延迟加载
    -->
    <association property="dept"
                 select="com.hda.mapper.EmpMapper.getEmpAndDeptByStepTwo"
                 column="did"
                 fetchType="eager">
    </association>
</resultMap>
<select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStepResultMap">
    SELECT * FROM t_emp WHERE eid = #{eid}
</select>
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
    SELECT * FROM t_dept WHERE did = #{did}
</select>

一对多映射处理

public class Dept {
    private Integer did;
    private String deptName;
    private List<Emp> emps;
}
public class Emp {
    private Integer eid;
    private String empName;
    private Integer age;
    private String sex;
    private String email;
}

一、collection属性赋值

mapper接口:
/**
 * 获取部门以及部门中所有员工的信息
 */
Dept getDeptAndEmp(@Param("did") Integer did);
xml映射文件:
<resultMap id="deptAndEmpResultMap" type="Dept">
    <id property="did" column="did"/>
    <result property="deptName" column="dept_name"/>
    <!--
        collection: 处理一对多的映射关系
        ofType: 表示该属性所对应的集合中存储数据的类型
    -->
    <collection property="emps" ofType="Emp">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="email" column="email"/>
    </collection>
</resultMap>

<select id="getDeptAndEmp" resultMap="deptAndEmpResultMap">
    SELECT * FROM t_dept
    LEFT JOIN t_emp ON t_dept.did = t_emp.did
    WHERE t_dept.did = #{did}
</select>

动态SQL

根据条件动态拼装SQL语句。

一、if

根据标签中test属性对应的表达式决定标签中的内容是否需要拼接到SQL中。

<select id="getEmpByConditionOne" resultType="Emp">
    SELECT * FROM t_emp
    WHERE 1 = 1
        <if test="empName != null and empName != ''">
            AND emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            AND age = #{age}
        </if>
        <if test="sex != null and sex != ''">
            AND sex = #{sex}
        </if>
        <if test="email != null and email != ''">
            AND email = #{email}
        </if>
</select>

二、where

当where标签内有内容时,会自动生成where关键字,并且会将内容前多余的and或or去掉

当where标签中没有内容时,此时where标签没有任何效果

注意:where标签不能将内容后多余的and或or去掉

<select id="getEmpByConditionTwo" resultType="Emp">
    SELECT * FROM t_emp
    <where>
        <if test="empName != null and empName != ''">
            AND emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            AND age = #{age}
        </if>
        <if test="sex != null and sex != ''">
            AND sex = #{sex}
        </if>
        <if test="email != null and email != ''">
            AND email = #{email}
        </if>
    </where>
</select>

三、trim

trim标签为where标签的增强版。

prefix|suffix: 将trim标签中内容的前面或者后面添加指定内容,
prefixOverrides|suffixOverrides:将trim标签中内容的前面或者后面去掉指定内容。

<select id="getEmpByCondition" resultType="Emp">
    SELECT * FROM t_emp
    <trim prefix="where" suffixOverrides="AND|OR">
        <if test="empName != null and empName != ''">
            emp_name = #{empName} AND
        </if>
        <if test="age != null and age != ''">
            age = #{age} OR
        </if>
        <if test="sex != null and sex != ''">
            sex = #{sex} AND
        </if>
        <if test="email != null and email != ''">
            email = #{email}
        </if>
    </trim>
</select>

四、foreach

当SQL语句要删除多个数据时,传进来的参数若为数组的形参,那么要么使用in(1,2,3)进行多条删除,要么使用where id = 1 or id = 2 or id = 3进行多条删除。

而foreach关键字则可以进行循环操作。

collection: 设置要循环的数组或集合
item:表示数组或集合中的每一个数据
separator:循环体之间的分隔符
open:循环体开头添加
close:循环体结尾添加

mapper接口:
/**
 * 通过数组实现批量删除
 */
Integer deleteByArray(@Param("eids") Integer[] eids);

xml映射文件:
<delete id="deleteByArray">
    DELETE FROM t_emp WHERE eid 
    IN(
        <foreach collection="eids" item="eid" separator=",">
            #{eid}
        </foreach>
    )
</delete>
或者:
<delete id="deleteByArray">
    DELETE FROM t_emp WHERE eid 
    <foreach collection="eids" item="eid" separator="," open="(" close=")">
        #{eid}
    </foreach>
</delete>
或者使用where id = 1 or id = 2 or id = 3<delete id="deleteByArray">
    DELETE FROM t_emp WHERE 
    <foreach collection="eids" item="eid" separator="or" >
        eid = #{eid}
    </foreach>
</delete>
mapper接口:
/**
 * 通过List集合实现批量添加
 */
Integer insertByList(@Param("emps") List<Emp> emps);
xml映射文件:
<insert id="insertByList">
    INSERT INTO t_emp VALUES
        <foreach collection="emps" item="emp" separator=",">
            (null, #{emp.empName}, #{emp.age}, #{emp.sex}, #{emp.email}, null)
        </foreach>
</insert>

MyBatis的缓存

一、MyBatis的一级缓存

MyBatis的一级缓存默认开启的。一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。

例如在同一个SqlSession下进行俩次相同的查询操作,会有缓存,那么SQL语句只会执行一次:

SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEid(1);
System.out.println(emp1);
CacheMapper mapper2 = sqlSession1.getMapper(CacheMapper.class);
Emp emp2 = mapper2.getEmpByEid(1);
System.out.println(emp2);
控制台执行结果:
DEBUG 09-02 11:27:51,943 ==>  Preparing: SELECT * FROM t_emp WHERE eid = ? (BaseJdbcLogger.java:137) 
DEBUG 09-02 11:27:51,961 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 09-02 11:27:51,983 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='张三', age=30, sex='男', email='123@qq.com', dept=null}
Emp{eid=1, empName='张三', age=30, sex='男', email='123@qq.com', dept=null}

若在俩个SqlSession下进行俩次相同的查询操作,那么SQL语句则会执行两次:

SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpByEid(1);
System.out.println(emp1);
SqlSession sqlSession2 = SqlSessionUtils.getSqlSession();
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp2 = mapper2.getEmpByEid(1);
System.out.println(emp2);
控制台执行结果:
DEBUG 09-02 11:31:35,261 ==>  Preparing: SELECT * FROM t_emp WHERE eid = ? (BaseJdbcLogger.java:137) 
DEBUG 09-02 11:31:35,281 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 09-02 11:31:35,290 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='张三', age=30, sex='男', email='123@qq.com', dept=null}
DEBUG 09-02 11:31:35,328 ==>  Preparing: SELECT * FROM t_emp WHERE eid = ? (BaseJdbcLogger.java:137) 
DEBUG 09-02 11:31:35,328 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 09-02 11:31:35,332 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='张三', age=30, sex='男', email='123@qq.com', dept=null}

一级缓存失效的四种情况

  1. 不同的SqlSession对应不同的一级缓存

  2. 同一个SqlSession但是查询条件不同

  3. 同一个SqlSession两次查询期间执行了任何一次增删改操作

  4. 同一个SqlSession两次查询期间手动清空了缓存

    sqlSession1.clearCache(); //清除缓存
    

二、MyBatis的二级缓存

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被
缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。

二级缓存开启需满足4个条件

  1. 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置;
  2. 在映射文件中设置标签******;
  3. 二级缓存必须在SqlSession关闭或提交之后有效;
  4. 查询的数据所转换的实体类类型必须实现序列化的接口。

二级缓存失效的情况

当两次查询之间执行了任意的增删改,会同时使一级和二级缓存同时失效。

三、MyBatis缓存查询的顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
  • 如果二级缓存没有命中,再查询一级缓存。
  • 如果一级缓存也没有命中,则查询数据库。
  • SqlSession关闭之后,一级缓存中的数据会写入二级缓存。

分页插件

一、分页插件的配置

  1. 添加依赖

    <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
    <dependency>
    	<groupId>com.github.pagehelper</groupId>
    	<artifactId>pagehelper</artifactId>
    	<version>5.2.0</version>
    </dependency>
    
  2. 配置分页插件(这里在xml文件配置):

    <plugins>
        <!--设置分页插件-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
    

二、分页插件的使用

limit index,pageSize
index: 当前页的起始索引 例如要从当前页的第一条数据开始分页显示,那么index为0;要从当前页的第5条数据开始,那么index为4;
pageSize:每页显示的条数
pageNum:当前页的页码

Page<Object> page = PageHelper.startPage(2, 4);
List<Emp> list = mapper.selectAll();
list.forEach(emp -> System.out.println(emp));

控制台执行结果:
DEBUG 09-02 14:14:49,994 ==>  Preparing: SELECT count(0) FROM t_emp (BaseJdbcLogger.java:137) 
DEBUG 09-02 14:14:50,009 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 09-02 14:14:50,019 <==      Total: 1 (BaseJdbcLogger.java:137) 
DEBUG 09-02 14:14:50,020 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp LIMIT ?, ? (BaseJdbcLogger.java:137) 
DEBUG 09-02 14:14:50,021 ==> Parameters: 4(Long), 4(Integer) (BaseJdbcLogger.java:137) 
DEBUG 09-02 14:14:50,022 <==      Total: 4 (BaseJdbcLogger.java:137) 
Emp{eid=5, empName='田七', age=28, sex='男', email='123@qq.com', did=2}
Emp{eid=10, empName='a1', age=23, sex='男', email='666@qq.com', did=null}
Emp{eid=11, empName='a2', age=23, sex='男', email='666@qq.com', did=null}
Emp{eid=12, empName='a3', age=23, sex='男', email='666@qq.com', did=null}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值