MyBatis加强

Mapper 接口

在使用Mapper接口时,如果写错了在编译时候就可以发现,原来那种方式命名空间很容易写错,参数也容易传错,对比来原来 DAO 接口,可以自己不写实现类。

使用注意:

类似之前的 DAO,在接口中定义 CRUD 等操作方法。Mapper组件 = Mapper接口 + Mapper XML文件

  • 接口的命名为实体名Mapper,一般和其对应 XML 文件放一起(只要编译之后字节码文件和 XML 文件在一起)
  • XML 命名空间用其对应接口的全限定名
  • Mapper 接口的方法名要和 Mapper XML 文件元素(select | update | delete | insert) id 值一样
  • 方法的返回类型对应 SQL 元素中定义的 resultType / resultMap 类型
  • 方法的参数类型对应 SQL 元素中定义的 paramterType 类型(一般不写)

定义和使用

定义:

public interface UserMapper {
	void save(User user);
}

使用:

@Test
	public void testNewSave() throws Exception {
		User user = new User();
		user.setUsername("李四");
		user.setPassword("123456");
		SqlSession session = MyBatisUtil.getSession();
		
		UserMapper mapper = session.getMapper(UserMapper.class);
		mapper.save(user);
		
		session.commit();
		session.close();
	}

使用@Param注解处理多个参数

由于Mapper接口本质的底层还是用传命名空间的方法,也就只支持传一个参数。但是跟调用者需求不可能只传一个参数,如:实现一个登录需求,根据用户名和密码查询用户。其实我们可以把参数封装成对象或存到Map里这样就满足传一个参数了,但这样代码会很臃肿。

@Param注解的使用:

修改Mapper接口中的方法,在形参贴注解即可。

// 本质相当于构建一个Map对象,key为注解 @Param的值,value为参数的值。
User login(@Param("username")String username,@Param("password")String password);

本质相当于构建一个Map对象,key为注解 @Param的值,value为参数的值。

集合/数组参数

当传递一个 List 对象或数组对象参数给 MyBatis 时,MyBatis 会自动把它包装到一个 Map 中,此时:

List 对象会以 list 作为 key,数组对象会以 array 作为 key,也可以使用注解 @Param 设置 key 名。

MyBatis 的 # 和 $ 区别

相同点

都可以获取对象(Map 对象或者 JavaBean 对象)的信息。

不同点

使用 # 传递的参数会先转换为,无论传递是什么类型数据都会带一个单引号;

使用 $ 传递的参数,直接把值作为 SQL 语句的一部分;

使用 # 支持把简单类型(八大基本数据类型及其包装类、String、BigDecimal 等等)参数作为值;

使用 $ 不支持把简单类型参数作为值;

使用 # 好比使用 PrepareStatement,没有 SQL 注入的问题,相对比较安全;

使用 $ 好比使用 Statement,可能会有 SQL 注入的问题,相对不安全。

在什么情况使用:
在 group by 子句或者 order by 子句取值,都是取列名,就使用 $。其他子句获取参数值使用 #

动态SQL

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

if和where

if元素,用于判断,一般用作是否应该符合某一个查询条件

<if test="boolean表达式"></if>
<!--
xml 中<> 转义符
 <  :  &lt;

 >  :  &gt:
 >  -->

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

<select id="selectBy" resultType="Employee">
		SELECT * FROM employee
		<where>
			<if test="name != null">
				AND name=#{name}
			</if>
			<if test="minSalary != null">
				AND salary &gt;=#{minSalary}
			</if>
			<if test="maxSalary != null">
				AND salary &lt;=#{maxSalary}
			</if>
		</where>
	</select>

set

set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,若里面条件都不成立,就会去除 SET 关键字。其用来解决更新时丢失数据的问题。

	<update id="update">
		UPDATE employee
          <set>
              <if test="name != null">
                  name=#{name},
              </if>
              <if test="salary != null">
                  salary=#{salary},
              </if>
              <if test="deptId != null">
                  deptId=#{deptId}
              </if>
          </set>
          WHERE id=#{id}
	</update>

foreach

