Mybatis学习笔记

1、概述

1.1 MyBatis

MyBatis是持久层框架,用于简化JDBC的开发。

官网:https://mybatis.org/mybatis-3/zh/index.html

使用Mybatis操作数据库,就是在Mybatis中编写SQL查询代码,发送给数据库执行,数据库执行后返回结果。

1.2 预编译SQL

性能更高更安全,能防止sql注入

SQL注入是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的目的。由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。

  1. 编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译。(只是输入的参数不同)
  2. 将敏感字进行转义
1.3 参数占位符
  • #{...} :执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值

    使用时机:参数传递,都使用#{…}

  • ${...}:拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题

    使用时机:如果对表名、列表进行动态设置时使用

2、配置说明

2.1 application.properties
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/数据库名
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234
#指定mybatis输出日志的位置, 输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 开启驼峰命名;如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.mapper-locations: classpath*:mapper/*.xml
2.2 数据库连接池(Druid)

官方地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

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

客户端执行SQL语句:要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行后又需要关闭连接对象从而释放资源,每次执行SQL时都需要创建连接、销毁链接,这种频繁的重复创建销毁的过程是比较耗费计算机的性能。

数据库连接池是个容器,负责分配、管理数据库连接(Connection)

程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象;允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;

客户端在执行SQL时,先从连接池中获取一个Connection对象,然后在执行SQL语句,SQL语句执行完之后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用);

释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏;客户端获取到Connection对象了,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池就会自动释放掉这个连接对象

官方(sun)提供了数据库连接池标准(javax.sql.DataSource接口);第三方组织必须按照DataSource接口实现:C3P0、DBCP、Druid、Hikari (springboot默认)

<dependency>
    <!-- Druid连接池依赖 -->
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
</dependency>
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/mybatis
spring.datasource.druid.username=root
spring.datasource.druid.password=1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
spring.datasource.username=root
spring.datasource.password=1234
public Connection getConnection() throws SQLException;  //获取连接 
2.3 lombok

一个实用的Java类库

通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高效率。

注解 作用
@Getter/@Setter 为所有的属性提供get/set方法
@ToString 会给类自动生成易阅读的 toString 方法
@EqualsAndHashCode 根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法
@Data 提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode)
@NoArgsConstructor 为实体类生成无参的构造器方法
@AllArgsConstructor 为实体类生成除了static修饰的字段之外带有各参数的构造器方法。
<!-- 在springboot的父工程中,已经集成了lombok并指定了版本号,故当前引入依赖时不需要指定version -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

Lombok会在编译时,会自动生成对应的java代码

在使用lombok时,还需要安装一个lombok的插件(新版本的IDEA中自带)

2.4 解决SQL警告与提示
  1. 在Idea中配置MySQL数据库连接时指定数据库可以识别表名(列名)
  2. 在@Select注解中编写SQL语句时自动提示功能:右键选择sql语句-->show context Actions-->inject language or reference---> MySQL(sql)
2.5 MybatisX

MybatisX是一款基于IDEA的快速开发Mybatis的插件,为效率而生。

2.6 数据封装

实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装;如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。

解决方案:起别名、结果映射、开启驼峰命名


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

@Select("select id, username, password, name, gender, image, job, entrydate, " +
        "dept_id AS deptId, create_time AS createTime, update_time AS updateTime " +
        "from emp " +
        "where id=#{id}")
public Emp getById(Integer id);

手动结果映射:通过 @Results及@Result 进行手动结果映射

@Results({@Result(column = "dept_id", property = "deptId"),
          @Result(column = "create_time", property = "createTime"),
          @Result(column = "update_time", property = "updateTime")})
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")
public Emp getById(Integer id);

开启驼峰命名(推荐):

  1. 如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射
  2. 实体类的属性 与 数据库表中的字段名严格遵守驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
2.7 参数名说明

条件查询中,需要保证接口中方法的形参名和SQL语句中的参数占位符名相同,否则会出现not found

参数名在不同的SpringBoot版本中,处理方案不同:

在springBoot的1.x版本单独使用mybatis,编译生成的字节码文件中,不会保留Mapper接口中方法的形参名称,而是使用var1、var2...这样的形参名字,此时要获取参数值时,就要通过@Param注解来指定SQL语句中的参数名

public List<Emp> list(String var1,SHort var2,LocalDate var3,LocalDate var4);
@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and " + 
	"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> list(@Param("name")string name,@Param("gender")Short gender, @Param("begin")LocalDate begin,@Param("end")localDate end);

在springBoot的2.x版本中(保证参数名一致),springBoot的父工程对compiler编译插件进行了默认的参数parameters配置,使得在编译时,会在生成的字节码文件中保留原方法形参的名称,所以#{…}里面可以直接通过形参名获取对应的值

@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and " + 
	"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> list(string name,Short gender, LocalDate begin,localDate end);

3、注解说明

@Mapper
  1. 表示当前接口是mybatis中的Mapper接口
  2. 程序运行时框架会自动生成接口的实现类对象(代理对象)并给交Spring的IOC容器管理
@Select
  1. select查询,后面书写select查询语句
@Delete
  1. 编写delete操作的SQL语句
  2. 如果mapper接口方法形参只有一个普通类型的参数,#{…} 里面的属性名可以随便写,但是建议保持与形参一致。
@Options[主键返回]

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

