Spring MVC+mybatis项目入门:旅游网(四)用户注册——mybatis的配置与使用以及Spring MVC重定向

个人博客:Spring MVC+mybatis项目入门:旅游网(四)用户注册2-持久化 | iwts's blog

先看这个!

这是18年的文章,回收站里恢复的,现阶段看基本是没有参考意义的,技术老旧脱离时代(2024年辣铁铁)

如果你在找相关的内容,建议先自我反省一下为什么会搜这么old school的关键词,其次请直接上b站搜索Spricing boo+培训班,看最新的项目相关视频

mybatis

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJO映射成数据库中的记录。

以上来自百度百科。其实不用mybatis的话,JDBC也能完成注册的操作。其实很简单,就是先找数据库是否有重复,如果没有重复将数据写入数据库即可。

mybatis进行数据库操作的过程

        每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为中心的。看名字可以猜出来设计模式是工厂方法,所以该实例应该是由工厂产生。也就是类SqlSessionFactoryBuilder。

        SqlSessionFactoryBuilder则是从配置文件中配置的Configuration的实例构造出来的。借此配置文件,接下来就能获得SqlSessionFactory对象。

        mybatis将所有的SQL语句全部转化成为了映射,这些映射在专门的xml配置文件中写。而对于每个POJO类,其下总共有两个文件:一个Java接口,一个xml文件。而我们将insert、delete等方法写进接口里面,声明形参列表、返回值等等。而xml文件就配置对于接口中的方法,映射了什么SQL语句对数据库进行操作。

        sqlSession利用SqlSessionFactory对象来获得,这个对象可以获得上面的具体映射mapper对象,而这个对象可以调用接口中的方法,从而间接使用SQL语句对数据库进行操作。大概有图:

实际上就是:写一个mapper接口,里面的方法对应了mapper配置文件里面的具体SQL语句。利用SqlSession可以生成mapper对象,然后直接调用mapper对象的方法就可以了。

        mybatis的具体原理不太入门,推荐去看官方文档,有中文版:

mybatis – MyBatis 3 | 简介

mybatis基本配置——基于MySQL

        一般,我们在src文件夹下创建mybatis的配置文件,可以起名为mybatis-config.xml。其内容基本上是固定的:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><!-- 声明什么方式连接,MySQL就是JDBC了 --><transactionManager type="JDBC"/><!-- JDBC配置 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><!-- ?后面是声明了编码等,防止数据库内容乱码 --><property name="url" value="jdbc:mysql://localhost:3306/你的数据库名?useUnicode=true&amp;characterEncoding=UTF-8"/><property name="username" value="数据库账号"/><property name="password" value="密码"/></dataSource></environment></environments><!-- 映射包位置 --><mappers><package name="me.iwts.mapper"/></mappers>
</configuration>

里面已经写了一部分注释了,所以说,最好对JDBC有一点认识。其实比较重要的就是加载JDBC驱动这个问题。根据本地数据库的不同,JDBC的驱动也是不一样的。这个需要视具体的MySQL版本而定。如果你的是5.7,那么直接用我的驱动就可以了,否则就需要去专门再找合适的,并且装载到项目里面。

        <mappers>标签,声明了利用这个配置文件,那些mapper的接口以及配置文件可以被找到。直接添加所在包就可以了,当然要是一个类一个类添加也行。

        值得注意的是,mybatis并不需要在web.xml或者dispatcher.xml里面进行声明或者配置,他们之间是独立的。

mapper接口与xml配置文件

        上面也说道,接口里面声明方法,而配置文件里面写具体的SQL语句。这里还是提醒一点:这部分内容非常庞大,推荐去看官方文档或者搜专门的教程。但是我的这篇博文只能说应该这样来写,而深层次的原理是没有的,只能说篇幅有限吧。

        首先,接口部分一定要写好,先看一下UserMapper.java文件的代码:

package me.iwts.mapper;import me.iwts.bean.User;
import java.util.List;public interface UserMapper {int insertUser(User user) throws Exception;int updateUser(User user) throws Exception;int deleteUser(User user) throws Exception;User selectUserByAccount(String account) throws Exception;List<User> selectAllUser() throws Exception;
}

