一、#{}和${}的区别
#{}所采取的时占位符方式(底层是预编译模式),与JDBC中的?方式相同,传参更加方便安全 ,防止了sql注入。当我们需要向sql传值,使用#{};
${}是将内容直接拼接到sql语句中(底层是字符串拼接),一般不用与sql传值。一般用于向sql中动态传递列名 并且在接口处需要通过@Param()进行绑定。其一般用于传列名,可以用于按照某一列排序(select * from表 order by ${列名} desc/asc),或是不确定列的查询(select ${列名} from 表)等操作
区别:
1、底层实现不同: #{}底层是预编译功能,防止sql注入,更加安全
${}}底层是字符串拼接,直接将值拼接到sql中
2、使用场景不同: #{}一般用于向sql中的列传值
${}一般用于向sql中动态传递列名,例如排序时后面列名可改变
例如select后面的列名可以自由选择
二、多表关联查询
将student表和major表关联
package com.wbc.myBatisProject.model;
public class Student {
private int id;
private String name;
private int num;
private String gender;
private Major major;//建议在学生中关联专业,将专业相关的属性放在major
//private int id; private String name 需学生类重新把关联的专业属性创建了一遍,太过冗余
public Major getMajor() {
return major;
}
public void setMajor(Major major) {
this.major = major;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
package com.wbc.myBatisProject.model;
import java.util.List;
public class Major {
private int id;
private String name;
private List<Student> studentList;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
studentDao层
package com.wbc.myBatisProject.dao;
import com.wbc.myBatisProject.model.Student;
public interface StudentDao {
Student getStudentById(int id);
}
student映射
(1)直接多表关联查询出需要的数据
<?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">
<!--namespace需要和接口路径对应-->
<mapper namespace="com.wbc.myBatisProject.dao.StudentDao">
<!-- 对关联查询道德学生信息进行自定义封装-->
<resultMap id="studentMap" type="Student">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
<!-- 映射关联数据 专业名称 首先会创建一个major对象 然后将major对象封装到student对象-->
<association property="major" javaType="major">
<result column="mname" property="name"></result>
</association>
</resultMap>
<select id="getStudentById" resultMap="studentMap" >
select s.id,s.num,s.name,s.gender,m.name mname from student s inner join major m on s.majorid = m.id where s.id = #{id}
</select>
</mapper>
在关联查询中通过select标签的resultMap属性绑定结果映射resultMap标签的id属性
resultMap标签的type属性绑定返回的类型
id标签的column属性绑定查询的主键 property属性是返回类型Student内部的属性
result标签用于绑定主键之外的查询值 column和property与id类似
association标签用于映射关联数据(查询关联表中的数据)property绑定关联的表 javaType表示关联的类,内部通过result标签绑定数据
(2) 先查询主表,再查询关联表
<resultMap id="studentMap1" type="Student">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
<association property="major" javaType="Major" select="findMajorById" column="majorid">
</association>
</resultMap>
<select id="getStudentById1" resultMap="studentMap1">
select id,num,name,gender,majorid from student where id = #{id}
</select>
<!-- 嵌套查询学生关联的专业-->
<select id="findMajorById" resultType="Major">
select name from major where id = #{majorid}
</select>
通过写两个select标签分别查询student表和major表,association标签中的select属性与查询关联表的select标签中的id属性进行绑定 column与主表中查询出的需要在关联表使用的列进行绑定。
select中的 resultType属性确定返回类型
(3)查询一对多关联信息
假设我们需要查询专业和专业内选择该专业的所有学生
正常查询需要两个major对象接收结果,而myBatis为我们提供了collection标签为我们解决了这个问题
<?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">
<!--namespace需要和接口路径对应-->
<mapper namespace="com.wbc.myBatisProject.dao.MajorDao">
<!-- 查询专业时,可以将多个学生封装到一个集合中-->
<resultMap id="majorMap" type="major">
<id column="id" property="id"></id>
<result property="name" column="name"></result>
<!-- 将查询到的多条结果封装到集合-->
<collection property="studentList" javaType="list" ofType="student">
<result column="num" property="num"></result>
<result column="sname" property="name"></result>
</collection>
</resultMap>
<select id="findMajorById" resultMap="majorMap" >
select m.id,m.name,s.num,s.name sname from major m inner join student s on m.id = s.majorid where m.id = #{id}
</select>
</mapper>
通过在major类中添加集合属性来接收多个结果
collection标签中的property属性对应着类中的集合名称,javaType对应着返回的集合类型,offType对应着集合中的泛型
在collection内部通过result标签接收查询结果
(4)一对多关联查询的两次查询
当我们进行分页操作时,一次操作会出现list集合中的数量和实际sql查询数量不一致的情况,这时候需要分两次查询
<!--分两次查询-->
<resultMap id="majorMap1" type="major">
<id column="id" property="id"></id>
<result property="name" column="name"></result>
<!-- 将查询到的多条结果封装到集合-->
<collection property="studentList" javaType="list" ofType="student" select="findStudents" column="id">
</collection>
</resultMap>
<select id="findMajors1" resultMap="majorMap1">
select id,name from major
</select>
<select id="findStudents" resultMap="majorMap1">
select id,name from student where majorid = #{id}
</select>
</mapper>
通过在collecetion标签中添加select属性和column属性绑定第二条sql语句并传入参数
(5)注释方式执行sql
@Insert("insert into major(naem)values(#{name})")
void insertMajor(Major major);
@Select("select id , name from major")
List<Major> majors();
当sql语句简单时,可以使用注解标签添加sql,但复杂的sql不建议使用注解
一般适用于单张表的简单增删改查
三、动态sql
动态sql是myBatis一个强大的功能,避免了根据不同条件需要拼接不同字符串的痛苦过程
以Teacher类为例
package com.wbc.myBatisProject.model;
import org.apache.ibatis.annotations.Insert;
public class Teacher {
private Integer id;
private String name;
private Integer num;
private String gender;
public Integer getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
package com.wbc.myBatisProject.dao;
import com.wbc.myBatisProject.model.Teacher;
import java.util.List;
public interface TeacherDao {
List<Teacher> teachers(Teacher teacher);
}
<?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">
<!--namespace需要和接口路径对应-->
<mapper namespace="com.wbc.myBatisProject.dao.TeacherDao">
<!--
动态SQL
可以在sql中添加逻辑判断
<if>标签中的test属性条件如果返回为true,则执行if标签体,不成立则不执行
<where>标签 当标签内的if语句有条件成立时,就会动态添加where关键字,如果where关键字后紧跟着的关键字(例如and,or),就会自动删除以保证sql语句语法错误
-->
<select id="teachers" resultType="Teacher" >
select * from teacher
<where>
<if test="num!=null">
num = #{num}
</if>
<if test="name!=null">
and name = #{name}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</where>
</select>
</mapper>
动态SQL可以在sql中添加逻辑判
<if>标签
标签中的test属性条件如果返回为true,则执行if标签体,不成立则不执行
<where>标签
当标签内的if语句有条件成立时,就会动态添加where关键字,如果where关键字后紧跟着的关键字(例如and,or),就会自动删除以保证sql语句语法错误
<trim>标签
同样的,用<trim>同样可以完成这项操作,tirm的功能更加强大,灵活度更高
<!--
<trim> 标签 当条件判断成立时,可以自定义前缀关键字和后缀关键字
prefix="where" 添加前缀关键字
prefixOverrides="and | or"覆盖指定后缀关键字
-->
<select id="teachers" resultType="Teacher" >
select * from teacher
<trim prefix="where" prefixOverrides="and | or">
<if test="num!=null">
num = #{num}
</if>
<if test="name!=null">
or name = #{name}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</trim>
</select>
<choose>标签
并且 if也可以用choose标签替换 choose标签的使用于java中的choose类似
<when>标签
<otherwise>标签
当我们使用choose标签时,内部必须使用when标签做条件判断,when标签的test属性用于判断条件。otherwise标签相当于else,在choose标签内部至多可以写一个otherwise标签,也可以不写otherwise标签
<!--
otherwise相当于else 可以不写,最多可以写一个
但是当使用<choose>标签时,必须在标签体内写<when>表签 when标签的test属性用于判断条件是否成立
-->
<select id="teachers" resultType="Teacher" >
select * from teacher
<trim prefix="where" prefixOverrides="and | or">
<choose >
<when test="name!=null">
name=#{name}
</when>
<otherwise>
name="李老师"
</otherwise>
</choose>
</trim>
</select>
<set>标签
当我们需要用if等选择标签进行列名拼接时,会出现最后一位仍有逗号的情况。原因是如果最后一位没有达成if标签中的条件时,其他位作为最后一位会将原本的逗号带着拼接进sql语句。而set标签就可以解决这一问题,它可以把最后一个逗号去掉
<!--set去逗号-->
<!--当每次拼接后需要逗号连接,但有可能前面的不符合条件不执行 set标签可以把最后一个,删除,以保证语法正确-->
<!--当然set标签也可以用item代替-->
<update id="updateTeacher" parameterType="Teacher" >
update teacher
<set>
<if test="num!=null">
num=#{num},
</if>
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
</set>
<where>
id = #{id}
</where>
</update>
测试
@Test
public void test1(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
TeacherDao teacherDao = sqlSession.getMapper(TeacherDao.class);
Teacher teacher = new Teacher();
teacher.setId(1);
teacher.setName("陈老师");
teacher.setNum(1004);
teacherDao.updateTeacher(teacher);
sqlSession.commit();
sqlSession.close();
}
传入的参数teacher没有调用setGender方法设置gender,在调用gender时gender=null不符合if条件,此时name是set标签中最后的需要拼接的元素,此时set自动将逗号去掉了,保障了语法的正确性
<foreach>标签
foreach标签的作用是循环
此处我们假设两个示例
第一个是我们的批量删除,如果我们每次删除时前端请求一次,将id发送给后端,后端再调用sql删除,是否太过麻烦。有了foreach之后,我们可以一次性拿到所有要删除事物的id发给后端,后端将其传入数组或List集合,将其传入dao层映射的方法中,通过foreach标签循环调用sql,完成批量删除的操作
假设1、2、3是需要批量删除的老师的id 通过数组传输
@Test
public void test2(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
TeacherDao teacherDao = sqlSession.getMapper(TeacherDao.class);
teacherDao.deleteTeacher(new Integer[]{1,2,3});
sqlSession.commit();
sqlSession.close();
}
dao层接口通过此方法映射给对应的Mapper
List<Teacher> findTeacher(List<String> columns);
从而调用这条sql
<!-- foreach标签循环 collection是传参类型 传入数组时等于array 传入List则等于list-->
<!--item 表示增强for循环中的每次循环取出的值 open是循环开始时需要拼接的 separator是传入参数后拼接的 close是结束时需要拼接的-->
<delete id="deleteTeacher">
delete from teacher where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
foreach 标签中,collection="传入数据的类型"(数组填array,集合填list)
item=”每次从数组或集合中取出的数据的别名“(随便起一个名字)
open=”循环开始前需要拼接的符号“
separator=”每次循环传入数据后需要拼接的符号“
close=”循环结束需要拼接的符号“
第二个例子是我们在前端通过标签筛选想要看到的东西,前端拿到这些标签传入后端,后端再根据传入的数据调取数据库数据返回前端,也需要我们通过foreach循环拼接列名
@Test
public void test3(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
TeacherDao teacherDao = sqlSession.getMapper(TeacherDao.class);
List<String> columns = new ArrayList<>();
columns.add("num");
columns.add("name");
columns.add("gender");
teacherDao.findTeacher(columns);
sqlSession.commit();
sqlSession.close();
}
这里由于sql语句不需要在循环前拼接,所以没有写 open属性和close属性
由于传入的是列名,所以不可以用#{xxx}传输,需要用¥{xxx}传输
<select id="findTeacher" resultType="Teacher">
select
<foreach collection="list" item="column" separator=",">
${column}
</foreach>
from teacher
</select>
自此,全部的动态sql学习完毕