@Mapper
public interface EmpMapper {
    //会自动将生成的主键值,赋值给emp对象的id属性
    @Options(useGeneratedKeys = true,keyProperty = "id")
    @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
    public void insert(Emp emp);
}
@Test
public void testInsert(){
    Emp emp = new Emp();
    emp.setUsername("jack");
    emp.setDeptId(1);
    //调用添加方法
    empMapper.insert(emp);
	// 返回主键
    System.out.println(emp.getDeptId());
    System.out.println(emp.getId());
}
@Results和@Result
  1. 实体类属性名和数据库表查询返回的字段名不一致时进行映射
@Results({@Result(column = "dept_id", property = "deptId"),
          @Result(column = "create_time", property = "createTime"),
          @Result(column = "update_time", property = "updateTime")})
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")
public Emp getById(Integer id);
@SpringBootTest
  1. 代表该测试类已经与SpringBoot整合,测试类在运行时会自动通过引导类加载Spring的环境(IOC容器)。
  2. 可以通过@Autowired注解注入需要测试的bean对象进行测试
@SpringBootTest
public class MybatisQuickstartApplicationTests {
    @Autowired
    private UserMapper userMapper;
    @Test
    public void testList(){
        List<User> userList = userMapper.list();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}
@Param
  1. Spring1.x版本时,写在形参中,用来指定SQL语句中的参数名
@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and " + 
	"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> list(@Param("name")string name,@Param("gender")Short gender, @Param("begin")LocalDate begin,@Param("end")localDate end);

4、注解开发

创建实体类时LocalDate类型对应数据表中的date类型;LocalDateTime类型对应数据表中的datetime类型

@Mapper
public interface EmpMapper {

    // 根据id删除数据
    @Delete("delete from emp where id = #{id}")//使用#{key}方式获取方法中的参数值
    public void delete(Integer id);
    
    // 新增员工信息,主键返回
    @Options(useGeneratedKeys = true,keyProperty = "id")
    @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
    public void insert(Emp emp);   // #{...} 里面写的名称是对象的属性名
    
    // 根据id修改员工信息
    @Update("update emp set username=#{username}, name=#{name}, gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entrydate}, dept_id=#{deptId}, update_time=#{updateTime} where id=#{id}")
    public void update(Emp emp);
    
    // 根据id查询员工信息
    @Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")
    public Emp getById(Integer id);
    
    // 模糊查询
    @Select("select * from emp " +
            "where name like concat('%',#{name},'%') " +
            "and gender = #{gender} " +
            "and entrydate between #{begin} and #{end} " +
            "order by update_time desc")
    public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
    
}

5、XML开发【重点】

在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:

  1. XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
  2. XML映射文件的namespace属性为Mapper接口全限定名一致
  3. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致

resultType属性指查询返回的单条记录所封装的类型。

5.1 dtd约束+例子
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lmcode.mapper.EmpMapper">

    <!--查询操作-->
    <select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        where name like concat('%',#{name},'%')
              and gender = #{gender}
              and entrydate between #{begin} and #{end}
        order by update_time desc
    </select>
</mapper>
5.2 动态sql

条件不是写死的,应该传递参数后再组装这个查询条件;如果没有传递参数就不应该组装这个查询条件。

情景:

搜索姓名带有"张"的员工:

select * from emp where name like '%张%' order by update_time desc;

搜索姓名带有"张"的男性员工:

select * from emp where name like '%张%' and gender = 1 order by update_time desc;

<sql>:定义可重用的SQL片段

<include>:通过属性refid,指定包含的SQL片段

<if>用于判断条件是否成立,如果条件为true,则拼接SQL

<where>只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR

<set>动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)

</foreach>遍历方法中传递的集合

@Mapper
public interface EmpMapper {
    public void update(Emp emp);
    public void deleteByIds(List<Integer> ids);
}
<sql id="commonSelect">
 	id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
</sql>

<select id="list" resultType="com.itheima.pojo.Emp">
    select <include refid="commonSelect"/> 
    from emp
    <where>
        <!-- if做为where标签的子元素 -->
        <if test="name != null">
            and name like concat('%',#{name},'%')
        </if>
        <if test="gender != null">
            and gender = #{gender}
        </if>
        <if test="begin != null and end != null">
            and entrydate between #{begin} and #{end}
        </if>
    </where>
    order by update_time desc
</select>

<update id="update">
    update emp
    <set>
        <if test="username != null">
            username=#{username},
        </if>
        <if test="name != null">
            name=#{name},
        </if>
        <if test="gender != null">
            gender=#{gender},
        </if>
        <if test="image != null">
            image=#{image},
        </if>
        <if test="job != null">
            job=#{job},
        </if>
        <if test="entrydate != null">
            entrydate=#{entrydate},
        </if>
        <if test="deptId != null">
            dept_id=#{deptId},
        </if>
        <if test="updateTime != null">
            update_time=#{updateTime}
        </if>
    </set>
    where id=#{id}
</update>

<delete id="deleteByIds">
    delete from emp where id in
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>

6、图片

XML配置查询

XML配置查询

XML映射文件

XML映射文件

预编译SQL

预编译SQL

posted @ 2024-03-28 21:34  燕子去了  阅读(2)  评论(0编辑  收藏  举报

我会翻山越岭,到每一个我想去的地方

...