可以看到,例如insert、update等操作,是需要数据的,而我们直接传输了User类。mybatis可以利用getter方法直接获得需要修改的值,而具体应该对应数据库表中的哪一项这个由配置文件决定。而返回值是int。其实这里就算没有返回值了——这个int类型的变量是我们确定是否正常运行的,实际上很少需要这个返回值。而后面的select方法就不同了,很明显我们需要获得数据库中的内容。在使用JDBC的时候,我们是利用select后获得了一个结果集,而mybatis却允许你直接获得对象,自然,是使用setter方法进行依赖注入。而具体数据库表中的哪个字段对应User类中的哪个属性,也是在配置文件里面完成的。

        上面挖了坑啊,下面就先看一下配置文件UserMapper.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.iwts.mapper.UserMapper"><resultMap id="userResultMap" type="me.iwts.bean.User"><id property="account" column="account" javaType="String" /><result property="passwd" column="passwd" javaType="String" /><result property="phone" column="phone" javaType="String" /><result property="email" column="email" javaType="String" /><result property="userName" column="userName" javaType="String" /></resultMap><insert id="insertUser" useGeneratedKeys="true" keyProperty="account">insert into user (account,passwd,phone,email,userName)values (#{account},#{passwd},#{phone},#{email},#{userName})</insert><update id="updateUser" >update userset passwd=#{passwd},phone=#{phone},email=#{email},userName=#{userName}where account=#{account}</update><delete id="deleteUser" parameterType="String">delete from user where account=#{account}</delete><select id="selectUserByAccount" resultMap="userResultMap">select *from userwhere account=#{account}</select><select id="selectAllUser" resultMap="userResultMap">select *from user</select>
</mapper>

可以看到,namespace完成了配置文件与接口的映射关系——其实这里博主也没有深入了解,因为网上是有说法的:接口的名字与xml文件的名字必须相同。而实际上博主第一次确实被这个坑了一下,因为时间久远也忘记了当时这个namespace具体是怎么写的。所以这就有一个问题——到底是相同的名字关联了两者,还是这个namespace。这里博主对mybatis理解不深,就不瞎说了。以后懂了随缘补在这里吧。

        首先,最上面的<resultMap>标签,标准的结果集了。而这也是上面说的:利用结果集设定了表中字段与POJO类的属性的对应关系。里面,<result>标签就是一个字段的映射。而<id>标签在一个结果集中只有一个,这个是mybatis的优化,跟我们的操作无关——或者说不影响我们的使用。

        而下面的具体的标签,就对应了我们的具体操作。例如<insert>、<select>标签,是说明了我们需要什么样的操作。而标签体里面写的就是具体的SQL语句。id属性就与接口的方法名建立了映射关系,我们调用方法的时候就能找到具体的SQL语句。其他的属性,其实入门没必要太理解,本质上是mybatis的优化,可以提高效率,但是写不写对我们的结果是没有影响的。但是对于select操作,一定要声明结果集的,这个结果集就是上面声明的<resultMap>,将id对应写上即可。

        可以看到SQL语句里面有很多类似EL表达式的写法。这个是占位符的意思,意思我们这里需要某属性,而这个属性是外部数据。这个数据其实就是接口中声明的传入的形参。

获得mapper对象

        其实这里是博主的问题:我的写法其实不太好,这也是后来才发现的——有同学写的时候将获取mapper对象的操作写进配置文件里面,这个其实跟Spring框架有点关系,大概就是在容器启动的时候就按照一定方式找到一定的配置文件来生成mapper,然后就可以在代码中调用了。但是因为到项目几乎完成,博主才发现这个操作,所以也没有改动。只能说这样写了不少重复的代码。如果不想按照博主这样写可以直接去网上找一下其他写法。

        博主是将这些写进了static静态块里面,这样,因为一个controller一般是对一个POJO进行各种操作的,所以在加载这个controller类的时候就能直接加载到JVM里面了,这样,很多controller方法都能使用这一个mapper。不过还是跟上面一样:虽然化简了重复代码,但是不能杜绝重复代码。可以看下这段代码:

package me.iwts.controller;import me.iwts.bean.User;
import me.iwts.mapper.UserMapper;
import me.iwts.tools.ViewTool;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.Reader;@Controller
public class UserController {public static SqlSessionFactory sessionFactory;public static SqlSession sqlSession;public static UserMapper mapper;// mybatis初始化static{try {Reader reader = Resources.getResourceAsReader("mybatis-config.xml");sessionFactory = new SqlSessionFactoryBuilder().build(reader);} catch (Exception e) {e.printStackTrace();}sqlSession =  sessionFactory.openSession();mapper = sqlSession.getMapper(UserMapper.class);}
}

这段代码其实没什么好说的,算是标准写法了,最终生成mapper对象。值得注意的是这些部分:

