【从零开始JavaEE课设】《影院系统》(三)配置mybatis框架,编写mapper文件并测试(附mybatis知识点)

前言

这一节是JavaEE课设的第三节了,目前还奋战在持久层。这一节主要战场是mybatis框架,在开始这一节的内容前,我们先来简单回顾下前面两个小节我们都干了什么:

  • 第一节析了需求,设计了数据库,设计了基础的model类,
  • 第二节编写了持久层的mapper接口

因此这一节,就应该开始编写mapper.xml文件了,也就是使用mybatis框架,给每个设计好的接口方法给予SQL实现。


知识点梳理

配置mybatis步骤

mybatis的配置首先需要拷贝相关组件并build path他们。
【从零开始JavaEE课设】《影院系统》(三)配置mybatis框架,编写mapper文件并测试(附mybatis知识点)_第1张图片
接着需要在src目录下编写mybatis的配置文件,SqlMapConfig.xml(名称不是固定的,可以自己任意命名,但是通常使用几种约定俗成的命名)

【从零开始JavaEE课设】《影院系统》(三)配置mybatis框架,编写mapper文件并测试(附mybatis知识点)_第2张图片




<configuration>
	
	<typeAliases>
		<package name="com.none.model"/>
	typeAliases>
	
	<environments default="development">

		<environment id="development">
			<transactionManager type="JDBC">transactionManager>
			<dataSource type="POOLED">
				<property name="url" value="jdbc:mysql://localhost:3306/cinema?serverTimezone=UTC"/>
				<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
				<property name="username" value="root"/>
				<property name="password" value="mysql"/>
			dataSource>
		environment>
	environments>
	
	
	<mappers>
		<package name="com.none.mapper"/>
	mappers>

configuration>

需要说明的是,整合ssm框架时,这些配置将会以bean的形式交由spring管理,目前就先使用配置进行持久层的开发测试。

在接下来,就可以为每个接口配置对应的mapper.xml文件了。
在mapper包下建立mapper文件:
【从零开始JavaEE课设】《影院系统》(三)配置mybatis框架,编写mapper文件并测试(附mybatis知识点)_第3张图片
当然也可以选择将他们分开放置,但是需要注意的是,xml文件的位置需要和中心配置文件的扫描路径对应。接口的位置,需要和mapper文件中的名称空间一致。

多个参数的访问

mapper方法中传入多个参数,有多种方法进行处理,其中最常见的就是这三种:

  • 将所有的参数封装进一个map中,仅传递一个map
  • 在mapper接口方法的参数前使用@Param注解进行映射。也是这个课设最常使用的方法
  • 在mapper xml中使用param1,param2等访问每一个参数

模糊查询

模糊查询需要使用百分号或者下划线用以当做占位符,但是mapper xml文件不方便进行拼接。
一种解决方法就是使用MySQL提供的拼接字符串函数CONCAT。这样就可以拼接出一个模糊查询的目标串

