Mybatis

2022-03-03  本文已影响0人  请叫我平爷

Mybatis

常见

#{} 和 ${}

Mybatis使用方式

Mapper类

public interface StudentMapper {
    List<Student> selectAll();
}

StudentMapper.xml

<select id="selectAll" resultMap="BaseResultMap">
    select `name`, code
    from student
</select>

Mapper类:

public interface StudentMapper{
    @Select("select * from student")
    List<Student> selectAll();
}

Mapper类:

public interface StudentMapper{
    @SelectProvider(type= StudentProvider.class,method="selectStudentById")
    Student selectStudentById(@Param(value="code") String code);
}

StudentProvider

public class StudentProvider {
    public String selectStudentById(Map<String, Object> para){
        return new SQL(){{
            SELECT("*");
            FROM("student");
            WHERE("code="+para.get("code"));
        }}.toString();
    }
}

获取自增长主键id

Mysql:

<insert id="insert" parameterType="com.mi.learn.mybatis.domin.Afinfo">
    <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into afinfo (`name`, age, birth, 
      sex, memo)
    values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{birth,jdbcType=DATE}, 
      #{sex,jdbcType=CHAR}, #{memo,jdbcType=VARCHAR})
</insert>
<insert id="insertAfinfo" parameterType="com.mi.learn.mybatis.domin.Afinfo" useGeneratedKeys="true" keyProperty="id">
    insert into afinfo (`name`, age, birth,
      sex, memo)
    values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{birth,jdbcType=DATE},
      #{sex,jdbcType=CHAR}, #{memo,jdbcType=VARCHAR})
</insert>

Oracle:

// 这个是创建表的自增序列
CREATE SEQUENCE student_sequence
INCREMENT BY 1
NOMAXVALUE
NOCYCLE
CACHE 10;

