首页 > 其他分享 >狂神说mybatis笔记

狂神说mybatis笔记

时间:2022-10-31 14:11:27浏览次数:56  
标签:笔记 public sqlSession dupeng mybatis import 狂神 com id

1、简介

1.1什么是mybatis

image-20221002121424675

  • MyBatis 是一款优秀的持久层框架
  • 它支持自定义 SQL、存储过程以及高级映射
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录

1.2持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
  • 内存:断电即失
  • 数据库(Jdbc),io文件持久化。

1.3持久层

DAO层、Service层、Controller层

  • 完成持久化工作的代码
  • 层界限十分明显

1.4为什么需要mybatis

  • 帮助程序员将数据存入到数据库中
  • 方便
  • 传统的JDBC代码复杂,简化,框架,自动化
  • 不用mybatis也可以,技术没有高低之分
  • 优点
    • 简单易学
    • 灵活
    • sql和代码的分离,提高了可维护性
    • 提供映射标签,支持对象与数据库的orm字段关系映射
    • 提供对象关系映射标签,支持对象关系组建维护
    • 提供xml标签,支持编写动态sql
  • 使用mybatis人多

2、第一个mybatis程序

1.1搭建环境

思路:搭建环境-->导入mybatis-->编写代码-->测试

CREATE TABLE `user` (
  `id` int(10) NOT NULL,
  `name` varchar(50) DEFAULT NULL,
  `pwd` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

导入依赖

<!--    父工程-->
    <groupId>com.dupeng</groupId>
    <artifactId>mybatis</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>mybatis_01</module>
    </modules>


    <dependencies>

        <!--mysqlq驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <!--在build中配置文件resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

1.2创建一个模板

mybatis01

在resource文件夹下,创建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核心配置文件-->
<configuration>
   <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC&amp;userSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--    每一个mapper.xml都需要mybatis核心配置文件中注册-->
    <mappers>
        <mapper resource="com/dupeng/Dao/UserMapper.xml"/>
    </mappers>

</configuration>

编写工具类mybatisUtils

package com.dupeng.utils;

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 java.io.InputStream;


public class MybatisUtils {

    private  static SqlSessionFactory sqlSessionFactory;
static {
    try {
        //使用mybatis第一步,获取sqlsessionFactory对象
        String resources="mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resources);
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    }catch (Exception e){
        e.printStackTrace();
    }
}


//既然有SqlSessionFactory,我们可以从中获得SqlSession 实例
//    sqlsession提供在数据库执行sql命令所需的方法,可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
  public  static SqlSession getsqlSession(){
    return sqlSessionFactory.openSession();
  }

}

1.3编写代码

实体类

package com.dupeng.pojo;
//实体类

public class User {

    private int id;
    private String name;
    private  String pwd;

    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

Dao接口-->后更名为userMapper

package com.kuang.dao;

import com.kuang.pojo.User;

import java.util.List;

public interface UserDao { // 或者 UserMapper
    List<User> getUserList();
}

接口实现类UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.dupeng.Dao.UserMapper">

    <!--select查询语句-->
    <select id="getUserList" resultType="com.dupeng.pojo.User">
        select * from mybatis.user
    </select>



</mapper>

Junit测试

package com.dupeng.Dao;

import com.dupeng.pojo.User;
import com.dupeng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UserMapperTest {
//    查询全部用户
    @Test
    public  void test(){
        //1.获取sqlsession对象
        SqlSession sqlSession = MybatisUtils.getsqlSession();
//        2.执行SQL
        //方式一:getMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }

        //关闭sqlSession
        sqlSession.close();
    }
}

3、CURD以及map、模糊查询

编写接口

package com.dupeng.Dao;

import com.dupeng.pojo.User;

import java.util.List;
import java.util.Map;

public interface UserMapper {
//    模糊查询
   List<User> getUserLike(String name);

    //使用map传递参数,查询用户
    User getUserByIdmap(Map<String,Object> map);

    //    增加用户
    void  addUser(User user);

//   根据id删除用户
    int deleteUser(int id);

// 修改用户
    int updateUser(User user);

//    查询所有用户
    List<User> getUserList();


//    根据id查询用户
    User getUserById(int id);



}

接口实现类

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.dupeng.Dao.UserMapper">
<!--    模糊查询-->
<select id="getUserLike" parameterType="String" resultType="com.dupeng.pojo.User">
    select * from mybatis.user where name like "%"#{value}"%"
</select>

<!--    map查询-->
    <select id="getUserByIdmap" parameterType="map" resultType="com.dupeng.pojo.User">
        select * from mybatis.user where id = #{userId} and name =#{userName};
    </select>

    <!--    插入语句-->
    <insert id="addUser" parameterType="com.dupeng.pojo.User">
        insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>

<!--    删除语句-->
    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id= #{id};
    </delete>

<!--    修改语句-->
<update id="updateUser" parameterType="com.dupeng.pojo.User">
    update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
</update>
    <!--select查询语句-->
    <select id="getUserList" resultType="com.dupeng.pojo.User">
        select * from mybatis.user
    </select>

<!-- 根据id查询用户   -->
<select id="getUserById" resultType="com.dupeng.pojo.User" parameterType="int">

    select * from mybatis.user where  id= #{id}
</select>

</mapper>

测试

package com.dupeng.Dao;

import com.dupeng.pojo.User;
import com.dupeng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class UserMapperTest {
//    模糊查询
@Test
    public void getUserLike(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    List<User> userLike = mapper.getUserLike("%李%");

    for (User user:userLike){
        System.out.println(user);
    }
    sqlSession.close();
}


//    使用map传递参数
    @Test
    public void getUserByIdmap(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

       Map<String, Object> map = new HashMap<>();
        map.put("userId",1);
        map.put("userName","张三");
        User user = mapper.getUserByIdmap(map);
        System.out.println(user);
        sqlSession.close();
    }


    //增加用户
    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.addUser(new User(4,"海儿","12345655"));

        sqlSession.commit();
        sqlSession.close();

    }
//    删除用户
    @Test
    public  void  deleteUser(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.deleteUser(5);

        sqlSession.commit();
        sqlSession.close();

    }

//修改用户
    @Test
    public  void updateUser(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.updateUser(new User(4,"小海儿","12444456"));
        sqlSession.commit();
        sqlSession.close();

    }


//    查询全部用户
    @Test
    public  void test(){
        //1.获取sqlsession对象
        SqlSession sqlSession = MybatisUtils.getsqlSession();
//        2.执行SQL
        //方式一:getMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }

        //关闭sqlSession
        sqlSession.close();
    }


//根据id查询用户
@Test
    public void getUserById(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);
    sqlSession.close();
    
}



}

