WUJUNWEI'S BLOG
WuJunwei
Jun 30, 2020
It takes 28 minutes to read this article.

多对一、一对多的使用示例;动态SQL语句;

多对一、一对多的使用示例

创建一对多数据库

## 一对多数据表
## 创建班级表
create table t_clazz(
	`id` int primary key auto_increment,
	`name` varchar(50)
);

## 插入班级信息
insert into t_clazz(`name`) values('javaEE20170228');
insert into t_clazz(`name`) values('javaEE20170325');
insert into t_clazz(`name`) values('javaEE20170420');
insert into t_clazz(`name`) values('javaEE20170515');

## 创建学生表
create table t_student(
	`id` int primary key auto_increment,
	`name` varchar(50),
	`clazz_id` int,
	foreign key(`clazz_id`) references t_clazz(`id`)
);

## 插入班级信息
insert into t_student(`name`,`clazz_id`) values('stu0228_1',1);
insert into t_student(`name`,`clazz_id`) values('stu0228_2',1);
insert into t_student(`name`,`clazz_id`) values('stu0228_3',1);
insert into t_student(`name`,`clazz_id`) values('stu0325_1',2);
insert into t_student(`name`,`clazz_id`) values('stu0325_2',2);
insert into t_student(`name`,`clazz_id`) values('stu0420_1',3);

一对多,立即加载

创建student类

public class Student {
	private Integer id;
	private String name;

创建Clazz类

public class Clazz {
	private Integer id;
	private String name;
	private List<Student> stus;

ClazzMapper接口

public interface ClazzMapper {
	/**
	 * 根据班级id的信息,直接查询出班级信息,以及这个班的全部学生信息。
	 */
	public Clazz queryClazzByIdForSample(Integer id);

}

ClazzMapper.xml配置文件:

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.ncu.zte.mybatis6_29.mapper.ClazzMapper">

<resultMap type="clazz" id="queryClazzByIdSample_resultMap">
	<id column="id" property="id"/>
	<result column="name" property="name"/>
	<collection property="stus" ofType="student">
		<id column="stu_id" property="id"/>
		<result column="stu_name" property="name"/>
	</collection>
</resultMap>

<!-- 	 * 根据班级id的信息,直接查询出班级信息,以及这个班的全部学生信息。 -->
<select id="queryClazzByIdSample" resultMap="queryClazzByIdSample_resultMap">
	SELECT t_clazz.*,t_student.id stu_id,t_student.`name` stu_name 
		FROM t_clazz LEFT JOIN t_student ON t_clazz.`id` = t_student.`clazz_id`
			 WHERE t_clazz.`id` = 2
</select>

</mapper>

测试代码:

@Test
public void testQueryClazzByIdSample() throws IOException {
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("com/ncu/zte/mybatis6_29/mybatis-config.xml"));
	SqlSession session =sqlSessionFactory.openSession();
	try{
		ClazzMapper mapper = session.getMapper(ClazzMapper.class);
		System.out.println(mapper.queryClazzByIdSample(2));
	}finally{
		session.close();
	}
	
	fail("Not yet implemented");
}

测试结果

一对多,赖加载

ClazzMapper接口

/**
 * 根据班级id查询班级信息(只查班级)
 */
public Clazz queryClazzByIdForTwoStep(Integer id);

StudentMapper接口

public interface StudentMapper {
	/**
	 * 根据班级编号查询本班所有学生信息
	 */
	public List<Student> queryStudentsByClazzId(Integer clazzId);

}

StudentMapper.xml配置文件

<mapper namespace="com.ncu.zte.mybatis6_29.mapper.StudentMapper">
<!-- 	 * 根据班级编号查询本班所有学生信息 -->
<select id="queryStudentByClazzId" resultType="student">
select id,name from t_student where clazz_id =#{clazzId}
</select>

</mapper>

ClazzMapper.xml配置文件

collection 标签是专门用来配置集合属性的(它可以通过调用一个select查询得到需要集合)。