// 方式一,使用 `<selectKey />` 标签 + BEFORE
<insert id="add" parameterType="Student">
  <selectKey keyProperty="student_id" resultType="int" order="BEFORE">
      select student_sequence.nextval FROM dual
    </selectKey>
    
     INSERT INTO student(student_id, student_name, student_age)
     VALUES (#{student_id},#{student_name},#{student_age})
</insert>

// 方式二,使用 `<selectKey />` 标签 + AFTER
<insert id="save" parameterType="com.threeti.to.ZoneTO" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT SEQ_ZONE.CURRVAL AS id FROM dual
    </selectKey>
    
    INSERT INTO TBL_ZONE (ID, NAME ) 
    VALUES (SEQ_ZONE.NEXTVAL, #{name,jdbcType=VARCHAR})
</insert>

Mapper传递多个参数

Afinfo selectAfinfoWithMap(Map<String,Object> map);

<select id="selectAfinfoWithMap" parameterType="Map" resultType="com.mi.learn.mybatis.domin.Afinfo">
    select id, `name`, age, birth, sex, memo
    from afinfo
    where `name` = #{name} and age=#{age}
  </select>
Afinfo selectAfinfoWithParam(@Param("name") String name,
                              @Param("age") Integer age
                              );
<select id="selectAfinfoWithParam" resultType="com.mi.learn.mybatis.domin.Afinfo">
    select id, `name`, age, birth, sex, memo
    from afinfo
    where `name` = #{name} and age=#{age}
  </select>
Afinfo selectAfinfoWithObject(String name,
                               Integer age);

<select id="selectAfinfoWithObject" resultType="com.mi.learn.mybatis.domin.Afinfo">
    select id, `name`, age, birth, sex, memo
      from afinfo
      where `name` = #{param1} and age=#{param2}
  </select>                               

批量插入

for (int i=0;i<5; i++){
    mapper.insertAfinfo(afinfo);   
}

<insert id="insertAfinfo" parameterType="com.mi.learn.mybatis.domin.Afinfo" useGeneratedKeys="true" keyProperty="id">
    insert into afinfo (`name`, age, birth,
      sex, memo)
    values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{birth,jdbcType=DATE},
      #{sex,jdbcType=CHAR}, #{memo,jdbcType=VARCHAR})
</insert>
mapper.insertBatch(list);

<insert id="insertBatch" parameterType="list" useGeneratedKeys="true" keyProperty="id">
    insert into afinfo (`name`, age, birth,
    sex, memo) values
    <foreach collection="list" item="item" index="index" separator=",">
    (#{item.name,jdbcType=VARCHAR}, #{item.age,jdbcType=INTEGER}, #{item.birth,jdbcType=DATE},
      #{item.sex,jdbcType=CHAR}, #{item.memo,jdbcType=VARCHAR})
    </foreach>
</insert>

控制台打印sql日志

logging.level.com.mi.learn.mybatis.mapper=debug

Mybatis缓存

# 开启缓存
mybatis.configuration.cache-enabled=true

Mybatis和Hibernate

转义字符

转义字符 字符 含义
< < 小于
> > 大于
& &
' ' 单引号
" " 双引号

或者

<![CDATA[    ]]>

XML标签

<select></select>
<insert></insert>
<update></update>
<delete></delete>
<resultMap></resultMap>
<parameterMap></parameterMap>
<sql></sql>
<include></include>
<selectKey></selectKey>

sql、include标签

List<Afinfo> selectAfinfoWithObject(String name,
                               Integer age);
<sql id="student_element">
    id,`name`,age,birth,sex,memo
</sql>

<select id="selectAfinfoWithObject" resultType="com.mi.learn.mybatis.domin.Afinfo">
    select <include refid="student_element"></include>
      from afinfo
      where `name` = #{param1} and age=#{param2}
  </select>

xml和Mapper接口的工作原理,Mapper接口参数不通,能否重载

Mybits分页

Mybatis的插件运行原理,怎么编写一个插件

package com.mi.learn.mybatis.handler;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Slf4j
@Component
@Intercepts({@Signature(type= Executor.class, method = "select", args = {MappedStatement.class,Object.class})})
public class MybatisInterceptor implements Interceptor {
    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget(); //被代理对象
        Method method = invocation.getMethod(); //代理方法
        Object[] args = invocation.getArgs(); //方法参数
        // do something ...... 方法拦截前执行代码块
        Object result = invocation.proceed();
        // do something .......方法拦截后执行代码块
        return result;
    }
    public Object plugin(Object target) {
        return Proxy.newProxyInstance(Interceptor.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return intercept(new Invocation(target, method, args));
            }
        });
    }
}

Mybatis一对多、多对一

```xml
<resultMap id="StudentExamResultMap" type="com.mi.learn.mybatis.bean.StudentExam">
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="code" jdbcType="VARCHAR" property="code" />
    <collection property="examList" ofType="com.mi.learn.mybatis.domin.Exam" column="code">
    <result column="code" jdbcType="VARCHAR" property="code"/>
    <result column="subject" jdbcType="VARCHAR" property="subject"/>
    <result column="score" jdbcType="VARCHAR" property="score"/>
    </collection>
</resultMap>


<select id="selectStudentExam" resultMap="StudentExamResultMap">
    select s.* , e.*
    from student s , exam e
    where s.code = e.code
    order by s.code
</select>

```
```xml
<resultMap id="ExamStudentResultMap" type="com.mi.learn.mybatis.bean.ExamStudent">
    <result column="code" jdbcType="VARCHAR" property="code"/>
    <result column="subject" jdbcType="VARCHAR" property="subject"/>
    <result column="score" jdbcType="VARCHAR" property="score"/>
    <association property="student" javaType="com.mi.learn.mybatis.domin.Student">
    <result column="code" jdbcType="VARCHAR" property="code"/>
    <result column="name" jdbcType="VARCHAR" property="name"/>
    </association>
</resultMap>

<select id="selectExamStudent" resultMap="ExamStudentResultMap">
    select s.* , e.*
    from student s , exam e
    where s.code = e.code
    order by s.code
</select>
```