4、配置解析

1.核心配置文件

mybatis-config.xml

在resources文件下创建db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&userSSL=false
username=root
password=123456

在mybatis-config.xml中引入文件,以及更改信息

<properties resource="db.properties"/>

image-20221007121136487

两者进行更改

image-20221007121047432

2.类型别名

在核心配置文件mybatis-config.xml中

  • 排在第三
    <typeAliases>
<typeAlias type="com.dupeng.pojo.User" alias="User"/>
    </typeAliases>

对应的插入语句中

image-20221007122249185

3.映射器

    <!--    每一个mapper.xml都需要mybatis核心配置文件中注册-->
    <mappers>
        <!--        方式一-->
        <mapper resource="com/dupeng/Dao/UserMapper.xml"/>

        <!--     方式二   -->
<!--       <mapper class="com.dupeng.Dao.UserMapper"/>-->
        
		<!--        方式三-->
<!--        <package name="com.dupeng.Dao"/>-->
    </mappers>

4、生命周期和作用域

SqlSessionFactoryBuilder

  • 一旦创建了sqlSessionFactory,就不需要它
  • 局部变量

SqlSessionFactory

  • 可以想象为:数据库连接池
  • SqlSessionFactory一旦创建就应该在运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
  • 最佳作用域就是应用作用域
  • 最简单的就是使用单例模式或静态单例模式

SqlSession

  • 连接到连接池的一个请求
  • SqlSession的实例不是线程安全的,因此是不能共享,所以最佳作用域是请求或方法作用域
  • 用完之后要赶紧关闭,否则资源被占用

image-20221009123420810

5、解决属性名和字段名不一致的问题

ResultMap

实体类属性:id    name     password
数据库字段:id    name     pwd

