Spring-MyBatis基本操作

发布于:2025-06-22 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

一.什么是MyBtais

 二.第一个MyBatis程序

        1.构建数据库表

                企业建表的规范:

        2.创建项目引入依赖

        3.编写代码   

        4.创建单元测试

三.使用MyBatis

        1.通过注解使用Mybatis和Mysql数据库进行交互

                a.@Insert(增)

                 b.@Delete(删)

                c.@Update(改) 

                 d.@Select(查)

                        对象中的成员属性和待查表中自动名称不匹配解决方法:

         2.通过XML文件的方式使用MyBatis和MySQL数据库进行交互

                使用前的配置:

                        a.增加

                        b.删除 

                        c.修改

                        d.查找

三.多表查询

        1.MySQL中的多表查询的SQL语句

        2.MyBatis中的多表查询 

四.#{} 和 ${}

        1.相同之处

        2.不同之处

                使用#查询:

                使用$查询:

                        $存在的漏洞:SQL注入 

                总结#和$的区别:

五.池化


一.什么是MyBtais

        MyBatis是一款优秀的持久层(Dao层)框架,用于简化JDBC的开发。

        MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由apache迁移到goole code,并且改名为MyBatis。2013年11月迁移到GitHub。

        官网:MyBatis中文网

        持久层:指的是持久化操作的层,通常指的是操作数据的层(Dao层),用来操作数据库的。

        简单来说MyBatis是更简单完成程序与数据库交互的框架,也就是更简单的操作和读取数据库的工具。 
        接下来,我们就通过一个入门程序,让大家感受一下通过MyBatis如何来操作数据库的。       

 二.第一个MyBatis程序

        1.构建数据库表
-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;

CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
-- 使⽤数据数据
USE mybatis_test;