1.Reader加载mybatis配置文件。博主最早就说了,将xml文件直接扔在src下,如果写在其他地方,请注意相对路径写正确。

2.mapper获取流程。上面也说过了,利用SqlSession可以获得mapper,但是这个需要加载一个具体的mapper接口。可以看到也是利用了Java的反射,所以每个mapper只能处理一个mapper映射,这里的类名在切换的时候也要修改。

注册逻辑

        终于到了具体注册逻辑的写法了。逻辑很简单,查数据库看是否重复——重复显示“该用户已注册”,否则注册成功并写入数据库。也可以看到,mapper在获取以后,调用具体的数据库操作就是一行代码的事了,很方便。代码:

package me.iwts.controller;import me.iwts.bean.User;
import me.iwts.mapper.UserMapper;
import me.iwts.tools.ViewTool;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.Reader;@Controller
public class UserController {public static SqlSessionFactory sessionFactory;public static SqlSession sqlSession;public static UserMapper mapper;// 查找数据库是否已经有该用户,并返回public static User selectUserByAccount(String account){try{User ret = mapper.selectUserByAccount(account);return ret;}catch (Exception e){e.printStackTrace();}return null;}// mybatis初始化static{try {Reader reader = Resources.getResourceAsReader("mybatis-config.xml");sessionFactory = new SqlSessionFactoryBuilder().build(reader);} catch (Exception e) {e.printStackTrace();}sqlSession =  sessionFactory.openSession();mapper = sqlSession.getMapper(UserMapper.class);}// 注册@RequestMapping("register.action")public ModelAndView register(@Valid @ModelAttribute User user, BindingResult bindingResult, Model model){if(bindingResult.hasErrors()){model.addAttribute("user",user);return new ModelAndView(ViewTool.REGISTER);}// 验证是否已存在用户User selectUser = selectUserByAccount(user.getAccount());// 注册成功或者返回重新输入账号if(selectUser == null){// 处理一下beanif(user.getUserName() == null || user.getUserName().compareTo("") == 0){user.setUserName(user.getAccount());}try {mapper.insertUser(user);sqlSession.commit();} catch (Exception e) {e.printStackTrace();sqlSession.rollback();}// 注册转发,方式重复提交return new ModelAndView("redirect:/registerRedirect");}else{user.setAccount("");model.addAttribute("user",user);String accountError = "该账号已被注册";model.addAttribute("accountError",accountError);return new ModelAndView(ViewTool.REGISTER);}}// 注册重定向@RequestMapping("registerRedirect")public ModelAndView registerRedirect(){return new ModelAndView(ViewTool.REGISTER_SUCCESS);}
}

最上面的静态方法,是博主实写的时候,发现大量操作这个查找,所以就单独写出来了。因为User的controller类里面不仅仅有注册的代码还有其他很多,而对查找用的非常多。

        具体的数据库操作,实际上就是mapper对象直接调用接口的方法就完成了。注意自己定义的形参列表与返回值就行了。代码中实现了先查找是否已经存在该用户。如果存在,就对model里面返回一个新的数据,这里显示“该账号已被注册”,同时返回到注册页面。这里算是多了一行提示吧,那么在JSP你想要显示这个错误信息的地方利用EL语句显示即可:

<span>${accountError}</span>

这样就ok。

        如果没有问题,就是可以注册了,具体逻辑直接写就行了。因为用户昵称是不强制要求的。所以,如果用户没有写这个昵称,就默认跟用户名一致。这里判定一下即可。然后直接insert插入。这里可以看到与select的不同。这里其实是默认使用了事务处理,即只有调用commit()方法,才能正常操作,而如果有问题,可以rollback()回滚。这里其实没有太大意义,因为我们并没有写线程安全这部分内容,但是代码还得写,否则mybatis是不会调用SQL语句的。

重定向

        重定向与转发的区别就不多说了,基础内容了,不懂的直接百度吧。这里为什么单独写了重定向?可以看到return的时候不是返回一个视图,而是写成redirec:/后面跟了另一个请求。

        这里大家可以试一下:如果不写重定向,直接返回到注册成功页面。然后按一下刷新,页面应该会出现提示框:是否重新提交表单。试想一下,如果这个是银行转账页面,我们转了1000块以后,跳转到了转账成功页面。然后用户点了一下刷新——是否重新提交表单,然后顺手点了个确定。本质上转账的controller又处理了一次,也就是说又转账了1000块钱。

        当然,我们这里就是重复注册了一次。这样是非常不安全的——银行的例子,用户绝对要去喷啊,转了1000块怎么少了2000。并且这里还不算用户误操作,而是你代码不够严谨。

        然后再运行重定向的代码,发现刷新就刷新了,什么提示都没有。因为重定向以后,表单的数据已经完全没有了,自然地址也发生了变化(详情参考重定向与转发的区别)。所以之前的表单与当前页面已经没有关系了。 

        重定向在Spring MVC中实现逻辑很简单,在需要返回视图的时候重定向到另外的请求,再其他方法里面再返回视图。等于说中间有一个跳板。具体的代码实现就像上面代码写的一样。

