Mybatis学习

写在前面的话

MyBatis是MVC(Model-View-Controller)中的Model层,在java的企业级开发中扮演极其重要的角色,这是在java日常开发和维护中频繁接触的对象,它可能会结合MyBatis-Plus进行对数据库的操作,这两者相辅相成。所以说了解MyBatis对我们的开发和维护是很大的帮助。
接下来我们来对MyBatis进行全面的了解。
这里先挖个坑,后续会出一期MyBatis-Plus的文章

一、MyBatis简介

MyBatis是ORM框架,即对象关系映射框架。

二、搭建MyBatis

不同的MySQL使用的JDBC不同

String dirver_name = "com.mysql.jdbc.Driver"//MySQL8.0之前
String driver_name = "com.mysql.cj.jdbc.Driver" //MySQL8.0及之后

不同版本的MySQL的url也不同

String url = "jdbc:mysql://localhost:3306/database" //MySQL8.0之前
String url = "jdbc:mysql://localhost:3306/database?serverTimezone=UTC" //MySQL8.0及之后

三、快速开始

1、引入相关依赖

<dependencies>
    <!-- MyBatis 核心 -->
    <dependency>
        <groupId>org.MyBatis</groupId>
        <artifactId>MyBatis</artifactId>
        <version>3.5.15</version>
    </dependency>

    <!-- junit 测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>

    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

2、编写MyBatis配置文件

作用:1. 配置目标数据库的的环境

​ 2. 配置MyBatis的存放路径 //src/main/resources

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//MyBatis.org//DTD Config 3.0//EN"
        "https://MyBatis.org/dtd/MyBatis-3-config.dtd">
<configuration>
    <!-- 配置连接数据库的环境 -->
    <environments default="development">
        <!-- 可以定义多个环境 -->
        <environment id="development">
            <!-- 定义事务管理器类型 -->
            <transactionManager type="JDBC"/>
            <!-- 数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
                <property name="username" value="lukeewin"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 引入MyBatis的映射文件 -->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>

3、Mapper接口

相当于DAO(Data Access Object),但是不用创建实现类,MyBatis底层会自动创建代理类来执行映射文件当中的SQL,实现对数据库中的数据访问。

起名规则为:“POJO”+Mapper

例如:下面的这段代码,User为POJO包下的实体类

public interface UserMapper {

    int insertUser();
}

4、创建MyBatis的映射文件

Mapper 接口当中的一个抽象方法对应映射文件当中的一个SQL语句。

起名规则:POJO名字 + Mapper.xml

放置位置:src/main/resources/mappers/UserMapper.xml

映射关系:

Java概念数据库概念
属性字段【列】
对象记录【行】
<?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="top.lukeewin.mybatis.mapper.UserMapper">
    <!--
        mapper 接口和映射文件要保持两个一致
        1. mapper 接口的全类名和映射文件的 namespace 一致
        2. mapper 接口中的方法的方法名要和映射文件中的 sql 的 id 保持一致
    -->
    <!-- int insertUser() -->
    <insert id="insertUser">
        insert into t_user values(null, 'admin', '123456', 22, '男', '123456@qq.com')
    </insert>
</mapper>

测试

openSession() 获得 SqlSession默认是不会自动提交事务,因此需要自己手动提交。

openSession(true) 获得 SqlSession默认自动提交事务。

public class MyBatisTest {

    @Test
    public void testInsert() throws IOException {
        // 1. 获取核心配置文件的输入流
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        // 2. 获取 SqlSessionFactoryBuilder 对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 3. 获取 SqlSessionFactory 对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        // 4. 获取 sql 的会话对象 SqlSession ,是 MyBatis 提供的操作数据库的对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 5. 获取 UserMapper 的代理实现类对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 6. 调用 mapper 接口中的方法,实现添加用户信息的功能
        int result = mapper.insertUser();
        System.out.println("结果:" + result);
        // 7. 提交事务,事务是不会自动提交的,需要手动提交事务
        sqlSession.commit();
        // 8. 关闭 sqlSession,最后一定要记得关闭会话
        sqlSession.close();
    }
}

