[华为云在线课程][MyBatis入门][一][介绍和简单用法][学习笔记]

举报
John2021 发表于 2022/07/12 07:05:12 2022/07/12
【摘要】 1.MyBatis简单介绍MyBatis官网地址:https://mybatis.org/mybatis-3/zh/index.htmlMyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。iBATIS一词来源于“inte...

1.MyBatis简单介绍

MyBatis官网地址:https://mybatis.org/mybatis-3/zh/index.html
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。
MyBatis支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。让开发者只关注SQL本身而不需要去编写注册驱动、创建connection、创建statement、手动设置参数、结果集检索等繁琐的代码。
MyBatis通过XML或注解的方式将要执行的各种statement(statement、preparedStatement、CallableStatement)进行配置,并通过Java对象和statement中的SQL进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL并将结果映射成Java对象并返回。MyBatis将Java方法与SQL语句关联,并没有将Java对象与数据库表关联。

1.1.JDBC的缺点及MyBatis解决方法

  1. 数据连接的创建、释放频繁造成系统资源浪费从而影响了性能,如果使用数据库连接池就可以解决这个问题。当然JDBC同样能够使用数据源。
    解决方法:在SQLMapConfig.xml中配置数据连接池,使用数据库连接池管理数据库连接。
  2. SQL语句在写代码中不容易维护,事件需求中SQL变化的可能性很大,SQL变动需要改变Java代码。
    解决方法:将SQL语句配置在mapper.xml文件中与Java代码分离。
  3. 向SQL语句传递参数麻烦,因为SQL语句的where条件不一定,可能多可能少,占位符需要和参数一一对应。
    解决方法:MyBatis自动将Java对象映射到SQL语句。
  4. 对结果集解析麻烦,SQL变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成POJO对象解析比较方便。
    解决方法:MyBatis自动将SQL执行结果映射到Java对象。

1.2.MyBatis架构介绍

  1. MyBatis配置:mybatis-config.xml,此文件作为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息。另一个mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在mybatis-config.xml中加载。
  2. 通过MyBatis环境等配置信息构造SqlSessionFactory即会话工厂。
  3. 由会话工厂创建SqlSession即会话,操作数据库需要通过SqlSession进行。
  4. MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器,一个是缓存执行器。
  5. Mapped Statement也是MyBatis一个底层封装对象,包装了MyBatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped Statement的id。
  6. Mapped Statement对sql执行输入参数进行定义,包括Map、List类型,基本数据类型,POJO类型,Executor通过Mapped Statement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是JDBC编程中对preparedStatement设置参数。
  7. Mapped Statement对sql执行输出结果进行定义,包括Map、List类型,基本数据类型,POJO类型,Executor通过Mapped Statement在执行sql后将输出结果映射至Java对象中,输出结果映射过程相当于JDBC编程中对结果的解析处理过程。

2.MyBatis基本用法

2.1.HelloWorld

首先准备一个数据库:

create database `mybatis01` default character set utf8mb4;

use `mybatis01`;

drop table if exists `mybatis01`;

create table `user`
(
    `id`       int(11) not null auto_increment,
    `username` varchar(255),
    `address`  varchar(255),
    primary key (`id`)
) engine = InnoDB
  auto_increment = 8
  default charset = utf8mb4;

insert into `user`(id, username, address)
values (1, 'aa', 'beijing'),
       (2, 'bb', 'shanghai'),
       (3, 'cc', 'guangzhou'),
       (4, 'dd', 'shenzhen');

完成数据库创建后,创建一个Maven工程,添加MyBatis的相关依赖:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.9</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>
</dependencies>

然后定义一个实体类User,

package org.example.mybatis01.model;

public class User {
    private Integer id;
    private String username;
    private String address;

    public User() {

    }