下一章链接

https://blog.csdn.net/iwts_24/article/details/84235411

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络协议——FTP(简介、搭建FTP服务端)

一、简介 1、什么是FTP&#xff1f; FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09; TCP/IP 协议组的协议之一。常用20&#xff08;数据&#xff09;、21&#xff08;命令&#xff09;端口作为通讯端口。&#xff08;22为SSH端口&#xff09;F…

BookStack VS HelpLook两款知识库软件的区别

现在很多企业都会进行知识管理&#xff0c;在这个过程中&#xff0c;选择一个合适的知识库软件是一个不可避免的问题。在众多知识库软件中&#xff0c;HelpLook和BookStack这两款软件备受企业瞩目。不知如何选择&#xff0c;今天LookLook同学就简单介绍一下这两款知识库的区别&…

微信公众号完成自动回复,自定义菜单

微信公众号完成自动回复&#xff0c;自定义菜单 首先要获取到微信公众号的开发者权限&#xff0c;这一步省略&#xff0c;可以自行百度 微信公众号对接自己的服务器 首先第一步需要有自己的服务器和固定的ip&#xff0c; 其中&#xff0c;80/443端口需要有其中一个&#xff0…

唯众云课堂:领航智慧教育,赋能职教未来,打造高效人才培养新平台

随着《中国智慧教育发展报告 2023》的发布&#xff0c;智慧教育被正式定义为数字教育发展的高级阶段。然而&#xff0c;各职院在智慧教育的发展道路上&#xff0c;往往面临着诸多挑战&#xff0c;如缺乏一体化教学平台、优质教学资源不足等。唯众凭借深厚的产业洞察与教育实践经…

【云原生】K8s管理工具--Kubectl详解(一)

一、陈述式管理 1.1、陈述式资源管理方法 kubernetes 集群管理集群资源的唯一入口是通过相应的方法调用 apiserver 的接口kubectl 是官方的 CLI 命令行工具&#xff0c;用于与 apiserver 进行通信&#xff0c;将用户在命令行输入的命令&#xff0c;组织并转化为apiserver 能识…

一文搞透常见的Python编码陷阱(上)(分析+案例)

一个认为一切根源都是“自己不够强”的INTJ 个人主页:用哲学编程-CSDN博客专栏:每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 一、别忘了冒号 1. if 语句 2. while 语句 3. for 语句 4. 函数定义 5. 类定义 6. try/except 语句 …

太速科技-基于FPGA Spartan6 的双路光纤PCIe采集卡(2路光纤卡)

基于FPGA Spartan6 的双路光纤PCIe采集卡(2路光纤卡) 1、板卡概述   板卡采用xilinx Spartan6系列芯片&#xff0c;支持 PCI Express Base Specification 1.1 x1。内含丰富的逻辑资源和存储单元&#xff0c;板卡FPGA外接双片32M*16bit DDR2缓存器&#xff0c;支持乒乓操作。…

Ubuntu执行命令出现乱码,菱形符号

1、问题描述 如题&#xff0c;Ubuntu执行命令出现乱码&#xff0c;菱形符号&#xff08;见下图&#xff09;&#xff1a; 2、解决办法 export LC_ALLC 再运行就好了

fpga系列 HDL 00 : 可编程逻辑器件原理

一次性可编程器件&#xff08;融保险丝实现&#xff09; 一次性可编程器件&#xff08;One-Time Programmable Device&#xff0c;简称 OTP&#xff09;是一种在制造后仅能编程一次的存储设备。OTP器件在编程后数据不可更改。这些器件在很多应用场景中具有独特的优势和用途。 …

Web组态可视化编辑器 快速绘制组态图

演示地址&#xff1a;by组态[web组态插件] 随着工业智能制造的发展&#xff0c;工业企业对设备可视化、远程运维的需求日趋强烈&#xff0c;传统的单机版组态软件已经不能满足越来越复杂的控制需求&#xff0c;那么实现Web组态可视化界面成为了主要的技术路径。 行业痛点 对于…

