一个SpringMVC的小项目

news2024/12/27 3:55:34

一个图书管理小项目:

在这里插入图片描述

定义对应的表结构,为了学习所以才添加大量的 SQL 规则,要记得针对货币的处理方案

create table if not exists tbl_books(
id bigint primary key auto_increment,
 book_name varchar(32) not null,
 book_price numeric(8,2) default 0,
 pub_date timestamp default current_timestamp
)engine=innodb default charset utf8;

使用 MyBatis 的反向引擎执行反向映射,生成对应的实体类、映射元文件和对应的映射接口

修改实体类的定义

@Data
public class Book implements Serializable{
    private Long id;
    private String bookName;
    private BigDecimal bookPrice;
    private Data pubData;
}

修改映射接口,首先添加一个通用的映射接口 BaseMapper

public interface BaseMapper <T extends Serializable, ID extends Serializable>{
    int deleteByPrimaryKey(ID id);
    int insertSelective(T row);
    T selectByPrimaryKey(ID id);
    int updateByPrimaryKeySelective(T row);
    //具体的分页查询考虑使用PogeHelper分页插件实现
    List<T> selectByExample(T row);
}

定义 BookMapper 接口,要求继承于 BaseMapper。一般在 BaseMapper 接口中定义通用方法,特殊需求的方法定义在对应的 Mapper 接口中,例如 BookMapper

public interface BookMapper extends BaseMapper<Book,Long>{
    
}

修改 mapper.xml 文件添加对应的 selectByExample 方法对应的 sql 语句

<select id = "selectByExample" parameterType="com.ma.entity.Book" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from tbl_books where 1 = 1;
    <if test = "bookName != null">
        and book_name like #{bookName,jdbcType = VARCHAP}
    </if>
    <if test = "bookPrice != null">
        and book_price like #{bookPrice,jdbcType = DECIMAL}
    </if>
    <if test = "pubData != null">
        and pub_data like #{pubData,jdbcType = DATE}
    </if>
    <if test = "id != null">
        and id like #{id,jdbcType = BIGINT}
    </if>
</select>

=Mybatis 持久层========

添加父容器,一般来说子容器中只包含 web 应用相关的配置,其它内容定义在父容器中

Resources/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns="http://www.springframework.org/schema/beans"
       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">
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" p:driverClassName="com.mysql.cj.jdbc.Driver"
          p:url="jdbc:mysql:///test?serverTimezone=UTC" p:username="root" p:password="10086" destroy-method="close"/>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="dataSource"
          p:mapperLocations="classpath:mapper/*.xml"/>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:sqlSessionFactoryBeanName="sqlSessionFactory"
          p:basePackage="com.yan.dao"/>

    <!-- 打开Spring的自动组件扫描,一般需要引入context名空间  -->
    <context:component-scan base-package="com.ma.biz"/>

    <!-- 启动声明式事务管理,一般需要引入tx名空间 -->
    <tx:annotation-driven/>  
<!-- 打开声明式事务管理的注解支持,在具体应用中一般是在业务类上添加注解针对事务进行说明 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>
</beans>

添加业务接口

public interface IBookServ{
    boolean create(Book book);
}

添加对应的业务实现,需要考虑事务的问题

@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
@Service
public class BookMapper bookMapper;
    @Autowired
    private BookMapper bookMapper;
    @Transactional(readOnly = false,propagation = Propagation.REQUIRED)
    public boolean create(Book book){
    //Spring提供的工具类,一般用于业务数据校验,如果不满足条件则会抛出运行时异常。
    //notnull是条件,book参数用于判断notnull,参数2是报错信息
    Assert.notNull(book,"参数不允许为空");
    Assert.notNull(book.getBookName(),"数据名称不允许为空");
    int res = bookMapper.insertSelective(book);
    return res > 0;
}

添加控制器

pom.xml 打包方式修改为 war

<groupId>com.ma</groupId>
<artifactId>demo0710</artifactId>
<version>1.0-SNAPSHDT</version>
<packaging>war</packaging>

