Mybatis基础教程

简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

入门

环境搭建

如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

写properties文件

将连接数据库要用到的信息写入一个properties文件中:

driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
username = root
password = 666666

配置mybatis-XML

XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。这里先给出一个简单的示例:

<?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>
    <properties resource="db.properties"/>  <!--引入properties文件-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>  <!--事务管理器-->
            <dataSource type="POOLED">      <!--数据源,有连接池-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

构建 SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。MyBatis 包含一个名叫 Resources 的工具类,使得从类路径或其它位置加载资源文件更加容易。

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

获取 SqlSession

既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。为了方便,将其封装在一个工具类中:

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

如果不支持带资源的try块,是因为maven项目默认使用的JDK版本过低,在pom.xml文件中加上配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>12</source>
                <target>12</target>   <!--1.7之后都支持-->
            </configuration>
        </plugin>
    </plugins>
</build>

创建POJO实体类

创建了一个简单的User表用作测试,后续我们要执行获取数据库中用户的操作:

在这里插入图片描述

随后写出对应的实体类:

package com.pickinstars.POJO;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private long id;
    private String name;
    private String password;
}

编写UserMapper接口

目前只含有一个得到所有用户的方法:

public interface UserMapper {
    ArrayList<User> getUsers();
}

编写UserMapper.XML文件

<?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.pickinstars.Mapper.UserMapper">
    <select id="getUsers" resultType="com.pickinstars.POJO.User">
        select * from mybatis.user;
    </select>
</mapper>

参数解释:

  • namespace:对应mapper接口
  • id:对应接口中的某个方法
  • resultType:返回的结果类型,这里是User型

注册Mapper

在mybatis的XML配置文件中注册刚刚写好的XML

<mappers>
    <mapper resource="com/pickinstars/Mapper/UserMapper.xml"/>
</mappers>

测试

public class UserMapperTest {
    @Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        ArrayList<User> users = mapper.getUsers();
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }
}

成功输出了数据库中所有User的信息。

注意:如果xml文件没有放在maven的resources目录下的话(如下图):

在这里插入图片描述

需要在pom.xml中添加配置:

<build>
    <resources>
        <resource>
            <!--所在的路径-->
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

作用域和生命周期

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,在应用运行期间不要重复创建多次。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。

增删查改

在上述过程中,已经将框架搭建好了,之后再写数据库的业务代码,只需要在上述定义的UserMapper.xml中进行sql的编写就可以了。

上方的test中实现了简单的查询,下面来看带参数的查询:

1.编写接口

User getUserById(long id);

2.在xml中编写sql

<select id="getUserById" parameterType="long" resultType="com.pickinstars.POJO.User">
    select * from mybatis.user where id = #{id};
</select>

parameterType是参数类型,要与接口一致,其中 #{id} 可以直接取到接口定义要传入的id。

3.最后就是测试了

同样的,如果要插入一条信息到数据库:

void insertUser(User user);  
<!-- {}中的内容要和实体类中字段的名字一致 -->
<insert id="insertUser" parameterType="com.pickinstars.POJO.User">
    insert into mybatis.user (id, name, pwd) value (#{id},#{name},#{password});
</insert>

测试:有关数据库修改的都需要commit

@Test
public void test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User(4,"梵","500191");
    mapper.insertUser(user);
    sqlSession.commit();   // ✳
    System.out.println("ok");
    sqlSession.close();
}

关于updatedelete不再赘述。

别名

在上述例子中,可以看到resultType="com.pickinstars.POJO.User"非常冗长,我们可以用别名来进行简化,只需要在xml中加入:

<typeAliases>
    <typeAlias type="com.pickinstars.POJO.User" alias="TestUser"/>
</typeAliases>

我们就可以用直接使用TestUser来替换

还有一种方式是指定一个包,这个包下所有实体类默认类名作为别名:

<typeAliases>
    <package name="com.pickinstars.POJO"/>
</typeAliases>

在指定包的情况下如果想自定义别名,可以在给类加一个注解,例如:

@Alias("TestUser")

注解开发