6、添加日志功能

<!-- log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

resources/log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5d %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n"/>
        </layout>
    </appender>

    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis" >
        <level value="info"/>
    </logger>

    <!-- 默认配置,级别为debug 且根据name为log.console和 log.file两个appender输出-->
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

在MyBatis核心配置文件中设置开启日志功能

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

四、核心配置文件详解

核心配置文件必须按照一定的顺序写,否则会报错。

properties,settings,typeAliases,typeHandlers,
objectFactory,objectWrapperFactory,reflectorFactory,
plugins,environments,databaseIdProvider,mappers

1、引入properties文件

使用properties标签引入properties文件,此后可以在当前文件中使用${key}的方式访问properties中的value

<properties resource="jdbc.properties"/>

2、起别名

使用typeAliases标签可以给实体类起别名。

方式一:可以使用typeAlias中的type的值是指定给哪个类起别名,而alias的值是指别名,如果不写alias,那么有默认别名,默认别名为不区分大小写的类名。

方式二:可以使用package指定给哪个包路径下的类起别名。

<typeAliases>
        <!--
            alias的值为别名,可以不设置,如果不设置则有默认的别名,默认别名分为类名且不区分大小写
         -->
        <!--<typeAlias type="top.lukeewin.mybatis.pojo.User" alias="abc"/>-->
        <!--<typeAlias type="top.lukeewin.mybatis.pojo.User"/>-->
        <!-- 通过包设置类型别名,则指定包下的所有类将拥有默认别名 -->
        <package name="top.lukeewin.mybatis.pojo"/>
</typeAliases>

3、配置连接数据库环境

可以定义多个环境,切换时在environments中通过default来指定

<environments default="development">
        <!-- 可以定义多个环境 -->
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!-- 数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

        <environment id="test">
            <!-- 定义事务管理器类型 -->
            <transactionManager type="JDBC"/>
            <!-- 数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
                <property name="username" value="lukeewin"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
</environments>

transactionManager: 定义事务管理器

type: 指定使用哪种事务管理器,有JDBC和MANGAED两种。

其中JDBC是原生的事务管理方式。

MANGAED是被管理的意思,比如被Spring管理。

dataSource: 数据源

type: 设置数据源的类型

type=“POOLED|UNPOOLED|JNDI”

POOLED: 表示使用数据库连接池

UNPOOLED: 不使用数据库连接池

JNDI: 表示使用上下文中的数据源

4、引入映射文件

有两种方式:

方式一:单个文件引入,不推荐。

方式二:通过包路径方式引入,推荐。

通过包路径的方式引入需要注意包路径必须和 mapper 接口路径一致,并且名字也必须要和 mapper接口的名字一致。

<mappers>
    <!--<mapper resource="mappers/UserMapper.xml"/>-->
    <!--
        以包的方式引入映射文件,但是必须要满足两个条件:
        1. mapper 接口和映射文件所在的包名必须一致
        2. mapper 接口的名字和映射文件的名字必须一致
    -->
    <package name="top.lukeewin.mybatis.mapper"/>
</mappers>

五、MyBatis获取参数的方式

有两种方式:${}和#{}

${}字符串拼接

#{}占位符

推荐使用#{}

因为占位符能避免SQL注入,同时可以为字符串和日期自动添加单引号。

1、单个字面量类型

若 mapper 接口方式的参数为单个的字面量类型(字符串、基本数据类型、包装类)

#{}${}:任意内容

注意:${}不会自动添加单引号

User getUserByUsername(String username);
<select id="getUserByUsername" resultType="User">
	select * from t_user where username = #{username}
</select>
<select id="getUserByUsername" resultType="User">
	select * from t_user where username = '${username}'
</select>

2、多个字面量类型

当 Mapper 接口中有多个参数的时候,MyBatis 会创建Map ,并使用 paramX 或 argX 为Key,参数值为 Value , 且两者均可混合使用

