mybatis进阶

mybatis进阶

接口代理方式实现 Dao 层

  • 接口代理方式可以让我们只编写接口即可,而实现类对象由 MyBatis 生成。
  • 实现规则
    • 映射配置文件中的名称空间必须和 Dao 层接口的全类名相同。
    • 映射配置文件中的增删改查标签的 id 属性必须和 Dao 层接口的方法名相同。
    • 映射配置文件中的增删改查标签的 parameterType 属性必须和 Dao 层接口方法的参数相同。
    • 映射配置文件中的增删改查标签的 resultType 属性必须和 Dao 层接口方法的返回值相同。
  • 获取动态代理对象
    • SqlSession 功能类中的 getMapper() 方法。

MyBatis映射配置文件 - 动态 SQL

动态SQL:if和where标签

目标
  1. 动态SQL语句if标签的使用
  2. 动态SQL语句where标签的使用
应用场景

如果使用一个实体类来封装所有的查询条件,如果这个实体类属性有值,则参与查询,否则就不做为查询条件。

在这里插入图片描述

if标签

作用

判断某个条件是否为真,如果为真则拼接标签体中的SQL语句

UserMapper.xml
  1. 根据用户名称和性别查询用户
  2. if:判断用户名称不为空,且不为空字符串,则作为查询条件
  3. if:判断用户性别不为空,且不为空字符串,则作为查询条件
<!--
根据用户名称和性别查询用户
if 标签的作用:判断某个条件是否为真,如果为真则拼接标签体中的SQL语句
-->
<select id="findUserByNameAndSex" resultType="user">
    select * from user where
    <if test="username!=null and username!=''">
        username like "%"#{username}"%"
    </if>
    <if test="sex!=null and sex!=''">
        and sex = #{sex}
    </if>
</select>
测试代码
  1. 通过用户名和性别查询多个用户
  2. 同时设置名字和性别
  3. 只设置名字或只设置性别
  4. 名字和性别一个都不设置
@Test
public void testFindUserByNameAndSex() {
    //封装查询条件
    User user = new User();
    user.setUsername("精");
    user.setSex("女");
    List<User> users = userMapper.findUserByNameAndSex(user);
    System.out.println(users);
}

疑问:if标签如果第1个条件没有,会出现什么情况?如何解决这个问题?

如果单独使用if标签,有时会出现问题,因为拼接的SQL语句是不正确的

where标签

作用
  1. 相当于where关键字,在SQL语句中可以省略where关键字
  2. 根据拼接SQL字符串的情况,自动去掉多余的and, or , where关键字
需求
  1. 只查性别
  2. 同时查姓名和性别
  3. 一个条件都不查
UserMapper.xml
  1. 根据用户名称和性别查询用户
  2. if标签写在where标签内部
    1. if:判断用户名称不为空,且不为空字符串,则作为查询条件
    2. if:判断用户性别不为空,且不为空字符串,则作为查询条件
<select id="findUserByNameAndSex" resultType="user">
    select * from user
    <where>
        <if test="username!=null and username!=''">
            username like "%"#{username}"%"
        </if>
        <if test="sex!=null and sex!=''">
            and sex = #{sex}
        </if>
    </where>
</select>
小结
  1. if标签的作用:如果判断条件为真,就拼接if标签体中的SQL语句
  2. where标签的作用:
    1. 相当于where关键字
    2. 去掉多余的and, or, where关键字

set标签

目标

set标签的使用

作用

用于update标签

  1. 相当于update中set关键字
  2. 同时修改多个字段,字段之间使用逗号分隔。可能拼接的时候会出现多余的逗号,set标签可以去掉多余的逗号
应用场景

更新用户信息的时候,有些实体类的属性为空,为空就不更新,不为空才更新这个字段。

在这里插入图片描述

需求

动态修改用户数据

  1. 如果username属性不为空,则更新这个字段。
  2. 如果sex不为空则更新这个字段

效果

