当前位置: 首页>后端>正文

Mybatis笔记

感谢狂神,讲解清晰,以下是原视频【狂神说Java】Mybatis最新完整教程IDEA版通俗易懂_哔哩哔哩_bilibili的笔记

环境:

  • JDK 1.8
  • Mysql 8.0+
  • maven 3.6.1
  • IDEA

回顾:

  • JDBC
  • Mysql
  • Java基础
  • Maven
  • Junit

SSM框架:配置文件的。(看官网文档);

简介

什么是Mybatis

Mybatis笔记,第1张
  • MyBatis 是一款优秀的持久层框架
  • 它支持自定义 SQL、存储过程以及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • MyBatis本是apache的一个iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。

如何获得:

  • Maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
  • Github:https://github.com/mybatis
  • 中文文档:MyBatis中文网

持久化

数据持久化

  • 持久化就是将程序的数据在瞬时状态和持久状态转化的过程
  • 内存:断电即失
  • 数据库(jdbc),io文件持久化
为什么需要持久化?
  • 有的对象不能失去
  • 内存贵

持久层

Dao层、Sevice层、Controller层...

  • 完成持久化工作的代码块
  • 层界限十分明显

为什么需要Mybatis?

  • 把数据存到数据库中
  • 方便
  • 传统的JDBC代码太复杂。简化、框架、自动化
  • 不用Mybatis也可以。

优点:

  • 解除SQL和程序代码的耦合
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组件维护
  • 提供xml标签,支持编写动态sql

第一个Mybatis程序

思路:搭建环境->导入Mybatis->编写代码->测试。

搭建环境

搭建数据库

CREATE TABLE user (
    id INT(20) NOT NULL,
    name VARCHAR(30) DEFAULT NULL,
    pwd VARCHAR(30) DEFAULT NULL,
    PRIMARY KEY(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO USER (id, name, pwd) VALUES
(1, '狂神', '123456'),
(2, '张三', '123456'),
(3, '李四', '123890')

新建项目

  1. 新建一个普通的maven项目
  2. 删除src目录
  3. 导入maven依赖(版本自定)
<!--  导入依赖  -->
    <dependencies>
        <!--Mysql-->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

        <!--Mybatis-->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

        <!--Junit-->
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

编写mybatis工具类:

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        String resource = "mybatis-config.xml";
        // InputStream inputStream = null;
        try (InputStream inputStream = Resources.getResourceAsStream(resource);){
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 通过factory获得sqlsession
    public static SqlSession getSqlSession() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }

}

目录结构:

Mybatis笔记,第2张

mybatis配置、用户、用户Dao以及用户Mapper:mybatis-config.xml、User、UserDao、UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://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/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF8&amp;serverTimezone=UTC"/>
                <property name="username" value="**你自己的用户名**"/>
                <property name="password" value="你自己的用户密码"/>
            </dataSource>
        </environment>
    </environments>
    <!--Mapper.XML-->
    <mappers>
        <mapper resource="com/dao/UserMapper.xml"/>
    </mappers>
</configuration>

其中,serverTimezone是针对MySQL8的设置。

public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}';
    }
}
public interface UserDao {
    List<User> getUserList();
}
<?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.dao.UserDao">
    <!--select 用户-->
    <select id="getUserList" resultType="com.pojo.User">
        select * from mybatis.user
    </select>
</mapper>

然后编写一个测试类:

public class UserDaoTest {
    @Test
    public void test() {
        // 得到Session
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        // form-1:getMapper
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        List<User> userList = mapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
}

注意1:此时,会报出找不到xml文件的错误,主要是由于maven导致的问题,应该在pom.xml的配置文件下添加如下语句:

<!--约定大于配置,需要配置maven导出文件-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

注意2:如果出现“ UTF-8 序列的字节 2 无效。”是因为项目的默认配置编码不是UTF-8。解决方法是:将IDEA设置为UTF-8编码。或者将中文注释的字符去掉。

最终输出为:

User{id=1, name='狂神', pwd='123456'}
User{id=2, name='张三', pwd='123456'}
User{id=3, name='李四', pwd='123890'}

总结:编写Mybatis工具类->写Mybatis配置->写实体类->写实体类Mapper
问题:1、注释带中文字符可能会报错;2、Maven约定大于配置,可能存在找不到配置文件的情况,此时要对Maven进行配置;3、mysql8和mysql5配置存在差异,体现在mybatis的xml配置文件中。

继续深入其他语句(CRUD)

