根据数据库环境的搭建,可以了解用户表和流派表为最简单的两张表。用户表仅用于登陆注册,流派表与其他表没有依赖关系且字段少且简单(专辑表没有依赖关系但是字段多,歌手表依赖流派表,歌曲表依赖流派、专辑和歌手三个表)。
接下来先搞后台搭建,按照流派、专辑、歌手和歌曲的顺序。
7. 后台搭建
7.1 导入页面原型
在WEB-INF下创建page文件夹,把jsp页面拷贝到page下

7.2 搭建springmvc架构
7.2.1 创建springmvc配置文件springmvc.xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
<property name="features">
<array>
<value>WriteMapNullValue</value>
<value>WriteNullStringAsEmpty</value>
</array>
</property>
<property name="dateFormat" value="yyyy-MM-dd"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<context:component-scan base-package="cn.tx.controller"/>
<!-- 视图解析器 -->
<bean id="viewResource" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1024000"></property>
</bean>
<!-- 开启主件 -->
<mvc:default-servlet-handler/>
<!-- 模拟创建controller -->
<mvc:view-controller path="/index" view-name="index"/>
</beans>
7.2.2 配置web.xml
配置springmvc三大组件:servlet、监听器、拦截器。
XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app
version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>filter1</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
启动服务器访问后台index首页