添加文件夹 webapp 以及 WEB-INF

在这里插入图片描述

配置 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- spring整合web应用还需要一个ContextLoaderLister复杂初始化IoC容器,并存储在application对象种 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 配置前端控制器,通过前端控制器可以将SpringMVC框架添加到web应用 -->
    <servlet>
        <!-- 配置的前端控制器名称,对应的默认配置文件位于WEB-INF目录下,名称为yan-servlet.xml -->
        <servlet-name>ma</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <!-- 后缀为.do的请求将触发前端控制器的执行 -->
        <servlet-name>ma</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <!-- 涉及一个父子容器的问题,ContextLoaderListener负责初始化一个IoC容器,这个叫做父容器;
        DispatcherServlet还要初始化一个IoC容器,这个叫做子容器;
        一般情况下,子容器可以直接访问父容器种的所有受管bean,而父容器无法访问子容器 -->
</web-app>

添加子容器配置/WEB-INF/ma-servlet.xml,实际上这个配置文件名称可以不遵守,目前讲究惯例优于配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns="http://www.springframework.org/schema/beans"
       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名空间以简化SpringMVC相关的配置 -->
    <mvc:annotation-driven/>  <!--打开SpringMVC的注解开发支持  -->

    <!-- 使用自动组件扫描将控制器类添加到IoC容器 -->
    <context:component-scan base-package="com.yan.action"/>

    <!--配置视图解析器,不需要配置handlerMapping处理映射器和handlerAdapter处理适配器,因为有默认的两个组件配置 -->
    <!-- 视图解析器并不在编程中直接使用,所以没有id值;prefix为前缀配置,suffix为后缀配置,例如ModelAndView中视图的逻辑名称为abc,则对应的
    物理地址为 prefix+逻辑视图名+suffix,例如/WEB-INF/content/abc.jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/content/"
          p:suffix=".jsp" p:viewClass="org.springframework.web.servlet.view.JstlView"/>
</beans>

使用 get 请求访问/books/add 则自动打开输入数据的页面,点击提交按钮,则以 post 提交的方式插入数据

@Contraller
@RequestMapping("/books")
public class BookContraller{
    @GetMapping("/add")
    public String add(){
        return "add";
    }
}

根据视图解析规则定义对应的 jsp 页面,位置为/WEB-INF/content/add.jspSpringMVC 为了简化 jsp 页面的编写,引入了自定义标签库,另外还有 JSTL 标签库【java 标准标签库】

1、在 jsp 页面中需要使用标签库,必须先引入标签库

<%@ taglib prefix=“form” uri=“http://www.springframework.org/tags/form” %> 这是 SpringMVC 针对页面的 form 表单提交和报错的标签库

<%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %> 这是 SUN 提供的 JSTL 标准标签库的核心标签。需要在 pom.xml 中添加依赖,否则会报错

2、定义页面

要求打开<form:form>时必须有一个对应的值 bean 用于接受和显示默认的数据

@Contraller
@RequestMapping("/books")
public class BookContraller{
    @GetMapping("/add")
    public String add(Model model){
        Book book = new Book();
        model.addAttribute("book",book);
        return "add";
    }
}

对应页面

<form:form action="add" modelAttribute="book">
    <form:label path="bookName">书籍名称:</from:label>
    <form:input path="bookName"/>
</form:form>

注意:名称对应 book

<form:form action="add" modelAttribute="book">
    <table>
        <tr>
            <td><form:label path="bookName">书籍名称:</from:label></td>
            <td><form:input path="bookName"/></td>
        </tr>
        <tr>
            <td><form:label path="bookPrice">书籍价格:</from:label></td>
            <td><form:input path="bookPrice"/></td>
        </tr>
        <tr>
            <td><form:label path="pubDate">出版日期:</from:label></td>
            <td><form:input path="pubDate"/></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="保存"/>
            </td>
        </tr>
    </table>
</form:form>

下一步:应该定义方法用于接受用户输入的数据