对于结构比较简单的SQL语句,可以使用注解开发,不需要写在XML文件中,只需要在接口中提供相应SQL的注解,再去mapper注册器中注册这个接口类就可以使用了,例如:

@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
<mappers>
    <mapper class="com.pickinstars.Mapper.UserMapper"/>
</mappers>

对于较复杂的语句,建议使用xml的方式。

映射

当POJO中的属性名与数据库中的字段名不一致时,就无法注入合理的值,例如,在我的User实体类中有一个password属性,对应数据库中的pwd字段,如果直接将ResultType设置为这个类,取得的user的password属性为null。虽然可以在 SELECT 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配。还可以有更优雅的方式:使用ResultMap

<!--与下方对应-->
<resultMap id="userResultMap" type="TestUser">
    <id property="password" column="pwd"/>
</resultMap>

<!--这里使用resultMap-->
<select id="getUserById" parameterType="long" resultMap="userResultMap">
    select * from mybatis.user where id = #{id};
</select>

关联映射

对于一个有关联的数据库,例如学生和老师,多个学生对应一个老师的这种关系,创建两张表模拟:

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');

学生的实体类如下:

package com.pickinstars.POJO;

import lombok.Data;

@Data
public class Student {
    private int id;
    private String name;
    private Teacher teacher;
}

可以发现,在数据库中不存在一个teacher的字段对应这个实体类中的teacher属性。那么假如我们要查询所有学生的信息,该怎么做呢?

首先还是先编写接口:

package com.pickinstars.Mapper;

import com.pickinstars.POJO.Student;

import java.util.ArrayList;

public interface StudentMapper {
    ArrayList<Student> getStudentList();
}

接下来再编写XML绑定时就有两种实现方式:查询嵌套结果嵌套

查询嵌套处理

类似于SQL语句中的子查询(嵌套查询)。使用resultMap中的association属性表实多对一的关联关系。

association中嵌套了一个select字句,相当于分成两次查询。

<select id="getStudentList" resultMap="StudentList">
    select * from student;
</select>
<resultMap id="StudentList" type="Student">
    <!--在Student类型的结果中有一个teacher的属性,对应字段为tid,Java类型为Teacher,通过select得到这个Teacher-->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!--子查询-->
<select id="getTeacher" resultType="Teacher">
    select * from teacher where id = #{tid};
</select>

结果嵌套处理

由于我们查询出的结果中有teacher属性,这是一个Teacher类,而Teacher类中有id和name属性,所以我们把需要的信息都用联表查询查出来,在结果映射中,单独对无法直接处理的teacher做映射,解释这个属性的javaType以及这个类型属性对应的字段即可:

<resultMap id="StudentList" type="Student">
    <association property="teacher" javaType="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
    </association>
</resultMap>
<select id="getStudentList" resultMap="StudentList">
    select s.id sid,s.name sname,s.tid tid,t.name tname
           from mybatis.student s ,mybatis.teacher t
           where tid = t.id;
</select>

集合映射

现在来讨论多对一的映射,老师对应的实体类如下:

package com.pickinstars.POJO;

import lombok.Data;

import java.util.ArrayList;

@Data
public class Teacher {
    private int id;
    private String name;
    private ArrayList<Student> students;
}

一个老师对应多个学生,包含了一个ArrayList类型的属性

还是从两种方法来从数据库中根据老师的id查出结果

查询嵌套处理

这里改用collection标签和里面的ofType属性,原理类似:

<select id="getTeacherById" resultMap="getTeacher">
    select *
    from mybatis.teacher t
    where id = #{id};
</select>
<resultMap id="getTeacher" type="Teacher">
    <result property="id" column="id"/>
    <collection property="students" ofType="Student" column="id" select="getStudents"/>
</resultMap>
<select id="getStudents" resultType="Student">
    select *
    from mybatis.student s
    where s.tid = #{id};
</select>

结果嵌套处理

<select id="getTeacherById" resultMap="getTeacher">
    select t.id tid,t.name tname,s.id sid,s.name sname
    from mybatis.teacher t ,mybatis.student s
    where s.tid = t.id and t.id = #{id};