Mybatis延迟加载

# 开启缓存
mybatis.configuration.cache-enabled=true
# 延迟加载
mybatis.configuration.lazy-loading-enabled=true
# 关闭积极加载
mybatis.configuration.aggressive-lazy-loading=false
    List<StudentExam> selectLazyStudent();

    List<Exam> selectExamByCode(@Param("code") String code);


    @Test
    @Transactional
    public void selectLazyStudentTest(){
        List<StudentExam> res = mapper.selectLazyStudent();
        log.info(JSON.toJSONString(res));
        log.info("==========================================");
        List<Exam> examList1 = mapper.selectExamByCode("001");
        List<Exam> examList2 = mapper.selectExamByCode("002");
        List<Exam> examList3 = mapper.selectExamByCode("003");
        log.info("==========================================");
        log.info("examList1:{}",JSON.toJSONString(examList1));
        log.info("examList1:{}",JSON.toJSONString(examList2));
        log.info("examList1:{}",JSON.toJSONString(examList3));
    }
<resultMap id="StudentExamLazyResultMap" type="com.mi.learn.mybatis.bean.StudentExam">
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="code" jdbcType="VARCHAR" property="code" />
    <association property="examList" javaType="List" select="com.mi.learn.mybatis.mapper.StudentMapper.selectExamByCode" column="code"/>
</resultMap>

<select id="selectLazyStudent" resultMap="StudentExamLazyResultMap">
    select s.*
    from student s
    order by s.code
</select>

<select id="selectExamByCode" resultType="com.mi.learn.mybatis.domin.Exam" parameterType="string">
    select * from exam where exam.code = #{code}
</select>

日志:

com.mi.learn.mybatis.StudentMapperTest   : ==========================================
com.mi.learn.mybatis.StudentMapperTest   : ==========================================
com.mi.learn.mybatis.StudentMapperTest   : examList1:[{"code":"001","score":"80","subject":"数学"},{"code":"001","score":"90","subject":"语文"},{"code":"001","score":"90","subject":"英语"}]
com.mi.learn.mybatis.StudentMapperTest   : examList2:[{"code":"002","score":"75","subject":"数学"},{"code":"002","score":"85","subject":"语文"},{"code":"002","score":"85","subject":"英语"}]
com.mi.learn.mybatis.StudentMapperTest   : examList3:[{"code":"003","score":"80","subject":"英语"}]

并没有打印查询Exam表的sql语句

Mybatis中xml的id是否能一样

Executor

Mybatis映射Enum枚举

SexEnum

public enum SexEnum {

    SEX_NO_KNOW(0,"未知"),

    SEX_ENUM_MAN(1,"男"),

    SEX_ENUM_WOMAN(2,"女");
    
    private Integer sex;
    private String context;

    SexEnum(int sex, String context) {
        this.sex = sex;
        this.context = context;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Integer getSex() {
        return sex;
    }

    public void setContext(String context) {
        this.context = context;
    }

    public String getContext() {
        return context;
    }

}

Afinfo

@Data
@ToString
public class Afinfo implements Serializable {
    private Integer id;

    private String name;

    private Integer age;

    private Date birth;

    private SexEnum sex;

    private String sexContext;


    private String memo;