    public User(Integer id, String username, String address) {
        this.id = id;
        this.username = username;
        this.address = address;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

接下来创建一个Mapper文件,用来在MyBatis中定义SQL的XML配置文件,首先取一个namespace,相当于一个分隔符,因为在项目中会存在多个Mapper,每个都会定义对应的增删改查方法,为了避免冲突和方便管理,每个Mapper都有自己的namespace,而且这个namespace不可重复。
现在在Mapper中定义一个简单的查询方法,根据id查询用户:

<!-- UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/schema/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mybatis01.mapper">
    <select id="getUserById" resultType="org.example.mybatis01.model.User">
        select * from user where id=#{id};
    </select>
</mapper>

首先定义一个select,id表示查询方法的唯一标识符,resultType定义了返回值的类型。在select节点中,定义查询SQL,#{id},表示这个位置用来接收外部传进来的参数。
然后创建MyBatis配置文件。

<?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:///mybatis01?serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

在这个配置文件中,只需要配置environments和mapper即可。

  1. environment就是MyBatis连接的数据库环境信息,它放在一个environments节点中,意味着environments中可以有多个environment,通常开发、测试、生产三个环境都单独配置一个environment,每个environment都有一个id,然后在environments中通过default属性,指定需要的environment。每个environment中定义一个数据的基本连接信息。
  2. 在mappers节点中,定义Mapper,也就是指定我们上一步所写的Mapper的路径。

接下来编写Main方法

package org.example.mybatis01;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.example.mybatis01.model.User;

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sessionFactory.openSession();
        User user = sqlSession.selectOne("org.example.mybatis01.mapper.getUserById", 3);
        System.out.println(user);
        sqlSession.close();
    }
}

但是运行会报错,

Caused by: java.io.IOException: Could not find resource org/example/mybatis01/mapper/UserMapper.xml
	at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:114)
	at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:100)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:377)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120)
	... 4 more

因为没有在Maven里面配置扫描路径,在pom.xml中进行添加。

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
</build>

配置后重新运行程序成功

package org.example.mybatis01;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.example.mybatis01.model.User;

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sessionFactory.openSession();
        User user = sqlSession.selectOne("org.example.mybatis01.mapper.getUserById", 3);
        System.out.println(user);  //User{id=3, username='cc', address='guangzhou'}
        sqlSession.close();
    }
}

2.2.SqlSessionFactory单例模式

  • 从上面的启动类看出,加载主配置文件,生成一个SqlSessionFactory类,再由SqlSessionFactory生成一个SqlSession,SqlSession相当于我们的一个会话,类似于JDBC中的一个连接,在SQL操作完成后,这个会话可以关闭的。
  • SqlSessionFactoryBuilder用于创建SqlSessionFactory,SqlSessionFactory一旦创建就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产,所以可将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
  • SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
  • SqlSession中封装了对数据库的操作,如增删改查等。通过SqlSessionFactory创建SqlSession,而SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建。SqlSession是一个面向用户的接口,SqlSession中定义了数据库操作,默认使用DefaultSqlSession实现类。每个线程都应该有自己的SqlSession实例。
  • SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。打开一个SqlSession,使用完毕就要关闭它。通常把这个关闭操作放到finally块中以确保每次都能执行关闭。

现在对SqlSessionFactory进行封装

package org.example.mybatis01.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;

public class SqlSessionFactoryUtils {
    private static SqlSessionFactory SQL_SESSION_FACTORY = null;