  • property属性设置你要配置哪个集合属性
  • select 属性设置你要调用哪个查询
  • column 将哪个列的值传递给查询做为参数

<resultMap type=”clazz” id=”queryClazzByIdForTwo_resultMap”>

<id column="id" property="id"/\>
<result column="name" property="name"/\>
<collection property="stus" column="id" select="com.ncu.zte.mybatis6_29.mapper.StudentMapper.queryStudentByClazzId" \></collection\>

</resultMap>

<!-- 	 * 根据班级id查询班级信息(只查班级) --\>
<select id="queryClazzByIdForTwo" resultMap="queryClazzByIdForTwo_resultMap"\>
	select id,name from t_clazz where id = #{id}
 <select\>

测试代码

只查班级信息:

@Test
public void queryClazzByIdForTwo() throws IOException {
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("com/ncu/zte/mybatis6_29/mybatis-config.xml"));
	SqlSession session =sqlSessionFactory.openSession();
	try{
		ClazzMapper mapper = session.getMapper(ClazzMapper.class);
		
		Clazz clazz = mapper.queryClazzByIdForTwo(1);

		System.out.println(clazz.getId()+clazz.getName());
		
		System.out.println(clazz.getStus());
	}finally{
		session.close();
	}
	
	fail("Not yet implemented");
}

查询班级中student信息

System.out.println(clazz.getStus());

双向关联

StudentMapper接口

/**
 * 把学生分两次查,一次还要查班级。
 */
public List<Student> queryStudentsByClazzIdForTwoStep(Integer clazzId);

StudentMapper.xml配置文件:

<resultMap type="student" id="queryStudentByClassIdForTwo_resultMap">
	<id column="id" property="id"/>
	<result column="name" property="name"/>
	<collection  property="clazz" column="clazz_id" select="com.ncu.zte.mybatis6_29.mapper.ClazzMapper.queryClazzByIdForTwo">
	
	</collection >
</resultMap>

<select id="queryStudentByClassIdForTwo" resultMap="queryStudentByClassIdForTwo_resultMap">
select id,name,clazz_id from t_student where clazz_id = #{clazzId}
</select>

还要修改ClazzMapper.xml配置文件:

<select id="queryClazzByIdForTwo" resultMap="queryClazzByIdForTwo_resultMap"\>
	select id,name from t_clazz where id = #{id}
</select\>

测试

@Test
public void queryStudentByClassIdForTwo() throws IOException {
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("com/ncu/zte/mybatis6_29/mybatis-config.xml"));
	SqlSession session =sqlSessionFactory.openSession();
	try{
		ClazzMapper mapper = session.getMapper(ClazzMapper.class);
		
		Clazz clazz = mapper.queryClazzByIdForTwo(1);

		List<Student> stus = clazz.getStus();
		
		for(Student student :stus){
			System.out.println(student.getId()+"	"+student.getName()+"	"+student.getClazz().getName());
		}
	}finally{
		session.close();
	}
	
	fail("Not yet implemented");
}

双向关联的常见问题:死循环。

如何解决双向关联的死循环: 方法一:不要调用toString方法。 方法二:在你需要终止调用的最后一次查询使用resultType,而不用resultMap

动态SQL语句

if 语句

说明: if语句,可以动态的根据你的值来决定,是否需要动态的添加查询条件。

UserMapper接口

public interface UserMapper {
	/**
	 * 根据user对象中的lastName和sex属性查询用户 <br/>
	 * 		希望lastName值不能为null,
	 * 		性别只能是0和1,有效值
	 * 
	 * @param user
	 * @return
	 */
	public List<User> queryUsersByNameAndSex(User user);

}

UserMapper.xml配置文件:

