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.mzph.cn/pingmian/15859.shtml

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

相关文章

C#【进阶】特殊语法

特殊语法、值和引用类型 特殊语法 文章目录 特殊语法1、var隐式类型2、设置对象初始值3、设置集合初始值4、匿名类型5、可空类型6、空合并操作符7、内插字符串8、单句逻辑简略写法 值和引用类型1、判断值和引用类型2、语句块3、变量的生命周期4、结构体中的值和引用5、类中的值…

重学java 45.多线程 下 总结 定时器_Timer

人开始反向思考 —— 24.5.26 定时器_Timer 1.概述:定时器 2.构造: Timer() 3.方法: void schedule(TimerTask task, Date firstTime, long period) task:抽象类,是Runnable的实现类 firstTime:从什么时间开始执行 period:每隔多长时间执行一次…

fpga问题整理

1、quartus联合modelsim仿真 无波形 问题&#xff1a; modelsim仿真无波形&#xff0c;打开transcript可以看到警告。 警告&#xff1a; # ** Warning: (vlog-2083) f:/program files/altera/ 13.1/quartus/ eda/sim_lib/ altera_lnsim.sv(22728): Carriage return (0x0D) is…

MySQL之Schema与数据类型优化(五)

Schema与数据类型优化 特殊类型数据 某些类型的数据并不直接与内置类型一致。低于秒级精度的时间戳就是一个例子。另外一个例子是一个IPv4地址。人们经常使用VARCHAR(15)列存储IP地址。然而&#xff0c;它们实际上是32位无符号整数。不是字符串。用小数点将地址分成四段的表示…

用AI比赛助手降维打击数学建模,比赛过程详细介绍,这保研不就稳了吗

数学建模是个小众的赛道&#xff0c;可能很多大学生不知道&#xff0c;简单来说&#xff1a;他能薅学分、保研加分、毕业好找工作(简历上写一辈子)&#xff0c;尤其是基于GPT-4o模型&#xff0c;简直对他们是降维打击。 数学建模每年的比赛非常多&#xff0c;像国赛、美赛、深…

asrpro softspi SD卡读写

采样 50M 1M&#xff1b;采样时间足够长&#xff0c;采样频率1M 避免信息遗漏 引脚 cs pa2 mosi pa3 sck pa5 miso pa6 vcc ->5v gnd ->gnd ARDUINO SD库与移植&#xff08;原本是打算移值tw ch32v103的sd库的&#xff0c;但没有对比&#xff0c;只能选择arduino ; …

Java进阶学习笔记22——泛型方法、通配符和上下限

泛型方法&#xff1a; package cn.ensource.d11_generics_method;public class Test {public static void main(String[] args) {// 泛型方法String res test("Java");System.out.println(res);Dog dog1 test(new Dog());System.out.println(dog1);}// 泛型方法pub…

手机上制作证件照

最近由于需要给老姐弄一组证件照&#xff0c;找了一通手机上的软件&#xff0c;找到一款性价比较高的&#xff0c;详细流程记录下来。vx小程序上搜索"泰世茂证件照"&#xff0c;打开首页如下图所示∶ 单击"开始制作" &#xff0c;选择一个证件照类别&#…

Paddle 傅里叶变换基础及领域应用

Paddle 傅里叶变换基础及领域应用 1. 傅里叶变换基础 1.1 傅里叶变换简介 傅里叶变换是一种重要的信号处理技术&#xff0c;它可以将一个信号从时域转换到频域。在频域中&#xff0c;信号的频率特性更加明显&#xff0c;有利于分析和处理。傅里叶变换的基本思想是将一个信号…

基于Rsoft的Fullwave仿真模块进行双芯波导能量耦合与波分复用

Rsoft中的Fullwave仿真模块可以更精确的仿真微小结构&#xff0c;按照建立模型&#xff0c;设置参数&#xff0c;监测能量&#xff0c;优化结构的思路对其进行仿真。图1是在Fullwave模块中建立的双芯波导仿真模型。在模型中设置好折射率、光源、光路、监测器等便可以进行仿真。…

