SSM第五讲 动态SQL与高级查询
动态SQL与高级查询
- 动态sql
- 高级查询
一. 动态SQL(重点)
1. 动态SQL是什么
就是相对与固定SQL。就是通过传入的参数不一样,可以组成不同结构的SQL语句. 这种根据参数的条件修改SQL结构的SQL语句,我们称为动态SQL语句.
2. 动态SQL有什么用
a.根据条件组装不同结构的SQL语句,可以提高SQL代码的重用性.
b.满足某些特定需求,如,条件判断查询
3. 基于XML的实现
3.1. 标签包括
用于声明公有的SQL语句块.,在操作标签中调用 [不建议用]
不建议的原因,会导致代码难以维护。
类似java if(){},用于判断
:类似java的foreach循环,一般用户批量处理的SQL语句
:切割标签,主要用于切割关键字的头和尾的字符.新版的Mybatis使用的几率很少.
:使用 set标签就是set关键字,可以在update 的时候set 关键字后面的,逗号可以自动忽略
:使用where标签作为where关键字,好处如果where后面的条件都不成立,忽略where关键字.
: java的swithc case
3.2. 示例代码
public interface StudentMapper {
/**
* 插入记录
* @param entity
* @return
*/
int insert(Student entity);
/**
* 批量插入记录
* @param entity
* @return
*/
int batchInsert(List<Student> entitys);
/**
* 删除记录
* @param sid
* @return
*/
int deleteById(int sid);
/**
* 批量删除记录
* @param sid
* @return
*/
int deleteByIds(int... sid);
/**
* 更新记录
* @param entity
* @return
*/
int update(Student entity);
/**
* 更新记录,如果传入的值不为空,才更新
* @param entity
* @return
*/
int updateByNotnull(Student entity);
/**
* 更新记录,如果传入的值不为空,才更新
* @param entity
* @return
*/
int updateByNotnull1(Student entity);
/**
* 查询所有的记录
* @return
*/
List<Student> findAll();
/**
* 将查询的数据放在map里面
* @return
*/
List<Map<String,Object>> findAllByMap();
/**
* 通过条件查询
* @param entity
* @return
*/
List<Student> findByCondition(Student entity);
/**
* 通过条件查询
* @param entity
* @return
*/
List<Student> findByCondition1(Student entity);
}
3.3. 映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<!-- 命名空间,用于指定动态创建的对象返回的映射接口 -->
<mapper namespace="cn.mapper.StudentMapper">
<!--
<sql> <include> 用于声明SQL语句块. [不建议用,原因导致SQL的代码维护性差]
需求:更新学生表,如果参数没有值的字段,就不用设置null值.保持不变.
需求:实现学生信息的批量插入.
需求:实现学生的批量删除
需求:通过传入的条件判断,实现模糊查询
-->
<sql id="sqlfeild">
<!-- 这里可以写任何的SQL语句块 -->
AID, SNAME, SEX, BIRTHDAY, AGE
</sql>
<insert id="insert">
INSERT INTO student(<include refid="sqlfeild"></include>) VALUES (#{sid}, #{sname}, #{sex}, #{birthday}, #{age})
</insert>
<insert id="batchInsert">
INSERT INTO student(AID, SNAME, SEX, BIRTHDAY, AGE) VALUES
<!--
collection集合类型:
list :如果参数是一个集合使用list
array:参数是一个数组使用array
注意:open,close一次循环只增加一次.不是每次循环都增加指定字符.
open:循环前面增加的字符
close:循环后面增加的字符
separator:每次循环的分割的字符,使用separator这个属性的好处,会忽略最后一个字符
index:索引
item:每次循环的参数对象变量名
-->
<foreach collection="list" separator="," item="item">
(#{item.aid}, #{item.sname}, #{item.sex}, #{item.birthday}, #{item.age})
</foreach>
</insert>
<!-- 删除记录 -->
<delete id="deleteById">
DELETE FROM student WHERE SID=#{sid}
</delete>
<delete id="deleteByIds">
DELETE FROM student WHERE SID in
<foreach collection="array" item="item" open="(" close=")" separator=",">
<!-- 注意,因为int deleteByIds(int... sid),的ids就是一个值,不是实体,所以item的值就是ids的值 -->
#{item}
</foreach>
</delete>
<update id="update">
UPDATE student SET AID=#{aid},SNAME=#{sname},SEX=#{sex},BIRTHDAY=#{birthday},AGE=#{age} WHERE SID=#{sid}
</update>
<update id="updateByNotnull">
<!-- 注意事项:
参数的属性名在标签里面,不要加#{}
-->
UPDATE student
<set>
<!-- SET -->
<if test="aid!=null">
AID=#{aid},
</if>
<if test="sname!=null">
SNAME=#{sname},
</if>
<if test="sex!=null">
SEX=#{sex},
</if>
<if test="birthday!=null">
BIRTHDAY=#{birthday},
</if>
<if test="age!=null">
AGE=#{age}
</if>
</set>
WHERE SID=#{sid}
</update>
<update id="updateByNotnull1">
<!-- 注意事项:
参数的属性名在标签里面,不要加#{}
-->
UPDATE student
<!-- 意思就是set关键字的最后的逗号,去掉 -->
<!--
prefix="" :就是在<trim>标签包括的SQL关键字,前面增加字符
prefixOverrides="" 就是在<trim>标签包括的SQL关键字,前面删除指定的字符
suffix="":就是在<trim>标签包括的SQL关键字的最后面,增加字符
suffixOverrides:就是在<trim>标签包括的SQL关键字最后面删除指定字符
注意,trim一定要注意,起作用的是关键字,如UPDATE student SET SEX=? WHERE SID=? ,<trim>包括在set关键字,
那么prefix就是set前面,suffix就是set关键的最后面,where的前面.
-->
<trim suffixOverrides="," >
SET
<if test="aid!=null">
AID=#{aid},
</if>
<if test="sname!=null">
SNAME=#{sname},
</if>
<if test="sex!=null">
SEX=#{sex},
</if>
<if test="birthday!=null">
BIRTHDAY=#{birthday},
</if>
<if test="age!=null">
AGE=#{age}
</if>
</trim>
WHERE SID=#{sid}
</update>
<select resultType="Student" id="findAll">
SELECT <include refid="sqlfeild"></include> FROM student
</select>
<!-- mybatis支持将数据放在map里面 -->
<select resultType="map" id="findAllByMap">
SELECT * FROM student
</select>
<!-- 模糊查询 -->
<select resultType="Student" id="findByCondition">
SELECT * FROM student
<!-- 使用where标签作用SQL的关键字,判断条件 -->
<where>
<!-- 使用choose,后面的条件必须要在前面的条件不成立的情况才会判断 -->
<!--
switch()
case:
break;
default:
-->
<choose>
<when test="aid!=null">
AID = #{aid}
</when>
<when test="sname!=null">
SNAME=#{sname}
</when>
<when test="sex!=null">
SEX = #{sex}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
<!-- 模糊查询 -->
<select resultType="Student" id="findByCondition1">
SELECT * FROM student
<!-- 使用where标签作用SQL的关键字,判断条件 ,好处,会自动忽略多余字符-->
<where>
<if test="aid!=null">
and AID = #{aid}
</if>
<if test="sname!=null">
and SNAME=#{sname}
</if>
<if test="sex!=null">
and SEX = #{sex}
</if>
</where>
</select>
</mapper>
动态SQL的其实就根据条件拼接SQL语句. 不管你使用哪种标签组合,只要最后的结果生成的SQL语句是你想要的就可以.
4. 基于注解的实现
4.1. 映射接口
public interface StudentMapper {
/**
* InsertProvider注解:作用是设置插入的动态SQL语句
* type:指定类的类型
* method:指定对应的方法名
* 批量插入记录
* @param entity
* @return
*/
@InsertProvider(type=StudentProvider.class,method="batchInsert")
int batchInsert(List<Student> entitys);
/**
* 批量删除
* @param sid
* @return
*/
@DeleteProvider(type=StudentProvider.class,method="deleteByIds")
int deleteByIds(int... sid);
/**
* 更新非空字段
* @param entity
* @return
*/
@UpdateProvider(type=StudentProvider.class,method="updateByNotnull")
int updateByNotnull(Student entity);
/**
* 通过条件查询
* @param entity
* @return
*/
@SelectProvider(type=StudentProvider.class,method="findByCondition")
List<Student> findByCondition(Student entity);
}
4.2. 动态SQL实现类
public class StudentProvider {
/**
* 注意: 1.动态SQL语句的提供类,提供的就是拼接好的SQL语句 2.如果参数不是标量类型或者单个实体对象,那么必须使用Map包装起来
* 标量类型=基础数据类型、基础数据类型包装类、String
*
*如下面的方法参数中,不对传入的list进行包装,会出现如下错误 Caused by: org.apache.ibatis.binding.BindingException: Parameter '0' not found. Available parameters are [collection, list]
* @param entitys
* @return 返回的是根据参数拼接好的SQL语句
*/
public String batchInsert(Map<String,List<Student>> map) {
List<Student> entitys= map.get("list");
String sql = "INSERT INTO tb_student (stu_name, stu_age, stu_password) VALUES ";
StringBuilder builder = new StringBuilder(sql);
for (int i = 0; i < entitys.size(); i++) {
builder.append("(#{list[" + i + "].stuName}, #{list[" + i + "].stuAge}, #{list[" + i + "].stuPassword})");
// 判断最后一次不可以有逗号
if (i < entitys.size() - 1) {
builder.append(",");
}
}
System.out.println(builder.toString());
return builder.toString();
}
/**
* 注意:为什么不使用... 答:原因是泛型语法不支持类型是...的类型,而可变参数类型,本质是一个数组,所以设置为一个数组
*
* @param sid
* @return
*/
public String deleteByIds(Map<String, int[]> idsMap) {
int[] ids = idsMap.get("array");
String sql = "DELETE FROM tb_student WHERE stu_id in ";
StringBuilder builder = new StringBuilder(sql);
builder.append("(");
for (int i = 0; i < ids.length; i++) {
// 注意事项:如果使用注解配置,只能通过数组的别名与下标来获得OGNL的值
builder.append("#{array[" + i + "]}");
// 判断最后一次不可以有逗号
if (i < ids.length - 1) {
builder.append(",");
}
}
builder.append(")");
System.out.println(builder.toString());
return builder.toString();
}
/**
* 单个对象不需要使用Map包起来 在字符串拼接SQL语句OGNL 如果是一个单对象,规则与配置文件一致 如果是集合或者数组,规则为
* list[下标].属性名 ,array[下标].属性名
*
* @param entity
* @return
*/
public String updateByNotnull(Student entity) {
String sql = "UPDATE tb_student SET ";
StringBuilder builder = new StringBuilder(sql);
if (entity.getStuName() != null) {
builder.append("stu_name = #{stuName},");
}
if (entity.getStuAge() != null) {
builder.append("stu_age = #{stuAge},");
}
if (entity.getStuPassword() != null) {
builder.append("stu_password = #{stuPassword},");
}
// 必须要去掉最后的逗号
builder.deleteCharAt(builder.length() - 1);
builder.append("WHERE stu_id = #{stuId}");
System.out.println(builder.toString());
return builder.toString();
}
/**
* 条件查询
* @param entity
* @return
*/
public String findByCondition(Student entity) {
String sql = "SELECT * FROM tb_student WHERE 1=1 ";
StringBuilder builder = new StringBuilder(sql);
if (entity.getStuName() != null) {
builder.append("AND stu_name like CONCAT('%',#{stuName},'%')");
}
if (entity.getStuAge() != null) {
builder.append("AND stu_age = #{stuAge}");
}
if (entity.getStuPassword() != null) {
builder.append("AND stu_password = #{stuPassword}");
}
System.out.println(builder.toString());
return builder.toString();
}
}
4.3. 测试类
public class StudentMapperTest {
/**
* 增加
*/
@Test
public void batchInsert(){
try {
//1.获得操作对象,session
SqlSession session = MybatisUtils.getSession();
//2.获得操作接口的动态对象
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Student student1=new Student();
student1.setStuName("张三");
Student student2=new Student();
student2.setStuName("李四");
List<Student> students=new ArrayList<>();
students.add(student1);
students.add(student2);
//3.插入对象
int count = studentMapper.batchInsert(students);
//4.提交事务
session.commit();
//5.关闭
session.close();
System.out.println("返回值:"+count);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 删除
*/
@Test
public void deleteByIds(){
//1.获得操作对象,session
SqlSession session = MybatisUtils.getSession();
//2.获得操作接口的动态对象
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
//3.删除指定Id的记录
int count = studentMapper.deleteByIds(new int[]{1,2});
//int count = studentMapper.deleteByIds(2);
//4.提交事务
session.commit();
//5.关闭
session.close();
System.out.println("返回值:"+count);
}
/**
* 更新
*/
@Test
public void updateByNotnull(){
//1.获得操作对象,session
SqlSession session = MybatisUtils.getSession();
//2.获得操作接口的动态对象
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Student student=new Student();
student.setStuName("张三2");
//student.setStuAge(20);
student.setStuId(2L);
//3.更新对象
int count = studentMapper.update(student);
//4.提交事务
session.commit();
//5.关闭
session.close();
System.out.println("返回值:"+count);
}
/**
* 条件查询
*/
@Test
public void findByCondition(){
//1.获得操作对象,session
SqlSession session = MybatisUtils.getSession();
//2.获得操作接口的动态对象
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Student entity=new Student();
entity.setStuName("张三");
entity.setStuId(1L);
//3.返回单条数据
List<Student> students = studentMapper.findByCondition(entity);
//5.关闭
session.close();
for (Student student : students) {
System.out.println(student.getStuName());
}
}
}
4.4. 为什么如何使用集合或者数组需要包一层Map
为什么Provider的方法,如果遇到数组或者集合,需要包一层Map。因为作者的设计就是这样。没有可以解释的理由。具体判断代码如下。在Mybatis的源码的
org.apache.ibatis.session.defaults.DefaultSqlSession.wrapCollection(Object)对应的代码!!!!
二.高级查询(难点)
前提:在使用POJO来绑定数据库表的数据的时候使用!!
所谓的高级查询,就是多表连接查询.并将查询的结果绑定到指定对象里面.
如果要实现将多个表的数据绑定到一个对象,那必须要将表与表之间关系建立在实体类里面。那么首先要理解表与表直接的关系有哪些?
表与表的关系:
一对一
一对多
多对一
多对多
Mybatis是使用标签
<association> :一的一方配置<association>标签
<collection> :多的一方配置<collection>:标签
1. 根据数据库的关系创建实体类
多表查询时,需要一个实体类多个表的数据.那么需要将表与表的关系,也要反应在实体类与实体类之间的关系.
配置一条数据的一方,使用实体类的引用
配置多条数据的一方,使用List集合
地址表
public class Address {
private Integer aid;
private String aname;
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public String getAname() {
return aname;
}
public void setAname(String aname) {
this.aname = aname;
}
public Integer getAid() {
return aid;
}
public void setAid(Integer aid) {
this.aid = aid;
}
}
学生表
public class Student {
private Integer sid;//int(11) not null auto_increment comment '编号',
private Integer aid;//int(11) null default '0',
private String sname;//varchar(50) null default null comment '姓名',
private String sex;//char(3) null default null comment '性别',
private Date birthday;//date null default null comment '生日',
private Integer age;//int(11) null default null comment '年龄',
//假定一个地址对应一个学生,那么这里就配置一个实体类
private Address address;
//多的一方,使用集合
private List<Course> courses;
public List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public Integer getAid() {
return aid;
}
public void setAid(Integer aid) {
this.aid = aid;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
}
课程表
public class Course {
private Integer cid;//int(11) not null auto_increment comment '科目编号',
private Integer sid;//int(11) null default null comment '学生编号',
private Integer tid;//int(11) null default null comment '教师编号',
private String courseName;//varchar(50) null default null comment '科目名',
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
}
根据实体类的定义,如果是属性集合就配置,如果返回的数据是一个实体的就配置
2. association标签的使用
接口代码
public interface StudentMapper {
/**
* 查询所有的学生信息,并包括地址信息
* @return
*/
List<Student> findStudentAndAddress();
/**
* 查询所有的学生信息,并包括地址信息
* @return
*/
List<Student> findStudentAndAddress1();
/**
* 查询学生的信息,包括学生选择的课程
* @return
*/
List<Student> findStudentAndCourse();
/**
* 查询多个个表,将数据放在map里面
* @return
*/
List<Map<String,Object>> findStudentAndCourseToMap();
}
映射文件代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<!-- 命名空间,用于指定动态创建的对象返回的映射接口 -->
<mapper namespace="cn.mapper.StudentMapper">
<!-- #查询学生的信息,并且包括学生的学习的课程 -->
<resultMap type="Student" id="studentAndCourseResultMap" autoMapping="true">
<!-- 多的一方使用collection -->
<!--
property:集合的属性名
ofType:集合元素的类型
select:就是指定查询的操作,操作的结果一定要与collection的类型匹配.
column:就是传入到select的ID的字段名
-->
<collection property="courses" column="sid" autoMapping="true" ofType="Course" select="findCourseBySid">
</collection>
</resultMap>
<!-- 创建一个resultMap组合 findStudentAndAddress操作的结果-->
<resultMap type="Student" id="studentAndAddressResultMap" autoMapping="true">
<!-- 如果是多个表组合autoMapping默认是false,如果不想所有字段都写,要显示声明autoMapping="true" -->
<!--
property:指定实体类的属性
column:指定这个实体类属性对应的外键字段名
javaType:指定实体类的类型
autoMapping:表示,如果数据库的字段和属性一样,自动匹配
-->
<association property="address" column="aid" javaType="Address" autoMapping="true">
<id property="aid" column="AID"/>
<result property="aname" column="aname"/>
</association>
</resultMap>
<resultMap type="Student" id="studentAndAddressResultMap1" autoMapping="true">
<!-- 一的一方 -->
<!-- 指定select查询的操作,查询的的结果一定要与property的实体类的字段匹配 -->
<!-- 注意事项,使用select去获得地址表的数据,因为学生表与地址是通过aid来关联的,那么需要指定aid -->
<!-- 一个映射调用另一个映射文件的操作是,使用"命名空间.操作ID" -->
<association property="address" column="aid" autoMapping="true" select="cn.mapper.AddressMapper.findAddressById">
</association>
</resultMap>
<!-- 需求:查询学生表的所有数据,并且包括地址表的记录 -->
<!-- 方案1:将两个表的数据使用一条SQL语句查询出来,然后使用ResultMap组合查询的结果 -->
<!-- 查询多个表数据不要使用resultType,因为resultType只能接收一个表的数据,如果要接收两个以上的表的数据使用resultMap -->
<select resultMap="studentAndAddressResultMap" id="findStudentAndAddress">
select * from student s,address a where s.AID=a.aid
</select>
<!-- 方案2: 先查询Student表的记录,然后在resultMap的子标签association的select属性指定查询的操作,将结果返回到实体类-->
<select resultMap="studentAndAddressResultMap1" id="findStudentAndAddress1">
SELECT * FROM student
</select>
<!-- 查询学生的信息 -->
<select resultMap="studentAndCourseResultMap" id="findStudentAndCourse">
SELECT * FROM student
</select>
<!-- 查询指定学生编号的课程,通过学生的编号,我们知道该学生选择了哪些课程 -->
<select resultType="Course" id="findCourseBySid">
select * from course where sid=#{sid}
</select>
<select resultType="map" id="findStudentAndCourseToMap">
select * from student s,course c where s.SID=c.SID
</select>
</mapper>
测试代码
public class StudentMapperTest {
@Test
public void findStudentAndAddress(){
SqlSession session = DbUtils.getSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> students = studentMapper.findStudentAndAddress();
for(Student s:students){
System.out.println("学生姓名:"+s.getSname()+",地址:"+s.getAddress().getAname());
}
}
@Test
public void findStudentAndAddress1(){
SqlSession session = DbUtils.getSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> students = studentMapper.findStudentAndAddress1();
for(Student s:students){
System.out.println("学生姓名:"+s.getSname()+",地址:"+s.getAddress().getAname());
}
}
@Test
public void findStudentAndCourse(){
SqlSession session = DbUtils.getSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> students = studentMapper.findStudentAndCourse();
for(Student s:students){
System.out.println("学生姓名:"+s.getSname());
for(Course c:s.getCourses()){
System.out.println("--课程:"+c.getCourseName());
}
}
}
@Test
public void findStudentAndCourseToMap(){
SqlSession session = DbUtils.getSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Map<String, Object>> students = studentMapper.findStudentAndCourseToMap();
for(Map<String, Object> map:students){
//必须跟数据库的字段一致
System.out.print("学生姓名:"+map.get("SNAME")+",");
System.out.println("--课程:"+map.get("COURSE_NAME"));
}
}
}
3. collection标签的使用
映射文件代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<!-- 命名空间,用于指定动态创建的对象返回的映射接口 -->
<mapper namespace="cn.mapper.StudentMapper">
<!-- #查询学生的信息,并且包括学生的学习的课程 -->
<resultMap type="Student" id="studentAndCourseResultMap" autoMapping="true">
<!-- 多的一方使用collection -->
<!--
property:集合的属性名
ofType:集合元素的类型
select:就是指定查询的操作,操作的结果一定要与collection的类型匹配.
column:就是传入到select的ID的字段名
-->
<collection property="courses" column="sid" autoMapping="true" ofType="Course" select="findCourseBySid">
</collection>
</resultMap>
<!-- 创建一个resultMap组合 findStudentAndAddress操作的结果-->
<resultMap type="Student" id="studentAndAddressResultMap" autoMapping="true">
<!-- 如果是多个表组合autoMapping默认是false,如果不想所有字段都写,要显示声明autoMapping="true" -->
<!--
property:指定实体类的属性
column:指定这个实体类属性对应的外键字段名
javaType:指定实体类的类型
autoMapping:表示,如果数据库的字段和属性一样,自动匹配
-->
<association property="address" column="aid" javaType="Address" autoMapping="true">
<id property="aid" column="AID"/>
<result property="aname" column="aname"/>
</association>
</resultMap>
<resultMap type="Student" id="studentAndAddressResultMap1" autoMapping="true">
<!-- 一的一方 -->
<!-- 指定select查询的操作,查询的的结果一定要与property的实体类的字段匹配 -->
<!-- 注意事项,使用select去获得地址表的数据,因为学生表与地址是通过aid来关联的,那么需要指定aid -->
<!-- 一个映射调用另一个映射文件的操作是,使用"命名空间.操作ID" -->
<association property="address" column="aid" autoMapping="true" select="cn.mapper.AddressMapper.findAddressById">
</association>
</resultMap>
<!-- 需求:查询学生表的所有数据,并且包括地址表的记录 -->
<!-- 方案1:将两个表的数据使用一条SQL语句查询出来,然后使用ResultMap组合查询的结果 -->
<!-- 查询多个表数据不要使用resultType,因为resultType只能接收一个表的数据,如果要接收两个以上的表的数据使用resultMap -->
<select resultMap="studentAndAddressResultMap" id="findStudentAndAddress">
select * from student s,address a where s.AID=a.aid
</select>
<!-- 方案2: 先查询Student表的记录,然后在resultMap的子标签association的select属性指定查询的操作,将结果返回到实体类-->
<select resultMap="studentAndAddressResultMap1" id="findStudentAndAddress1">
SELECT * FROM student
</select>
<!-- 查询学生的信息 -->
<select resultMap="studentAndCourseResultMap" id="findStudentAndCourse">
SELECT * FROM student
</select>
<!-- 查询指定学生编号的课程,通过学生的编号,我们知道该学生选择了哪些课程 -->
<select resultType="Course" id="findCourseBySid">
select * from course where sid=#{sid}
</select>
<select resultType="map" id="findStudentAndCourseToMap">
select * from student s,course c where s.SID=c.SID
</select>
</mapper>
如果记得这样关联配置复杂,使用map可以代替上面 的配置.
4. 基于注解配置
映射接口
–学生表
/**
* 注意事项:操作接口的方法名是不可以相同的 答:因为方法名对应的xml的是ID,Id意味必须是唯一的。
*
* @author ranger
*
*/
public interface StudentMapper {
/**
* 通过ID查询指定的记录
*
* @param id
* @return
*/
@Select(value="SELECT * FROM tb_student WHERE stu_id=#{id}")
/**
* Results注解,对应<resultmap>
* Result注解,对应的是<result 标签>同时可以配置One注解和Many注解
* One注解:对应<association>标签
* Many注解:对应<collection>标签
* @param id
* @return
*/
@Results(value=@Result(many=@Many(select="cn.mapper.ResultMapper.findByStuId",fetchType=FetchType.LAZY),property="results",column="stu_id"))
Student findById(Object id);
}
–成绩表
/**
* 注意事项:操作接口的方法名是不可以相同的 答:因为方法名对应的xml的是ID,Id意味必须是唯一的。
*
* @author ranger
*
*/
public interface ResultMapper {
@Select(value="SELECT * FROM tb_result WHERE stu_id=#{stuId}")
List<Result> findByStuId(Object sutId);
/**
* 通过ID获得对应的成绩信息
* @param id
* @return
*/
@Select(value="SELECT * FROM tb_result WHERE res_id=#{resId}")
@Results(value=@org.apache.ibatis.annotations.Result(property="student",column="stu_id",one=@One(fetchType=FetchType.EAGER,select="cn.mapper.StudentMapper.findById")))
Result findById(Object id);
}
5 延迟加载
在一些多表连接的查询中,只要用到主表数据,不需要使用关联表数据的时候不查询关联表,就是延迟加载 。
mybatis延迟加载配置:
1.在主配置文件中设置两个setting
lazyLoadingEnabled需要为true(默认true),表示开启延迟加载
aggressiveLazyLoading需要为false(默认为true) 表示取消积极加载策略
(积极加载策略的意思就是当前的对象关联的属性也会立即加载)
2.在关联查询的xml或者注解中配置Fetch=lazy
<settings>
<!-- 使用驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--
lazyLoadingEnabled 默认值为true 延迟加载开启
aggressiveLazyLoading默认值是true
积极加载,如果它为true,使用了延迟加载,所有的级联属性都会加载(发送sql)
如果为false,级联属性不会自动加载,需要用到才加载
-->
<!-- <setting name="lazyLoadingEnabled" value="true"/> -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
@Select("select * from tb_result")
@Results(id="resultAndStudentMap",value={
/*@Result(column="res_id",property="resId"),
@Result(column="res_subject",property="resSubject"),
@Result(column="res_score",property="resScore"),*/
@Result(column="stu_id",property="stuId"),
//one=@One(select="",fetchType=): one表示关联一方属性的映射如何封装 @One设置一方映射操作 等于association select:需要发送的sql语句id mapper接口.方法名
//fetchType:设置是否懒加载 设置了懒加载FetchType.LAZY,如果不需要用到关联属性,则不发送关联sql语句
@Result(column="stu_id",property="student",one=@One(select="cn.nyse.mapper.StudentMapper.selectByPrimaryKey",fetchType=FetchType.LAZY)),
})
List<cn.nyse.entity.Result> findAllResultAndStudent();
测试
public void testFindAllResultAndStudent(){
List<Result> list = resultMapper.findAllResultAndStudent();
for (Result result : list) {
//在默认情况下,不使用关联的属性信息(学生),也会关联学生表查询
// System.out.println("当前的学生为:"+result.getStudent());
//不使用不加载的延迟策略,不能直接打印对象信息 只要不使用关联的student属性,就不会查tb_student表
System.out.println("成绩为:"+result.getResSubject()+","+result.getResScore()+result.getStuId()+result.getResId());
System.out.println("-------------------------------开始查询student数据--------------------------------------");
System.out.println(result.getStudent());
System.out.println(result.getClass().getName());
}
}
ject",property=“resSubject”),
@Result(column=“res_score”,property=“resScore”),*/
@Result(column=“stu_id”,property=“stuId”),
//one=@One(select="",fetchType=): one表示关联一方属性的映射如何封装 @One设置一方映射操作 等于association select:需要发送的sql语句id mapper接口.方法名
//fetchType:设置是否懒加载 设置了懒加载FetchType.LAZY,如果不需要用到关联属性,则不发送关联sql语句
@Result(column=“stu_id”,property=“student”,one=@One(select=“cn.nyse.mapper.StudentMapper.selectByPrimaryKey”,fetchType=FetchType.LAZY)),
})
List<cn.nyse.entity.Result> findAllResultAndStudent();
测试
~~~java
public void testFindAllResultAndStudent(){
List<Result> list = resultMapper.findAllResultAndStudent();
for (Result result : list) {
//在默认情况下,不使用关联的属性信息(学生),也会关联学生表查询
// System.out.println("当前的学生为:"+result.getStudent());
//不使用不加载的延迟策略,不能直接打印对象信息 只要不使用关联的student属性,就不会查tb_student表
System.out.println("成绩为:"+result.getResSubject()+","+result.getResScore()+result.getStuId()+result.getResId());
System.out.println("-------------------------------开始查询student数据--------------------------------------");
System.out.println(result.getStudent());
System.out.println(result.getClass().getName());
}
}
- explain 深入剖析 MySQL 索引及其性能优化指南
- 图文并茂详解 SQL JOIN
- 自定义 hadoop MapReduce InputFormat 切分输入文件
- Hadoop MapReduce 二次排序原理及其应用
- MySQL Tips【Updating】
- Meltdown、Spectre攻击---CPU乱序执行和预测执行导致的安全问题
- WordPress 4.6远程代码执行漏洞(CVE-2016-10033)复现环境搭建指南
- 相似文档查找算法之 simHash 简介及其 java 实现
- Hadoop 中利用 mapreduce 读写 mysql 数据
- Android O中对TEE加解密算法的新要求
- storm 原理简介及单机版安装指南
- Python Tips, Tricks, and Hacks
- 英特尔放出Linux微代码以修复Meltdown和Spectre漏洞
- python基础(5):深入理解 python 中的赋值、引用、拷贝、作用域
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Yii框架视图、视图布局、视图数据块操作示例
- 用php定义一个数组最简单的方法
- laravel-admin自动生成模块,及相关基础配置方法
- laravel-admin select框默认选中的方法
- Laravel-admin之修改操作日志的方法
- php使用curl伪造浏览器访问操作示例
- 关于laravel后台模板laravel-admin select框的使用详解
- 基于Laravel-admin 后台的自定义页面用法详解
- php解决约瑟夫环算法实例分析
- 浅谈laravel-admin的sortable和orderby使用问题
- 使用composer安装使用thinkphp6.0框架问题【视频教程】
- 基于laravel-admin 后台 列表标签背景的使用方法
- 解决laravel-admin 自己新建页面里 js 需要刷新一次的问题
- laravel-admin 中列表筛选方法
- Laravel框架控制器的middleware中间件用法分析