当实体类的属性和数据库的字段名字不同的时候,查询出来的值为null。

解决方式一:取别名

<select id="getUserById" resultMap="UserMap" >
    select * from mybatis.user where id = #{id}
    <!--方式一:取别名   select id,name,pwd as password from mybatis.user where id = #{id}   -->
</select>

解决方式二:

<resultMap id="UserMap" type="User">
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>

<!-- 根据id查询用户   -->
<select id="getUserById" resultMap="UserMap" >

    select * from mybatis.user where  id= #{id}
</select>

6、日志

日志工厂

  • SLF4J
  • LOG4J(deprecated since 3.5.9) 【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】
  • NO_LOGGING

STDOUT_LOGGING标准日志输出

在mybatis核心配置文件中,配置我们的日志

mybatis-config.xml

<!--    日志工厂:注意格式,大小写,空格-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

Log4j

导入依赖

<!--    long4j日志    -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

在resources下创建log4j.properties

#????DEBUG????????console?file???????console?file?????????
log4j.rootLogger=DEBUG,console,file

#??????????
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#?????????
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/dupeng.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#??????
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sq1.PreparedStatement=DEBUG

核心配置文件

<!--    日志工厂:注意格式,大小写,空格-->
    <settings>
<!--        <setting name="logImpl" value="STDOUT_LOGGING"/>-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>

简单使用

package com.dupeng.Dao;

import com.dupeng.pojo.User;
import com.dupeng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;




public class UserMapperTest {
   static Logger logger = Logger.getLogger(UserMapperTest.class);
//根据id查询用户
@Test
    public void getUserById(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    logger.info("info进入了testlog4j");
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);


    sqlSession.close();
    
}
@Test
    public void testlog4j(){
logger.info("info进入了testlog4j");
logger.debug("debug进入了testlog4j");
logger.error("error进入了testlog4j");
}


}

7、分页

思考:为什么要分页?

减少数据的处理量!

使用Limit分页

SELECT * from user limit startIndex,pageSize

1.接口

List<User> getUserByLimit(Map<String,Integer>map);

2.mapper.xml

<!--    分页查询-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
    select * from user limit #{startIndex},#{pageSize}
</select>

3.测试

//分页查询测试
@Test
    public void getUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
   map.put("startIndex",1);
    map.put("pageSize",2);
    List<User> list = mapper.getUserByLimit(map);
    for (User user: list) {
        System.out.println(user);
    }

    sqlSession.close();
 }

8、注解开发

8.1面向接口开发

三个面向对象区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性和方法;
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现;
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构;

8.2使用注解开发

1.接口

@Select("select * from user")
List<User> getUsers();

2.核心配置文件

<mappers>
    <mapper class="com.dupeng.Dao.UserMapper"/>
</mappers>

3.测试

@Test
public void test(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.getUsers();

    for (User user : users) {
        System.out.println(user);
    }

    sqlSession.close();
}

9、mybatis执行流程剖析

2225625-20210928222801146-364222310

10、注解CRUD

编写方法

//   方法存在多个参数,所有的参数前面必须加上@Param注解
    @Select("select * from user where id=#{id}")
    User getuserById(@Param("id") int id);

    @Insert("insert into user(id,name,password) values(#{id},#{name},#{password})")
    int adduser(User user);

    @Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")
    int updateUser(User user);

    @Delete("delete from user where id=#{uid}")
    int deleteUser(@Param("uid") int id);

测试

   @Test
   public void test(){
       SqlSession sqlSession = MybatisUtils.getsqlSession();

      UserMapper mapper = sqlSession.getMapper(UserMapper.class);

      mapper.deleteUser(5);
       sqlSession.close();
   }

/*
*  查询
*  List<User> users = mapper.getUsers();

       for (User user : users) {
           System.out.println(user);
       }
 * ===============================================
 * 查询
 *  User userById = mapper.getuserById(1);
       System.out.println(userById);
       * =====================================================
       * 添加

       mapper.adduser(new User(5, "hello", "213123"));
       *
       * 删除
       *   mapper.updateUser(new User(5,"hello","147852"));
* */

11、Lombox

Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。

1.在idea中安装Lombox插件

image-20221012154254052

2.导入依赖

<!--Lombox插件-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
    <scope>provided</scope>