【竞技宝】英超:滕哈格命真硬!足总杯夺冠获欧联资格

足总杯决赛结束,曼联爆冷2比1击败联赛冠军曼城夺冠,滕哈格再一次用顶级理解带队拿到杯赛冠军。赛前曼彻斯特当地有媒体爆料,曼联管理层已经决定要在足总杯决赛之后解雇滕哈格,这个消息让不少球迷都很担心滕哈格的状态。但是荷兰主帅凭借强大的内心,带领球队击败了不可一世的曼城…

买房送户口!多城加入“抢人大战”

业内人士认为&#xff0c;近期&#xff0c;多地推出的购房落户政策已区别于此前的人才落户政策&#xff0c;更聚焦于住房消费&#xff0c;降低了落户门槛&#xff0c;体现了各地对导入人口的重视&#xff0c;有利于人才流动&#xff0c;推动新型城镇化建设。 千万人口城市“后…

echarts配置记录,一些已经废弃的写法

1、normal&#xff0c;4.0以后无需将样式写在normal中了 改前&#xff1a; 改后&#xff1a; DEPRECATED: normal hierarchy in labelLine has been removed since 4.0. All style properties are configured in labelLine directly now. 2、axisLabel中的文字样式无需使用te…

文本处理工具grep及sed

文章目录 一、grep文本处理工具二、sed文本处理工具基本用法sed脚本格式搜索替代 一、grep文本处理工具 选项含义-color对匹配到的文本着色显示-m 次数匹配到规定的次数后停止-v显示不被命令匹配到的行,即取反-i忽略字符大小写-n显示匹配的行号-c统计匹配的行数-o仅显示匹配到…

log4j2远程代码执行

漏洞复现 漏洞复现2 这个框架不是web框架了&#xff0c;不是服务器web网站框架了&#xff0c;是java日志框架&#xff0c;就是记录日志信息&#xff0c;每一个程序都有一个日志文件&#xff0c;这个就是java里面记录日志的一个框架&#xff0c;它存在的点也是日志框架那几个代…

我用LLaMA-Factory微调大模型来实现商品评论情感分析,准确率高达91.70%

大家好&#xff0c;我是程序锅。 最近在modelscope上闲逛的时候&#xff0c;在数据集板块发现有一个商品评论情感预测数据集。这个数据集源自一个比赛&#xff0c;它的目的是为了预测电商平台顾客的评论是好评还是差评。 数据示例如下所示&#xff08;其中0代表差评&#xff…

Go 和 Delphi 定义可变参数函数的对比

使用可变参数函数具有灵活性、重用性、简化调用等优点&#xff0c;各个语言有各自定义可变参数函数的方法&#xff0c;也有通用的处理方法&#xff0c;比如使用数组、定义参数结构体、使用泛型等。 这里总结记录一下 go、delphi 的常用的定义可变参数函数的方式&#xff01; 一…

基于图卷积网络的人体3D网格分割

深度学习在 2D 视觉识别任务上取得了巨大成功。十年前被认为极其困难的图像分类和分割等任务&#xff0c;现在可以通过具有类似人类性能的神经网络来解决。这一成功归功于卷积神经网络 (CNN)&#xff0c;它取代了手工制作的描述符。 NSDT工具推荐&#xff1a; Three.js AI纹理开…

1301-习题1-1高等数学

1. 求下列函数的自然定义域 自然定义域就是使函数有意义的定义域。 常见自然定义域&#xff1a; 开根号 x \sqrt x x ​&#xff1a; x ≥ 0 x \ge 0 x≥0自变量为分式的分母 1 x \frac{1}{x} x1​&#xff1a; x ≠ 0 x \ne 0 x0三角函数 tan ⁡ x cot ⁡ x \tan x \cot x …