  1. Namespace要对应到Dao;

  2. Select选择查询语句

  • id:对应namespace的方法名
  • resultType:sql返回值
  • parameterType:参数类型
<select id="getUserById" parameterType="int" resultType="com.pojo.User">
        select * from mybatis.user where id = #{id}
</select>
  1. Insert
<insert id="insertUser" parameterType="com.pojo.User">
        insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd});
</insert>
  1. update
<update id="updateUser" parameterType="com.pojo.User">
        update mybatis.user set name=#{name}, pwd=#{pwd} where id = #{id};
</update>
  1. delete
<delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id = #{id};
</delete>

总结:编写接口->编写对应的mapper里的sql语句->测试(增删改需要提交事务)

  1. 万能Map
<insert id="addUser" parameterType="map">
        insert into mybatis.user (id, name, pwd) values (#{userId}, #{userName}, #{passWord});
</insert>
  1. 模糊查询
<select id="getUserLike" resultType="com.pojo.User">
        select * from mybatis.user where name like "%" #{value} "%";
</select>

总结:

  1. map传递使用参数为Key名
  2. Object传递使用参数为属性名
  3. 单个基本类型参数可以直接在sql取到
  4. 多个参数用map或者注解
  5. 通配符尽可能固定,需要考虑到注入问题。

配置解析

1、 核心配置文件

  • mybatis-config.xml
  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

重点在:properties、environments、mappers等。

  • transactionManager默认为JDBC
  • dataSource默认为POOLED
  • 学会如何配置多套environment
  • properties可以动态替换(使用外部的配置文件比如"db.properties")
  • typeAliases可以给实体类起别名(可以指定包名,找到对应的Beans)
  • Settings里面"logImpl",“useGeneratedKeys”,“mapUnderscoreToCamelCase”等需要了解。
  • Others
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
      • MyBatis-generator-core
      • MyBatis-plus
      • 通用Mapper

配置文件里面可以使用properties来引入外部文件里的属性:(配置的位置需要在Configuration里面的最开始,不然会报错)

<properties resource="db.properties">
    <property name="" value=""/>
</properties>

db.properties的内容(即属性)。(原本的&用&代替,不然会报错)

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC
username=你自己的用户名
password=你自己的密码

typeAliases(别名)的使用

直接指定别名:(实体类少时,推荐使用)

<typeAliases>
        <typeAlias type="com.pojo.User" alias="User"/>
</typeAliases>

指定包名让MyBatis自己去找对应的:(实体类多时,推荐使用)

<typeAliases>
        <package name="com.pojo"/>
</typeAliases>

或者在User里面使用注解:

@Alias("User")
public class User

就可以使用User来代替之前的com.pojo.User

<select id="getUserList" resultType="User">
        select * from mybatis.user
</select>

Mappers

用class来配置mapper时,接口和配置文件要同名且在同一个包下,用package配置同理。以下是错误示范:

<mappers>
    <mapper class="com.dao.UserDao"/>
</mappers>

解决:应该将UserDao重命名为UserMapper。

最终,可以通过三种方式来配置,任选其一都可以测试成功:

<mappers>
        <mapper resource="com/dao/UserMapper.xml"/>
        <mapper class="com.dao.UserMapper"/>
        <package name="com.dao"/>
</mappers>

作用域和生命周期

  • 一旦创建了 SqlSessionFactory,就不再需要SqlSessionFactoryBuilder了。(只用一次)
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。(单例)
  • 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。(用完关闭,如下所示:)
try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

ResultMap(解决对象属性名和数据表字段名不一致的问题)详见:XML 映射器_MyBatis中文网

比如User此时为:

public class User {
    private int id;
    private String name;
    private String password;

属性password与数据库中pwd不同名,此时查询出来的结果如下。

User{id=1, name='狂神', password='null'}
User{id=2, name='张三', password='null'}
User{id=3, name='李四', password='null'}

可见,丢失了最后一个字段,有两种解决方法。

  1. 别名(修改查询语句):
    select id, name, pwd as password from mybatis.user

  2. ResultMap(对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。):设定一个UserMap的resultMap,类型为User。然后,指定方法的resultMap为UserMap,即可完成映射。

<resultMap id="UserMap" type="User">
        <result column="id" property="id"/>
        <result column="pwd" property="password"/>
        <result column="name" property="name"/>
</resultMap>
<select id="getUserList" resultMap="UserMap">
        select * from mybatis.user
</select>

总结:

  • ResultMap 元素是 MyBatis 中最重要最强大的元素。
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • ResultMap 的优秀之处——你完全可以不用显式地配置它们。

日志

日志工厂

异常排错:sout、debug、日志工厂

使用logImpl,指定 MyBatis 所用日志的具体实现,未指定时将自动查找。可选为:SLF4J(掌握?) | LOG4J(出Bug,掌握) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING(掌握) | NO_LOGGING。默认无。

对mybatis-config.xml进行配置,注意settings所在的顺序位置。比如下面的命令行LOG。

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

Log4j

是什么(引自百度百科):Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等。我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,通过一个配置文件来灵活地进行配置,精细控制日志的生成过程,而不改代码。

怎么用

  • 先导包:
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
  • 再配置:resources/log4j.properties,以下是一个简单的示例:
# 将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

# 控制台输出的相关设置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

# 文件输出的相关设置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
  • 再把settings改成使用LOG4J:
<settings>
        <setting name="logImpl" value="LOG4J"/>
</settings>
  • 使用:
static Logger logger = Logger.getLogger(userDaoTest.class); // 当前类
@Test
public void testLog4j(){ //多种日志的级别
  logger.info("info:进入了testlog4j");
  logger.debug("debug:进入了testlog4j");
  logger.warn("warn:进入了testlog4j");
  logger.error("error:进入了testlog4j");
}
  • 输出:
[dao.userDaoTest]-info:进入了testlog4j
[dao.userDaoTest]-debug:进入了testlog4j
[dao.userDaoTest]-warn:进入了testlog4j
[dao.userDaoTest]-error:进入了testlog4j

分页

目的:减少运算数据量。

1、 LIMIT实现分页

实现sql的相关接口。

List<User> getUserByLimit(Map<String, Integer> map);
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
        select * from mybatis.user limit #{startIndex}, #{endIndex};
</select>

进行测试。

@Test
public void testGetUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> parameterMap = new HashMap<>();
    parameterMap.put("startIndex", 1);
    parameterMap.put("endIndex", 2);
    List<User> userByLimit = mapper.getUserByLimit(parameterMap);
    for (User user: userByLimit) {
        System.out.println(user);
    }
    sqlSession.close();
}

2、RowBounds实现分页

List<User> getUserByRowBounds();
<select id="getUserByRowBounds" resultMap="UserMap">
  select * from mybatis.user;
</select>
@Test
public void testGetUserByRowBounds() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    // Java层面来直接selectList
    RowBounds rowBounds = new RowBounds(1, 2);
    List<User> userList = sqlSession.selectList("com.dao.UserMapper.getUserByRowBounds", null, rowBounds);
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

3、分页插件
PageHelper等。

使用注解开发

面向接口编程

目的:解耦

  • 个体的抽象:abstract class
  • 个体的某个方面的抽象:interface

三个面向

  • 面向对象:考虑对象的属性和方法。
  • 面向过程:以流程(事务)考虑实现。
  • 接口设计与非接口设计:与另外两个考虑的不是一个问题,更多的是对系统整体的架构。

注解开发

可以不配置对应mapper的xml,使用简单的注解。

@Select("select * from user")
List<User> getUsers();

然后,绑定接口:

<!--Bind interfaces-->
<mappers>
        <mapper class="com.dao.UserMapper"/>
</mappers>
@Test
public void testGetUsers() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.getUsers();
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}
  • 实现:反射
  • 底层:动态代理

