创建两个关联表,一个老师teacher表,一个student学生表,student有一个t_id关联teacher表的主键id,数据库如下图:
teacher表:
student表:
分层结构如下
DemoSqlMap.xml连接数据库的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="mysql">
<environment id="mysql">
<!--配置事务的类型,使用本地事务策略-->
<transactionManager type="JDBC"></transactionManager>
<!--是否使用连接池 POOLED表示使用链接池,UNPOOLED表示不使用连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/artic"/>
<property name="username" value="root"/>
<property name="password" value="123456789"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/StudentDao.xml"></mapper>
<mapper resource="mapper/TeacherDao.xml"></mapper>
</mappers>
</configuration>
teacher实体类,含有get和set和tostring方法:
public class Teacher {
private Integer id;
private String tname;
//一个老师对应多个学生,查询时用List来接收
private List<Student> students;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", tname='" + tname + '\'' +
", students=" + students +
'}';
}
}
student实体类:
public class Student {
private Integer id;
private String sname;
private String sex;
private Integer age;
private Integer t_id;
//一个学生对应一个教师,用Teacher来接收而不是List
private Teacher teacher;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getT_id() {
return t_id;
}
public void setT_id(Integer t_id) {
this.t_id = t_id;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", sname='" + sname + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", t_id=" + t_id +
", teacher=" + teacher +
'}';
}
}
mybatis有两种常用的联表查询方法:
第一种: 连表查询
使用的联表查询语句
教师因为有多个学生,所以需要用List来接收这个教师所对应的的学生
查询全部教师的和每个教师对应的全部学生的dao层方法如下:
List<Teacher> getTeacher();
这个方法对应的xml中的sql语句如下:
<select id="getTeacher" resultMap="Map">
SELECT * FROM teacher LEFT JOIN student on student.t_id = teacher.id
</select>
<resultMap id="Map" type="com.qcby.entity.Teacher">
<result property="id" column="id"/>
<result property="tname" column="tname"/>
<!-- 复杂的属性我么需要单独去处理 对象:association 集合:collection
在集合中的泛型信息,我们使用ofType获取
-->
<collection property="students" ofType="com.qcby.entity.Student">
<result property="sname" column="sname"/>
</collection>
</resultMap>
里面select标签对应的就是查询方法,在里面写查询方法,这种方法是通过连表查询语句来获取数据的.
resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。
如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,resultMap实质上还需要将查询结果映射到pojo对象中。 resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
这里需要使用resultMap,resultMap标签中的id属性和select标签中的resultMap属性相对应,type属性是映射到哪个实体类。
resultMap标签中的result 标签的property属性对应的是java实体类中的字段名,column属性对应的是数据库中的字段名。
collection 标签是对一对多查询时,一个教师对应的多个学生,学生是个集合,所以需要用collection 标签来接收多个学生
我们在测试类中对它进行测试,输出结果如下:
而如果是学生进行查询,一个学生对应的是一个教师就不能使用 collection 标签而是使用association 标签,
学生的dao层方法:
List<Student> getStudent();
查询的mapper配置文件 如下:
<select id="getStudent" resultMap="Map">
SELECT * FROM student
LEFT JOIN teacher on student.t_id = teacher.id
</select>
<resultMap id="Map" type="com.qcby.entity.Student">
<id property="id" column="id"/>
<result property="sname" column="sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="t_id" column="t_id"/>
<association property="teacher" javaType="com.qcby.entity.Teacher">
<id property="id" column="t_id"/>
<result property="tname" column="tname"/>
</association>
</resultMap>
这里学生和教师不同的就是学生使用的是association 标签它对应的是一个实体。
测试类查询结果如下:
第二种方法 分步查询:
和第一种方法不同,第一种方法是直接通过连表查询语句把数据直接查询出来,第二种是把查询语句分成两步。
用教师来举例,会先把教师查出来,再通过教师id来找到对应的全部学生,这是使用的两个sql语句。
teacher的xml查询如下:
<select id="getTeacher" resultMap="Map">
select * from teacher
</select>
<resultMap id="Map" type="com.qcby.entity.Teacher">
<id property="id" column="id"/>
<result property="tname" column="tname"/>
<collection javaType="ArrayList" ofType="com.qcby.entity.Student"
select="getStudent" property="students" column="id"/>
</resultMap>
<select id="getStudent" resultType="com.qcby.entity.Student">
select * from student where t_id=#{id}
</select>
collection 标签里的select属性是这个集合会对应哪个查询的id会把查询的数据放入这个集合内, column属性作用就是会传给这个查询语句什么参数,这个参数来自上一个查询语句的某个数据库字段。总体意思就是会先查全部教师,再查学生,而学生里面对应的id是通过column获取的。
学生查询的第二种方式的xml如下,
<select id="getStudent" resultMap="Map">
select * from student
</select>
<resultMap id="Map" type="com.qcby.entity.Student">
<id property="id" column="id"/>
<result property="sname" column="sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<association property="teacher" column="t_id" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="com.qcby.entity.Teacher">
select * from teacher where id=#{id}
</select>
查询结果和第一种的查询结果一致,只是查询方式不同,这种方法采用了分布查询,使用哪种方式都可以。
#{}和${}
在查询时where语句后会有#{},括号里放的就是我们传来的参数。
${}和#{}作用相同,${}的本质是字符串拼接,#{}的本质是占位符赋值
${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;
#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段赋值时,可以自动添加单引号;
使用#{}这种方式最好它比较安全,而${}容易出现sql注入的问题,不建议使用。
mybatis的增删改:
在xml文件中的添加语句:
<!--返回主键 :我们的主键需要设置自动递增 -->
<insert id="insertGetId" parameterType="com.qcby.entity.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
parameterType属性是传入的参数来自哪个类或属于哪种数据类型。
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
它的作用就是返回主键 ,就是添加数据后会把新添加的数据的主键id返回给这个实体类,不加它就没有返回主键这个功能。
更新语句如下:
<update id="update" parameterType="com.qcby.entity.User">
update user set username = #{username},birthday = #{birthday}, sex = #{sex},address = #{address} where id = #{id}
</update>
删除语句如下:
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id = #{id}
</delete>
动态sql
什么是动态sql:在不同条件下拼接不同的sql
Mybatis框架的动态sql技术是一种根据特定条件动态拼接SQl语句的功能,他存在的意义是为了解决拼接SQL语句字符串时的痛点问题。其中一些条件可以选择也可以不选择,那么如果使用传统的方式进行查询,反而在拼接sql的时候会造成一些列的问题。
<if>标签
看如下sql:
<select id="selectUserByUsernameAndSex" parameterType="com.qcby.entity.User"
resultType="com.qcby.entity.User">
select * from user where username = #{username} and sex = #{sex}
</select>
如果查询时username或sex为空,那么我们就不让username或sex出现在查询语句上,那么我们就需要使用<if>标签,写法如下:
<select id="selectUserByUsernameAndSex" parameterType="com.qcby.entity.User"
resultType="com.qcby.entity.User">
select * from user where
<if test="username != null and username = ''">
username=#{username}
</if>
<if test="sex != null and sex !=''">
and sex=#{sex}
</if>
</select>
那如果username为空时,sex前面就会多一个and,sql语句就会不正确。
<where>标签
上面导致的错误如何解决,使用where标签,会帮我们把where后第一个and或者or去除掉,代码如下。
<select id="selectUserByUsernameAndSex" parameterType="com.qcby.entity.User"
resultType="com.qcby.entity.User">
select * from user
<where>
<if test="username != null and username = ''">
username=#{username}
</if>
<if test="sex != null and sex !=''">
and sex=#{sex}
</if>
</where>
</select>
<set>标签
set标签 用在update更新语句,和if标签组装使用,代码如下:
<update id="update" parameterType="com.qcby.entity.User">
update user
<set>
<if test="username !=null and username!=''">
username = #{username} ,
</if>
<if test="address != null and address != ''">
address = #{address}
</if>
</set>
where id = #{id}
</update>
set可以去除掉无用的逗号,假如address为空,username不为空,就会导致username那一行多个逗号,导致语句错误,而这里使用了set标签,就不会产生错误。
<choose>、<when>和<otherwise>标签
这个标签相当于是我们java当中的if.....elseif.....else
<choose>标签是<when>标签和<otherwise>标签的父标签,都在<choose>标签内部。
<when>标签就相当于是我们的 if 和 elseif
<otherwise>标签相当于是我们的 else
代码案例如下:
<select id="selectUserByChoose" resultType="com.qcby.entity.User"
parameterType="com.qcby.entity.User">
select * from user
<where>
<choose>
<when test="id !='' and id != null">
id=#{id}
</when>
<when test="username !='' and username != null">
and username=#{username}
</when>
<otherwise>
and sex=#{sex}
</otherwise>
</choose>
</where>
</select>
其实和<if>标签作用一样。
<trim>标签
trim标记是一个格式化的标记,可以完成set或者是where标记的功能
trim里面有两个属性:
1.prefix:前缀,里面写where代表是where标签,写set代表set标签
2.prefixoverride:去掉第一个and或者是or还或者是逗号,写什么可以去除哪一项
trim充当where代码如下:
<select id="selectUserByUsernameAndSex" parameterType="com.qcby.entity.User"
resultType="com.qcby.entity.User">
select * from user
<trim prefix="where" prefixOverrides="and | or">
<if test="username != null">
and username=#{username}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</trim>
</select>
trim充当set代码如下:
<update id="update" parameterType="com.qcby.entity.User">
update user
<trim prefix="set" suffixOverrides=",">
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="sex != null and sex != ''">
sex = #{sex},
</if>
</trim>
where id = #{id}
</update>
<foreach>标签
我们进行批量删除和批量添加的时候,数据是数组或是list集合,就需要使用foreach标签来进行批量插入和批量删除。
foreach中有如下几种属性:
1. collection:当前要循环的数组或者集合
2. item: 我们指定要循环的数组的每一个元素
3. separator:每一个元素应该用什么来做分割
4. open:当前循环是以什么开始
5. close:当前循环是以什么结束
批量删除案例如下:
mapper层:
/**
* 通过数组批量删除
* @param ids
* @return
*/
int deleteMoreByArray(@Param("ids") Integer[] ids);
<!-- 批量删除的sql语句:delete from user where id in (1,2,3,4,5); -->
<delete id="deleteMoreByArray">
delete from user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
批量插入案例:
/**
* 通过集合批量添加
* @param users
* @return
*/
int insertMoreByList(@Param("users") List<User> users);
xml:
<insert id="insertMoreByList" >
insert into user(id,username,birthday,sex,address) values
<foreach collection="users" item="user" separator=",">
(null,#{user.username},#{user.birthday},#{user.sex},#{user.address})
</foreach>
</insert>
java中的foreach循环如下:
for ( User user : users) {}
collection属性中的users代表迭代对象,item代表迭代出的实例。