</dependency>

注解:

@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val

3使用方式

//实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private  String pwd;
}

12、复杂查询环境搭建

1.导入Lombok

2.新建实体类teacher,Student

package com.dupeng.pojo;

import lombok.Data;

@Data
public class Student {
    private int id;
    private String name;
    private  Teacher tid;
}
package com.dupeng.pojo;

import lombok.Data;

@Data
public class Teacher {
    private  int id;
    private  String name;
}

3.新建mapper接口

package com.dupeng.Dao;

public interface StudentMapper {
}
package com.dupeng.Dao;

import com.dupeng.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface TeacherMapper {
    @Select("select * from teacher where id=#{tid}")
    Teacher getTeacher(@Param("tid")int id);
}

4.创建techear,student的xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dupeng.Dao.StudentMapper">


</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dupeng.Dao.TeacherMapper">


</mapper>

5.在核心配置文件中注册接口

<?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核心配置文件-->

<configuration>
<!--    导入外部文件-->
    <properties resource="db.properties"/>

<!--    日志工厂:注意格式,大小写,空格-->
    <settings>
<!--        <setting name="logImpl" value="STDOUT_LOGGING"/>-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

<!--实体类别名-->
<!--    <typeAliases>-->
<!--<typeAlias type="com.dupeng.pojo.User" alias="User"/>-->
<!--    </typeAliases>-->

   <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC&amp;userSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>


    </environments>

<mappers>
    <mapper class="com.dupeng.Dao.TeacherMapper"/>
    <mapper class="com.dupeng.Dao.StudentMapper"/>
</mappers>


</configuration>

6.测试

package com.dupeng;

import com.dupeng.Dao.TeacherMapper;
import com.dupeng.pojo.Teacher;
import com.dupeng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;

public class MapperTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);
        sqlSession.close();


    }
}

13、多对一处理

mybatis_06

创建数据库表

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

复杂环境搭建

1.导入lombok

2.新建实体类teacher,student

package com.dupeng.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    private  Teacher teacher;
}
package com.dupeng.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private  int id;
    private  String name;
}

3.创建接口

package com.dupeng.Dao;

import com.dupeng.pojo.Student;

import java.util.List;

public interface StudentMapper {
    public List<Student> getStudent();
    public List<Student> getStudent2();
}
package com.dupeng.Dao;

import com.dupeng.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface TeacherMapper {
    @Select("select * from teacher where id=#{tid}")
    Teacher getTeacher(@Param("tid")int id);

}

4.创建xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--绑定接口-->
<mapper namespace="com.dupeng.Dao.StudentMapper">

</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dupeng.Dao.TeacherMapper">


</mapper>

6.在核心配置文件中注册接口和文件

<mappers>
    <mapper class="com.dupeng.Dao.TeacherMapper"/>
    <mapper class="com.dupeng.Dao.StudentMapper"/>
</mappers>

7.按照查询嵌套和结果集嵌套

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">





<!--绑定接口-->
<mapper namespace="com.dupeng.Dao.StudentMapper">

   <!--    按照结果嵌套处理-->
   <select id="getStudent2" resultMap="StudentTeacher2">
      select s.id sid, s.name sname,t.name tname
      from student s,teacher t
      where s.tid = t.id
   </select>

   <resultMap id="StudentTeacher2" type="Student">
      <result property="id" column="sid"/>
      <result property="name" column="sname"/>
      <association property="teacher" javaType="Teacher">
         <result property="name" column="tname"/>
      </association>
   </resultMap>

   <!--========================================================-->

<select id="getStudent" resultType="Student">
   select * from student
</select>

   <resultMap id="StudentTeacher" type="Student">
      <result property="id" column="id"/>
      <result property="name" column="name"/>
      <!--复杂的属性,我们需要单独出来 对象:association 集合:collection-->
      <collection property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
   </resultMap>

   <select id="getTeacher" resultType="Teacher">
      select * from teacher where id = #{id}
   </select>

</mapper>

8.测试

package com.dupeng;