在这里插入图片描述

声明mapper接口方法

编写更新用户的方法,参数是user对象

/**
 * 修改姓名和性别
 */
int updateUser(User user);
配置mapper映射文件

通过id来更新用户名或性别

  1. update标签更新用户数据
  2. 如果username不为空而且不为空串,则更新
  3. 如果sex不为空而且不为空串,则更新
  4. 最后加上where条件

注:每个更新的字段后面都加上了逗号

<!--
1. 如果username属性不为空,则更新这个字段。
2. 如果sex不为空则更新这个字段
-->
<update id="updateUser">
    update user
    <set>
        <if test="username!=null and username!=''">
            username=#{username},
        </if>
        <if test="sex!=null and sex!=''">
            sex=#{sex}
        </if>
    </set>
    where id=#{id}
</update>
测试
@Test
public void testUpdateUser() {
    //封装实体对象
    User user = new User();
    user.setUsername("黑熊精");
    user.setSex("男");
    user.setId(8);
    //调用方法
    int row = userMapper.updateUser(user);
    System.out.println("更新了" + row + "行记录");
}
小结

set标签的作用是?

  1. 相当于更新语句中set关键字
  2. 可以去掉多余的逗号
foreach标签:参数类型是集合
目标

foreach标签的使用:参数类型是集合的情况

作用

遍历集合或数组,将集合或数组中每个元素都做为SQL语句的一部分进行拼接,拼接多次

需求

使用list集合封装多个User对象,添加到数据库中。

声明mapper接口方法

提问:一条insert语句插入多条记录的MySQL语句如何编写?

insert into 表名 values (全部列名),(全部列名),(全部列名)
批量添加用户的方法
  1. 参数是List,包含多个User对象
int addUsers(List<User> users);
配置mapper映射文件
UserMapper.xml

批量新增用户,参数类型是:list

