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

news2024/12/23 10:18:43

个人博客: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 user
        set 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 user
        where 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){
            // 处理一下bean
            if(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.coloradmin.cn/o/1697455.html

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

相关文章

qt-C++笔记之QThread使用

qt-C笔记之QThread使用 ——2024-05-26 下午 code review! 参考博文&#xff1a; qt-C笔记之使用QtConcurrent异步地执行槽函数中的内容&#xff0c;使其不阻塞主界面 qt-C笔记之QThread使用 文章目录 qt-C笔记之QThread使用一:Qt中几种多线程方法1.1. 使用 QThread 和 Lambda…

【二叉树】LeetCode.144:二叉树的前序遍历(小细节把握)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;初阶初阶结构刷题 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 1.题目描述&#xff1a;​编辑 2.问题分析&#xff1a; &#x1f354;函数解读&#xff1a; …

功率电感的设计步骤

文章目录 1&#xff1a;高导磁气隙&#xff08;铁氧体&#xff09;1.1设计原理1.2 设计步骤 2 铁粉芯2.1&#xff1a;设计原理2.2&#xff1a;设计步骤 TI电感设计 学习视频原链接 截图 1 截图1 截图1 截图 2 截图2 截图2 1&#xff1a;高导磁气隙&#xff08;铁氧体&#…

solidworks画螺栓学习笔记

螺栓 单位mm 六边形 直径16mm 水平约束 拉伸 选择厚度6mm 拉伸切除 画相切圆 切除厚度6mm&#xff0c;反向切除 &#xff0c;拔模角度45 螺栓 直径9mm&#xff0c;长度30mm 倒角 直径1mm&#xff0c;角度45 异形孔向导 螺纹线 偏移打勾&#xff0c;距离为2mm&#…

【C语言】八进制、十六进制

前言 在我们日常生活中使用的数往往是十进制的&#xff0c;而当我们学习C语言后我们会接触到许多不同的进制并且时常需要去思考与使用这些不同的进制&#xff08;尤其是2的幂相关的进制&#xff0c;因为这种计数系统比十进制更接近于计算机的二进制系统&#xff09;&#xff0…

牛客网刷题 | BC100 直角三角形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

爽!AI手绘变插画,接单赚爆了!

我最近发现一款名叫Hyper-SD15-Scribble的AI项目&#xff0c;可以实现一键手绘变插画的功能&#xff0c;而且它搭载了字节出品的超快速生成图片的AI大模型Hyper-SD15&#xff0c;可以实现几乎实时生成图片&#xff0c;有了它&#xff0c;拿去接一些手绘商单分分钟出图&#xff…

java生产制造执行系统MES源码:系统环境:Java EE 8、Servlet 3.0、Apache Maven 3 2;

MES系统技术选型 系统环境&#xff1a;Java EE 8、Servlet 3.0、Apache Maven 3 2&#xff1b; 主框架&#xff1a;Spring Boot 2.2.x、Spring Framework 5.2.x、Spring Security 5.2.x 3 持久层&#xff1a;Apache MyBatis 3.5.x、Hibernate Validation 6.0.x、Alibaba Dru…

基于STM32实现智能气体检测报警系统

⬇帮大家整理了单片机的资料 包括stm32的项目合集【源码开发文档】 点击下方蓝字即可领取&#xff0c;感谢支持&#xff01;⬇ 点击领取更多嵌入式详细资料 问题讨论&#xff0c;stm32的资料领取可以私信&#xff01; 目录 引言环境准备智能气体检测报警系统基础代码示例&…

ZDH-智能营销-插件服务

目录 主题 项目源码 预览地址 安装包下载地址 插件服务 插件服务使用场景 插件服务日志 感谢支持 主题 本篇文章主要介绍ZDH-智能营销平台下的插件服务,包含插件的应用场景 项目源码 zdh_web: GitHub - zhaoyachao/zdh_web: 大数据采集,抽取平台 zdh_magic_mirror: …

AIGC002-LoRA让大模型微调更加轻盈方便!

AIGC002-LoRA让大模型微调更加轻盈方便&#xff01; 文章目录 0 论文工作1 论文方法2 效果 0 论文工作 这篇论文名为 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS&#xff0c;作者是 Edward Hu 等人。它提出了一种名为 低秩自适应 (Low-Rank Adaptation, LoRA) 的新方…

AI 谈“浔川AI翻译机”

在天工AI&#xff0c;天工AI在全网搜索“浔川AI翻译机”。 1 创作助手谈“浔川AI翻译机”&#xff1a; “浔川AI翻译机”是一个利用人工智能技术进行语言翻译的设备或应用程序。它可以将一种语言的文字或口语翻译成另一种语言&#xff0c;以实现不同语言之间的沟通和理解。浔…

网络布线与数制转换

信号与传输介质 信号概述 什么是信号 信息 人对现实世界事物存在方式或运动状态的某种认识 数据 用于描述事物的某些属性的具体量值 信号 信息传递的媒介 例如&#xff0c;描述某一件物体&#xff0c;它的长、宽、高、质地、颜色、气味等就是用以形容该物体的数据。通…

图书管理系统——Java版

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaSE 顺序表的学习&#xff0c;点我 目录 图书管理系统菜单 基本框架&#xff1a; 书&#xff1a; 书架&#xff1a; 用户&#xff…

web及网络基础图文详解

目录 1.1TCP/IP 协议族 1.2TCP/IP 的分层管理 1.3TCP/IP通信传输流 1.4 与 HTTP 关系密切的协议 : IP、TCP 和 DNS &#xff08;1&#xff09;负责传输的 IP协议&#xff08;网络层&#xff09; &#xff08;2&#xff09;确保可靠的 TCP协议&#xff08;传输层&#xff…

sklearn实现线性回归

sklearn实现线性回归 一、数据集介绍二、使用sklearn实现线性回归一、数据集介绍 本案例使用女性身高体重数据集,数据集如下图所示: 可以看到,数据集有15行2列。 二、使用sklearn实现线性回归 sklearn中的线性模型模块是linear_model。这里使用linear_model下的普通线性…

TiDB学习4:Placement Driver

目录 1. PD架构 2. 路由功能 2. TSO 2.1 TSO 概念 2.2 TSO分配过程 2.3 TSO时间窗口 3. 调度 3.1 信息收集 3.2 生成调度(operator) 3.3 执行调度 4. Label 与高可用 4.1 Label 的配置 5. 小结 1. PD架构 PD是整个TiDB的总控&#xff0c;相当于集群的大脑 PD集成了…

易备数据备份软件: 快速备份 MySQL\SQL Server\Oracle\泛微 OA 数据库

易备数据备份软件支持对 SQL Server、Oracle、MySQL、PostgreSQL、MariaDB、泛微 OA 等数据库进行快速备份&#xff0c;备份过程不会对任何服务造成中断。 使用一份授权&#xff0c;可以备份无限量的数据库&#xff0c;不管数据库服务器是否在本机、本地网络、或是远程网络。可…

用LabVIEW进行CAN通信开发流程

本文详细介绍了在LabVIEW中开发CAN&#xff08;Controller Area Network&#xff09;通信的流程&#xff0c;包括硬件配置、软件编程和调试步骤。重点讨论了开发过程中需要注意的问题&#xff0c;如节点配置、数据帧格式和错误处理等&#xff0c;为开发高效可靠的CAN通信应用提…