-- 创建表[⽤⼾表]
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (
 `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
 `username` VARCHAR ( 127 ) NOT NULL,
 `password` VARCHAR ( 127 ) NOT NULL,
 `age` TINYINT ( 4 ) NOT NULL,
 `gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-⼥ 0-默认',
 `phone` VARCHAR ( 15 ) DEFAULT NULL,
 `delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
 `create_time` DATETIME DEFAULT now(),
 `update_time` DATETIME DEFAULT now() ON UPDATE now(),
 PRIMARY KEY ( `id` ) 
 ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; 

 -- 添加⽤⼾信息
 INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
 VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
 INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
 VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
 INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
 VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
 INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
 VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );
                企业建表的规范:

                        字段名/表名全部小写

                        表中必备的三个字段:id、gmt_create、gmt_modified 

                        delete_flag是用来标记这个数据是否被删除了

                                逻辑删除:从逻辑的角度上删除了数据,但是数据依然保存在数据库中,只是在查找的时候不打印这个数据了。

                                物理删除:从硬盘的角度上将数据彻底的删除。(开发中很少用到这种删除)

        2.创建项目引入依赖

                这里下面的MariaDB Driver引入错误了,应该是MySQL Driver 

                配置数据库的相关信息:

       配置文件中的代码https://blog.csdn.net/weixin_52159554/article/details/148771292?spm=1001.2014.3001.5502

                 试运行

        3.编写代码   

                创建用于接收数据库中字段信息的对象

import lombok.Data;

import java.util.Date;

@Data
public class UserInfo {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.example.springmabatisdemo.model.UserInfo;

import java.util.List;

@Mapper
public interface UserInfoMapper {

    @Select("select * from user_info")
    List<UserInfo> selectAll();
}

        4.创建单元测试

import lombok.extern.slf4j.Slf4j;
import org.example.springmabatisdemo.model.UserInfo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Slf4j
class UserInfoMapperTest {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @BeforeEach
    void setUp() {
        log.info("setUp");
    }

    @AfterEach
    void tearDown() {
        log.info("tearDown");
    }

    @Test
    void selectAll() {
        List<UserInfo> userInfos = userInfoMapper.selectAll();
        log.info("userInfos:"+ userInfos.toString());
    }
}

                方便对比引入配置 :

                        配置文件中内容,在上面的链接中

                        打印日志是一个很消费性能的行为,因为在实际开发中,需要打印的日志非常的多,如果不进行筛选的情况下,会对系统性能造成一个很大的影响,所以在开发中只在关键的地方进行日志的打印,而且打印日志只在项目的开发阶段用于程序员开发使用。

                        开发环境:本地开发,一些公司也会有单独的服务器上员工进行开发测试,开发环境一般有专属于开发环境的数据库。

                        测试环境:给测试人员用于测试项目搭建的环境,包括测试数据库。

                        预发布环境:和发布环境一样的配置,但是不对外提供服务只是用于内部测试。在这个环境中和发布环境公用一个数据库服务器。

                        发布环境 :用户可以访问到项目的环境。

                        灰度发布:项目在完成测试和开发后不会一下子发布到全部的线上服务器中,而是只发布到少量的服务器中,也就是控制访问的流量,用于小面积的测试,判断项目是否有问题,然后一点一点的增大流量,直到发布到所有服务器中。

三.使用MyBatis

        1.通过注解使用Mybatis和Mysql数据库进行交互
                a.@Insert(增)

                        新增之前的数据表:

    //1.指定新增的数据
    @Insert("insert into user_info (username,password,age,gender,phone)" +
            "values (\"张三\",\"123456\",\"18\",\"1\",\"15678881542\")")
    Integer insertOne();
    //2.通过接收对象来新增数据,并获取自增id
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into user_info (username,password,age,gender,phone)" +
            "values (#{username},#{password},#{age},#{gender},#{phone})")
    Integer insertTwo(UserInfo userInfo);
    //3.参数为对象时对参数进行重命名
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into user_info (username,password,age,gender,phone)" +
            "values (#{userInfo.username}," +
            "#{userInfo.password}," +
            "#{userInfo.age}," +
            "#{userInfo.gender}," +
            "#{userInfo.phone})")
    Integer insertThree(@Param("userInfo") UserInfo userInfo);

                        单元测试中的代码: 

   @Test
    void insertOne() {
        log.info(userInfoMapper.insertOne().toString());
    }

    @Test
    void insertTwo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("李四");
        userInfo.setPassword("9874566");
        userInfo.setAge(25);
        userInfo.setGender(2);
        userInfo.setPhone("1345678912");

        log.info(userInfoMapper.insertTwo(userInfo).toString() + " " + "id:" + userInfo.getId());
    }

    @Test
    void insertThree() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("李四");
        userInfo.setPassword("9874566");
        userInfo.setAge(25);
        userInfo.setGender(2);
        userInfo.setPhone("1345678912");

        log.info(userInfoMapper.insertThree(userInfo).toString() + " " + "id:" + userInfo.getId());
    }

                        新增之后的数据表: 

                 b.@Delete(删)

                        删除前的数据表:

 //删除指定数据
    @Delete("delete from user_info where id=#{id}")
    Integer deleteOne(@Param("id") Integer id);
 @Test
    void deleteOne() {
        log.info(userInfoMapper.deleteOne(1).toString());
    }

                        删除后的数据表: 

                c.@Update(改) 

                        修改前的数据表:

//修改指定id的数据
    @Update("update user_info set age=#{age} where id=#{id}")
    Integer updateOne(UserInfo userInfo);
@Test
    void updateOne() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setAge(30);
        log.info(userInfoMapper.updateOne(userInfo).toString());
    }

                        修改后的数据表: 

                 d.@Select(查)
  @Select("select * from user_info")
    List<UserInfo> selectAll();
 @Test
    void selectAll() {
        List<UserInfo> userInfos = userInfoMapper.selectAll();
        log.info("userInfos:"+ userInfos.toString());
    }

                        运行后发现有些成员为空: 

                        原因是对象中的成员属性和待查表中自动名称不匹配: 

                        对象中的成员属性和待查表中自动名称不匹配解决方法:

                                对mysql查询结果进行重命名: 

                                        这里一定要注意换行写的情况下,需要留意空格,不然会导致SQL语句错误

 @Select("select id, username, password, age, gender, phone," +
            " delete_flag as deleteFlag, create_time as createTime, update_time as updateTime" +
            " from user_info")
    List<UserInfo> selectAll();

                                使用@Results注解

 @Results(id="BaseMap", value={
            @Result(column = "delete_flag", property = "deleteFlag"),
            @Result(column = "create_time", property = "createTime"),
            @Result(column = "update_time", property = "updateTime")
    })
    @Select("select * from user_info")
    List<UserInfo> selectAllTwo();

    @ResultMap(value = "BaseMap")
    @Select("select * from user_info")
    List<UserInfo> selectAllThree();
@Test
    void selectAllTwo() {
        List<UserInfo> userInfos = userInfoMapper.selectAllTwo();
        log.info("userInfos:"+ userInfos.toString());
    }
    @Test
    void selectAllThree() {
        List<UserInfo> userInfos = userInfoMapper.selectAllThree();
        log.info("userInfos:"+ userInfos.toString());
    }

                                通过配置来实现自动转驼峰: 
  MyBatis自动转驼峰配置代码https://blog.csdn.net/weixin_52159554/article/details/148771292?spm=1001.2014.3001.5502                                        此时只需要将配置代码导入到properties或yml文件中,就可以不需要上面的操作,只使用最原始是@Select,就就可以让MyBatis自动完成映射。

         2.通过XML文件的方式使用MyBatis和MySQL数据库进行交互
                使用前的配置:

                通过XML文件的方式和通过注释的方式第一步都是一样的,在properties或yml文件中配置连接mysql服务器的相关信息。

                指明XML文件的路径 


固定格式的XML文件代码https://blog.csdn.net/weixin_52159554/article/details/148797089?sharetype=blogdetail&sharerId=148797089&sharerefer=PC&sharesource=weixin_52159554&spm=1011.2480.3001.8118

import org.apache.ibatis.annotations.Mapper;
import org.example.springmabatisdemo.model.UserInfo;

@Mapper
public interface UserInfoXMLMapper {

   
}

                        a.增加
Integer insertOne(UserInfo userInfo);

Integer insertTwo(@Param("userInfo")UserInfo userInfo);
<?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="org.example.springmabatisdemo.mapper.UserInfoXMLMapper">
    <insert id="insertOne" useGeneratedKeys="true" keyProperty="id">
        insert into user_info (username,password,age,gender,phone)
        values (#{username},#{password},#{age},#{gender},#{phone})
    </insert>
    <insert id="insertTwo" useGeneratedKeys="true" keyProperty="id">
        insert into user_info (username,password,age,gender,phone)
        values (#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})
    </insert>
</mapper>
import lombok.extern.slf4j.Slf4j;
import org.example.springmabatisdemo.model.UserInfo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Slf4j
class UserInfoXMLMapperTest {
    @Autowired
    private UserInfoXMLMapper userInfoXMLMapper;

    @BeforeEach
    void setUp() {
        log.info("setUp");
    }

    @AfterEach
    void tearDown() {
        log.info("tearDown");
    }

    @Test
    void insertOne() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("李四");
        userInfo.setPassword("9874566");
        userInfo.setAge(25);
        userInfo.setGender(2);
        userInfo.setPhone("1345678912");
        log.info(userInfoXMLMapper.insertOne(userInfo).toString() + " " + "id:" + userInfo.getId());
    }

    @Test
    void insertTwo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("李四");
        userInfo.setPassword("9874566");
        userInfo.setAge(25);
        userInfo.setGender(2);
        userInfo.setPhone("1345678912");
        log.info(userInfoXMLMapper.insertTwo(userInfo).toString() + " " + "id:" + userInfo.getId());
    }
}

                        b.删除 
Integer deleteOne(@Param("id")Integer id);
 <delete id = "deleteOne">
        delete from user_info where id = #{id}
    </delete>
 @Test
    void deleteOne() {
        log.info(userInfoXMLMapper.deleteOne(5).toString());
    }
                        c.修改
Integer updateOne(@Param("id")Integer id,@Param("username")String username);
 <update id = "updateOne">
        update user_info set username = #{username}
        where id = #{id}
    </update>
@Test
    void updateOne() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setUsername("wuhuwhuwhuw");
        log.info(userInfoXMLMapper.updateOne(userInfo.getId(),userInfo.getUsername()).toString());
    }
                        d.查找

                                使用XML文件进行查找,也会遇到字段名称和Java属性名称不匹配的情况,解决办法同注释的方法一样。 

 List<UserInfo> selectOne();

    List<UserInfo> selectTwo();
<resultMap id="XmlBaseMap" type="org.example.springmabatisdemo.model.UserInfo">
        <id column="id" property="id"></id>
        <result column="delete_flag" property="deleteFlag"></result>
        <result column="create_time" property="createTime"></result>
        <result column="update_time" property="updateTime"></result>
    </resultMap>
    <select id = "selectOne" resultType="org.example.springmabatisdemo.model.UserInfo">
        select * from user_info
    </select>

    <select id = "selectTwo" resultMap="XmlBaseMap">
        select * from user_info
    </select>

三.多表查询

        在项目开发中最好尽量避免使用多表查询,尤其是在对性能有很高要求的项目中。因为多表查询是MySQL服务器帮我们进行的优化,我们无法选择是何种优化,这样在进行大量数据查询的时候会非常占用性能,所以使用多表查询必然很慢。

a.select * from ta
b.select * from tb
c.select * from ta left join tb ta.xx=tb.xx

        对于上面的SQL代码,假设a,b代码的运行时间分别是10ms,则c代码的运行时间肯定会大于10ms,此时虽然a,b代码分别执行后的总时间是20ms,看似使用多表查询是更省时间的选择,但是对于a,b代码的运行,在实际的项目开发中我们是会对这样的操作进行优化的,比如引入多线程,此时就会比多表查询更加节省时间,但是多表查询是MySQL服务器帮我们进行的优化,我们无法直接选择。

        而且通常情况下,数据库集群是很多项目一起使用的,当出现慢查询时,会影响整个数据库集群,从而也就影响了所有使用该集群的项目。

        同时比如在执行很多类似于a,b代码的操作时,虽然会导致Java服务器的吞吐量变大,造成系统卡顿,但是此时我们只需要很简单的将Java服务器扩容就好了,这种服务器扩容的方式是非常简单的,但是如果你想要扩容数据库集群的话就不是很方便了,此时就需要让DBA(数据库管理员,是另一个团队)来做扩容,自己本来可以通过别的方法解决的事情,此时引入第三方,就不会那么方便了。

        但是虽然多表查询在性能上的开销很大,但是多表查询会自动帮我们拼接我们需要查的多个表,帮我们对数据进行一些操作。

        1.MySQL中的多表查询的SQL语句

                添加新的数据表:

CREATE TABLE article_info (
 id INT PRIMARY KEY auto_increment,
 title VARCHAR ( 100 ) NOT NULL,
 content TEXT NOT NULL,
 uid INT NOT NULL,
 delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
 create_time DATETIME DEFAULT now(),
 update_time DATETIME DEFAULT now() 
 ) DEFAULT charset 'utf8mb4';

 -- 插⼊测试数据
 INSERT INTO article_info ( title, content, uid ) VALUES ( 'Java', 'Java正⽂', 1
);
select ta.*,tb.username,tb.age from article_info ta
 LEFT JOIN user_info tb on ta.uid = tb.id
 where ta.id=1

        2.MyBatis中的多表查询 

                在项目中,我们获取多表查询数据的时候,并不在乎是多少个表组成的数据,所以只需要将需要数据构建成一个对象,将数据保存到这个对象中就可以了。

                构建用于接收数据的对象:

import lombok.Data;

import java.util.Date;

@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    private Integer uid;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
    //接收用户信息
    private String userName;
    private Integer age;
}

                使用使用注释的方法使用MyBatis操作数据库: 

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.example.springmabatisdemo.model.ArticleInfo;

@Mapper
public interface ArticleInfoMapper {

    @Select("select ta.*,tb.username,tb.age from article_info ta" +
            " LEFT JOIN user_info tb on ta.uid = tb.id\n" +
            " where ta.id= #{id}")
    ArticleInfo getArticleInfoById(Integer id);
}

                单元测试代码: 

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Slf4j
class ArticleInfoMapperTest {

    @Autowired
    private ArticleInfoMapper articleInfoMapper;

    @BeforeEach
    void setUp() {
        log.info("setUp");
    }

    @AfterEach
    void tearDown() {
        log.info("tearDown");
    }

    @Test
    void getArticleInfoById() {
        log.info(articleInfoMapper.getArticleInfoById(1).toString());
    }
}

四.#{} 和 ${}

        1.相同之处

                #{}和${}都是用来获取传入变量的值的。、

        2.不同之处
                使用#查询:

                        会自动根据传入的类型进行匹配: 

                                会自动根据传入的数据的类型进行匹配的原因是,#是预编译SQL: 

                        涉及排序操作的时候,不能使用# ,因为如果使用#会自动为其添加' ':

                                使用$就可以了: 

                        进行模糊查询的时候也不可以使用#: 

                                使用$就可以了:  

                使用$查询:

                        使用$并不会自动转换,给什么它就传什么,但是以username为条件的时候,需要' '引起来的字符串,#会自动添加,$不会。 

                        解决办法:不论传输何种数据,都手动加上' ',这样MySQL服务器会自动帮我们进行优化,但是会产生一定的性能问题。 

                                以下方法选其一:

                        $存在的漏洞:SQL注入 

                                此时因为or也是SQL关键词,MySQL也就同时将or 1='1' 给执行了,'1'MySQL会自动优化为1,但是这并不是想要的结果。

                                因为$是即时的SQL,所以给它啥它就会立马赋值为啥,此时就会有问题,这是一个非常严重的问题,如果想上面的代码这样直接写,如果有人这样访问数据库,带上了DROP操作,此时损失就非常的大了。

                                 此时使用#就可以了。

                总结#和$的区别:

                                #是预编译SQL而$是即时SQL

                                        预编译SQL性能高

                                        预编译SQL不存在SQL注入问题

                                        排序时不能用#,即时使用$时也要在代码中进行参数检查,防止代码注入的问题

                                        表名,字段名等作为参数时,也不能使用#

                                        模糊查询时,如果非要使用#,需要搭配mysql的内置函数concat

                                        一般在项目开发中,能用#的先用#,实在不行了再用$。 

五.池化

        Hikari : SpringBoot默认使⽤的数据库连接池

        Druid
                如果我们想把默认的数据库连接池切换为Druid数据库连接池, 只需要引⼊相关依赖即可
<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid-spring-boot-3-starter</artifactId>
 <version>1.2.21</version>
</dependency>
                如果果SpringBoot版本为2.X, 使⽤druid-spring-boot-starter 依赖        
<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid-spring-boot-starter</artifactId>
 <version>1.1.17</version>
</dependency>


网站公告

今日签到

点亮在社区的每一天
去签到