<!--使用foreach标签遍历一个集合,生成SQL语句-->
<insert id="addUsers">
    insert into user (username, birthday, sex, address) values
    <!--
    foreach标签的属性:
    collection 指定是集合还是数组,如果是集合使用list,如果是数组使用array
    item 表示集合中每个元素的变量名,这里是一个实体类对象
    separator 每次拼接完以后,添加1个符号分隔
    -->
    <foreach collection="list" item="user" separator=",">
        (#{user.username},#{user.birthday},#{user.sex},#{user.address})
    </foreach>
</insert>
测试
  1. 创建ArrayList,每个元素是User
  2. 调用方法添加多条记录
/**
 * 添加多个用户
 */
@Test
public void testAddUsers() {
    //创建一个List集合,添加3个元素
    List<User> users = new ArrayList<>();
    users.add(new User(null,"牛魔王", Date.valueOf("1980-01-30"),"男","火焰山"));
    users.add(new User(null,"红孩儿", Date.valueOf("2009-05-08"),"男","火云洞"));
    users.add(new User(null,"玉面狐狸", Date.valueOf("2005-11-01"),"女","狐狸洞"));
    int row = userMapper.addUsers(users);
    System.out.println("添加了" + row + "行记录");
}
小结
foreach标签的属性作用:用于遍历集合或数组,多次拼接生成SQL语句
collection取值:如果是集合使用list,如果是数组使用array
item代表集合中每个元素,给元素定义一个名字
separator每次遍历完成以后添加1个分隔符
#{变量名.属性}标签体中引用的时候,必须使用变量名.属性名

foreach标签:参数类型是数组

目标

foreach标签:参数类型是数组

需求

实现批量删除用户

分析

删除多条记录的SQL语句如何编写?

delete from 表名 where id in (1,3,5)
声明mapper接口方法
/**
 批量删除用户
 */
int deleteUsers(int[] ids);
配置mapper映射文件

foreach标签

  • 注:parameterType 因为内置别名中没有数组类型的参数,所以当参数传递的是list和数组,都写成list
UserMapper.xml
<!--批量删除用户 -->
<delete id="deleteUsers">
    delete from user where id in
    <!--
    collection: 表示要遍历的集合类型,数组使用array
    item: 表示其中每个元素的变量名
    open:表示循环开始前添加一个符号,只会执行1次
    separator 每次拼接完以后,添加1个符号分隔,执行多次
    close:表示循环结果以后添加一个符号,只会执行1次
    -->
    <foreach collection="array" open="(" item="id" separator="," close=")">
        #{id}
    </foreach>
</delete>
测试代码
  1. 删除多个用户
  2. 返回影响的行数
//删除多个用户
@Test
public void testDeleteUsers() {
    int [] ids = {2,4,6};
    int row = userMapper.deleteUsers(ids);
    System.out.println("删除了" + row + "条记录");
}
小结
foreach标签中的属性说明
collection如果遍历数组,取值是array
open遍历开始前添加1次符号
close遍历结束后添加1次符号
item每次元素的变量名,通过#{变量名}来引用
separator每次遍历的时候添加分隔符

sql和include标签

目标

学习sql和include标签的作用

作用

  1. sql标签:定义一个SQL语句代码段,并且使用id设置唯一的标识
  2. include标签:包含上面定义的语句代码段

这2个标签是要配合使用的,对定义的SQL代码段进行重用

需求

应用场景:以前讲过分页的操作,至少要查询2条SQL语句

  1. 这一页的数据,同时满足生日在这个范围之类
  2. 总共有多少条记录,同时满足生日在这个范围之类

如果有多条SQL语句,它后面的where查询条件是一样的,我们就可以将这个查询条件定义成一个代码片段进行重用。

  1. 通过生日最小值,最大值的区间,组合查询多个用户。
select * from user where 条件
  1. 使用相同的查询条件,查询一共有多少条记录。
select count(*) from user where 条件

上面两条语句where后面的条件是一样的,可以考虑重用。

步骤
  1. 使用Map封装上面的条件: minDate,maxDate
  2. 在UserMapper.xml中使用sql标签定义查询条件代码块
  3. 在查询方法中,使用include引用上面的条件代码块
  4. 查询符合条件的用户信息,返回List<User>
  5. 查询符合条件的记录数,返回int
  • 注:关于大于号小于号的处理。可以使用<(小于),>(大于)或者使用CDATA
UserMapper接口
/**
 * 查询满足条件的所有用户
 * Map封装多个查询条件
 */
List<User> findUsersByMap(Map<String, String> map);

/**
 * 查询满足条件的记录有多少条
 */
int findCountByMap(Map<String, String> map);
UserMapper.xml
<!--
1. sql标签定义查询条件代码块
    Map = {minDate='1999-9-9', maxDate='2000-1-1'}
    在XML中<或>符号是有特殊含义的,通常不能直接写在XML中
    有两种解决方案:
    1) 使用转义:&lt; &gt;
    2) 使用CDATA包裹起来的内容表示纯文本内容,XML解析器不对它进行解析
 -->
<sql id="sqlCondition">
    <where>
        <if test="minDate!=null and minDate!=''">
            birthday >= #{minDate}
        </if>
        <if test="maxDate!=null and maxDate!=''">
            <![CDATA[
              and birthday <= #{maxDate}
            ]]>
        </if>
    </where>
</sql>

<!--
include标签的作用:引用使用sql标签定义的代码段
   属性:refid对应上面sql的id值
-->
<select id="findUsersByMap" resultType="user">
    select * from user  <include refid="sqlCondition"/>
</select>

<select id="findCountByMap" resultType="int">
    select count(*) from user <include refid="sqlCondition"/>
</select>
测试代码
@Test
public void testFindUsersByMap() {
    HashMap<String, String> map = new HashMap<>();
    map.put("minDate","1980-01-01");
    map.put("maxDate", "1984-01-01");
    List<User> users = userMapper.findUsersByMap(map);
    users.forEach(System.out::println);
}