<!-- 	/** -->
<!-- 	 * 根据user对象中的lastName和sex属性查询用户 -->
<!-- 	 *  -->
<!-- 	 * @param user -->
<!-- 	 * @return -->
<!-- 	 */ -->
<!-- 	public List<User> queryUsersByNameAndSex(User user); -->
	<select id="queryUsersByNameAndSex" resultType="com.atguigu.pojo.User">
		select 
			id,last_name lastName,sex 
		from 
			t_user 
		where 
			<if test="lastName != null">
				last_name like concat('%',#{lastName},'%')
			</if>
			<if test="sex == 0 || sex == 1">
				 and sex = #{sex}
			</if>
	</select\>

where 语句

说明: where语句,可以帮我们在多个动态语句中,有效的去掉前面的多余的and 或 or 之类的多余关键字,比如上面的if语句中,如果前一个判断为false,则语句就变成了where and sex = ${sex},自然就会出错

<select id="queryUsersByNameAndSex" resultType="com.atguigu.pojo.User">
	select 
		id,last_name lastName,sex 
	from 
		t_user 
		
	<!-- where标签可以动态的去掉包含的内容前面的关键字and 和 or
			where 标签还会在包含的内容有的情况,添加一个where关键字
	 -->
	<where>
		<!-- if标签用来判断一个条件是否成立。如果成立 就执行里面的内容 -->
		<if test="lastName != null">
			last_name like concat('%',#{lastName},'%')
		</if>
		<if test="sex == 0 || sex == 1">
			 and sex = #{sex}
		</if>
	</where> 
</select>

trim语句

说明: trim 可以动态在包含的语句前面和后面添加内容。也可以去掉前面或者后面给定的内容,比如上面的if语句中,如果后一个判断为false,则语句就变成了where last_name like concat(‘%’,’bb’,’%’) ,自然就会出错

  • prefix 前面添加内容
  • suffix 后面添加内容
  • suffixOverrides 去掉的后面内容
  • prefixOverrides 去掉的前面内容

    <select id=”queryUsersByNameAndSex” resultType=”com.atguigu.pojo.User”> select id,last_name lastName,sex from t_user

      <!-- where标签可以动态的去掉包含的内容前面的关键字and 和 or
              where 标签还会在包含的内容有的情况,添加一个where关键字
       -->
      <trim suffixOverrides="and" prefix="where">
          <!-- if标签用来判断一个条件是否成立。如果成立 就执行里面的内容 -->
          <if test="lastName != null">
              last_name like concat('%',#{lastName},'%')  and 
          </if>
          <if test="sex == 0 || sex == 1">
              sex = #{sex}
          </if>
      </trim>    </select>
    

choose( when , otherwise )语句

说明:choose when otherwise 可以执行多路选择判断,但是只会有一个分支会被执行。

类似switch case 语句

UserMapper接口

/**
 * 如果lastName有值,则使用它查询
 * 如果lastName没有值,sex值有效,则使用sex查询
 * 否则,你自己添加一个查询条件(默认)
 */
public List<User> queryUsersByNameAndSexChoose(User user);

UserMapper.xml配置文件:

<select id="queryUsersByNameAndSexChoose" resultType="com.atguigu.pojo.User">
	select 
		id,last_name lastName,sex 
	from 
		t_user 
	<where>
		<choose>
			<when test="lastName != null">
				last_name like concat('%',#{lastName},'%')
			</when>
			<when test="sex == 0 || sex == 1">
				sex = #{sex}
			</when>
			<otherwise>
				last_name = 'wzg168'
			</otherwise>
		</choose>
	</where> 
</select>

set语句

删除条件后的逗号

UserMapper接口

public int updateUser(User user);

UserMapper配置文件:

<update id="updateUser" parameterType="com.atguigu.pojo.User">
	update 
		t_user 
	<set>
		<if test="lastName != null">
			last_name = #{lastName},
		</if>
		<if test="sex == 0 || sex == 1">
			sex = #{sex}
		</if>
	</set>
	where 
		id = #{id}
</update>

foreach语句

select * from t_user where id in (1,3,4);

UserMapper接口

/**
 * 执行类似的语句:select * from t_user where id in (1,3,4);
 */
public List<User> queryUsersByIds(List<Integer> ids);

UserMapper配置文件:

<!-- 		/** -->
<!-- 	 * 执行类似的语句:select * from t_user where id in (1,3,4); -->
<!-- 	 */ -->
<!-- 	public List<User> queryUsersByIds(List<Integer> ids); -->

<select id="queryUsersByIds" resultType="com.atguigu.pojo.User">
	select 
		id,last_name lastName,sex 
	from 
		t_user 
	<where>
		id in 
		<!-- 
			foreach 遍历输出
				collection你要遍历的数据源
				items 表示当前遍历的数据
				open 表示遍历之前添加的内容 
				close 表示遍历之后添加的内容
				separator 给遍历的每个元素中间添加的内容
		 -->
		<foreach collection="list" item="i" open="(" close=")" separator=",">
			#{i}
		</foreach>
	</where>
</select>