import com.dupeng.Dao.StudentMapper;
import com.dupeng.Dao.TeacherMapper;
import com.dupeng.pojo.Student;
import com.dupeng.pojo.Teacher;
import com.dupeng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MapperTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);
        sqlSession.close();

    }

    @Test
    public  void  TestStudent(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
         List<Student> studentList = mapper.getStudent();
         for(Student student : studentList){
       System.out.println(student);
   }
        sqlSession.close();
    }


    @Test
    public  void  TestStudent2(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentList = mapper.getStudent2();
        for(Student student : studentList){
            System.out.println(student);
        }
        sqlSession.close();
    }

}

14、一对多处理

1.实体类

package com.dupeng.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data

public class Student {
    private int id;
    private String name;
   private int tid;
}
package com.dupeng.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data

public class Teacher {
    private  int id;
    private  String name;
//    一个老师拥有多个学生
    private List<Student> students;
}

2.接口

package com.dupeng.Dao;

import com.dupeng.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface TeacherMapper {
//获取所有老师
//    List<Teacher> getTeacher();
//    获取指定老师下的所有学生以及老师的信息
    Teacher getTeacher(@Param("tid") int id);
}

3.配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dupeng.Dao.TeacherMapper">
    <!--    按结果嵌套查询-->
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid, s.name sname, t.name tname, t.id tid
        from student s,teacher t
        where s.tid = t.id and t.id = #{tid}
    </select>

    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--        Teacher类中有List类型的属性,javaType:指定属性的类型。
                    集合中的泛型信息,使用ofType获取-->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

</mapper>

4.测试

package com.dupeng;

import com.dupeng.Dao.StudentMapper;
import com.dupeng.Dao.TeacherMapper;
import com.dupeng.pojo.Student;
import com.dupeng.pojo.Teacher;
import com.dupeng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MapperTest {
@Test
    public  void test(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher);
    sqlSession.close();
    }

}

15、动态SQL

环境搭建