@Test
public void testFindCountByMap() {
    HashMap<String, String> map = new HashMap<>();
    map.put("minDate","1980-01-01");
    map.put("maxDate", "1984-01-01");
    int count = userMapper.findCountByMap(map);
    System.out.println("满足条件的有" + count + "条记录");
}

小结

  1. sql标签的作用:定义SQL语句的代码段

    id属性:定义代码块的标识

  2. include标签的作用:引用上面代码段

    refid属性:引用上面的id的值

MyBatis 核心配置文件 – 分页插件

在核心配置文件中添加

<!--集成分页助手插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
//通过分页助手来实现分页功能
        // 第一页:显示3条数据
        //PageHelper.startPage(1,3);
        // 第二页:显示3条数据
        //PageHelper.startPage(2,3);
        // 第三页:显示3条数据
        PageHelper.startPage(3,3);
//获取分页相关参数
        PageInfo<Student> info = new PageInfo<>(list);
        System.out.println("总条数:" + info.getTotal());
        System.out.println("总页数:" + info.getPages());
        System.out.println("当前页:" + info.getPageNum());
        System.out.println("每页显示条数:" + info.getPageSize());
        System.out.println("上一页:" + info.getPrePage());
        System.out.println("下一页:" + info.getNextPage());
        System.out.println("是否是第一页:" + info.isIsFirstPage());
        System.out.println("是否是最后一页:" + info.isIsLastPage());

MyBatis 多表操作(配置文件方式实现)

一对一

案例:人与身份证

<?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.itheima.table01.OneToOneMapper">
    <!--配置字段和实体对象属性的映射关系-->
    <resultMap id="oneToOne" type="card">
        <id column="cid" property="id" />
        <result column="number" property="number" />
        <!--
            association:配置被包含对象的映射关系
            property:被包含对象的变量名
            javaType:被包含对象的数据类型
        -->
        <association property="p" javaType="person">
            <id column="pid" property="id" />
            <result column="name" property="name" />
            <result column="age" property="age" />
        </association>
    </resultMap>

    <select id="selectAll" resultMap="oneToOne">
        SELECT c.id cid,number,pid,NAME,age FROM card c,person p WHERE c.pid=p.id
    </select>
</mapper>

标签解释:

<resultMap>:配置字段和对象属性的映射关系标签。
	id 属性:唯一标识
	type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
	column 属性:表中字段名称
	property 属性: 实体对象变量名称
<association>:配置被包含对象的映射关系标签。
	property 属性:被包含对象的变量名
	javaType 属性:被包含对象的数据类型

一对多

案例:一个班级与多个学生

<?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.itheima.table02.OneToManyMapper">
    <resultMap id="oneToMany" type="classes">
        <id column="cid" property="id"/>
        <result column="cname" property="name"/>

        <!--
            collection:配置被包含的集合对象映射关系
            property:被包含对象的变量名
            ofType:被包含对象的实际数据类型
        -->
        <collection property="students" ofType="student">
            <id column="sid" property="id"/>
            <result column="sname" property="name"/>
            <result column="sage" property="age"/>
        </collection>
    </resultMap>
    <select id="selectAll" resultMap="oneToMany">
        SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage FROM classes c,student s WHERE c.id=s.cid
    </select>
</mapper>

标签解释:

<resultMap>:配置字段和对象属性的映射关系标签。
	id 属性:唯一标识
	type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
	column 属性:表中字段名称
	property 属性: 实体对象变量名称
<collection>:配置被包含集合对象的映射关系标签。
	property 属性:被包含集合对象的变量名
	ofType 属性:集合中保存的对象数据类型

多对多

案例:学生和课程,一个学生可以选择多门课程、一个课程也可以被多个学生所选择。