User checkLogin(String username, String password);
<select id="checkLogin" resultType="User">
	select * from t_user where username = #{arg0} and password = #{arg1}
</select>
<select id="checkLogin" resultType="User">
	select * from t_user where username = #{param1} and password = #{param2}
</select>

3、以Map集合的方式传值

/**
 * {username: "xxxx"}
 * {password: "xxxx"}
 * @param map
 * @return
 */
User checkLoginByMap(Map<String, Object> map);
<!-- User checkLoginByMap(Map<String, Object> map) -->
<select id="checkLoginByMap" resultType="user">
    SELECT * FROM t_user WHERE username = #{username} and password = #{password};
</select>

4、参数为实体类(常用)

通过#{}或${}访问实体类中的属性

<!-- void insertUser(User user) -->
<insert id="insertUser">
	insert into t_user values(null, #{username}, #{password}, #{age}, #{gender}, #{email})
</insert>

5、通过@Param注解方式传参(常用)

在 mapper 接口中的方法形参中添加@Param注解指定map中的key

会替换原来的argX,以@Param中的值作为map中的key

User checkLoginByParam(@Param("username") String username, @Param("password") String password);
<select id="checkLoginByParam" resultType="User">
	select * from t_user where username = #{username} and password = #{password}
</select>
<select id="checkLoginByParam" resultType="User">
	select * from t_user where username = #{param1} and password = #{param2}
</select>

推荐:除了实体类型为,推荐使用@Param

六、MyBatis中的各种查询功能

User getUserById(@Param("id") Integer id);
<select id="getUserById" resultType="User">
	select * from t_user where id = #{id}
</select>

如返回了多条记录,则会报错。

2、返回多条记录

若SQL语句查询的结果为多条时,一定不能使用实体类型接收,否则会报异常。

要有集合接收。

若SQL语句查询的结果返回一条时,可以使用集合接收。

<!-- List<User> getAllUser() -->
<select id="getAllUser" resultType="User">
    select * from t_user
</select>

3、查询单个数据

类型别名

java.lang.Integer-->int|integer
int-->_int|_integer
Map-->map
List-->list

4、查询一条记录

查询的结果没有对应的实体类的时候,就可以使用Map集合。

resultType 设置成 map 即可。

查询为null 的字段是不会放到Map集合里面。

Map<String, Object> getUserByIdToMap(@Param("id") Integer id);
<!-- Map<String, Object> getUserByIdToMap(@Param("id") Integer id) -->
<select id="getUserByIdToMap" resultType="map">
	select * from t_user where id = #{id}
</select>

5、查询多条记录

5.1、使用List集合

把Map放到List集合中

List<Map<String, Object>> getAllUserToMap();
<select id="getAllUserToMap" resultType="map">
    select * from t_user
</select>
5.2、使用@MapKey

可以将每条数据转换为 map 集合放在一个大的 map 中,但必须要通过 @MapKey 注解将查询的某个字段的值作为大的 map 的键。

@MapKey("id")
Map<String, Object> getAllUserToMap();
<select id="getAllUserToMap" resultType="map">
    SELECT * FROM t_user
</select>
{2={password=123456, gender=, id=2, age=23, email=123456@qq.com, username=admin},
4={password=123456, gender=, id=4, age=23, email=123456@qq.com, username=admin}, 
5={password=123456, gender=, id=5, age=23, email=123456@qq.com, username=admin},
6={password=123456, gender=, id=6, age=23, email=123456@qq.com, username=admin},
7={password=123456, gender=, id=7, age=23, email=123456@qq.com, username=admin},
8={password=123456, gender=, id=8, age=23, email=123456@qq.com, username=admin},
9={password=123456, gender=, id=9, age=23, email=123456@qq.com, username=admin},
10={password=123456, gender=, id=10, age=23, email=123456@qq.com, username=admin},
11={password=123456, gender=, id=11, age=23, email=123456@qq.com, username=admin},
12={password=123456, gender=, id=12, age=23, email=123456@qq.com, username=admin},
15={password=123456, gender=, id=15, age=25, email=12345678@qq.com, username=赵丽颖},
16={password=123, gender=, id=16, age=25, email=12345678@qq.com, username=迪丽热巴},
17={password=123456, gender=, id=17, age=25, email=12345678@qq.com, username=高圆圆},
18={password=123456, gender=, id=18, age=23, email=123456@qq.com, username=admin},
19={password=123456, gender=, id=19, age=33, email=123456@qq.com, username=root}}

6、模糊查询

有三种方案

方案一:使用${}

方案二:使用concat(’%’, #{mohu}, ‘%’)函数进行字符串拼接

方案三:使用"%“#{mohu}”%"

<!-- List<User> getUserByLike(@Param("mohu") String mohu -->
<select id="getUserByLike" resultType="User">
    <!--select * from t_user where username like '%${mohu}%'-->
    <!--select * from t_user where username like concat('%', #{mohu}, '%')-->
    select * from t_user where username like "%"#{mohu}"%"
</select>

七、批量删除

<!-- void deleteUserByBatch(@Param("ids") String ids) -->
<delete id="deleteMoreUser">
	delete from t_user where id in(${ids})
</delete>

八、动态设置表名

表名不能用引号,所以不能用#{}

<!-- List<User> getUserList(@Param("tablename") String tablename) -->
<select id="getUserList" resultType="User">
    select * from ${tablename}
</select>

九、获取自增主键

void insertUser()返回的是受影响的行数,不能使用返回值返回主键

useGeneratedKeys:表示是否使用自增主键

keyProperty:指定获取到的主键存储到实体中的哪个属性中

void insertUser(User user);
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
    insert into t_user values(null, #{username}, #{password}, #{age}, #{gender}, #{email})
</insert>
@Test
public void testInsertUser() {
	SqlSession sqlSession = SqlSessionUtil.getSqlSession();
	SpecialSQLMapper mapper = sqlSession.getMapper(SpecialSQLMapper.class);
	User user = new User(null, "xiaoming", "123456", 23, "男", "123456@qq.com");
	mapper.insertUser(user);
	System.out.println(user);
}

十、自定义映射

字段名和属性名不一致

方式一:使用别名

Emp getEmpByEmpId(@Param("empId") Integer empId);
<select id="getEmpByEmpId" resultType="Emp">
    select emp_id empId, emp_name empName, age, gender from t_emp where emp_id = #{empId}
</select>

方式二:在 MyBatis核心配置文件中配置字段下划线自动转属性驼峰

<settings>
  <!-- 下划线 自动映射 驼峰 -->
  <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

方式三:使用resultMap

id: 唯一标识

type: 映射到哪个实体类

id标签: 主键的映射关系

result标签: 普通字段和属性之间的映射关系

column: 查询出来对应的字段名

property: 映射到实体中的属性名

<resultMap id="empResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
</resultMap>

<select id="getEmpByEmpId" resultMap="empResultMap">
    select * from t_emp where emp_id = #{empId}
</select>

1、多对一

多个emp对一个dep

public class Emp {
    private Integer empId;
    private String empName;
    private Integer age;
    private String gender;

    private Dept dept;
    
    // getter/setter/toString
}
Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);
1.1、使用级联的方式
<resultMap id="empResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
    <result column="dept_id" property="dept.deptId"/>
    <result column="dept_name" property="dept.deptName"/>
</resultMap>

<!-- Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId) -->
<select id="getEmpAndDeptByEmpId" resultMap="empResultMap">
    select t_emp.*, t_dept.dept_name
    from t_emp
    left join t_dept
    on t_emp.dept_id = t_dept.dept_id
    where t_emp.emp_id = #{empId}
</select>
1.2、使用association标签

association:专门处理多对一的映射关系(处理实体类类型的属性)

property:属性名

javaType:属性的类型

<resultMap id="empResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
    <association property="dept" javaType="Dept">
        <id column="dept_id" property="deptId"/>
        <result column="dept_name" property="deptName"/>
    </association>
</resultMap>
1.3、分步查询

property:属性名

select:第二步查询的唯一标识

column:上一步查询出来的字段传递到下一步

注意:必须要在 MyBatis 核心配置文件中开启下划线自动映射驼峰,如果不开启,那么获取不了部门信息

/**
 * 通过分步查询来查询员工以及员工所对应的部门信息的第一步
 * @param empId
 * @return
 */
Emp getEmpAndDeptByEmpIdOne(@Param("empId") Integer empId);
/**
 * 通过分步查询员工以及员工所对应的部门信息的第二步
 * @param deptId
 * @return
 */
Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId);

第一步:

<resultMap id="empAndDeptByStepResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
    <association property="dept"
                 select="top.lukeewin.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                 column="dept_id"/>
</resultMap>
<!-- Emp getEmpAndDeptByEmpIdOne(@Param("empId") Integer empId) -->
<select id="getEmpAndDeptByEmpIdOne" resultMap="empAndDeptByStepResultMap">
    select * from t_emp where emp_id = #{empId}
</select>
<!-- Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId) -->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
    select * from t_dept where dept_id = #{dept_id}
</select>
1.3.1、延迟加载

在 MyBatis 核心配置文件中开启延迟加载

全局性

aggressiveLazyLoading和lazyLoadingEnabled共同决定延迟加载

<settings>
    <!-- 开启延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 按需加载 -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

局部性

fetchType=“eager|lazy”

eager:立即迟加载

lazy:延迟加载

<resultMap id="empAndDeptByStepResultMap" type="Emp">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="age" property="age"/>
    <result column="gender" property="gender"/>
    <association property="dept"
                 select="top.lukeewin.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                 column="dept_id"
                 fetchType="lazy"
    />
</resultMap>

2、一对多

2.1、使用collection标签

collection:一对多(集合)

ofType:对应集合中泛型的类型

public class Dept {

    private Integer deptId;

    private String deptName;

    private List<Emp> emps;
    
    // 省略构造器,setter,getter,toString方法
}
Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);
<resultMap id="empResultMap" type="Dept">
    <id column="dept_id" property="deptId"/>
    <result column="dept_name" property="deptName"/>
    <collection property="emps" ofType="Emp">
        <id column="emp_id" property="empId"/>
        <result column="emp_name" property="empName"/>
        <result column="age" property="age"/>
        <result column="gender" property="gender"/>
    </collection>
