欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

mybatis的使用及源码分析(一) mybatis介绍以及原生Mybatis项目搭建

程序员文章站 2022-07-12 22:38:59
...

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
mybatis github官方地址:https://github.com/mybatis/mybatis-3
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
ORM(Object/Relational Mapping)对象关系映射。是一种数据持久化的技术。在对象模型和关系型数据库之间建立关系,并且提供了一种机制,通过JavaBean对象去操作数据库表中的数据。MyBatis通过简单的XML或者注解进行配置和原始映射,将实体类和SQL语句之间建立映射关系,是一种半自动化的ORM实现。

这次的搭建不使用Spring或基于Mybatis的封装(如Mybatis-Plus)等,只使用原生Mybatis配置,更易于对于框架的了解。构建工具采用了Maven、开发工具采用IDEA、数据库采用Mysql
此次搭建项目github地址:https://github.com/zhuquanwen/mybatis-learn/releases/tag/one-base
下面介绍第一个Mybatis项目搭建:
1、项目总体结构
mybatis的使用及源码分析(一) mybatis介绍以及原生Mybatis项目搭建
2、pom.xml介绍

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.learn.zqw</groupId>
    <artifactId>mybatis-learn</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>central</id>
            <name>aliyun maven</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <layout>default</layout>
            <!-- 是否开启发布版构件下载 -->
            <releases>
                <enabled>true</enabled>
            </releases>
            <!-- 是否开启快照版构件下载 -->
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <dependencies>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>

        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>


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


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

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source> <!-- 源代码使用的JDK版本 -->
                    <target>1.8</target> <!-- 需要生成的目标class文件的编译版本 -->
                    <encoding>UTF-8</encoding><!-- 字符集编码 -->
                    <skip>true</skip><!-- 跳过测试 -->
                    <verbose>true</verbose>
                    <showWarnings>true</showWarnings>
                    <fork>true</fork><!-- 要使compilerVersion标签生效,还需要将fork设为true,用于明确表示编译版本配置的可用 -->
                    <executable><!-- path-to-javac --></executable><!-- 使用指定的javac命令,例如:<executable>${JAVA_1_4_HOME}/bin/javac</executable> -->
                    <compilerVersion>1.3</compilerVersion><!-- 指定插件将使用的编译器的版本 -->
                    <meminitial>128m</meminitial><!-- 编译器使用的初始内存 -->
                    <maxmem>512m</maxmem><!-- 编译器使用的最大内存 -->
                   <!-- <compilerArgument>-verbose -bootclasspath ${java.home}\lib\rt.jar</compilerArgument>--><!-- 这个选项用来传递编译器自身不包含但是却支持的参数选项 -->
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

使用maven-compiler-plugin插件配置了一些基本配置,包括JDK版本、编码方式等
repository下配置了阿里云MAVEN镜像加快依赖拉取速度
除了日志相关依赖,引入junit做单元测试,引入mysql做mysql驱动连接,引入lombok做自动get、set方法注入,最后引入本文最重要的依赖org.mybatis

3、user.sql 介绍
user.sql为本文测试用的Sql脚本,可自行导入Mysql