动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候,这里就会使用到foreach元素。

	<delete id="delete">
		 DELETE FROM employee WHERE id in
        <!-- 
        	collection:要迭代的数组或集合,集合的key默认使用list,数组默认使用array,但也可以通过注解@Param 修改
        	open:迭代开始符号
        	item:迭代元素遍量
        	separator:分隔符号
        	close:迭代结束符号
        	index:迭代索引变量
        -->
       <foreach collection="ids" open="(" item="id" separator="," close=")">
           #{id}
       </foreach>
	</delete>

关系概述

生活中数据很多是存在关系的,就是把生活中有关系的数据通过 MyBatis 持久化到数据库,且存储的数据也能表示出来这种关系,再由数据库中把有关系的数据查询出来在页面展示。

保存:页面的数据 ----> 使用 Java 对象封装 —> 通过 MyBatis —> 数据库表的数据

查询:数据库表的数据 —> 通过 MyBatis —> 封装成 Java 对象 —> 页面展示数据

对象关系分类

  • 泛化关系
  • 实现关系
  • 依赖关系
  • 关联关系
  • 聚合关系
  • 组合关系

关联关系

A 对象依赖 B 对象,并且把 B 作为 A 的一个成员变量,则 A 和 B 存在关联关系。在 UML 中依赖通常使用实线箭头表示。

关联关系分类

  • 按照导航性分

    若通过 A 对象中的某一个属性可以访问到 B 对象,则说 A 可以导航到 B。

    ​ 1. 单向:只能从 A 通过属性导航到 B,B 不能导航到 A。

    ​ 2. 双向:A 可以通过属性导航到 B,B 也可以通过属性导航到 A。

  • 按照多重性分

    一对一,一对多,多对一,多对多。

判断对象的关系

判断都是从对象的实例上面来看的;

判断关系需要根据对象的属性;

判断关系必须确定具体需求。

单项多对一的查询保存

需求

  • 保存一个部门和两个员工,且这两个员工都是这个部门的。
  • 根据员工id查询员工,并知道该员工的所在的部门。

表设计

在这里插入图片描述

类设计

Employee

@Setter
@Getter
@ToString
public class Employee {
	private Long id;
	private String name;
	private Department dept; // 关联部门
}

Department

@Setter
@Getter
@ToString
public class Department {

	private Long id;
	private String name;
}

Mapper接口和Mapper XML文件编写

Employee

public interface EmployeeMapper {

	void save(Employee emp);
	
	Employee get(Long id);
}
<insert id="save" useGeneratedKeys="true" keyProperty="id">
		INSERT INTO employee(name,deptId) VALUES(#{name},#{dept.id})
</insert>

<resultMap type="Employee" id="baseResultMap">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <!-- 
  使用额外SQL的方式:
  association:针对关联属性的配置。非集合类型的
  javaType:额外SQL查询结果集封装的类型
  column:列的值传递额外SQL的参数值
  select:发送的额外SQL
   -->
    <association column="deptId" property="dept" select="cn.wolfcode.crud.mapper.DepartmentMapper.get"/> 
</resultMap>

<select id="get" resultMap="baseResultMap">
    SELECT * FROM employee WHERE id = #{id}
</select>

Department

public interface DepartmentMapper {

	void save(Department dept);
	
	Department get(Long id);
}
<insert id="save" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO department(name) VALUES(#{name})
</insert>

<select id="get" resultType="Department">
    SELECT * FROM department WHERE id = #{id}
</select>

编写单元测试

@Test
public void testSave() {
    SqlSession session = MyBatisUtil.getSession();
    DepartmentMapper departmentMapper = session.getMapper(DepartmentMapper.class);
    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);

    // 先保存部门
    Department dept = new Department();
    dept.setName("开发部");
    departmentMapper.save(dept);

    // 再保存员工
    Employee emp1 = new Employee();
    emp1.setName("张老大");
    emp1.setDept(dept);
    employeeMapper.save(emp1);

    Employee emp2 = new Employee();
    emp2.setName("李老二");
    emp2.setDept(dept);
    employeeMapper.save(emp2);

    session.commit();
    session.close();
}

@Test
public void testGet() throws Exception {
    SqlSession session = MyBatisUtil.getSession();
    EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);

    Employee employee = employeeMapper.get(2L);
    System.out.println(employee);

    session.close();
}

注意:

​ 保存:先保存部门后保存员工,执行代码是从上往下的

​ 查询:手动编写mysql,再让MyBatis发送额外的SQL

多对多查询保存删除