    public static SqlSessionFactory getInstance() {
        if (SQL_SESSION_FACTORY == null) {
            try {
                SQL_SESSION_FACTORY = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return SQL_SESSION_FACTORY;
    }
}

在需要使用的时候,通过这个工厂方法来获取SqlSessionFactory的实例。

2.3.通过Log4j查看MyBatis运行日志

首先在pom.xml中导入log4j的相关依赖:

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.17.2</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>2.0.0-alpha7</version>
    </dependency>
</dependencies>

在resources编写log4j的配置文件log4j.properties(位置和文件名都必须是这个)

log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d[yyyy-MM-dd] method:%l%n%m%n

2.4.MyBatis增删改查

这里还以之前的数据库案例作为演示

2.4.1.增

添加记录,id有两种不同的处理方式,一种就是自增长,另一种则是Java代码传入id,这个id可以是UUID也可以是其他自定义id。MyBatis中对这两种方式都提供了相应的支持。

  1. 主键自增长
    在Mapper中添加SQL插入语句:
<insert id="addUser" parameterType="org.example.mybatis01.model.User">
    insert into user(username,address) values(#{username},#{address});
</insert>

parameterType表示传入的参数类型。参数都是通过#来引用。然后在Java代码中调用这个方法。

public class Main {
    public static void main(String[] args) throws IOException {
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sessionFactory.openSession();
        User user = new User();
        user.setUsername("testUsername01");
        user.setAddress("testAddress01");
        int insert = sqlSession.insert("org.example.mybatis01.mapper.addUser", user);
        System.out.println(insert);
        sqlSession.commit();
        sqlSession.close();
    }
}

注意,插入完成后,一定要进行提交,sqlSession.commit()。

  1. 使用UUID主键
    使用UUID作主键有两种方法,一种是在Java代码中生成UUID直接作为参数传入到SQL中,这种方式和普通参数传递一样,另一种方式是使用MySQL自带的UUID函数来生成UUID。
    在这里使用MySQL自带的UUID函数,步骤:首先调用MySQL中的UUID函数,获取到一个UUID,然后将这个UUID赋值给User对象的ID属性,然后再执行插入操作,插入时使用UUID。注意,数据ID类型改为varchar。
<insert id="addUser2" parameterType="org.example.mybatis01.model.User">
    <selectKey resultType="java.lang.String" keyProperty="id" order="BEFORE">
        select uuid();
    </selectKey>
    insert into user(id,username,address) values(#{id},#{username},#{address});
</insert>

selectKey表示查询key。
keyProperty属性表示将查询的结果赋值给传递进来的User对象的id属性。
resultType表示查询结果的返回类型。
order表示这个查询操作的执行时机,BEFORE表示这个查询操作在insert之前执行。
在selectKey节点的外面定义insert操作。

package org.example;

import org.apache.ibatis.session.SqlSession;
import org.example.mybatis01.model.User;
import org.example.mybatis01.utils.SqlSessionFactoryUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class Test01 {
    private SqlSession sqlSession;
    @Before
    public void before() {
        sqlSession= SqlSessionFactoryUtils.getInstance().openSession();
    }

    @Test
    public void addUser2() {
        User user = new User();
        user.setUsername("测试添加用户01");
        user.setAddress("北京");
        int insert = sqlSession.insert("org.example.mybatis01.mapper.addUser2", user);
        System.out.println(insert);
        sqlSession.commit();
    }


    @After
    public void after() {
        sqlSession.close();
    }
}

2.4.2.删

删除操作比较容易,首先在UserMapper中定义删除SQL:

<delete id="deleteUserById" parameterType="java.lang.Integer">
    delete from user where id=#{id}
</delete>

小坑:由于上例中将数据ID类型改为varchar后生成的UUID超出了int类型的长度,所以直接执行修改字段属性会报错,报错信息为:Incorrect integer value:‘e15b42ad-f8fe-11ec-b263-005056c00001’ for column ‘id’ at row 6。所以要先将UUID删除,然后修改成功,alter table user modify id int;

@Test
public void deleteUserById() {
    int delete = sqlSession.delete("org.example.mybatis01.mapper.deleteUserById", 8);
    System.out.println(delete);
    sqlSession.commit();
}

SQL执行后返回数据库受影响的行数。

2.4.3.改

在UserMapper.xml中定义SQL:

<update id="updateUser" parameterType="org.example.mybatis01.model.User">
    update user set username=#{username} where id=#{id}
</update>

然后在测试类中进行测试

@Test
public void updateUser() {
    User user = new User();
    user.setId(1);
    user.setUsername("aaaa");
    int update = sqlSession.update("org.example.mybatis01.mapper.updateUser", user);
    System.out.println(update);  // 1
    sqlSession.commit();
}

调用返回值也是SQL受影响的行数。

2.4.4.查

要查询所有记录

<select id="getAllUser" resultType="org.example.mybatis01.model.User">
    select * from user;
</select>
@Test
public void getAllUser() {
    List<User> selectList = sqlSession.selectList("org.example.mybatis01.mapper.UserMapper.getAllUser");
    System.out.println(selectList);
    sqlSession.commit();
}

2.5.MyBatis引入Mapper

之前编写的增删改查存在冗余代码过多,模板化代码过多的问题。比如,每个方法都要获取SqlSession,有增删改操作的话,还要用到commit,SqlSession使用完毕后还要进行关闭,SqlSession执行时需要的参数就是方法的参数,SqlSession要执行的sql和xml中的定义是一一对应的。是一个模板化程度很高的代码。
既然模板化程度很高,可以将当前方法简化成一个接口:

package org.example.mybatis01.mapper;

import org.example.mybatis01.model.User;

import java.util.List;

public interface UserMapper {
    User getUserById(Integer id);

    Integer addUser(User user);

    Integer addUser2(User user);

    Integer deleteUserById(Integer id);

    Integer updateUser(User user);

    List<User> getAllUser();
}

这个接口对应的Mapper文件如下(UserMapper.xml和UserMapper需要放在同一个包下)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/schema/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mybatis01.mapper.UserMapper">
    <!--    根据userid进行查找-->
    <select id="getUserById" resultType="org.example.mybatis01.model.User">
        select * from user where id=#{id};
    </select>

    <!--增加用户-->
    <insert id="addUser" parameterType="org.example.mybatis01.model.User">
        insert into user(username,address) values(#{username},#{address});
    </insert>

    <!--增加用户,使用UUID-->
    <insert id="addUser2" parameterType="org.example.mybatis01.model.User">
        <selectKey resultType="java.lang.String" keyProperty="id" order="BEFORE">
            select uuid();
        </selectKey>
        insert into user(id,username,address) values(#{id},#{username},#{address});
    </insert>

    <!--根据id删除用户-->
    <delete id="deleteUserById" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <!--修改用户信息-->
    <update id="updateUser" parameterType="org.example.mybatis01.model.User">
        update user set username=#{username} where id=#{id}
    </update>

    <!--查询用户信息-->
    <select id="getAllUser" resultType="org.example.mybatis01.model.User">
        select * from user;
    </select>

</mapper>

这个接口可以替代之前编写的冗余代码,因为这个接口提供了Dao层所需的最核心的东西,根据这个接口可以自动生成Dao层的代码:

  1. 首先Dao定义了SqlSessionFactory,这是固定的代码。
  2. UserMapper所在的包+UserMapper类名+UserMapper中定义好的方法名,可以定位到要调用的sql。
  3. 要调用SqlSession中的哪个方法,根据定位到的sql节点就能确定。

因此在MyBatis开发中,实际上不需要自己提供Dao层的实现只需要提供一个UserMapper即可。然后在全局配置中,配置一下UserMapper:

<?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:///mybatis01?serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="org.example.mybatis01.mapper"/>
    </mappers>
</configuration>

然后加载配置文件获取UserMapper,并调用它里边的方法:

public class Main2 {
    public static void main(String[] args) {
        SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = instance.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUser = mapper.getAllUser();
        System.out.println(allUser);
    }
}

注意,在Maven默认情况下要求我们将xml配置、properties配置等都放在resources目录下,如果我们强行放在Java目录下,默认情况下打包时配置文件会被自动忽略掉。对于这两个问题有两种解决方法:

  1. 不要忽略xml配置
    可以在pom.xml中,添加如下配置,让Maven不要忽略在Java目录下的xml配置:
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
</build>
  1. 按照Maven的要求来
    按照Maven要求来,将xml文件放到resources目录下,但MyBatis默认情况要求UserMapper.xml和UserMapper接口必须放在一起,所以需要手动在resources目录下,创建一个和UserMapper接口相同的目录,这样就不需要在pom.xml文件中添加配置了,因为这种写法同时满足Maven和MyBatis的要求。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。