/*
Navicat MySQL Data Transfer

Source Server         : localhost
Source Server Version : 50722
Source Host           : localhost:3306
Source Database       : mybatis_learn

Target Server Type    : MYSQL
Target Server Version : 50722
File Encoding         : 65001

Date: 2020-03-06 21:51:56
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '张三', '123456');
INSERT INTO `user` VALUES ('2', '李四', '123456');
INSERT INTO `user` VALUES ('3', '王五', '123456');

4、实体类
domain包下定义了数据库对应的实体类User

package com.learn.zqw.domain;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private Integer id;

    private String username;

    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

}

5、mapper.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.learn.zqw.mapper.IUserMapper">

    <resultMap id="BaseResultMap" type="com.learn.zqw.domain.User">
        <id     column="id"       property="id"/>
        <result column="username"     property="username"/>
        <result column="password"     property="password"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, username, password
    </sql>

    <!--此处配置dao接口所要实现的SQL语句-->

    <!--插入一条记录-->
    <insert id="insert" parameterType="com.learn.zqw.domain.User">
        insert into user (username, password) values (#{username}, #{password})
    </insert>

    <!--插入记录并返回ID-->
    <insert id="insertAndGetId" useGeneratedKeys="true" keyProperty="id" keyColumn="id" parameterType="com.learn.zqw.domain.User">
        insert into user(username, password)
        values(#{username}, #{password})
    </insert>

    <!--插入记录并返回ID第二种方式-->
    <insert id="insertAndGetId2" parameterType="com.learn.zqw.domain.User" >
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID()
        </selectKey>
        insert into user(username, password)
        values(#{username}, #{password})
    </insert>

    <!--条件插入-->
    <insert id="insertSelective" parameterType="com.learn.zqw.domain.User">
        insert into user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username != null and username != ''">username,</if>
            <if test="password != null and password != ''">username,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="username != null and username != ''">#{username},</if>
            <if test="password != null and password != ''">#{username},</if>
        </trim>
    </insert>

    <!--条件修改-->
    <update id="updateByPrimaryKeySelective" parameterType="com.learn.zqw.domain.User">
        update user
        <set>
            <if test="username != null and username != ''">
                username = #{username},
            </if>
            <if test="password != null and password != ''">
                password = #{password},
            </if>

        </set>
        where ID = #{id}
    </update>

    <!--修改-->
    <update id="updateByPrimaryKey" parameterType="com.learn.zqw.domain.User">
        update user set username = #{username}, password = #{password} where id = #{id}
    </update>

    <!--查询所有-->
    <select id="findAll" resultType="com.learn.zqw.domain.User">
        select * from user
    </select>

    <!--按照ID查询-->
    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer">
        select
        <include refid="Base_Column_List"/>
        from user where id = #{id}
    </select>

    <!--按照ID删除-->
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
        delete from user  where id = #{id}
    </delete>
</mapper>

这里定义里各类查询Sql,包括:查询、新增、能够返回ID的新增、按条件新增、删除、修改、按条件修改等。
namespace对应mapper层的接口,parameterType对应实体层的实体User

6、mapper层

package com.learn.zqw.mapper;

import com.learn.zqw.domain.User;

import java.util.List;

public interface IUserMapper {


    List<User> findAll();
    int insert(User user);
    Integer insertAndGetId(User user);
    Integer insertAndGetId2(User user);
    int insertSelective(User user);
    int updateByPrimaryKeySelective(User user);
    int updateByPrimaryKey(User user);
    User selectByPrimaryKey(Integer id);
    int deleteByPrimaryKey(Integer id);

}

接口内定义了与IUserMapper.xml中对应的函数。

7、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">
<!--mybatis的主配置文件-->
<configuration>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql环境-->
        <environment id="mysql">
            <!--配置事务类型-->
            <transactionManager type="JDBC" />
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <!--配置连接数据库的4个基本信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_learn?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
    <mappers>
        <mapper resource="com/learn/zqw/IUserMapper.xml" />
    </mappers>
</configuration>

这里transactionManager 定义了事务类型,POOLED为Mybatis内置的连接池,dataSource中定义了数据库连接信息,注意url中最好加上编码,不然可能会出现中文乱码问题,mappers标签定义了定义Mapper的XML的位置。

8、单元测试

package com.learn.zqw;

import com.learn.zqw.domain.User;
import com.learn.zqw.mapper.IUserMapper;
import lombok.Cleanup;
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.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

@RunWith(JUnit4.class)
public class BaseTest {

    /**
     * 测试基本Mybatise的运行
     * */
    @Test
    public void baseTest() throws IOException {
        //1.读取配置文件
        @Cleanup InputStream in = Resources.getResourceAsStream ("SqlMapConfig.xml");
        //2. 创建SqlSessionFactory
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产SqlSession对象
        @Cleanup SqlSession session = factory.openSession ();
        //4.使用SqlSession创建Mapper接口的代理对象
        IUserMapper userMapper = session.getMapper(IUserMapper.class);


        //5.使用代理对象执行分法

        //1)获取所有数据
        List<User> users = userMapper.findAll();
        Assert.assertNotNull(users);
        System.out.println("=========查询所有的结果======");
        if (users != null) {
            users.forEach(System.out::println);
        }
        System.out.println("=========查询所有的结果End======");

        //2)插入记录
        int insertResult = userMapper.insert(new User(UUID.randomUUID().toString(), "123456"));
        Assert.assertEquals(1, insertResult);

        //3)插入记录返回ID
        User user1 = new User(UUID.randomUUID().toString(), "123456");
        int getId = userMapper.insertAndGetId(user1);
        System.out.printf("插入并返回ID:[%d]\n", user1.getId());
        Assert.assertNotNull(user1.getId());


        //4)插入记录返回ID2
        User user2 = new User(UUID.randomUUID().toString(), "123456");
        int getId2 = userMapper.insertAndGetId2(user2);
        System.out.printf("插入并返回ID:[%d]\n", user2.getId());
        Assert.assertNotNull(user2.getId());

        //5)按条件插入
        User user3 = new User();
        user3.setUsername(UUID.randomUUID().toString());
        int selectiveInsertId = userMapper.insertSelective(user3);
        Assert.assertEquals(1, selectiveInsertId);
        Assert.assertNull(user3.getPassword());

        //6)按ID查询
        User user = userMapper.selectByPrimaryKey(1);
        Assert.assertNotNull(user);

        //7)按条件修改
        user.setUsername(null);
        user.setPassword("1234567");
        int x = userMapper.updateByPrimaryKeySelective(user);
        Assert.assertEquals(1, x);

        //8)全部修改
        User user4 = userMapper.selectByPrimaryKey(1);
        user4.setPassword("12345678");
        int y = userMapper.updateByPrimaryKey(user4);
        Assert.assertEquals(1, y);
        session.commit();

        //9)删除,先删了再回滚下
        int i = userMapper.deleteByPrimaryKey(1);
        Assert.assertEquals(1, i);
        session.rollback();

    }
}