</resultMap>
<!-- Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId) -->
<select id="getDeptAndEmpByDeptId" resultMap="empResultMap">
    select *
    from t_dept
    left join t_emp
    on t_dept.dept_id = t_emp.dept_id
    where t_dept.dept_id = #{deptId}
</select>
2.2、使用分步查询

先根据传递过来的部门 id 在部门表中查询,如果查到了就根据查询出来的部门 id 在用户表中查询对应有哪些用户

DeptMapper.java

/**
 * 通过分步查询查询部门以及部门的员工信息第一步
 * @param deptId
 * @return
 */
Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);

EmpMapper.java

/**
 * 通过分步查询查询部门以及部门的员工信息第二步
 * @param deptId
 * @return
 */
List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);

DeptMapper.xml

<resultMap id="deptAndEmpResultMapByStep" type="Dept">
    <id column="dept_id" property="deptId"/>
    <result column="dept_name" property="deptName"/>
    <collection property="emps"
                select="top.lukeewin.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                column="dept_id"/>
</resultMap>
<!-- Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId) -->
<select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpResultMapByStep">
    select * from t_dept where dept_id = #{deptId}
</select>

EmpMapper.xml

<!-- List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId) -->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
    select * from t_emp where dept_id = #{deptId}
</select>

3、多对多

