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 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MiniMax 悄咪咪上线的这款 AI 产品,好用到爆炸!

大模型太卷了&#xff01;上周国外某款多模态大模型的出现&#xff0c;立刻掀起了 AI 领域对话式多模态交互的热潮。不管是文字、语音&#xff0c;还是图片&#xff0c;都能与你进行实时交互。随后&#xff0c;谷歌也推出了类似的 Astra。 然而&#xff0c;国外的交互式大模型…

线性回归模型之套索回归

概述 本案例是基于之前的岭回归的案例的。之前案例的完整代码如下&#xff1a; import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import Ridge, LinearRegression from sklearn.datasets import make_regression from sklearn.model_selectio…

2024年弘连网络FIC大会竞赛题线下决赛题

总结&#xff1a; FIC决赛的时候&#xff0c;很多小问题没发现&#xff0c;在pve平台做题确实很方便。 这套题目复盘完&#xff0c;服务器这块的知识确实收获了很多&#xff0c;对pve集群平台和网络拓扑也有了一定的认识&#xff0c;感谢各位大佬悉心指导。 接下来&#xff0…

【数据结构】哈希表的原理及其实现

文章目录 哈希表的概念哈希函数的设计常见的哈希函数 哈希冲突1. 闭散列代码实现 2. 开散列拉链法的优点 针对开散列哈希的扩展基于开散列拉链法封装哈希表MyHash.h 基于哈希表实现unordered_map类Myunordered_map.h 基于哈希表实现unordered_set类Myunordered_map.h 哈希表的概…

ROCm上运行Transformer

10.7. Transformer — 动手学深度学习 2.0.0 documentation (d2l.ai) 代码 import math import pandas as pd import torch from torch import nn from d2l import torch as d2l#save class PositionWiseFFN(nn.Module):"""基于位置的前馈网络""&qu…

解决Error: error:0308010C:digital envelope routines::unsupported的四种解决方案

问题描述&#xff1a; 报错&#xff1a;Error: error:0308010C:digital envelope routines::unsupported 报错原因&#xff1a; 主要是因为 nodeJs V17 版本发布了 OpenSSL3.0 对算法和秘钥大小增加了更为严格的限制&#xff0c;nodeJs v17 之前版本没影响&am…

Rust后台管理系统Salvo-admin源码编译

1.克隆salvo-admin后台管理系统源码: https://github.com/lyqgit/salvo-admin.git 2.编译 编译成功 3.创建mysql数据库与执行sql脚本 输入名称ry-vue 执行sql脚本 全部执行上面3个sql 修改数据库用户名与密码: 清理及重新编译 cargo clean cargo build 4.运行并测试 cargo…

基于JAVA GUI体育馆管理系统的会员功能

Java GUI即Java图形用户界面&#xff0c;是一种使用图形化元素&#xff08;如窗口、按钮、文本框等&#xff09;来构建用户界面的技术。它基于Java的Swing框架&#xff0c;可以用于创建各种复杂的用户界面&#xff0c;包括窗口、对话框、菜单、按钮、文本框、复选框、下拉列表等…

仅需一块 4GB 的 GPU ,就能运行开源大语言模型:Llama3 70B

最强的开源大语言模型 Llama3 已经发布一段时间了&#xff0c;一些盆友资源有限&#xff0c;私信询问是否可以使用 4GB 的 VRAM 在本地运行 Llama3 70B。 与 GPT-4 相比&#xff0c;Llama3 的性能如何&#xff1f;Llama3 使用了哪些关键的前沿技术使其变得如此强大&#xff1f…

Oracle 并行和 session 数量的

这也就是为什么我们指定parallel为4&#xff0c;而实际并行度为8的原因。 insert create index&#xff0c;发现并行数都是加倍的 Indexes seem always created with parallel degree 1 during import as seen from a sqlfile. The sql file shows content like: CREATE INDE…

SwiftUI中List的样式及使用详解(添加、移动、删除、自定义滑动)

SwiftUI中的List可是个好东西&#xff0c;它用于显示可滚动列表的视图容器&#xff0c;类似于UITableView。在List中可以显示静态或动态的数据&#xff0c;并支持垂直滚动。List是一个数据驱动的视图&#xff0c;当数据发生变化时&#xff0c;列表会自动更新。 针对List&#x…

windows 7 10 11快捷键到启动页面

1.快速打开用户启动文件夹 shell:startup 方式2&#xff1a;快速打开系统启动文件夹 shell:Common Startup shell:Common Startup

燃数科技前端25-40K*14薪一面超简单,下周二面啦

​​​​​​​ 文章末尾扫描二维码领取地址 一面 1、自我介绍 2、低代码如何设计的 3、react路由原理 4、react生命周期 5、什么是回调地狱&#xff0c;如何解决 6、jwt和session有什么区别 7、js文件相互引用有什么问题&#xff1f;如何解决 8、一个很大的json文件…

腾讯TDSQL-C灰度发布列存索引; Azure Copilot集成支持Azure上各种托管数据库;

重要更新 1. Copilot for Azure新增了对Azure SQL、 Azure Database for MySQL的支持([8] [14])。Copilot for Azure是微软云提供的基于大模型技术的助手工具&#xff0c;主要能力包括了&#xff1a;该大模型可以获得最新的文档&#xff0c;以及客户的Azure资源情况&#xff0c…

每日百万交易的支付系统,如何设置JVM堆内存大小?

每日百万交易的支付系统,如何设置JVM堆内存大小? 1、支付背景的引入2、支付的核心业务流程3、每日百万交易支付系统的压力在哪里?4、支付系统每秒钟需要处理多少笔支付单5、每个支付订单处理需要耗时多久6、每个支付订单大概需要多大的内存空间7、每秒发起的支付请求对内存的…

9.3 Go语言入门(变量声明和函数调用)

Go语言入门&#xff08;变量声明和函数调用&#xff09; 目录二、变量声明和函数调用1. 变量声明1.1 使用 var 关键字声明1.2 简短声明1.3 零值1.4 常量 2. 函数调用2.1 函数定义2.2 多个返回值2.3 命名返回值2.4 可变参数2.5 匿名函数和闭包 目录 Go 语言&#xff08;Golang&a…

按月爬取天气数据可视化展示

从天气网分析,可以查询每个月的天气情况,这里按照url规则,传入年月,获取数据,最后进行可视化展示,最终效果: 下面是获取过程: 第一步: import requestsdef get_weather(month):url = f"https://lishi.tianqi.com/nanning/{month}.html"response = reques…

最新:windows下安装pcl点云库

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

日志的介绍及简单实现

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 日志是什么&#xff1f; 为什么需要日志&#xff1f; 实现一个简单日志 时间戳 clock_gettime time & localtime 可变模板参数(使用C语言)&#xff0c;va_start & va_end & vsprintf 宏 __LINE__…

Anaconda+CUDA+CUDNN+Pycharm+Pytorch安装教程(第一节 Anconda安装)

1.选择和对应的anconda版本 官网地址&#xff1a;Index of / (anaconda.com) 下载地址&#xff1a;Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 2.安装流程 (1)下载安装包 (2)点击next &#xff08;3&#xff09;点击I agree &a…