<?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.itheima.table03.ManyToManyMapper">
    <resultMap id="manyToMany" type="student">
        <id column="sid" property="id"/>
        <result column="sname" property="name"/>
        <result column="sage" property="age"/>

        <collection property="courses" ofType="course">
            <id column="cid" property="id"/>
            <result column="cname" property="name"/>
        </collection>
    </resultMap>
    <select id="selectAll" resultMap="manyToMany">
        SELECT sc.sid,s.name sname,s.age sage,sc.cid,c.name cname FROM student s,course c,stu_cr sc WHERE sc.sid=s.id AND sc.cid=c.id
    </select>
</mapper>

标签解释:

<resultMap>:配置字段和对象属性的映射关系标签。
	id 属性:唯一标识
	type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
	column 属性:表中字段名称
	property 属性: 实体对象变量名称
<collection>:配置被包含集合对象的映射关系标签。
	property 属性:被包含集合对象的变量名
	ofType 属性:集合中保存的对象数据类型

MyBatis 多表操作(注解的方式来实现)

一对一

案例:人与身份证

public interface CardMapper {
    //查询全部
    @Select("SELECT * FROM card")
    @Results({
            @Result(column = "id",property = "id"),
            @Result(column = "number",property = "number"),
            @Result(
                    property = "p",             // 被包含对象的变量名
                    javaType = Person.class,    // 被包含对象的实际数据类型
                    column = "pid",             // 根据查询出的card表中的pid字段来查询person表
                    /*
                        one、@One 一对一固定写法
                        select属性:指定调用哪个接口中的哪个方法
                     */
                    one = @One(select = "com.itheima.one_to_one.PersonMapper.selectById")
            )
    })
    public abstract List<Card> selectAll();
}


public interface PersonMapper {
    //根据id查询
    @Select("SELECT * FROM person WHERE id=#{id}")
    public abstract Person selectById(Integer id);
}

标签解释:

@Results:封装映射关系的父注解。
	Result[] value():定义了 Result 数组
@Result:封装映射关系的子注解。
	column 属性:查询出的表中字段名称
	property 属性:实体对象中的属性名称
	javaType 属性:被包含对象的数据类型
	one 属性:一对一查询固定属性
@One:一对一查询的注解。
	select 属性:指定调用某个接口中的方法

一对多

案例:一个班级与多个学生

public interface ClassesMapper {
    //查询全部
    @Select("SELECT * FROM classes")
    @Results({
            @Result(column = "id",property = "id"),
            @Result(column = "name",property = "name"),
            @Result(
                    property = "students",  // 被包含对象的变量名
                    javaType = List.class,  // 被包含对象的实际数据类型
                    column = "id",          // 根据查询出的classes表的id字段来查询student表
                    /*
                        many、@Many 一对多查询的固定写法
                        select属性:指定调用哪个接口中的哪个查询方法
                     */
                    many = @Many(select = "com.itheima.one_to_many.StudentMapper.selectByCid")
            )
    })
    public abstract List<Classes> selectAll();
}

public interface StudentMapper {
    //根据cid查询student表
    @Select("SELECT * FROM student WHERE cid=#{cid}")
    public abstract List<Student> selectByCid(Integer cid);
}

标签解释:

@Results:封装映射关系的父注解。
	Result[] value():定义了 Result 数组
@Result:封装映射关系的子注解。
	column 属性:查询出的表中字段名称
	property 属性:实体对象中的属性名称
	javaType 属性:被包含对象的数据类型
	many 属性:一对多查询固定属性
@Many:一对多查询的注解。
	select 属性:指定调用某个接口中的方法

多对多

案例:学生和课程,一个学生可以选择多门课程、一个课程也可以被多个学生所选择。

public interface StudentMapper {
    //查询全部
    @Select("SELECT DISTINCT s.id,s.name,s.age FROM student s,stu_cr sc WHERE sc.sid=s.id")
    @Results({
            @Result(column = "id",property = "id"),
            @Result(column = "name",property = "name"),
            @Result(column = "age",property = "age"),
            @Result(
                    property = "courses",   // 被包含对象的变量名
                    javaType = List.class,  // 被包含对象的实际数据类型
                    column = "id",          // 根据查询出student表的id来作为关联条件,去查询中间表和课程表
                    /*
                        many、@Many 一对多查询的固定写法
                        select属性:指定调用哪个接口中的哪个查询方法
                     */
                    many = @Many(select = "com.itheima.many_to_many.CourseMapper.selectBySid")
            )
    })
    public abstract List<Student> selectAll();
}