    private static final long serialVersionUID = 1L;
}

AfinfoMapper.xml

<resultMap id="BaseResultMap" type="com.mi.learn.mybatis.domin.Afinfo">
    <result column="id" jdbcType="INTEGER" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="age" jdbcType="INTEGER" property="age" />
    <result column="birth" jdbcType="DATE" property="birth" />
    <result column="sex" property="sex" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.mi.learn.mybatis.myenum.SexEnum" />
    <result column="memo" jdbcType="VARCHAR" property="memo" />
  </resultMap>

Test

List<Afinfo> afinfoList11 = mapper.selectAfinfoWithMap(map);
afinfoList11.forEach(item->{
    SexEnum sexEnum = item.getSex();
    log.info(sexEnum.getContext());
});

log日志

com.mi.learn.mybatis.AfinfoMapperTest    : 男
com.mi.learn.mybatis.AfinfoMapperTest    : 男
com.mi.learn.mybatis.AfinfoMapperTest    : 男
com.mi.learn.mybatis.AfinfoMapperTest    : 男
com.mi.learn.mybatis.AfinfoMapperTest    : 男
com.mi.learn.mybatis.AfinfoMapperTest    : 女
com.mi.learn.mybatis.AfinfoMapperTest    : 未知
com.mi.learn.mybatis.AfinfoMapperTest    : 男
com.mi.learn.mybatis.AfinfoMapperTest    : 男
com.mi.learn.mybatis.AfinfoMapperTest    : 男

数据库中的数据

sex
1
1
1
1
1
2
0
1
1
1
  1. 在ResultMap中加入固定的typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
  2. 加入javaType="com.mi.learn.mybatis.myenum.SexEnum",SexEnum为Enum包
  3. Afinfo的sex属性改成SexEnum引用对象

使用

pom.xml

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.4.4</version>
        </dependency>

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

配置自动生成代码

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.7</version>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.12</version>
                    </dependency>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.7</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>MyBatis Artifacts</id>
                        <phase>package</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!--允许移动生成的文件 -->
                    <verbose>true</verbose>
                    <!-- 是否覆盖 -->
                    <overwrite>true</overwrite>
                    <!-- 自动生成的配置 -->
                    <configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>
                </configuration>
            </plugin>
        </plugins>

    </build>

generatorConfig.xml

<?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>
    <!--加载配置文件,为下面读取数据库信息准备-->
    <properties resource="application.properties"/>

    <!--defaultModelType="flat" 大数据字段,不分表 -->
    <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
        <property name="autoDelimitKeywords" value="true" />
        <property name="beginningDelimiter" value="`" />
        <property name="endingDelimiter" value="`" />
        <property name="javaFileEncoding" value="utf-8" />
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />

        <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />

        <!-- 注释 -->
        <commentGenerator >
            <property name="suppressAllComments" value="true"/><!-- 是否取消注释 -->
            <property name="suppressDate" value="false" /> <!-- 是否生成注释代时间戳-->
        </commentGenerator>

        <!--数据库链接地址账号密码-->
        <jdbcConnection driverClass="${spring.datasource.driver-class-name}"
                        connectionURL="${spring.datasource.url}"
                        userId="${spring.datasource.username}"
                        password="${spring.datasource.password}">
        </jdbcConnection>

        <!-- 类型转换 -->
        <javaTypeResolver>
            <!-- 是否使用bigDecimal, false可自动转化以下类型(Long, Integer, Short, etc.) -->
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!--生成Model类存放位置-->
        <javaModelGenerator targetPackage="com.mi.learn.mybatis.domin" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- 生成mapxml文件 -->
        <sqlMapGenerator targetPackage="mapping" targetProject="src/main/resources/" >
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>

        <!-- 生成mapxml对应client,也就是接口dao -->
        <javaClientGenerator targetPackage="com.mi.learn.mybatis.mapper" targetProject="src/main/java" type="XMLMAPPER" >
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <table tableName="afinfo" enableCountByExample="true" enableUpdateByExample="true" enableDeleteByExample="true" enableSelectByExample="true" selectByExampleQueryId="true">
            <generatedKey column="id" sqlStatement="Mysql" identity="true" />
        </table>
    </context>
</generatorConfiguration>

application.properties

# 端口
server.port=8001

# xxxMapper.xml文件的路径
mybatis.mapper-locations=classpath:mapping/*.xml
# 日志显示sql
logging.level.com.mi.learn.mybatis.mapper=debug

# mysql数据库用户名
spring.datasource.username=root
# mysql数据库用户密码
spring.datasource.password=123456
# mysql连接url
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
# mysql连接驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

文档

Mybatis官网

Mybatis面试题

上一篇 下一篇

猜你喜欢

热点阅读