需求

  • 保存两个学生和两个老师,且这两个老师都教了这个两个学生。
  • 根据id查询学生,并查询其老师。
  • 根据id删除学生。

表设计

在这里插入图片描述

类设计

Teacher

@Getter
@Setter
public class Teacher {

	private Long id;
	private String name;
}

Student

@Getter
@Setter
@ToString
public class Student {

	private Long id;
	private String name;
	
	// 关联属性
	private List<Teacher> teacher = new ArrayList<Teacher>();
}

Mapper接口和Mapper XML文件编写

Teacher

public interface TeacherMapper {

	void save(Teacher teacher);

	List<Teacher> queryById(Long id);
}
<insert id="save" useGeneratedKeys="true" keyProperty="id">
INSERT
INTO teacher(name) VALUES(#{name})
</insert>

<select id="queryById" resultType="Teacher">
select id,name from teacher s
JOIN teacher_student s_t
ON s.id =s_t.teacher_id
where student_id=#{id}
</select>

Student

public interface StudentMapper {

	void save(Student student);

	// 维护关系
	void insertRelationWithTeacher(@Param("studentId") Long studentId, @Param("teacherId") Long teacherId);

	Student queryById(Long id);

	void deleteById(Long id);

	// 删掉关系
	void deleteRelationById(Long id);
}
<insert id="save" useGeneratedKeys="true" keyProperty="id">
    INSERT
    INTO student(name) VALUES(#{name})
</insert>

<insert id="insertRelationWithTeacher">
    INSERT INTO teacher_student(teacher_id,student_id)
    VALUES(#{teacherId},#{studentId})
</insert>

<resultMap type="Student" id="baseResultMap">
    <id column="id" property="id" />
    <result column="name" property="name" />
    <collection select="cn.wolfcode.crud.mapper.TeacherMapper.queryById"
                column="id" property="teacher" />
</resultMap>

<select id="queryById" resultMap="baseResultMap">
    select id,name from student where id=#{id}
</select>

<delete id="deleteById">
    delete from student where id=#{id}
</delete>

<delete id="deleteRelationById">
    delete from teacher_student where
    student_id=#{student_id}
</delete>

编写单元测试

@Test
public void testSave() {
    SqlSession session = MyBatisUtil.getSession();
    TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
    StudentMapper studentMapper = session.getMapper(StudentMapper.class);

    // 添加老师
    Teacher teacher1 = new Teacher();
    teacher1.setName("孔子");
    Teacher teacher2 = new Teacher();
    teacher2.setName("孟子");
    teacherMapper.save(teacher1);
    teacherMapper.save(teacher2);

    // 添加学生
    Student student1 = new Student();
    student1.setName("张三");
    student1.getTeacher().add(teacher1); // 添加关系
    student1.getTeacher().add(teacher2);

    Student student2 = new Student();
    student2.setName("李四");
    student2.getTeacher().add(teacher1); // 添加关系
    student2.getTeacher().add(teacher2);

    studentMapper.save(student1);
    studentMapper.save(student2);

    // 数据保存到中间表
    for (Teacher teacher : student1.getTeacher()) {
        studentMapper.insertRelationWithTeacher(student1.getId(), teacher.getId());
    }
    for (Teacher teacher : student2.getTeacher()) {
        studentMapper.insertRelationWithTeacher(student2.getId(), teacher.getId());
    }

    // 提交 关闭
    session.commit();
    session.close();

}

@Test
public void testQuery() throws Exception {
    SqlSession session = MyBatisUtil.getSession();
    StudentMapper studentMapper = session.getMapper(StudentMapper.class);
    Student stu = studentMapper.queryById(2L);
    session.close();
    System.out.println(stu);
}

@Test
public void testDelete() throws Exception {
    SqlSession session = MyBatisUtil.getSession();
    //获取mapper
    StudentMapper studentMapper = session.getMapper(StudentMapper.class);
    //调用方法先删掉关系的数据表中信息,防止有其他关联外键问题
    studentMapper.deleteRelationById(1L);
    //删掉学生用户的信息
    studentMapper.deleteById(1L);
    //提交事务和关闭session 
    session.commit();
    session.close();
}

注意:

​ 保存:在多对多表查询中,要设计一张中间表来表示之间的关系

​ 查询:MyBatis发送额外的SQL

​ 删除:先从中间表把关系数据删除,再删除要删除数据

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小云很优秀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值