8. 流派模块开发
8.1 列表查询
8.1.1 创建查询SQL
在mybatis中定义查询的SQL,本条SQL我们考虑分页组合查询
(1)查询数据的SQL
XML
<!-- 分页查询:防止SQL注入风险(concat替代${}) -->
<select id="selectByPage" parameterType="cn.tx.query.MtypeQuery" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from mtype
<where>
<if test="tname != null and tname != '' ">
tname like concat('%', #{tname}, '%')
</if>
</where>
limit #{startNum},#{pageSize}
</select>
(2)查询总记录数的SQL
XML
<!-- 总数查询:防止SQL注入风险 + 复用条件 -->
<select id="selectCount" parameterType="cn.tx.query.MtypeQuery" resultType="java.lang.Integer">
select count(*) from mtype
<where>
<if test="tname != null and tname != '' ">
tname like concat('%', #{tname}, '%')
</if>
</where>
</select>
8.1.2 提供分页的Page对象
java
package cn.tx.util;
import java.util.List;
/**
* 分页工具类
* @param <T>
*/
public class Page<T> {
/**
* 每页记录数(已知)
*/
private int pageSize = 5;
/**
* 页码(已知)
*/
private int pageNum = 1;
/**
* 指定查询条件下的总记录数(已知)
*/
private int totalCount = 0;
/**
* 指定查询条件下 的总页数
*/
private int totalPage = 1;
/**
* 使用sql查询的时候的开始行号
*/
private int startNum = 0;
/**
* 结果集
*/
private List<T> list;
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
/**
* totalCount pageSize totalPage
* 0 10 1
* 55 10 6
* 100 10 10
* @return
*/
public int getTotalPage() {
totalPage = totalCount/pageSize;
if(totalCount == 0 || totalCount%pageSize != 0){
totalPage++;
}
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getStartNum() {
return (pageNum -1 )*pageSize;
}
public void setStartNum(int startNum) {
this.startNum = startNum;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
8.1.3 BaseService中提供相应的分页查询接口
java
/**
* 分页查询
* @param q
* @return
*/
public Page<T> selectObjectByCondition(Q q);
8.1.4 BaseService中提供相应的分页查询接口实现
java
@Override
public Page<T> selectObjectByCondition(Q q) {
//获得查询对象的类对象
Class<? extends Object> qclass = q.getClass();
Page<T> page = new Page<T>();
try {
//获得getPageNo对象
Method method = qclass.getMethod("getPageNo", null);
//反射调用getPageNo方法
Integer pageNo = (Integer) method.invoke(q, null);
//创建page对象
page.setPageNo(pageNo);
//计算开始行号和结束行号
int startNum = page.getStartNum();
//好的查询对象 的设置开始行号和结束行号的方法
Method setStartNumMethod = qclass.getMethod("setStartNum", new Class[]{int.class});
setStartNumMethod.invoke(q, startNum);
} catch (Exception e) {
e.printStackTrace();
}
//查询结果集
List<T> list = baseDao.selectObjectByCondition(q);
//查询指定查询条件下的总记录数
int count = baseDao.selectObjectByConditionCount(q);
//把总记录数设置给page对象
page.setTotalCount(count);
page.setList(list);
return page;
}
8.1.5 设置对象查询对象的分页属性
java
package cn.tx.query;
import cn.tx.model.Mtype;
/**
* 作为Mtype表现层接受参数的实体类
* 与返回(响应)封装的实体类区分
* 这是"分层设计"的最佳实践,目的是解耦、安全、可维护。
* MtypeQuery 是"前端要什么",Mtype 是"数据库有什么"
*/
public class MtypeQuery extends Mtype {
/**
* 每页记录数(已知)
*/
private Integer pageSize = 5;
/**
* 页码(已知)
*/
private Integer pageNum = 1;
/**
* 使用sql查询的时候的开始行号
*/
private Integer startNum = 0;
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getStartNum() {
return startNum;
}
public void setStartNum(Integer startNum) {
this.startNum = startNum;
}
}
8.1.6 在MtypeController里面调用Service

java
@Autowired
private MtypeService mtypeService;
/**
* 分页条件查询所有数据
* 1.每页数据的数量 pageSize 已知
* 2.页码 pageNum 已知
* 3.要查看的当前页数据集合 List<Mtype>
* 4.开始行号 startNum = (pageNum - 1) * pageSize
* 5.总页数 totalPage
* 6.数据总页数 totalCount
* @return
*/
@RequestMapping("/list")
public String mytypeList(MtypeQuery Query, Model model){
Page<Mtype> page = mtypeService.selectByPage(Query);
model.addAttribute("page",page);
model.addAttribute("mq",Query);
return "mtype";
}
8.1.7 页码数据展示
表格数据展示

html
<table class="table table-striped table-images" style="color: white;font-size: 14px">
<thead>
<tr>
<th class="hidden-xs-portrait">序号</th>
<th class="hidden-xs">专辑</th>
<th class="hidden-xs">流派</th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach items="${page.list}" var="mtype" varStatus="status">
<tr>
<td class="hidden-xs-portrait">${status.count}</td>
<td class="hidden-xs-portrait">${mtype.tname}</td>
<td class="hidden-xs"> ${mtype.tdesc} </td>
<td><button modify class="btn btn-sm btn-primary" tid="${mtype.tid}" type="button"> 修改 </button>
<button data-toggle="button" tid="${mtype.tid}" class="btn btn-sm btn-warning"> 删除 </button></td>
</tr>
</c:forEach>
</tbody>
</table>
分页条数据展示 pagination.jsp

html
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="header.jsp"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<div class="clearfix text-right">
<%--隐藏域--%>
<input type="hidden" id="pageNum" name="pageNum" value="${mq.pageNum}">
<input type="hidden" id="totalPage" value="${page.totalPage}">
<ul class="pagination no-margin">
<li id="prev" class="disabled"><a href="#">Prev</a></li>
<c:forEach begin="1" end="${page.totalPage}" var="myPageNum">
<li <c:if test="${myPageNum == mq.pageNum}">class="active"</c:if>><a
pageNumButton href="#">${myPageNum}</a></li>
</c:forEach>
<li id="next"><a href="#">Next</a></li>
</ul>
</div>
</body>
</html>
8.1.8 搜索查询
搜索条件注意通过form要包含搜索条件和翻页的pageNum,通过js来提交搜索的表单,切记提交前要把pageNum置成1
javascript
// 搜索按钮:重置页码为1
$("#search").click(function () {
$("#pageNum").val(1);
$("#txForm").submit();
})
8.1.9 分页条的 disabled 设置(包括翻页)

javascript
/**
* 分页逻辑
*/
var pageNum = $("#pageNum").val();
var totalPage = $("#totalPage").val();
pageNum = parseInt(pageNum);
totalPage = parseInt(totalPage);
// 禁用状态判断
if (pageNum == 1 && pageNum == totalPage) {
$("#prev").addClass("disabled");
$("#next").addClass("disabled");
}
if (pageNum == 1 && pageNum < totalPage) {
$("#prev").addClass("disabled");
$("#next").removeClass("disabled");
}
if (pageNum > 1 && pageNum < totalPage) {
$("#prev").removeClass("disabled");
$("#next").removeClass("disabled");
}
if (pageNum > 1 && pageNum == totalPage) {
$("#prev").removeClass("disabled");
$("#next").addClass("disabled");
}
// 上一页
$("#prev").click(function () {
if ($(this).hasClass("disabled")) return; // 新增:阻止禁用状态点击
$("#pageNum").val(--pageNum);
$("#txForm").submit();
})
// 下一页(pageNo → pageNum)
$("#next").click(function () {
if ($(this).hasClass("disabled")) return; // 新增:阻止禁用状态点击
$("#pageNum").val(++pageNum);
$("#txForm").submit();
})
// 页码按钮
$("a[pageNumButton]").click(function () {
var pageNum = $(this).html();
$("#pageNum").val(pageNum);
$("#txForm").submit();
})
8.2 流派的添加
流派添加使用layui的弹出层,预先定义好对应的div
html
<div id="mtypePop" style="margin-right: 50px;margin-top: 50px; display: none">
<form id="addMtypeForm" class="layui-form" method="post" action="/type/addMtype" lay-filter="example">
<div class="layui-form-item" >
<label class="layui-form-label">输入框</label>
<div class="layui-input-block">
<input type="text" name="tname" style="color: black;" lay-verify="title" autocomplete="off" placeholder="请输入流派名" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">文本域</label>
<div class="layui-input-block">
<textarea style="color: black;" placeholder="请输入流派描述" class="layui-textarea" name="tdesc"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal layui-btn-radius" lay-submit="" lay-filter="demo1">添加流派</button>
</div>
</div>
</form>
</div>
通过js点击添加流派弹出
javascript
var pop;
$("#addSong").click(function () {
pop = layer.open({
type: 1,
area:[600, 350],
content: $('#mtypePop')
});
})
提交表单
javascript
layui.use('form', function(){
var form = layui.form;
//监听提交
form.on('submit(demo1)', function(data){
//layer.msg(JSON.stringify(data.field));
$.ajax({
url:"/mtype/addMtype",
type:"post",
data:data.field,
dataType:"text",
success:function (text) {
if(text == "success"){
layer.msg("添加成功");
layer.close(pop);
$("#txForm").submit(); // 添加成功后刷新列表
}
}
})
//阻止页面跳转 防止同步提交 使用ajax异步提交表现
return false;
});
});
后台流派添加的Controller
java
@ResponseBody
@PostMapping("/addMtype")
public String addMtype(Mtype mt){
mtypeService.insert(mt);
return "success";
}
8.3 流派的修改
弹出层的div
html
<div id="mtypePop1" style="margin-right: 50px;margin-top: 50px; display: none">
<form id="updateMtypeForm" class="layui-form" method="post" action="/type/updateMtype" lay-filter="example">
<input type="hidden" name="tid" id="tid">
<div class="layui-form-item" >
<label class="layui-form-label">输入框</label>
<div class="layui-input-block">
<input id="ptname" type="text" name="tname" style="color: black;" lay-verify="title" autocomplete="off" placeholder="请输入流派名" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">文本域</label>
<div class="layui-input-block">
<textarea id="ptdesc" style="color: black;" placeholder="请输入流派描述" class="layui-textarea" name="tdesc"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal layui-btn-radius" lay-submit="" lay-filter="demo2">修改流派</button>
</div>
</div>
</form>
</div>
修改弹出层
javascript
var pop1;
$("[modify]").click(function () {
var tid = $(this).attr("tid");
$.ajax({
//原来数据回显
url:"/mtype/getMtype",
type:"post",
dataType:"json",
data:{"tid":tid},
success:function (json) {
$("#tid").val(json.tid);
$("#ptname").val(json.tname);
$("#ptdesc").val(json.tdesc);
}
});
pop1 = layer.open({
type: 1,
area:[600, 350],
content: $('#mtypePop1')
});
})
流派修改的js
javascript
layui.use('form', function(){
var form = layui.form
form.on("submit(demo2)",function (data) {
$("#updateMtypeForm").ajaxSubmit({
dataType:"text",
success:function (text) {
if(text == "success"){
layer.msg("修改成功");
layer.close(pop1);
//location.reload();
$("#mtFrom").submit();
}
}
})
return false;
})
});
后台流派修改的Controller
java
/**
* 流派修改回显数据方法
* @return
*/
@ResponseBody
@PostMapping("/getMtype")
public Mtype getMtype(int tid){
Mtype mtype = mtypeService.selectByPrimaryKey(tid);
return mtype;
}
/**
* 流派修改方法
* @param
* @return
*/
@ResponseBody
@PostMapping("/updateMtype")
public String updateMtype(Mtype mtype){
int i = mtypeService.updateByPrimaryKey(mtype);
return "success";
}
8.4 流派的删除
流派删除的js
javascript
$(".btn-warning").click(function () {
var tid = $(this).attr("tid");
layer.confirm('是否删除该流派?', {icon: 3, title:'提示'}, function(index){
//layer.close(index);
$.ajax({
url:"/mtype/delMtype",
dataType:"text",
type:"post",
data:{"tid":tid},
success: function (text) {
if (text == "success") {
layer.msg("删除成功");
layer.close(index);
//将分页条件查询的页码值归1
$("#pageNum").val(1);
$("#txForm").submit();
}
}
})
});
})
后台流派删除的Controller
javascript
@ResponseBody
@PostMapping("/delMtype")
public String delMtype(Integer tid){
mtypeService.deleteByPrimaryKey(tid);
return "success";
}
9. 数据库模型的解读

一对多的关系:从流派这一端来关注
多对一的关系:从歌手端关注
箭头指向的端是1的一段,背向箭头是多的一端