nvm安装教程及使用nvm管理多个node版本

文章目录 前言一、nvm 安装教程温馨提示macOS/LinuxWindows 二、安装 node 前言 工作中&#xff0c;你可能会遇到以下场景&#xff1a; 我想使用 pnpm 命令安装依赖&#xff0c;但是在使用 pnpm 命令时提示如下 $ pnpm -v ERROR: This version of pnpm requires at least No…

移动云主机ECS搭建Kubernetes集群:详细步骤与指南

目录 云主机 ECS&#xff1a;云计算的强大引擎什么是云主机ECS&#xff1f;为何选择云主机ECS&#xff1f; 使用移动云ECS进行Kubernetes集群搭建1. 环境准备2. 安装步骤2.1 在每一个节点上执行的操作2.1.1 系统准备2.1.2 安装Docker2.1.3 安装Kubernetes的安装组件 2.2 在Mast…

chrome浏览器驱动下载

跑自动化的时候&#xff0c;需要打开谷歌浏览器&#xff0c;这个时候提示浏览器驱动找不到咋办呢&#xff1f; 1、网上搜索找到了这篇文章&#xff1a;https://www.cnblogs.com/laoluoits/p/17710501.html&#xff1b;按照文章介绍&#xff0c; 首先找到&#xff1a;CNPM Bin…

捷报!恒瑞医药ADC创新药SHR-A1921卵巢癌适应症拟纳入突破性治疗品种公示

近日&#xff0c;恒瑞医药自主研发的TROP-2抗体偶联药物&#xff08;antibody-drug-conjugate, ADC&#xff09;注射用SHR-A1921用于治疗铂耐药复发上皮性卵巢癌、输卵管癌或原发性腹膜癌适应症被国家药品监督管理局药品审评中心拟纳入突破性治疗品种公示名单。今年3月&#xf…

内网安全--域渗透准备知识

目录 知识点&#xff1a; 0x01 0x02 0x03 系列点&#xff1a; Linux主机信息收集 windows主机信息收集 知识点&#xff1a; 0、域产生原因 1、内网域的区别 2、如何判断在域内 3、域内常见信息收集 4、域内自动化工具收集 -局域网&工作组&域环境区别 -域…

Linux如何在目录下灵活创建、浏览、删除百万个文件

文章目录 一、创建百万级小文件1、单核CPU情况2、多核CPU情况3、执行效率对比3.1、单核的顺序执行3.2、多核的并发执行 二、如何列出/浏览这些文件1、查看目录下文件的数量2、列出&#xff1f;3、ls -f&#xff08;关闭排序功能&#xff09;3.1、执行效率对比 4、通过重定向导入…

HiWoo Box边缘计算网关

​在数字化浪潮汹涌的今天&#xff0c;边缘计算网关成为了连接物理世界与数字世界的桥梁&#xff0c;其重要性日益凸显。HiWoo Box&#xff0c;作为一款功能强大的边缘计算网关&#xff0c;不仅具备了传统网关的基本功能&#xff0c;更在数据采集、处理、传输等方面展现出了卓越…

“二叉堆:不是,啊?”

目录 前言一、堆的概念及结构堆的性质&#xff1a;堆的结构:最大堆最小堆堆顶注意 二、堆的实现1.初始化堆2. 堆的插入什么是堆的向上调整算法&#xff1f; 3.堆的删除什么是堆的向下调整算法&#xff1f; 4.获取堆顶的数据5.获取堆的数据个数6.堆的判空7.堆的销毁 三、建堆的时…

一文了解安卓内存抖动

目录 目录一、什么是内存抖动&#xff1f;1.1 Android里的内存抖动1.2 如何直观查看这种现象1.3 内存抖动带来的风险 二、如何避免内存抖动 目录 一、什么是内存抖动&#xff1f; 在程序里&#xff0c;每创建一个对象&#xff0c;就会有一块内存分配给它&#xff0c;每分配一…

超详细的前后端实战项目(Spring系列加上vue3)前后端篇(四)(一步步实现+源码)

兄弟们&#xff0c;继昨天的代码之后&#xff0c;继续完成最后的用户模块开发&#xff0c; 昨天已经完成了关于用户的信息编辑页面这些&#xff0c;今天再完善一下&#xff0c; 从后端这边开始吧&#xff0c;做一个拦截器&#xff0c;对用户做身份校验&#xff0c; 拦截器 这…