CREATE TABLE `blog` (
  `id` varchar(50) NOT NULL COMMENT '博客id',
  `title` varchar(100) NOT NULL COMMENT '博客标题',
  `author` varchar(30) NOT NULL COMMENT '博客作者',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

基础工程创建

1.导包

2.编写配置文件

在核心配置文件下

<mappers>
    <mapper class="com.dupeng.Dao.BlogMapper"/>

</mappers>

3.编写实体类

package com.dupeng.pojo;

import lombok.Data;

import java.util.Date;

@Data
public class Blog {
    private String id;
    private  String title;
    private  String author;
    private Date createTime;//需解决实体类属性和数据库字段不一致
    private int views;

}

4.编写接口和对应的配置文件

package com.dupeng.Dao;

import com.dupeng.pojo.Blog;

public interface BlogMapper {
//    插入数据
    int addBlog(Blog blog);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--绑定接口-->
<mapper namespace="com.dupeng.Dao.BlogMapper">

<!--    插入数据-->
    <insert id="addBlog" parameterType="Blog">
        insert into mybatis.blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>
</mapper>

5.工具类

package com.dupeng.utils;

import org.junit.jupiter.api.Test;

import java.util.UUID;

public class IDUtils {
    public  static  String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    @Test
    public  void addInitBlog(){
        System.out.println(IDUtils.getId());
    }
}

6.插入Blog数据

package com.dupeng;

import com.dupeng.Dao.BlogMapper;
import com.dupeng.pojo.Blog;
import com.dupeng.utils.IDUtils;
import com.dupeng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.Date;

public class MapperTest {
    @Test
    public void addInitBlog(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDUtils.getId());//随机的数
        blog.setTitle("Mybatis如此简单");
        blog.setAuthor("狂神说");
        blog.setCreateTime(new Date());
        blog.setViews(999);

        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("Java如此简单");
        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("Spring如此简单");
        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("微服务");
        mapper.addBlog(blog);

        sqlSession.close();
    }


}

if语句

1.Blog接口

package com.dupeng.Dao;

import com.dupeng.pojo.Blog;

import java.util.List;
import java.util.Map;

public interface BlogMapper {
//    插入数据
    int addBlog(Blog blog);

//    查询博客
    List<Blog> queryBlogIf(Map map);
}

2.BlogMapper.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--绑定接口-->
<mapper namespace="com.dupeng.Dao.BlogMapper">

<!--    插入数据-->
    <insert id="addBlog" parameterType="Blog">
        insert into mybatis.blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>

<!--查询博客    -->
    <select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from mybatis.blog where 1=1

 <if test="title != null" >
and title=#{title}
 </if>

 <if test="author != null" >
            and author=#{author}
 </if>

 </select>
</mapper>

3.测试

  @Test
    public void testquerBlogIf(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("title","Java如此简单");
//        map.put("author","狂神说");

        List<Blog> blogs = mapper.queryBlogIf(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }

    }

choos语句

1.编写接口

//    选择查询
    List<Blog> queryBlogChose(Map map);

2.xml配置文件

<!--    选择查询-->
    <select id="queryBlogChose" parameterType="map" resultType="Blog">
        select * from mybatis.blog
<where>
    <choose>
        <when test="title != null">
            title=#{title}
        </when>

    <when test="author !=null">
   and author=#{author}
    </when>

<otherwise>
    and views =#{views}
</otherwise>
    </choose>
</where>

    </select>

3.测试

 @Test
    public void testqueryBlogChose(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("title","Java如此简单");
//        map.put("author","狂神说");

//        至少要传递一个views 否则数据为null
        map.put("views",9999);
        List<Blog> blogs = mapper.queryBlogChose(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }

        sqlSession.close();

    }

trim(where,set)

Where:条件的追加,IF语句的高级版本。若语句开头为 ”And“ 或 ”OR“,将自动去除。

set

1.接口

//    添加信息
    int updateBlog(Map map);

2.xml

<!--    添加信息-->
    <update id="updateBlog" parameterType="map">
    update mybatis.blog
<set>
    <if test="title !=null ">
         title =#{title},
    </if>
    <if test="author != null">
         author =#{author}
    </if>
</set>
where id=#{id}
    </update>

3.测试

@Test
public  void TestupdateBlog(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();

    map.put("title","Java如此简单2");
    map.put("author","狂神说3");
    map.put("id","fd424c272a2845d78a001f1843bc821c");

    mapper.updateBlog(map);


    sqlSession.close();
}

SQL片段

实现公用代码的复用,对多sql语句共有的语句进行抽取封装。

  1. 使用SQL标签抽取公共的部分
<!--    sql片段-->
<sql id="if-title-author">
    <if test="title != null" >
        and title=#{title}
    </if>

    <if test="author != null" >
        and author=#{author}
    </if>
</sql>

2.在需要的地方引入即可

<!--查询博客    -->
    <select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<!-- 引入脚本片段 -->
 <include refid="if-title-author"> </include>

    
</where>
        

注意事项:

  • 最好基于单表来定义SQL片段。
  • 不要存在where标签。
  • SQL片段内一般只是IF判断。

Foreach

open:以…开头
close:以…结尾
seperator:以…分割

1.接口

//    遍历查询
    List<Blog> queryBlogForeach(Map map);

2.xml

<!--    遍历查询-->
    <select id="queryBlogForeach" parameterType="map" resultType="Blog">
        select * from mybatis.blog
        <where>
            <foreach collection="ids" item="id" open="(" close=")" separator="or">
                id=#{id}
            </foreach>
        </where>
    </select>

3.测试

@Test
public  void  testqueryBlogForeach(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();

    ArrayList<Integer> ids =new ArrayList<>();
    ids.add(1);
    ids.add(3);
    ids.add(2);
    map.put("ids",ids);
    List<Blog> blogs = mapper.queryBlogForeach(map);

    for (Blog blog : blogs) {
        System.out.println(blog);

    }
    sqlSession.close();
}

16.缓存

1.简介

1.1什么是缓存?

  • 存在内存中的临时数据
  • 将用户经常查询的数据放在缓存(内存)中,用户查询时不需从磁盘上查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

1.2为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

1.3什么样的数据使用缓存?

  • 经常查询并且不经常改变的数据。

2.mybatis缓存

Mybatis系统中默认定义了两级缓存:一级缓存二级缓存

  • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
  • 二级缓存需要手动开启和配置,是基于namespace级别的缓存。
  • 为了提高扩展性,Mybatis定义了缓存接口Cache,可通过Cache接口自定义二级缓存。

3.一级缓存

工程创建

创建mybatis_09

实体类

package com.dupeng.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    private int id;
    private  String name;
    private  String pwd;
}

持久层