多对多

十一、动态SQL

1、if

通过 test 属性中的表达式判断标签中的内容是否有效(是否会拼接到 sql 中)

/**
 * 根据条件查询员工信息
 * @param emp
 * @return
 */
List<Emp> getEmpByCondition(Emp emp);
<!-- List<Emp> getEmpByCondition(Emp emp) -->
<select id="getEmpByCondition" resultType="Emp">
    select *
    from t_emp
    where
    <if test="empName != null and empName != ''">
        emp_name = #{empName}
    </if>
    <if test="age != null and age != ''">
        and age = #{age}
    </if>
    <if test="gender != null and gender != ''">
        and gender = #{gender}
    </if>
</select>

2、where

where 1 = 1

<!-- List<Emp> getEmpByCondition(Emp emp) -->
<select id="getEmpByCondition" resultType="Emp">
    select *
    from t_emp
    where 1 = 1
    <if test="empName != null and empName != ''">
        and emp_name = #{empName}
    </if>
    <if test="age != null and age != ''">
        and age = #{age}
    </if>
    <if test="gender != null and gender != ''">
        and gender = #{gender}
    </if>
</select>

作用:可以自动去掉内容前多余的 and ,不会自动去掉内容后多余的 and

<!-- List<Emp> getEmpByCondition(Emp emp) -->
<select id="getEmpByCondition" resultType="Emp">
    select *
    from t_emp
    <where>
        <if test="empName != null and empName != ''">
            emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            and age = #{age}
        </if>
        <if test="gender != null and gender != ''">
            and gender = #{gender}
        </if>
    </where>