MyBatis执行流程

加载配置->SqlSessionFactoryBuilder(构建XMLConfigBuilder->转换Configuration)->SqlSessionFactory->(transaction事务管理->executor->sqlSession->CRUD->是否执行成功)->提交事务->关闭

继续CRUD

增改删(注解版)及测试:

@Insert("insert into user(id, name, pwd) values (#{id},#{name},#{password})")
int addUser(User user);

@Update("update user set name=#{name}, pwd=#{password} where id = #{id}")
int updateUser(User user);

@Delete("delete from user where id = #{id}")
int deleteUser(int id);
@Test
public void testAddUser() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int res = mapper.addUser(new User(6, "aaa", "1234555"));
    if (res > 0){
        System.out.println("插入成功");
    }
    sqlSession.commit();
    sqlSession.close();
}

@Test
public void testUpdateUser() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int res = mapper.updateUser(new User(6, "aaa", "6666"));
    if (res > 0){
        System.out.println("改变成功");
    }
    sqlSession.commit();
    sqlSession.close();
}

@Test
public void testDeleteUser() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int res = mapper.deleteUser(6);
    if (res > 0){
        System.out.println("删除成功");
    }
    sqlSession.commit();
    sqlSession.close();
}

关于@Param注解

  • String或者基本类的需要加上
  • 引用类型不需要加
  • 只有一个基本类型可以忽略
  • SQL中引用的就是@Param中的属性名

