Mybatis | 豆包MarsCode AI刷题

48 阅读6分钟

概述

mybatis是什么?

它是一款半自动的ORM持久层框架,用于简化JDBC的开发

具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低

什么是ORM?

Object Relation Mapping,对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系,比如用一个Java的Student类,去对应数据库中的一张student表,类中的属性和表中的列一一对应。Student类就对应student表,一个Student对象就对应student表中的一行数据

为什么mybatis是半自动的ORM框架?

用mybatis进行开发,需要手动编写SQL语句。而全自动的ORM框架,如hibernate,则不需要编写SQL语句。用hibernate开发,只需要定义好ORM映射关系,就可以直接进行CRUD操作了。由于mybatis需要手写SQL语句,所以它有较高的灵活性,可以根据需要,自由地对SQL进行定制,也因为要手写SQL,当要切换数据库时,SQL语句可能就要重写,因为不同的数据库有不同的方言(Dialect),所以mybatis的数据库无关性低。虽然mybatis需要手写SQL,但相比JDBC,它提供了输入映射和输出映射,可以很方便地进行SQL参数设置,以及结果集封装。并且还提供了关联查询和动态SQL等功能,极大地提升了开发的效率。并且它的学习成本也比hibernate低很多

什么是JDBC?

JDBC(Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API

什么是数据库连接池?

  • 数据库连接池是个容器,负责分配、管理数据库连接
  • 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
  • 释放空闲时间超过预设的最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏

优势

  1. 资源重用
  2. 提升系统响应速度
  3. 避免数据库连接遗漏

常见连接池

  • C3P0
  • DBCP
  • Druid(德鲁伊,alibaba开源)
  • HiKari(springboot默认)

切换Druid连接池

只需引入依赖即可

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
</dependency>

快速入门

1、创建springboot工程

  • 创建springboot工程

  • 导入mybatis的起步依赖、mysql的驱动包

  • 项目创建完成后,会自动在pom.xml文件中,导入 Mybatis依赖和 MySQL驱动依赖

2、连接数据库

3、新建架构

(如果已经有架构,此步可忽略)

4、新建数据库表

5、修改配置文件

将application.properties改为application.yml

server:
  port: 8080 #默认端口8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo_2024 #jdbc:mysql://localhost:3306/+自己的数据库架构名称
    username: root #统一
    password: **** #自己数据库密码

6、创建实体类

package com.example.springbootlogindemo.entity;

public class User {
    private Integer id;
    private String username;
    private String password;
    private String age;

    //以下这些get,set方法以后可以引入lombok后使用@Data代替
    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

7、创建mapper接口

package com.example.springbootlogindemo.mapper;

import com.example.springbootlogindemo.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper//在运行时,会自动生成该接口的实现类对象(代理对象),并将该对象交给IOC容器管理
public interface UserMapper {

    //增
    @Insert("insert into `user` (username,password,age)values (#{username},#{password},#{age})")
    void insert(User user);

}

8、编写测试类进行测试

package com.example.springbootlogindemo;

import com.example.springbootlogindemo.entity.User;
import com.example.springbootlogindemo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootLoginDemoApplicationTests {

    @Autowired
    private UserMapper userMapper;
    @Test
    void insert() {
        User user = new User();
        user.setUsername("admin");
        user.setPassword("123456");
        user.setAge("18");
        userMapper.insert(user);
    }
}

9、查看user表确认数据是否插入进去

success!

拓展CRUD

以上是新增操作

要求完善基础的删、改、查的功能

package com.example.springbootlogindemo.mapper;
import com.example.springbootlogindemo.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;

@Mapper
public interface UserMapper {

    //增
    @Insert("insert into `user` (username,password,age)values (#{username},#{password},#{age})")
    void insert(User user);

    //删(根据id)
    @Delete("delete from `user`where id=#{id}")
    void deleteUser(Integer id);

    //改
    @Update("update `user` set username=#{username},password=#{password},age=#{age} where id=#{id}")
    void updateUser(User user);

    //查(根据id)
    @Select("select * from `user`where id=#{id} order by id desc ")
    User selectById(Integer id);

    //查(所有)
    @Select("select * from `user`order by id desc ")
    List<User> selectAll();

    //查(多条件)
    @Select("select * from `user` where age=#{age} and username=#{username} order by id desc") //注意是用and连接
    List<User> selectMore(@Param("age") String age, @Param("username") String username);
}
package com.example.springbootlogindemo;
import com.example.springbootlogindemo.entity.User;
import com.example.springbootlogindemo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootLoginDemoApplicationTests {

    @Autowired
    private UserMapper userMapper;
    @Test//增
    void insert() {
        User user = new User();
        user.setUsername("admin4");
        user.setPassword("123456");
        user.setAge("18");
        userMapper.insert(user);
    }

    @Test//删(根据id)
    void delete() {
        userMapper.deleteUser(1);
    }

    @Test//改
    void updateUser() {
        User user = new User();
        user.setId(1);
        user.setUsername("admin2");
        user.setPassword("654321");
        user.setAge("20");
        userMapper.updateUser(user);
    }

    @Test//查(根据id)
    void selectById() {
        User user = userMapper.selectById(1);
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        System.out.println(user.getAge());
    }

    @Test//查(所有)
    void selectAll() {
        for (User user : userMapper.selectAll()) {
            System.out.println(user.getUsername());
            System.out.println(user.getPassword());
            System.out.println(user.getAge());
        }
    }

    @Test//查(多条件)
    void selectMore() {
        for (User user : userMapper.selectMore("18", "admin3")) {
            System.out.println(user.getUsername());
            System.out.println(user.getPassword());
            System.out.println(user.getAge());
        }
    }
}

其他

如何配置SQL提示

选中任意sql代码->右键->显示上下文操作->语言注入设置->选择MySQL->确认

开启Mybatis日志

只需在application.yml加入以下代码

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

SQL注入

通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法

例:select conut(*) from emp where username='dfgauygfagfsuyd' and password='';

SQL注入后:

select conut(*) from emp where username='dfgauygfagfsuyd' and password='' or '1'='1';

预编译SQL

优势:

  • 性能更高
  • 更安全(防止SQL注入)

参数占位符

#{}

执行SQL时,将#{……}替换为? 生成预编译SQL,会自动设置参数值

适用于:参数传递

${}

执行SQL时,直接将参数拼接在SQL语句中,存在SQL注入问题

适用于:对表名,列表进行动态设置时使用

主键返回

在数据添加成功后,需要获取插入数据库数据的主键

实现:在SQL语句所对应的注解上方添加@Options(keyProperty="id", useGeneratedKeys=true)

数据库字段与实体类不一致

如:user_name != userName

为了能查询到数据,解决方案:

方案一:起别名,在SQL语句中,对不一样的列名起别名,和实体类属性名一样

方案二:使用@Results和@Restult进行手动结果映射

@Select(select * from emp where id = #{id})

@Results({

@Result(column="create_time",property="createTime"),

@Result(column="update_time",property="updateTime")

})

方案三:(推荐)开启驼峰命名,在配置文件中添加

mybatis:
  configuration:
    map-underscore-to-camel-case: true

总结

模糊查询

XML映射文件

XML映射文件名称与Mapper接口名称一致,并且XML映射文件名称与Mapper接口放在同一个包下(同包同名)

XML映射文件的namespace属性与Mapper接口名称一致

XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致

<?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.example.sp.mapper.ShopMapper">
  <select id="list" resultType="com.example.sp.pojo.Shop">
    所要查询的sql语句
  </select>
</mapper>

动态SQL

注意:如果是String类型时,除了判断不能为null,还要判断不能为''(空字符串)

用于判断条件是否成立,使用test属性进行条件判断,若条件为true,则拼接SQL

只会在子元素有内容的情况下才插入where语句,自动去除子语句开头的and或or

<?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.example.sp.mapper.ShopMapper">
  <select id="list" resultType="com.example.sp.pojo.Shop">
    select * from shop
    <where>
      <if test="categoryId!=null">
        category_id=#{categoryId}
      </if>
      <if test="state!=null">
        and state=#{state}
      </if>
      and create_user=#{userId}
    </where>
  </select>
</mapper>

动态插入set关键字,并会删去额外的逗号(用在update语句中)

<?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.example.sp.mapper.ShopMapper">
  <update id="updata">
    <set>
      <if test="categoryId!=null">
        category_id=#{categoryId},
      </if>
      <if test="state!=null">
        and state=#{state}
      </if>
    </set>
    where id=#{id}
  </update>
</mapper>

循环遍历

collection 遍历的集合

item 遍历出来的元素

separator 分隔符

open 遍历开始前拼接的SQL片段

close 遍历结束后拼接的SQL片段

<?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.example.sp.mapper.ShopMapper">
  <delete id="deleteByIds">
    <foreach collection="ids" item="id" separator="," open="(" close=")">
      #{id}
    </foreach>
  </delete>
</mapper>

抽取SQL片段

引入SQL片段