</select>

3、trim

prefix:在前面添加内容

suffix:在后面添加内容

prefix:覆盖前面的内容

suffixOverrives:覆盖后面的内容

<!-- List<Emp> getEmpByCondition(Emp emp) -->
<select id="getEmpByCondition" resultType="Emp">
    select *
    from t_emp
    <trim prefix="where" suffixOverrides="and">
        <if test="empName != null and empName != ''">
            emp_name = #{empName} and
        </if>
        <if test="age != null and age != ''">
            age = #{age} and
        </if>
        <if test="gender != null and gender != ''">
            gender = #{gender}
        </if>
    </trim>
</select>

4、choose、when、otherwise

类似 if-else if-else

多选一

<!-- List<Emp> getEmpByChoose(Emp emp) -->
<select id="getEmpByChoose" resultType="Emp">
    select * from t_emp
    <where>
        <choose>
            <when test="empName != null and empName != ''">
                emp_name = #{empName}
            </when>
            <when test="age != null and age != ''">
                age = #{age}
            </when>
            <when test="gender != null and gender != ''">
                gender = #{gender}
            </when>
        </choose>
    </where>
</select>

5、foreach

作用:批量添加或批量删除

collection:设置要循环的数组或集合

item:要循环的每一个元素

separator:分隔符

open:以什么开头

close:以什么结束

index:索引

批量添加

<!-- void insertMoreEmp(@Param("emps") List<Emp> emps) -->
<insert id="insertMoreEmp">
    insert into t_emp values
    <foreach collection="emps" item="emp" separator=",">
        (null, #{emp.empName}, #{emp.age}, #{emp.gender}, null)
    </foreach>
</insert>

批量删除

方式一:

<!-- void deleteMoreEmp(@Param("empIds") Integer[] empIds) -->
<delete id="deleteMoreEmp">
    delete from t_emp where emp_id in
    <foreach collection="empIds" item="empId" separator="," open="(" close=")">
        #{empId}
    </foreach>
</delete>

方式二:

<!-- void deleteMoreEmp(@Param("empIds") Integer[] empIds) -->
<delete id="deleteMoreEmp">
    delete from t_emp where
    <foreach collection="empIds" item="empId" separator="or">
        emp_id = #{empId}
    </foreach>
</delete>

6、sql

作用:将重复的 SQL 代码抽取出来被多条语句重复使用

include 标签中的 refid 指定要使用 SQL 代码段的 id

<sql id="empColumns">
    emp_id,emp_name,age,gender,dept_id
</sql>
<!-- List<Emp> getEmpByCondition(Emp emp) -->
<select id="getEmpByCondition" resultType="Emp">
    select <include refid="empColumns"/>
    from t_emp
    <trim prefix="where" suffixOverrides="and">
        <if test="empName != null and empName != ''">
            emp_name = #{empName} and
        </if>
        <if test="age != null and age != ''">
            age = #{age} and
        </if>
        <if test="gender != null and gender != ''">
            gender = #{gender}
        </if>
    </trim>
</select>

十二、MyBatis中的缓存

一共有两级缓存

默认开启一级缓存 SqlSession级别 通过同一个 SqlSession 执行的 SQL 语句会被缓存

1、一级缓存

通过同一个 sqlSession 对象查询同一个数据,会直接从缓存中获取

1.1、一级缓存失效的情况

SqlSessionFactory 级别,多个 SqlSession 可以共享

先保存到一级缓存中,之后一级缓存关闭之后才会被保存到二级缓存中

通过同一个 sqlSessionFactory 所获取的 sqlSession 对象查询的数据会被缓存

在通过同一个 sqlSessionFactory 所获取的 sqlSession 查询相同的数据会从缓存中获取

条件:

  • 在 MyBatis 核心配置文件中设置 cacheEnabled = “true”,默认为true
  • 在映射文件中设置标签<cache/>
  • 二级缓存必须在 sqlSession 关闭或提交之后有效
  • 查询的数据所转换的实体类类型必须实现Serializable接口
2.1、二级缓存失效的情况

两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

2.2、二级缓存的相关配置
2.3、整合第三方缓存
2.3.1、引入相关依赖
<!-- Mybatis EHCache整合包 -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
jar包名称作用
mybatis-ehcacheMybatis和EHCache的整合包
ehcacheEHCache核心包
slf4j-apiSLF4J日志门面包
logback-classic支持SLF4J门面接口的一个具体实现
2.3.2、添加logback.xml配置文件

注意:由于引入了logback依赖,所以log4j就失效了,我们必须要建立logback.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是: 时间、日志级别、线程名称、打印日志的类、日志主体内容、换行
            -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger]
                [%msg]%n</pattern>
        </encoder>
    </appender>
    <!-- 设置全局日志级别。日志级别按顺序分别是: DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT" />
    </root>
    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="top.lukeewin.mybatis.mapper" level="DEBUG"/>
</configuration>
2.3.3、添加ehcache.xml配置文件

位置:src/resources

<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="D:\Works\javalianxi\ehcache"/>
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>
2.3.4、修改映射文件

需要在映射文件中的cache标签中使用type属性指定使用第三方缓存,如果没有指定,则默认使用 MyBatis 自带的二级缓存

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

3、MyBatis缓存查询的顺序

  1. 先查询二级缓存,因为二级缓存当中可能会有其他线程已经查询出来的数据。
  2. 二级缓存没有命中,则再查询一级缓存。
  3. 一级缓存也没有命中,则执行查询数据库。
  4. SqlSession 关闭之后,一级缓存当中的数据会写入到二级缓存。

十三、MyBatis逆向工程

本质:代码生成器

在pom.xml中引入插件

<!-- 控制Maven在构建过程中相关配置 -->
<build>
    <!-- 构建过程中用到的插件 -->
    <plugins>
        <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.0</version>
            <!-- 插件的依赖 -->
            <dependencies>
                <!-- 逆向工程的核心依赖 -->
                <dependency>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-core</artifactId>
                    <version>1.3.2</version>
                </dependency>
                <!-- MySQL驱动 -->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.16</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

1、简单版本

src/resources下创建generatorConfig.xml配置文件

targetRuntime="MyBatis3Simple"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
    targetRuntime: 执行生成的逆向工程的版本
    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
    MyBatis3: 生成带条件的CRUD(奢华尊享版)
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"
                        userId="lukeewin"
                        password="123456">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="top.lukeewin.mybatis.pojo" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="top.lukeewin.mybatis.mapper" targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="top.lukeewin.mybatis.mapper" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>