#{}和${}

  • $直接拼接,无法防止注入
  • $多用在传入数据库参数时
  • 尽量使用#

Lombok!

是什么:Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.

  • 通过IDEA进行插件安装
  • 导包
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.16</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Lombok注解内容:

@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Lombok config system
Code inspections
Refactoring actions (lombok and delombok)

  • @Data:无参构造,get,set,toString,hashcode,equals
  • @AllArgsConstructor:有参构造
  • @NoArgsConstructor:无参构造
  • @Getter
  • @Setter
  • @ToString

复杂查询环境

  • 多个学生对应/关联一个老师(多对一)
  • 集合,一个老师有很多学生(一对多)

先建表搭建环境:

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

流程:导入lombok->建立实体类->建立Mapper接口->建立Mapper.xml文件->在核心配置文件中绑定Mapper->测试查询是否成功。

多对一

按照查询嵌套处理

查找学生的时候,通过外键查询对应的老师。

    <select id="getAllStudents" resultMap="StudentTeacher">
        select * from student;
    </select>

    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>

        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id = #{tid};
    </select>

按照结果嵌套处理

    <select id="getAllStudents2" resultMap="StudentTeacher2">
        select s.id sid, s.name sname, t.id tid, t.name tname
        from student s, teacher t
        where s.tid = t.id;
    </select>

    <resultMap id="StudentTeacher2" type="Student">
        <result column="sid" property="id"/>
        <result column="sname" property="name"/>
        <association property="teacher" javaType="Teacher">
            <result column="tid" property="id"/>
            <result column="tname" property="name"/>
        </association>
    </resultMap>

一对多

按照结果嵌套处理

    <select id="getTeacherById" resultMap="TeacherStudent" parameterType="int">
        select t.id tid, t.name tname, s.id sid, s.name sname
        from teacher t, student s
        where t.id = #{tid} and t.id = s.tid;
    </select>

    <resultMap id="TeacherStudent" type="Teacher">
        <result column="tname" property="name"/>
        <result column="tid" property="id"/>

        <collection property="students" ofType="Student">
            <result column="sid" property="id"/>
            <result column="sname" property="name"/>
            <result column="tid" property="tid"/>
        </collection>
    </resultMap>

按照查询嵌套处理

    <select id="getTeacherById2" resultMap="TeacherStudent2">
        select * from teacher where id = #{tid};
    </select>

    <resultMap id="TeacherStudent2" type="Teacher">
        <result property="id" column="id"/>
        <result property="name" column="name"/>

        <collection property="students" javaType="ArrayList" ofType="Student" select="getStudent" column="id"/>
    </resultMap>

    <select id="getStudent" resultType="Student">
        select * from student where tid = #{tid};
    </select>

总结:

  • 保证SQL可读性
  • 一对多和多对一的字段和属性名的对应问题
  • 问题如果不好排查,使用日志系统,如:Log4j
  • 最终SQL优化的方向为:学习和理解Mysql引擎、InnoDB底层原理、索引、索引优化!

动态SQL

是什么:根据不同条件生成SQL语句。

搭建环境:

CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8;
    @Test
    public void testAddBlogs() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDutils.getID());
        blog.setAuthor("KyoDante");
        blog.setCreateTime(Timestamp.from(Instant.now()));
        blog.setTitle("Mybatis如此简单");
        blog.setViews(7777);
        mapper.addBlog(blog);

        blog.setId(IDutils.getID());
        blog.setTitle("Spring如此简单");
        blog.setCreateTime(Timestamp.from(Instant.now()));
        mapper.addBlog(blog);

        blog.setId(IDutils.getID());
        blog.setTitle("Java如此简单");
        blog.setCreateTime(Timestamp.from(Instant.now()));
        mapper.addBlog(blog);

        blog.setId(IDutils.getID());
        blog.setTitle("微服务如此简单");
        blog.setCreateTime(Timestamp.from(Instant.now()));
        mapper.addBlog(blog);

        sqlSession.commit();

        sqlSession.close();
    }
public class IDutils {
    public static String getID() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}

IF

    <select id="queryBlogIf" resultType="Blog" parameterType="map">
        select * from blog where true
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </select>

常用标签

where和if搭配(只要满足就填入SQL)

    <select id="queryBlogChoose" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <if test="title != null">
                title = #{title}
            </if>
            <if test="author != null">
                and author = #{author}
            </if>
        </where>
    </select>

Choose(类似Switch,多选一)

    <select id="queryBlogChooseWhen" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
            </choose>
        </where>
    </select>

Trim

与 set 元素等价的自定义 trim 元素:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

sql和include

  • 用sql抽取复用的部分
    <sql id="if-title-author">
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </sql>
  • 用include将该片段包括进去
    <select id="queryBlogChoose" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <include refid="if-title-author"/>
        </where>
    </select>

FOREACH

    <select id="queryBlogForeach" parameterType="map" resultType="Blog">
        select * from Blog
        <where>
            <foreach collection="ids" item="uuid" open="(" close=")" separator="or">
                id = #{uuid}
            </foreach>
        </where>
    </select>

缓存

是什么:查询连接数据库,耗资源,存到某些地方,下次可以直接使用!内存->缓存(解决高并发性能问题)
为什么:减少和数据库的交互开销,提高系统效率
哪里使用:经常查询且不经常改变的数据。

  • Mybatis有一级缓存二级缓存
  • 默认为开启一级缓存。(SqlSession级别的缓存;也称为本地缓存)
  • 二级缓存是手动开启和配置,基于namespace级别的缓存
  • 为了提高扩展性,定义了缓存接口Cache,可以实现它来定义二级缓存

一级缓存

    @Test
    public void test() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserById(1);

        System.out.println("==================");
        User user2 = mapper.queryUserById(1);

        System.out.println(user2);
        System.out.println(user);
        System.out.println(user == user2);
        
        sqlSession.close();
    }
  • 增删改会刷新缓存
    @Test
    public void test() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserById(1);

        User user3 = new User();
        user3.setId(2);
        user3.setName("aaaa");
        user3.setPwd("bbbb");
        int res = mapper.updateUserById(user3);
        if (res > 0) {
            System.out.println("改变成功");
        }
        System.out.println("==================");
        User user2 = mapper.queryUserById(1);

        System.out.println(user2);
        System.out.println(user);
        System.out.println(user == user2);

        sqlSession.close();
    }
  • 或者清除缓存
    sqlSession.clearCache();

二级缓存

为了什么:一级缓存只在SqlSession作用域内,会话没了,还会存在于二级缓存中。新的会话可以从二级缓存中获得内容。不同mapper放在对应的map中。

在Mapper中设置(如果直接这么设置,需要序列化实体类,即implements Serializable)

<cache/>

在核心配置中使用(默认开启):

<setting name="cacheEnabled" value="true"/>
  • 最开始是一级缓存
  • 只有在关闭会话,或者提交会话的时候,才会转到二级缓存

缓存原理

提高查询效率
从用户的角度:先查二级再查一级。
从程序的角度:先从SqlSession,也即一级缓存;再到Mapper,也即二级缓存。

EhCache

  • 导包
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.2.1</version>
        </dependency>
  • 配置resources/ehcache.xml
<?xml version="1.0" encoding="UTF-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>

使用

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

Redis:K-V来实现缓存。(现在常用)


https://www.xamrdz.com/backend/38e1920684.html

相关文章: