mybatis&mybatis-plus

MyBatis

持久层框架

MyBatis 是一个流行的 Java 持久层框架,专注于简化数据库操作。它通过 XML 或注解配置,支持自定义 SQL、存储过程和高级映射,极大地减少了繁琐的 JDBC 代码。MyBatis 的灵活性使其适用于需要精细控制 SQL 性能的场景,同时也能实现 SQL 与 Java 代码的分离,提升代码的可维护性

CRUD步骤

1.添加mybatis依赖

 <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.4</version>
        </dependency>

2.创建java实体类

@Data
public class Emp {
    private Integer id;
    private String empName;
    private Integer age;
    private Double empSalary;
}

3.写Service接口,但此时是Mapper(加@Mapper注解)

@Mapper//告诉Spring,这是MyBatis操作数据库的Mapper接口
public interface EmpMapper {
   Emp getEmpById(Integer id);
   void addEmp(Emp emp);
   void deleteEmpById(Integer id);
   void updateEmp(Emp emp);
   List<Emp> getAllEmp();
}

4.为接口写xml文档,编写sql语句

5.调用接口方法即可

数据处理

#{}和${}

1. #{}(PreparedStatement 方式,占位符)
特点
  • 采用 预编译 方式,使用 JDBC PreparedStatement 进行 SQL 传参。

  • 防止 SQL 注入,因为参数是作为占位符传递,而不是直接拼接 SQL 语句。

  • 性能较优,SQL 解析和执行效率较高。

2. ${}(字符串替换方式)
特点
  • 直接进行 字符串拼接,参数会被原样替换到 SQL 语句中。

    扫描二维码关注公众号,回复: 17574729 查看本文章
  • 容易引发 SQL 注入,因为参数值直接拼接到 SQL 语句里。

  • 主要用于 动态 SQL(表名、列名) 需要动态拼接的场景。

<select id="getUserByColumn" resultType="User">
    SELECT * FROM user WHERE ${column} = #{value}
</select>
    编译的语句
SELECT * FROM user WHERE username = ?
 
 

多个参数时

@param注解可指定参数

`

编写sql语句时,若类的属性名和数据库的字段名不同,会导致数据无法赋值

解决方式

1.让属性名和字段名一样(不推荐)

2.编写sql语句时,给字段名起别名成属性名

3.在配置文件中声明驼峰命名转换mybatis.configuration.map-underscore-to-camel-case=true

4.在Mapperxml文件里写resultMap改名

<resultMap id="EmpRM" type="org.example.mybatis01helloworld.bean.Emp">
       <id column="id" property="id" />
       <result column="emp_name" property="empName" />
       <result column="age" property="age" />
       <result column="emp_salary" property="empSalary" />
   </resultMap>

最佳实践:

1.开启驼峰命名规则

2.1 搞不定的用自定义映射(resultMap)

关联查询

一对一的映射用association标签

<resultMap id="userResultMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="address" javaType="Address">
        <id property="id" column="address_id"/>
        <result property="city" column="city"/>
    </association>
</resultMap>
<select id="getUserWithAddress" resultMap="userResultMap">
    SELECT u.id, u.name, a.id as address_id, a.city
    FROM users u
    LEFT JOIN addresses a ON u.address_id = a.id
    WHERE u.id = #{id}
</select>

一对多映射使用collection标签

<resultMap id="departmentResultMap" type="Department">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="employees" ofType="Employee">
        <id property="id" column="employee_id"/>
        <result property="name" column="employee_name"/>
    </collection>
</resultMap>
<select id="getDepartmentWithEmployees" resultMap="departmentResultMap">
    SELECT d.id, d.name, e.id as employee_id, e.name as employee_name
    FROM departments d
    LEFT JOIN employees e ON d.id = e.department_id
    WHERE d.id = #{id}
</select>
原生分布查询

编写多个sql语句,调用多次方法

自动分布查询
<resultMap id="DepartmentResultMap" type="Department">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <!-- 一对多分步查询 -->
    <collection property="employees" column="id"#传入的参数 select="getEmployeesByDeptId"#自动执行的sql语句 fetchType="lazy"/>
</resultMap>
​
<select id="getDepartmentById" resultMap="DepartmentResultMap">
    SELECT * FROM department WHERE id = #{id}
</select>
​
<select id="getEmployeesByDeptId" resultType="Employee">
    SELECT * FROM employee WHERE department_id = #{id}
</select>
要点: select属性写sql语句 column传入参数

动态sql

<if test="   ">判断是否执行

<select id="getUsers" resultType="User">
    SELECT * FROM user
    WHERE 1=1#防止逻辑错误
    <if test="name != null">
        AND name = #{name}
    </if>
    <if test="age != null">
        AND age = #{age}
    </if>
</select>
 
 

where标签 解决where后面无条件或多and

set 标签 解决不传值时多逗号的情况

trim标签 可以代替where 和set 标签

  • prefix:指定需要添加的前缀,例如 WHERESET

  • prefixOverrides:指定需要移除的前缀,比如 ANDOR

  • suffix:指定需要添加的后缀。

  • suffixOverrides:指定需要移除的后缀。

choose/when/ortherwise 相当于switch(break)一旦找到配匹配的when就不会继续检查后面的when了

<select id="getUser" resultType="User">
    SELECT * FROM user
    <where>
        <choose>
            <when test="id != null">
                id = #{id}
            </when>
            <when test="name != null">
                name = #{name}
            </when>
            <otherwise>
                1=1
            </otherwise>
        </choose>
    </where>
</select>

foreach标签 collection 集合名

item 集合中的变量

separator 设置分隔符

open 指定开始的前缀

close 结束后缀

<select id="getUsersByIds" resultType="User">
    SELECT * FROM user WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

抽取可复用的sql片段

<sql id = "column_name">

xxxx

</sql>

缓存机制

两级缓存机制:

一级缓存机制

同一个 SqlSession 共享缓存,第一次查询数据库并缓存结果,第二次查询相同的sql时不访问数据库,直接从缓存取

二级缓存机制:

手动开启,在同一个mapper内,跨sqlsession共享

查找顺序:二一库

1.事务级别 默认开启

<!---同一个事务期间,前面查询的数据,后面如果要执行相同查询,会从一级缓存中获取数据-->

有时候缓存会失效:缓存不命中

  1. 查询的东西不一样

  2. 两次查询之间,进行了增删改(Mybatis会认为数据库发生了改变,所以要再发送一次查询)

2.所有事务共享 :需要手动配置开启

分页插件

1.手写limit语句

SELECT * FROM table_name LIMIT offset, pageSize;

offset:跳过的记录数((pageNum - 1) * pageSize

pageSize:每页显示的记录数

2.使用PageHelper插件

导入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.2</version>
</dependency>

service层

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
​
public PageInfo<User> getUsersByPage(int pageNum, int pageSize) {
    PageHelper.startPage(pageNum, pageSize);  // 开启分页
    List<User> users = userMapper.getAllUsers();  // 查询所有
    return new PageInfo<>(users);  // 封装分页信息
}

controller层实现


MybatisPlus

适合单表查询,多表查询还是自己编写更好

快速入门

导入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.10.1</version>
</dependency>

在接口继承BaseMaper<T>

注入依赖.就可调用各种单表查询的方法

MybatisPlus通过扫描实体类,基于反射获取实体类信息作为数据库表信息

  • 类名驼峰转下划线作为表名

  • 名为id的字段作为主键

  • 变量名驼峰转下划线作为表的字段名

常用注解

@TableName: 用来指定表名

@TableId: 用来指定表中的主键字段

@TableField: 用来指定表中的普通字段信息

成员变量名和数据库字段名不一致

成员变量名以is开头,且是布尔值

成员变量名和数据库关键字冲突

成员变量不是数据库字段@TableField(exist = false)

常见配置

核心功能

条件构造器Wrapper

基于QueryWrapper的查询

1.查询出名字中带o的,存款大于等于1000元的人的id,username,info,balance字段
     @Test
    public void testQueryWrapper(){
        //构建查询对象
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<User>().select("id", "username", "info", "balance")
                .like("username", "o")
                .gt("balance", 1000);
        //查询
        List<User> users = userMapper.selectList(userQueryWrapper);
        log.info("users = " + users);
        users.forEach(System.out::println);
    }
2.更新用户名为jack的用户工资 为2k
    @Test
    public void testUpdateByQueryWrapper(){
        User user = new User();
        user.setBalance(20000);
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "jack");
        userMapper.update(user,wrapper);
    }

基于UpdateWrapper的查询

1.把id为 1,2,4的用户工资减200
    @Test
    public void testReduceUserBalance(){
        List<Long> ids = List.of(1L, 2L,  4L);
        UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance =  balance-200")
                                                            .in("id",ids);
        userMapper.update(null,wrapper);
    }

使用lambdaQueryWrapper和LambdaUpdateWrapper

 @Test
    public void testQueryWrapper(){
        //构建查询对象
        LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<User>().select(User::getId,User::getUsername,User::getInfo,User::getBalance)
                .like(User::getUsername, "o")
                .gt(User::getBalance, 1000);
        //查询
        List<User> users = userMapper.selectList(userQueryWrapper);
        
        users.forEach(System.out::println);
    }

自定义SQL

1.通过注解方式编写自定义sql

public interface UserMapper extends BaseMapper<User> {
​
    @Select("SELECT * FROM user WHERE age > #{age}")
    List<User> selectByAge(int age);
}

@Select 注解可以用于执行 查询操作

你也可以使用 @Insert@Update@Delete 注解来编写相应的操作 SQL。

利用mp构建复杂的where条件,然后剩下的sql语句自己写

IService接口
创建一个继承自IService的接口

public interface IUserService extends IService<User> {
    void deductBalanceById(Long id, Integer money);//可自定义方法
}
创建接口的实现类,还要继承ServiceImpl<UserMapper,User>

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
​
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 通过继承 ServiceImpl,就不需要显式实现 CRUD 方法
    @Override
    public User getUserByEmail(String email) {
        return baseMapper.selectOne(new QueryWrapper<User>().eq("email", email));
    }
}

实际开发中,使用IService接口更常见

猜你喜欢

转载自blog.csdn.net/2402_86577162/article/details/146279038
今日推荐