这一节是JavaEE课设的第三节了,目前还奋战在持久层。这一节主要战场是mybatis框架,在开始这一节的内容前,我们先来简单回顾下前面两个小节我们都干了什么:
因此这一节,就应该开始编写mapper.xml文件了,也就是使用mybatis框架,给每个设计好的接口方法给予SQL实现。
mybatis的配置首先需要拷贝相关组件并build path
他们。
接着需要在src目录下编写mybatis的配置文件,SqlMapConfig.xml
(名称不是固定的,可以自己任意命名,但是通常使用几种约定俗成的命名)
<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文件:
当然也可以选择将他们分开放置,但是需要注意的是,xml文件的位置需要和中心配置文件的扫描路径对应。接口的位置,需要和mapper文件中的名称空间一致。
mapper方法中传入多个参数,有多种方法进行处理,其中最常见的就是这三种:
模糊查询需要使用百分号或者下划线用以当做占位符,但是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的。可是面对业务需要(例如该项目中的查询票房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 >= 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 >= 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,仅编写了其中的四个 (边复习边整理边写真的是太慢了,不过好在学的深刻)
先把所有的mapper放在这里:
这个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>
<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>
<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可以快速的对某个方法进行单元测试而不必是主方法,用这项技术,我们可以编写mapper接口类对应的测试类以测试我们的mapper文件有没有写对。
但在使用junit进行测试前,不妨先说明mybatis访问mapper接口中方法的方法,即我们如何用到被mapper文件实现了的mapper接口方法。
一般来说,我们在编写mapper文件时使用名称空间与mapper接口进行绑定,在配置核心文件时对所有的mapper进行了扫描。这样一来,mybatis组件就可以顺藤摸瓜地找到所有的接口并根据mapper文件生成相应的实现方法,这些方法可以被封装在mapper类的实现类对象中呈现。只要拿到了这个对象,我们就可以调用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实现对象。:
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,有了这个类,我们就可以开始测试了,下面是几个对应的测试类:
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();
}
}
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();
}
}
}
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();
}
}
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课设】《影院系统》(四)搞定持久层