</select>
<resultMap id="getTeacher" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <collection property="students" ofType="Student">
        <result column="sid" property="id"/>
        <result column="sname" property="name"/>
    </collection>
</resultMap>

动态SQL

动态SQL就是根据不同的条件生成不同的SQL语句。参考

sql片段

对于经常使用到的一些sql代码片段,为了提高复用性,可以将其写在<sql>标签中,在需要使用的地方,用include标签引入即可,id要对应,例如:

<sql id="mytest">
    select t.id tid,t.name tname,s.id sid,s.name sname
    from mybatis.teacher t ,mybatis.student s
    where s.tid = t.id and t.id = #{id};
</sql>
<select id="getTeacherById" resultMap="getTeacher">
    <include refid="mytest"/>
</select>

缓存

Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大地提升查询效率。

Mybatis系统中中默认定义了两级缓存:一级缓存二级缓存

  • 默认情况下,一级缓存开启(sqlSession级别的缓存,也称s本地缓存)
  • 二级缓存需要手动开启和配置,是基于namespace级别的缓存
  • 为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存

一级缓存

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中
  • 之后再获取相同的数据,会直接从缓存中获取,不会再执行SQL查询数据库

缓存失效的情况:

  • 查询不同的东西
  • 增删改操作,可能会改变原来的数据,所以会刷新缓存
  • 查询不同的Mapper.xml
  • 手动清理缓存sqlSession.clearCache()

二级缓存

工作机制:

  • 一个会话查询一条数据,这个会话就会被放在当前会话的一级缓存中
  • 如果当前会话关闭了,这个会话的一级缓存就没了;但是我们希望会话关闭后,一级缓存中的数据会被保存在二级缓存中
  • 新的会话查询信息,就可以从二级缓存中获取内容
  • 不同的mapper查询出的数据会放在自己对应的缓存中

在mapper.xml中使用<cache/>就可以使用二级缓存,例如:

<cache eviction="FIFO"    <!--先进先出-->
       flushInterval="60000" 
       size="520" 
       readOnly="true"/>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,我可以为您提供MyBatis Plus的教程MyBatis Plus是一个开源的MyBatis框架的增强工具,它简化了MyBatis的使用,并提供了许多便利的功能。 首先,你需要在你的项目中引入MyBatis Plus的依赖。你可以在Maven或Gradle中添加以下依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本号</version> </dependency> ``` 接下来,你需要配置MyBatis Plus的相关信息。在Spring Boot项目中,可以在`application.properties`或`application.yml`文件中添加如下配置: ```yaml mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml type-aliases-package: com.example.entity global-config: db-config: id-type: auto ``` 其中,`mapper-locations`配置指定了Mapper文件的位置,`type-aliases-package`配置指定了实体类的包路径,`global-config.db-config.id-type`配置指定了主键生成策略,这里使用了自增主键。 完成配置后,你可以开始编写实体类和Mapper接口了。实体类可以使用注解来映射数据库表,Mapper接口可以继承MyBatis Plus提供的基础Mapper接口,无需手动编写SQL语句。 下面是一个简单的示例: ```java // 实体类 @Data @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; private String username; private String password; } // Mapper接口 @Repository public interface UserMapper extends BaseMapper<User> { } ``` 在上述示例中,`@TableName`注解指定了实体类对应的表名,`@TableId`注解指定了主键的生成策略。`UserMapper`接口继承了`BaseMapper<User>`,这样就可以直接使用MyBatis Plus提供的CRUD方法。 最后,你可以在Service层调用Mapper接口的方法来实现数据库操作。比如: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User getById(Long id) { return userMapper.selectById(id); } @Override public boolean save(User user) { return userMapper.insert(user) > 0; } } ``` 以上就是一个简单的MyBatis Plus的教程。当然,MyBatis Plus还提供了更多的功能,比如分页查询、条件构造器等,你可以根据具体需求去学习和使用。希望对你有帮助!如果你有其他问题,可以继续问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值