举个例子,根据名称查询对应影厅,使用模糊查询
<select id="selectByName" resultType="room" parameterType="string">
		SELECT
		<include refid="content">include>
		FROM room
		WHERE name LIKE CONCAT('%',#{value},'%');
	select>

子查询中不能使用limit

在当前版本的数据库中,不支持在子查询中使用limit的。可是面对业务需要(例如该项目中的查询票房topK的功能),有时不得不在二级查询结果中做出限制(例如要限制查询出的电影按照票房降序排列后取前k个的Id号码并以此获取这些电影)

这时使用子查询的一种写法:

<select id="selectTopK" resultMap="filmMap">
		SELECT
		<include refid="filmContent"></include>
		FROM
		
		JOIN film ON ids.film_id = id
		film LEFT JOIN film_post ON film.id = film_post.film_id
		WHERE film.id IN
		(
		SELECT plan.film_id from 
		record JOIN screening_plan plan ON record.plan_id = plan.id
		WHERE record.time &gt;= CONCAT(#{begin},' 00:00:00') AND record.time <= CONCAT(#{end},' 23:59:59') 
		GROUP BY plan.film_id
		ORDER BY SUM(money) DESC
		LIMIT 0,#{k}
		) ;
	</select>

然而这样写会遭到慈祥的警告。
所以可以将子查询换做一张表,使用连接来解决问题

<select id="selectTopK" resultMap="filmMap">
		SELECT
		<include refid="filmContent"></include>
		FROM
		(
		SELECT plan.film_id from 
		record JOIN screening_plan plan ON record.plan_id = plan.id
		WHERE record.time &gt;= CONCAT(#{begin},' 00:00:00') AND record.time <= CONCAT(#{end},' 23:59:59') 
		GROUP BY plan.film_id
		ORDER BY SUM(money) DESC
		LIMIT 0,#{k}
		) ids
		JOIN film ON ids.film_id = id
		LEFT JOIN film_post ON film.id = film_post.film_id;
	</select>

不等号的替换

在各种ML文件中,尖括号都是一种敏感的存在,可同时它又正好是常用的大于号和小于号。如果想要正常的使用大于小于或是大于等于和小于等于关系,就需要使用替换符来代替左右尖括号。替换方式如下:

  • > <==> >
  • < <==> <
  • >= <==> >=
  • <= <==> <=

(其实在码这些替换符号时,如果不用代码块括起来,显示的就是转换过后的符号)


编写mapper并使用Junit进行测试

这一次并没有编写完所有的mapper,仅编写了其中的四个 (边复习边整理边写真的是太慢了,不过好在学的深刻)

先把所有的mapper放在这里:

FilmMapper

这个mapper有技术含量的(对博主来说),Film聚合了一个FilmPost集合,这需要用到结果集映射。这个问题有机会我们不妨单独使用一篇内容来讨论它。

另外有些查询需要联结多个表并需要需要用到分组、聚合函数等内容。(例如取topK的操作)




<mapper namespace="com.none.mapper.FilmMapper">
	<sql id="content">
		 id,name,director,country,length,release_time,offline_time,language,introduction
	sql>
	
	<sql id="noId">
		 name,director,country,length,release_time,offline_time,language,introduction 
	sql>
	
	<sql id="value">
		 #{name},#{director},#{country},#{length},#{releaseTime},#{offlineTime},#{language},#{introduction}
	sql>
	
	<sql id="filmContent">
		 film.id id,film.name name,film.director director,film.country country,film.length length,film.release_time release_time,film.offline_time offline_time,film.language language,film.introduction introduction,
		 film_post.id pid,film_post.dir dir,film_post.film_id film_id 
	
	sql>
	
	
	<resultMap type="film" id="filmMap">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
		<result column="director" property="director"/>
		<result column="country" property="country"/>
		<result column="length" property="length"/>
		<result column="release_time" property="releaseTime"/>
		<result column="offline_time" property="offlineTime"/>
		<result column="language" property="language"/>
		<result column="introduction" property="introduction"/>
	
		<collection property="post" javaType="list" ofType="filmPost">
			<id column="pid" property="id"/>
			<result column="film_Id" property="filmId"/>
			<result column="dir" property="dir"/>
		collection>
			
	resultMap>
	
	<select id="selectByName" parameterType="string" resultMap="filmMap">
		SELECT 
		<include refid="filmContent">include>
		FROM 
		film LEFT JOIN film_post ON film.id = film_post.film_id
		WHERE name LIKE CONCAT('%',#{value},'%');
	select>
	
	<select id="selectOnLine" parameterType="string" resultMap="filmMap">
		SELECT
		<include refid="filmContent">include>
		FROM 
		film LEFT JOIN film_post ON film.id = film_post.film_id
		WHERE #{value} BETWEEN release_time AND offline_time;
	select>
	
	<select id="selectByCountry" parameterType="string" resultMap="filmMap">
		SELECT
		<include refid="filmContent">include>
		FROM 
		film LEFT JOIN film_post ON film.id = film_post.film_id
		WHERE country LIKE CONCAT('%',#{value},'%');
	select>
	
	<select id="selectFilmPlaned" resultMap="filmMap">
		SELECT
		DISTINCT
		<include refid="filmContent">include>
		FROM 
		film JOIN screening_plan plan ON film.id = plan.film_id  
		LEFT JOIN film_post ON film.id = film_post.film_id
		WHERE plan.date = #{date} AND plan.time >= #{time};
	select>
	
	<select id="selectTopK" resultMap="filmMap">
		SELECT
		<include refid="filmContent">include>
		FROM
		(
		SELECT plan.film_id from 
		record JOIN screening_plan plan ON record.plan_id = plan.id
		WHERE record.time >= CONCAT(#{begin},' 00:00:00') AND record.time <= CONCAT(#{end},' 23:59:59') 
		GROUP BY plan.film_id
		ORDER BY SUM(money) DESC
		LIMIT 0,#{k}
		) ids
		JOIN film ON ids.film_id = id
		LEFT JOIN film_post ON film.id = film_post.film_id;
	select>
	
	<insert id="insertFilm" parameterType="film">
		INSERT INTO film
		(<include refid="noId">include>)
		VALUE
		(<include refid="value">include>);
	insert>
	
	<update id="updateFilm" parameterType="film">
		UPDATE film
		<set>
			<if test="#{name} != null">
				name = #{name},
			if>
			<if test="#{dirctor} != null">
				director = #{director},
			if>
			<if test="#{country} != null">
				country = #{country},
			if>
			<if test="#{releaseTime} != null">
				release_time = #{release_time},
			if>
			<if test="#{offlineTime} != null">
				offline_time = #{offlineTime},
			if>
			<if test="#{language} != null">
				language = #{language},
			if>
			<if test="#{introduction] != null">
				introduction = #{introduction},
			if>
		set>
		WHERE id = #{id};
	update>
	
	<delete id="deleteById" parameterType="int">
		DELETE FROM film WHERE id = #{id};
	delete>
mapper>

RoomMapper




<mapper namespace="com.none.mapper.RoomMapper">
	<sql id="content"> 
		 id,seat,name,type 
	sql>
	
	<sql id="noId">
		seat,name,type
	sql>
	
	<insert id="insertRoom" parameterType="room">
		INSERT INTO 
		room(<include refid="noId">include>)
		VALUE
		(#{seat},#{name},#{type});
	insert>
	
	<delete id="deleteById" parameterType="int">
		DELETE FROM room WHERE id = #{value};
	delete>
	
	<update id="updateRoom" parameterType="room">
		UPDATE room SET
		seat = #{seat},
		name = #{name},
		type = #{type} 
		WHERE id = #{id};
	update>
	
	<select id="selectAll" resultType="room">
		SELECT 
		<include refid="content">include>
		FROM room;
	select>	
	
	<select id="selectByName" resultType="room" parameterType="string">
		SELECT
		<include refid="content">include>
		FROM room
		WHERE name LIKE CONCAT('%',#{value},'%');
	select>
	
	<select id="selectByType" resultType="room" parameterType="string">
		SELECT
		<include refid="content">include>
		FROM room
		WHERE type LIKE CONCAT('%',#{value},'%');
	select>
mapper>

FilmPostMapper




  
<mapper namespace="com.none.mapper.FilmPostMapper">
	
	<insert id="insertPost" parameterType="filmPost">
		INSERT INTO film_post(film_id,dir) VALUE(#{filmId},#{dir});
	insert>
	
	<delete id="deleteById" parameterType="int">
		DELETE FROM film_post WHERE id = #{id};
	delete>

mapper>

使用Junit进行测试

使用Junit可以快速的对某个方法进行单元测试而不必是主方法,用这项技术,我们可以编写mapper接口类对应的测试类以测试我们的mapper文件有没有写对。

但在使用junit进行测试前,不妨先说明mybatis访问mapper接口中方法的方法,即我们如何用到被mapper文件实现了的mapper接口方法。

一般来说,我们在编写mapper文件时使用名称空间与mapper接口进行绑定,在配置核心文件时对所有的mapper进行了扫描。这样一来,mybatis组件就可以顺藤摸瓜地找到所有的接口并根据mapper文件生成相应的实现方法,这些方法可以被封装在mapper类的实现类对象中呈现。只要拿到了这个对象,我们就可以调用mapper中的方法了(这可是典型的多态体现)。

现在的问题就是,我们要怎么拿到这个对象呢?

大致需要以下几个步骤:

  • 以流的形式读取核心配置文件
  • 根据流创建SqlSessionFactoryBuilder对象
  • 使用SqlSessionFactoryBuilder创建SqlSessionFactory对象
  • 使用SqlSessionFactory创建SqlSession对象
  • 传入需要的mapper类给SqlSession对象以获得mapper接口实现对象
    (获取流的步骤需要捕获异常)

写成代码,大致是下面的样子(以获取FilmMapper为例):

try {
	InputStream stream = Resources.getResourceAsStream("SqlMapConfig.xml");
	SqlSession session = new SqlSessionFactoryBuilder().build(stream).openSession();
	FilmMapper mapper = session.getMapper(FilmMapper.class);
} catch (IOException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}

但是每一次测试都要这样写,就太费劲了,我们不妨将这个过程封装起来,只需要传入一个mapper类,就可以吐出一个mapper实现对象。:

MapperUtil工具类

package com.none.util;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MapperUtil {
	static InputStream stream;
	static SqlSession session;
	static{
		try {
			stream = Resources.getResourceAsStream("SqlMapConfig.xml");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		session= new SqlSessionFactoryBuilder().build(stream).openSession();
	}
	 
	public static Object getMapper(Class mapper) {
		return session.getMapper(mapper);
	}
	
	public static void commit()
	{
		session.commit();
	}
}

nice,有了这个类,我们就可以开始测试了,下面是几个对应的测试类:

RoomTest

package com.none.tests;

import java.util.List;

import org.junit.Test;

import com.none.mapper.RoomMapper;
import com.none.model.Room;
import com.none.util.MapperUtil;

public class RoomTest {
	RoomMapper mapper = (RoomMapper) MapperUtil.getMapper(RoomMapper.class);
	
	@Test
	public void select()
	{
		
		List<Room> list = mapper.selectAll();
		
		for(Room room:list)
		{
			System.out.println(room);
		}
		
		System.out.println("------------------");
		
		System.out.println(mapper.selectByName("测试厅"));
		
		System.out.println("------------------");
		
		System.out.println(mapper.selectByType("IMAX"));

	}
	
	@Test
	public void insert()
	{
		Room room = new Room();
		room.setName("测试厅4");
		room.setSeat(60);
		room.setType("IMAX");
		System.out.println(room);
		mapper.insertRoom(room);
		
		
		MapperUtil.commit();
	}
	
	@Test
	public void update()
	{
		Room room = mapper.selectByName("测试厅4").get(0);
		
		room.setType("普通影厅");
		
		mapper.updateRoom(room);
		
		MapperUtil.commit();
	}
	
	@Test
	public void delete()
	{
		Room room = mapper.selectByName("测试厅4").get(0);
		
		mapper.deleteById(room.getId());
		
		MapperUtil.commit();
	}
	

}

FilmTest

package com.none.tests;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.none.mapper.FilmMapper;
import com.none.model.Film;
import com.none.util.DateUtil;
import com.none.util.MapperUtil;

public class FilmTest {
	
	FilmMapper mapper = (FilmMapper) MapperUtil.getMapper(FilmMapper.class);
	
	@Test
	public void selectAll()
	{
		List<Film> list = mapper.selectOnLine(DateUtil.getCurrentDate());
		for(Film f: list)
		{
			System.out.println(f);
		}
	}
	
	@Test
	public void selectBy()
	{
		System.out.println(mapper.selectByCountry("国"));
		System.out.println(mapper.selectByName("星际穿越"));
	}
	
	@Test
	public void selectPlaned()
	{
		List<Film> list = mapper.selectFilmPlaned("2020-6-25","00:00:00");
		for(Film f: list)
		{
			System.out.println(f);
		}
	}
	
	@Test
	public void selectTopK()
	{
		List<Film> list = mapper.selectTopK("2020-06-01", "2020-07-01", 2);
		for(Film f: list)
		{
			System.out.println(f);
		}
	}
	
	@Test
	public void insert()
	{
		Film film = new Film();
		film.setName("测试电影1");
		film.setIntroduction("这是一个测试电影");
		/*film.setCountry("");
		film.setDirector("");
		film.setLanguage("");
		film.setOfflineTime("");
		film.setReleseTime("");*/
		
		mapper.insertFilm(film);
		
		MapperUtil.commit();
	}
	
	@Test
	public void update()
	{
		Film film = mapper.selectByName("测试").get(0);
		film.setDirector("王五");
		film.setName("更改后的名称");
		
		mapper.updateFilm(film);
		
		MapperUtil.commit();
	}
	
	@Test
	public void delete()
	{
		Film film = mapper.selectByName("更改").get(0);
		mapper.deleteById(film.getId());
		MapperUtil.commit();
	}
	
	public void test()
	{
		try {
			InputStream stream = Resources.getResourceAsStream("SqlMapConfig.xml");
			SqlSession session = new SqlSessionFactoryBuilder().build(stream).openSession();
			FilmMapper mapper = session.getMapper(FilmMapper.class);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

FilmPostTest

package com.none.tests;

import org.junit.Test;

import com.none.mapper.FilmPostMapper;
import com.none.model.FilmPost;
import com.none.util.MapperUtil;

public class FilmPostTest {

	FilmPostMapper mapper = (FilmPostMapper) MapperUtil.getMapper(FilmPostMapper.class);
	@Test
	public void insert()
	{
		FilmPost post = new FilmPost();
		post.setFilmId(2);
		post.setDir("/post/2/1.jpg");
		
		mapper.insertPost(post);
		MapperUtil.commit();
	}
}

StaffTest

package com.none.tests;

import org.junit.Test;

import com.none.mapper.StaffMapper;
import com.none.util.MapperUtil;

public class StaffTest {
	
	StaffMapper mapper = (StaffMapper) MapperUtil.getMapper(StaffMapper.class);
	@Test
	public void select() {
		
		System.out.println(mapper.selectByIdCard("123456789"));
	}
}


第三节就先搞到这里,明天继续把剩下的mapper写完,就该整合ssm框架了。火速赶制中~
第四节:【从零开始JavaEE课设】《影院系统》(四)搞定持久层

你可能感兴趣的:(JavaWeb)