@Autowired
private IBookServ bookService;
@PostMapping("add")
public String add(Book book){
    //接收数据
    //调用业务方法执行数据入库
    bookService.create(book);
    //重定向列显示页面
    return "redirect:show";
}
@RequestMapping("/show")
public String show(){
    System.out.println("show");
    return"show";
}
@InitBinder
public void initBinder(WedDataBinder binder){
    binder.registerCustomEditor(Data.class,new CustomDataEditor(
        new SimpleDateFormat("yyyy-mm-dd"),true));
}

显示所有图书信息,下一步在考虑分页

控制器调用业务获取所有图书信息,并存储在 request 中转向到页面显示

@RequestMapping ("/show")
public String show(Model model){
    List<Book> bookList=bookService.getAllBooks();
    model.addAttribute("bookList",bookList);
    return "show";
}

由于返回 String 类型的 show,则需要定义页面,从 requet 中获取 bookList 并使用 table 进行迭代显示

在这里插入图片描述

在/WEB-INF/content/show.jsp 页面执行数据显示

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix = "fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<table border="1">
    <thead>
        <tr><td>&nbsp;</td>
            <th>编号</th><th>书名</th><th>价格</th><th>出版日期</th><th>操作</th></tr>
    </thead>
    <tbody>
    <c:forEath item="${bookList}" var = "book">
        <tr>
            <td></td>
            <td>${book.id}</td>
            <td>${book.bookName}</td>
            <td>${book.bookPrice}</td>
            <td><fmt:formatDate value="${book.pubDate}" patten="yyyy年M月d日"/></td>
            <td>删除 编辑</td>
        </tr>
    </c:forEach>
    </tbody>
</table>
</body>
</html>

实际上针对客户段提交的数据需要在接受到数据后进行服务器端数据校验

在 SpringMVC 中可以使用注解的方式针对客户端提交数据进行校验

1、添加依赖 hibernate-validator

2、在 ma-servlet.xml 中配置校验器

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator">
    </property>  
</bean>

校验器注入到处理器适配器中

这段配置 validator 属性意思是:在调用 controller 方法之后,马上 让数据校验 bean 开始工作,对数据进行校验

<mvc:annotation-driven validator="validator">

3、在接受客户端提交数据的值 bean 中使用注解添加验证规则

4、在控制器方法中接收数据的值 bean 前添加注解@Validated,并紧邻一 Errors/BindingResult 类型参数,用于接收校验中的报错信息

提交数据后执行方法 前会自动进行数据校验,如果校验中有报错信息则自动填充到 errors 中,但是不会自动跳转页面,需要编码判断进行页面跳转

@PostMapping("/add")
public String add(@Validate Book book,Errors errors){
    //接收数据
    if(errors.hasError()){  //判断是否有报错信息
        return "add";//如果有报错信息则返回输入界面进行报错显示
    }
    //调用业务方法执行数据入库
    bookService.create(book);
    //重定向到显示界面
    return "redirect:show";
}

5、在页面上显示报错信息

<td><form:label path="bookName">书籍名称:</form:label></td>
<td><form:input path="bookName"/>
<form:errors path="bookName"/>
</td>

对应的报错显示

在这里插入图片描述

使用 InitBinder 来处理 Date 类型的参数

@RequestMapping("/date")
public String date(Date date){
    System.out.println(date);
    return "hello"; 
}
@InitBinder
public void initBinder(ServletRequestDataBinder binder){
    binder.registerCustomEditor(Date.class, new CustomDateEditor(new
SimpleDateFormat("yyyy-MM-dd"), true));
}

系统预定义的方法 registerCustomEditor 用于针对特定数据类型的格式转换,参数 1 为目标类型 String–>Date,参数 2 为 SpringMVC 提供的预定义转换器,其中的参数为日期类型的格式

自动匹配参数