public interface CourseMapper {
    //根据学生id查询所选课程
    @Select("SELECT c.id,c.name FROM stu_cr sc,course c WHERE sc.cid=c.id AND sc.sid=#{id}")
    public abstract List<Course> selectBySid(Integer id);
}

标签解释:

@Results:封装映射关系的父注解。
	Result[] value():定义了 Result 数组
@Result:封装映射关系的子注解。
	column 属性:查询出的表中字段名称
	property 属性:实体对象中的属性名称
	javaType 属性:被包含对象的数据类型
	many 属性:一对多查询固定属性
@Many:一对多查询的注解。
	select 属性:指定调用某个接口中的方法
                column = "id",          // 根据查询出student表的id来作为关联条件,去查询中间表和课程表
                /*
                    many、@Many 一对多查询的固定写法
                    select属性:指定调用哪个接口中的哪个查询方法
                 */
                many = @Many(select = "com.itheima.many_to_many.CourseMapper.selectBySid")
        )
})
public abstract List<Student> selectAll();

}

public interface CourseMapper {
//根据学生id查询所选课程
@Select(“SELECT c.id,c.name FROM stu_cr sc,course c WHERE sc.cid=c.id AND sc.sid=#{id}”)
public abstract List selectBySid(Integer id);
}


标签解释:

@Results:封装映射关系的父注解。
Result[] value():定义了 Result 数组
@Result:封装映射关系的子注解。
column 属性:查询出的表中字段名称
property 属性:实体对象中的属性名称
javaType 属性:被包含对象的数据类型
many 属性:一对多查询固定属性
@Many:一对多查询的注解。
select 属性:指定调用某个接口中的方法


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
校园悬赏任务平台对字典管理、论坛管理、任务资讯任务资讯公告管理、接取用户管理、任务管理、任务咨询管理、任务收藏管理、任务评价管理、任务订单管理、发布用户管理、管理员管理等进行集中化处理。经过前面自己查阅的网络知识,加上自己在学校课堂上学习的知识,决定开发系统选择小程序模式这种高效率的模式完成系统功能开发。这种模式让操作员基于浏览器的方式进行网站访问,采用的主流的Java语言这种面向对象的语言进行校园悬赏任务平台程序的开发,在数据库的选择上面,选择功能强大的Mysql数据库进行数据的存放操作。校园悬赏任务平台的开发让用户查看任务信息变得容易,让管理员高效管理任务信息。 校园悬赏任务平台具有管理员角色,用户角色,这几个操作权限。 校园悬赏任务平台针对管理员设置的功能有:添加并管理各种类型信息,管理用户账户信息,管理任务信息,管理任务资讯公告信息等内容。 校园悬赏任务平台针对用户设置的功能有:查看并修改个人信息,查看任务信息,查看任务资讯公告信息等内容。 系统登录功能是程序必不可少的功能,在登录页面必填的数据有两项,一项就是账号,另一项数据就是密码,当管理员正确填写并提交这二者数据之后,管理员就可以进入系统后台功能操作区。项目管理页面提供的功能操作有:查看任务,删除任务操作,新增任务操作,修改任务操作。任务资讯公告信息管理页面提供的功能操作有:新增任务资讯公告,修改任务资讯公告,删除任务资讯公告操作。任务资讯公告类型管理页面显示所有任务资讯公告类型,在此页面既可以让管理员添加新的任务资讯公告信息类型,也能对已有的任务资讯公告类型信息执行编辑更新,失效的任务资讯公告类型信息也能让管理员快速删除。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值