接口和xml配置文件

package com.dupeng.dao;

import com.dupeng.pojo.User;
import org.apache.ibatis.annotations.Param;

public interface UserMapper {
//    根据id查询用户
    User queryUserById(@Param("id") int id);

//    修改用户
    int updateeUser(User user);

}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--绑定接口-->
<mapper namespace="com.dupeng.dao.UserMapper">
<select id="queryUserById" resultType="User">
    select * from user where id=#{id}
</select>

    <update id="updateeUser" parameterType="User" >
 update  mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
    </update>
</mapper>

测试

package com.dupeng;

import com.dupeng.dao.UserMapper;
import com.dupeng.pojo.User;
import com.dupeng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class Mytest {
    @Test
    public  void test(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = mapper.queryUserById(1);
        System.out.println(user);

//        添加的修改语句
        System.out.println("========添加的修改语句============");
    mapper.updateeUser(new User(2,"aaa","bbb"));

        System.out.println("====================");
        User user2 = mapper.queryUserById(1);
        System.out.println(user2);
        System.out.println(user==user2);
        sqlSession.close();

    }
}

缓存失效的情况:

  1. 查询不同的东西。
  2. 增删改操作,可能会改变原来的数据,所以会刷新缓存。
  3. 查询不同的Mapper.xml。
  4. 手动清楚缓存。 sqlSession.clearCache();

4.二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用太低,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果当前会话关闭,这个会话对应的一级缓存;但是我们想要的是,会话关闭了一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存中;

步骤;

1.开启全局缓存--核心配置文件

 <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
  • 在要使用二级缓存的Mapper中开启,可以自定义参数

xml映射配置文件

 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

小结:

  • 只要开启了二级缓存,在同一个Mapper下就有效。
  • 所有的数据都会先放在一级缓存中。
  • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中

5.缓存原理

image-20221023172138131

6.自定义缓存-ehcache

  <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>

标签:笔记,public,sqlSession,dupeng,mybatis,import,狂神,com,id
From: https://www.cnblogs.com/DuPengBG/p/16844036.html

相关文章

  • MySQL(狂神)
    1、初识MySQLJavaEE:企业级Java开发Web前端(页面:展示,数据!)后台(连接点:连接数据库JDBC,连接前端(控制,控制视图跳转,和给前端传递数据))数据库(存数据,txt,excel,world)......
  • 前端基础7天快速入门——狂神说css笔记
    1、css的3种导入方式优先级:就近原则<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><!--内部样式-->......
  • 前端基础7天快速入门——狂神说html笔记
    网页基本信息<!--DOCTYPE——规范--><!DOCTYPEhtml><htmllang="en"><!--head标签代表网页头部--><head><!--meta描述性标签,它用来描述我们网站的一些信息......
  • 前端基础7天快速入门——狂神说JavaScript笔记
    1、快速入门1.1引入JavaScript内部标签<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><script>aler......
  • Springcloud学习:狂神说Springcloud学习相关资料
    链接:https://pan.baidu.com/s/1eYjdQVzbALau-zNStKWMKA提取码:b6a6复制这段内容后打开百度网盘手机App,操作更方便哦......
  • Mybatis 报错Mapper method 'xxx' has an unsupported return type
    报错原因:出现这种错误,说明sql语句执行成功,只是返回类型出了问题。解决方法:insert、delete、update操作默认返回一个int类型的整数,将增删改的接口改成int或者void即可。......
  • 赏金猎人笔记-手动sqli
      声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。本文选......
  • 赏金猎人笔记-sqli几个小技巧
      几个技巧小结1.查找后台登陆界面:google:“www.target.comlogin”有效率达到60%的一个sqli的payload:2.sqli一个有效载荷:admin'OR1=1--'3.对于类似这种:......
  • vue3笔记
    1.toRefs可以把对象变成响应式对象2.生命周期vue2里面的beforeDestory-->beforeUnmount​ destoryed-->unmounted3.侦测变化-watchwatch()第一个参数是......
  • 赏金猎人笔记-电子邮件中所存在的sqli
      前言本篇文章主要测试的是密码忘记功能;过程我第一次嘗試沒有空格的輸入:[email protected]=>valid“a”@.com=>valid然後用空格:[email protected]=>無效“d......