@RequestMapping("/person")
public String toPerson(String name, double age){
    System.out.println(name+" "+age);
    return "hello";
}
//使用@RequestParam 注解指定参数的 name
@RequestMapping(value="/param")
public String testRequestParam(@RequestParam(value="id") Integer id, @RequestParam(value="name") String name){
    System.out.println(id+" "+name);
    return "/hello";
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/740980.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

专业的PDF文件压缩工具推荐,让你的PDF文件轻松压缩

​在参加专业的比赛时&#xff0c;就需要用到pdf文件&#xff0c;如果pdf文件过大操作和分享起来就特别不方便&#xff0c;其实可以使用专业的pdf文件压缩工具来处理。今天就分享一款pdf在线压缩工具&#xff0c;通过浏览器就可以快速完成pdf压缩&#xff08;https://www.yasuo…

SQL22 统计每个学校的答过题的用户的平均答题数

SELECT university,COUNT(qt.question_id)/COUNT(distinct(qt.device_id)) avg_answer_cnt FROM question_practice_detail qt LEFT JOIN user_profile ut ON qt.device_idut.device_id GROUP BY university

使用Word轻松实现PDF转Word

以前WPS还能通过每天打卡白嫖会员&#xff0c;最近不行了&#xff0c;害&#xff0c;羊毛没了 现在重新回归Word&#xff0c;利用Word就可以将PDF转化为Word 一、通过Word新建一个Word文档并打开 二、点击 文件 —> 打开 三、浏览&#xff0c;找到要转的PDF 四、点击确定&…

基础篇--初识STM32

初识STM32 STM32是什么 ST&#xff1a;意法半导体 M&#xff1a;MCU/MPU32:32位 ST累计推出了&#xff1a;5大类、18个系列、1000多个型号的Cortex内核微控制器 STM32芯片分类 ST中文社区网&#xff1a;https://www.stmcu.org.cn/ ST官网&#xff1a;https://www.st.com …

4.5Java EEMyBatis缓存机制

一、 一级缓存 MyBatis的一级缓存级别 MyBatis的一级缓存是SqlSession级别的缓存。如果同一个SqlSession对象多次执行完全相同的SQL语句时&#xff0c;在第一次执行完成后&#xff0c;MyBatis会将查询结果写入到一级缓存中&#xff0c;此后&#xff0c;如果程序没有执行插入、…

Mysql (insert,update操作)

1.创建表&#xff1a; 创建员工表employee&#xff0c;字段如下&#xff1a; id&#xff08;员工编号&#xff09;&#xff0c;name&#xff08;员工名字&#xff09;&#xff0c;gender&#xff08;员工性别&#xff09;&#xff0c;salary&#xff08;员工薪资&#xff09; …

脚踏Java知识点

对上节Java的基础语法续讲 三元运算符和if语句格式的区别 语法格式&#xff1a; 三元运算符的语法格式是&#xff1a;(condition) ? expression1 : expression2&#xff1b; if语句的语法格式是&#xff1a; if (condition) { // 执行 expression1 } else { // 执行 express…

Stage模型HarmonyOS服务卡片开发整体说明

服务卡片&#xff08;以下简称“卡片”&#xff09;是一种界面展示形式&#xff0c;可以将应用的重要信息或操作前置到卡片&#xff0c;以达到服务直达、减少体验层级的目的。卡片常用于嵌入到其他应用&#xff08;当前卡片使用方只支持系统应用&#xff0c;如桌面&#xff09;…

cyclo(Ser-Ser),23409-30-5,环(L-丝氨酰基-L-丝氨酰),具有明确的生物活性

​资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ 产品描述&#xff1a; cyclo(Ser-Ser)&#xff08;CAS号&#xff1a;23409-30-5&#xff09;&#xff0c;环二肽(2,5-哌嗪二酮)是Z小的环肽&#xff0c;许多天然环二肽化合物都具有明确的生物活性&#xff0c;例如作为抗…

什么是矢量数据库?

我们正处于人工智能革命之中。它颠覆了它所接触的任何行业&#xff0c;承诺了伟大的创新 – 但它也带来了新的挑战。对于涉及大型语言模型、生成式 AI 和语义搜索的应用程序&#xff0c;高效的数据处理变得比以往任何时候都更加重要。 所有这些新应用程序都依赖于向量嵌入&…

【Android Framework系列】5章 AMS启动流程

1 AMS简介 AMS&#xff08;Activity Manager Service&#xff09;是Android中最核心的服务&#xff0c;管理着四大组件的启动、切换、调度及应用进程的管理和调度等工作。AndroidQ将Activity移到了ActivityTaskManagerService中&#xff0c;但也和AMS相关联。 AMS通过使用一些…

3.元素的显示与隐藏

类似网站广告, 当我们点击关闭就不见了, 但是我们重新刷新页面, 会重新出现! 本质:让一个元素在页面中隐藏或者显示出来。 display显示隐藏&#xff0c;不保留原来的位置visibility 显示隐藏&#xff0c;保留原来的位置overflow 溢出显示隐藏&#xff0c;只对溢出的部分进行处…

1000道网络安全必备面试题合集,秋招金九银十必看!!!

前言 以下为网络安全各个方向涉及的面试题&#xff0c;星数越多代表问题出现的几率越大&#xff0c;祝各位都能找到满意的工作。 注&#xff1a;本套面试题&#xff0c;已整理成pdf文档&#xff0c;但内容还在持续更新中&#xff0c;因为无论如何都不可能覆盖所有的面试问题&a…

Ctfshow web入门 PHP特性篇 web89-web151 全

web入门 PHP特性篇的wp都一把梭哈在这里啦~ 有点多&#xff0c;师傅们可以收藏下来慢慢看&#xff0c;写的应该挺全面的叭… 有错误敬请斧正&#xff01; CTFshow PHP web89 看题目&#xff0c;有个flag.php文件。题目要求get有个num&#xff0c;是数字但是不包含0-9。 intv…

【opencv之cv::Mat数据深拷贝和浅拷贝探讨】

cv::Mat数据深拷贝和浅拷贝 cv::Mat 拷贝方法实验测试1.matA matSrc2.matB(matSrc)3.matC matSrc.clone()4.matSrc.copyTo(matD) 很多时候写程序除了一个强大的架构&#xff0c;细节也很重要&#xff0c;俗话说的话细节决定成败嘛&#xff0c;在使用cv::Mat做图片处理的时候发…

C#(五十六)之线程池

线程池&#xff1a; 线程池是一种多线程的形式&#xff0c;其中的任务被添加到队列中&#xff0c;并在创建线程时自动启动。 ThreadPool类&#xff1a;以下都是静态方法&#xff1a;&#xff08;不需要new的&#xff09; GetAvailableThreads剩余空闲线程数 GetMaxThreads最…

生成token的两种方式

方式一&#xff1a;自定义工具类 手动编写代码&#xff0c;写两个方法&#xff0c;一个生成&#xff0c;一个解析&#xff1b; 第一步&#xff1a;导入依赖 <!--JWT令牌依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjw…

i2c_tool的使用

文章目录 前言一、交叉编译i2c_tool二、板子上使用i2c_tool三、为什么不需要编写驱动也能够访问到对应设备四、命令行使用i2_tool操作AP3216模块五、使用i2c_tool代码操作IIC设备六、相关函数讲解1.open_i2c_dev2.int set_slave_addr 七、具体代码编写总结 前言 本篇文章将带大…

精益生产对制造业真的那么重要吗?

说到底是精益生产值不值得的问题。 重要且可得&#xff0c;那它就值得。国内的很多制造企业之所以对“精益生产”持怀疑甚至否度态度&#xff0c;大都经历过实施过程中的“水土不服”难题。抛砖引玉讲一下&#xff1a; 1、精益生产的最典型案例 1991年&#xff0c;在当时整个…

架构训练营笔记:可扩展设计

可扩展 复杂度模型 业务复杂度&#xff1a;业务固有的复杂度&#xff0c;主要体现为难以理解、难以扩展&#xff0c;例如业务数量多&#xff08;微信&#xff09;、业务流程长&#xff08;支付宝&#xff09;、业务之间关系复杂&#xff08;例如ERP&#xff09;。 质量复杂度…