目录
前言
一、项目模拟部署
1.创建一个Maven项目
2. 导入所需依赖
数据库连接池(jdbc.properties)
加载驱动(spring-mybatis.xml)
配置数据库信息
编辑
日志文件配置(log4j.xml)
Spring MVC与Mabatis集成配置
spring-context.xml
spring-mvc.xml
3. 生成所需的实体类
二、项目模拟编写
分页工具类(PageBean)
实体类(Movie)
数据访问方法编写文件(MovieMapper.xml)
MovieMapper.java
接口类及接口实现类代码
切片类(PageAspect)
分页助手类(PageTag)
分页标签配置(.tld)
子控制器类(MovieController)
jsp页面代码
list.jsp
edit.jsp
三、页面显示(功能测试)
数据显示
功能模拟
模糊查询
新增
删除
修改
注意事项
前言
在前面几期的有关SpringMVC的博客中,我们对SpringMVC的知识以及运用已经有所了解,但这是远远不够的,如果我们要想要灵活运用SpringMVC的知识到我们的开发中,那必须进行实际运用。今天这期博客给大家带来SpringMVC模拟实现增删改查页面版。
一、项目模拟部署
1.创建一个Maven项目
创建好Maven项目之后,我们可以实现创建好一些文件夹由于存放一些文件,用利于我们区分每个文件的作用,使我们的项目条理更加清晰。
2. 导入所需依赖
数据库连接池(jdbc.properties)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_ssm?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
注:数据库的连接路径根据自身的数据库而改变,其次是自己数据库连接的账号和密码要和自己电脑安装的数据库连接账号和密码一致,否则无法连接。
加载驱动(spring-mybatis.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--1. 注解式开发 -->
<!-- 注解驱动 -->
<context:annotation-config/>
<!-- 用注解方式注入bean,并指定查找范围:com.javaxl.ssm及子子孙孙包-->
<context:component-scan base-package="com.yx"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!--初始连接数-->
<property name="initialSize" value="10"/>
<!--最大活动连接数-->
<property name="maxTotal" value="100"/>
<!--最大空闲连接数-->
<property name="maxIdle" value="50"/>
<!--最小空闲连接数-->
<property name="minIdle" value="10"/>
<!--设置为-1时,如果没有可用连接,连接池会一直无限期等待,直到获取到连接为止。-->
<!--如果设置为N(毫秒),则连接池会等待N毫秒,等待不到,则抛出异常-->
<property name="maxWaitMillis" value="-1"/>
</bean>
<!--4. spring和MyBatis整合 -->
<!--1) 创建sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 自动扫描XxxMapping.xml文件,**任意路径 -->
<property name="mapperLocations" value="classpath*:com/yx/**/mapper/*.xml"/>
<!-- 指定别名 -->
<property name="typeAliasesPackage" value="com/yx/**/model"/>
<!--配置pagehelper插件-->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
helperDialect=mysql
</value>
</property>
</bean>
</array>
</property>
</bean>
<!--2) 自动扫描com/javaxl/ssm/**/mapper下的所有XxxMapper接口(其实就是DAO接口),并实现这些接口,-->
<!-- 即可直接在程序中使用dao接口,不用再获取sqlsession对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--basePackage 属性是映射器接口文件的包路径。-->
<!--你可以使用分号或逗号 作为分隔符设置多于一个的包路径-->
<property name="basePackage" value="com/yx/**/mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<aop:aspectj-autoproxy/>
</beans>
该文件是通过获取jdbc.properties文件中的数据库连接信息进行加载,从而加载数据库驱动,实现连接数据库做铺垫,为后续数据库数据传输及操作做准备工作。
配置数据库信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!-- 引入配置文件 -->
<properties resource="jdbc.properties"/>
<!--指定数据库jdbc驱动jar包的位置-->
<classPathEntry location="D:\\MYsoftware\\Maven\\mvn_repsoitory\\mysql\\mysql-connector-java\\5.1.44\\mysql-connector-java-5.1.44.jar"/>
<!-- 一个数据库一个context -->
<context id="infoGuardian">
<!-- 注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true"/><!-- 是否取消注释 -->
<property name="suppressDate" value="true"/> <!-- 是否生成注释代时间戳 -->
</commentGenerator>
<!-- jdbc连接 -->
<jdbcConnection driverClass="${jdbc.driver}"
connectionURL="${jdbc.url}" userId="${jdbc.username}" password="${jdbc.password}"/>
<!-- 类型转换 -->
<javaTypeResolver>
<!-- 是否使用bigDecimal, false可自动转化以下类型(Long, Integer, Short, etc.) -->
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 01 指定javaBean生成的位置 -->
<!-- targetPackage:指定生成的model生成所在的包名 -->
<!-- targetProject:指定在该项目下所在的路径 -->
<javaModelGenerator targetPackage="com.yx.model"
targetProject="src/main/java">
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="false"/>
<!-- 是否对model添加构造函数 -->
<property name="constructorBased" value="true"/>
<!-- 是否针对string类型的字段在set的时候进行trim调用 -->
<property name="trimStrings" value="false"/>
<!-- 建立的Model对象是否 不可改变 即生成的Model对象不会有 setter方法,只有构造方法 -->
<property name="immutable" value="false"/>
</javaModelGenerator>
<!-- 02 指定sql映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.yx.mapper"
targetProject="src/main/java">
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- 03 生成XxxMapper接口 -->
<!-- type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象 -->
<!-- type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象 -->
<!-- type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口 -->
<javaClientGenerator targetPackage="com.yx.mapper"
targetProject="src/main/java" type="XMLMAPPER">
<!-- 是否在当前路径下新加一层schema,false路径com.oop.eksp.user.model, true:com.oop.eksp.user.model.[schemaName] -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!-- 配置表信息 -->
<!-- schema即为数据库名 -->
<!-- tableName为对应的数据库表 -->
<!-- domainObjectName是要生成的实体类 -->
<!-- enable*ByExample是否生成 example类 -->
<!--<table schema="" tableName="t_book" domainObjectName="Book"-->
<!--enableCountByExample="false" enableDeleteByExample="false"-->
<!--enableSelectByExample="false" enableUpdateByExample="false">-->
<!--<!– 忽略列,不生成bean 字段 –>-->
<!--<!– <ignoreColumn column="FRED" /> –>-->
<!--<!– 指定列的java数据类型 –>-->
<!--<!– <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" /> –>-->
<!--</table>-->
+
<!-- 配置要生成的实体类对应的数据库表-->
<table schema="" tableName="t_mvc_movie" domainObjectName="Movie"
enableCountByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" enableUpdateByExample="false">
</table>
</context>
</generatorConfiguration>
该文件中的targetPackage和targetProject根据自身创建的项目的文件路径进行修改,其次是根据自己数据库中的表来配置自己所需要生成对应数据库中表的实体类。
(该文件是实现连接数据库,根据配置的信息从数据库中获取相应的信息生成对应的实体类)
日志文件配置(log4j.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!-- status : 指定log4j本身的打印日志的级别.ALL< Trace < DEBUG < INFO < WARN < ERROR
< FATAL < OFF。 monitorInterval : 用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s. -->
<Configuration status="WARN" monitorInterval="30">
<Properties>
<!-- 配置日志文件输出目录 ${sys:user.home} -->
<Property name="LOG_HOME">/root/workspace/lucenedemo/logs</Property>
<property name="ERROR_LOG_FILE_NAME">/root/workspace/lucenedemo/logs/error</property>
<property name="WARN_LOG_FILE_NAME">/root/workspace/lucenedemo/logs/warn</property>
<property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t-%L] %-5level %logger{36} - %msg%n</property>
</Properties>
<Appenders>
<!--这个输出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<!-- 控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="trace" onMatch="ACCEPT"
onMismatch="DENY" />
<!-- 输出日志的格式 -->
<!-- %d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间 %p : 日志输出格式 %c : logger的名称
%m : 日志内容,即 logger.info("message") %n : 换行符 %C : Java类名 %L : 日志输出所在行数 %M
: 日志输出所在方法名 hostName : 本地机器名 hostAddress : 本地ip地址 -->
<PatternLayout pattern="${PATTERN}" />
</Console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 -->
<!--append为TRUE表示消息增加到指定文件中,false表示消息覆盖指定的文件内容,默认值是true -->
<File name="log" fileName="logs/test.log" append="false">
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size, 则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
<RollingFile name="RollingFileInfo" fileName="${LOG_HOME}/info.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY" />
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<!-- 基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。 modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am...而不是7am. -->
<!-- 关键点在于 filePattern后的日期格式,以及TimeBasedTriggeringPolicy的interval, 日期格式精确到哪一位,interval也精确到哪一个单位 -->
<!-- log4j2的按天分日志文件 : info-%d{yyyy-MM-dd}-%i.log -->
<TimeBasedTriggeringPolicy interval="1"
modulate="true" />
<!-- SizeBasedTriggeringPolicy:Policies子节点, 基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小. -->
<!-- <SizeBasedTriggeringPolicy size="2 kB" /> -->
</Policies>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${WARN_LOG_FILE_NAME}/warn.log"
filePattern="${WARN_LOG_FILE_NAME}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT"
onMismatch="DENY" />
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="2 kB" />
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20" />
</RollingFile>
<RollingFile name="RollingFileError" fileName="${ERROR_LOG_FILE_NAME}/error.log"
filePattern="${ERROR_LOG_FILE_NAME}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd-HH-mm}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT"
onMismatch="DENY" />
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<!-- log4j2的按分钟 分日志文件 : warn-%d{yyyy-MM-dd-HH-mm}-%i.log -->
<TimeBasedTriggeringPolicy interval="1"
modulate="true" />
<!-- <SizeBasedTriggeringPolicy size="10 MB" /> -->
</Policies>
</RollingFile>
</Appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
<Loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息 -->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<!-- 第三方日志系统 -->
<logger name="org.springframework" level="ERROR" />
<logger name="org.hibernate" level="ERROR" />
<logger name="org.apache.struts2" level="ERROR" />
<logger name="com.opensymphony.xwork2" level="ERROR" />
<logger name="org.jboss" level="ERROR" />
<!-- 配置日志的根节点 -->
<root level="all">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileInfo" />
<appender-ref ref="RollingFileWarn" />
<appender-ref ref="RollingFileError" />
</root>
</Loggers>
</Configuration>
Spring MVC与Mabatis集成配置
spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<!-- spring框架与mybatis整合的配置文件加载到spring的上下文中-->
<import resource="classpath:spring-mybatis.xml"></import>
</beans>
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/context/spring-aop-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--1) 扫描com.zking.zf及子子孙孙包下的控制器(扫描范围过大,耗时)-->
<context:component-scan base-package="com.yx"/>
<!--2) 此标签默认注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter -->
<mvc:annotation-driven />
<!--3) 创建ViewResolver视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- viewClass需要在pom中引入两个包:standard.jar and jstl.jar -->
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--4) 单独处理图片、样式、js等资源 -->
<mvc:resources location="/static/" mapping="/static/**"/>
<aop:aspectj-autoproxy/>
</beans>
3. 生成所需的实体类
二、项目模拟编写
分页工具类(PageBean)
package com.yx.utils;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Map;
public class PageBean implements Serializable {
private static final long serialVersionUID = 2422581023658455731L;
//页码
private int page=1;
//每页显示记录数
private int rows=10;
//总记录数
private int total=0;
//是否分页
private boolean isPagination=true;
//上一次的请求路径
private String url;
//获取所有的请求参数
private Map<String,String[]> map;
public PageBean() {
super();
}
//设置请求参数
public void setRequest(HttpServletRequest req) {
String page=req.getParameter("page");
String rows=req.getParameter("rows");
String pagination=req.getParameter("pagination");
this.setPage(page);
this.setRows(rows);
this.setPagination(pagination);
this.url=req.getContextPath()+req.getServletPath();
this.map=req.getParameterMap();
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Map<String, String[]> getMap() {
return map;
}
public void setMap(Map<String, String[]> map) {
this.map = map;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public void setPage(String page) {
if(null!=page&&!"".equals(page.trim()))
this.page = Integer.parseInt(page);
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public void setRows(String rows) {
if(null!=rows&&!"".equals(rows.trim()))
this.rows = Integer.parseInt(rows);
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public void setTotal(String total) {
this.total = Integer.parseInt(total);
}
public boolean isPagination() {
return isPagination;
}
public void setPagination(boolean isPagination) {
this.isPagination = isPagination;
}
public void setPagination(String isPagination) {
if(null!=isPagination&&!"".equals(isPagination.trim()))
this.isPagination = Boolean.parseBoolean(isPagination);
}
/**
* 获取分页起始标记位置
* @return
*/
public int getStartIndex() {
//(当前页码-1)*显示记录数
return (this.getPage()-1)*this.rows;
}
/**
* 末页
* @return
*/
public int getMaxPage() {
int totalpage=this.total/this.rows;
if(this.total%this.rows!=0)
totalpage++;
return totalpage;
}
/**
* 下一页
* @return
*/
public int getNextPage() {
int nextPage=this.page+1;
if(this.page>=this.getMaxPage())
nextPage=this.getMaxPage();
return nextPage;
}
/**
* 上一页
* @return
*/
public int getPreivousPage() {
int previousPage=this.page-1;
if(previousPage<1)
previousPage=1;
return previousPage;
}
@Override
public String toString() {
return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", isPagination=" + isPagination
+ "]";
}
}
实体类(Movie)
package com.yx.model;
import lombok.ToString;
@ToString
public class Movie {
private Integer mid;
private String mname;
private Float price;
public Movie(Integer mid, String mname, Float price) {
this.mid = mid;
this.mname = mname;
this.price = price;
}
public Movie() {
super();
}
public Integer getMid() {
return mid;
}
public void setMid(Integer mid) {
this.mid = mid;
}
public String getMname() {
return mname;
}
public void setMname(String mname) {
this.mname = mname;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
}
数据访问方法编写文件(MovieMapper.xml)
<?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.yx.mapper.MovieMapper" >
<resultMap id="BaseResultMap" type="com.yx.model.Movie" >
<constructor >
<idArg column="mid" jdbcType="INTEGER" javaType="java.lang.Integer" />
<arg column="mname" jdbcType="VARCHAR" javaType="java.lang.String" />
<arg column="price" jdbcType="REAL" javaType="java.lang.Float" />
</constructor>
</resultMap>
<sql id="Base_Column_List" >
mid, mname, price
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from t_mvc_movie
where mid = #{mid,jdbcType=INTEGER}
</select>
<select id="ListPager" resultType="com.yx.model.Movie" parameterType="com.yx.model.Movie" >
select
<include refid="Base_Column_List" />
from t_mvc_movie
<where>
<if test="mname != null">
and mname like concat('%',#{mname},'%')
</if>
</where>
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from t_mvc_movie
where mid = #{mid,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.yx.model.Movie" >
insert into t_mvc_movie (mid, mname, price
)
values (#{mid,jdbcType=INTEGER}, #{mname,jdbcType=VARCHAR}, #{price,jdbcType=REAL}
)
</insert>
<insert id="insertSelective" parameterType="com.yx.model.Movie" >
insert into t_mvc_movie
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="mid != null" >
mid,
</if>
<if test="mname != null" >
mname,
</if>
<if test="price != null" >
price,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="mid != null" >
#{mid,jdbcType=INTEGER},
</if>
<if test="mname != null" >
#{mname,jdbcType=VARCHAR},
</if>
<if test="price != null" >
#{price,jdbcType=REAL},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.yx.model.Movie" >
update t_mvc_movie
<set >
<if test="mname != null" >
mname = #{mname,jdbcType=VARCHAR},
</if>
<if test="price != null" >
price = #{price,jdbcType=REAL},
</if>
</set>
where mid = #{mid,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.yx.model.Movie" >
update t_mvc_movie
set mname = #{mname,jdbcType=VARCHAR},
price = #{price,jdbcType=REAL}
where mid = #{mid,jdbcType=INTEGER}
</update>
</mapper>
MovieMapper.java
package com.yx.mapper;
import com.yx.model.Movie;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface MovieMapper {
int deleteByPrimaryKey(Integer mid);
int insert(Movie record);
int insertSelective(Movie record);
Movie selectByPrimaryKey(Integer mid);
int updateByPrimaryKeySelective(Movie record);
int updateByPrimaryKey(Movie record);
List<Movie> ListPager(Movie movie);
}
接口类及接口实现类代码
package com.yx.biz;
import com.yx.model.Movie;
import com.yx.utils.PageBean;
import java.util.List;
public interface MovieBiz {
int deleteByPrimaryKey(Integer mid);
int insert(Movie record);
int insertSelective(Movie record);
Movie selectByPrimaryKey(Integer mid);
int updateByPrimaryKeySelective(Movie record);
int updateByPrimaryKey(Movie record);
List<Movie> listPager(Movie movie, PageBean pageBean);
}
//=====================以上是接口类代码======================================
//=====================以下是接口实现类代码======================================
package com.yx.biz.Impl;
import com.yx.biz.MovieBiz;
import com.yx.mapper.MovieMapper;
import com.yx.model.Movie;
import com.yx.utils.PageBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author 君易--鑨
* @site www.yangxin.com
* @company 木易
* @create 2023-09-08 21:23
* 接口实现类
*/
@Service
public class MovieBizImpl implements MovieBiz {
@Autowired
private MovieMapper movieMapper;
@Override
public int deleteByPrimaryKey(Integer mid) {
return movieMapper.deleteByPrimaryKey(mid);
}
@Override
public int insert(Movie record) {
return movieMapper.insert(record);
}
@Override
public int insertSelective(Movie record) {
return movieMapper.insertSelective(record);
}
@Override
public Movie selectByPrimaryKey(Integer mid) {
return movieMapper.selectByPrimaryKey(mid);
}
@Override
public int updateByPrimaryKeySelective(Movie record) {
return movieMapper.updateByPrimaryKeySelective(record);
}
@Override
public int updateByPrimaryKey(Movie record) {
return movieMapper.updateByPrimaryKey(record);
}
@Override
public List<Movie> listPager(Movie movie, PageBean pageBean) {
return movieMapper.ListPager(movie);
}
}
切片类(PageAspect)
package com.yx.aspect;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yx.utils.PageBean;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author 君易--鑨
* @site www.yangxin.com
* @company 木易
* @create 2023-08-25 20:00
*/
@Aspect//代表当前类为切片类
@Component//代表当前类交给Spring进行管理 该注解是@Service的父类
public class PageAspect {
/*
* * *..*Biz.*Pager(..)
* *:代表返回值类型
* *..:任意包,不限层级
* *Service:以Service结尾的类/或者接口
* *Pager:以Pager结尾的方法
* 符合上述条件,即为目标类或者目标方法
*/
@Around("execution(* *..*Biz.*Pager(..))")
public Object invoke(ProceedingJoinPoint args) throws Throwable {
// 实例化分页工具类
PageBean pageBean=null;
// 获取所有参数
Object[] ags = args.getArgs();
// 遍历
for (Object param : ags) {
if (param instanceof PageBean){
pageBean= (PageBean) param;
break;//中断
}
}
if (pageBean !=null && pageBean.isPagination())
PageHelper.startPage(pageBean.getPage(),pageBean.getRows());
// 执行目标方法a
Object lst = args.proceed();
if (pageBean !=null && pageBean.isPagination()){
PageInfo info = new PageInfo((List) lst);
pageBean.setTotal((int) info.getTotal());
}
return lst;
}
}
分页助手类(PageTag)
package com.yx.Tag;
import com.yx.utils.PageBean;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class PageTag extends BodyTagSupport{
private PageBean pageBean;// 包含了所有分页相关的元素
public PageBean getPageBean() {
return pageBean;
}
public void setPageBean(PageBean pageBean) {
this.pageBean = pageBean;
}
@Override
public int doStartTag() throws JspException {
// 没有标签体,要输出内容
JspWriter out = pageContext.getOut();
try {
out.print(toHTML());
} catch (IOException e) {
e.printStackTrace();
}
return super.doStartTag();
}
private String toHTML() {
StringBuffer sb = new StringBuffer();
// 隐藏的form表单---这个就是上一次请求下次重新发的奥义所在
// 上一次请求的URL
sb.append("<form action='"+pageBean.getUrl()+"' id='pageBeanForm' method='post'>");
sb.append(" <input type='hidden' name='page'>");
// 上一次请求的参数
Map<String, String[]> paramMap = pageBean.getMap();
if(paramMap != null && paramMap.size() > 0) {
Set<Entry<String, String[]>> entrySet = paramMap.entrySet();
for (Entry<String, String[]> entry : entrySet) {
// 参数名
String key = entry.getKey();
// 参数值
for (String value : entry.getValue()) {
// 上一次请求的参数,再一次组装成了新的Form表单
// 注意:page参数每次都会提交,我们需要避免
if(!"page".equals(key)) {
sb.append(" <input type='hidden' name='"+key+"' value='"+value+"' >");
}
}
}
}
sb.append("</form>");
// 分页条
sb.append("<ul class='pagination justify-content-center'>");
sb.append(" <li class='page-item "+(pageBean.getPage() == 1 ? "disabled" : "")+"'><a class='page-link'");
sb.append(" href='javascript:gotoPage(1)'>首页</a></li>");
sb.append(" <li class='page-item "+(pageBean.getPage() == 1 ? "disabled" : "")+"'><a class='page-link'");
sb.append(" href='javascript:gotoPage("+pageBean.getPreivousPage()+")'><</a></li>");// less than 小于号
// sb.append(" <li class='page-item'><a class='page-link' href='#'>1</a></li>");
// sb.append(" <li class='page-item'><a class='page-link' href='#'>2</a></li>");
sb.append(" <li class='page-item active'><a class='page-link' href='#'>"+pageBean.getPage()+"</a></li>");
sb.append(" <li class='page-item "+(pageBean.getPage() == pageBean.getMaxPage() ? "disabled" : "")+"'><a class='page-link' href='javascript:gotoPage("+pageBean.getNextPage()+")'>></a></li>");
sb.append(" <li class='page-item "+(pageBean.getPage() == pageBean.getMaxPage() ? "disabled" : "")+"'><a class='page-link' href='javascript:gotoPage("+pageBean.getMaxPage()+")'>尾页</a></li>");
sb.append(" <li class='page-item go-input'><b>到第</b><input class='page-link'");
sb.append(" type='text' id='skipPage' name='' /><b>页</b></li>");
sb.append(" <li class='page-item go'><a class='page-link'");
sb.append(" href='javascript:skipPage()'>确定</a></li>");
sb.append(" <li class='page-item'><b>共"+pageBean.getTotal()+"条</b></li>");
sb.append("</ul>");
// 分页执行的JS代码
sb.append("<script type='text/javascript'>");
sb.append(" function gotoPage(page) {");
sb.append(" document.getElementById('pageBeanForm').page.value = page;");
sb.append(" document.getElementById('pageBeanForm').submit();");
sb.append(" }");
sb.append(" function skipPage() {");
sb.append(" var page = document.getElementById('skipPage').value;");
sb.append(" if (!page || isNaN(page) || parseInt(page) < 1 || parseInt(page) > "+pageBean.getMaxPage()+") {");
sb.append(" alert('请输入1~"+pageBean.getMaxPage()+"的数字');");
sb.append(" return;");
sb.append(" }");
sb.append(" gotoPage(page);");
sb.append(" }");
sb.append("</script>");
return sb.toString();
}
}
分页标签配置(.tld)
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>zking 1.1 core library</description>
<display-name>zking core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>zking</short-name>
<uri>http://jsp.veryedu.cn</uri>
<tag>
<name>page</name>
<tag-class>com.yx.Tag.PageTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>pageBean</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
子控制器类(MovieController)
package com.yx.web;
import com.yx.biz.MovieBiz;
import com.yx.model.Movie;
import com.yx.utils.PageBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* @author 君易--鑨
* @site www.yangxin.com
* @company 木易
* @create 2023-09-08 21:56
*/
@Controller
@RequestMapping("/movie")
public class MovieController {
@Autowired
private MovieBiz movieBiz;
// 增
@RequestMapping("/add")
public String add(Movie movie){
int i = movieBiz.insertSelective(movie);
return "redirect:list";
}
// 删
@RequestMapping("/del/{mid}")
public String del(@PathVariable("mid") Integer mid){
int i = movieBiz.deleteByPrimaryKey(mid);
return "redirect:list";
}
// 改
@RequestMapping("/edit")
public String del(Movie movie){
int i = movieBiz.updateByPrimaryKeySelective(movie);
return "redirect:list";
}
// 查
@GetMapping("/list")
public String list(Movie movie, HttpServletRequest request){
// movie接口参数
PageBean pageBean=new PageBean();
pageBean.setRequest(request);
List<Movie> movies = movieBiz.listPager(movie, pageBean);
request.setAttribute("lst",movies);
request.setAttribute("pageBean",pageBean);
//最终跳转到WEB-IEF/movie/list.jsp页面
return "movie/list";
}
// 数据回显
@RequestMapping("/preSave")
public String preSave(Movie movie, Model model){
if (movie !=null && movie.getMid() !=null && movie.getMid() != 0){
Movie m = movieBiz.selectByPrimaryKey(movie.getMid());
model.addAttribute("m",m);
}
return "movie/edit";
}
}
jsp页面代码
list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://jsp.veryedu.cn" prefix="z"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link
href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css"
rel="stylesheet">
<script
src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.js"></script>
<title>博客列表</title>
<style type="text/css">
.page-item input {
padding: 0;
width: 40px;
height: 100%;
text-align: center;
margin: 0 6px;
}
.page-item input, .page-item b {
line-height: 38px;
float: left;
font-weight: 400;
}
.page-item.go-input {
margin: 0 10px;
}
</style>
</head>
<body>
<form class="form-inline"
action="${pageContext.request.contextPath }/movie/list" method="post">
<div class="form-group mb-2">
<input type="text" class="form-control-plaintext" name="mname"
placeholder="请输入电影名称">
<!-- <input name="rows" value="20" type="hidden"> -->
<!-- 不想分页 -->
<%-- <input name="pagination" value="false" type="hidden">--%>
</div>
<button type="submit" class="btn btn-primary mb-2">查询</button>
<a class="btn btn-primary mb-2" href="${pageContext.request.contextPath }/movie/preSave">新增</a>
</form>
<table class="table table-striped ">
<thead>
<tr>
<th scope="col">电影ID</th>
<th scope="col">名称</th>
<th scope="col">价格</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
<c:forEach var="b" items="${lst }">
<tr>
<td>${b.mid }</td>
<td>${b.mname }</td>
<td>${b.price }</td>
<td>
<a href="${pageContext.request.contextPath }/movie/preSave?mid=${b.mid}">修改</a>
<a href="${pageContext.request.contextPath }/movie/del?mid=${b.mid}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
<!-- 这一行代码就相当于前面分页需求前端的几十行了 -->
<z:page pageBean="${pageBean }"></z:page>
</body>
</html>
edit.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>电影编辑界面</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/${empty m ? 'movie/add' : 'movie/edit'}" method="post">
电影编号:<input type="text" name="mid" value="${m.mid }"><br>
电影名称:<input type="text" name="mname" value="${m.mname }"><br>
电影价格:<input type="text" name="price" value="${m.price }"><br>
<input type="submit">
</form>
</body>
</html>
三、页面显示(功能测试)
数据显示
功能模拟
模糊查询
新增
删除
修改
注意事项
spring-mvc.xml中的<aop:aspectj-autoproxy/>如果被注释了则,运行项目的话则不会显示使用分页功能,因为直接Spring的过滤。注释后运行显示数据如下图所示。
好了。这就是这期给大家带来模拟事项CRUD增删改查功能实现与SpringMVC相结合的一期博客,后续还会继续更新,希望老铁能三连支持一波。