双击maven中的plugins下的mybatis-generator下的mybatis-generator.generate即可生成代码。

2、复杂版本

targetRuntime=“MyBatis3”

xxxByExample通过条件进行操作

xxxSelective选择性进行操作,例如insertSelective选择性进行添加,如果某个字段为null那么,不会为这个字段赋值

updateByExampleSelective根据条件选择性修改,如果某个字段为null,那么不会修改该字段的值

updateByExample根据条件进行修改,如果某个字段为null,那么会修改该字段的值为null

2.1、测试

根据主键 id 来查询

@Test
public void testMBG() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = mapper.selectByPrimaryKey(1);
    System.out.println(emp);
}

查询所有数据

@Test
public void testMBG1() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    List<Emp> list = mapper.selectByExample(null);
    list.forEach(System.out::println);
}

根据条件来查询特定的数据

通过XxxExample对象中的createCriteria()后的方法来指定条件

 @Test
 public void testMBG2() {
     SqlSession sqlSession = SqlSessionUtil.getSqlSession();
     EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
     EmpExample empExample = new EmpExample();
     empExample.createCriteria().andEmpNameEqualTo("高圆圆");
     List<Emp> list = mapper.selectByExample(empExample);
     list.forEach(System.out::println);
 }

要使条件之间用or连接,xxxExample调用or()方法之后,然后拼接其它条件即可

empExample.createCriteria().andEmpNameEqualTo("高圆圆").andAgeGreaterThanOrEqualTo(22);
empExample.or().andGenderEqualTo("女");

测试普通修改

@Test
public void testMBG3() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = new Emp(2, "小明", null, "男");
    mapper.updateByPrimaryKey(emp);
}

测试选择性修改

@Test
public void testMBG4() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = new Emp(2, "小明", null, "男");
    mapper.updateByPrimaryKeySelective(emp);
}

十四、分页

添加依赖

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

配置分页插件

在 MyBatis 核心配置文件中配置

<plugins>
    <!--设置分页插件-->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

测试

@Test
public void test() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    // 要在查询之前开启分页功能
    PageHelper.startPage(1, 4);
    List<Emp> list = mapper.selectByExample(null);
    list.forEach(System.out::println);
}

分页信息保存在 page 对象中,更多的分页信息保存在 pageInfo 中

PageInfo<>(list, 5)第一个参数为查询出来的数据,第二个参数为导航分页

@Test
public void test() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    // 要在查询之前开启分页功能
    Page<Object> page = PageHelper.startPage(1, 4);
    List<Emp> list = mapper.selectByExample(null);
    // 查询功能之后可以获取分页相关的所有数据
    PageInfo<Emp> pageInfo = new PageInfo<>(list, 5);
    list.forEach(System.out::println);
    System.out.println(pageInfo);


ssion = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = new Emp(2, “小明”, null, “男”);
mapper.updateByPrimaryKeySelective(emp);
}


[外链图片转存中...(img-gfM8WOxI-1726998661873)]

## 十四、分页

添加依赖

```xml
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

配置分页插件

在 MyBatis 核心配置文件中配置

<plugins>
    <!--设置分页插件-->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

测试

@Test
public void test() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    // 要在查询之前开启分页功能
    PageHelper.startPage(1, 4);
    List<Emp> list = mapper.selectByExample(null);
    list.forEach(System.out::println);
}

分页信息保存在 page 对象中,更多的分页信息保存在 pageInfo 中

PageInfo<>(list, 5)第一个参数为查询出来的数据,第二个参数为导航分页

@Test
public void test() {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    // 要在查询之前开启分页功能
    Page<Object> page = PageHelper.startPage(1, 4);
    List<Emp> list = mapper.selectByExample(null);
    // 查询功能之后可以获取分页相关的所有数据
    PageInfo<Emp> pageInfo = new PageInfo<>(list, 5);
    list.forEach(System.out::println);
    System.out.println(pageInfo);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王路飞的挚友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值